Conversation
`reportDocsReach` bucketed every docs call by whether it carried a page, and Codex's `web_search` never carries one: the search is hosted, so the hits reach the model on the provider's side and the CLI receives nothing. `buildDocsResult` already records that honestly — no pages, `hasContent` unknown, `resultChars` omitted — and the reporter read the absence as a result. It was wrong in both directions. On the 14 September snapshot all 254 Codex searches were unobserved: 216 counted as empty and 38 counted as page reads, the latter because a url-shaped query is recorded as the page it probably opened. The transcripts show the "empty" ones working — between them the agent narrates finding the versioned API base and the retry endpoint, then calls both correctly. `resultChars` is the discriminator rather than `pages`, because it separates a result we saw and that was empty from no result to see. Closes #83. #61's headline rests on the old bucket and needs re-deriving. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two buckets folded three different things into one number, and the reporter drew a conclusion from it out loud: "a high empty rate is an external search index returning nothing". There are four. Only `read` means page text reached the agent. `hits` is a list of links, which is what Claude's WebSearch returns and what ten of that arm's forty-six credited page reads actually were. `none` is a result we saw that carried nothing. `unobserved` is a hosted search whose hits go to the model on the provider's side and never reach the CLI — all 254 Codex searches in the 14 September snapshot, of which 216 were counted as failures and 38 as page reads. `--queries` prints what each arm asked and what came back, grouped by scenario so the two arms of a pair sit together. For a hosted search the query is the only observable there is, and it is the only way to ask what an agent was trying to find out: `verification-002`'s baseline searched twice for an ElevenLabs source type and then built a generic WEBHOOK source with hand-rolled HMAC, while the arm with the skill named the preset and passed. Closes #83. #61's headline rests on the old bucket and needs re-deriving. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The task
#83 — docs-call reporting counts results we never saw as evidence. Widened, on
review, to cover the other half of the same error and to make the observable part
of a search available for analysis.
What changed
apps/framework/lib/docs-reach.ts— one pure function classifying a docs callfour ways, and a line of prose per bucket for whatever prints them.
readweb_fetch,shell_fetchhitsWebSearchnoneunobservedweb_searchTwo fields do the work, and both are needed.
resultCharsseparates we saw aresult and it was empty (
0) from there was no result to see (omitted).hasContentseparates page text from a list of links —docs-results.tsalreadysets it to
falsefor Claude'sWebSearchfor exactly this reason.--queriesonreport-resultsprints what each arm asked and what came back,grouped by scenario so both arms of a pair sit together.
The error, in both directions and both agents
On
results/runs/2026-09-14T10-36-39-000Z.json:search that failed — and 38 as reached a page, because a url-shaped query is
recorded as the page it probably opened.
Titles and urls. It was credited for seeing a list of links.
noneis zero across this snapshot. The bucket is still right to keep — aWebSearchwith no hits lands there — it just says nothing here was an observedfailure, which is the opposite of what we published.
Why the queries view earns its place
For a hosted search the query is the only observable there is, and it turns out to
be the half that answers the question.
verification-002, 14 September:It then built a generic
WEBHOOKsource with hand-rolled HMAC againstElevenLabs-Signatureand failed the first check. The arm with the skill ran--source-type ELEVENLABSand passed. The pass rate cannot show that; two queriesand their outcome can.
Off by default, because it is long.
Why widen rather than file a follow-up
The
hitsover-credit is the same defect as theunobservedone — crediting thereporter's assumption instead of an observation — in the same function, on the same
line. Splitting it across two PRs would have meant changing that line twice and
leaving a published number wrong in between.
Verified
Plus both report modes against a real snapshot, output above. Nothing was already
failing.
pnpm installneeds/opt/homebrew/binahead of the asdf shims.Tests
apps/framework/test/docs-reach.test.ts, seven cases, in the place this repoalready puts them —
test/againstlib/, never againstscripts/, which is whythe classifier is a lib module rather than a private function in the script.
The two that matter are the ones that were published wrong: a hit list is not a
read, and a url-shaped query with no result is not a page read.
Not done
returns nothing", 58% — rests on the old bucket. Re-deriving it is work on that
issue, which also has a second half this does not touch: the two arms really do
differ in tool mix. Commented there.
docs.callsis diagnostic; the scoreboard doesnot read it.
--queriesprints every call rather than sampling. It is opt-in and piped togrepin practice; a filter is easy to add if anyone wants one.provider, so there is nothing to capture. That is the limitation this makes
visible rather than one it fixes.