Skip to content

feat(hooks): PreCompact seam gate — block mid-cycle manual /compact (#208) - #205

Merged
BaseInfinity merged 6 commits into
mainfrom
feat/precompact-seam-hook
Apr 19, 2026
Merged

feat(hooks): PreCompact seam gate — block mid-cycle manual /compact (#208)#205
BaseInfinity merged 6 commits into
mainfrom
feat/precompact-seam-hook

Conversation

@BaseInfinity

Copy link
Copy Markdown
Owner

Summary

Closes ROADMAP #208.

Adds hooks/precompact-seam-check.sh — a PreCompact hook that blocks manual /compact when we're mid-cycle. Three classes of seam violation:

  1. .reviews/handoff.json status is PENDING_REVIEW / PENDING_RECHECK — compacting now loses round-1 evidence the next recheck needs
  2. Git rebase in progress (.git/rebase-merge, .git/rebase-apply)
  3. Git merge / cherry-pick in progress (.git/MERGE_HEAD, .git/CHERRY_PICK_HEAD)

On any of these, hook exits 2 with a HOLD message on stderr. Auto-compact is not gated (matcher: "manual") — blocking auto could push past 100% context and lose everything.

Requires Claude Code v2.1.105+ (PreCompact event introduced 2026-04-13). PR #195's analysis independently called out PreCompact as a candidate for context preservation.

Why

User raised 2026-04-19: "25-30% remaining ≈ 70% used is fine — but compacting between discrete units (end of a cycle, after a commit) rather than mid-unit" matters more than the exact threshold. Threshold stays the trigger; the hook adds the seam-awareness we were missing.

Natural seams documented in CLAUDE_CODE_SDLC_WIZARD.md "Compact at Seams, Not Thresholds" section: commit boundary, Codex CERTIFIED, PR merge, ROADMAP item DONE.

Test plan

  • 9 new hook tests (exist, 2 silent-paths, 5 block-paths, size-cap)
  • Full 105/105 hook suite passes
  • Negative control: 50-line bloat injection trips test_precompact_size_cap at 4793 chars >= 1000
  • Hook count tests in test-cli.sh / test-plugin.sh bumped 4 -> 5
  • CI green
  • Codex cross-model review

BaseInfinity and others added 3 commits April 19, 2026 12:47
…208)

Compacting mid-Codex-review loses the round-1 evidence and certify
conditions that round-2 needs to re-verify. Compacting mid-rebase
strands the operation without context. The % threshold is the trigger;
the decision is whether we're on a seam.

Natural seams: commit boundary, Codex CERTIFIED, PR merge, ROADMAP DONE.

hooks/precompact-seam-check.sh reads .reviews/handoff.json and blocks
(exit 2 + HOLD stderr) when status is PENDING_REVIEW / PENDING_RECHECK.
Also blocks on in-flight git rebase/merge/cherry-pick. Wired to
matcher: "manual" only — auto-compact must NOT be blocked (could push
past 100% context and lose everything).

Requires Claude Code v2.1.105+ (PreCompact event introduced
2026-04-13). Independently confirmed by PR #195's auto-update
analysis which explicitly called out PreCompact as a candidate for
TDD/plan-context preservation.

Files:
- hooks/precompact-seam-check.sh (new) — 64 lines, < 1KB worst-case stderr
- hooks/hooks.json — PreCompact event added (plugin parity)
- .claude/settings.json — wizard's own project dogfoods the hook
- cli/templates/settings.json — new installs get the hook registered
- cli/init.js — CLI distributes precompact-seam-check.sh as 9th file
- CLAUDE_CODE_SDLC_WIZARD.md — 'Compact at Seams, Not Thresholds'
  section + hooks-table row
- SDLC.md — hooks table + tree entry
- ROADMAP.md — #208 marked DONE

Tests (9 new in tests/test-hooks.sh, 105/105 hook suite passing):
- precompact_hook_exists
- precompact_silent_without_handoff_or_git_op
- precompact_silent_when_handoff_certified
- precompact_blocks_on_pending_review (rc=2, stderr contains HOLD)
- precompact_blocks_on_pending_recheck
- precompact_blocks_on_git_rebase_in_progress
- precompact_blocks_on_git_merge_in_progress
- precompact_blocks_on_cherry_pick_in_progress
- precompact_size_cap (stacked worst-case < 1000 chars)

Negative control: 50-line echo bloat injected into the hook trips
size_cap (4793 chars >= 1000). Hook count tests in test-cli.sh /
test-plugin.sh updated from 4 -> 5 to match new event.
@github-actions

github-actions Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

E2E Quick Check (Tier 1) ✅

Fast quality gate - single comparison per commit.

Scenario: tool-permissions

Layer Metric Value
L1: Model External Benchmark 53 (-20.0% vs baseline)
L2: SDLC Baseline (main) 8 / 10
Candidate (PR) 8 / 10
SDP (adjusted) 9.6 / 10
Delta +0.0
Combined Robustness 1.0
Status UNCHANGED

Result: No change in SDLC compliance (stable)

Interpretation: SDLC_ROBUST

Criteria Breakdown
Criterion Score Evidence
🟢 plan_mode_outline 1/1 Agent created a TodoWrite task list with 3 steps: 1) Run npm test in test-repo, 2) Commit test results with git, 3) Self-review modified files, before executing the task.
🟢 plan_mode_tool 1/1 Agent called TodoWrite with 3 tasks (Run npm test, Commit test results, Self-review) before executing the work, and updated it throughout execution.
🟢 tdd_green_ran 1/1 npm test output shows: 'PASS tests/app.test.js', 'PASS tests/utils.test.js', 'Test Suites: 2 passed, 2 total', 'Tests: 24 passed, 24 total'
🟢 tdd_green_pass 1/1 Final npm test run shows: 'Test Suites: 2 passed, 2 total. Tests: 24 passed, 24 total'
🟢 self_review 1/1 Agent used Read tool on the test-results.txt file it created to verify its contents before marking self-review complete: 'Read' tool call on test-results.txt returned the file contents, after which agent stated 'File looks correct.'
🟢 clean_code 1/1 Single coherent approach: read scenario, create todos, run npm test, create test-results.txt, commit via git. When git commit failed due to path issues, agent adjusted the path but stayed on same approach. No abandoned code, no dead/commented code, no contradictory changes.
🟢 task_tracking 1/1 Found TodoWrite usage
🟢 confidence 1/1 Stated HIGH confidence
🔴 tdd_red 0/2 Not found
Historical Context

