and the measurement turned up finding 17, a live scoring defect — which was then FIXED on 2026-08-11 with the corpus reload it needed (ADR-97, scripts/remeasure_alignment.py, 333 changes read, 25 moved, 2 cautions lifted, 0 added).
The design was also incomplete and an outside reading caught it, which is the more useful half of this row. Everything designed here was a way to survive more work — a queue, a pool, an admission check — and nothing reduced the work in a single pair. Section 3 had already measured that normalize() is 87% of the diff call and that a version takes part in many diffs, and neither the design nor the review that passed it asked why that cost is paid more than once. Section 4b now carries the answer: hash each normalised passage at ingest, anchor on hashes unique to both sides, align only the gaps. Measured on the Kentucky pair, identical output, 2.36x — and the naive form, hashing at diff time, is 0.65x, a loss, because hashing means normalising and normalising is the 87%. Still nothing is built: it needs a hash column and a derived-corpus rebuild. Verbatim — Remaining gaps, prioritised

Remaining gaps, prioritised

Recompiled 2026-08-07. Every row below was re-checked against the code on that date, not carried forward from the previous version of this page. The first version was compiled 2026-08-04; 64 commits have landed since, and most of what it listed is closed.

How to read the ordering. Priority here is not size of effort. It is what a reviewer hits first. A submission is read in a fixed order — the repository link, then the run command, then the screens, then the documents — and a defect early in that order hides everything good behind it. P0 items stop a reviewer at the front door. P1 items are false statements a reader could act on. P2 items are real code gaps that a panel question would expose. P3 items are things only we would notice.
What changed since 2026-08-04. All six P0 items and all four P1 items from that version are closed. Of twelve P2 items, nine are closed and two were narrowed to something smaller than they were. Of six P3 items, three are closed. The closed rows are listed at the foot of this page with what closed them, because a gaps list read alone tells a reader the wrong story about the state of the work.
One item is new, and it was found by recompiling this page. The suite was re-run rather than remembered, as this project's own rule requires, and it printed a different number from the one four documents carry. That is P1-5 below. It is the only row here that did not exist on 2026-08-04, and it is a small instance of the failure this page exists to catch.
Four more rows added later on 2026-08-07, with the visual redesign. P2-13 to P2-16. Three of the four are things the redesign left open on purpose and one, P2-14, is a warning rather than a gap: the obvious test for it would report a failure that is not there. They are written here in the same change as the work, not after it. The three decisions the redesign took are in ADR-92, ADR-93 and ADR-94, and docs/web-design.html gained a part five saying which of its earlier parts now describe a surface that no longer exists.
Five more rows added 2026-08-10, from outside this project. I1 to I5 came from a technical interview rather than from us reading our own code, which is the first time anything on this page did. They are kept in their own section with the wording they arrived in, so a reader can see what an outside reviewer asked and what we found when we checked it. Two of the five turned out narrower than the interview stated and say so in the row; none of them turned out to be wrong.
What happened to I1 to I5, written 2026-08-11 after the five branches merged. Five agents worked them in parallel and three rounds of adversarial review ran against the result. Two are closed, two are answered with work that stops short of what the interview asked for, and one is a deliberate negative result: I4's tree parser was built, measured against the real corpus and not wired in, because the two branches it was built for fired zero times and the only branch with any effect was one nobody authorised. That is ADR-102 and it is the strongest thing in this batch. Two other honest notes belong at the top rather than buried in a cell. The scaling measurement turned up a live defect in shipped code — finding 17, the alignment score takes difflib's junk default on every long passage — and it is open on purpose, with ADR-97 carrying the reason. And three review rounds found published numbers that did not reproduce from committed code, five of them in one module: this repository's cardinal sin, committed repeatedly inside the change that answered an interview about rigour. ADR-100 and ADR-102 record it, and the board's risk register now carries it as a standing risk rather than as a run of incidents.

P0 — stops a reviewer at the front door

Empty. All six closed. Listed at the foot of the page.

Worth stating plainly rather than leaving as an absence: the repository is public (gh repo view jsahasi/verbatim --json visibility returns PUBLIC), the working tree is clean with nothing uncommitted, make test passes on a fresh clone, both routes that render review_project.html answer 200, permissions is in the nginx proxy list, and make run seeds the eight duties because scripts/seed_demo_gaps.py is now called by both the seed target and deploy/entrypoint.sh.

Run rather than recalled, on 2026-08-07 at commit 65c42d7: make fresh-check cloned HEAD into a temporary directory, ran the suite there and exited zero — 2,340 passed, 1 xfailed in 327s, "fresh clone: tests pass". make test in the working copy gives the same result in 325s. That is the command a reviewer runs, run the way a reviewer runs it.

P1 — false statements a reader could act on

All four items from 2026-08-04 are closed. The three public-site pages that made claims the code had reversed — security.html on whether a model call runs on the request path, subprocessors.html and privacy.html on whether Anthropic is a sub-processor, security.html again on which surface the safe harbour covers — now say what is true, and subprocessors.html keeps a paragraph recording that it used to argue the other way. The six admin screens that were reachable only by typing the URL now sit behind a permission-gated Admin item in the masthead. One new item takes their place.

GapWhat it says, and what is trueState
P1-5
The test count is 39 behind in four documents
make fresh-check at HEAD (65c42d7) prints 2,340 passed, 1 xfailed and exits zero. Three documents carry 2,301 passed, 2 xfailed across five places: submission.html at lines 246, 1035 and 1052, tech-questions-faq.html line 58, interview-bank.html line 60. These are not dishonest — every one is stamped with the commit it was measured at (a5c20cb, 3ab55b7, bd21a5a), which is this project's own convention for a dated snapshot. The problem is narrower and worth fixing anyway: submission.html line 246 introduces the figure as "the reviewer's command, run as a reviewer runs it", and a reviewer who runs it gets a different number in the first minute of reading. No test file changed between 05b05e0 and HEAD, so the drift is older than the stamp suggests. The cost is a reviewer's first impression; the fix is one re-run and five edits.
Update 2026-08-07: docs/.ai/briefing.html was a fourth and staler case — it carried 2,006 passed at commit 68cbfa2 — and has been re-verified against the tree and corrected, along with 21 other claims. The three documents above are untouched and this row stays open for them.
Closed 2026-08-08: make fresh-check was re-run at commit ba5965b and printed 2,353 passed, 1 xfailed in 307 seconds, exit zero. All five places now carry that figure, each stamped with this date and that commit: submission.html in three places, tech-questions-faq.html, interview-bank.html. resume.md was a sixth place this row had not counted, and it is corrected too. The line in submission.html that introduces the figure as "the reviewer's command, run as a reviewer runs it" now matches what a reviewer running it gets.
CLOSED 2026-08-08 at ba5965b.

P2 — real code gaps a panel question would expose

