One interactive session picks 1–2 lanes, spawns a sibling session per lane, tracks their status, and closes the day by writing the diary entry.
The mechanics already exist — the work is writing the procedure down. Sibling sessions (claude --bg), machine-readable per-session status, the result:/needs input:/failed: protocol, enforced worktree isolation, and a per-session diary cadence are all live in the harness today, and the pattern was proven on 2026-07-04 when the safety.viz scaffold ran as a briefed background session. What's missing is the orchestration procedure as durable skills: two new skills in safety.agent (roadmap-session, diary-entry), a formal lane-briefing contract, and one new Requirement on the hub. No new infrastructure.
01 · Concept
Jeremy opens a Claude Code session in obot2 and invokes the roadmap agent. It reads the hub, proposes one or two lanes — each a concrete unit of work, typically an open sub-issue with a designed requirement behind it — and, on approval, spawns a sibling session per lane. While lanes run, the roadmap session is the control tower: it reports status on demand, relays questions from lanes to Jeremy, and stays available for hub work (requirement edits, reviews, planning). When lanes land, it collects each one's result and PR links and writes the day's diary entry.
Why siblings, not sub-agents. A sub-agent (Agent tool) runs inside the orchestrator's context and dies with it. A sibling is a full top-level session: its own context window, model, permission mode, transcript, and worktree — visible in the agents view, resumable, and it survives the orchestrator ending. Status lives in the harness, not in the orchestrator's memory, which is what makes orchestration restartable (see §06). This matches the existing preference and the scaffold /spawn pattern already in use.
02 · Inventory
Everything below exists today and was verified in this session. The orchestrator consumes these; it does not build them.
| Mechanism | What it gives the orchestrator |
|---|---|
| claude --bg "<prompt>" | Spawns a sibling session and returns immediately. Takes --model, --permission-mode, --effort. Already allowlisted via the scaffold /spawn command. |
| claude agents --json [--all] [--cwd <path>] | Scriptable session list (no TTY needed): active lanes, or with --all the full history. --cwd scopes to sessions started under the workspace. |
| ~/.claude/jobs/<id>/state.json | Per-session state: state, detail, tempo, output.result, children (PR/issue links auto-scanned from the transcript), the original intent briefing, resumeSessionId, timestamps, token count. |
| working needs input: result: failed: | The background-session status protocol. A classifier reads each session's message text and keeps state.json current — lanes that follow the protocol are trackable for free. |
| bg worktree isolation | Background sessions must isolate into a git worktree before editing — enforced by the harness, and aligned with the AGENTS.md worktree convention. Lanes can't collide with each other or with Jeremy's checkout. |
| diary/ conventions | obot.roadmap's diary README already specifies per-working-session cadence (design decision D2): write the day's entry at session end, append a session section if the file exists. |
| scaffold /spawn briefing | The context-briefing pattern (<300 words: paths, decisions, constraints, what's ruled out). The lane briefing below is this, formalized with a status-protocol and conventions section. |
03 · Lifecycle
Build the picture before proposing anything: open requirements and sub-issue status from the hub (gh + Project status), the latest diary entry, and — critically — sessions already in flight or recently finished:
claude agents --json --all --cwd ~/Documents/obot2
Any stray result: from a previous day that never made a diary entry gets picked up here (see §06).
Propose 1–2 lanes as a multiple-choice prompt per the Decision Prompt convention. A lane is fully specified before spawn: repo · issue · done-condition · model/effort/permission-mode. Cap at two — siblings are full sessions, and the cap keeps cost and Jeremy's review load sane.
Compose a lane briefing (contract in §04) and launch each sibling from the lane repo's directory, recording the job id → lane mapping in a session ledger:
cd ~/Documents/obot2/<repo> && \
claude --bg --model sonnet --permission-mode auto \
"<lane briefing>\n\n---\n\nTASK: <issue + done-condition>"
Default mode is on-demand: when Jeremy asks (or before any decision), render a status board from claude agents --json plus each lane's state.json — state chip, one-line detail, and any children PRs. When the session is just waiting for lanes to land, start a background watcher so the orchestrator is woken instead of polling:
# run_in_background Bash — exits (and wakes the orchestrator) on any state change
until [ "$(jq -r .state ~/.claude/jobs/<id>/state.json)" != "working" ]; do sleep 30; done
On needs input:, surface the lane's question to Jeremy as a multiple-choice prompt, then relay the answer by resuming the lane in place:
claude --bg --resume <resumeSessionId> "<answer>" # spike first — see §07
For each lane, collect output.result and children links from state.json, fold in the orchestrator's own hub work, and write diary/YYYY-MM-DD.md in the existing format (meta line, work completed, PRs/issues touched, blockers, 🙋 ToDo). Lanes still working at wrap-up are listed as in-flight; tomorrow's entry closes them out. Commit direct to main under the standing autonomy grant, let Pages deploy, and hand Jeremy the deployed URL.
04 · Contract
The scaffold /spawn briefing, formalized. Three sections, in this order — the third is what makes lanes trackable, and it's the part the current pattern leaves implicit:
CONTEXT — ~300 words: repo + paths, the spec source (issue link), decisions
already made (do not re-litigate), constraints, what's been ruled out.
TASK — the issue to implement + an explicit done-condition
(usually: draft PR open with `Closes #N`, checks green, never merge).
PROTOCOL — work in a worktree (../<repo>-worktrees/<branch>); follow
.github/AGENTS.md conventions + attribution-at-bottom; emit
`result:` / `needs input:` / `failed:` faithfully — your state.json
is read by the roadmap session and becomes the diary entry.
This lives as a template in the roadmap-session skill (references/lane-briefing.md) so every spawn — human-initiated or, later, autonomous — produces sessions the tracker can read.
05 · Landing zones
skills/roadmap-session/SKILL.md — the §03 procedure, with references/lane-briefing.md and references/status-protocol.md.skills/diary-entry/SKILL.md — the wrap-up procedure, separate so the future nightly-diary lane (#18, lane 2) reuses it unchanged..claude/skills/ (existing workspace pattern).Bash(claude --bg:*) and Bash(claude agents:*) in workspace settings so spawn/track don't prompt.roadmap-session skill.state.json is the runtime source of truth; the diary is the durable record.06 · Fit
Every #18 candidate lane maps onto a piece built here: the background dev lane is exactly a lane briefing minus the human at the top; the nightly diary is the diary-entry skill on a schedule; the rollup reuses the same status-reading. Running lanes attended first surfaces the failure modes (briefing gaps, protocol drift, needs-input handling) while a human is watching — which is the cheap time to find them.
Restartability falls out of the architecture: because status lives in state.json, not in the orchestrator's context, the roadmap session can end while lanes run. The next roadmap session's Situate phase (--all) finds finished lanes and folds them into that day's diary. No lane is orphaned by a closed laptop.
07 · Decisions
D1Track as a new hub Requirement, or fold into #18's design?
Recommend: new Requirement. #18 is explicitly unattended operation gated on the App identity (#3); this is attended, needs no new identity, and can start now. Frame it as depending on #17 (overlay home) and feeding #18 (lane graduation).
D2Who answers a lane's needs input:?
Recommend: the orchestrator relays — question surfaces in the roadmap session as a multiple-choice prompt, answer goes back via --resume. Jeremy answering directly in the agents view stays as the escape hatch. Keeps one conversation as the control surface.
D3Standing watcher or on-demand status?
Recommend: on-demand by default, background watcher only when the session is idle-waiting for lanes to land (wrap-up). A standing 30s poll adds noise to a session Jeremy is actively using.
D4One skill or two?
Recommend: two. roadmap-session (orchestrate) + diary-entry (wrap-up), because the diary procedure is reused verbatim by #18's nightly lane and by any substantive non-orchestrated session.
08 · Plan
claude --bg --resume, and the watcher-wakes-orchestrator loop. Everything else is verified; these two aren't. Do this before writing skills around them.diary-entry in safety.agent S — smallest piece, immediately useful in every session, no dependency on the spike.roadmap-session + lane-briefing template + status-protocol reference M — incorporating spike findings.09 · Caveats
state.json is a harness-internal surface, not a published API. The skill should pin the fields it reads and re-verify on CLI upgrades — cheap insurance against a rename.--resume relay is the least-proven step (hence spike first). Fallback if it misbehaves: the orchestrator reports the question and Jeremy answers in the agents view directly.main: the autonomy grant covers it, but the permission classifier still auto-denies main pushes unless Jeremy directs one in-session — the skill should end wrap-up with his explicit go, until the settings opt-out question is resolved.