#+TITLE: Helper Mode Workflow #+AUTHOR: Craig Jennings & Claude #+DATE: 2026-06-15 * Overview The role contract for a *helper instance*: a second Claude session running in the same project as a live primary session, spawned to look something up or make a scoped task update while the primary keeps working. This file is the single canonical home of the helper rules. [[file:../protocols.org][protocols.org]] carries a one-paragraph pointer here; the spawn paths and the wrap-up reference this file rather than restating it. A helper is not a subagent. When the work fits a dispatched subagent (the Agent tool) — a bounded lookup or analysis the primary folds back into its own context — use that instead; no second session exists and none of this applies. A helper is for interactive, long-lived parallel work Craig drives himself in a second terminal. The governing fact behind every rule below: the session-context split isolates each agent's /session state/ (=.ai/session-context.d/.org=), but everything else in the project — =todo.org=, =.ai/notes.org=, =inbox/=, docs, the git index — is shared mutable state. Two Edit-tool writers on one org file lose updates silently: both read, both write, last write wins. The helper's job is to stay useful without ever being the second writer that clobbers the primary. * When to Use This Workflow No operator trigger phrase. A helper reaches this contract one of three ways: - The =ai --helper= launcher routes here after the roster confirms a live agent (the deterministic path). - Startup's roster check finds the session is not alone and routes here instead of running normal startup (the safety net for a raw =claude= launch). - An explicit "you are a helper, follow helper-mode.org" instruction (the manual fallback). If none of those applies — the roster shows the session is alone — this is a primary session. Run normal [[file:startup.org][startup.org]], not this. * Identity A helper is =helper-= (four random hex/alphanumeric characters, e.g. =helper-a83f=). - If the launcher exported =AI_AGENT_ID=, use it. Otherwise self-assign =helper-= now. - Record the chosen id as the *first line* of the session-context file, so it survives across tool calls (shell state does not). The id lives in the file, never in ambient shell state. - The active session-context path is =.ai/session-context.d/.org=. Resolve it by prefixing the id explicitly wherever a script consumes =AI_AGENT_ID=: #+begin_src bash AI_AGENT_ID= .ai/scripts/session-context-path #+end_src The id must be unique per run; =helper-= and the launcher's epoch-tailed id both satisfy that (see protocols.org "Agent-scoped path"). * Read/Write Contract Four tiers, by how much coordination the write needs. ** Always safe Any read, anywhere in the project. Writes to the helper's own =.ai/session-context.d/.org=. ** Safe by discipline — scoped task updates (the case Craig named) Scoped writes to shared org files (=todo.org=, =.ai/notes.org=) are allowed under four rules, together: 1. Re-read the file region immediately before each edit. 2. Anchor the edit on a unique heading. 3. Scope each edit to that single heading's subtree. 4. Never reflow, restructure, or sweep the file. Appending a new =**= task at a section's end and editing one existing task's body or state both qualify. The race window collapses to seconds, and a collision corrupts one heading rather than the whole file. Log the intended edit first (see Write-ahead journal below). ** Primary-only — never as a helper - File-wide passes: =todo-cleanup.el=, =lint-org.el=, =wrap-org-table.el=, archive sweeps. - Inbox processing and template sync. - ALL git mutation: commit, push, pull, stash. Two committers in one worktree contend on the index lock and interleave staging. - Startup's Phase A.0 pulls and the =.ai/= rsync. The primary already did them, and a concurrent pull-under-edit is exactly the race the startup guards exist to prevent. - Memory writes. =MEMORY.md= is a shared read-modify-write index with no heading anchors, so it has the lost-update shape of =todo.org= with none of the scoped-edit protection. The git ban is concurrency-scoped. /Helper wrap-up/ below lifts it for exactly one case: an orphaned helper that finds itself alone. ** Escalation Anything the contract blocks gets reported to Craig, or — for a cross-project handoff — routed through =inbox-send= to the owning project's =inbox/=. The helper leaves its tree changes for the primary's next commit, or describes them in a note to Craig. * Data-Integrity Rules The scoped-edit discipline covers helper-vs-primary edits on /different/ headings. Four loss windows remain. They matter doubly in a consolidated project where one =todo.org= carries every task and corruption has maximal blast radius. 1. *Primary file-wide passes vs a live helper.* A whole-file rewrite run while a helper is mid-edit clobbers the helper's just-written change. Enforced primary-side (the live-helper gate before any hygiene pass), but the helper's part is to keep its own session-context file current so the primary's gate can see it is live. 2. *A new primary starting while a helper runs.* The helper's uncommitted edits make the tree dirty; Phase A.0 already skips pulls on a dirty tree, and startup surfaces live =session-context.d/= files so the new primary knows /why/ the tree is dirty rather than treating it as mess to resolve. 3. *Write-ahead edit journal.* Before applying any shared-file edit, log it — file, heading, one-line intent — to the helper's own session-context file. This tightens the Session Log discipline to log-before-write for shared files specifically, so after a crash the journal shows which edits landed. 4. *The memory dir.* Helpers don't write memory at all. Candidate memories go into the helper's session log; the primary (or its wrap-up promotion check) writes them. One collision nit: =inbox-send= filenames carry minute-resolution timestamps, so a helper and primary sending to the same target in the same minute with the same slug would collide. Helper-originated sends include the agent id in the slug. * Light Startup A helper does not run normal startup. It runs a light version: 1. Self-assign or adopt the identity (above) and create =.ai/session-context.d/.org= with the id on the first line. 2. Read [[file:../protocols.org][protocols.org]] and this file. Read =.ai/notes.org= for project context if useful. 3. Do NOT run Phase A.0 pulls, =make install=, or the =.ai/= rsync — the primary owns those. 4. Do NOT process the inbox — primary-only. 5. Begin the work Craig spawned the helper for. * Helper Wrap-Up When the helper's work is done: 1. Re-run the roster (=.ai/scripts/agent-roster=) to learn whether a primary is still live. 2. *Primary still live (the normal case):* finalize the Summary in the helper's own =.ai/session-context.d/.org=, archive it to =.ai/sessions/YYYY-MM-DD-HH-MM--.org=, and stop. Do NOT commit, push, or run hygiene — the primary's next commit picks up the archived file and any scoped edits the helper left in the tree. 3. *Orphaned helper (roster shows the helper is now alone):* the primary already exited, so the helper assumes full closing duties — the git ban lifts because the concurrency that justified it is gone. Commit and push the tree (including the helper's own edits, which would otherwise strand as a dirty tree), per the normal wrap-up flow in [[file:wrap-it-up.org][wrap-it-up.org]]. * Status Phase 1.5 of the generic-agent-runtime spec. This contract is the canonical home; the spawn paths (=ai --helper=, startup's roster branch) and the [[file:wrap-it-up.org][wrap-it-up.org]] helper branch route here. Those wiring pieces ship behind the spec's bats-then-drills-then-pilot gate and are not yet live; until then, the manual "you are a helper" instruction is how a session adopts this contract.