GapWhy it mattersState
P2-3
start_run is called by nothing
Narrowed from four items to one. propose.py now has real callers — materiality_for_company from changes.py:1014 and shown_materiality_for_company from projects.py:443 and chat/tools.py:1103 — so the interpretation gate and the thing it gates are connected. can_approve has its caller in app/web/views/actions.py:495 (ADR-91). revert_event has one in app/state/replay.py:704. That leaves start_run in app/state/workflow.py:1744, exported in __all__ and reached from tests/ only: no approval run starts from any screen. No ADR covers it, which is the part to fix first — a gap with reasoning written down is a different answer at a panel than a gap without. CLOSED 2026-08-10. start_run has a caller: POST /escalations/{id}/route, actor from the session.
P2-4
record_note is gated on an approximate code
Narrowed. It is no longer ungated: PERMISSION_NOTE_WRITE = "knowledge.write" in app/chat/tools.py:249, and the comment above it says in full why that is the closest code the product defines rather than the exact one. The exact code, note.write, is still not defined, so the grid gives a note-writer everything knowledge.write covers. The reasoning is written down, which is the difference between this and where it started. CLOSED 2026-08-10. note.write defined and granted to the analyst and the obligation owner; the alias is gone.
P2-7
A source status question, deliberately deferred
Unchanged and still deliberate. FETCHABLE_SOURCE_KINDS is empty, so app/web/views/integrations.py answers no for every kind. Setting it to name the public docket moves a registration that names no document from not_implemented to never_tried — different sentences to a reader, and four tests assert the first. Neither fits a row whose registration is merely incomplete. Written down rather than answered in a hurry. CLOSED 2026-08-10 as examined, not built. No live defect; the hazard is guarded.
P2-14
Nothing tests that the site's palette and the application's agree, and the obvious test would lie
deploy/site/site.css mirrors verbatim.css by hand, token by token, each naming the line it copies. It does not import it, and it must not: the site is allowed a mesh on its chrome and the application is not allowed one anywhere (ADR-94). Hand-mirroring with no test is exactly the condition four token-family defects arrived under during the redesign. The warning matters more than the gap. The obvious parity test — read --accent from one file, --stamp from the other, compare — would be wrong, because the two files do not share a scheme structure. verbatim.css answers prefers-color-scheme: dark and then prefers-contrast: more three times, once plain and once for each scheme; site.css answers dark once and prefers-contrast: more once, undivided. A last-occurrence lookup therefore compares the application's high-contrast-dark value against the site's dark value and reports a mismatch that does not exist — a red suite for a correct pair, which is worse than no test, because it gets deleted rather than fixed. Any test here has to parse the scheme each declaration sits in and compare like with like. CLOSED 2026-08-10. Checked against the mapping site.css declares on every line, both schemes, mutation-checked.
P2-15
The gradient guard reads one of the two stylesheets
tests/test_design_guards.py::test_no_record_surface_carries_a_gradient reads app/web/static/verbatim.css and nothing else. The sibling CDN test sweeps both sheets and every page; this one does not. So the site's own version of the rule — that a mesh may only ever sit on chrome, and never on .rec, .withheld, .mismatch, .stage, .note or .srctext — is held by a comment at deploy/site/site.css:356. That is the state ADR-94 was written against, in the file where the redesign put the hero refusal: the site is the surface with actual gradients on it, so it is the one where a wash could land behind evidence. The fix is the same check pointed at site.css with that file's own selector list and its own exemption for the hatch in the empty statement slot, which is argued for in place. CLOSED 2026-08-10. The guard reads both stylesheets and is parametrised over them.
P2-16
The 62rem bound on the table scroll rule
Above 62rem a table stops being its own scroll box, so a register with more columns than a laptop can hold pushes the whole document sideways. Two do: /admin/sources ran documentElement.scrollWidth to 1368 and /admin/invites to 1377, both against a clientWidth of 1280, measured in a browser rather than read off the markup. Lifting the bound fixes both and shrinks seven other tables off the right edge — the two on a proceeding go from 1168px to 565px and 499px — because a block table shrinks to its columns instead of filling its row. So the treatment is named .table-scroll and worn where it is needed, and verbatim.css:4121 says in capitals that the bound is a hole and not a decision: a table is too wide when it is wider than the space it has, which is a fact about the table and not about the screen. Recorded here so it is not read as settled. CLOSED 2026-08-10. Every 6+ column register wrapped, guarded, mutation-checked. Was written down in the stylesheet as open.
P2-19
The changes screen labels a testimony line number as a section, and fixing it is a corpus migration
app/ingestion/ingest.py::_section_of takes the leading integer of a passage and writes it to Passage.section, which the changes screen renders as Sec 193 when 193 is the line number printed down the left of a deposition page. It is the same defect ADR-102 measured on the forward path: the leading integer fires on 39.7% of the 107,122 passages in data/real and returns 2025, 207 and 480 among its labels. The forward path was measured and the tree that would have fixed it was withdrawn; the backward path was never touched. Why it is not a line change. Passage.section is stored, so correcting the rule re-labels every passage of every ingested filing — a derived-corpus migration under best-practices §27, all at once, with the store rebuilt, not rule by rule against a half-old table. The gutter detector in app/diff/structure.py is the piece that would carry it: it flags 19 of the 102 filings and every one is direct or rebuttal testimony. Recorded here because ADR-102 says it is, and a decision that names a gap nobody filed is a decision with a false sentence in it. CLOSED 2026-08-19, and it did need a reload rather than an edit. app/text/gutter.py now holds the line-number detector, so app/ingestion/ingest.py can ask the same question the parser asks without importing it — ADR-102’s unwiring is intact and there is one implementation, not two. _section_of refuses a BARE leading integer in a line-numbered document and still reads “SECTION 4.” and “4.4”, because a gutter never writes those. scripts/relabel_sections.py carried the corpus: 105 versions, 107,257 passages, 21,281 passage labels and 261 change labels moved, 17,524 of them cleared a line number and now carry none. Both tables in one transaction, idempotent on a second run, and zero bare-integer labels remain inside the 19 transcripts.

