Legacy-debris sweep: what the purge never covered
ork 2026-09-01 · after the #3835 blocking-hook purge (176 to 172), an outside-in reachability sweep of every "legacy" and "deprecated" marker in the hook tree
The question that triggered it: "why do we support legacy shit?" Answer: we should not, and mostly we did not, but four remnants and two lying comments survived the purge because they were never registrations, so no registration-counting audit could see them.
The sweep, item by item
profileInjector(): a @deprecated stub returning silent success, still wired in the
entries/prompt.ts map. No hooks.json entry ever dispatched it. The live path,
materializeProfileRules() at SessionStart, is untouched.
DELETED
CC < 2.1.88 relative-path compat branch in project-write-retry.ts. The support
floor is 2.1.251; file_path is guaranteed absolute. A version-floor branch 163 releases stale.
DELETED
The legacy name worktree-slug field on HookInput. WorktreeCreate is not a
registered event here and zero production readers existed (typecheck arbitrated).
FIXED
entries/prompt.ts labeled thrash-detector "Legacy... not in hooks.json" while it IS
registered on UserPromptSubmit. An inverted comment that misled this sweep's own explorer.
FIXED
The wave-3 registry changelog said "hence 176 to 175" in its body against a "175 to 174"
heading (the body figure was wave 2's move).
FILED
generate-http-hooks.ts (deprecated CLI, zero HTTP hooks shipped) still has entry
points: a package.json script and doc references. Deleting it is #3867, an EPIC A child,
because a completeness claim needs every consumer surface enumerated first.
KEPT
Live legacy fallbacks that earn their keep: the redact-secrets alias reads,
mcp-output-transform's documented synonym, the osascript notification path behind an env
opt-in, self-pruning state migrations. Reachable code with users is not debris.
Why the purge could not see these
The #3835 purge audited registrations: 29 live blocking hooks, 16 deleted, counts re-stamped at every step. These remnants were never registrations. A dead map slot, a stale version branch, an unused type field, and a wrong comment all sit below the registration layer, so the registration audit was structurally blind to them.
registration layer hooks.json (172) audited by #3835, clean dispatch layer entries maps (184->183) this sweep: 1 dead slot code layer branches + fields this sweep: 2 dead branches prose layer comments + changelog this sweep: 2 inversions
The rule the sweep followed
"Establish reachability from the outside: search for imports of the module and follow the chain up to a route, an entry point, or a registration site that is itself reached. Do this before reviewing the file's contents." (Cross-Session Engineering Patterns, EP-109; a file's own "deprecated" header is the weakest evidence of its state.)
Verification: full hook suite 6629 passed 0 failed, typecheck clean, counts gate PASSED,
and profile-injector grep-confirmed absent from both shipped prompt.mjs bundles
(the deletion has to land in the artifact, not just the source).