5.3 KiB
Row 41, slice 1 S6: build plan (Filbert)
Issue #1523. Brief: docs/plans/2026-10-04_slice-1.md § Slice 1 S6, blob
72d11de2. Base 915e00e5. Built in a detached worktree; the packet is
build.patch plus candidate-manifest.sha256, as in Rocko's
agents/rocko/work/s4-follow-up/. Filbert doesn't commit the candidate
source and doesn't push.
Shape
The launcher has to live in the trusted bus host (packages/cli/src/host.mjs).
Only the host holds the IPC channel that binds a launch, and only the broker
can authorize role.launch. So:
PM session --(launch tool: cap, instance, model)--> host launch socket
host --IPC identity/authorize--> broker (action.allowed or refusal evidence)
host: instance list, family capacity, credential status, then spawn
host --IPC bindLaunch--> broker (session.launched)
host writes the cap file; the runner reads it, role.claim
session exit --IPC endLaunch--> broker (session.ended, claim released)
A managed session is a runner (packages/harness/src/runner.mjs) inside
unshare --user --map-current-user --pid --fork --kill-child --mount-proc.
The runner claims the role, loops on message.receive, runs one harness
turn per batch through the adapter (persistent session directory), and
sends the turn's answer back to the sender. A wall clock bounds each turn
(S0 line 4).
Pieces
| Piece | Where | What |
|---|---|---|
| Bundle | packages/harness/src/bundle.mjs |
prompt, policy, skills list, typed tools, manifest from resolveInstance output; per harness files (Pi: extension args; Claude: settings with the wrapped gate, MCP config) |
| Typed tools | packages/harness/src/tools.mjs |
vocabulary action → tool; only actions the instance holds, plus reads |
| Pi extension | packages/harness/src/pi-extension.mjs |
registers the typed tools; tool_call gate blocks tools outside the policy (S0 lines 1-3) |
| Claude gate and MCP server | packages/harness/src/claude-gate.mjs, mcp-server.mjs |
PreToolUse command hook, wrapped `timeout -k 2 10 node gate |
| Runner | packages/harness/src/runner.mjs |
the managed process; founder-credential stop (REQ-CRED-2) |
| Claude adapter | adapters/claude/adapter.sh |
adapter contract, plus the bundle's settings and MCP files |
| Pi adapter | adapters/pi/adapter.sh |
takes MOSAIC_EXTENSIONS (-e), still --no-extensions |
| Session launcher | packages/seat/src/session.mjs |
spawn under unshare, registry file, launch log, stop |
| CLI | packages/seat/src/cli.mjs |
mosaic talk, mosaic stop <run>, `mosaic launches off |
| Host | packages/cli/src/host.mjs, cli.mjs |
launch socket; mosaic bus start <business> --pm <harness>:<model> launches the PM without a window |
| Broker | packages/bus/src/{broker,runtime,process}.mjs |
IPC ops identity, authorize, refuse, endLaunch, credentialStatus; Broker.endLaunch |
Choices and tradeoffs (consequential)
- Outside the brief's file list:
packages/bus(three IPC ops and one trusted method, the counterpart ofbindLaunch) andpackages/cli(host launch socket,--pm). The brief says the PM launches "through the broker", and the bus README leaves spawning, allowlists and capacity to S6; neither can happen without these. No socket verb is added to the broker, no event kind and no schema change. mosaic launchname:mosaic launch <seat>stays the T3 seat launcher. Role sessions are started by the host (the PM at boot, with--pm) and by the PM'slaunchtool.mosaic stopandmosaic talkare new verbs;mosaic launches offis Jason's one word (the broker'slaunch.revoke).- Capability handoff: a 0600 file in the run directory, written after the bind (the launch record needs the pid first). Weaker than an inherited fd; same-UID either way.
- Lead decision 62 gap: each session gets its own PID namespace, so a
setsid -f env -ichild reparents to the runner, which is still under the launch record's pid, and the human CLI's ancestry check refuses it. Limits stay: the broker socket is shared, and a same-UID agent can still ask something outside its tree (a systemd user manager, an existing tmux server) to run a command. Recorded, not called a wall. - Capacity: families are the keys of
launch.max; the family is the model name containingopusorsonnet. A model in no listed family is refused. The count includes every live managed session, the PM's too. - Founder credentials (REQ-CRED-2): the session environment is an
allowlist, so
GITEA_TOKENand friends never pass. The host puts the broker's credential status (metadata only) for the instance in the policy. The runner stops before claiming if a service the role needs has no usable role token, or if a known founder credential variable reached its environment.
Gate
- Suites:
packages/harness,packages/seat, every node suite, everyscripts/test-*.sh, sequential, teed, Docker pointed at a missing socket. - Recorded run: a scratch data root and a test business with fixture
tokens (no tracker), host started with
--pm,mosaic talkasks the PM to launch a coder,mosaic agentsshows both claims. Not on Astra (R26), and not against the livemosaic-bus@mosaic-stackunit. - Darkwing approves on #1523.