fix(#4683): detect cross-plan threat-ID duplicates before execution (#4828)

admin_reason: missing-secondary-reviewer — self-authored overnight sweep; two orthogonal agent reviews ran (isolated adversarial REQUEST-CHANGES with all six findings dispositioned, plus a bypass/consumer-lens APPROVE) and the sha-pinned bench passed 46362/0 on the merged head.
This commit is contained in:
Tom Boucher
2026-09-17 19:06:55 -04:00
committed by GitHub
parent bff99a8bb5
commit c9a5cc3e12
33 changed files with 519 additions and 14 deletions

View File

@@ -0,0 +1,28 @@
Apply response_language to all user-facing prose — narration between tool calls, status updates, progress notes, and findings included; preserve code, paths, and identifiers.
<step name="threat_id_gate">
Cross-plan threat-ID duplicates — a `T-{phase}-NN` ID claimed by more than one live PLAN file in
this phase (#4683). The reserved `T-{phase}-SC` supply-chain row is never listed: every plan keeps
it by design.
This is a hard stop BEFORE any dispatch — do not spawn executors, do not update state, do not
write artifacts. A reused ID names two different threats, so `SECURITY.md` rows and
`VALIDATION.md`'s Threat Ref column are ambiguous until it is fixed; executing the phase would
silently mark the wrong threat.
Report the full `threat_id_duplicates` list verbatim — each ID with its claiming plan files:
```
### GSD ► THREAT ID DUPLICATES — EXECUTION BLOCKED (#4683)
{for each {id, plans}: "{id} — claimed by {plans.join(', ')}"}
Threat IDs must be unique within a phase. Renumber the newer plans' registers to continue
after the phase's highest in-use `T-{phase}-NN` (see @~/.claude/gsd-core/references/planner-gap-closure.md §9),
then re-run /gsd:execute-phase {phase}.
```
Do not offer to execute anyway. The only forward paths are renumbering the colliding registers
or (if a plan was superseded after the check) re-running init so the stale plan drops out of the
scan — superseded plans never hold IDs against their replacements (#2349).
</step>

View File

@@ -0,0 +1,16 @@
Apply response_language to all user-facing prose — narration between tool calls, status updates, progress notes, and findings included; preserve code, paths, and identifiers.
<step name="worktree_base_check">
`USE_WORKTREES` and `ISOLATION` are also reset for the run when `worktree base-check` detects the
orchestrator HEAD has diverged from the worktree fork base (#683 — e.g. an unmerged milestone
branch). This runs for **any** isolated run, not only Claude: fork-base divergence is a property
of the repository, so it degrades a GSD-created worktree exactly as a harness-created one. The
auto-degrade prints a one-line warning to stderr and falls through to the sequential path so
executors do not hit the exit-42 worktree-branch-check halt. Setting `worktree.baseRef:"head"`
restores parallel execution only where GSD itself creates the worktrees (orchestrator-managed
runtimes — Codex, OpenCode, Kimi, Kimi Code); harness-isolated runtimes (Claude Code, Cursor) do
not read the setting (#48, verified 5/5; upstream claude-code#44965), so there the check compares
against the real fork base and parallel execution returns once HEAD is merged/pushed so
`origin/HEAD` matches it (#3659). The `worktree-branch-check` exit-42 guard inside each executor
remains in place as a backstop.
</step>