You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
formatter hooks: nine siblings duplicate the PostToolUse prologue as structure the drift gate cannot see (deepening candidate, top recommendation) #3408
Nine sibling hooks (go-format, ruff-format, biome-format, bash-format, powershell-format, markdown-format, typos-format, eol-normalizer, actionlint) each re-implement the same PostToolUse prologue: kill-switch, EPOCHREALTIME guard, telemetry emit, stdin buffer (bare cat, never </dev/stdin>), jq-free pre-filter, require_jq, repo-root plus cygpath FILE_REL normalization, build_data_json, emit_skipped. One conceptual change to that prologue costs nine edits with no locality and no gate.
Measured (scan, 2026-08-27): normalized (comments stripped, plugin name tokenized), go/ruff/biome/bash/powershell share 45 code lines, go/ruff/biome share 61, out of 133/142/139 total non-comment lines, i.e. roughly 46% of go-format's code is shared skeleton. The EPOCHREALTIME guard and the fd0 comment appear in 9 of 9 hooks (reproduced by grep before filing).
ADR 0019's drift mechanism cannot reach this: scripts/check-cross-plugin-source-drift.sh clusters by whole-file byte identity at the same path-within-plugin, and scripts/cross-plugin-source-registry.txt lists 5 clusters, none of which can cover a prologue interleaved inside files that legitimately differ. Nothing detects its drift, and the fix history shows the cost: the same class of fix lands one plugin at a time (#3401 powershell, #3394 go-format telemetry, #3405/#3406 still open).
This was the scan's top recommendation because three sibling candidates are facets of the same missing module: the mutation-disclosure protocol (filed separately, with live defects #3405 and #3406), the envelope-schema check, and the triple applicability test.
Proposed work
Extract the prologue into one vendored module using the existing ADR-0019 cluster mechanism (canonical file, sync script, drift-gate registry entry), so each hook keeps only formatter-specific behavior behind the shared interface. Vendored copies keep per-run cost unchanged, honoring .claude/rules/hook-budget.md.
One suite over a fake formatter on PATH asserting the prologue contract once: non-matching extension produces no jq notice and no telemetry; jq absent produces exactly one skip notice; EPOCHREALTIME unset still formats and emits no envelope; unwired HOOK_TELEMETRY_SINK spawns zero telemetry-only subprocesses; data.file is repo-relative under both POSIX and a cygpath-shaped root.
Acceptance criteria
The shared prologue lives in one canonical file registered in cross-plugin-source-registry.txt and synced by the ADR-0019 mechanism.
Each of the nine hooks consumes the vendored prologue; formatter-specific code remains per plugin.
The prologue contract is asserted by one suite instead of nine partial transcriptions.
Hook latency budget is unchanged (no extra subprocess or file sourced at runtime beyond the vendored copy).
Found by the /architecture:improve deepening scan (2026-08-27); shallow-signal reproduced before filing. Dependency category: local-substitutable. Recommendation: Strong.
Context
Nine sibling hooks (
go-format,ruff-format,biome-format,bash-format,powershell-format,markdown-format,typos-format,eol-normalizer,actionlint) each re-implement the same PostToolUse prologue: kill-switch,EPOCHREALTIMEguard, telemetry emit, stdin buffer (barecat, never</dev/stdin>), jq-free pre-filter,require_jq, repo-root plus cygpathFILE_RELnormalization,build_data_json,emit_skipped. One conceptual change to that prologue costs nine edits with no locality and no gate.Measured (scan, 2026-08-27): normalized (comments stripped, plugin name tokenized), go/ruff/biome/bash/powershell share 45 code lines, go/ruff/biome share 61, out of 133/142/139 total non-comment lines, i.e. roughly 46% of go-format's code is shared skeleton. The
EPOCHREALTIMEguard and the fd0 comment appear in 9 of 9 hooks (reproduced by grep before filing).ADR 0019's drift mechanism cannot reach this:
scripts/check-cross-plugin-source-drift.shclusters by whole-file byte identity at the same path-within-plugin, andscripts/cross-plugin-source-registry.txtlists 5 clusters, none of which can cover a prologue interleaved inside files that legitimately differ. Nothing detects its drift, and the fix history shows the cost: the same class of fix lands one plugin at a time (#3401 powershell, #3394 go-format telemetry, #3405/#3406 still open).This was the scan's top recommendation because three sibling candidates are facets of the same missing module: the mutation-disclosure protocol (filed separately, with live defects #3405 and #3406), the envelope-schema check, and the triple applicability test.
Proposed work
.claude/rules/hook-budget.md.EPOCHREALTIMEunset still formats and emits no envelope; unwiredHOOK_TELEMETRY_SINKspawns zero telemetry-only subprocesses;data.fileis repo-relative under both POSIX and a cygpath-shaped root.Acceptance criteria
cross-plugin-source-registry.txtand synced by the ADR-0019 mechanism.References
plugins/{go,ruff,biome,bash,powershell,markdown,typos}-format/hooks/*.sh,plugins/eol-normalizer/hooks/eol-normalizer.sh,plugins/actionlint/hooks/actionlint-check.shscripts/check-cross-plugin-source-drift.sh,scripts/cross-plugin-source-registry.txt,docs/adr/0019-share-code-across-plugins-by-vendoring-with-a-sync-gate.md,.claude/rules/hook-budget.md/architecture:improvedeepening scan (2026-08-27); shallow-signal reproduced before filing. Dependency category: local-substitutable. Recommendation: Strong.