diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-15 08:25:37 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-15 08:25:37 -0500 |
| commit | 0b681dca75299a81549c78681850f353e6a64a87 (patch) | |
| tree | 1bbad672fb1dc1be370096eb2126db16240bca66 | |
| parent | f8bdf302fd71ba73ae1424d86275e1e223f09cf3 (diff) | |
| download | rulesets-0b681dca75299a81549c78681850f353e6a64a87.tar.gz rulesets-0b681dca75299a81549c78681850f353e6a64a87.zip | |
feat(ai): add helper-mode workflow contract
helper-mode.org is the canonical home of the helper-instance rules: a second Claude alongside a live primary in the same project. It defines the four read/write tiers (always-safe reads and own-context writes, safe-by-discipline scoped single-heading edits, primary-only file-wide passes and all git mutation, escalation), the four data-integrity windows, the light startup, and the helper wrap-up (archive own context file, skip commit, with the git ban lifting only for an orphaned helper that ends up alone).
protocols.org gets a one-paragraph pointer, and INDEX.org gets a triggerless catalog entry like startup.org, so the no-trigger workflow clears the integrity check without a special case.
The contract is the canonical home. The routing that sends a session here (ai --helper, startup's roster branch, the wrap-up helper branch) ships behind the feature's drill gate and isn't live yet. Until then a session adopts it by an explicit "you are a helper" instruction.
| -rw-r--r-- | .ai/protocols.org | 2 | ||||
| -rw-r--r-- | .ai/workflows/INDEX.org | 1 | ||||
| -rw-r--r-- | .ai/workflows/helper-mode.org | 101 | ||||
| -rw-r--r-- | claude-templates/.ai/protocols.org | 2 | ||||
| -rw-r--r-- | claude-templates/.ai/workflows/INDEX.org | 1 | ||||
| -rw-r--r-- | claude-templates/.ai/workflows/helper-mode.org | 101 |
6 files changed, 208 insertions, 0 deletions
diff --git a/.ai/protocols.org b/.ai/protocols.org index caa5303..05f889b 100644 --- a/.ai/protocols.org +++ b/.ai/protocols.org @@ -106,6 +106,8 @@ The epoch is baked into the id by the spawner, never minted inside =session-cont Resolve the path with =.ai/scripts/session-context-path= rather than hardcoding =.ai/session-context.org=; it prints the right path for the current =AI_AGENT_ID=. Fall back to =.ai/session-context.org= if the script isn't present (older checkouts mid-sync). Everything below — the record/recovery purpose, the update triggers, the startup existence check, the wrap-up rename — operates on that resolved path. The prose says "session-context.org" as the default name; read it as "the resolved active path" when =AI_AGENT_ID= is set. +A helper instance (a second agent running in this project while a primary session is live) follows a different contract: it skips the pulls and rsync, makes only scoped single-heading edits to shared files, leaves all git mutation to the primary, and wraps up by archiving its own context file without committing. The full rules — read/write tiers, data-integrity, light startup, helper wrap-up — live in [[file:workflows/helper-mode.org][workflows/helper-mode.org]]. A session is a helper only when something routes it there (the =ai --helper= launcher, startup's roster check, or an explicit "you are a helper" instruction); the routing itself ships behind the helper-instance feature gate and isn't live yet. + This file serves two purposes with one mechanism: 1. *Crash recovery* — if the session dies mid-work, the live file is all that's left. On 2026-01-22 a session crashed during a 20-minute design discussion and all context was lost because this file wasn't being updated. 2. *Session archive* — at wrap-up the file is renamed into =.ai/sessions/=, becoming the permanent record. No transcription to notes.org; the file IS the record. diff --git a/.ai/workflows/INDEX.org b/.ai/workflows/INDEX.org index 17963ef..95fe37d 100644 --- a/.ai/workflows/INDEX.org +++ b/.ai/workflows/INDEX.org @@ -15,6 +15,7 @@ This index must list every =.org= file in =.ai/workflows/= except this one and e ** Session lifecycle - =startup.org= — runs automatically at session start. No manual trigger. +- =helper-mode.org= — role contract for a helper instance (a second Claude in the same project as a live primary). No manual trigger; the spawn paths route to it, "you are a helper" is the manual fallback. - =first-session.org= — initialize =.ai/= for a brand-new project. - Triggers: "this is a new project", "let's set this project up". Auto-runs if =.ai/sessions/= is empty. - =wrap-it-up.org= — end-of-session: write summary, archive, commit, push. diff --git a/.ai/workflows/helper-mode.org b/.ai/workflows/helper-mode.org new file mode 100644 index 0000000..8ead37b --- /dev/null +++ b/.ai/workflows/helper-mode.org @@ -0,0 +1,101 @@ +#+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/<id>.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-<rand4>= (four random hex/alphanumeric characters, e.g. =helper-a83f=). + +- If the launcher exported =AI_AGENT_ID=, use it. Otherwise self-assign =helper-<rand4>= 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/<id>.org=. Resolve it by prefixing the id explicitly wherever a script consumes =AI_AGENT_ID=: + + #+begin_src bash + AI_AGENT_ID=<id> .ai/scripts/session-context-path + #+end_src + + The id must be unique per run; =helper-<rand4>= 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/<id>.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 routes through the cross-agent message form (=machine.project.agent-id=), or just gets reported to Craig. The helper leaves its tree changes for the primary's next commit, or describes them in a targeted message. + +* 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/<id>.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/<id>.org=, archive it to =.ai/sessions/YYYY-MM-DD-HH-MM-<id>-<description>.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. diff --git a/claude-templates/.ai/protocols.org b/claude-templates/.ai/protocols.org index caa5303..05f889b 100644 --- a/claude-templates/.ai/protocols.org +++ b/claude-templates/.ai/protocols.org @@ -106,6 +106,8 @@ The epoch is baked into the id by the spawner, never minted inside =session-cont Resolve the path with =.ai/scripts/session-context-path= rather than hardcoding =.ai/session-context.org=; it prints the right path for the current =AI_AGENT_ID=. Fall back to =.ai/session-context.org= if the script isn't present (older checkouts mid-sync). Everything below — the record/recovery purpose, the update triggers, the startup existence check, the wrap-up rename — operates on that resolved path. The prose says "session-context.org" as the default name; read it as "the resolved active path" when =AI_AGENT_ID= is set. +A helper instance (a second agent running in this project while a primary session is live) follows a different contract: it skips the pulls and rsync, makes only scoped single-heading edits to shared files, leaves all git mutation to the primary, and wraps up by archiving its own context file without committing. The full rules — read/write tiers, data-integrity, light startup, helper wrap-up — live in [[file:workflows/helper-mode.org][workflows/helper-mode.org]]. A session is a helper only when something routes it there (the =ai --helper= launcher, startup's roster check, or an explicit "you are a helper" instruction); the routing itself ships behind the helper-instance feature gate and isn't live yet. + This file serves two purposes with one mechanism: 1. *Crash recovery* — if the session dies mid-work, the live file is all that's left. On 2026-01-22 a session crashed during a 20-minute design discussion and all context was lost because this file wasn't being updated. 2. *Session archive* — at wrap-up the file is renamed into =.ai/sessions/=, becoming the permanent record. No transcription to notes.org; the file IS the record. diff --git a/claude-templates/.ai/workflows/INDEX.org b/claude-templates/.ai/workflows/INDEX.org index 17963ef..95fe37d 100644 --- a/claude-templates/.ai/workflows/INDEX.org +++ b/claude-templates/.ai/workflows/INDEX.org @@ -15,6 +15,7 @@ This index must list every =.org= file in =.ai/workflows/= except this one and e ** Session lifecycle - =startup.org= — runs automatically at session start. No manual trigger. +- =helper-mode.org= — role contract for a helper instance (a second Claude in the same project as a live primary). No manual trigger; the spawn paths route to it, "you are a helper" is the manual fallback. - =first-session.org= — initialize =.ai/= for a brand-new project. - Triggers: "this is a new project", "let's set this project up". Auto-runs if =.ai/sessions/= is empty. - =wrap-it-up.org= — end-of-session: write summary, archive, commit, push. diff --git a/claude-templates/.ai/workflows/helper-mode.org b/claude-templates/.ai/workflows/helper-mode.org new file mode 100644 index 0000000..8ead37b --- /dev/null +++ b/claude-templates/.ai/workflows/helper-mode.org @@ -0,0 +1,101 @@ +#+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/<id>.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-<rand4>= (four random hex/alphanumeric characters, e.g. =helper-a83f=). + +- If the launcher exported =AI_AGENT_ID=, use it. Otherwise self-assign =helper-<rand4>= 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/<id>.org=. Resolve it by prefixing the id explicitly wherever a script consumes =AI_AGENT_ID=: + + #+begin_src bash + AI_AGENT_ID=<id> .ai/scripts/session-context-path + #+end_src + + The id must be unique per run; =helper-<rand4>= 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/<id>.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 routes through the cross-agent message form (=machine.project.agent-id=), or just gets reported to Craig. The helper leaves its tree changes for the primary's next commit, or describes them in a targeted message. + +* 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/<id>.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/<id>.org=, archive it to =.ai/sessions/YYYY-MM-DD-HH-MM-<id>-<description>.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. |
