Files
Claude 5d4d877608 fix(harness): parse every content block, not just text
`flattenContent` read `b.text` and returned "" for anything else. Eight of the
sixteen `ContentBlockParam` variants carry their payload in `content`, so half
the union was invisible to every instrument built on `extractRequest` —
`requestContains`, refs-nudge, injectable-events-delivery.

What that cost. Claude Code <= 2.1.227 delivered a PostToolUse hook's
additionalContext as its own `text` block; from 2.1.228 (a PATCH) it arrives
appended to the `tool_result` block's `content` inside a <system-reminder>.
Both deliver. Our probe went blind, three tests reported a false negative, and
that reading was written up as #231 and nearly filed upstream as a regression
in somebody else's product.

Proven by changing only the parser on one machine at claude 2.1.267:
blind = "landed=false", typed = "landed=true". Both harnesses now green on
2.1.267 with no change to `injectableEvents`.

The guard is a typed exhaustive switch over the union from @anthropic-ai/sdk
(MIT, types only, devDependency — erased at compile time, never in a consumer's
tree). Mutation-proven: deleting the `tool_result` case fails tsc with
"Type 'ToolResultBlockParam' is not assignable to type 'never'". An unknown
block serialises rather than vanishing — this is a measurement instrument and
its proven failure mode is the false negative.

mock-model.test.ts asserted the blind spot as INTENDED ("no `text` -> ''"),
which is why no run could find it; that assertion is now inverted and a
newer-API block type is asserted as over-included.

Refs #231.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 16:12:03 +00:00
..