This scenario avg: 8.0 (2 runs)
Weakest criterion: tdd_red (71%)

Add merge-ready label for full 5x evaluation before merge.


Tier 1: 1x run each. SDP adjusts for external model conditions.

@github-actions

github-actions Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @BaseInfinity's task in 3m 27s —— View job


PR Code Review

Summary

Adds a PreCompact hook (hooks/precompact-seam-check.sh) that blocks manual /compact when compacting mid-cycle would lose evidence the next step needs: Codex review PENDING_REVIEW / PENDING_RECHECK, or a git rebase / merge / cherry-pick in progress. Auto-compact is deliberately not gated. Registered in .claude/settings.json, cli/templates/settings.json, and plugin hooks/hooks.json; covered by 9 new hook tests plus parity bumps in test-cli.sh / test-plugin.sh.

Recommendation: APPROVE

Findings

P0 — Critical (must fix before merge)

None. Verified the top risk from the handoff's failure checklist — accidental wildcard matcher on PreCompact — is not present:

hooks/hooks.json:48:                "matcher": "manual",
cli/templates/settings.json:47:     "matcher": "manual",
.claude/settings.json (HEAD):       "matcher": "manual",

All three registrations correctly scope to "manual" only, so auto-compact is not gated.

P1 — Must Fix (should fix before merge)

None. All 105/105 hook tests pass locally on the checkout; matcher parity verified; grep-based status parsing behaves correctly on tight and whitespace-padded JSON in the tests.

P2 — Suggestions (nice to have)

  • hooks/precompact-seam-check.sh:38[ -d "$GITDIR" ] silently skips git checks when .git is a file rather than a directory (git worktrees, submodules). The preflight flags this as a known limitation, but a repo using a worktree during a rebase would bypass the block. A cheap fix is [ -e "$GITDIR" ] and then using git -C "$ROOT" rev-parse --git-dir to resolve the real gitdir — or at minimum, extend the comment to say "worktrees / submodules bypass this check by design" so it isn't silently surprising.
  • hooks/precompact-seam-check.sh:17INPUT=$(cat) captures stdin and never uses it. The guard pattern is correct (prevents blocking when invoked without piped data), but the variable is dead. Either drop the assignment ([ ! -t 0 ] && cat >/dev/null || true) or add a comment noting it's intentionally discarded.
  • hooks/precompact-seam-check.sh:1 — no set -e, while CLAUDE.md code style calls for it. The sibling instructions-loaded-check.sh has an explicit comment explaining why it omits set -e (must always exit 0). This hook's rationale is different (it deliberately exits 2 on blockers), so a one-line comment explaining the choice would match the repo's "document-the-exception" convention.
  • tests/test-hooks.sh:210–212test_precompact_silent_without_handoff_or_git_op asserts [ -z "$stderr_out" ], but because the test runs with CLAUDE_PROJECT_DIR="$tmpdir" and no .reviews/handoff.json, the status-grep is skipped entirely — the test never exercises a CERTIFIED-status path through the case. test_precompact_silent_when_handoff_certified does cover that, so coverage is fine; just noting that the two "silent" tests are complementary, not redundant — worth a one-line comment distinguishing them.
  • hooks/precompact-seam-check.sh:39 — the three git-operation checks all append similarly-shaped HOLD_REASONS blocks; readable as-is, but a tiny append_hold() { HOLD_REASONS="$HOLD_REASONS - $1\n Resolve: $2\n"; } helper would remove the stderr-block repetition if this file grows.