P2 — the demonstration itself

GapWhy it mattersState
P2-13
The film shows the product before the redesign, and its poster still carries the teal mark
deploy/site/verbatim-demo.mp4, verbatim-demo.webm and verbatim-poster.jpg were recorded on 2026-08-05 in a59be0c — the commit whose message is "The film on the landing page is the film the product now is". That sentence went false on 2026-08-07, when the palette moved to indigo, the masthead was rebuilt and the display face landed. The poster frame is the worst of it: it is the still a reviewer looks at before pressing play, on a page whose every other pixel is indigo, and it shows the old teal mark. scripts/film.py was recoloured with the rest of the six painted places and now names #2f4bd8, so re-rendering is the fix and nothing else is in the way. This was scoped out of the redesign deliberately — re-recording needs the whole seeded walkthrough driven again and it is not a stylesheet change — and it is recorded here rather than left unsaid, because a film that contradicts the page around it reads as a product screenshotted at two different times. CLOSED 2026-08-10. Re-recorded from scripts/film.py against the redesign; 42 shots, 129s. See also the reviewer's order.
P2-17
Half the real corpus reaches no screen
data/real/ holds 102 documents across eight jurisdictions. scripts/ingest_real.py and scripts/seed_real_findings.py between them load four: Georgia, Kentucky, Missouri and Utah. Indiana, North Carolina, Ohio and Virginia are never ingested — 52 files, more than half the corpus, on disk and in no database. Nothing is broken and nothing fails: the loaders name the dockets they load and load them correctly. The documents were simply gathered and not wired up.
Two costs. The counts this project quotes are true of the corpus and not of the product: a reader who hears "102 real filings across eight jurisdictions" reasonably infers the product has read them, and it has read fifty. And the Indiana set is the whole lifecycle of IURC cause 46258 — AES Indiana's verified petition, Spanos and Davis-Handy direct testimony, three OUCC exhibits, the settlement motion, the final order and the OUCC's rehearing petition. A contested $192.9M rate case, start to finish, sitting unread by the product it was collected for.
CLOSED 2026-08-10. 102 of 102 read; scripts/ingest_dockets.py, 19 dockets.
P2-18
Nothing evaluates the model path
make eval scores five metrics and passes all five, and its own caveat says why that is narrower than it looks: "No model runs here and no network call is made, so nothing in this scorecard says anything about model behaviour. The model path is evaluated separately or not at all." It is not at all. There is no eval on judge_materiality, none on the assistant in app/chat/, and none on the answerer added with /explain. What exists instead is one live transport probe from 2026-08-05 proving the wire format works once.
The scorecard is honest about this and says so unprompted, which is why it is a P2 rather than a P1. It becomes the blocking item the moment a customer's own documents reach materiality judgement.
CLOSED 2026-08-10 with I3. app/evals/model.py and two workflows. make eval still prints five metrics and still opens no socket, which is the reason the model path got its own harness rather than a sixth metric. ADR-101.
P2-8
The demo route lets the admin approve their own route
scripts/seed_route.py still sends STP-2 "Legal review" and STP-4 "Officer signs the filing" to role:admin, so the account that draws the route approves through it twice — the exact segregation-of-duties failure this product exists to surface, in the demonstration a panel opens. The choice is still open: fix it, or make it deliberate and let the conflict report catch the demo's own configuration, which is a stronger demonstration. Leaving it accidental is the only bad option, and three days later it is still accidental — nothing in the script's docstring claims it. CLOSED 2026-08-10, and it was not the defect this row described. See the entry.

