feat(rewrite-guard): deepen the content-mutation disclosure protocol into a shared vendored lib - #3452
Conversation
…into a shared vendored lib One implementation of the snapshot/compare/disclose/release protocol every mutating formatter hook previously hand-rolled: new ADR-0019 cluster hooks/rewrite-guard.sh (canonical lib/rewrite-guard.sh with a 13-case contract suite, sync gate scripts/sync-rewrite-guard.sh, registry row, rewrite-guard-sync CI job), adopted by go-format, ruff-format, biome-format, bash-format, powershell-format and eol-normalizer. Closes #3405: go-format released its snapshot only on the clean arm; the guard arms an EXIT trap so the release is structural on every arm, and the cp-failure mktemp orphan all six hand-rolled copies leaked is removed. Closes #3406: ruff/biome/bash emitted the rewrite disclosure as its own JSON document, so a rewrite-plus-findings run printed two documents; the disclosure is now taken at each exit arm and composed into that arm's single document. New rewrite-plus-findings suite cases assert exactly one document carrying both channels; the old hooks were mutation-checked to fail these assertions. Part of #3409 (the first vendored slice of #3408). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TwGsRVvv8izGrnpQXnDB4B
|
Claude finished @kyle-sexton's task in 2m 40s —— View job Code review —
|
|
Claude finished @kyle-sexton's task in 1m 56s —— View job Security review — PR #3452
Scope: reviewed the full diff ( No security issues found. Notes from the review (informational, not findings):
|
…ck on the new fixtures The plugin-gate lane runs ShellCheck at default severity (info included), stricter than the -S warning used during development. Two info findings on the PR's new test code: SC2016 on the rewrite+findings fixture (the literal $undefined_variable is deliberate - it is what makes the hook's ShellCheck pass report SC2154), silenced with a scoped directive per suite precedent; and SC2015 in lib/rewrite-guard.test.sh's jq-absent fallback, restructured as if/else. Refs #3452. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TwGsRVvv8izGrnpQXnDB4B
|
Last security-reviewed head: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 830b52a3a2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Claude has reviewed this PR 4 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
|
Claude finished @kyle-sexton's task in 2m 2s —— View job Security review complete
No security issues found. This PR centralizes a content-mutation disclosure protocol (snapshot → compare → disclose → release) that six formatter hooks previously hand-rolled, into one vendored lib (
Traced the logic for all six hooks' exit arms (go, ruff, biome, bash, powershell, eol-normalizer) and confirmed each This is an internal dev-tooling hygiene refactor with no change to what's trusted, no new attack surface, and no security-relevant behavior change — findings are all pre-existing correctness/architecture ones already called out in the PR description, not new to this review. |
|
Claude finished @kyle-sexton's task in 3m 6s —— View job Claude finished the code review
|
…placing it Codex P2 on #3452, verified: bash `trap` on a signal replaces the prior handler wholesale, so begin's release trap silently disabled any EXIT cleanup the caller had armed - demonstrated by the lib's own test suite, whose workspace cleanup trap was clobbered and leaked one /tmp/rewrite-guard-test-* directory per run. begin now captures the previous EXIT handler (trap -p, frame stripped, shell-quoting evaled back) and the guard's named handler runs it after the release; a second begin recognizes its own handler and keeps the original chain instead of chaining to itself. Two new contract cases lock both behaviors; the suite-workspace leak is gone. Copies synced to the six carrying plugins; hook behavior is unchanged (none arms its own EXIT trap). Refs #3452. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TwGsRVvv8izGrnpQXnDB4B
|
Claude finished @kyle-sexton's task in 1m 11s —— View job Security review — PR #3452
Skip gate: PR is open. Per the auto-comment above, the last security review covered head Diff reviewed: No security issues found. What changed and why it doesn't open new surface:
This confirms the conclusion of the three prior review rounds on this PR: no security-relevant surface, now including the trap-chaining fix. |
|
Claude finished @kyle-sexton's task in 2m 30s —— View job Code review —
|
|
Claude finished @kyle-sexton's task in 36s —— View job Security review — PR #3452
Skip gate: the last security review on this thread covered head Per the skip gate in |
|
Claude finished @kyle-sexton's task in 47s —— View job Code review —
|

Closes #3405, closes #3406, closes #3409
Summary
Six mutating hook plugins each hand-rolled the content-mutation disclosure protocol (snapshot, compare, disclose, release), and the copies drifted defect by defect: go-format leaked its snapshot on both error arms (#3405) and ruff/biome/bash printed the rewrite disclosure as a second JSON document on rewrite-plus-findings runs (#3406). This PR deepens the protocol into one vendored module and adopts it in all six hooks, closing both defect classes through the shared interface.
Fix
hooks/rewrite-guard.sh(canonicallib/rewrite-guard.sh, sync gatescripts/sync-rewrite-guard.sh, registry row, CI jobrewrite-guard-sync). Three entry points:hook::rewrite_guard_begin(best-effort snapshot; arms an EXIT trap so release is structural on every arm, including the cp-failure orphan every hand-rolled copy leaked),hook::rewrite_take_disclosure(destructive read: setsHOOK_REWRITE_MESSAGE, releases the snapshot; callers pass it as the systemMessage of their onehook::emit_channelscall), andhook::rewrite_disclose(take-and-emit for arms with no context of their own).EFFECTIVE_ACTIONverdict).Verification
bash lib/rewrite-guard.test.sh— 13/13 (contract: release on every arm incl. non-zero exits, destructive read, no orphan on failed snapshot, single-document composition).TMPDIRfor the syntax-error and tool-break arms), ruff-format 56/56, biome-format 46/46, bash-format 51/51 (each with a rewrite-plus-findings case asserting exactly one JSON document viajq -s), powershell-format 84/84 (full behavioral run with PSScriptAnalyzer), eol-normalizer 36/36.scripts/affected-tests.sh --run— 121 shell suites passed; the selected Python and Node lanes run green locally (the knowledge vitest suite needs its own lane's deps and is untouched by this change).scripts/sync-rewrite-guard.sh --check/--check-bump,scripts/sync-hook-utils.sh --check,scripts/check-cross-plugin-source-drift.sh --check,scripts/check-changelog-parity.sh --check+--check-bump+--check-order,actionlint,shellcheck,shfmt -d,typos,markdownlint-cli2— all clean, re-verified after mergingorigin/main(6ec8455).Related
/architecture:improvedeepening scan; candidate record in the topic's memory slice.🤖 Generated with Claude Code
https://claude.ai/code/session_01TwGsRVvv8izGrnpQXnDB4B
Generated by Claude Code