Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 99 additions & 2 deletions docs/launchpad-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ of them. The original bounded proof is unchanged and is not relabelled as a full
## Settings: structure, routes and the T3 Code pattern

The Launchpad is one page with two views (decision F15 addendum 2026-09-28): the
Launchpad home (`/`, the development Application panel) and Settings. Settings follows
Launchpad home and Settings. Since slice P4 of the Launchpad parity the home is the
catalog of the Folder's Organizations and modules
([below](#launchpad-home-the-catalog)); the addendum's sentence that the development
Application panel stays on the home needs the Principal's amendment. Settings follows
the settings UX of T3 Code, as the Principal asked, in plain CSS inside
`src/launchpad/index.html` and without a framework or a new dependency.

Expand All @@ -83,7 +86,7 @@ the frame.
| This Machine (read-only handover) | Settings → This Machine, one row per recorded fact |
| Tools (groups, cards, dialogs, MCP card) | Settings → Tools; Refresh status is its page action in the header |
| Product update pill, with the read-only "Folder refresh needed" line (F17 addendum) | Sidebar footer above Settings/Back, visible from every route, and only while an update is available or under way (Principal 2026-09-28, as in T3 Code); the Folder refresh line is independent of the pill, a subdued notice right above it with the command in selectable monospace |
| Application (development lifecycle) | Launchpad home `/`, not a setting |
| Application (development lifecycle) | Launchpad home `/`, not a setting; since P4 replaced there by the catalog, its API kept until P5 |

**Patterns adopted from T3 Code** (source: `pingdotgg/t3code` at `d15210cd3d`,
`apps/web/src/`, and the installed T3 Code 0.0.42 bundle):
Expand Down Expand Up @@ -127,6 +130,100 @@ per-row reset are not built: the page has three sections and no defaults to rese
The General settings keep the explicit Preview → Apply of a Folder change instead of
T3 Code's immediate apply. Icons are Lucide (ISC), inlined as SVG symbols.

## Launchpad home: the catalog

Slice P4 of the Launchpad parity (shaping `docs/launchpad-parity.md` B1 on its review
branch; decision F22, whose points the Principal decided on 2026-09-28). The home shows the
Organizations and modules of the Folder the Launchpad serves; the developer form and
its "Development fixture only" banner are gone.

**Source.** One core, `readFolderCatalog` in `src/organizations/catalog.ts`: every
directory in `<Folder>/organizations/` (not files, not hidden entries) is one candidate,
resolved by the canonical reader (`read-applications.ts`, `root-resolution.ts`). No
allowlist, no planned slots, no `launchpad.gen3*.json`, no state: the catalog is
recomputed on every read. A candidate that cannot be read keeps its typed reason
(`canonical-documents-required`, `organization-conflict`, `template-not-runtime`,
`organization-changed`, `organization-unavailable` for a link or a directory that is
not the operator's) and never hides the others; two candidates that declare the same
slug are both `organization-duplicate`, because `<Org>/<Module>` would be ambiguous.
Per module: Organization slug, module id, path, apps and the default app, Teams (N:M)
with their source `teamsSource`, the root state and `executable`, or a typed `reason`
(`organization-not-executable`, `declaration-conflict`, `module-unavailable`,
`explicit-apps-required`, `no-app`, `default-app-invalid`). Executable means the
declarations admit a start of the default app under the one admission rule of the
[organization contract](organization-contract.md) (variant B, decided 2026-09-28 on
question H1: a canonical-only `current` Organization runs); it is not readiness, provider permission or a lease. A malformed
Team membership is reported as `teams-invalid` on the module and never blocks it.

**Teams.** The canonical form is `module_slots[].teams`. The catalog resolves membership
exactly as the resident's read model does (`organizationSlotTeams` in the legacy root's
`lazurio/core/organization-slot-scope-lib.mjs`, the order of `declaredSlotTeams` in
`lazurio/runtime/discovery-lib.mjs`): `teams` when it is a list; otherwise, for
compatibility with older manifests, the legacy alias, the `workspaces` list and then the
singular `workspace`; blank entries and `productionspace` are dropped; nothing left means
the default Team `workspace` (decision 0041, as the resident's Launchpad README says).
`teamsSource` is `teams`, `legacy-alias` or `default`. An Organization with any
`legacy-alias` module is named once, never per module: a note under the CLI tables and a
"Team membership" fact on its page, so its manifest can be migrated to `teams`.

**CLI first.** `lazurio organization list [--folder <F>] [--json]` prints the catalog
(`--json`: exactly the object below), `lazurio module list [<Org>] [--folder <F>]
[--json]` its modules, for one Organization named by slug (case-insensitive) or
directory name. Without `--folder` the Folder is found as `lazurio update` finds it:
the supervised unit's `[X-Lazurio] Folder=`, otherwise the hosted Machine's declared
operator (`standardFolder` in `src/update/cli.ts`, one function for both); a
workstation without a unit names it. Human output is aligned columns as in
`lazurio tools list`.

**HTTP.** `POST /api/catalog` with `{}` answers the same catalog (tests compare it to
`organization list --json`), behind the same admission as every other route: the
fragment token locally, the gateway's cookie hosted.

**Routes.** `/` is every Organization's modules with their default app; `/o/<org>` one
Organization (directory, resolution state, Teams, issues, then its modules per Team);
`/o/<org>/<module>` one module (Organization, Teams, apps with the default marked,
path, resolution state, whether it can run). `<org>` is the slug and each segment is
URL-encoded; an Organization that could not be read has no route and is shown with
its reason on `/`. A route the Folder does not have says so with a link to all
Organizations. The paths live in `src/launchpad/routes.ts` next to the settings
routes, the server answers them with the same page (Bun route parameters) and nothing
deeper, and they behave like the settings routes: deep links, back and forward,
focus on the heading, the breadcrumb "Organization / module", the document title.

**Sidebar in T3 Code's pattern.** On the home frame the sidebar lists "All
Organizations", then each Organization as a group (T3's projects) with its modules as
rows (T3's threads) and a status dot (green: can run; grey: cannot, with the reason
in the row's accessible name), under a subheader per Team: the declared Teams in
their order, Teams a module names without a declaration, then "Other modules"; a
module of two Teams is a row under both, and without any Team there is no subheader.
The current Organization or module is `aria-current="page"`. "Refresh" is the page
action in the header. Below 768 px the sidebar is the same off-canvas sheet as in
Settings, and choosing a row closes it.

**What a module row shows, and what not yet.** Name, Teams, default app, and "Can run"
or the reason in words with its code. There is no start, open, stop or logs action
and no disabled button in its place: the module lifecycle `lazurio module
status|prepare|start|open|stop|logs` and its page actions are the next slice (P5),
which also retires `/api/apps/*`, `app-request` and the development flags
`--organization-directory` and `--bun-executable`. Until then those keep working
unchanged for their tests and `scripts/smoke-application-ui.ts`, which drives them
over the API. `src/launchpad/catalog-view.ts` holds the pure presentation (tested in
`tests/catalog-view.test.ts`), `src/launchpad/catalog-panel.ts` the DOM, drawn with
`textContent` only.

**Verification 2026-09-28.** Unit and HTTP tests (`tests/organization-catalog.test.ts`,
`tests/catalog-view.test.ts`, `tests/launchpad-routes.test.ts`, the hosted test) use a
fixture Folder with two Organizations (one `transition` with a module in two Teams,
one canonical-only `current`), one invalid, one template, a linked candidate and a
duplicated slug; both admission variants were run by flipping the one constant. The
home was driven in headless Chromium (Playwright) against an isolated home, an empty
PATH and a temporary fixture Folder, in English and Czech: no form or banner, four
groups, a module under two Teams, no row actions, sidebar and body links to the
Organization and module routes with focus on the heading, the breadcrumb, back and
forward, a deep link with the token, an unknown route, Settings and Escape back,
the narrow sheet, light and dark, without page errors. Screen-reader output was not
qualified by a manual run.

## Tools section

The page has a section "Tools" / "Nástroje" (decision F18,
Expand Down
29 changes: 22 additions & 7 deletions docs/module-adoption.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,22 @@ Discovery does not install application authorization adapters: a discovery-only
still refuses app control. Live provider/operation binding remains a separate missing
integration, not an implied permission from this local view.

**Since 2026-09-28 (slice P4 of the Launchpad parity)** the Launchpad home is the
catalog of the Folder ([launchpad development](launchpad-development.md#launchpad-home-the-catalog)),
read from every directory in `<Folder>/organizations/` by `lazurio organization list`,
`lazurio module list` and `POST /api/catalog`; the panel's discovery and selection form
is gone from the page. `POST /api/apps/discover`, the application operations
`/api/apps/*` and `app-request` described here are unchanged and stay until the
module lifecycle `lazurio module …` (P5) retires them; `scripts/smoke-application-ui.ts`
now drives them over the API. Which Organization states may run applications is the
one admission rule of the [organization contract](organization-contract.md).

The existing Launchpad server can compose one application lifecycle with trusted
authorization/toolchain adapters. Its authenticated application API serves both the
browser controls and the compiled development CLI's `app-request` stdin transport.
The latter accepts an explicit existing private session URL, operation and declared
selection, not a second locator, process owner or persisted credential. The standard
authorization/toolchain adapters. Its authenticated application API serves the
compiled development CLI's `app-request` stdin transport; until 2026-09-28 it also
served the page's controls, which slice P4 removed. `app-request` accepts an
explicit existing private session URL, operation and declared selection, not a
second locator, process owner or persisted credential. The standard
`launchpad --folder` command still configures only profiles; production Organization
bindings and the complete preparation/install journey remain incomplete.

Expand All @@ -47,7 +58,10 @@ the CLI/server path. These deadlines do not cancel an operation, prove rollback,
or solve queue admission, reconnect/status tracking and uncooperative adapter
timeouts; those remain integration work, not reasons to automatically retry a write.

The browser exposes explicit preparation/start/status/link/stop in Czech and English. A link is restricted
Until 2026-09-28 the page's form exposed explicit preparation/start/status/link/stop
in Czech and English; since slice P4 the page shows no application control and these
operations are reachable only through the application API until the module lifecycle
(P5) brings start, open and stop to the catalog. A link is restricted
to the selected execution Machine's observed loopback web listener, never production
metadata. Remote-profile context does not expose that address as a local browser link;
a qualified remote access route remains required. Process start, health observation,
Expand All @@ -56,8 +70,9 @@ opening a page and functional acceptance are distinct results.
Run `bun run scripts/smoke-application-ui.ts cs` and the same command with `en` using
an explicitly supplied external Playwright installation and its Chromium (for example,
via the test environment's `NODE_PATH`). This optional local harness compiles the CLI,
creates synthetic Folder/module data and an isolated browser, opens the synthetic app,
then stops its owner and removes only its temporary fixture. It is not a release gate
creates synthetic Folder/module data, drives the Launchpad's application API (since
2026-09-28; before that the page's form), opens the synthetic app in an isolated
browser, then stops its owner and removes only its temporary fixture. It is not a release gate
or evidence of either real candidate's installation/DB readiness. The main `bun run
check` includes transport, lifecycle and presentation unit/integration tests; it does
not implicitly download a browser or run this separate browser harness.
Expand Down
48 changes: 31 additions & 17 deletions docs/organization-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,41 @@ interim implementation of the same compatibility-state table, not a second schem
missing inventory is `conflict`. This recognizes drift only where parity is
proven and is stricter than upstream, never looser.
- Execution admission (`resolveOrganizationApplication`, hence Launchpad/CLI
`prepare`, `clean-prepare` and `start`) accepts only parity-valid `transition`;
it is the single executable state. A canonical-only `current` root is observable
and inspection-only until a separately proven finalization: decision 0145 lets
the projection disappear only after the finalization gate has covered every
mutation-capable reader, and a valid digest proves the projection's content, not
that the gate ran. `current` becomes executable only once the pinned Core
envelope carries an explicit, verified finalization admission signal. `legacy`,
`projection_drift`, `conflict`, `missing`, a template and an unresolvable root
refuse before descendant inspection, the owner lock, preparation, script start
or any write. Every present document is normalized with the upstream issue codes
(slot path grammar and scope, `module_port_pool` range, legacy identity and
schema) before any state is assigned.
`prepare`, `clean-prepare` and `start`, and `executable` in the Folder catalog)
asks one rule, `isExecutableOrganizationState` in
`src/organizations/root-resolution.ts`, with two variants in one constant:
**transition-only** (variant A, F12 as accepted: only parity-valid `transition`
executes, a canonical-only `current` root is observable and inspection-only) and
**transition-and-current** (variant B: `current` executes as well). The default is
**variant B, decided by the Principal on 2026-09-28** (question H1 of the
Launchpad parity shaping, decision F22 point 1): the checkout exists because
GitHub allowed the clone, and the projection gate was migration machinery.
Variant A stays one line away for the record and tests cover both variants. Under either variant `legacy`, `projection_drift`,
`conflict`, `missing`, a template and an unresolvable root refuse before
descendant inspection, the owner lock, preparation, script start or any write.
Every present document is normalized with the upstream issue codes (slot path
grammar and scope, `module_port_pool` range, legacy identity and schema) before
any state is assigned.
- Discovery stays inspection-only: `applications-observed` carries the resolution
state and an explicit `admission: executable | inspection-only`; a
`projection_drift` root is still listed from the canonical file but is not
executable. Conflict returns `organization-conflict` with issue codes only.
state and an explicit `admission: executable | inspection-only`; a root the
admission rule does not execute (`projection_drift`, and `current` under variant
A) is still listed from the canonical file but is not executable. Conflict
returns `organization-conflict` with issue codes only.
- The Folder catalog (`lazurio organization list`, `lazurio module list`, the
Launchpad home) applies this reader to every directory in
`<Folder>/organizations/`; a candidate that cannot be read, a template and two
candidates declaring one slug are isolated with a typed reason and never hide
the others. See [launchpad development](launchpad-development.md#launchpad-home-the-catalog).

## Exit from transition-only admission

Accepted direction (2026-09-19, [decision F12](decisions.md#f12--canonical-only-organizations-and-a-deliberately-narrow-first-delivery)),
not implemented. **Canonical-only Organizations are the target normal case.** Upstream
Accepted direction (2026-09-19, [decision F12](decisions.md#f12--canonical-only-organizations-and-a-deliberately-narrow-first-delivery)).
**Decided 2026-09-28 (question H1 of the Launchpad parity shaping, decision F22
point 1):** the interim gate ends now. No owner of the identity continuity proof is
named, and a canonical-only Organization would otherwise lose its applications when
the Platform Launchpad replaces the resident one. The implementation runs variant B
above and keeps variant A one line away for the record; the text below is F12 as
accepted and stays as history of the gate. **Canonical-only Organizations are the target normal case.** Upstream
decision 0145 deprecates the legacy projection and makes `current` the end state of
every Organization. Admitting only parity-valid `transition` roots, as described above,
is an **interim gate** tied to upstream finalization readiness. It is not a product
Expand Down
Loading
Loading