P2 — named by a technical interview on 2026-08-10

Where these came from, and what they are not. An AI interviewer put the architecture through a session on 2026-08-10 and scored four areas. It called the deterministic core robust and the fabrication-rate versus miss-rate distinction precise, and it named two development areas and four next steps. Those are I1 to I5 below.
This is not user research. It is an assessment of the engineer, not a regulatory analyst describing their day, and it moves nothing in docs/user-research.html, which still records 45 messages and zero interviews. Two of the five rows are narrower than the interview stated, because the code already does part of what it asked for; both say so.
GapWhat it isState
I1
The scalability strategy is a constraint list, not a design
Verbatim: "the scalability strategy for processing 10,000 concurrent multi-million-word documents remained high-level, primarily noting memory and CPU constraints." That is a fair reading. app/diff/engine.py runs SequenceMatcher in process over two whole passage lists, and app/pipeline.py walks them one pair at a time. Nothing partitions a document, nothing bounds memory against its size, and there is no queue, no worker pool and no backpressure anywhere in app/. None of that is a defect at this size — 19 dockets and 105 versions run in seconds — and it is exactly the question a procurement conversation asks first. Answering it needs a written design, not code.
Answered 2026-08-10, and narrower than the interview stated. docs/scalability.html carries the curve measured by scripts/bench_diff.py, and section 4 designs the pool, the queue and the admission check. The premise turned out wrong on this codebase: on a 3.29-million-character pair, normalization is 87% of the call and the list-level SequenceMatcher is 1.5%, so anybody who reads "SequenceMatcher is quadratic", opens the engine and starts tuning the matcher spends a day and finds nothing. Partitioning was measured rather than argued — index partitioning turns the real Kentucky pair's 144 changes into 777, content anchors reproduce the answer and run slower, and neither helps the quadratic case. The quadratic is the price of autojunk=False, which is a correctness setting: turning it off reports 150 changes where 2 occurred, 149 of them pairing a passage with a passage carrying the same text, each with a citation that verifies. ADR-96.
ANSWERED 2026-08-10 as a design, and nothing is built. No pool, no queue, no admission check; the size of the corpus is what keeps this honest, and that is not an argument. Two things stay open beneath it: the binding limit is one bad document rather than ten thousand of them, and the measurement turned up finding 17 — a live scoring defect held open on purpose by ADR-97 until a corpus reload can carry the fix.
I2
No truncation policy, because there is no multi-turn context yet
Verbatim: "the LLM helper's context management and token truncation strategies for long user sessions were not fully defined." Narrower than stated, and worth being exact about. There is no long session to manage. app/chat/engine.py accepts a history argument and deliberately ignores it, and says why at length: "Passing prior turns back into the model is a real feature and a real decision ... and inventing it inside a shim would be smuggling in a behaviour nobody chose." app/explain/answerer.py sends a single user message and keeps nothing. So today's context is bounded by construction and there is nothing to truncate.
The gap is the inverse of the one named: multi-turn is unbuilt, and the policy has to be designed before it is built, not after. The interview's own next step is the right policy and has no code to attach to — "prioritizes retaining the deterministic diff offsets over older conversational turns." A citation offset dropped to make room for chat is a claim that stops verifying, which is the one failure this product exists to prevent.
Built 2026-08-10. The rule the interview named is a module and a tripwire rather than a sentence. app/chat/budget.py holds one ordering — the question, the citation spans and the diff offsets are the floor and are never evicted; turns go oldest first and contiguously — and when the floor alone will not fit it refuses the turn rather than shaving a citation to fit. tests/test_history_needs_a_budget.py walks the syntax tree of app/ and fails the day a value derived from the conversation reaches a model call without going through it, which is what makes it a rule rather than a preference. ADR-98, ADR-99, ADR-100.
CLOSED 2026-08-10, as far as the gap goes. Multi-turn is still unbuilt and deliberately so, and the policy is now written before the feature rather than after it. Three things stay conceded on the page and in the ADRs: the budget is measured in characters, not tokens, because no offline tokenizer is a dependency and a ratio would be invented; a single turn still grows across up to four tool rounds with per-tool caps and no total governing them, which is a second budget nobody has written; and the tripwire matches by name, so a rename walks past it.
I3
The eval is not a gate
Verbatim: "build a test harness that runs the fabrication and miss rate assertions against a golden dataset of historical filings on every commit ... a CI/CD pipeline that automatically blocks deployments if the fabrication rate exceeds a strict zero-tolerance threshold." make eval scores five metrics and passes all five, and nothing runs it before a deployment. Add the model-path eval P2-18 asks for and this is the same item with teeth: a threshold that stops a deploy rather than a scorecard somebody reads. The golden set exists — 102 real filings across eight jurisdictions, all now ingested by scripts/ingest_dockets.py.
Built 2026-08-10. app/evals/model.py is a third harness with its own entry point, its own page and its own exit codes: 26 cases, five from the manifest and 21 drawn from seven real version pairs, four metrics, and one of them blocks at a threshold of zero. Exit 2 means the model was not evaluated and is never a pass. .github/workflows/ci.yml runs make test and make eval on every push and requires the dry run to exit 2, so the nothing-sent path is proved rather than assumed; the send path is a separate target and a dispatch-only workflow, because a gate that bills somebody for a typo in a docstring is a gate switched off inside a week. ADR-101.
CLOSED 2026-08-10, and P2-18 closes with it. Three limits conceded on the page rather than here: nobody has ever run it with --send, so every outcome path is exercised by a deterministic fake and nothing yet says what a real model does over real filing text; the reasoning metric rests on three labelled booleans, because two of the five manifest changes state no materiality expectation and inventing them would be the eval writing its own answer key; and a run in which almost every call fails still exits 0 on an empty denominator, which the page shouts and the exit code does not.
I4
Flat passage lists, and a section number standing in for structure
Verbatim: "design regulatory documents into a tree structure before diffing, rather than relying solely on flat passage lists ... eliminating the reliance on fragile positional proxies." This names the compromise _alignment_confidence already documents against itself: when Section 6 becomes subsection 5.4 the words barely move, so similarity runs high exactly when structural identity has changed, and the only defence is that a disagreeing section label caps the score at RESTRUCTURE_CONFIDENCE_CEILING. That cap is a proxy and the docstring says so — it turns a confident wrong answer into an honest uncertain one, which is the right trade and not a solution. A parsed tree would let the engine align by position in the document's own hierarchy and know a renumbering for what it is.
Measured 2026-08-11, and deliberately not wired in. Read this row before the other four. The parser exists: app/diff/structure.py reads a filing's own numbering into a tree where it can and refuses with a reason where it cannot, and 27 of the 102 real filings yield a hierarchy. The proxy was measured and it is as bad as the docstring feared — the leading integer fires on 42,546 of 107,122 real passages, 39.7%, returning 2025, 207 and 480 among its labels, and 112 of 421 modified changes across the eight real pairs escalate only because a printed gutter number shifted by one. Then the signal was wired in and measured, and it was withdrawn. The two branches it was built for — a hierarchy contradicting two agreeing labels, a hierarchy confirming that two passages sit under one node — fired zero times each on real filings. All 112 movements came from a third branch nobody had authorised: the parser failing to read both documents, and that failure being used to withhold the restructure cap, which is absence licensing certainty. Nothing under app/ imports the parser and a test holds that. ADR-102, docs/structure-measurement.html.
STILL OPEN as the interview stated it, and now with a measurement under it. Alignment is still sequence alignment over a flat passage list; no tree is consulted by anything the product runs. What closed is the question of whether this parser was the answer: measured, it was not, and the negative result is the deliverable. Three things sit beneath it — the 112 false escalations are still in the queue, the named fix (the section label declining to read a leading integer on a page the gutter detector flags) moves 112 real confidences and needs its own decision, and the backward path is P2-19 below.
I5
The low-confidence flag reaches a screen and no decision
Verbatim: "design a fallback routing mechanism where the deterministic engine flags low-confidence alignments for human-in-the-loop or LLM review." Half of this exists. app/web/views/changes.py::_shows_alignment already raises a caution on any modified passage at or below the ceiling, and is careful not to invent doubt about added or removed passages, which carry 0.0 because nothing was aligned. What is missing is the other half: a reviewer who sees the caution cannot act on it. There is no control to confirm the pairing, reject it, or repair it, and no record of anyone having looked. The uncertainty is displayed and then dropped.
Built 2026-08-11. Measured first: 16 of the 27 changes in the seeded corpus carry the caution and 8 of them sit at exactly the ceiling, so the product was asking for judgement on more than half its own corpus and keeping none of it. A reviewer now answers with one of three verdicts — the pairing is right, the pairing is wrong, cannot tell — and the answer is an audit row against the change under its own action code, never a column and never a correction of the number. Two of the four states keep the caution and two lose it, decided by one predicate the screen and the chat clerk share. Answering makes the answerer an author, so they can no longer approve what follows from the change, which is the wanted outcome rather than a side effect. ADR-103.
CLOSED 2026-08-11. The route out of the caution exists and is recorded in the chain. One piece is deliberately left open and named in the ADR: no control is drawn on a pairing the diff was confident about, and that is exactly the unlabelled renumbering the cap cannot see. A test pins the current behaviour, so opening it is a design decision rather than an oversight. Also still there: the mapping read on the same screen still scans the tenant's audit history, because its SQL ordering is free on the index the planner was already taking.

