One commit. Server-side only — no plugin change, so no manifest mint and nothing about how the plugin reaches a session moves.
The problem
retrieval_telemetry returned distributions and left the reading to the caller, so every readout was the same four checks done by hand — #3430's baseline, #3835's rule near-misses, the #1038 rerank gate. Mechanical, and therefore forgettable.
Tonight's acceptance pass on #3898 is the case for fixing it. Reading that window by hand meant catching three separate things: two surfaces with covers_window: false; prompt_rule's floor having moved three times inside the window, which made the readout self-contradictory (deliveries at 0.622 beside refusals at 0.7199); and 15 of 20 near-misses being one record matched against text no operator wrote. Miss any one and the obvious conclusion was "the bar is too tight" — a floor change that would have injected a single preference into every task notification.
What it adds
warnings, always present and empty when clean, so its emptiness is an answer rather than a gap. Each entry carries the numbers that produced it: "345 calls, 0 declined" is the analysis, "check write_path_rule" is an instruction to redo it.
cannot_decline — an unbidden arm that answered every call. Three exemptions, each a bug it would otherwise cause: asked surfaces (a search returning a list every time is working), unregistered sources (nothing says which kind they are), and arms not known to log unconditionally — that last one is #3497 exactly, where both rule arms recorded only their hits, so a decline count of zero was a logging defect and this warning would have pointed the reader at a threshold that was never involved.
band_hugs_floor — the weakest tenth sitting on the bar, so tuning moves volume rather than quality.
no_duration — rows written without timings. A logging gap, not a slow arm.
surfaced_never_pulled — distinct records shown and never opened, per corpus.
unregistered_source — rows under a source missing from the registry.
silent_surfaces is the half the rows cannot show: an arm that emitted nothing is invisible to every row-based check and looks identical to an arm that does not exist.
The registry, and why it is a second one
New services/retrieval_registry.py, 33 points. Deliberately notretrieval_surfaces.SURFACES, which answers "what can be TUNED" and excludes the reserved slots because a budget of 1 is their feature. This answers "what can be MEASURED", and the reserved slots belong in it precisely because they are judgeable without being tunable. One is a control panel, the other an inventory; a test asserts they cannot drift.
Its test derives sources from the call sites with ast rather than grep, and the difference is load-bearing: wide_net and report_preference reach their recorder as source=SOURCE through a module constant, so a grep for source=" is blind to both — the narrowing #3191 warns about. Three sites pass source as a variable and are declared in FAN_OUT_SITES; the test pins those sites but not the values they can pass, which is stated in the code and is why unregistered_source exists to catch the rest at first fire.
Verification
CI run 7086 green on e2c3a5c.
Locally, the warning logic is pure by design, so it was exercised directly against the real registry: 24/24 including a negative control asserting a healthy window across all 33 points yields exactly zero warnings — a guard that cannot pass cleanly is not a guard.
The registry guard failed first, on a real defect in itself.FAN_OUT_SITES keys were spelled services/... while the extractor emits paths relative to src/, so the startswith compared against a prefix that could never match and the check passed by matching nothing. Now keyed by exact string and matched by equality, with the near-miss recorded inline.
Note for after the merge
This needs a deploy to reach the running instance. Unrelated to it, migration 0105 has been on main since PR #170 and applies on the same deploy if that one was not taken.
One commit. Server-side only — no plugin change, so no manifest mint and nothing about how the plugin reaches a session moves.
## The problem
`retrieval_telemetry` returned distributions and left the reading to the caller, so every readout was the same four checks done by hand — #3430's baseline, #3835's rule near-misses, the #1038 rerank gate. Mechanical, and therefore forgettable.
Tonight's acceptance pass on #3898 is the case for fixing it. Reading that window by hand meant catching three separate things: two surfaces with `covers_window: false`; `prompt_rule`'s floor having moved **three times inside the window**, which made the readout self-contradictory (deliveries at 0.622 beside refusals at 0.7199); and 15 of 20 near-misses being one record matched against text no operator wrote. Miss any one and the obvious conclusion was "the bar is too tight" — a floor change that would have injected a single preference into every task notification.
## What it adds
`warnings`, always present and empty when clean, so its emptiness is an answer rather than a gap. Each entry carries the numbers that produced it: "345 calls, 0 declined" is the analysis, "check write_path_rule" is an instruction to redo it.
- **cannot_decline** — an unbidden arm that answered every call. Three exemptions, each a bug it would otherwise cause: asked surfaces (a search returning a list every time is working), unregistered sources (nothing says which kind they are), and arms not known to log unconditionally — **that last one is #3497 exactly**, where both rule arms recorded only their hits, so a decline count of zero was a *logging* defect and this warning would have pointed the reader at a threshold that was never involved.
- **band_hugs_floor** — the weakest tenth sitting on the bar, so tuning moves volume rather than quality.
- **no_duration** — rows written without timings. A logging gap, not a slow arm.
- **surfaced_never_pulled** — distinct records shown and never opened, per corpus.
- **unregistered_source** — rows under a source missing from the registry.
`silent_surfaces` is the half the rows cannot show: an arm that emitted nothing is invisible to every row-based check and looks identical to an arm that does not exist.
## The registry, and why it is a second one
New `services/retrieval_registry.py`, 33 points. Deliberately **not** `retrieval_surfaces.SURFACES`, which answers "what can be TUNED" and excludes the reserved slots because a budget of 1 is their feature. This answers "what can be MEASURED", and the reserved slots belong in it precisely because they are judgeable without being tunable. One is a control panel, the other an inventory; a test asserts they cannot drift.
Its test derives sources from the call sites with `ast` rather than grep, and the difference is load-bearing: `wide_net` and `report_preference` reach their recorder as `source=SOURCE` through a module constant, so a grep for `source="` is blind to both — the narrowing #3191 warns about. Three sites pass `source` as a variable and are declared in `FAN_OUT_SITES`; the test pins those sites but **not** the values they can pass, which is stated in the code and is why `unregistered_source` exists to catch the rest at first fire.
## Verification
CI run 7086 green on `e2c3a5c`.
Locally, the warning logic is pure by design, so it was exercised directly against the real registry: 24/24 including a negative control asserting a healthy window across all 33 points yields exactly zero warnings — a guard that cannot pass cleanly is not a guard.
**The registry guard failed first, on a real defect in itself.** `FAN_OUT_SITES` keys were spelled `services/...` while the extractor emits paths relative to `src/`, so the `startswith` compared against a prefix that could never match and the check **passed by matching nothing**. Now keyed by exact string and matched by equality, with the near-miss recorded inline.
## Note for after the merge
This needs a deploy to reach the running instance. Unrelated to it, migration `0105` has been on `main` since PR #170 and applies on the same deploy if that one was not taken.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
The tool returned distributions and left the reading to the caller, so every
readout was the same four checks done by hand — #3430's baseline, #3835's rule
near-misses, the #1038 rerank gate. Mechanical, and therefore forgettable.
Tonight's acceptance pass on #3898 was the case for doing this. Reading it by
hand meant catching that two surfaces had `covers_window: false`, that
`prompt_rule`'s floor had moved three times inside the window (which made the
readout self-contradictory: deliveries at 0.622 beside refusals at 0.7199),
and that 15 of 20 near-misses were one record against text no operator wrote.
Miss any of those and the obvious conclusion was "the bar is too tight" — a
floor change that would have injected one preference into every notification.
`warnings` is always present and empty when clean, so its emptiness is an
answer rather than a gap. Each entry carries the numbers that produced it:
"345 calls, 0 declined" is the analysis, "check write_path_rule" is an
instruction to redo it. Five codes — cannot_decline, band_hugs_floor,
no_duration, surfaced_never_pulled, unregistered_source.
cannot_decline has three guards, each a bug it would otherwise cause. Asked
surfaces are exempt (a search returning a list every time is working). An arm
not known to log unconditionally is exempt — that is #3497 exactly, where both
rule arms recorded only their hits, so a decline count of zero was a LOGGING
defect and this warning would have sent the reader to a threshold that was
never involved. Unregistered sources get numbers but no verdict.
`silent_surfaces` is the half the rows cannot show: an arm that emitted
nothing is invisible to every row-based check and looks exactly like an arm
that does not exist. It is driven by a new declared registry,
`retrieval_registry.POINTS` — deliberately NOT `retrieval_surfaces.SURFACES`,
which answers "what can be tuned" and excludes the reserved slots because a
budget of 1 is their feature. This answers "what can be measured", and the
reserved slots belong in it precisely because they are judgeable without being
tunable. A test asserts the two cannot drift apart.
The registry test derives sources from the call sites with `ast`, not grep,
and the difference is not theoretical: `wide_net` and `report_preference`
reach their recorder as `source=SOURCE` through a module constant, so a grep
for `source="` is blind to both — the narrowing #3191 warns about. Three sites
pass `source` as a variable and are declared in FAN_OUT_SITES; the test pins
those sites but not the values they can pass, which is why the
`unregistered_source` warning exists to catch the rest at first fire.
Thresholds are settings (rule 25) defaulted so a fresh install with almost no
data produces no warnings at all (rule 115) — a new user's first readout
naming five broken things would be describing the emptiness.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
One commit. Server-side only — no plugin change, so no manifest mint and nothing about how the plugin reaches a session moves.
The problem
retrieval_telemetryreturned distributions and left the reading to the caller, so every readout was the same four checks done by hand — #3430's baseline, #3835's rule near-misses, the #1038 rerank gate. Mechanical, and therefore forgettable.Tonight's acceptance pass on #3898 is the case for fixing it. Reading that window by hand meant catching three separate things: two surfaces with
covers_window: false;prompt_rule's floor having moved three times inside the window, which made the readout self-contradictory (deliveries at 0.622 beside refusals at 0.7199); and 15 of 20 near-misses being one record matched against text no operator wrote. Miss any one and the obvious conclusion was "the bar is too tight" — a floor change that would have injected a single preference into every task notification.What it adds
warnings, always present and empty when clean, so its emptiness is an answer rather than a gap. Each entry carries the numbers that produced it: "345 calls, 0 declined" is the analysis, "check write_path_rule" is an instruction to redo it.silent_surfacesis the half the rows cannot show: an arm that emitted nothing is invisible to every row-based check and looks identical to an arm that does not exist.The registry, and why it is a second one
New
services/retrieval_registry.py, 33 points. Deliberately notretrieval_surfaces.SURFACES, which answers "what can be TUNED" and excludes the reserved slots because a budget of 1 is their feature. This answers "what can be MEASURED", and the reserved slots belong in it precisely because they are judgeable without being tunable. One is a control panel, the other an inventory; a test asserts they cannot drift.Its test derives sources from the call sites with
astrather than grep, and the difference is load-bearing:wide_netandreport_preferencereach their recorder assource=SOURCEthrough a module constant, so a grep forsource="is blind to both — the narrowing #3191 warns about. Three sites passsourceas a variable and are declared inFAN_OUT_SITES; the test pins those sites but not the values they can pass, which is stated in the code and is whyunregistered_sourceexists to catch the rest at first fire.Verification
CI run 7086 green on
e2c3a5c.Locally, the warning logic is pure by design, so it was exercised directly against the real registry: 24/24 including a negative control asserting a healthy window across all 33 points yields exactly zero warnings — a guard that cannot pass cleanly is not a guard.
The registry guard failed first, on a real defect in itself.
FAN_OUT_SITESkeys were spelledservices/...while the extractor emits paths relative tosrc/, so thestartswithcompared against a prefix that could never match and the check passed by matching nothing. Now keyed by exact string and matched by equality, with the near-miss recorded inline.Note for after the merge
This needs a deploy to reach the running instance. Unrelated to it, migration
0105has been onmainsince PR #170 and applies on the same deploy if that one was not taken.🤖 Generated with Claude Code
https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy