#3354 · playground · 2026-08-11

The directory was empty because nobody could reach it.

#3354 was filed as "delete 3 writers to a directory with 0 readers". The readers really are zero. But the directory is not empty because handoffs are useless: all three hooks matched BARE agent names while CC sends ork:-prefixed ones, so the writers were unreachable and the code behind them had never run.

The evidence, from 21,759 rows of real telemetry

~/.claude/analytics/agent-usage.jsonl

bare names        (what every matcher tested)          1 row
ork:-prefixed     (what CC actually sends)         1,000 rows

And normalizeAgentName already existed at lib/agent-attribution-types.ts:35. These three hooks simply never called it.

What the fix restored, not just removed

auto-spawn-quality: 0 "Rule matched" across a 1.0MB log
                    queueSpawn -> spawn-queue.json  NEVER created
                    systemMessage                    NEVER emitted

DoD evidence: the same payload against two bundles

Deleting never-executed code cannot produce a failing-then-passing test, so the honest measurement drives run-hook.mjs with the payload CC really sends (subagent_type: "ork:test-generator") against the shipped bundle and this branch's.

bundlespawn-queue.jsonRule matchedhandoffs dir
shipped alpha.17 (pre-fix)NOT created0absent
this branchCREATED1absent

The last column is the second half of the change: the deleted writes do not recreate the directory.

Deletions, split by purpose rather than uniformly

hookactionwhy
handoff-preparerwhole hook removedits only two side effects were the handoff JSON and a log describing that JSON, neither with a reader. Nothing survives a surgical edit.
feedback-loopwrites onlywriteDecision, task status and systemMessage are separate decisions; the routing choice is still recorded.
auto-spawn-qualitywrites onlyqueueSpawn + systemMessage are its real product, and they now fire.

What was deliberately NOT touched

lib/atomic-write.ts                  many other callers
~/.claude/analytics/handoffs/        LIVE (session-handoff-injector, handoff-claim)
.claude/design-handoffs/             LIVE (posttool/design-import/auto-verify)
.claude/logs/agent-handoffs/         separate namespace

a bare `handoffs` grep spans all four -> only `context/handoffs` was swept

Tests: retargeted, not deleted

12 files asserted the removed hook

Registration assertions (async flag, parallel execution, error logging, dispatcher order) were retargeted to feedback-loop so the invariants stay pinned rather than vanishing with the hook. Only the assertions on the deleted writes were retired, each with a note naming why. Counts moved by exactly one and were re-stamped: async 107 to 106, global 151 to 150, total 217 to 216.

Verification

typecheck                clean
hook TS suite            309 files / 7,514 passed
validate-counts          exit 0
test:manifests           exit 0
security suite           exit 0
hooks + plugin build     clean
net                      47 files, -1,325 lines

Found in the same files, filed separately, not fixed here. handoff-preparer and feedback-loop were each registered TWICE, as standalone hooks.json entries and as elements of unified-dispatcher's array, so both bodies ran twice per SubagentStop. Pre-existing, and a behavior change to measure on its own rather than ride along with a deletion.