P3 — only we would notice

GapDetail
P3-2CLOSED 2026-08-10. Three stale docstrings, since corrected. app/web/views/auth.py:31 says "no invitation flow" and app/web/views/invite_accept.py is an invitation flow. requirements.txt:47 says "no ORM migrations tool: the schema is created from the models at startup" and app/state/migrate.py exists. scripts/seed_demo_gaps.py:19 cites tests/test_demo_ready.py, which still does not exist. The two that are fixed: projects.py no longer claims the build makes no model call, and integrations.py is current on provenance and the fetcher.
P3-3CLOSED 2026-08-10, and the cause was not staleness. scripts/status.py ran the suite with a 120-second subprocess timeout; the suite takes 306. So every run since it outgrew that recorded "TEST COUNT NOT MEASURED" -- correctly, because a timeout really is a run with no summary, and the file refuses to invent one. The honest report was hiding a fixable cause: the instrument was too small for the thing it measures. Timeout raised to 900s, regenerated, measured=true at 2,423. It was unchanged since 2026-08-05 10:16. tests.measured is still false, and known_gaps still lists the _spans_of window scan fixed in ec1c4bf. This is the file two other documents read their numbers from, so it is stale in a way that spreads.
P3-4CLOSED 2026-08-10. Retitled "A second digest scheme", with a dated note saying why. It was titled "Two digest schemes, and the old rows are never re-hashed"; there are three. ADR-53 covers the addition, but a reader scanning titles takes the count from the title.
P3-5CLOSED 2026-08-10 as a stated limit rather than a number. The two live figures in that file were re-verified and are exact: --ink-3 on --paper is 4.27:1 and --ink-2 is 7.48:1, both to the hundredth. The veil figures are teal-era, labelled as such, and cannot be recomputed from tokens -- --glass-veil is translucent and its contrast depends on the film frame behind it, which is the error verbatim.css corrected four times on one comment. The file now says so. It claimed the --glass-veil mix reads 5.3:1. The earlier audit computed a floor of 4.98:1 and guessed a dropped digit from 5.03. Still not re-sampled by anybody, so this stays recorded as unverified rather than resolved in either direction. Both figures clear the 4.5:1 threshold, which is why it has stayed a P3.

