Skip to content

fix: close bug-hunter stale-state paths (re-open) - #146

Merged
BigSimmo merged 10 commits into
mainfrom
reopen-bug-hunter-fixes
Jul 2, 2026
Merged

fix: close bug-hunter stale-state paths (re-open)#146
BigSimmo merged 10 commits into
mainfrom
reopen-bug-hunter-fixes

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • re-apply the bug-hunter stale-state/rollback hardening from fix: close bug-hunter stale-state paths #137 after revert
  • keep retry-route rollback protection and related regressions
  • align route test expectations with the current main error contract/route param validation

Checks run locally

  • npm run test -- tests/private-access-routes.test.ts tests/worker-visual-capture.test.ts tests/forms-clipboard-fallback.test.ts ✅
  • npm run verify:cheap ❌ (currently blocked by existing repo-wide typecheck errors in scripts/scratch files on main, unrelated to this PR)

Context

This safely reintroduces the intended fix set while preserving current mainline contracts.

BigSimmoand others added 2 commits July 2, 2026 16:27
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:470df91d00

ℹ️ 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".

Comment threadsrc/app/api/ingestion/jobs/[id]/retry/route.ts Outdated
Comment threadsrc/app/api/upload/route.ts Outdated

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2e0a47a11a

ℹ️ 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".

Comment threadsrc/app/api/documents/[id]/reindex/route.ts Outdated
Comment threadsrc/app/api/documents/bulk/reindex/route.ts Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

const{error: rollbackError}=awaitsupabase
.from("documents")
.update(rollbackDocumentPayload)
.eq("id",id)
.eq("owner_id",user.id);

P2 Badge Make single reindex rollback atomic

The new competingJobs read still does not protect the rollback write itself. If this insert fails, the read sees no active jobs, and then another reindex request enqueues or quickly completes work for the same document before this update runs, the rollback is still filtered only by id/owner_id and can restore the old status/counts over the newer job's document state. Put the no-competing-job/document-state guard in the same transaction/RPC or conditional statement as the rollback.


const{error: rollbackError}=awaitsupabase
.from("documents")
.update(rollbackDocumentPayload)
.eq("id",document.id)
.eq("owner_id",user.id);

P2 Badge Make bulk reindex rollback atomic

The new pre-rollback active-job check has the same TOCTOU window in the bulk path: after the competingJobs query returns empty, another request can enqueue or finish a replacement job for this document before this rollback update executes. Because the update is only scoped by id/owner_id, it can write the stale status/count snapshot over that newer work; move the competing-job guard into the same DB transaction/RPC or conditional rollback statement.


.eq("id",id)
.eq("status","pending")
.eq("stage","queued")
.eq("progress",0)
.eq("attempt_count",0)

P2 Badge Tie retry rollback to this reset attempt

When the document status update fails, this rollback predicate only proves the job is in a generic pending/queued state. A second retry of the same job can pass the endpoint's non-processing guard and write those same fields with a newer next_run_at; this older failing request would still match here and put the job back to its previous failed state after the newer retry queued the document. Guard on a per-reset value returned by the update, such as the new next_run_at, or make the retry/document update atomic.

ℹ️ 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".

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6f58313e62

ℹ️ 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".

Comment threadsrc/app/api/documents/[id]/reindex/route.ts Outdated
Comment threadsrc/app/api/documents/bulk/reindex/route.ts Outdated
Comment threadtests/private-access-routes.test.ts
Comment threadsrc/app/api/ingestion/jobs/[id]/retry/route.ts Outdated
BigSimmoand others added 3 commits July 2, 2026 18:14
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address the open Codex review comments on #146:
- retry route: next_run_at doubles as a per-request rollback fence, so a
losing retry's rollback matches zero rows instead of reverting a
concurrent retry's reset; the document re-queue also stamps updated_at
- single/bulk reindex routes: queue-state writes stamp documents.updated_at
with a per-request value and the enqueue-failure rollback matches on that
stamp, turning it into one atomic conditional UPDATE; the competing-job
SELECT remains only as a fast path
- shared ingestionRollbackFenceStamp helper generates microsecond-unique
timestamptz stamps
- tests assert the fence filters and stamped payloads
The preceding merge of origin/main adopts the current jsonError 5xx
contract, aligning the routes with the rollback tests' 500 expectations.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:ab6d70b20f

ℹ️ 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".

Comment threadsrc/app/api/documents/[id]/reindex/route.ts
@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

Addressed all open review feedback in ab6d70b (plus a merge of origin/main in 60e5d23):

  • Retry rollback is now fenced on a per-request next_run_at stamp, so it can only revert the exact reset this request wrote.
  • Single & bulk reindex rollbacks are now single conditional UPDATEs fenced on a per-request documents.updated_at stamp — atomic server-side; the competing-job SELECT is only a fast path. The retry route's document re-queue stamps updated_at too, so cross-route races are covered.
  • Error contract: merging main brought back jsonError(error) → 500/'Request failed.' for unexpected failures, aligning the routes with the rollback tests' expectations.

Verification: focused suites (private-access-routes, api-validation-contract, worker-visual-capture, forms-clipboard-fallback — 103 tests) and npm run verify:cheap (lint + typecheck + full vitest: 842 passed, 2 skipped) are green. The typecheck blockage mentioned in the PR description is resolved by the main merge.

@codex review

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:e627b71f5d

ℹ️ 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".

Comment threadsrc/app/api/documents/[id]/reindex/route.ts
Comment threadsrc/components/forms/form-detail-page.tsx
@BigSimmo
BigSimmo enabled auto-merge (squash) July 2, 2026 13:13
@BigSimmo
BigSimmo merged commit 151e4e3 into mainJul 2, 2026
4 checks passed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:1d115153e3

ℹ️ 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".

// precision while timestamptz stores microseconds, so random microsecond
// digits keep two same-millisecond requests distinct.
export function ingestionRollbackFenceStamp(now = new Date()) {
const microseconds = String(Math.floor(Math.random() * 1000)).padStart(3, "0");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make rollback fence collision-proof

When two retry/reindex requests for the same job or document are created in the same millisecond, this helper has only 1,000 possible stamp values; a Math.random() collision makes both writes use the same next_run_at/updated_at fence. In that case, a failed request's conditional rollback can still match and undo the successful concurrent reset that the fence is meant to protect, so use a guaranteed per-request token or DB-generated/monotonic value instead of a random microsecond suffix.

Useful? React with 👍 / 👎.

@BigSimmo
BigSimmo deleted the reopen-bug-hunter-fixes branch July 2, 2026 16:31
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
Fourth id collision on this one row. `main` (#1461) claimed `#145` for "A
branch sync during in-flight CI cancels a near-complete run, and nothing
warns" — which is, as it happens, the same CI-cancellation behaviour this
branch hit an hour ago.
Resolved by taking main's `docs/outstanding-issues.md` wholesale and
re-applying this capture at `#146`, rather than hand-editing the conflict
region: hand-editing a 144-row table around a conflict marker is how the
duplicate and wrong-width rows got in before. Marker bumped to 147.
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
The merge of origin into this branch hit exactly the damage #133/#140
describe: merge=union concatenated both sides of the ledger rather than
merging it.
Two collisions, both repaired without dropping either side's rows:
- Another agent had already allocated #141-#144 on main for different
items while this branch used #141-#143. The incoming rows renumber, per
the ledger rule, so the capture becomes #145 (adopt a consolidated
answer-home notice block), #146 (answer mode ships no verify-before-use
caveat) and #147 (verify:pr-local exits 0 when its build step refuses to
run). Their cross-references were updated to match, and the two
duplicated next-id markers collapse to one at 148.
- #140 appeared in both tables: it was closed on main as a duplicate of
#133 (PR #1444) while this branch still carried it open. The resolution
is honoured — the stale open row goes, the archive row stays.
check:outstanding-issues: 145 rows (72 open, 73 archived), unique ids,
next-id=148 above the highest, no merge driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPyFcMfn1jMmphr6AqiWBg
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
Resolves the one conflicted file, docs/outstanding-issues.md. With
merge=union removed by #1444 the ledger now conflicts honestly instead of
silently doubling, so this is a real resolution rather than a repair.
main had allocated #145 for "a branch sync during in-flight CI cancels a
near-complete run" while this branch used #145-#147 for the answer-home
notice capture. Per the ledger's own rule the incoming rows renumber: main's
#141-#145 are kept verbatim and the capture becomes #146 (adopt a
consolidated answer-home notice block), #147 (answer mode ships no
verify-before-use caveat) and #148 (verify:pr-local exits 0 when its build
step refuses to run), with their cross-references updated to match and the
marker lifted to 149. No row was dropped from either side — main's #141-#145
each appear exactly once, verified against origin/main.
check:outstanding-issues: 146 rows (73 open, 73 archived), unique ids,
next-id=149 above the highest, no merge driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPyFcMfn1jMmphr6AqiWBg
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
Third ledger collision in a row, same shape as the last two: main allocated
#146 for the `ui-phone-scroll` Services anchor issue while this branch used
#146-#148 for the answer-home notice capture. Main's row is kept and the
incoming rows renumber to #147 (adopt a consolidated answer-home notice
block), #148 (answer mode ships no verify-before-use caveat) and #149
(verify:pr-local exits 0 when its build step refuses to run), with their
cross-references and the marker (150) following.
Verified against origin/main that #141-#146 each still appear exactly once;
nothing was taken wholesale from either side.
check:outstanding-issues: 147 rows (74 open, 73 archived), unique ids,
next-id=150 above the highest, no merge driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPyFcMfn1jMmphr6AqiWBg
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
The three captured rows are dropped from docs/outstanding-issues.md, leaving
it byte-identical to origin/main. They will be re-landed as their own
single-file change once this PR merges.
AGENTS.md recommends bundling append-only ledger rows because they are
normally zero-risk, but that assumes a quiet file. Right now it is the
hottest file in the repo: bundling them here cost three ID collisions and
three full CI restarts in about thirty minutes (main took #141-#144, then
#145, then #146, while this branch needed #145-#149 in turn). #133 already
records that this file conflicts on nearly every main advance.
The mockups diff itself touches four uncontended files and has not conflicted
once today, so removing the ledger rows takes this PR out of a race it has no
reason to be in. No content is lost: the row text is preserved verbatim and
re-applied against a fresh main with clean ids.
check:outstanding-issues: 144 rows (70 open, 74 archived), unique ids,
next-id=147 above the highest, no merge driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPyFcMfn1jMmphr6AqiWBg
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
…and #146
Conflict was docs/outstanding-issues.md only. Took main's copy wholesale (it
carries #122's closure and the #98/#130 records from #1455) and re-applied the
two one-line relocation notes, rather than hand-editing the conflict region.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHLPEV4o1rzipPDqshCSHY
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
…ecord
The two Services viewport-anchor data points from PR #1457 were dropped when the
withdrawn #149 row and its id allocation were restored. They are unrelated to
that decision, so this puts them back and changes nothing else.
#149 stays exactly as set: archived as a withdrawn record, with issues:next-id
preserved at 150 so the id is retired rather than reused.
Restored to #146: the test failed once more on head c739340 (anchorTop expected
-138, received -7) then passed on 9da02d9 and a6f2281 across all three shards
with the spec byte-identical — six data points, two failures, shard 1 only, both
failures on a mockups-only PR. Notes that the 131px delta is roughly 2x the 64px
viewport shrink rather than sub-pixel drift, which constrains the element
attribution that row already asks for.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r
BigSimmo added a commit that referenced this pull request Jul 30, 2026
…#1481)
* issues: capture the production live-region defect; record the landed #1457 review
Adds #147 — `search-results-header-band.tsx` sets aria-live="off" when faulted,
so a clinical search that fails while focus is elsewhere is announced to nobody
and the user is never told Retry appeared. Affects all twelve call sites that
render the band. Raised by Codex against the copied line on PR #1457, fixed
there, and confirmed to exist unchanged in production. The merged mockup carries
the proven pattern and a non-vacuous assertion to port.
Appends two further data points to #146 from PR #1457: the Services viewport
anchor failed once more on head c739340 (expected -138, received -7) and then
passed on two later heads with the diff byte-identical — six data points, two
failures, shard 1 only. Both failures landed on a PR touching only mockups,
which strengthens the unchanged-code reading. Notes that the 131px delta is
roughly 2x the 64px shrink rather than sub-pixel drift.
Records the prlanded verification for PR #1457 in the branch review ledger:
squash e79e499, content diff against the branch tip empty, and the late
aria-live/role="alert" commit confirmed present on main rather than orphaned by
the squash.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r
* issues: withdraw #149 — the live-region defect was false, and the fix was worse
Codex caught this on PR #1481 and it is correct. I filed #149 claiming a failed
clinical search is announced to nobody because the count span sets
aria-live="off" when faulted. I never checked whether another node makes the
announcement. It does.
search-results-header-band.tsx mounts a fault panel with role="alert" carrying
the failure title, body and Retry (lines 407-414), and the mute is deliberate,
documented in place: "While faulted the live region is silenced
(aria-live='off') and the freshly-mounted fault role='alert' below makes the
single announcement, rather than both speaking."
tests/search-results-header-band.dom.test.tsx already pins exactly that with
singular role queries that throw on duplicates.
Escalating the count span in production, as #149 recommended, would have added a
second alert beside the fault panel — a duplicate announcement and a broken
test. The row is withdrawn to the archive rather than deleted, with the reasoning
recorded so nobody re-files it.
The mockup is unaffected: search-refine-adaptive-mockups.tsx has no fault panel,
so there the count span is the only announcement channel and its escalation is
correct. It simply does not port to production.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r
* docs: record PR 1481 review
* docs: preserve issue 149 allocation
* docs: format withdrawn issue record
* issues: restore the #146 data points lost while preserving the #149 record
The two Services viewport-anchor data points from PR #1457 were dropped when the
withdrawn #149 row and its id allocation were restored. They are unrelated to
that decision, so this puts them back and changes nothing else.
#149 stays exactly as set: archived as a withdrawn record, with issues:next-id
preserved at 150 so the id is retired rather than reused.
Restored to #146: the test failed once more on head c739340 (anchorTop expected
-138, received -7) then passed on 9da02d9 and a6f2281 across all three shards
with the spec byte-identical — six data points, two failures, shard 1 only, both
failures on a mockups-only PR. Notes that the 131px delta is roughly 2x the 64px
viewport shrink rather than sub-pixel drift, which constrains the element
attribution that row already asks for.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdPa3mHCX5ZQZZvU5GHU3r
---------
Co-authored-by: Claude <noreply@anthropic.com>
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
Second time a real conflict has blocked this PR's CI entirely — GitHub could
not build refs/pull/1466/merge, so no pull_request workflow ran and the thin
check list read as pending rather than blocked (#116).
Only docs/outstanding-issues.md conflicted; scripts/ci-change-scope.mjs and
docs/process-hardening.md auto-merged (main's regions are 150+ lines from this
branch's). Took main's ledger wholesale rather than hand-editing a 140-row
table around conflict markers.
Only ONE of this branch's two ledger edits was re-applied:
- #146 keeps its relocation note. The row is still open, and main's #1481 added
two further data points to it (head c739340, anchorTop expected -138 received
-7; six data points, two failures, shard 1 only) which are left untouched.
- #127's edit is DROPPED as obsolete. Main's #1487 archived that row, and the
archived form no longer cites tests/ui-phone-scroll.spec.ts at all, so there
is nothing left to relocate. Re-applying it would have matched nothing or
corrupted a differently-shaped row.
Marker is main's 149 (not this branch's 147): #149 was allocated, withdrawn and
retired rather than reused.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHLPEV4o1rzipPDqshCSHY
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
…onflict loop
Third consecutive real conflict blocking this PR's CI, always the same file,
and the fix is to stop touching it rather than to resolve it a fourth time.
main's newest commit is `fix(ledger): stop Prettier padding the issues table,
closing #133 (#1479)` — a whole-file reformat of docs/outstanding-issues.md.
Every row changed shape, so any edit to any row conflicts. That file is also
touched by nearly every main PR (row appends, archival moves), and this branch
is competing with a queue that merges several times an hour.
This PR's entire stake in it was ONE cosmetic line: a note on #146 saying the
cited spec moved to ui-phone-scroll-page-owned.spec.ts in the split. Three
CI-blocking conflicts — each one stopping GitHub building refs/pull/1466/merge
so no pull_request workflow ran at all — is a bad trade for that.
So the file is taken from main verbatim and the note is not re-applied. This
PR's diff no longer contains docs/outstanding-issues.md, which removes its only
remaining contact point with the hottest file in the repo.
Cost, stated rather than hidden: #146 keeps citing the old path. That row
already records that the exact test title is the durable identity because
declaration lines drift, so the stale path misleads nobody who reads it. The
note belongs in a docs-only PR when the ledger is not mid-reformat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHLPEV4o1rzipPDqshCSHY
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
…f sleeping
Root-caused from the failure trace (run 30582678887 artifact 8775836025), not
inferred. Two snapshots either side of the resize:
before anchorTop -138 documentScrollTop 504 headerBottom 0
after anchorTop -7 documentScrollTop 504
`documentScrollTop` is IDENTICAL at 504. The scroll position never moved; the
content shifted down 131 px. And 131 is not arbitrary — the trace records
collapseHeight 72, and gotoPhoneSurface injects --safe-area-top:59px. 72 + 59 =
131. The resize transiently re-shows the shared header, which reclaims its
collapse row AND its safe-area band, and the assertion measured mid-transient.
The cause is the fixed `waitForTimeout(100)` immediately after
`setViewportSize`. That is the same sleep-and-hope pattern PR #1427 removed from
`addPhoneScrollRunway`, and it explains every observation #146 has collected:
- bimodal, never in between: the two states are "settled hidden" (-138) and
"header fully re-shown" (-7), nothing between them
- pixel-identical across runs: -138/-7/131 reproduced on three separate heads
and two different PRs, which timing jitter cannot do
- "under CI load": load pushes the settle past 100 ms, nothing more exotic
- documentScrollTop unchanged: only chrome layout moved
This also retroactively settles the narrowing that #146 WITHDREW. That row once
argued the scroll position held because the sibling documentScrollTop assertion
did not fail — invalid, since Playwright aborts at the first failing expect so
it never ran. The conclusion was right anyway: the trace shows 504 on both
sides. It is now measured rather than inferred.
The fix polls the same geometry the hidden-state assertions already use
(`header#search` bottom <= 1) with a 10 s budget and a message naming the
condition. The tolerance is untouched — #146's stop rule forbids loosening it,
and loosening it would have hidden a real 131 px content jump.
Note what this does NOT claim: if the header ever fails to re-hide rather than
merely settling slowly, the poll times out and the test fails naming that. That
is strictly better than a 100 ms coin flip either way.
Verified: the previously-failing test 3x consecutively (1 passed each), and the
full spec 16 passed (43.0s). Local passes are weak evidence for a load-dependent
race; the mechanism change is the argument.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHLPEV4o1rzipPDqshCSHY
BigSimmo pushed a commit that referenced this pull request Jul 31, 2026
Resolves the fifth conflict on docs/outstanding-issues.md by taking
main's side on both hunks, after verifying that was correct rather than
convenient: main renumbered the queue to fix a duplicate rank 18 this
branch introduced, and it archived #138 and #146 rather than dropping
them (confirmed present in the archive table). The resulting id set is
byte-identical to main's.
#150 escalated. That row records CodeRabbit reviewing none of a full
day's PRs and rests explicitly on the Codex connector being the
surviving reviewer that "found three real defects that had survived
local gates". On PR #1505 Codex posted its own usage-limit notice
alongside CodeRabbit's spending-cap notice, so that PR received zero
automated review and so will anything opened while both caps hold. This
is the same issue with its fallback removed, not a new one — which
matters because the single Codex finding on #1459 was correct and
caught a verification that had matched the wrong component, closing
#105 on bad evidence. Local gates did not catch it.
The attribution harness lands as scripts/measure-cls-attribution.mjs.
#147's next step needs a before/after CLS pair, and #118 will want
element attribution again; without this, both mean re-deriving it. Build
and serve mirror run-lighthouse-budget.mjs so the numbers sit beside the
Lighthouse reports. Two traps that cost real time are encoded rather
than left to be rediscovered: CHROME_PATH must be set where the browser
is outside a standard location, and an init script attaching a
MutationObserver to document.documentElement before <html> exists throws
and silently takes the CLS observer with it — so the script now fails
loudly on an all-zero result instead of reporting a false clean bill.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01361jh3eYVjJCzXWjAhdZiF
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@BigSimmo