Render-reachability guard: make built-but-unread a CI failure
Three consecutive orders shipped a backend-correct field that never
reached a screen, all on a green suite: gradeBands (required by no
serving code), served_grade (dropped at the adapter boundary),
GradeScaleLegend (imported by nothing). Each was caught by luck on a later
re-check, and in two of the three I had already reported the wiring done.
WHY GREEN TESTS COULD NOT SEE IT: backend tests stop at the API payload.
They prove a field is PRODUCED and say nothing about whether it is
CONSUMED. Invisible by construction, not an oversight in any one test.
THE TRAP, NAMED: the difficulty pools in the backend, so by the time a
field exists on the payload it feels finished. What remains is a
three-line adapter change nobody considers worth verifying, so it gets
claimed rather than traced. The last inch is the one with no friction,
which is exactly why it gets skipped. "I added the field" and "a user can
see it" are different claims and only the first is fun.
THE GUARD traces each promised field the whole way: payload -> adapter
consumes -> component renders -> component is MOUNTED. Mounted is
transitive to a Next entry point (page/layout/template), the only thing
that puts a pixel on screen, depth-limited so an import cycle cannot hang
the suite.
Container rows are exempted EXPLICITLY, not silently: served_grade carries
container:true plus a rendersVia list, and a separate assertion checks
every named part actually renders. The exemption is auditable and cannot
hide an unrendered field.
The guard tests itself -- an orphan component must report unmounted, and
the contract must be non-empty, since an empty contract passing vacuously
is how this would most plausibly rot.
RETRO-PROOF: run unchanged against 3591c76, before the wiring, it goes
11 failed / 8 passed and names the exact bugs -- "the ceiling stance /
grade scale legend - its component is MOUNTED, not merely written", "the
served grade object - the adapter consumes it", "whether the band
separates from the baseline - a component actually renders it". Green on
the current tree.
HONEST SCOPE LIMIT: gradeBands is NOT in the contract and would not be
caught. It is a backend module, not a promised user-facing field, and it
is correctly unwired -- every band collapses to base-rate at current
resolution. Out of scope by design, not oversight.
Now in the standing suite, so the three-gate floor is tests green
(including reachability) + build exit 0 + fingerprint. No serving or model
change. p_win never mutated. No Bonferroni slot.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1sivYNqY2TS5ftykmHBU9
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# Built-but-unread — the defect class, and the guard that ends it
|
||||
|
||||
## The class
|
||||
|
||||
Three consecutive orders shipped a backend-correct field that never reached a
|
||||
screen. All three passed a fully green suite.
|
||||
|
||||
| order | what was built | why it never rendered |
|
||||
|---|---|---|
|
||||
| grade bands | `gradeBands.js`, six orders of work | required by no serving code |
|
||||
| `91927a4` | `served_grade` on the payload | dropped at the adapter boundary |
|
||||
| `3591c76` | `GradeScaleLegend.tsx` | imported by nothing |
|
||||
|
||||
Each was caught by luck on a later re-check, and in two of the three I had
|
||||
already **reported the wiring as done**.
|
||||
|
||||
### Why green tests could not see it
|
||||
|
||||
Backend tests stop at the API payload. They prove a field is **produced** and say
|
||||
nothing about whether it is **consumed**. The failure is invisible to them by
|
||||
construction — not an oversight in any individual test.
|
||||
|
||||
### The cognitive trap, named
|
||||
|
||||
The difficulty pools in the backend. Deriving the grade, proving the factors,
|
||||
measuring resolution — that is where the thinking happens, and by the time a
|
||||
field exists on the payload it *feels* finished. The remaining step is a
|
||||
three-line adapter change that nobody considers worth verifying, so it gets
|
||||
claimed rather than traced. **The last inch is the one with no friction, which is
|
||||
exactly why it is the one that gets skipped.**
|
||||
|
||||
Nothing here is a frontend-competence problem. It is that "I added the field" and
|
||||
"a user can see it" are different claims, and only the first one is fun.
|
||||
|
||||
---
|
||||
|
||||
## The contract
|
||||
|
||||
`tests/unit/renderReachability.test.js` holds the promised-field contract — the
|
||||
things the grade product commits to a user seeing:
|
||||
|
||||
| promise | payload | adapter | component |
|
||||
|---|---|---|---|
|
||||
| the served grade object | `served_grade` | `served_grade` (container) | GradeResultCard |
|
||||
| what this grade means | `served_grade.meaning` | `gradeMeaning` | GradeResultCard |
|
||||
| whether the band separates | `separates_from_base_rate` | `separatesFromBaseRate` | GradeResultCard |
|
||||
| what the band realized | `band_realized_rate` | `bandRealizedRate` | GradeResultCard |
|
||||
| which factors moved the read | `factor_adjustment` | `factorsApplied` | GradeResultCard |
|
||||
| the ceiling stance | — | — | GradeScaleLegend |
|
||||
|
||||
Adding a served field without adding it here is allowed. Adding it **here**
|
||||
without wiring it to a mounted component is not.
|
||||
|
||||
---
|
||||
|
||||
## The guard
|
||||
|
||||
For each promised field it traces the whole path:
|
||||
|
||||
```
|
||||
payload field -> adapter consumes it -> component renders it -> component is MOUNTED
|
||||
```
|
||||
|
||||
**"Mounted" is transitive to a Next entry point** (`page`/`layout`/`template`) —
|
||||
the only thing that puts a pixel on a screen. A component that exists and renders
|
||||
its field perfectly but is imported by nothing fails. Depth-limited so an import
|
||||
cycle cannot hang the suite.
|
||||
|
||||
**Container rows are exempted explicitly, not silently.** `served_grade` is
|
||||
consumed by the adapter but not rendered directly, so it carries
|
||||
`container: true` plus a `rendersVia` list — and a separate assertion checks that
|
||||
**every named part actually renders.** The exemption is auditable; it cannot hide
|
||||
an unrendered field.
|
||||
|
||||
The guard also tests itself: it asserts that an orphan component reports
|
||||
unmounted, and that the contract is non-empty (an empty contract would pass
|
||||
vacuously — the way this guard would most plausibly rot).
|
||||
|
||||
### Retro-proof
|
||||
|
||||
Run unchanged against the tree at `3591c76`, before the wiring:
|
||||
|
||||
```
|
||||
Tests: 11 failed, 8 passed
|
||||
● the ceiling stance / grade scale legend — its component is MOUNTED, not merely written
|
||||
● the served grade object — the adapter consumes it
|
||||
● whether the band separates from the baseline — a component actually renders it
|
||||
● what this grade means — the adapter consumes it
|
||||
● which proven factors moved the read — a component actually renders it
|
||||
...
|
||||
```
|
||||
|
||||
**It names the exact three bugs.** Green on the current tree.
|
||||
|
||||
### One honest scope limit
|
||||
|
||||
`gradeBands` is **not** in the contract and would not be caught. It is a backend
|
||||
module, not a promised user-facing field, and it is correctly unwired — every
|
||||
band it produces collapses to base-rate at current resolution. The guard covers
|
||||
*promised* fields; a backend module that should not yet render is out of scope by
|
||||
design, not by oversight.
|
||||
|
||||
---
|
||||
|
||||
## In the deploy floor
|
||||
|
||||
The guard runs in the standing suite, so it is part of the three-gate floor:
|
||||
**tests green** (now including reachability) + web build exit 0 + post-deploy
|
||||
fingerprint. A future order that adds a served field without wiring it to a
|
||||
mounted component fails CI rather than a hand-check three orders later.
|
||||
|
||||
No serving or model change in this order. `p_win` never mutated. No Bonferroni
|
||||
slot.
|
||||
Reference in New Issue
Block a user