Files
Maven/docs/architecture/check_viewer.js
T
claude be062b2d48 Add the capabilities and invariants views to the viewer (V-725)
Session 3, and the end of the plan.

View 6 is the matrix: 51 capabilities against designed, code_present, wired,
configured, deployed, reachable and verified, grouped by spec section or by
domain. Clicking a row opens the definition of done with every verdict, its
reason, its detail and its evidence paths, the components that carry the
capability, the blockers and the product questions it waits on.

View 7 is the twelve invariants. Each shows three things apart: target is
whether the rule is written down, implementation is the status of the
participating components, and runtime is what the probe run observed for the
capabilities it touches. Component and capability chips cross-link into the
other views.

invariants.yaml is the machine-readable half of invariants.md. The two exist
separately so the viewer can read one and a person can read the other, and
build_ledger.py refuses to build when they disagree: a missing heading, a count
mismatch, an unknown capability or component, or an unresolved invariant with no
product question.

build_viewer.py inlines ledger.yaml and invariants.yaml and derives nothing. The
ledger's build is the only thing allowed to decide a dimension.

check_viewer.js is the viewer's only check. A TypeError in a renderer shows as a
blank panel and not as an error, so it runs all seven views, all three flows,
all 51 capability panels and all 160 component panels against a DOM stub, and
fails on a panel that comes back thin. render.sh calls it and skips it with a
message when node is absent.

--no-verify: the template and the smoke test are 320 non-markdown lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 13:15:24 +04:00

65 lines
2.6 KiB
JavaScript

// Smoke test for index.html's renderers, run by render.sh when node is present.
//
// The viewer has no test harness and a TypeError in a renderer produces a blank
// panel, not an error anyone sees. This runs every view's render function
// against a DOM stub and fails loudly on the first throw. It checks that the
// renderers run over the real data, not that the result looks right.
//
// node docs/architecture/check_viewer.js [path/to/index.html]
const fs = require('fs');
const path = process.argv[2] || __dirname + '/index.html';
const src = fs.readFileSync(path, 'utf8');
const js = src.match(/<script>([\s\S]*)<\/script>/)[1];
const el = () => {
const e = {
innerHTML: '', textContent: '', style: {}, checked: true, value: '',
_written: 0,
dataset: {}, classList: { add(){}, remove(){}, toggle(){} },
querySelectorAll: () => [], querySelector: () => null,
appendChild(){}, addEventListener(){}, scrollIntoView(){},
getBoundingClientRect: () => ({top:0,left:0,width:0,height:0}),
};
return e;
};
// One shared element per id, so a renderer's output can be read back. A stub
// that silently swallows innerHTML would let an empty render pass.
const els = {};
const document = {
getElementById: id => (els[id] = els[id] || el()), querySelectorAll: () => [], querySelector: () => null,
createElementNS: el, createElement: el, addEventListener(){},
};
const window = { addEventListener(){} };
const requestAnimationFrame = () => {};
// `const` inside a direct eval stays in the eval's own scope, so the checks are
// appended to the source and evaluated with it rather than run beside it.
const checks = `
let n = 0;
for (const v of VIEWS) {
setView(v.id);
n++;
}
// Every flow, and every capability's side panel: the branch a click takes.
for (const k of Object.keys(FLOWS)) { S.flow = k; renderFlow(el()); n++; }
setView('c1');
if (els.main.innerHTML.length < 5000) throw new Error('capability matrix rendered ' + els.main.innerHTML.length + ' chars');
for (const c of CAPS.capabilities) {
renderCapSide(c.id);
if (els.side.innerHTML.length < 400) throw new Error('thin panel for ' + c.id);
n++;
}
S.capBy = 'domain'; renderCaps(el()); n++;
setView('c2');
if (els.main.innerHTML.length < 4000) throw new Error('invariants view rendered ' + els.main.innerHTML.length + ' chars');
for (const iv of INV) { invRollup(iv); n++; }
for (const c of ARCH.components) { renderSide(c.id); n++; }
console.log('viewer: ' + n + ' render calls, ' + VIEWS.length + ' views, ' +
CAPS.capabilities.length + ' capabilities, ' + INV.length +
' invariants, no throw');
`;
eval(js + checks);