Skip to content

P5: lazurio module start|stop|status|logs and /api/modules — the module lifecycle over the catalog, systemd user units with journal logs, hosted links only from the recorded entry (base-instructions-11) - #73

Merged
immakermatty merged 9 commits into
mainfrom
claude/DEV-6628-module-lifecycle
Sep 28, 2026

Conversation

@immakermatty

@immakermatty immakermatty commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Why

Slice P5 of the Launchpad parity program (root decision 0167 points 1–2, 0137 as amended; shaping #65 B3/B6): the resident Launchpad's module lifecycle, done properly in the Platform and CLI first, so an agent or an operator can run a module's application without the Launchpad, and the Launchpad composes the same core.

What changes

  • lazurio module start|stop|status|logs <Org>/<module> [--app <package>] [--json] over the catalog of Launchpad home is the catalog: organization list, module list, POST /api/catalog; execution admission variant B (H1 decided) #68: the module is named through the one selection rule (an ambiguous slug is refused), a module that cannot run is refused with its typed reason, unknown or invalid apps are refused. Start runs the module's default app (or the declared --app) from its own declaration with the operator's Bun (~/.local/bin/bun; missing → toolchain-missing, nothing happens); it does not install dependencies (prerequisites-not-ready names the fix). No new Folder state: what runs is what the OS service manager or the session reports.
  • Linux: the app is a transient systemd user unit that survives a Launchpad restart; unit output goes to the journal (StandardOutput/Error=journal), logs is a bounded journalctl --user tail of that unit. macOS: apps stay session-bound; the CLI answers launchpad-required / logs-unavailable.
  • The link: runtime.url only while healthy. Locally the loopback address the app's owner observed; on a hosted Machine only moduleOrigin(template, id) from the recorded entry (P3: the Platform reads the handover entry (Launchpad, T3 Code, module origins), schema re-pinned to Machines 0.12.93; module origins by the gateway's label rule #71), default app only, never a loopback address — no recorded entry, no link (hosted-entry-missing).
  • HTTP GET /api/modules/<org>/<module>/status, POST …/start|stop behind the existing admission, returning the same object as the CLI (tests compare; 409 on refusal). /api/apps/* and app-request are kept unchanged (their retirement is recorded as open in the docs).
  • Launchpad module page: an Application card with a status dot, Start/Stop and Open only when there is a link; cs/en.
  • Generated manual base-instructions-11: "Module applications" section (commands, toolchain, hosted = systemd user service, the link is only runtime.url), the hosted "never send a localhost link" rule now names runtime.url, and the Recovery page sentence for PR Recovery page: Recovery mode serves the page, GET /api/recovery = lazurio recover, copy the prompt and the prepared issue; the issue names the start refusal (F21) #72 (hosted and workstation).
  • Marked seams for ensure (P6), the T3 chat link (P7) and prepare/open/--source (P9); none implemented.

What deliberately does NOT change

No content sync, no ensure, no T3 link, no doctor, nothing of the update core; /api/apps/*, --organization-directory, --bun-executable stay.

Verified

Author of the slice: bun run check 886 pass, exit 0, and the page driven in headless Chromium against the compiled executable (Start → healthy with Open → Stop, no action on a module that cannot run). Thread owner: bun run check on 71cb9c4 (with the manual sentence added): exit 0.
Not verified: a real Ubuntu systemd user manager and journal (whether journalctl --user --unit reads without persistent per-user journal storage) — that is the C.5 qualification (M3) and the first hosted run.

Risks / follow-ups

🤖 Generated with Claude Code

immakermatty and others added 8 commits September 28, 2026 17:19
…unded journalctl tail of one generated unit (launchpad-parity B6)

The fixed unit policy changes one property pair: an application's output
goes to the user journal instead of being discarded. readApplicationJournal
reads the newest lines of one generated unit name (-o cat, at most 1000
lines) through the service-manager process adapter, which gains the
read-only journalctl. A unit an older release started with null output is
foreign (service-unrecognized) until it is stopped once; the ownership
table now proves exactly that transition.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…older.ts

writeOrganization and folderFixture are shared by the catalog tests and the
module lifecycle tests of slice P5; unchanged otherwise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…/…: the module lifecycle over the catalog, one core for the CLI and the Launchpad (launchpad-parity B3)

- src/modules/module-operations.ts: names a module <Org>/<module> through
  the catalog's selection rule (an ambiguous slug is refused, a module that
  cannot run is refused with its catalog reason, an undeclared or invalid
  --app likewise) and runs its default app, or the named one, through the
  existing lifecycle, runners and localApplicationAdapters. No state: the
  running state is the service manager's (Linux) or the Launchpad
  session's (macOS).
- Toolchain from the standard path: Bun is <home>/.local/bin/bun (B2);
  missing, start is refused as toolchain-missing before any effect. On Linux
  a started app gets the installed unit's PATH line, so the CLI and the
  Launchpad start identical units.
- The link (runtime.url) only while the app reports healthy: locally the
  loopback address its owner observed; on a hosted Machine only the
  recorded entry's moduleOrigin(template, id) for the default app, never a
  loopback address; without a recorded entry no link (hosted-entry-missing).
- CLI: runModuleCommand in src/modules/module-cli.ts; the CLI builds a
  lifecycle per call, so on a session platform it answers
  launchpad-required, and logs there logs-unavailable.
- HTTP: GET /api/modules/<org>/<module>/status, POST …/start and …/stop
  behind the existing admission, answering the same objects (409 when
  refused). The Launchpad holds one lifecycle per Organization and closes
  them on shutdown (session apps end with it, service apps stay).
- /api/apps/* and app-request are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d Open while it reports a link (slice P5)

The module route /o/<org>/<module> of an executable module shows an
Application card in the settings-row pattern: the status (running,
starting, not ready, stopping, ended, stopped) with its dot, who keeps it
running, why a healthy app has no link, the sentence after the last action
(polite live region), and on the right Open (a new tab, only the root of an
https hostname or a loopback port) and the one primary action. After Start
the page reads the status until the app reports healthy (about half a
minute at most); focus returns to the action. The requests are GET
.../status and POST .../start|stop, the same core as lazurio module.
Pure presentation in src/launchpad/module-view.ts; Czech and English copy;
the catalog intro no longer says starting comes later.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ted link only from runtime.url (base-instructions-11)

For every preset with Organizations a new section names the commands kept
from the resident (root decision 0167): module list, start, stop, status
with runtime.url, logs from the journal, the toolchain at ~/.local/bin/bun
(toolchain-missing) and prerequisites-not-ready; hosted it says apps are
transient user services that survive a Launchpad restart, locally it adds
the macOS session rule (launchpad-required). The hosted "Application links"
section keeps "never a localhost link", carries the interim sentence of
launchpad-parity B14 (a localhost link works only for a process on this
Machine) and now names the link as runtime.url, composed only from the
recorded entry and never from a naming convention; a null runtime means no
link. Generated output changes, so the template revision moves to
base-instructions-11 and the existing snapshots are regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s it changes, and the marked seams of P6, P7 and P9

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ained the held ones

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… and workstations (base-instructions-11)

The Recovery page (LazurioPlatform PR #72) offers the repair-agent prompt
and the prepared issue; the manual says so and that nothing of it leaves the
Machine automatically. Rides on the template revision this branch bumps.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ecycle

Both slices add messages and a startLaunchpad parameter; the Recovery
page's recovery use case keeps main's position and the module host comes
last. The page routes moved to page.ts with #72; the module routes stay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-head review of 81720e1 (P5). Verified the hosted runtime link derives only from the recorded machine.entry.moduleOriginTemplate, never from a hostname convention or a loopback URL; missing entry and non-default app have no hosted link. CLI --json and HTTP start/stop/status share the core and match on the fixture; ambiguous/non-executable/unknown app and missing-Bun starts refuse before spawning. The systemd transient unit has explicit PATH and journal stdout/stderr, journalctl reads a bounded tail, and another Launchpad can rediscover and stop the exact owned unit without changing foreign units. The macOS session ends with its Launchpad. base-instructions-11 snapshots and the Recovery sentence pass. Local bun run check: 897 pass, 4 skip, 0 fail; lint, typecheck, public guard, proof build and smoke pass. Both required GitHub checks (Ubuntu and macOS) succeeded on this exact head; zero unresolved review threads. No blocking findings. No merge performed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants