4.4 KiB
Enforce hard rules — implementation plan
Spec: docs/superpowers/specs/2026-05-25-enforce-hard-rules-design.md
Branch: feat/enforce-hard-rules
Estimate: 4-8 hours autonomous (overnight)
Tasks (in commit order — each commit standalone testable)
T1 — Shared hook helpers + override vocab
Files: tools/enforce-hook-helpers.mjs, tools/enforce-hook-helpers.test.mjs, tools/enforce-override-vocab.json
Helpers: readStdinJson, readTranscript, getCoverageFromLastAssistant, hasOverridePhrase, loadVocab, sentinelPath, writeSentinel, readSentinel, expectedBranchPath, getExpectedBranch, setExpectedBranch, readRationalizationFlags, appendRationalizationFlag.
Override vocab content: initial 6 phrases per spec §9.
Coverage: skill:superpowers:test-driven-development
T2 — Rule #5 memory-sync coverage (PreToolUse)
File: tools/enforce-memory-coverage.mjs + test.
Simplest rule, easy validation. RED test: prod-code edit with TDD coverage → block. GREEN: memory edit with memory-sync coverage → allow.
T3 — Rule #7 branch-switch detection (PreToolUse Bash)
File: tools/enforce-branch-switch.mjs + test.
Reads expected-branch file, runs git branch --show-current, compares.
T4 — Rule #4 verify-before-push (PreToolUse + PostToolUse Bash)
Files: tools/enforce-verify-before-push.mjs (PreToolUse) + tools/enforce-verify-record.mjs (PostToolUse to write sentinel) + tests.
PostToolUse runs after Bash with vitest/pest pattern. If exit 0 + stdout has PASS marker → write sentinel.
PreToolUse on git commit/push checks sentinel age + exists.
T5 — Rule #2 coverage-verify (Stop)
File: tools/enforce-coverage-verify.mjs + test.
Parses last assistant message for coverage line, checks against transcript tool_use history.
T6 — Rule #1 mandatory re-classification injection (UserPromptSubmit)
File: tools/enforce-prompt-injection.mjs + test.
Reads classifier output from router-state-*.json, injects mandatory coverage list via stdout JSON.
T7 — Rule #3 + Rule #6 TDD + writing-plans gate (PreToolUse Edit/Write/MultiEdit)
File: tools/enforce-tdd-gate.mjs + test.
Path-match, transcript-scan for test-edit + vitest-fail-output, OR plan-file-exists.
T8 — Rule #8 classifier-mismatch (Stop)
File: tools/enforce-classifier-match.mjs + test.
Reads classifier output, checks turn for matching Skill/Task tool_use, gates on confidence threshold.
T9 — Rule #10 rationalization flags (PostToolUse Bash + Edit/Write)
File: tools/enforce-rationalization-audit.mjs + test.
Scan transcript for rationalization phrases / weak tests; append flag JSONL.
T10 — Atomic wire-up
File: .claude/settings.json — add all hooks to PreToolUse/PostToolUse/UserPromptSubmit/Stop.
Critical: this must be the LAST commit. Pre-wire commits keep hooks inert.
T11 — Smoke + push
Manual smoke each hook with synthetic stdin. Then git push origin feat/enforce-hard-rules:main via FF (or merge-commit if main moved).
T12 — Memory + state sync
Create memory/project_enforce_hard_rules.md, update MEMORY.md index, project_state.md, reference_github.md.
Risks identified, mitigations
- R1: Parallel session edits
.claude/settings.jsonwhile I'm working. Mitigation: Read settings.json fresh right before T10. Usegit stashfor any concurrent local changes if needed. - R2: A rule blocks my own work mid-task. Mitigation: Rules inert until T10. If T10 wire-up succeeds and immediately blocks me on T11 push, override-vocab is in place (
recoveryphrase). - R3: Hook scripts crash → all subsequent tool calls hang. Mitigation: Every hook wraps logic in try/catch, exits 0 with empty {} on internal error (fail-quiet). NEVER exit 2 unless intentional violation found.
- R4: Override-vocab phrase appears coincidentally in user's normal speech. Mitigation: Phrases chosen to be unusual (включают «без скилов» which is unlikely normal).
- R5: PreToolUse latency on Bash slows every command. Mitigation: Hook target deltay <100ms by reading minimum (cached classifier-state, sentinel file, no transcript-parse unless rule triggers).
Acceptance criteria
- All 10 rules implemented with unit tests
- All hooks wired in settings.json
- Manual smoke per hook: fake-stdin → expected exit code + stderr
- Push to origin/main (or PR if main is unstable)
- Memory + project_state synced