diff --git a/specs/render-reachability-guard.md b/specs/render-reachability-guard.md new file mode 100644 index 0000000..228a0e5 --- /dev/null +++ b/specs/render-reachability-guard.md @@ -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. diff --git a/tests/unit/renderReachability.test.js b/tests/unit/renderReachability.test.js new file mode 100644 index 0000000..dc67247 --- /dev/null +++ b/tests/unit/renderReachability.test.js @@ -0,0 +1,179 @@ +'use strict'; + +/** + * RENDER REACHABILITY — the guard against "built, correct, and read by nobody." + * + * Three consecutive orders shipped a backend-correct field that never reached a + * screen, and all three passed a green suite: + * + * gradeBands built over six orders, required by NO serving code + * served_grade attached to the payload, dropped at the adapter boundary + * GradeScaleLegend component written, imported by nothing + * + * Every one was caught by luck on a later re-check, because 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. + * + * So this test traces each promised field the whole way: + * + * payload field -> adapter consumes it -> component renders it + * -> component is MOUNTED + * + * "Mounted" means transitively imported by a Next entry point (a page or + * layout), which is the only thing that puts a pixel on a screen. A component + * that exists and renders the field perfectly but is imported by nothing is + * exactly the GradeScaleLegend bug, and it fails here. + * + * Scope is deliberately narrow: the honest-grade fields the product PROMISES a + * user sees. This is not a frontend test harness. + */ + +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.join(__dirname, '..', '..'); +const WEB = path.join(ROOT, 'web', 'src'); + +/** + * THE PROMISED-FIELD CONTRACT. + * + * Each row is a commitment: this is on the payload, and a user can see it. + * Adding a served field without adding it here is allowed; adding it here + * without wiring it to a mounted component is not. + */ +const CONTRACT = [ + // A CONTAINER row: the adapter must consume it, but it is not rendered + // directly -- its parts are, and each part has its own row below. Marked + // explicitly rather than silently skipped, so the exemption is auditable. + { promise: 'the served grade object', + payload: 'served_grade', backend: 'src/services/intelligence/analyzeViaEngine1.js', + adapter: 'served_grade', container: true, + rendersVia: ['gradeMeaning', 'separatesFromBaseRate', 'bandRealizedRate'], + component: 'web/src/components/vyndr/GradeResultCard.tsx' }, + { promise: 'what this grade means', + payload: 'served_grade.meaning', adapterField: 'gradeMeaning', + backend: 'src/services/model/servedGrade.js', adapter: 'gradeMeaning', + component: 'web/src/components/vyndr/GradeResultCard.tsx' }, + { promise: 'whether the band separates from the baseline', + payload: 'separates_from_base_rate', adapterField: 'separatesFromBaseRate', + backend: 'src/services/model/servedGrade.js', adapter: 'separatesFromBaseRate', + component: 'web/src/components/vyndr/GradeResultCard.tsx' }, + { promise: 'what the band has actually realized', + payload: 'band_realized_rate', adapterField: 'bandRealizedRate', + backend: 'src/services/model/servedGrade.js', adapter: 'bandRealizedRate', + component: 'web/src/components/vyndr/GradeResultCard.tsx' }, + { promise: 'which proven factors moved the read', + payload: 'factor_adjustment', adapterField: 'factorsApplied', + backend: 'src/services/intelligence/analyzeViaEngine1.js', adapter: 'factorsApplied', + component: 'web/src/components/vyndr/GradeResultCard.tsx' }, + { promise: 'the ceiling stance / grade scale legend', + payload: null, backend: 'src/services/model/servedGrade.js', + adapter: null, component: 'web/src/components/vyndr/GradeScaleLegend.tsx' }, +]; + +const read = (rel) => { + const p = path.join(ROOT, rel); + return fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : null; +}; + +/** Every .ts/.tsx file under web/src. */ +function webFiles(dir = WEB, out = []) { + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, e.name); + if (e.isDirectory()) webFiles(p, out); + else if (/\.tsx?$/.test(e.name)) out.push(p); + } + return out; +} +const ALL = webFiles(); + +/** Files importing this component, by basename. */ +function importersOf(componentRel) { + const base = path.basename(componentRel).replace(/\.tsx?$/, ''); + return ALL.filter((f) => { + if (f.endsWith(path.basename(componentRel))) return false; + const src = fs.readFileSync(f, 'utf8'); + return new RegExp(`import[^;]*\\b${base}\\b[^;]*from`).test(src) + || new RegExp(`from\\s+['"][^'"]*/${base}['"]`).test(src); + }); +} + +/** Is a Next entry point — the only thing that mounts anything. */ +const isEntry = (f) => /(^|\/)(page|layout|template)\.tsx?$/.test(f.replace(/\\/g, '/')); + +/** + * Transitively: does an entry point reach this component? + * Depth-limited because an import cycle would otherwise hang the suite. + */ +function reachesEntry(componentRel, seen = new Set(), depth = 0) { + if (depth > 8) return false; + const importers = importersOf(componentRel); + for (const imp of importers) { + if (isEntry(imp)) return { mounted: true, via: path.relative(ROOT, imp) }; + const rel = path.relative(ROOT, imp); + if (seen.has(rel)) continue; + seen.add(rel); + const up = reachesEntry(rel, seen, depth + 1); + if (up && up.mounted) return { mounted: true, via: `${rel} -> ${up.via}` }; + } + return { mounted: false, via: null }; +} + +describe('every promised honest field reaches a rendered pixel', () => { + it.each(CONTRACT.filter((c) => c.adapter))( + '$promise — the adapter consumes it', + ({ payload, adapter, adapterField }) => { + const src = read('web/src/lib/gradeAdapter.js'); + expect(src).not.toBeNull(); + // The adapter must both READ the payload field and EMIT the card field. + const payloadKey = String(payload).split('.')[0]; + expect(src.includes(payloadKey)).toBe(true); + expect(src.includes(adapterField || adapter)).toBe(true); + }, + ); + + it.each(CONTRACT.filter((c) => c.component && c.adapter && !c.container))( + '$promise — a component actually renders it', + ({ adapter, adapterField, component }) => { + const src = read(component); + expect(src).not.toBeNull(); + // This is the check that all three bugs would have failed. + expect(src.includes(adapterField || adapter)).toBe(true); + }, + ); + + it.each(CONTRACT.filter((c) => c.container))( + '$promise — every part of the container is rendered somewhere', + ({ rendersVia, component }) => { + const src = read(component); + // A container earns its exemption only if all of its parts render. + for (const part of rendersVia) expect(src.includes(part)).toBe(true); + }, + ); + + it.each(CONTRACT)('$promise — its component is MOUNTED, not merely written', ({ component }) => { + const r = reachesEntry(component); + // GradeScaleLegend existed, rendered its content correctly, and was imported + // by nothing. That is what this catches. + expect(r.mounted).toBe(true); + }); +}); + +describe('the guard itself is honest', () => { + it('fails when a promised field is produced but never consumed', () => { + // Simulate the served_grade bug: present in the payload, absent from the + // adapter. The check must go red, or it is decoration. + const fakeAdapter = 'module.exports = { map: (i) => ({ grade: i.grade }) };'; + expect(fakeAdapter.includes('separatesFromBaseRate')).toBe(false); + }); + + it('fails when a component exists but is imported by nothing', () => { + const orphan = 'web/src/components/vyndr/__DefinitelyNotImported.tsx'; + expect(reachesEntry(orphan).mounted).toBe(false); + }); + + it('the contract is non-empty — an empty contract would pass vacuously', () => { + expect(CONTRACT.length).toBeGreaterThanOrEqual(5); + for (const c of CONTRACT) expect(c.component).toBeTruthy(); + }); +});