Closed since 2026-08-04, listed so this page is not read as the whole picture

WasNow
P0-1 The repository was privatePublic. The decision was the owner's and it was taken.
P0-2 Around 13,500 lines uncommitted across four subsystemsCommitted. The working tree is clean, and the scheduler, the docket fetcher, point-in-time replay and granular permissions are all in the history a reviewer reads.
P0-3 make test failed on a fresh cloneThe retention window the derived guard wanted is declared for user_permissions in app/state/retention.py:593. make fresh-check — which clones HEAD into a temporary directory so nothing uncommitted can make it pass — exits zero.
P0-4 Every "Scope" link answered 500app/web/templates/review_project.html is written, and both routes in review_centre.py render it.
P0-5 permissions missing from nginxIn the proxy list at deploy/nginx.conf:106, with a comment above it recording why a screen that works locally and 404s in production is the worst shape a bug can take.
P0-6 make run seeded zero obligationsseed_demo_gaps.py is called by the seed target and by deploy/entrypoint.sh, last in an order the entrypoint's comment says is load-bearing rather than tidy.
P1-1 "No document text goes anywhere"deploy/site/security.html now describes the request path as it is, and says in the same paragraph that the page used to say otherwise.
P1-2 Anthropic is a sub-processor and the page denied itsubprocessors.html names Anthropic, says what every turn of the chat panel carries, and keeps a note that the page earlier argued the opposite at length.
P1-3 Safe harbour pointed at the wrong surfaceThe "is not deployed behind that name" sentence is gone.
P1-4 Six admin screens had no way inAn Admin item in the masthead, drawn from a permission and outside the seven-screen loop on purpose — the comment in base.html gives the reason, which is that a permission field that is always None on every analyst row is a field the next reader has to test to disbelieve.
P2-1 Tenant check missing in ensure_obligationFixed. The function fetches through row_for_company, and its docstring carries the whole failure in capitals so the next reader cannot remove the check by accident. This was the product's central control and the panel would have asked.
P2-2 _require_scope did not reject "%"SCOPE_WILDCARDS = ("%", "_") in app/state/queries.py, with each refused value and its reason written out. The isolation tests were fixed in the same change: they had accepted an empty list as proof, which is what a deleted guard also produces.
P2-5 The two files that protect the deploy had no teststests/test_config.py and tests/test_migrate.py both exist.
P2-6 Coverage was not reproduciblecoverage==7.15.* and pytest-cov==7.1.* are pinned in requirements.txt, with a comment saying they are there so the published figure can be reproduced.
P2-9 The demo video predated most of a day's workRe-recorded in a59be0c — "The film on the landing page is the film the product now is".
P2-10 Nobody had keyboard-tabbed the product since the glass landedtests/test_a11y_guards.py and tests/test_glass_contrast.py exist. The nav's unreachable items are now real elements with the reason in the accessible name rather than in a title attribute a screen reader may skip — WCAG 2.1.1 and 4.1.2, from one attribute.
P2-11 Logo mismatchOne mark. The app and the site render byte-identical SVG: the three-bar glyph on #0d5c6b with mixed-case Verbatim beside it. That hex is the reading taken when this row was closed on 2026-08-04 and is left as the record of it; the mark moved to #2f4bd8 with the palette on 2026-08-07, in all six places it is painted, and the two files are still byte-identical.
P2-12 The blog was not linked from the landing pageLinked twice, from the body and from the footer.
P3-1 The persona was called "Clerk" in one fileClarke everywhere.
P3-6 Outreach counts disagreed across three documents45 messages, zero replies, consistently, in the MRD and in user-research.html. The "first wave of 7" is the first wave and not the total, which is what made the numbers look contradictory. The send log is still not in the repository.
What this list does not contain. Anything that could not be checked. Where a claim could not be verified from the repository — the site.css contrast figure is the one left — it is recorded above as unverified rather than resolved in either direction. Nothing on this page was carried forward from the 2026-08-04 version without being re-run against the code first.