53407a77cd
Closes the TDD-gate cross-actor gap: when a subagent (spawned by a Task in the controller's current turn) writes the failing test and confirms RED, the controller's subsequent production edit was falsely blocked because the gate only scanned the controller's own turn. Net strengthening, no discipline weakened. - Part 1 (enforce-runtime-write-deny): block the Write tool from any ~/.claude/projects/**/*.jsonl (session/subagent transcripts). Memory *.md there stays writable (never matches .jsonl$). Resolving normalizer defeats ./.. evasion. This makes the agent-<id>.jsonl that Part 2 trusts unforgeable (it was the last ungated write channel; Bash/PowerShell/Read gates already covered it). - Part 2 (enforce-tdd-gate): decide() also credits a subagent's matching test edit + RED via a new subagentEntriesList. turnTaskAgentIds() reads the hex agentId from the harness-written Task tool_result (the controller cannot forge its own tool_result; hex-only match blocks "agentId: ../../x" path traversal). subagentTranscriptPaths() derives <dir>/<controller-session>/subagents/agent-<id>.jsonl. main() reads them best-effort (missing/unreadable -> no extra credit = stricter). No new weakening: a delegated subagent doing real TDD is legitimate; the only forgery vector (overwriting the agent jsonl) is closed by Part 1. Existing controller-turn behaviour is preserved (empty subagent list == old logic). OWNER (settings.json, Claude can't edit it): enforce-tdd-gate is already a registered PreToolUse hook -> Part 2 goes live on merge. enforce-runtime-write-deny must be registered on PreToolUse(Edit|Write|MultiEdit|NotebookEdit) for Part 1 to be live. TDD: RED -> GREEN per behavior. tools-vitest 2027 passed / 2 skipped. Backlog item C (=Z); plan docs/superpowers/plans/2026-05-31-discipline-guard-backlog.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>