disclosure_surface_registry grades by SURFACE, not by FIELD, so a field that is correct on one surface and collapsed on another is structurally invisible to it #152

Closed
opened 2026-09-05 12:54:59 +02:00 by buildagent · 0 comments
Member

Found by a disclosure-field trace. The registry is not vacuous — its axis is wrong, which is a failure this project already has a name for ("audits checking the wrong axis").

The live defect it cannot see (A1)

render_offers exists specifically so activation_available is never rendered as "nothing to activate" (#86 A2). It has exactly one caller: crates/mcp-server/src/server.rs:8481, the project_overview tool.

  • stats_resource (server.rs:19632) re-renders only plugin_activation through render_activation and passes everything else through raw.
  • Stats::activation_available carries #[serde(default, skip_serializing_if = "Option::is_none")] (crates/daemon/src/local_index.rs:1157).

So code-index://stats drops the field precisely when the store could not be consulted — the exact collapse the whole doctrine exists to prevent, on a shipped surface. project_overview_resource never carries it at all.

Verified in the tree, not inferred.

Siblings found in the same trace

  • A2 — package_duplicate_ids and package_set_consulted ship twice in one payload on code-index://stats: raw at top level and inside the three-state block. The top-level copy hands the reader exactly the collapse the nested one prevents.
  • A3 — PathClaim::claimed_by (crates/daemon/src/local_index.rs:1312) is produced at crates/daemon/src/eligibility.rs:375. Its doc says it "is the field that makes the reply project-scoped rather than a restatement of what the client could have computed alone." Eligibility::from_claims (crates/mcp-server/src/eligibility.rs:262-289) reads c.kind, c.reason, c.path — never c.claimed_by. The only other read in the tree is a test assertion. 24 B per path on every eligibility batch (changed_symbols, review_diff, index_coverage), zero readers.
  • A4 — index_coverage's claimed_by: "none" is annotated in its own source (server.rs:11924) as "UNREACHABLE ON EVERY INDEX IN THE WILD" and no test pins it. active_generation_stale is graded only over a stubbed block.
  • A5 — plugin_state_unavailable and availability: "unavailable" are the same bit in the same object at project level.

The generic fix

Give the registry a per-field axis: for each disclosure field, enumerate every surface that can carry it, and require the same three-state rendering on all of them. That catches A1 and A2 by construction and every future instance, rather than a fix per field — which is this project's own stated rule.

Fix A1 and A2 by routing both resources through render_offers and stripping the raw Stats duplicates. Fix A3 by reading the field or deleting it.

No opposite-direction shim

Related and worth its own thought: the doctrine is built entirely on "an absent or zero answer must never read as a measured negative" and has no shim for the reverse — a retired reason code arriving from a peer reporting the same version string. coverage::RETIRED is referenced only by its own unit tests. The trace hit this on its first live call: the installed build returned coverage_reasons: ["rules_coverage_absent"] beside a coverage_semantics string still defining it — a code the current tree has retired.

Trim candidates found in the same pass

The activation family costs 2,932 B on every project_overview, 75% of it two constant prose strings (activation::SEMANTICS 1,177 B + coverage::SEMANTICS 1,013 B). Per-row influence costs 1,850 B/page of an invariant value, retained even in concise — a page-level influence_scope would replace it. states_not_derived is ~630 B per overview describing four states the producer cannot reach. See #111/#120.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K

Found by a disclosure-field trace. The registry is not vacuous — its **axis is wrong**, which is a failure this project already has a name for ("audits checking the wrong axis"). ## The live defect it cannot see (A1) `render_offers` exists specifically so `activation_available` is **never** rendered as "nothing to activate" (#86 A2). It has exactly **one** caller: `crates/mcp-server/src/server.rs:8481`, the `project_overview` **tool**. - `stats_resource` (`server.rs:19632`) re-renders only `plugin_activation` through `render_activation` and passes everything else through raw. - `Stats::activation_available` carries `#[serde(default, skip_serializing_if = "Option::is_none")]` (`crates/daemon/src/local_index.rs:1157`). So `code-index://stats` **drops the field precisely when the store could not be consulted** — the exact collapse the whole doctrine exists to prevent, on a shipped surface. `project_overview_resource` never carries it at all. Verified in the tree, not inferred. ## Siblings found in the same trace - **A2** — `package_duplicate_ids` and `package_set_consulted` ship **twice in one payload** on `code-index://stats`: raw at top level *and* inside the three-state block. The top-level copy hands the reader exactly the collapse the nested one prevents. - **A3** — `PathClaim::claimed_by` (`crates/daemon/src/local_index.rs:1312`) is produced at `crates/daemon/src/eligibility.rs:375`. Its doc says it *"is the field that makes the reply project-scoped rather than a restatement of what the client could have computed alone."* `Eligibility::from_claims` (`crates/mcp-server/src/eligibility.rs:262-289`) reads `c.kind`, `c.reason`, `c.path` — **never `c.claimed_by`**. The only other read in the tree is a test assertion. **24 B per path on every eligibility batch** (`changed_symbols`, `review_diff`, `index_coverage`), zero readers. - **A4** — `index_coverage`'s `claimed_by: "none"` is annotated in its own source (`server.rs:11924`) as *"UNREACHABLE ON EVERY INDEX IN THE WILD"* and no test pins it. `active_generation_stale` is graded only over a stubbed block. - **A5** — `plugin_state_unavailable` and `availability: "unavailable"` are the same bit in the same object at project level. ## The generic fix Give the registry a **per-field** axis: for each disclosure field, enumerate every surface that can carry it, and require the same three-state rendering on all of them. That catches A1 and A2 by construction and every future instance, rather than a fix per field — which is this project's own stated rule. Fix A1 and A2 by routing both resources through `render_offers` and stripping the raw `Stats` duplicates. Fix A3 by reading the field or deleting it. ## No opposite-direction shim Related and worth its own thought: the doctrine is built entirely on *"an absent or zero answer must never read as a measured negative"* and has **no shim for the reverse** — a **retired** reason code arriving from a peer reporting the same version string. `coverage::RETIRED` is referenced only by its own unit tests. The trace hit this on its first live call: the installed build returned `coverage_reasons: ["rules_coverage_absent"]` beside a `coverage_semantics` string still defining it — a code the current tree has retired. ## Trim candidates found in the same pass The activation family costs **2,932 B on every `project_overview`, 75% of it two constant prose strings** (`activation::SEMANTICS` 1,177 B + `coverage::SEMANTICS` 1,013 B). Per-row `influence` costs **1,850 B/page of an invariant value, retained even in `concise`** — a page-level `influence_scope` would replace it. `states_not_derived` is ~630 B per overview describing four states the producer cannot reach. See #111/#120. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index#152
No description provided.