Extract consolidation fixes - #86
Conversation
# Conflicts: # src/lib/clinical-search.ts # tests/worker-visual-capture.test.ts
There was a problem hiding this comment.
Pull request overview
This PR consolidates indexing/reindexing improvements onto a clean branch by introducing generation-committed index artifacts (for atomic reindex), adding bounded concurrency to image captioning, and tightening search semantics to only consider committed generations.
Changes:
- Adds an index-generation “commit” RPC + generation filters so search only returns artifacts from the committed generation.
- Introduces bounded concurrency for image caption/classification calls, while keeping deterministic selection/budgeting.
- Adds small, bounded caching for clinical query analysis and expands tests around dedupe and reindex generation helpers.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| worker/main.ts | Implements atomic reindex behavior, generation commit fallback, and bounded image caption concurrency while tagging artifacts with index_generation_id. |
| tests/worker-visual-capture.test.ts | Adds a regression assertion to ensure optional index write issues are surfaced in the worker source. |
| tests/reindex-pipeline.test.ts | Adds coverage for atomic reindex candidate detection and committed-generation comparison helpers. |
| tests/chunking.test.ts | Adds a test ensuring same-page chunk dedupe survives punctuation/table-label noise. |
| supabase/schema.sql | Updates schema snapshot: generation-aware uniqueness, commit RPC, and generation-committed filters inside retrieval functions. |
| supabase/migrations/20260628000000_atomic_reindex_generation_commit.sql | Adds migration for atomic generation commit + patches retrieval functions to enforce committed-generation filtering. |
| src/lib/reindex-pipeline.ts | Adds helpers for extracting committed generation metadata and comparing artifact generations. |
| src/lib/clinical-search.ts | Adds a small bounded in-memory cache for clinical query analysis results. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await insertPageRows(args.pages); | ||
| await upsertIndexQuality(args.quality); | ||
| } |
| async function processJob(job: JobRow) { | ||
| const atomicReindex = isAtomicReindexCandidate(job.documents); | ||
| await updateJobProgress(job.id, { | ||
| stage: "downloading", | ||
| progress: 5, | ||
| }); | ||
| await updateDocument(job.document_id, { status: "processing", error_message: null }); | ||
| if (atomicReindex) { | ||
| await updateDocument(job.document_id, { error_message: null }); | ||
| } else { | ||
| await updateDocument(job.document_id, { status: "processing", error_message: null }); | ||
| } | ||
| await updateBatch(job.batch_id); | ||
| let extracted: ExtractedDocument | null = null; | ||
| try { | ||
| await resetDocumentIndex(job.document_id); | ||
| if (!atomicReindex) await resetDocumentIndex(job.document_id); | ||
| const buffer = await downloadDocument(job.documents.storage_path); |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:02d1ea5e35
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try { | ||
| await resetDocumentIndex(job.document_id); | ||
| if (!atomicReindex) await resetDocumentIndex(job.document_id); |
There was a problem hiding this comment.
Filter direct document reads during atomic reindex
When this atomic path reindexes an already indexed document, it leaves the document visible and inserts new document_chunks/document_images before commit_document_index_generation publishes the generation. I checked the document viewer/search routes: src/app/api/documents/[id]/route.ts still selects chunks/images directly, and src/app/api/documents/[id]/search/route.ts calls search_document_chunks, whose migration only checks d.status = 'indexed'. If extraction or embedding fails before commit, those new-generation rows remain uncommitted but are still returned by these direct reads, so users can see duplicate or partial failed reindex artifacts. Please add committed-generation filtering/cleanup for the non-RAG direct read paths too.
Useful? React with 👍 / 👎.
| try { | ||
| await resetDocumentIndex(job.document_id); | ||
| if (!atomicReindex) await resetDocumentIndex(job.document_id); |
There was a problem hiding this comment.
Avoid overwriting committed image files before commit
When atomicReindex is true for a document with extracted images, skipping the reset leaves the old document_images rows committed while uploadAndCaptionImages still writes deterministic paths like .../images/${document_id}/image-${index + 1} with upsert: true. If the reindex fails after those uploads but before commit_document_index_generation, the document remains indexed and the old rows still point at the same storage paths, but the objects now contain bytes from the failed new generation. Use generation-scoped image paths or delay overwriting the committed paths until the generation is committed.
Useful? React with 👍 / 👎.
| embedding_model: env.OPENAI_EMBEDDING_MODEL, | ||
| ...metrics, | ||
| }, | ||
| const committedCoreMetadata = { |
There was a problem hiding this comment.
Clear stale agent state in the atomic commit
For an atomic reindex of a document that previously completed enrichment, this metadata object preserves old keys such as indexing_v3_agent_status: "completed" until the later final metadata update. If the worker exits after commitDocumentIndexGeneration succeeds but before that final update, the commit RPC has already deleted the old sections/memory cards, while claim_indexing_v3_agent_jobs skips documents whose agent status is completed; the new core index is then left without a repair job to rebuild enrichment. Clear/stamp the agent status as pending in the metadata passed to the atomic commit instead of carrying stale state forward.
Useful? React with 👍 / 👎.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| const atomicReindex = isAtomicReindexCandidate(job.documents); | ||
| await updateJobProgress(job.id, { | ||
| stage: "downloading", | ||
| progress: 5, | ||
| }); | ||
| await updateDocument(job.document_id, { status: "processing", error_message: null }); | ||
| if (atomicReindex) { | ||
| await updateDocument(job.document_id, { error_message: null }); | ||
| } else { | ||
| await updateDocument(job.document_id, { status: "processing", error_message: null }); | ||
| } | ||
| await updateBatch(job.batch_id); | ||
| let extracted: ExtractedDocument | null = null; | ||
| try { | ||
| await resetDocumentIndex(job.document_id); | ||
| if (!atomicReindex) await resetDocumentIndex(job.document_id); | ||
| const buffer = await downloadDocument(job.documents.storage_path); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
* fix: ledger merge driver dedupes exact rows on sync Replace stock merge=union with a custom ledger driver that unions concurrent appends and drops byte-identical twins, add ledger:dedupe for checkouts without the driver, tighten Run PR babysit append policy, and close#88 now that the residual exact-dupe class is gated. * feat: rotate branch-review ledger into quarterly archives (L4) Add ledger:rotate to move older dated rows into docs/archive/branch-review-ledger-<yyyy-qN>.md, teach lookup/sweep/check to read the archive corpus, bootstrap by archiving pre-2026-07-29 rows, and mark maturity backlog L4 done. Also fix the CLI entry guard so check-branch-review-ledger no longer falsely matches as the ledger CLI. * fix: harden ledger CLI entry guards against suffix false-match check-branch-review-ledger.mjs ends with branch-review-ledger.mjs, so a bare endsWith guard can execute the wrong CLI when modules import each other. Match on a path segment instead. * fix: add JSDoc types for ledger rotate helpers TypeScript inferred calendarQuarterStart(value = new Date()) as Date-only and dropped `before` from rotateLedgerMarkdown's options object, which failed Static PR typecheck on the hygiene tests. * issues: drop L4 from #86 Remaining; capture #126 quarterly rotate L4 shipped in #1418. Track a quarterly ledger:rotate reminder so the live table does not grow unbounded again. * docs: mark L4 DONE and clarify ledger merge/rotate residual risks Progress summary table still said OPEN after L4 shipped. Document exact-only dedupe, install-required merge driver, and quarterly rotate practice. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
main's #1418 (ledger merge dedupe + L4 quarterly archive rotation) and #1413 both edited docs/outstanding-issues.md, so this was a real content conflict rather than staleness: git merge-tree --write-tree confirmed CONFLICT before any resolution was attempted. Resolved by taking main's version of the ledger wholesale and re-applying this branch's five-row archive move on top, so neither side's work is lost: - from main: #88 and #97 archived, new open row #126 (quarterly ledger rotation) with queue order 35, the #23 "When" update (release-browser-matrix no longer blocked by pr-required), the #86 detail update, and the issues:next-id bump to 127. - from this branch: #95, #96, #104, #109 and #115 moved from Open items to Resolved / archive. No row from either side was dropped, and no id appears in both tables. Verified: 121 rows (52 open, 69 archived), marker next-id=127 above the highest; each of #88, #97, #95, #96, #104, #109, #115 resolves to exactly one archive row and #126 to one open row; zero conflict markers remain. npm run verify:cheap -> EXIT=0; "Gate-manifest OK: all 29 verify:cheap gates are enforced in CI"; "Test Files 431 passed (431)"; "Tests 4496 passed | 4 skipped (4500)". npx prettier --check . -> "All matched files use Prettier code style!" Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YdPS2KhKqz2buzsUgmX3c
Resolves the docs/outstanding-issues.md conflict with PR #1453. That file deliberately carries no merge driver (#133), so overlapping appends conflict loudly rather than being silently concatenated. Resolved as the issues skill requires: rebuilt the file from origin/main and re-applied only the two rows this branch owns (the #86 in-place update and the new #145), so none of #1453's rows were dropped. Verified #140-#144 all still present and #144's cell content byte-identical to main. Re-checked that #145 was still free on main before reusing the id — main's next-id marker was untouched at 145, so there was no id collision to reallocate around. docs/branch-review-ledger.md auto-resolved through its merge=ledger driver. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
Codex was right on PR #1461. The previous wording claimed extracting the hydration cluster re-homes all five rag.ts-only symbols that prepareCoverageGateResults depends on, so that it could then move cleanly. It re-homes only two. Verified by symbol location on 102bb1f: the hydration cluster is rag.ts:1487-1718 and holds attachDocumentRankingMetadata and attachPageVisualEvidence. The other three are outside it — selectRankedRetrievalResults (1825), applySecondStageRerankIfNeeded (679), and measureSearchPhase (1975), the last being a shared pipeline timing wrapper with 21 references of which only two are hydration phases. The row now separates the actual hydration symbols from the remaining orchestration/ranking seam and states that hydration alone is not sufficient, so a future contributor is not sent at a false module boundary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
Main's #1461 reflowed the open-items table while this branch appended #17's live Web-Vitals verdict, so the two sides conflicted for real (`git merge-tree` dirty, not staleness). Resolved row by row rather than by taking either side: kept this branch's #17 BREACH verdict and #105 preconnect evidence, took main's #86 X3 progress and its new #145 row. #105 closes. Its remaining half — the `LoadingPanel` fallbacks — is now verified, and by a different method than the row prescribed. It claimed the fallback renders solely while the client chunk is in flight, so only a throttled-network run could observe it. That is wrong: the installed Next 16 loader wraps an `ssr:false` import in Suspense whenever a `loading` element is supplied, and `BailoutToCSR` throws on the server, so the fallback is emitted in the server response HTML. Confirmed against the running dev server — `role="status" aria-label="Loading"` appears 1x on `/`, 2x on `/dsm`, 2x on `/forms`. No throttled run and no `verify:ui` were needed. Finding raised by Codex on PR #1459. The two recommended-queue entries now match their detail rows: #105 is dropped from the queue, and #17 no longer directs a reader to capture evidence that has already been captured and graded — its next action is ranking the mobile findings by measured contribution and cross-checking the raw Lighthouse JSON artifact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01361jh3eYVjJCzXWjAhdZiF
Resolves the docs/outstanding-issues.md conflict against #1441 and #1470. That file deliberately carries no merge driver (#133), so overlapping edits conflict loudly rather than being silently concatenated. Resolved by the prescribed recipe: rebuilt the file from origin/main and re-applied only this branch's own change (the #86 row's "Hydration SHIPPED (#101)" edit). Verified the result has an identical row count and an identical id set to origin/main, so #1470's closures were preserved and nothing was dropped; the only content delta against main is that one row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
Second docs/outstanding-issues.md conflict, this time against #1459. That file carries no merge driver by design (#133), so any overlapping edit conflicts. Resolved by the same prescribed recipe: rebuilt from origin/main and re-applied only this branch's own #86 "Hydration SHIPPED (#101)" edit. Verified identical row count (144) and identical id set to origin/main, so #1459's ranking change, withdrawn verification and restored row are all preserved; the only content delta against main is that one row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
Third docs/outstanding-issues.md conflict, this time against #1462, #1467 and #1482. That file carries no merge driver by design (#133), so any overlapping edit conflicts; main is landing issue-ledger commits continuously. Resolved by the same prescribed recipe: rebuilt from origin/main and re-applied only this branch's own #86 "Hydration SHIPPED (#101)" edit. Verified identical row count (146) and identical id set to origin/main, so the archived rows from all three of those PRs are preserved and nothing was dropped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
Fourth docs/outstanding-issues.md conflict. Same cause and same prescribed resolution: that file has no merge driver by design (#133), main is landing issue-ledger commits continuously, so every sync collides on it. Rebuilt from origin/main and re-applied only this branch's own #86 "Hydration SHIPPED (#101)" edit. Verified identical row count (146) and identical id set to origin/main before committing, so no other session's rows were dropped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
Fifth docs/outstanding-issues.md conflict. Applying the default announced to the user after the fourth: drop the #86 "Hydration SHIPPED (#101)" row from this PR rather than keep re-resolving it. That file has no merge driver by design (#133) and main lands issue-ledger commits continuously, so every sync collided on it — five conflicts, each costing a full CI cycle, for one documentation line unrelated to the extraction. This branch now takes origin/main's copy verbatim and no longer modifies the file at all, making the PR immune to that churn. Nothing else changes: rag-hydration.ts, rag.ts at 4543, the budget ratchet, the codebase-index row and the X3 work-order entry all remain. The #86 row will be recorded in a separate follow-up PR after this merges — the same pattern used for #1454 via #1461. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
Both defects were raised by Codex on PR #1495 and both are real; verified against the files before accepting. 1. #101 is NOT this extraction. docs/outstanding-issues.md:138 shows #101 is "Canary-gated retrieval parallelisation candidates" (P3, rec) — a separate, still-open recommendation gated on a live canary pair. Calling the hydration extraction "#101" marked that unrelated work as shipped and could have caused the live-evaluation work to be skipped. The label came from the original task brief and was propagated without checking it against the ledger. Both the #86 row and the X3 work-order entry now identify the change as the X3 hydration unit (PR #1463) instead. #101's own row is untouched and still open. 2. The ledger row did not resolve. `npm run ledger:lookup -- dba7356` returned NOT REVIEWED, because the ref cell held only the slash-form branch token and that branch no longer resolves locally, so the throttling record could not prevent a repeat review. Appended a superseding record keyed to the landed SHA; the same lookup now returns ALREADY REVIEWED. The original row is retained, per the ledger's append-only rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS
* issues: capture the unreadable-CI token, at-risk worktree work, and the unpushed hook fix Three findings from the 2026-07-30 organisation session that were recorded nowhere durable: - #149 the session GitHub PAT lacks Checks: Read, so no agent can confirm a PR is green. The endpoint that does work returns an empty result rather than an error, so it reads like an absence of checks rather than an absence of permission. - #150 four worktrees on already-merged branches hold uncommitted work that exists in no branch and no PR, the largest being +395/-200 across 19 files including CI config. - #151 the pre-commit fail-open for #143 lives only on a never-pushed local branch, which is also 17 behind main and conflicts on the file whose count sentence main's new docs:update generator now owns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(ledger): record the session-followup capture review for PR #1490 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(ledger): record #143/#151/#149 reconciliation for PR #1490 Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs(ledger): supersede PR #1490 reconciliation after remote sync Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * issues: record the worktree snapshots and redirect #151 to PR #1494#150 — the four at-risk worktrees were snapshotted onto their own already-merged branches (748ef018f, 5dbd9f965, b7eae51a4, d949859c3), so the work survives a worktree reclaim. All four are clean now. None is pushed or reviewed; the next action is per-snapshot promote-or-reset. #151 — the never-pushed branch is superseded rather than salvageable: its script and hook reached main by other routes, so the fail-open guard was applied to main's committed hook in PR #1494 instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: remove credential metadata and correct audit dates * docs: consolidate session follow-up findings * docs: record consolidated follow-up review * issues: record that #101 hydration shipped PR #1463 merged as dba7356, so #86's "Next X3 unit — rag-hydration.ts" is now stale. The row records the extraction as shipped and keeps the corrected boundary: hydration re-homed only two of prepareCoverageGateResults's five rag.ts-only dependencies, so it did not unblock that function — exactly as the Codex review on PR #1461 predicted. This row was deliberately dropped from #1463 itself (commit 6290d02) after docs/outstanding-issues.md conflicted on five consecutive main syncs. Recording it separately here is the same pattern used for #1454 via #1461. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS * docs(ledger): record the landed X3 hydration review Appended with npm run ledger:append (never hand-written), keyed to the squash commit dba7356 so ledger:lookup can resolve it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS * docs: fix the #101 mislabel and key the ledger row to a resolvable ref Both defects were raised by Codex on PR #1495 and both are real; verified against the files before accepting. 1. #101 is NOT this extraction. docs/outstanding-issues.md:138 shows #101 is "Canary-gated retrieval parallelisation candidates" (P3, rec) — a separate, still-open recommendation gated on a live canary pair. Calling the hydration extraction "#101" marked that unrelated work as shipped and could have caused the live-evaluation work to be skipped. The label came from the original task brief and was propagated without checking it against the ledger. Both the #86 row and the X3 work-order entry now identify the change as the X3 hydration unit (PR #1463) instead. #101's own row is untouched and still open. 2. The ledger row did not resolve. `npm run ledger:lookup -- dba7356` returned NOT REVIEWED, because the ref cell held only the slash-form branch token and that branch no longer resolves locally, so the throttling record could not prevent a repeat review. Appended a superseding record keyed to the landed SHA; the same lookup now returns ALREADY REVIEWED. The original row is retained, per the ledger's append-only rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGEBHp4Seoh1jK1vGTNtYS * docs: record consolidated PR reviews * docs: record ingestion recovery review * docs(visual): document the platform-scoped baseline layout and how to seed it `playwright.visual.config.ts` records snapshots under `__screenshots__/{platform}/`, so a baseline taken on Windows lands in `win32/` and is never consulted by the `ubuntu-24.04` CI job, which reads `linux/`. Nothing said so, and committing `win32/` images looks like protection while providing none. Records the constraint, names the CI artifact as the supported recorder for `linux/` baselines, and notes that comparison stays advisory until the jobs come off `continue-on-error`. Also creates the tracked directory `.gitignore` already claims exists, which sets `ui_changed=true` (`scripts/ci-change-scope.mjs`) so the visual job can run and produce that first artifact. No baselines are added here — they cannot be produced on this platform. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: correct visual baseline adoption steps * docs: record visual baseline guidance review * fix(ui): repair mockup accent token references * docs: record token-reference repair review * docs: archive advisory UI scoping task * docs: record advisory UI closure review * issues: archive #151 after #1494 and mark #143 fully resolved PR #1494 landed the fail-open guard on main, so close the open salvage row and update the #143 archive from PARTIAL to resolved across #1442 and #1494. Also carries the merge of origin/main that cleared the GitHub DIRTY mergeability state. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs(ledger): record PR #1490 main-sync and #151 closeout Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs(ledger): record #1496 id-collision renumber for PR #1490 Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * issues: record the withdrawn live-region finding as #151 so it is not re-filed Archive-only row. There is no defect and no work to do — the row exists purely as a guard rail against repeating a misreading that already happened once. search-results-header-band.tsx sets aria-live={faulted ? "off" : "polite"} on its count/status span, which reads like a silenced failure announcement. It is not: the band mounts a separate fault panel with role="alert" carrying the failure title, body and Retry, and the mute is deliberate so the two do not both speak. The reasoning is in a comment directly above the attribute, and tests/search-results-header-band.dom.test.tsx pins it with singular role queries that throw on duplicates. During session 2026-07-30 (PR #1481) this was filed as a real P2 defect on the strength of the attribute alone, and the proposed fix — escalating the count span to role="alert"/aria-live="assertive" — would have produced a duplicate announcement and a red test, making it worse than no change. Codex caught it. An earlier withdrawal row was then lost to the squash that merged #1481, which is the row-deletion shape #148 now guards against. Also records that the mockup's escalation is correct in the mockup and must not be ported: search-refine-adaptive-mockups.tsx has no fault panel, so there the count span is the only announcement channel. #148 needed no work — the merge-base deletion check landed on main independently, and its output now reports the base it compared against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r * docs(rag): record refuted lexical probe collapse (#98) * issues: capture the residual id-allocation hazard as #151#133 is resolved: #1444 removed merge=union and #1479 excluded the ledger from Prettier, which together fixed conflict frequency. Neither changes id allocation, which is still read-modify-write against the next-id marker, so concurrent branches still claim the same number. Measured on PR #1451: one row was renumbered #135 -> #141 -> #145 -> #147 -> #149 across four sync cycles. The sharper finding is that GitHub's Update-branch button resolved one such collision into duplicate #141 rows with the marker left below main's highest id — git reported success and only check:outstanding-issues caught it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(issues): attribute the mobile CLS breach — a 128px reserve round trip #147 asked which elements shift. Driving Chromium against the same offline production build with a PerformanceObserver on layout-shift (Lighthouse mobile emulation, reading entry.sources[].node) gives one dominant cause on all four breaching routes: the entire main content region moves down 128px and straight back up 128px within 15-60ms. Both moves score, so it is pure cost with zero net movement — 100% of /documents/search's 0.220 and about 75% of /dsm's. The shifting element is the max-sm:pt-[var(--phone-overlay-chrome-h)] wrapper around <main>. A MutationObserver timeline on the root style attribute pins the mechanism rather than inferring it: the property goes CSS seed -> 200px -> 72px, and the 200px is written when the header stack ALREADY measures 72px (t=1552ms reserve=200px stack=72, corrected at t=1612ms). usePhoneOverlayChromeReserve reads stack.offsetHeight while the stack is transiently tall, publishes a value that is stale by the time it lands, and its ResizeObserver then corrects it. The CSS seed at globals.css:375 is correct for the settled stack, which corrects the mechanism recorded on the now-archived #130 — that framed the defect as the seed under-reserving by 0-8px. Measured, the driver is a 128px transient over-reserve written by the hook, not the seed. / is the control: it never writes the property and is the one clean route. Variance is stated rather than smoothed: /dsm measured 0.363 and 0.219 across two runs, and this harness has no network throttling so /forms and /therapy-compass run high locally. Only /dsm, /documents/search and / reproduced the live dispatch exactly. Also recorded: attaching a MutationObserver to document.documentElement inside a Playwright addInitScript throws before the document element exists, silently killing the CLS observer and reporting a uniform CLS=0.000 — a false clean bill that voided one run of this harness. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01361jh3eYVjJCzXWjAhdZiF * docs(ledger): record the #151 capture review for PR #1506 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(review): clarify snapshot branch state * docs(ledger): record PR #1490 main sync after snapshot wording Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs: archive rendered style contract task * docs: record style contract closure review * docs: record synced style contract review * docs: record post-121 style closure review * docs: normalize style review ledger after sync * docs: record post-1490 style closure review * docs: record consolidated PR 1490 review * docs: record replacement consolidation review * docs: record reconciled consolidation review * docs: record post-1511 consolidation review * docs: normalize PR 1510 ledger after main sync * docs: record PR 1510 post-sync review * docs: correct false #98 canary evidence and NOTES triage Remove the incorrect probe-collapse canary attribution from #98 and point the unread --med-accent-soft note at #157 without breaking the seven-token TOKENS_MISSING accounting. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs(ledger): record PR #1510 evidence-correction review Supersede the prior approve-with-no-findings row after correcting the false #98 canary attribution and NOTES triage drift. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs: keep concurrency note inside issue table * docs: record post-1513 consolidation review * docs: address CodeRabbit notes on PR #1510 Fix the computed-value-time wording in design-sync notes, give #33 a unique recommended-queue order, and drop the duplicated #98 Done block. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs(ledger): record PR #1510 CodeRabbit fix review Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
… queue text (#1890) * docs(issues): retire 22 non-actionable ledger rows and correct the #231 queue text A yield review of all 114 open rows against current main. The queue had become roughly 60 tasks and 50 notes; this removes the notes and fixes two places where the ledger was actively misdirecting. The correction that matters most: the recommended-queue entry for #231, the top clinical P1, told every session to "measure and fix the fast-route budget / generation timeout" — an approach #231's own detail records as tested and rejected, because the decisive 40-second probe completed generation in 25.272s with route_deadline_exceeded=false and still failed quality. The session-start hook prints the queue, not the row, so the refuted text was the text agents read. Closed 22 rows: - #304 was already done on main (commit d182844 refreshed the ranking snapshot; generatedAt is 0 days old, not 2026-07-20), yet sat in the queue advertising a freshness fuse that is not armed. - #241#244#272#294#300#257 were standing cautions whose own text says "no action". Each one's knowledge now lives in the code it protects, so closing the row loses nothing. - #196-#200 are five steps of the disaster-recovery checklist that is canonical in docs/operator-backlog.md, with no trigger until a restore. - #86#188 were index rows over children that are individually findable. - #250#253#254 were superseded; #250 and #253 say so themselves. - #156#301#152#236#260 merged into #168, #292 and #169 respectively — each pair or group was one problem recorded two to four times. Demoted 20 rows with a stated reason (premature ops for a single-user prototype, upstream-blocked, measurement-gated, or design-system adoption competing with an open clinical P1). The Pri cell is unchanged because the writer has no --pri flag — which is now #313. Added three rows for mechanism gaps this sweep exposed: rows outliving their own completion (#312), the missing --pri flag (#313), and the queue being able to contradict the row it cites with no guard (#314). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuYJz8hauCsCdx8r4fXiZU * docs(ledger): record the ledger yield review handoff Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuYJz8hauCsCdx8r4fXiZU * Keep recovery work visible and pin forced colors --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Verification
npm test -- tests/chunking.test.ts tests/worker-visual-capture.test.tsnpm run verify:cheapNotes
mainto reduce merge friction.npm run verify:cheappasses with one existing lint warning invitest.config.mts.