|
| 1 | +--- |
| 2 | +id: DE-134 |
| 3 | +slug: subagent_worktree_base_ref_alignment_and_compensation_defences |
| 4 | +name: Delta - Subagent worktree base-ref alignment and compensation defences |
| 5 | +created: "2026-04-26" |
| 6 | +updated: "2026-04-26" |
| 7 | +status: draft |
| 8 | +kind: delta |
| 9 | +aliases: [] |
| 10 | +relations: |
| 11 | + - type: relates_to |
| 12 | + target: DE-132 |
| 13 | + - type: relates_to |
| 14 | + target: DE-133 |
| 15 | +context_inputs: |
| 16 | + - type: brief |
| 17 | + ref: ./brief.md |
| 18 | + summary: "Source brief — three-layer defence for subagent worktree isolation (stale-fork + silent-compensation)." |
| 19 | + - type: reference |
| 20 | + ref: supekku/agents/dispatch-worker.md |
| 21 | + summary: "Existing managed subagent that uses isolation: worktree." |
| 22 | + - type: reference |
| 23 | + ref: supekku/claude.settings.json |
| 24 | + summary: "Hook registration template installed to .claude/settings.json." |
| 25 | + - type: reference |
| 26 | + ref: supekku/claude.hooks/ |
| 27 | + summary: "Source for installable hook scripts (currently startup.sh, artifact_event.py)." |
| 28 | +applies_to: |
| 29 | + specs: [] |
| 30 | + requirements: [] |
| 31 | +--- |
| 32 | + |
| 33 | +# DE-134 – Subagent worktree base-ref alignment and compensation defences |
| 34 | + |
| 35 | +```yaml supekku:delta.relationships@v1 |
| 36 | +schema: supekku.delta.relationships |
| 37 | +version: 1 |
| 38 | +delta: DE-134 |
| 39 | +revision_links: |
| 40 | + introduces: [] |
| 41 | + supersedes: [] |
| 42 | +specs: |
| 43 | + primary: [] |
| 44 | + collaborators: [] |
| 45 | +requirements: |
| 46 | + implements: [] |
| 47 | + updates: [] |
| 48 | + verifies: [] |
| 49 | +phases: [] |
| 50 | +``` |
| 51 | +
|
| 52 | +## 1. Summary & Context |
| 53 | +
|
| 54 | +- **Source brief**: [brief.md](./brief.md) — three-layer defence for subagent worktree isolation. |
| 55 | +- **Implementation Plan**: [IP-134](./IP-134.md) |
| 56 | +- **Design Revision**: [DR-134](./DR-134.md) |
| 57 | +- **Change Drivers**: Empirical incident in a sibling spec-driver project where a `isolation: worktree` subagent forked from `origin/main` (38 commits behind the supervisor's local `main`), then silently compensated by re-staging files from elsewhere. Result: a branch unmergeable against trunk and only diagnosable by inspecting the diff base. |
| 58 | +- **Related deltas**: DE-132 (sub-agent orchestration / `/dispatch`), DE-133 (installer support for `.claude/agents/`). This delta extends the same surface — managed subagent definitions, installer-managed Claude config, and harness hook scripts. |
| 59 | + |
| 60 | +## 2. Motivation |
| 61 | + |
| 62 | +Two coupled failure modes in worktree-isolated subagents: |
| 63 | + |
| 64 | +1. **Stale fork**: Claude Code's `isolation: worktree` does not reliably fork from the supervisor's HEAD. Empirically observed forking from a tracking ref (e.g. `origin/main`) instead. The subagent then operates in a tree missing all in-flight supervisor work. |
| 65 | +2. **Silent compensation**: When the worktree state contradicts the delegation prompt (missing files, missing commits), subagents have been observed reconstructing state by copying files from outside the worktree. The resulting branch looks plausible in isolation but is unmergeable against trunk because its diff baseline is wrong. |
| 66 | + |
| 67 | +Loud failure is recoverable; silent compensation is not. The defence must be three-layer (pre-spawn alignment, in-prompt refusal, handback verification) because no single layer covers all paths. |
| 68 | + |
| 69 | +## 3. Scope & Objectives |
| 70 | + |
| 71 | +- **Primary Outcomes**: |
| 72 | + - **Layer 1 — pre-spawn alignment**: a `SubagentStart` hook script that aligns a worktree-isolated subagent's working tree to the supervisor's HEAD (or a documented project-level override). |
| 73 | + - **Layer 2 — compensation refusal**: a shared subagent prompt fragment that instructs worktree-isolated subagents to stop and report rather than reconstruct missing state. Inherited by `dispatch-worker` (and any future managed subagent declaring `isolation: worktree`) without per-file edits. |
| 74 | + - **Layer 3 — handback verification**: a `SubagentStop` hook script that runs a merge-base sanity check against the captured base ref, scans for the compensation signature (added files identical to base-ref state), and writes incidents to a project-local log. |
| 75 | + - **Installer support**: `spec-driver install` sources both new hook scripts from `supekku/claude.hooks/` and registers them in the installed `.claude/settings.json`. Per-agent log/state directory is created (or auto-created on first run). |
| 76 | + - **Documentation**: `supekku/claude.hooks/README.md` (or equivalent) documents the parent-HEAD-vs-trunk policy choice and the project-level + per-subagent override mechanisms. |
| 77 | +- **Operational Constraints**: |
| 78 | + - Source-of-truth changes belong in `supekku/`. The `.spec-driver/` installation is regenerated by `spec-driver install` and must not be hand-edited. |
| 79 | + - Hooks must no-op cleanly for non-isolated subagents; existing dispatch flow must remain functional. |
| 80 | + - No persistent per-agent state files after subagent completion. |
| 81 | +- **Dependencies**: |
| 82 | + - DE-133 lands installer-managed `.claude/agents/` sync. DE-134 extends installer-managed scope to `.claude/hooks/` entries that are *added* by this delta and to the `SubagentStart` / `SubagentStop` keys in the settings template. |
| 83 | + |
| 84 | +## 4. Out of Scope |
| 85 | + |
| 86 | +- Path enforcement during subagent execution (separate brief if needed). |
| 87 | +- Read sandboxing. |
| 88 | +- Replacing Claude Code's worktree creation entirely. |
| 89 | +- Process or network isolation. |
| 90 | +- Automatic resolution of stale-fork incidents — the supervisor diagnoses, the hook only surfaces. |
| 91 | +- Generic hook framework refactor — extend the existing `supekku/claude.hooks/` pattern rather than redesigning it. |
| 92 | + |
| 93 | +## 5. Approach Overview |
| 94 | + |
| 95 | +- **System Touchpoints**: |
| 96 | + - `supekku/claude.hooks/` — two new scripts (working names `align-worktree-to-parent.sh`, `verify-worktree-base.sh`). |
| 97 | + - `supekku/claude.settings.json` — register `SubagentStart` and `SubagentStop` hook entries. |
| 98 | + - `supekku/agents/dispatch-worker.md` — adopt the shared compensation-refusal directive. |
| 99 | + - `supekku/templates/agents/` (or a new shared snippet location) — single-source compensation-refusal directive that managed subagents reference. |
| 100 | + - `supekku/scripts/` — installer changes if `_install_claude_config` / `_install_agents` need to learn about new hook files or new state-dir conventions. |
| 101 | +- **Key Changes**: |
| 102 | + 1. Author both hook scripts under `supekku/claude.hooks/`. Resolve hook input shape (parent CWD, agent_id, worktree path) by capturing real `SubagentStart` / `SubagentStop` invocations — see DR open question 1. |
| 103 | + 2. Extend `supekku/claude.settings.json` with the two new hook entries. Confirm the settings installer copies/merges this file correctly into per-project `.claude/settings.json`. |
| 104 | + 3. Add the compensation-refusal directive as a single-source fragment, included by `dispatch-worker.md` (and any future worktree-isolated managed subagent) without copy-paste. |
| 105 | + 4. Document parent-HEAD-vs-trunk policy and the override surface (`.claude/agent-base-ref` project-level, per-subagent frontmatter opt-out) in a README under `supekku/claude.hooks/`. |
| 106 | + 5. Confirm installer behaviour for the new hook script files and the runtime state directory (`.claude/state/agent-base-ref/`, `.claude/state/worktree-incidents.log`). |
| 107 | +- **Migration / Rollout Notes**: Existing installs pick up the new hooks on next `spec-driver install`. No data migration required. New state directory is created on first subagent spawn. |
| 108 | + |
| 109 | +## 6. Verification Strategy |
| 110 | + |
| 111 | +- **VT**: |
| 112 | + - Unit-style coverage for the hook scripts via shellcheck plus a small harness that feeds synthetic hook input JSON and asserts side-effects (worktree HEAD match, exit code, state file presence/absence). |
| 113 | + - Installer test confirming new hook files land in the installed workspace and `.claude/settings.json` registers the hook entries. |
| 114 | +- **VA**: |
| 115 | + - Controlled `/dispatch` run where the supervisor is several commits ahead of the tracking ref; confirm subagent worktree HEAD matches supervisor and `worktree-incidents.log` is empty. |
| 116 | + - Adversarial run where a delegation prompt mentions a file the subagent is not given; confirm the subagent reports rather than fabricates, and the SubagentStop hook flags the run if compensation occurs. |
| 117 | +- **VH**: User attestation that the integration into a real delta pass works end-to-end without disrupting `/dispatch` ergonomics. |
| 118 | +- **Acceptance Criteria** (from brief): |
| 119 | + - Worktree-isolated subagent starts at supervisor's HEAD-at-delegation regardless of Claude Code's default resolution. |
| 120 | + - Subagent whose worktree state contradicts its delegation prompt stops and reports rather than compensating. |
| 121 | + - Stale or different-ancestry merge-base at handback is surfaced before the supervisor accepts the result. |
| 122 | + - Compensation signature (added files identical to base-ref state) is detected and flagged. |
| 123 | + - Subagents without `isolation: worktree` are unaffected — hooks no-op cleanly. |
| 124 | + - Project-level base-ref override via `.claude/agent-base-ref`. |
| 125 | + - Per-subagent opt-out via documented frontmatter flag. |
| 126 | + - No per-agent state files persist after completion. |
| 127 | + - Incidents accumulate in `.claude/state/worktree-incidents.log` for pattern review. |
| 128 | + - Subagent template/generator update is single-source; existing definitions inherit without per-file edits. |
| 129 | + - `supekku/claude.hooks/README.md` documents the design and override surface. |
| 130 | + |
| 131 | +## 7. Risks & Mitigations |
| 132 | + |
| 133 | +- **Risk**: Hook input shape (especially how to retrieve parent session CWD from `SubagentStart`) is undocumented and may vary across Claude Code releases. – _Likelihood_: medium – _Impact_: medium – _Mitigation_: Capture real invocations during DR work; fall back to deriving parent CWD from `.git/worktrees/{name}/gitdir` or `git worktree list --porcelain` run from inside the worktree. |
| 134 | +- **Risk**: `git reset --hard` in a worktree could destroy uncommitted state if Claude Code has already populated the worktree with WIP. – _Likelihood_: low – _Impact_: high – _Mitigation_: Hook checks for clean tree before reset; aborts loudly otherwise. Per-subagent opt-out covers any legitimate WIP-carry case. |
| 135 | +- **Risk**: Compensation-signature scan produces false positives when a subagent legitimately re-introduces a file deleted on a sibling branch. – _Likelihood_: medium – _Impact_: low – _Mitigation_: Default to warn-only output; surface signal without blocking. Tunable threshold deferred to later if needed. |
| 136 | +- **Risk**: Installer-managed settings merge clobbers user-customised hook entries. – _Likelihood_: medium – _Impact_: medium – _Mitigation_: Verify installer's existing settings-handling semantics; document install-time behaviour in DR. |
| 137 | +- **Risk**: Single-source directive mechanism does not yet exist for subagent prompts; introducing one risks scope creep into a generic templating concern. – _Likelihood_: medium – _Impact_: low – _Mitigation_: Choose the simplest viable mechanism (e.g. a referenced skill or an installer-time include) and resist building a generic engine. |
| 138 | + |
| 139 | +## 8. Follow-ups & Tracking |
| 140 | + |
| 141 | +- **Future Phases / Deltas**: |
| 142 | + - Path-enforcement layer for subagent execution (separate brief). |
| 143 | + - Consider extending merge-base verification to all worker-produced branches, not only worktree-isolated ones. |
| 144 | +- **Backlog Items**: To be created if scope splits during DR refinement. |
| 145 | +- **Open Decisions / Questions** (carried into DR-134): |
| 146 | + 1. Confirm exact JSON shape of `SubagentStart` / `SubagentStop` hook inputs and the reliable way to retrieve the parent session's working directory. |
| 147 | + 2. Confirm Claude Code's actual base-ref resolution rule for `isolation: worktree`. |
| 148 | + 3. Decide whether stale-fork warnings hard-block via SubagentStop exit code, or warn-only. Default: warn-only. |
| 149 | + 4. Decide precedence between `.claude/agent-base-ref` (project) and per-subagent frontmatter override. Default: per-subagent wins, project config is the framework default. |
| 150 | + |
| 151 | +## 9. Implementation Notes |
| 152 | + |
| 153 | +- All source-of-truth edits land in `supekku/`. The `.spec-driver/` installed copy is regenerated via `spec-driver install` and must not be edited directly. |
| 154 | +- Manual end-to-end verification requires a `/dispatch` invocation against a deliberately-stale tracking ref to exercise Layer 1 + Layer 3 in concert. |
0 commit comments