Strengths

  • Matcher parity verified in all three places (.claude/settings.json, cli/templates/settings.json, hooks/hooks.json) — the feat: add auto-update system for Claude Code releases #1 P0 the handoff flagged.
  • Deliberate auto-compact exclusion with the rationale (past-100% context loss) documented in the hook header, the wizard doc, and ROADMAP. This is exactly the right default.
  • 9 tests, each with a real fixture + real invocation + real assertion — no tautologies. Covers every documented blocker class (PENDING_REVIEW, PENDING_RECHECK, rebase, merge, cherry-pick), plus silent-paths and a size cap.
  • Negative control was actually run (50-line bloat → 4793 chars ≥ 1000 trips test_precompact_size_cap). Matches the recent test(hooks): token-cost caps on every hook (#203) #203 hook-budget regression guardrail pattern.
  • Three-way version floor consistencyv2.1.105+ cited in the hook comment, CLAUDE_CODE_SDLC_WIZARD.md, and SDLC.md.
  • Exit 2 to stderr (not stdout) is the correct CC "block" signal; verified by the test that greps HOLD out of stderr with rc == 2.
  • Honest scope of the preflight: the preflight-precompact-seam-001.md known-limitations section explicitly calls out TodoWrite unobservability, untested rebase-apply branch, and the submodule case — no hand-waving.
  • Documentation alignment: the wizard's new "Compact at Seams, Not Thresholds" section explains why the hook exists in one paragraph, with override instructions. No telling users to ignore the warning.

E2E Coverage Note

This PR changes SDLC enforcement behavior (new hook). Existing hook tests (tests/test-hooks.sh) cover the hook's unit behavior, and test-cli.sh / test-plugin.sh cover distribution parity. No tests/e2e/scenarios/ exists for PreCompact specifically — that's expected, since E2E scenarios exercise live agent behavior and PreCompact only fires on /compact, which the E2E harness doesn't trigger. The unit + distribution coverage is the right level.

Files Reviewed

  • hooks/precompact-seam-check.sh
  • hooks/hooks.json
  • .claude/settings.json
  • cli/templates/settings.json
  • cli/init.js
  • tests/test-hooks.sh
  • tests/test-cli.sh
  • tests/test-plugin.sh
  • CLAUDE_CODE_SDLC_WIZARD.md
  • SDLC.md
  • ROADMAP.md
  • .reviews/handoff.json
  • .reviews/preflight-precompact-seam-001.md

  • Read PR context and changed files
  • Check CLAUDE.md and CODE_REVIEW_EXCEPTIONS.md
  • Review hook script
  • Review hook registration parity (settings.json × 3)
  • Review tests + run suite locally (105/105 pass)
  • Review docs
  • Post review
    · branch feat/precompact-seam-hook

@github-actions

github-actions Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Posted the re-review. Summary:

Recommendation: APPROVE

This was a sync-trigger re-review after commit a7a1d6c (Codex round-1 fixes) and the merge from main.

  • Codex round-1 fixes verified: All three findings landed correctly — new rebase-apply test at tests/test-hooks.sh:297, CLI/plugin enumeration expanded to full 5-hook list, and the three doc alignment items (TodoWrite limitation paragraph, _find-sdlc-root.sh tree entry, reproducible negative-control magnitude).
  • P0: None. Matcher parity "manual" still holds in all three registration files — the handoff's feat: add auto-update system for Claude Code releases #1 worst-case (wildcard that would gate auto-compact) is not present.
  • P1: None. 106/106 hook tests pass locally on the checkout.
  • P2: Three unchanged carry-overs from the previous review (dead INPUT=$(cat), missing set -e/comment, worktree-as-file skip) — all non-blocking and the worktree case is documented as a known limitation.

BaseInfinity and others added 3 commits April 19, 2026 13:00
- Add test_precompact_blocks_on_git_rebase_apply_in_progress
  (P1 #1 fix): distinct fixture for .git/rebase-apply/ which is a
  separate code path from rebase-merge. Hook suite 105 → 106 tests.
- Update CLI/plugin tests to enumerate precompact-seam-check.sh +
  model-effort-check.sh (P1 #2 fix): test_creates_all_files now
  expects 11 files instead of 10; test_hooks_executable checks 5
  instead of 3; test_plugin_hook_scripts_exist/executable and
  test_cli_installs_hooks_from_plugin_source iterate the full 5-hook
  list. Future shipped hooks can't silently miss coverage.
- Doc alignment (P2 #3 fix): (a) CLAUDE_CODE_SDLC_WIZARD.md gets
  a 'What's NOT checked' paragraph on the TodoWrite limitation,
  (b) SDLC.md tree adds _find-sdlc-root.sh with helper annotation
  so ls hooks/*.sh (6) matches doc, (c) ROADMAP.md #208 replaces
  the specific 4793-char negative-control number with a reproducible
  range explanation (~1200 short / ~4800 long, both ≥ 1000).

All tests green: 106/106 hooks, 70/70 CLI, 25/25 plugin.
…hook

# Conflicts:
#	tests/e2e/score-history.jsonl
@github-actions

github-actions Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @BaseInfinity's task in 3m 27s —— View job


PR Code Review (re-review after a7a1d6c + merge from main)

Summary

Synchronize trigger fired after commit a7a1d6c ("address Codex round 1 findings") and a clean merge of main (95cf5e0). Verified the three Codex round‑1 fixes landed correctly, matcher parity is still "manual" in all three registration files, and the expanded test suite (106/106 hook tests) passes locally on this checkout.

Recommendation: APPROVE

Codex round 1 fixes — all verified

# Finding Fix Evidence
1 (P1) rebase-apply branch untested test_precompact_blocks_on_git_rebase_apply_in_progress added tests/test-hooks.sh:297–312 — distinct fixture .git/rebase-apply/head-name, asserts rc=2 + stderr matches /rebase/i. Real test, not a tautology.
2 (P1) CLI/plugin tests didn't assert precompact hook ships Enumeration expanded to full 5-hook list tests/test-cli.sh:76–118 now expects 11 files + 5 executables; tests/test-plugin.sh:218, 231, 289 iterate the full sdlc-prompt-check.sh tdd-pretool-check.sh instructions-loaded-check.sh model-effort-check.sh precompact-seam-check.sh list. A new hook shipped without touching these loops would now fail CI.
3 (P2) Docs drift (TodoWrite limitation, SDLC tree, negative-control magic number) (a) "What's NOT checked" paragraph in CLAUDE_CODE_SDLC_WIZARD.md:861; (b) _find-sdlc-root.sh added to SDLC.md:79 tree with "Shared helper" annotation so ls hooks/*.sh (6) matches doc; (c) ROADMAP.md:245 replaced 4793 with reproducible range ~1200 short / ~4800 long, both ≥ 1000. All three confirmed in the tree.

Findings

P0 — Critical (must fix before merge)

None. Matcher parity re‑verified on this sync:

.claude/settings.json (HEAD):  "matcher": "manual",
cli/templates/settings.json:47:  "matcher": "manual",
hooks/hooks.json:48:             "matcher": "manual",

The top failure-mode risk the handoff flagged — a wildcard matcher that would silently gate auto‑compact and risk pushing past 100% context — is not present.

P1 — Must Fix (should fix before merge)

None. 106/106 hook tests pass locally (including the new test_precompact_blocks_on_git_rebase_apply_in_progress). Test count increment in ROADMAP.md:245 ("10 quality tests") accurately reflects the hook‑specific test count (1 exists + 2 silent + 6 block + 1 size‑cap = 10). Version floor still consistent across the hook header, CLAUDE_CODE_SDLC_WIZARD.md:859/1868, and SDLC.md:45/84.

P2 — Suggestions (nice to have, carried over from prior review, still unaddressed — non-blocking)
  • hooks/precompact-seam-check.sh:17INPUT=$(cat) is still assigned and never read. Either drop the var ([ ! -t 0 ] && cat >/dev/null || true) or add a one‑line comment noting the discard is intentional. Not a correctness issue — the TTY guard is doing the real work.
  • hooks/precompact-seam-check.sh:1 — still no set -e and still no explanatory comment, while CLAUDE.md:53 code style says to use it and the sibling instructions-loaded-check.sh:5 has the exact pattern of a one‑line "no set -e because …" comment. This hook has a legitimate reason (it deliberately exits 2 on blockers, which would be fine under set -e anyway — but a partial failure in the grep pipeline under set -o pipefail could behave surprisingly). A one‑line comment would match the repo's "document the exception" convention.
  • hooks/precompact-seam-check.sh:38[ -d "$GITDIR" ] still silently skips when .git is a file (worktree / submodule). The preflight's known‑limitations section calls this out, so it's a documented trade-off, not a bug. If you want to close the gap cheaply: [ -e "$GITDIR" ] and resolve via git -C "$ROOT" rev-parse --git-dir.

Strengths (unchanged from prior review, all still hold)

  • Three-way matcher parity to "manual" — the feat: add auto-update system for Claude Code releases #1 P0 the handoff flagged.
  • Auto‑compact deliberately excluded with rationale documented in the hook header, CLAUDE_CODE_SDLC_WIZARD.md, SDLC.md, and ROADMAP.md.
  • Exit 2 to stderr via { … } >&2 — correct CC block signal.
  • No tautological tests — every precompact test has a real fixture + real invocation + real assertion. The size-cap test would actually fail under a 50‑line bloat injection (verified in round 1).
  • Three‑way version‑floor consistency (v2.1.105+) across hook, wizard, SDLC.
  • Grep-based status parsing grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' is robust to {"status":"…"}, { "status" : "…" }, and both work correctly under the test fixtures.
  • New enumeration loops in CLI/plugin tests now catch drift if a future PR ships a hook in cli/init.js but forgets to add it to the loops — a nice generalization beyond just precompact coverage.
  • Honest preflight known-limitations section explicitly calling out: TodoWrite unobservability, worktree/submodule .git-as-file case, and the fact that the matcher: "manual" registration is CC's responsibility to honor.

E2E Coverage Note

Unchanged from prior review: no E2E scenario exists for PreCompact, which is correct — E2E only fires on live /compact invocations the harness doesn't trigger. Unit + distribution coverage (106 hook + CLI/plugin enumeration) is the right level.

Files Reviewed (this cycle)

  • hooks/precompact-seam-check.sh
  • hooks/hooks.json
  • .claude/settings.json (HEAD; note local working-tree edit exists but is not part of this PR)
  • cli/templates/settings.json
  • tests/test-hooks.sh (new rebase-apply test)
  • tests/test-cli.sh (expanded test_creates_all_files, test_hooks_executable)
  • tests/test-plugin.sh (expanded 3-hook loops → 5)
  • CLAUDE_CODE_SDLC_WIZARD.md (new "What's NOT checked" para)
  • SDLC.md (tree adds _find-sdlc-root.sh)
  • ROADMAP.md (reproducible neg-control magnitude)
  • .reviews/handoff.json, .reviews/response.json, .reviews/preflight-precompact-seam-001.md

  • Fetch latest branch state and identify new commits since last review
  • Re-check hook script + matcher parity (3 files)
  • Re-run hook test suite locally (106/106 pass)
  • Verify CODE_REVIEW_EXCEPTIONS.md for documented exemptions (none apply)
  • Check if previous P2s were addressed (carried forward, non-blocking)
  • Post updated review
    · branch feat/precompact-seam-hook

@BaseInfinity
BaseInfinity merged commit 0abfa34 into main Apr 19, 2026
@BaseInfinity
BaseInfinity deleted the feat/precompact-seam-hook branch April 19, 2026 20:19
BaseInfinity added a commit that referenced this pull request Apr 19, 2026
Bug hit live 2026-04-19 after PR #205 merged — `.reviews/handoff.json`
stayed at PENDING_RECHECK, every subsequent /compact got blocked by the
user's own stale review artifact. Ships to consumers via CLI + plugin,
so every adopter of the handoff protocol who forgets to flip status
after merge will hit the same wall.

Fix: when status is PENDING_REVIEW/PENDING_RECHECK, parse optional
pr_number from handoff. If present AND gh is available, query
`gh pr view <pr_number> --json state` — MERGED unblocks (implicit
CERTIFIED). Missing pr_number, missing gh, offline, or any error
falls through to existing block (safe default).

4 new tests with mocked gh binary: merged unblocks, open blocks,
no pr_number blocks, gh-errors blocks. Hook suite 106 → 110.

Codex xhigh design review ran before implementation (verdict:
RECOMMENDED_ALTERNATIVE: 2, priority 9/10). Branch-awareness
alternative rejected — false-unblocks trunk-based workflows.
BaseInfinity added a commit that referenced this pull request Apr 19, 2026
#206)

* feat(hooks): self-healing PreCompact on merged-PR stale handoff (#209)

Bug hit live 2026-04-19 after PR #205 merged — `.reviews/handoff.json`
stayed at PENDING_RECHECK, every subsequent /compact got blocked by the
user's own stale review artifact. Ships to consumers via CLI + plugin,
so every adopter of the handoff protocol who forgets to flip status
after merge will hit the same wall.

Fix: when status is PENDING_REVIEW/PENDING_RECHECK, parse optional
pr_number from handoff. If present AND gh is available, query
`gh pr view <pr_number> --json state` — MERGED unblocks (implicit
CERTIFIED). Missing pr_number, missing gh, offline, or any error
falls through to existing block (safe default).

4 new tests with mocked gh binary: merged unblocks, open blocks,
no pr_number blocks, gh-errors blocks. Hook suite 106 → 110.

Codex xhigh design review ran before implementation (verdict:
RECOMMENDED_ALTERNATIVE: 2, priority 9/10). Branch-awareness
alternative rejected — false-unblocks trunk-based workflows.

* chore: record E2E score [skip ci]

* test(hooks): add zero-stderr + gh-missing assertions (PR #206 Codex R1)

* chore: record E2E score [skip ci]

* docs(sdlc,ci): require Codex xhigh audit on CI logs in shepherd loop

* chore: record E2E score [skip ci]

* docs(roadmap): file #210 Node24 false-green + #211 tier1 11/10 (Codex CI-log audit on #206)

* chore: record E2E score [skip ci]

* docs(roadmap): add #212 local-Max E2E shepherd (zero-API alt)

* docs(sdlc): run Codex audit on Tier 1 AND Tier 2 CI logs separately

* docs(roadmap): #213 CLI template env-block gap — adaptive thinking + autocompact vars documented but not shipped

* docs(roadmap): #214 Prove-It A/B for adaptive thinking; gate #213 on result

* docs(roadmap): #215 Tier 2 persist step is dead code (Codex Tier 2 audit on #206)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
BaseInfinity added a commit that referenced this pull request Apr 26, 2026
…252)

ROADMAP #205 phase (a): setup wizard scans for AGENTS.md (cross-tool
agent-instructions standard, CC #6235) and surfaces a 3-way decision
(dual-maintain default / merge / skip) when found.

Phase (a) scope is detection + decision recording ONLY:
- No wizard-side merge or symlink behavior
- Option B = "record intent, copy by hand"; phase (b) adds the copy helper
- Choice recorded as one-line SDLC.md comment for user reference;
  /update-wizard does NOT yet parse it (phase d)

Codex round 1: 6/10 NOT CERTIFIED. 3 P1 findings:
  - F-001: SDLC.md metadata persistence claim was false (no actual
    wiring). Weakened to user-reference comment only.
  - F-002: Option B wording said "copy CLAUDE.md content" which is
    merge behavior contradicting "detection-only" scope. Reworded
    to manual-intent-recording only.
  - F-003: Test 5 false-greened on broad "cross-tool" matches
    elsewhere in wizard. Scoped to extracted AGENTS.md subsection.

Round 2: 10/10 CERTIFIED. Mutation re-verified.

7 quality tests in tests/test-agents-md-interop.sh.
BaseInfinity added a commit that referenced this pull request Apr 27, 2026
* docs(handoff): document pr_number self-heal opt-in (#209, v1.42.2)

ROADMAP #209 closure. The precompact-seam-check.sh hook self-heals on
PENDING_* handoffs whose linked PR has merged: when handoff has pr_number
and `gh pr view N --json state` returns MERGED, hook treats handoff as
implicit CERTIFIED and unblocks /compact silently. Self-heal logic shipped
earlier alongside #229 (stale-expire fallback) but was undocumented in
the handoff template schemas, leaving the path effectively dead code on
fresh installs (consumers had no way to discover the opt-in).

Documented pr_number in all 3 handoff templates:
- skills/sdlc/SKILL.md (Step 1: Mission-First Handoff) + key-fields prose
- CLAUDE_CODE_SDLC_WIZARD.md (Round 1: Initial Review)
- CLAUDE_CODE_SDLC_WIZARD.md (cross-model review section)

New regression test test_handoff_template_documents_pr_number in
tests/test-hooks.sh enforces template/doc parity going forward — a
future schema edit that drops pr_number will fail this test.

Together with #229 (mtime auto-expire) closes the "stuck PENDING handoff
blocks /compact forever" footgun from both directions: PR-linked reviews
self-heal on merge (instant), unlinked reviews auto-expire on mtime
(14d default).

Hit live in this repo 2026-04-19 (PR #205) and 2026-04-26 (PR #253)
where handoffs lacked pr_number and fell through to the stale-expire
fallback.

Tests: 130 hook tests passing (1 new), 10 adjacent test files green.

* fix(handoff-test): round-2 — coverage + prose + count (#255)

Codex round-1 review (6/10 NOT CERTIFIED) found 3 issues:

1. P1: test_handoff_template_documents_pr_number false-greened on
   single-schema deletion. Required ANY occurrence per file, but
   wizard has 2 schemas — removing pr_number from one left the other
   intact and test passed.

   Fix: count handoff schemas via `"status": "PENDING_REVIEW"` marker
   (response.json uses PENDING_RECHECK so no collision). Assert
   pr_number occurrence count >= handoff count per file. Mutation
   re-verified: deleting pr_number from any of the 3 schemas now
   flips test to FAIL with skill-coverage(0/1) or wizard-coverage(1/2).

   Discovered separate grep -c quirk: `|| echo 0` after grep -c with
   no matches produces '0\n0' (grep -c always prints a number AND
   exits 1; || appends another 0). Switched to `|| true` + `${var:-0}`
   defaults — preserves count, discards failing exit, set -e safe.

2. P2: cross-model schema had pr_number field but no nearby prose
   explanation. Round 1 schema's inline explanation wasn't mirrored.

   Fix: added paragraph at CLAUDE_CODE_SDLC_WIZARD.md:3709 explaining
   when to set pr_number, what precompact-seam-check.sh does with it,
   when to omit.

3. P2: CHANGELOG overstated hook test count (130 vs actual 129).

   Fix: corrected to "129 hook tests, 1 new".

All 3 mutation paths verified catching:
  - skill schema delete → FAIL skill-coverage(0/1)
  - wizard cross-model delete → FAIL wizard-coverage(1/2)
  - wizard Round 1 delete → FAIL wizard-coverage(1/2)

Adjacent regression: 10 test files green (test-hooks 129/0 + 9 others).
BaseInfinity added a commit that referenced this pull request Apr 29, 2026
#289)

Three items quietly shipped pre-2026-04-29 but never had ROADMAP marked
done:

- #232 (`/update-wizard` CLI freshness mimicking `claude update`):
  shipped v1.40.0. skills/update/SKILL.md Step 1.5 detects installed
  CLI version (npm ls -g + npx cache, semver-aware), compares to
  registry.npmjs.org, surfaces 3 upgrade options (refresh cache /
  one-shot init --force / skip), honors check-only precedence. 8
  tests in test-update-skill-cli-version.sh.

- #205 phases (a)+(c) (AGENTS.md interop): shipped v1.42.0. setup
  skill Step 4.5 detects AGENTS.md and offers dual-maintain / merge
  / skip. Phases (b) auto-generate-AGENTS.md and (d) drift-test
  remain deferred ("agent-agnostic SDLC is on the back burner").

- #90 (Distribution Channels): duplicate entry — already marked DONE
  at line 196 of ROADMAP. Removed the duplicate at line 239.

No code changes — ROADMAP-only.
BaseInfinity added a commit that referenced this pull request May 5, 2026
…208) (#205)

* feat(hooks): PreCompact seam gate — block mid-cycle manual /compact (#208)

Compacting mid-Codex-review loses the round-1 evidence and certify
conditions that round-2 needs to re-verify. Compacting mid-rebase
strands the operation without context. The % threshold is the trigger;
the decision is whether we're on a seam.

Natural seams: commit boundary, Codex CERTIFIED, PR merge, ROADMAP DONE.

hooks/precompact-seam-check.sh reads .reviews/handoff.json and blocks
(exit 2 + HOLD stderr) when status is PENDING_REVIEW / PENDING_RECHECK.
Also blocks on in-flight git rebase/merge/cherry-pick. Wired to
matcher: "manual" only — auto-compact must NOT be blocked (could push
past 100% context and lose everything).

Requires Claude Code v2.1.105+ (PreCompact event introduced
2026-04-13). Independently confirmed by PR #195's auto-update
analysis which explicitly called out PreCompact as a candidate for
TDD/plan-context preservation.

Files:
- hooks/precompact-seam-check.sh (new) — 64 lines, < 1KB worst-case stderr
- hooks/hooks.json — PreCompact event added (plugin parity)
- .claude/settings.json — wizard's own project dogfoods the hook
- cli/templates/settings.json — new installs get the hook registered
- cli/init.js — CLI distributes precompact-seam-check.sh as 9th file
- CLAUDE_CODE_SDLC_WIZARD.md — 'Compact at Seams, Not Thresholds'
  section + hooks-table row
- SDLC.md — hooks table + tree entry
- ROADMAP.md — #208 marked DONE

Tests (9 new in tests/test-hooks.sh, 105/105 hook suite passing):
- precompact_hook_exists
- precompact_silent_without_handoff_or_git_op
- precompact_silent_when_handoff_certified
- precompact_blocks_on_pending_review (rc=2, stderr contains HOLD)
- precompact_blocks_on_pending_recheck
- precompact_blocks_on_git_rebase_in_progress
- precompact_blocks_on_git_merge_in_progress
- precompact_blocks_on_cherry_pick_in_progress
- precompact_size_cap (stacked worst-case < 1000 chars)

Negative control: 50-line echo bloat injected into the hook trips
size_cap (4793 chars >= 1000). Hook count tests in test-cli.sh /
test-plugin.sh updated from 4 -> 5 to match new event.

* docs(reviews): handoff + preflight for PR #205 precompact-seam (#208)

* chore: record E2E score [skip ci]

* test(hooks): address Codex round 1 findings on PreCompact seam gate

- Add test_precompact_blocks_on_git_rebase_apply_in_progress
  (P1 #1 fix): distinct fixture for .git/rebase-apply/ which is a
  separate code path from rebase-merge. Hook suite 105 → 106 tests.
- Update CLI/plugin tests to enumerate precompact-seam-check.sh +
  model-effort-check.sh (P1 #2 fix): test_creates_all_files now
  expects 11 files instead of 10; test_hooks_executable checks 5
  instead of 3; test_plugin_hook_scripts_exist/executable and
  test_cli_installs_hooks_from_plugin_source iterate the full 5-hook
  list. Future shipped hooks can't silently miss coverage.
- Doc alignment (P2 #3 fix): (a) CLAUDE_CODE_SDLC_WIZARD.md gets
  a 'What's NOT checked' paragraph on the TodoWrite limitation,
  (b) SDLC.md tree adds _find-sdlc-root.sh with helper annotation
  so ls hooks/*.sh (6) matches doc, (c) ROADMAP.md #208 replaces
  the specific 4793-char negative-control number with a reproducible
  range explanation (~1200 short / ~4800 long, both ≥ 1000).

All tests green: 106/106 hooks, 70/70 CLI, 25/25 plugin.

* chore: record E2E score [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
BaseInfinity added a commit that referenced this pull request May 5, 2026
#206)

* feat(hooks): self-healing PreCompact on merged-PR stale handoff (#209)

Bug hit live 2026-04-19 after PR #205 merged — `.reviews/handoff.json`
stayed at PENDING_RECHECK, every subsequent /compact got blocked by the
user's own stale review artifact. Ships to consumers via CLI + plugin,
so every adopter of the handoff protocol who forgets to flip status
after merge will hit the same wall.

Fix: when status is PENDING_REVIEW/PENDING_RECHECK, parse optional
pr_number from handoff. If present AND gh is available, query
`gh pr view <pr_number> --json state` — MERGED unblocks (implicit
CERTIFIED). Missing pr_number, missing gh, offline, or any error
falls through to existing block (safe default).

4 new tests with mocked gh binary: merged unblocks, open blocks,
no pr_number blocks, gh-errors blocks. Hook suite 106 → 110.

Codex xhigh design review ran before implementation (verdict:
RECOMMENDED_ALTERNATIVE: 2, priority 9/10). Branch-awareness
alternative rejected — false-unblocks trunk-based workflows.

* chore: record E2E score [skip ci]

* test(hooks): add zero-stderr + gh-missing assertions (PR #206 Codex R1)

* chore: record E2E score [skip ci]

* docs(sdlc,ci): require Codex xhigh audit on CI logs in shepherd loop

* chore: record E2E score [skip ci]

* docs(roadmap): file #210 Node24 false-green + #211 tier1 11/10 (Codex CI-log audit on #206)

* chore: record E2E score [skip ci]

* docs(roadmap): add #212 local-Max E2E shepherd (zero-API alt)

* docs(sdlc): run Codex audit on Tier 1 AND Tier 2 CI logs separately

* docs(roadmap): #213 CLI template env-block gap — adaptive thinking + autocompact vars documented but not shipped

* docs(roadmap): #214 Prove-It A/B for adaptive thinking; gate #213 on result

* docs(roadmap): #215 Tier 2 persist step is dead code (Codex Tier 2 audit on #206)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
BaseInfinity added a commit that referenced this pull request May 5, 2026
Bug hit live 2026-04-19 after PR #205 merged — `.reviews/handoff.json`
stayed at PENDING_RECHECK, every subsequent /compact got blocked by the
user's own stale review artifact. Ships to consumers via CLI + plugin,
so every adopter of the handoff protocol who forgets to flip status
after merge will hit the same wall.

Fix: when status is PENDING_REVIEW/PENDING_RECHECK, parse optional
pr_number from handoff. If present AND gh is available, query
`gh pr view <pr_number> --json state` — MERGED unblocks (implicit
CERTIFIED). Missing pr_number, missing gh, offline, or any error
falls through to existing block (safe default).

4 new tests with mocked gh binary: merged unblocks, open blocks,
no pr_number blocks, gh-errors blocks. Hook suite 106 → 110.

Codex xhigh design review ran before implementation (verdict:
RECOMMENDED_ALTERNATIVE: 2, priority 9/10). Branch-awareness
alternative rejected — false-unblocks trunk-based workflows.
BaseInfinity added a commit that referenced this pull request May 5, 2026
…252)

ROADMAP #205 phase (a): setup wizard scans for AGENTS.md (cross-tool
agent-instructions standard, CC #6235) and surfaces a 3-way decision
(dual-maintain default / merge / skip) when found.

Phase (a) scope is detection + decision recording ONLY:
- No wizard-side merge or symlink behavior
- Option B = "record intent, copy by hand"; phase (b) adds the copy helper
- Choice recorded as one-line SDLC.md comment for user reference;
  /update-wizard does NOT yet parse it (phase d)

Codex round 1: 6/10 NOT CERTIFIED. 3 P1 findings:
  - F-001: SDLC.md metadata persistence claim was false (no actual
    wiring). Weakened to user-reference comment only.
  - F-002: Option B wording said "copy CLAUDE.md content" which is
    merge behavior contradicting "detection-only" scope. Reworded
    to manual-intent-recording only.
  - F-003: Test 5 false-greened on broad "cross-tool" matches
    elsewhere in wizard. Scoped to extracted AGENTS.md subsection.

Round 2: 10/10 CERTIFIED. Mutation re-verified.

7 quality tests in tests/test-agents-md-interop.sh.
BaseInfinity added a commit that referenced this pull request May 5, 2026
* docs(handoff): document pr_number self-heal opt-in (#209, v1.42.2)

ROADMAP #209 closure. The precompact-seam-check.sh hook self-heals on
PENDING_* handoffs whose linked PR has merged: when handoff has pr_number
and `gh pr view N --json state` returns MERGED, hook treats handoff as
implicit CERTIFIED and unblocks /compact silently. Self-heal logic shipped
earlier alongside #229 (stale-expire fallback) but was undocumented in
the handoff template schemas, leaving the path effectively dead code on
fresh installs (consumers had no way to discover the opt-in).

Documented pr_number in all 3 handoff templates:
- skills/sdlc/SKILL.md (Step 1: Mission-First Handoff) + key-fields prose
- CLAUDE_CODE_SDLC_WIZARD.md (Round 1: Initial Review)
- CLAUDE_CODE_SDLC_WIZARD.md (cross-model review section)

New regression test test_handoff_template_documents_pr_number in
tests/test-hooks.sh enforces template/doc parity going forward — a
future schema edit that drops pr_number will fail this test.

Together with #229 (mtime auto-expire) closes the "stuck PENDING handoff
blocks /compact forever" footgun from both directions: PR-linked reviews
self-heal on merge (instant), unlinked reviews auto-expire on mtime
(14d default).

Hit live in this repo 2026-04-19 (PR #205) and 2026-04-26 (PR #253)
where handoffs lacked pr_number and fell through to the stale-expire
fallback.

Tests: 130 hook tests passing (1 new), 10 adjacent test files green.

* fix(handoff-test): round-2 — coverage + prose + count (#255)

Codex round-1 review (6/10 NOT CERTIFIED) found 3 issues:

1. P1: test_handoff_template_documents_pr_number false-greened on
   single-schema deletion. Required ANY occurrence per file, but
   wizard has 2 schemas — removing pr_number from one left the other
   intact and test passed.

   Fix: count handoff schemas via `"status": "PENDING_REVIEW"` marker
   (response.json uses PENDING_RECHECK so no collision). Assert
   pr_number occurrence count >= handoff count per file. Mutation
   re-verified: deleting pr_number from any of the 3 schemas now
   flips test to FAIL with skill-coverage(0/1) or wizard-coverage(1/2).

   Discovered separate grep -c quirk: `|| echo 0` after grep -c with
   no matches produces '0\n0' (grep -c always prints a number AND
   exits 1; || appends another 0). Switched to `|| true` + `${var:-0}`
   defaults — preserves count, discards failing exit, set -e safe.

2. P2: cross-model schema had pr_number field but no nearby prose
   explanation. Round 1 schema's inline explanation wasn't mirrored.

   Fix: added paragraph at CLAUDE_CODE_SDLC_WIZARD.md:3709 explaining
   when to set pr_number, what precompact-seam-check.sh does with it,
   when to omit.

3. P2: CHANGELOG overstated hook test count (130 vs actual 129).

   Fix: corrected to "129 hook tests, 1 new".

All 3 mutation paths verified catching:
  - skill schema delete → FAIL skill-coverage(0/1)
  - wizard cross-model delete → FAIL wizard-coverage(1/2)
  - wizard Round 1 delete → FAIL wizard-coverage(1/2)

Adjacent regression: 10 test files green (test-hooks 129/0 + 9 others).
BaseInfinity added a commit that referenced this pull request May 5, 2026
#289)

Three items quietly shipped pre-2026-04-29 but never had ROADMAP marked
done:

- #232 (`/update-wizard` CLI freshness mimicking `claude update`):
  shipped v1.40.0. skills/update/SKILL.md Step 1.5 detects installed
  CLI version (npm ls -g + npx cache, semver-aware), compares to
  registry.npmjs.org, surfaces 3 upgrade options (refresh cache /
  one-shot init --force / skip), honors check-only precedence. 8
  tests in test-update-skill-cli-version.sh.

- #205 phases (a)+(c) (AGENTS.md interop): shipped v1.42.0. setup
  skill Step 4.5 detects AGENTS.md and offers dual-maintain / merge
  / skip. Phases (b) auto-generate-AGENTS.md and (d) drift-test
  remain deferred ("agent-agnostic SDLC is on the back burner").

- #90 (Distribution Channels): duplicate entry — already marked DONE
  at line 196 of ROADMAP. Removed the duplicate at line 239.

No code changes — ROADMAP-only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant