Fix API contract semantics and pagination metadata - #135
Merged
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
BigSimmo added a commit
that referenced
this pull request
Jul 2, 2026
Revert #135: API contract semantics and pagination metadata
BigSimmo pushed a commit
that referenced
this pull request
Jul 30, 2026
The `merge=union` driver on `docs/outstanding-issues.md` preserves concurrent appends, but when both sides restructure the same region it concatenates them wholesale. Merging the latest main did exactly that: every open row appeared twice and both `issues:next-id` markers survived — 66 duplicate-id errors from `check:outstanding-issues`, which is precisely the failure that gate exists to catch (#112). Resolved by rebuilding on main's canonical file rather than by hand-editing the duplicated table: reset to `origin/main`, then re-apply this branch's five captured rows at #131-#135 (main had advanced its allocation to #130 while this branch was open, so the earlier #128-#132 numbering collided again) and re-apply the #127 narrowing note. Marker bumped to 136. Union merge cannot allocate unique ids; only the structural gate can catch when it has produced an invalid file. It did.
BigSimmo pushed a commit
that referenced
this pull request
Jul 30, 2026
`PR mergeability` flagged this branch, but `git merge-tree` returned a clean tree — behind-but-clean staleness, not a content conflict. The merge itself then reported success while producing every open-items row twice (`#59 appears 2 times (lines 101, 166)` and so on for the whole table): `.gitattributes` sets `merge=union` on this file, which is git's built-in concatenate-both-sides driver with no dedupe, and the table is not append-only. Rebuilt from `origin/main` (now through #134) with only the one row this branch actually changed re-applied. Recorded as #135, since the driver turns a resolvable conflict into a guaranteed guard failure and makes `merge-tree` look clean. Also corrects #127's own framing. `Production UI` PASSED on run 30530393684, so the failure is intermittent at 2 of 3 completed runs, not reproducible as the previous row claimed — that was premature on two datapoints. The `data-scroll-signal` diagnostic therefore has not yet had a failure to report; it is still the thing that will name the cause when one comes. Verified: check:outstanding-issues 133 rows / 67 open / unique ids / next-id=136; check:branch-review-ledger 112 live + 1206 archived; whole-tree prettier clean; no conflict markers under docs/, tests/ or src/. Not re-run for this merge: verify:cheap and the phone-scroll spec — the code changes are unchanged from 5495f28, where both passed (434 test files / 4562 tests, and 56 passed), and this commit touches only the ledger plus main's own already-verified tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ
BigSimmo pushed a commit
that referenced
this pull request
Jul 30, 2026
Third staleness resolution on this branch in about forty minutes. `PR mergeability` again reported `mergeable_state: dirty` and "a real merge conflict", but `git merge-tree --write-tree` returned a clean tree, so this is behind-but-clean staleness, not a content conflict. The single overlapping path between this branch and main is `docs/outstanding-issues.md` — nothing else on this branch is contested, which is why the code files are byte-identical to 5495f28. The file is rebuilt from `origin/main` with this branch's two rows re-applied (#127 corrected, #135 added after #134, marker 136) instead of keeping the union driver's output, which concatenates both sides of every overlapping hunk without dedupe and doubled the whole table last time — that behaviour is what #135 records. Main's #127 still carried the withdrawn "sharedChromePinned is stuck" text and #135 was unclaimed, so neither graft overwrote anyone else's edit. Verified: check:outstanding-issues 133 rows / unique ids / next-id=136; check:branch-review-ledger 113 live + 1206 archived; whole-tree prettier clean. Not re-run: verify:cheap and the phone-scroll spec — `git diff 5495f28 -- src/ tests/` is empty, so the code carries that commit's evidence (434 test files / 4562 tests, and 56 passed) unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ
BigSimmo pushed a commit
that referenced
this pull request
Jul 30, 2026
…ocation CI caught what I did not: `static-pr` failed on `check:outstanding-issues` with #131-#134 duplicated and two `issues:next-id` markers. Cause: main's PR #1424 allocated #131-#134 for its own findings at the same time this branch held #131-#135, and `merge=union` did what union does — kept both sides under the same ids. That is #112's documented limit: union preserves concurrent appends but cannot allocate unique ids, so the structural gate is the only thing that catches it. My error was pushing without re-running that gate. The previous push resolved a `docs/branch-review-ledger.md` conflict, and I validated only that file before pushing to win the race against main — but the same merge also touched `docs/outstanding-issues.md`. `verify:cheap` would have caught it locally. Main's rows keep #131-#134 (already merged and referenced elsewhere); this branch's five renumber to #136-#140, one marker at 141, and the cold-cache cross-reference in process-hardening follows its row. Two of main's new rows also make a planned addition here redundant: #134 is the absent ledger merge driver and #133 is the outstanding-issues merge churn — both hit during this branch's work, both already captured upstream, so nothing new is filed for them.
BigSimmo pushed a commit
that referenced
this pull request
Jul 30, 2026
The union merge duplicated the whole open and archive tables again (two header rows, every id twice) because main restructured the file while this branch held rows in it. Same resolution as before and for the same reason: rebuild on main's canonical file rather than hand-editing a doubled table, then re-apply this branch's five rows. Main is now at next-id=135, so they land as #135-#139 with the marker at 140. None of the five is duplicated upstream — checked by summary before re-applying. This is the third renumber of the same five rows in one PR. That is not a mistake being repeated, it is #133 ("outstanding-issues conflicts on nearly every main advance") happening: any branch that holds rows in this file re-collides every time main lands one. Worth weighing whether captures should land in their own PR ahead of the work rather than riding along with it.
BigSimmo added a commit
that referenced
this pull request
Jul 30, 2026
…t did (#1427) * test(phone-scroll): prove the drag delivered before asserting the chrome hid CI run 30518866604 failed `ui-phone-scroll.spec.ts:423` on expect(getByTestId('universal-header-collapse')) .toHaveAttribute('data-scroll-hidden', 'true') // received "" after the full 10s auto-retry, and the classifier recorded it as "needs investigation". The assertion was right; the scroll never happened. `dragScrollBy` moved the scroller with `scrollTop +=`, which clamps silently at the end of the range, and returned nothing. When a page lays out shorter than the test assumed — content still settling under full-suite CI load — a 720px request delivers a fraction of that, the chrome correctly stays visible because document-detail chrome only hides past `scrollTop > 120`, and the failure surfaces ten seconds later looking like a product regression. The helper also resolved the scroll owner once up front, so a mid-drag layout change left it pushing an element that had stopped scrolling. - `dragScrollBy` now re-resolves the owner each step and returns the distance actually travelled. - `dragScrollUntilHidden` waits for the remaining downward runway (a condition wait, not a settle sleep), drags, and fails naming the shortfall if the drag could not cross the threshold. Used at the four sites that assert a hide immediately after a fixed-distance drag. - `addPhoneScrollRunway` waits for its 1600px filler to reach layout instead of sleeping 50ms. All 14 call sites already depend on that runway existing. Every assertion is byte-identical: a genuinely stuck header still fails exactly as before, once the drag is proven to have happened. No `.first()` was added (#93's stop rule) and no tolerance was relaxed. * ci: shard Production UI across three runners Measured on 2026-07-30 from the Actions API, two full UI-scope PR runs (30520443076, 30519912667): `Production UI` took 15m26-16m31 of a 16.8-18.6 minute run — 83-89% of wall clock — while every other job finished by minute 4 and then waited. Playwright itself reported `339 passed (13.5m)`; the balance is the isolated production build. That single job is also where the churn cost lands: 42% of PR runs in the sampled window were cancelled (25 of 60 completed), almost all superseded mid-Production-UI. Sharding is across runners, not workers. `workers: 1`, `fullyParallel: false` and `retries: 0` are unchanged inside each shard, so determinism is identical and per-runner load falls — which matters because #93's duplicate page root is load-dependent. `run-playwright.mjs` already forwards argv to `playwright test`, so `--shard` needed no runner change. The shard count is measured, not chosen. `fullyParallel: false` makes a spec file indivisible, so shard sizes are lumpy and more shards is not monotonically faster. Over the 340 required chromium tests: N=3 -> 121/106/113 largest 121 N=4 -> 121/106/96/17 largest 121 (same critical path, one more runner) N=6 -> 65/56/106/5/91/17 largest 106 N=5 -> 121/106/0/96/17 and N=8 -> two empty shards N=4 buys nothing over N=3, and any N with an empty shard would go red because `test:e2e:pr` deliberately omits `--pass-with-no-tests`. Expected critical path ~15.5 -> ~7 min, assuming per-test cost is roughly uniform. `fail-fast: false` so a failing shard cannot cancel its siblings and re-create the cancelled-vs-failed ambiguity #95 removed. Artifact names are shard-scoped because upload-artifact runs with `overwrite: false`. Branch protection requires only the `pr-required` aggregate, and `needs` on a matrix job yields the roll-up of all shards, so the aggregate is unchanged. Also adds `restore-keys` to both Playwright browser caches: without a prefix fallback a lockfile bump forced a cold browser download in every UI job at once, now three times over. * ci: bound the codex auto-resolve jobs and serialise the visual config Two inconsistencies found while mapping the pipeline, neither load-bearing but both silent: - `codex-autofix-review-comments.yml` was the only workflow in the repo with no `timeout-minutes` on either job, so both inherited GitHub's 360-minute default for work that reads PR metadata and posts one comment. - `playwright.visual.config.ts` set neither `workers` nor `fullyParallel`, so it inherited Playwright's default `workers = 50% of CPUs`. The production config pins both to serial deliberately; the visual lane was quietly opting out of the anti-flake posture the rest of the suite is configured for. * chore(gates): pin the documented gate count to the real chain Both numbers were wrong. `CLAUDE.md` said 24 static/consistency gates against an actual 25 — `check:assets` landed before that line was written, so it was wrong at authoring — and the `gates` skill said "check 2 of 26" against an actual 28. A stale count is not cosmetic here. The skill's whole point at that line is that `verify:cheap` stops at the first failure and everything after it never ran; an agent that believes the chain is 26 long cannot say how much a mid-chain failure skipped. `check:gate-manifest` already derives the real count from `verify:cheap:internal`, so it now asserts the documented numbers against it. The assertions fail closed: if the anchor phrasing disappears, the guard reports a lost anchor rather than passing on a document it no longer checks. Mutation-proven: reverting the skill to "26" fails with ".claude/skills/gates/SKILL.md says 26 where the chain has 28". * docs(issues): capture the CI review's deferred findings Five items from the CI/testing review that should not be changed blind: - #125 `ui_changed` matches all of `src/app`, so an API-only diff pays the 15-minute UI gate. Narrowing it can hide a real regression, so it needs a decision plus a compensating check rather than a quieter filter. - #126 the Playwright build writes to a per-run distDir, so Next's build cache is cold every run (~2 min, now ~29% of the sharded critical path). Fixing it means suppressing the runner's documented always-cleanup, which must not ship without executing the runner. - #127 the advisory UI lane spends ~3 min per UI PR on 5 mockup tests; there are currently zero `@quarantine` tests for it to cover. - #128 CI Triage is complete and self-tested but inert pending a repo variable. - #129 four `changes` outputs are computed and consumed by nothing, and `coverage_changed` fires on any non-doc file. * docs(ledger): record the ci-testing-review pass at this HEAD * ci: re-measure the shard split on the merged tree and refresh stale gate counts The merge changed both numbers this branch had recorded. Shard balance, re-measured against 342 required chromium tests (was 340): N=3 -> 121/111/110 largest 121 N=4 -> 121/106/98/17 largest 121 N=3 remains correct — one 121-test spec group bounds both, so N=4 spends an extra runner for the same critical path. The re-measure command is now in the workflow comment so the next person does not have to rediscover it. Gate counts: merging main added `check:gitleaks-pinned` and `check:pr-mergeability` to `verify:cheap:internal`, so the documented counts went stale the moment the merge landed — 25 -> 27 static, 28 -> 30 total. The guard added earlier in this branch caught it immediately rather than letting the docs drift again, which is the whole reason it exists. Also records the `ui-critical-fast` interaction: the UI critical path is now that 15-test fail-fast job plus the slowest shard, not the full 13.5-minute suite, so neither of this branch's pre-merge timings can be read on its own. * docs(issues): rebuild the ledger after a union-merge duplication The `merge=union` driver on `docs/outstanding-issues.md` preserves concurrent appends, but when both sides restructure the same region it concatenates them wholesale. Merging the latest main did exactly that: every open row appeared twice and both `issues:next-id` markers survived — 66 duplicate-id errors from `check:outstanding-issues`, which is precisely the failure that gate exists to catch (#112). Resolved by rebuilding on main's canonical file rather than by hand-editing the duplicated table: reset to `origin/main`, then re-apply this branch's five captured rows at #131-#135 (main had advanced its allocation to #130 while this branch was open, so the earlier #128-#132 numbering collided again) and re-apply the #127 narrowing note. Marker bumped to 136. Union merge cannot allocate unique ids; only the structural gate can catch when it has produced an invalid file. It did. * ci: record the measured shard result, correcting the predicted one First real run of the sharded shape (CI 30530618838, all green, whole run 13m39 against a 16.8-18.6 min unsharded baseline): ui-critical-fast 15 tests 3m14 Production UI (1) 121 tests 9m36 Production UI (2) 111 tests 6m54 Production UI (3) 110 tests 6m20 The prediction was wrong by ~40%. ~6.8 min was expected for the largest shard from 121/342 tests x 13.5 min; 9m36 happened. Per-test cost is not uniform — 111 tests took 6m54 while 121 took 9m36 — so a count-balanced split understates the slowest shard whenever the slow specs land in one group. `--shard` can only balance by count; balancing by duration would mean splitting the slow spec files themselves. The win is real but smaller than claimed, and the workflow comment and process-hardening now carry the measured numbers plus the reason the arithmetic misleads, so the next person re-measures instead of re-deriving. Also merges origin/main. The ledger conflict was GitHub-visible only: that file carries merge=union locally, which GitHub does not honour (#129). Resolved by keeping the one genuinely new record and dropping three that main already had elsewhere in the file — append-only forbids dropping a record that exists once, not keeping a second copy. Superseding record appended for this HEAD, since the prior one asserted a root cause that #127's trace evidence refutes. * docs(issues): renumber this branch's rows above main's concurrent allocation CI caught what I did not: `static-pr` failed on `check:outstanding-issues` with #131-#134 duplicated and two `issues:next-id` markers. Cause: main's PR #1424 allocated #131-#134 for its own findings at the same time this branch held #131-#135, and `merge=union` did what union does — kept both sides under the same ids. That is #112's documented limit: union preserves concurrent appends but cannot allocate unique ids, so the structural gate is the only thing that catches it. My error was pushing without re-running that gate. The previous push resolved a `docs/branch-review-ledger.md` conflict, and I validated only that file before pushing to win the race against main — but the same merge also touched `docs/outstanding-issues.md`. `verify:cheap` would have caught it locally. Main's rows keep #131-#134 (already merged and referenced elsewhere); this branch's five renumber to #136-#140, one marker at 141, and the cold-cache cross-reference in process-hardening follows its row. Two of main's new rows also make a planned addition here redundant: #134 is the absent ledger merge driver and #133 is the outstanding-issues merge churn — both hit during this branch's work, both already captured upstream, so nothing new is filed for them. * docs(issues): rebuild against main's current id allocation The union merge duplicated the whole open and archive tables again (two header rows, every id twice) because main restructured the file while this branch held rows in it. Same resolution as before and for the same reason: rebuild on main's canonical file rather than hand-editing a doubled table, then re-apply this branch's five rows. Main is now at next-id=135, so they land as #135-#139 with the marker at 140. None of the five is duplicated upstream — checked by summary before re-applying. This is the third renumber of the same five rows in one PR. That is not a mistake being repeated, it is #133 ("outstanding-issues conflicts on nearly every main advance") happening: any branch that holds rows in this file re-collides every time main lands one. Worth weighing whether captures should land in their own PR ahead of the work rather than riding along with it. * docs: record PR 1427 review --------- Co-authored-by: Claude <noreply@anthropic.com>
BigSimmo pushed a commit
that referenced
this pull request
Jul 30, 2026
…it asks for A real conflict this time, not staleness: main's PR #1427 rewrote the same helpers this branch touches, and it very likely found the actual cause of #127. `addPhoneScrollRunway` slept 50ms and merely hoped the appended 1600px runway had reached layout; `dragScrollBy` clamped silently at the end of the range while reporting nothing. Under CI load the drag therefore delivered less than it asked for and the chrome was right to stay visible. #1427 polls for the runway, returns the distance actually travelled, and `dragScrollUntilHidden` refuses to expect a hide until remaining runway and delivered travel both clear 160px. Main's helpers are taken whole. This branch keeps only what #1427's own comment says is still missing: "Separating THOSE two still needs the pin state exposed in the DOM; today only the composite `data-scroll-hidden` (`scrollHidden && !sharedChromePinned`) is observable, so both look identical." `data-scroll-signal` publishes the raw signal, and `expectChromeHidden` is cut down to answer only that question, now layered after `dragScrollUntilHidden` rather than duplicating its travel proof. #127 is rewritten again and withdraws a second wrong diagnosis of my own: a short/clamped drag was ruled out early using a maxOffset of 2753 read at a different trace moment than the failing drag, when the pre-runway reading in that same trace was 1153 — and a runway not fully landed puts the offset in the near-bottom band where computeScrollHideUpdate legitimately refuses. That is exactly what #1427 fixes. The row now points at #1427 as the likely fix, keeps the observability gap as the only open part, and says to close it if no recurrence appears on a post-#1427 head. Main also claimed #135 for an unrelated issue, so the union-driver finding renumbers to #140, marker 141. Verified: typecheck 0 errors, lint 0 problems, whole-tree prettier clean, check:outstanding-issues 138 rows / unique ids / next-id=141, and the merged phone-scroll spec 56 passed (4.1m) against an isolated production build — under Chromium 1194, not CI's bundled 1234 (#121). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ
BigSimmo added a commit
that referenced
this pull request
Jul 30, 2026
…1430) * test(phone-chrome): name which value holds the header open, not just that it did `data-scroll-hidden` on the collapse wrapper is `scrollHidden && !sharedChromePinned`, so a missing attribute has two very different causes the assertion cannot separate: the scroll state machine never fired, or it fired and a pin held the chrome open. The bare assertion reads as the first even when it is the second — which is how a stuck pin was misread as a flaky scroll gesture across two CI runs on 2026-07-30. `expectChromeHidden` keeps the same pass condition and adds a failure message. The discriminator is already in the DOM: DocumentViewer's page-owned composer hides on `composerScrollHidden`, which consults `scrollHidden` and not the pin, so composer-hidden plus header-visible proves the pin. Every term of `sharedChromePinned` also has a DOM tell — an `aria-expanded` trigger, a popover, or focus inside the portaled addon host — so when all read false the pin is a stale latch rather than a live surface, and the message says so. Also updates ledger #127 with what the traces establish: `scrollHidden` is TRUE and `sharedChromePinned` is stuck, reproducible on both completed full-suite runs and both variants, always at the reduced-motion hide that follows the section-sheet round-trip and never at the first hide. `main` only looks green because `Production UI` is skipped on its docs-only pushes; it has not run this test since 90b3e34, with zero `src/` changes since. Deliberately not the fix. Which term latched is proven; the mechanism is inferred, and it does not reproduce locally — every local run used the container's Chromium 1194 rather than the bundled 1234 CI installs (#121), so no local green is evidence here. This makes the next CI failure name its own cause instead of costing another trace download. Verified: typecheck clean, lint clean, prettier clean, check:outstanding-issues 125 rows / unique ids / next-id=128, and both affected tests still pass locally (2 passed, 12.6s). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ * fix(test): publish the header's own scroll signal; withdraw the pin claim Codex review is correct and this retracts the previous commit's central claim. Reading `form.document-viewer-composer[data-scroll-hidden]` as a proxy for the header's `scrollHidden` was wrong: they are separate state machines. The header is driven by the shell's `chromeScrollHide` (global-search-shell.tsx:332), fed only by `useDocumentScrollHideReporter` (line 345) and passed in at line 876, while DocumentViewer runs its own two `useHideOnScroll` instances (use-document-viewer-chrome-scroll.ts:20-30). Composer-hidden therefore proves DocumentViewer's reporter fired and says nothing about the header's, so it never separated a pin from a reporter-never-fired — the exact distinction the helper claimed to make. `data-scroll-signal` on the collapse wrapper now publishes the header's raw `scrollHidden` before the pin is applied, and `expectChromeHidden` reports it alongside DocumentViewer's so a divergence between the two feeds is visible instead of collapsed into one verdict. Nothing styles the attribute; no CSS or code reads it (verified by grep), so behaviour is unchanged. Ledger #127 is corrected rather than patched over: the "traces prove sharedChromePinned is stuck" claim is explicitly withdrawn, what the traces do establish is separated from what they do not, and the new leading hypothesis is recorded as untested — the shell's feed is document-only, so where `#main-content` owns scrolling `window.scrollY` never moves and the shell reporter cannot see the gesture, which would explain the standalone-PWA variant directly. It also now says not to infer the header's scroll state from any page-owned composer. Verified: verify:cheap exit 0 — Test Files 434 passed (434), Tests 4562 passed | 4 skipped (4566); typecheck and lint clean; prettier clean; the full phone-scroll spec 56 passed (4.4m) against an isolated production build. That build used the container's Chromium 1194, not the bundled 1234 CI installs (#121), so it proves the attribute broke nothing and nothing more. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ * Merge origin/main; rebuild the ledger the union driver doubled `PR mergeability` flagged this branch, but `git merge-tree` returned a clean tree — behind-but-clean staleness, not a content conflict. The merge itself then reported success while producing every open-items row twice (`#59 appears 2 times (lines 101, 166)` and so on for the whole table): `.gitattributes` sets `merge=union` on this file, which is git's built-in concatenate-both-sides driver with no dedupe, and the table is not append-only. Rebuilt from `origin/main` (now through #134) with only the one row this branch actually changed re-applied. Recorded as #135, since the driver turns a resolvable conflict into a guaranteed guard failure and makes `merge-tree` look clean. Also corrects #127's own framing. `Production UI` PASSED on run 30530393684, so the failure is intermittent at 2 of 3 completed runs, not reproducible as the previous row claimed — that was premature on two datapoints. The `data-scroll-signal` diagnostic therefore has not yet had a failure to report; it is still the thing that will name the cause when one comes. Verified: check:outstanding-issues 133 rows / 67 open / unique ids / next-id=136; check:branch-review-ledger 112 live + 1206 archived; whole-tree prettier clean; no conflict markers under docs/, tests/ or src/. Not re-run for this merge: verify:cheap and the phone-scroll spec — the code changes are unchanged from 5495f28, where both passed (434 test files / 4562 tests, and 56 passed), and this commit touches only the ledger plus main's own already-verified tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ * Merge origin/main (fcd9041); rebuild the ledger rather than trust union Third staleness resolution on this branch in about forty minutes. `PR mergeability` again reported `mergeable_state: dirty` and "a real merge conflict", but `git merge-tree --write-tree` returned a clean tree, so this is behind-but-clean staleness, not a content conflict. The single overlapping path between this branch and main is `docs/outstanding-issues.md` — nothing else on this branch is contested, which is why the code files are byte-identical to 5495f28. The file is rebuilt from `origin/main` with this branch's two rows re-applied (#127 corrected, #135 added after #134, marker 136) instead of keeping the union driver's output, which concatenates both sides of every overlapping hunk without dedupe and doubled the whole table last time — that behaviour is what #135 records. Main's #127 still carried the withdrawn "sharedChromePinned is stuck" text and #135 was unclaimed, so neither graft overwrote anyone else's edit. Verified: check:outstanding-issues 133 rows / unique ids / next-id=136; check:branch-review-ledger 113 live + 1206 archived; whole-tree prettier clean. Not re-run: verify:cheap and the phone-scroll spec — `git diff 5495f28 -- src/ tests/` is empty, so the code carries that commit's evidence (434 test files / 4562 tests, and 56 passed) unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ * docs: record PR 1430 review --------- Co-authored-by: Claude <noreply@anthropic.com>
BigSimmo added a commit
that referenced
this pull request
Jul 30, 2026
* fix(ledger): remove merge=union from the issues ledger, per its own #133 Ledger #133 already recorded union as the wrong driver for this file — "two sides each bumping the marker produce two `next-id` lines, corrupting the file silently where a conflict would fail loudly" — but `.gitattributes` still set it and `check-outstanding-issues.mjs` *required* it, so the repo's own tested conclusion was contradicted by its own config. PR #1430 confirmed the cost at scale: four merges in one session, each reporting success while duplicating the entire open-items table (`#59 appears 2 times (lines 101, 166)` and so on for every row), each needing a manual rebuild from origin/main. Union also makes `git merge-tree` report a clean tree, so the pre-merge conflict check cannot warn. Unlike docs/branch-review-ledger.md — which keeps its custom `merge=ledger` driver, union plus exact-row dedupe — this file allocates IDs by read-modify-write. Concurrent appends therefore need manual renumbering whatever the driver does (hit twice on 2026-07-30: #125 and #135 collisions), so union bought nothing and only hid the overlap. Default 3-way merge conflicts honestly instead. The gate's attribute check is inverted rather than deleted, so a driver reappearing here is a red gate. AGENTS.md, docs/process-hardening.md, .claude/skills/issues/SKILL.md and docs/scripts-index.md are updated to match, and #133's driver half is marked resolved with its still-open half (fixed-width padding making every row edit one hunk) left intact. Verified: reintroducing `docs/outstanding-issues.md merge=union` fails the gate with "must have NO merge driver (found merge=union)", and removing it passes with "no merge driver" — the gate bites, not just passes. verify:cheap exit 0: Test Files 434 passed (434), Tests 4563 passed | 4 skipped (4567). Whole-tree prettier clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ * fix(gate): reject `-merge` too, not just a named driver Codex review is right: the new check accepted `unset` alongside `unspecified`, and those are not the same state. Per gitattributes, an Unspecified `merge` attribute is the documented default 3-way text merge — the contract this PR establishes — while Unset (`-merge`) takes the current branch's version and declares the merge conflicted, so every two-sided edit becomes a manual resolution. A global or future attributes file could therefore have violated the contract with the gate still printing "no merge driver". Reproduced before fixing: appending `docs/outstanding-issues.md -merge` made `git check-attr` report `merge: unset` and the guard passed. It now fails with a message naming the Unset/Unspecified distinction and telling the reader to drop the negated attribute rather than add one. The acceptance decision moves into an exported `mergeAttributeProblem` so the distinction is unit-tested rather than only reasoned about, with four cases in tests/repo-hygiene.test.ts: `unspecified` accepted; `unset`, `union`/`ledger`, and an empty reading all rejected. The empty case matters because an unparsed check-attr output would otherwise make the whole check vacuous. Verified: with `-merge` present the gate fails on the new message; with it removed it passes "no merge driver". repo-hygiene 47 passed (47). verify:cheap exit 0 — Test Files 435 passed (435), Tests 4508 passed | 4 skipped (4512). Whole-tree prettier clean. (Test total differs from this branch's earlier run because it now carries main's #1423/#1427/#1438; this commit adds four.) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrPbbfU9yWuEjEVypCr4ZQ --------- Co-authored-by: Claude <noreply@anthropic.com>
BigSimmo added a commit
that referenced
this pull request
Jul 30, 2026
…ocs content The three conflicts were all the squash-merge ancestry break: this branch was stacked on the pre-merge #1436 head, so its copies of docs/codebase-index.md, docs/scripts-index.md and docs/outstanding-issues.md collided with main's squashed version. Resolutions: kept this branch's corrected scripts-index counts (191/206 against main's stale 188/203) and its three new root-directory rows; took main's outstanding-issues wholesale because main renumbered the rows on merge (#135 became #144, next-id 145) and its ids are authoritative, then re-applied the #143 correction on top. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BigSimmo added a commit
that referenced
this pull request
Jul 30, 2026
The id this branch allocated was taken by main three times running (#135 -> #141 -> #145 -> #147), and the third merge conflicted as one hunk covering the entire open-items table, so main's table was taken wholesale and the branch's deltas re-applied by script. Two further findings: the GitHub Update-branch button auto-merged this file into duplicate #141 rows with the marker left below main's highest id, a head that would have failed check:outstanding-issues; and removing merge=union did not reduce collision frequency, it converted silent duplication into loud conflicts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BigSimmo added a commit
that referenced
this pull request
Jul 31, 2026
* 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>
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 freeto 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.
Summary