Files
stack/agents/filbert/work/s6/PLAN.md
T

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)

  1. Outside the brief's file list: packages/bus (three IPC ops and one trusted method, the counterpart of bindLaunch) and packages/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.
  2. mosaic launch name: 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's launch tool. mosaic stop and mosaic talk are new verbs; mosaic launches off is Jason's one word (the broker's launch.revoke).
  3. 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.
  4. Lead decision 62 gap: each session gets its own PID namespace, so a setsid -f env -i child 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.
  5. Capacity: families are the keys of launch.max; the family is the model name containing opus or sonnet. A model in no listed family is refused. The count includes every live managed session, the PM's too.
  6. Founder credentials (REQ-CRED-2): the session environment is an allowlist, so GITEA_TOKEN and 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, every scripts/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 talk asks the PM to launch a coder, mosaic agents shows both claims. Not on Astra (R26), and not against the live mosaic-bus@mosaic-stack unit.
  • Darkwing approves on #1523.