Phase 168: a test tier that can see the page, seams in the three big files, and a clean root - #212
Merged
Merged
Conversation
Eight files had accumulated in the root, and the giveaway that none of them belonged there was that seven were unreferenced by anything in the repo. The two node diagnostics join the frontend tooling they mirror: cluster-eval.mjs and person-dup-diagnostic.mjs go to frontend/scripts/, beside verify-reconciliation.mjs, which is the same kind of thing — the precedent backend/scripts/ already sets, where eval harnesses live next to the code they exercise. Their usage lines now give the path you would actually type from the repo root. PR_narrative_summaries.md is a design writeup for the Phase 53 narrative work, so it becomes docs/narrative-summaries.md. The four social cards and the explainer graphic go to docs/social/ with a README, because one of them is not litter at all: opencheck-social-b.html carries the live source count and is on the "add a new source" checklist in CLAUDE.md (path updated). The only thing distinguishing it from the three drafts beside it was that you had to know. unilever.json is untracked rather than moved. It is 252 KB of one captured /lookup answer, and it was already listed in .gitignore — listed there while still tracked, which does nothing at all, and is exactly why it survived. It stays on disk for whoever has it; both diagnostics document the curl that makes one. The .gitignore entry now says so, and covers *.lookup.json for the next one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YB1QPTL3x1HwpmcZc64xGv
The frontend suite was logic-only for 167 phases — no `.test.tsx`, no browser — and the three regressions that cost the most all walked past it: the v1/v2 component mix, the verdict rendered twice, the mode tabs overflowing their strip at 390px. None of them was a wrong value, so no amount of `lib/` testing could have seen any of them. Two tiers are added above it, and the rule between them is which question they can answer. Component tests (`*.test.tsx`, jsdom + testing-library) pin what the markup is: how many of a thing there are, an element's accessible name, what a control does when pressed. `VerdictStrip` gets ten — including "states the verdict exactly once", the double verdict pinned where it happened — and `SourcesPage` gets eight, over the disclosure Phase 161 added. jsdom is per-file, chosen by the extension: `environmentMatchGlobs` gives a DOM to `.test.tsx` and leaves the fast `node` suite alone. The Playwright smoke (`frontend/e2e/`, `npm run test:e2e`) pins what a whole page is, over the six routes a reader can reach and a curated BP report: one `<h1>`, one header, one footer, nothing overflowing sideways, no console errors, and — at 390px — four check-mode tabs all inside the viewport with 44px targets. It runs a real backend and a production build, offline: no keys are set, so adapters serve stubs or the committed bulk BODS the curated examples already use, and external requests are answered empty so a font CDN cannot fail a merge. It found two things on its first run. **Four pages had no `<h1>` at all.** /sources, /about, /api and /changelog each carried a page of `<h2>`s under nothing, while a comment beside the hero had claimed since Phase 122 that the homepage heading was the page's h1 — true there, and only there. Each view that does not title itself now renders an sr-only title from `PAGE_TITLES`, typed `Exclude<View, SelfTitledView>` so a new view cannot be added without either naming its page or declaring that it has its own heading, as /batch does. **`vite preview` was not previewing production.** Vite defaults `preview.proxy` to `server.proxy`, so the built preview proxied `/sources` to the backend's JSON inventory and the SPA route of that name could not be reached at all — while the deployed static site, which has no proxy, serves it fine. `preview.proxy` is now empty, which is what production is. Three smaller things fall out. `tsconfig.json` no longer excludes test files and now includes `e2e/`, so `tsc -b` — already in CI — typechecks the tests it was silently skipping. `PLAYWRIGHT_CHROMIUM_PATH` is honoured for environments that ship a browser rather than download one. And a new `e2e` CI job installs one browser and uploads traces on failure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YB1QPTL3x1HwpmcZc64xGv
App.tsx 4,152 → 3,107. lookup.py 3,214 → 2,543. mapper.py 10,824 → 8,512. Nothing here changes behaviour: code moves, and every name keeps its address, because 62 files import from bods.mapper and sources/probes.py resolves a mapper by getattr. **App.tsx** gives up the two static pages and the homepage panels — none of which read a line of its state. ApiPage and BehindTheScenesPage each mount from one line of App and are documents, not components with a life; HomePanels holds EXAMPLE_LEIS, the picker, BatchInvite and How It Works; BtsCard holds the two primitives both pages are built from, which had been sitting inside whichever page imported them first. The design-system baseline is re-seeded so the moved literals follow their code — the totals are unchanged at 79 hex and 382 arbitrary sizes, App.tsx dropping 12→7 and 117→66 with the four new files picking up exactly the difference, so the ratchet is where it was. **lookup.py** gives up the per-source hit builders. CLAUDE.md's "add a source" checklist has said `routers/lookup.py — _bh_<name>() hit builder (only this)` for a hundred phases; hit_builders.py is now a file where only that is true. It takes the whole contract with it: the 36 builders, the `_hit` factory they all go through, and `_LookupCtx`, which is half of their signature. **mapper.py** gives up its core and its two largest sections. statements.py holds what every one of the 148 functions uses — the three statement factories, `_stable_id`, the source and date blocks — and `set_beneficial_ownership`, which had been living in the middle of the FtM section although it decides a policy about every source. bods/mappers/ then takes FtM (with its vendored edge-schema table) and Wikidata, the two largest per-source blocks. The remaining sections are banner-separated the same way and can move the same way. Two things the move turned up, both fixed here: a bare string statement at App.tsx module scope, left behind when the Neo4j download came off in Phase 122, along with the doc comment for the field it described; and test_curated_leis_sync, which greps App.tsx for EXAMPLE_LEIS and would have caught the move on its own — it now reads HomePanels.tsx. The fail-fast guard for a missing hit builder now patches the module the collector actually reads, which is the point of that test. Backend 3,872 tests pass; frontend 472 vitest and 10 smoke tests pass; tsc, build and the design lint are clean. Ruff on the new modules is down to the nine findings that travelled with the code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YB1QPTL3x1HwpmcZc64xGv
Status row for Phase 168 (implementation commits cd1e790, ecc2599, 0c4edef), README latest line, spelled-out phase count, frontend footer (472 vitest tests across 40 files, and the ten-test Playwright smoke), regenerated changelog JSON. The backend figure is unchanged: the phase added no backend tests — main and this branch both run 3,874 in the same environment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YB1QPTL3x1HwpmcZc64xGv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three commits. Root litter: seven unreferenced files out of the repository root;
unilever.jsonuntracked (it was gitignored while still tracked). Test tier: jsdom component tests and a ten-test Playwright smoke over six routes and a curated report, real backend, production build, offline — it found four pages with no<h1>and avite previewthat proxied/sourcesto the backend instead of the SPA, both fixed;tsc -bnow typechecks the tests it was skipping. Splits: App.tsx 4,152→3,107, lookup.py 3,214→2,543, mapper.py 10,824→8,512, everything re-exported.Backend 3,874 tests, frontend 472 + 10 smoke.
🤖 Generated with Claude Code
https://claude.ai/code/session_01YB1QPTL3x1HwpmcZc64xGv