Skip to content

ci: gate the advisory UI lane, drop dead outputs, and record what the rest measured - #1453

Merged
BigSimmo merged 6 commits into
mainfrom
claude/ci-followups-outstanding
Jul 30, 2026
Merged

ci: gate the advisory UI lane, drop dead outputs, and record what the rest measured#1453
BigSimmo merged 6 commits into
mainfrom
claude/ci-followups-outstanding

Conversation

@BigSimmo

@BigSimmoBigSimmo commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

Works the five outstanding CI items from the #1427 review. Two became code; three resolved to measurements that say the change should not be made, which is a more useful result than the change would have been.

  • #137 — gate the advisory UI lane. It covers @quarantine + @mockup. With an empty flake ledger and zero @quarantine tags in the suite, it was spending ~3m14 on every UI PR to run five mockup tests. It now also requires advisory_ui_changed: a mockup surface changed, or the flake ledger is non-empty. Reading the ledger rather than hard-coding "no quarantines exist" makes it self-correcting — quarantining a test brings the lane straight back with nobody remembering to re-enable it. Fails open on an unreadable ledger, because a lane that silently never runs is the failure mode worth guarding.
  • #139 — stop exporting outputs nothing reads.source_changed, workflow_changed, changed_files were job outputs with zero consumers, which reads as live wiring and invites a future job to gate on a value nobody maintains. The script still computes all three (docs_only derives from two of them); they are simply no longer exported. #139's separate coverage-breadth half is untouched and stays open.
  • #135, #136, and the shard imbalance — documented with the measurements, no behaviour change.

RAG impact: no retrieval behaviour change — nothing under src/lib/rag/**, clinical-search, retrieval-selection, ranking-config, answer-ranking, the eval harness, or the golden fixture is touched.

Correction: the first cut of #137's gate had a real hole

Codex found it, and it was wider than the single file it named. mockupPatterns matched -mockups.tsx only, so eight singular -mockup.tsx components missed it, and three of the five specs the advisory project actually runs — ui-tools, ui-tools-collapse, ui-tools-task-directory — carry @mockup with no "mockup" in the filename and missed it too.

The failure mode was silent and total. Editing src/components/tools-page-mockups/task-directory-mockup.tsx on its own gave ui_changed=true, advisory_ui_changed=false: the production projects grepInvert its @mockup tag, the advisory lane never starts, and that component's only browser coverage runs nowhere — on a green PR. So the gate I added to save 3m14 could have cost real coverage.

Fixed in 6b9b79b: matched by mockup component directory as well as singular/plural filename, plus the three name-less specs.

The root cause is that mockupPatterns was a second hand-maintained copy of a list playwright.config.ts already owns, so the second list is now checked against the first. assertMockupSpecParity reads mockupSpecPattern out of the config, expands the alternation, and asserts every advisory spec matches mockupPatterns. It fails closed on a lost anchor rather than degrading into a guard that checks nothing.

The three that measured out

#135 (ui_changed over-fires on src/app/api/**) — already implemented, and now validated.isUiChangedPath already excludes it. I nearly reported a coverage hole: 16 of 17 API route groups appear in the Playwright specs. But 92 of those references are page.route( / context.route( stubs — the journeys mock the API layer, so an API handler change genuinely cannot move them. The exclusion is correct. Recorded with a stop rule, since the grep that suggested otherwise is the obvious thing to run again.

#136 (cold Next build cache) — built, benchmarked, rejected. The mechanism works and is proven: symlinking /cache to a directory outside the run root leaves the runner's always-delete cleanup contract completely untouched, because rmSync removes the symlink and not the target. The arithmetic is what fails:

cold compile 97s
warm compile 63s -> 34s saved
cache size 804 MB

34s for 804 MB does not pay. Worse, my first cut keyed on github.sha, which writes a fresh multi-hundred-MB entry per commit against a 10 GB repo-wide budget with LRU eviction — it would evict the Playwright browser caches that save considerably more. Reverted in full; the symlink trick is recorded as reusable if the cache ever shrinks.

Shard imbalance — measured. Running --shard=1/3 locally and summing per-test durations (121 passed, 6.9 min):

SpecTimeTestsPer test
ui-phone-scroll.spec.ts267.0s564.77s
ui-chrome-scroll.spec.ts56.3s173.31s
ui-accessibility.spec.ts28.1s151.87s
everything else~58s33

ui-phone-scroll.spec.ts is 65% of shard 1 (267s of 409s), at the worst per-test rate of any large spec in it, and it is also the file behind both #127 and #141. Splitting it is the only lever that rebalances, since --shard cannot divide a single file.

#127 — no recurrence across four post-#1427 runs (30530618838, 30532582652, 30534158395, 30535651470), including the one whose shard 1 failed on a different test. That is the evidence its own next step asked for; recorded, held open only for a wider sample on main.

Not done

#138 (CI Triage inert) needs the CI_TRIAGE_ENABLED repository variable set. That is a GitHub settings action, not a code change — it can only be done by a maintainer.

Verification

  • npm run verify:cheap at head 6b9b79bTest Files 436 passed (436), Tests 4574 passed | 4 skipped (4578), exit 0. (An earlier run before this branch was rebased onto main read 435 (435) / 4569 passed; the delta is main's new tests/rag-round-trip-budget.test.ts, not this diff.)
  • npm run check:ci-scope — self-test passes, now including Mockup spec parity: 5 advisory specs all match mockupPatterns and eleven advisory-lane cases
  • Parity guard mutation-proven in both directions: dropping the new spec pattern reports tests/ui-tools.spec.ts unrun (exit 1); renaming mockupSpecPattern in playwright.config.ts reports the missing anchor (exit 1)
  • npm run check:github-actions, check:outstanding-issues, check:branch-review-ledger
  • npx vitest run tests/ci-cache-safety.test.ts tests/container-ci-contract.test.ts — 18 passed
  • npx prettier --check .All matched files use Prettier code style!
  • #136 benchmarked end-to-end with real builds before being reverted; symlink/cleanup interaction proven directly

UI verification not run: no UI source changes. This PR gates the advisory lane rather than the required Production UI job, so the shards are unaffected. Confirmed on this PR's own runs that Advisory UI skips — before and after the widened patterns, since scripts/ci-change-scope.mjs is a workflow path and not a mockup surface.

Not run, deliberately: verify:release, eval:*, check:supabase-project are provider-backed and not authorised here.

Risk and rollout

  • Risk: low-to-medium, and confined to CI scoping. The advisory lane is continue-on-error and outside pr-required, so gating it cannot make a PR red; the realistic downside is it not running when it should — which is exactly what Codex caught, and what the parity guard now blocks. Dropping three unread outputs cannot change any job's behaviour — nothing consumed them.
  • Rollback: git revert either of the first two commits independently. The first is the only behavioural one; the third is its correction and should be reverted with it.
  • Provider or production effects: None. No application code, no schema, no provider calls.

Notes

The two rejected items are the point of the exercise rather than a shortfall: #135 would have been a silent coverage hole if the grep had been taken at face value, and #136 would have traded 34s for a cache-budget eviction cascade. Both stop rules are in the ledger so the next pass starts from the measurement.

The Codex finding belongs in the same column. A cost-saving CI gate whose miss is invisible is worse than the cost it saves, and the fix worth keeping is not the widened regex — it is that the duplicated list is now checked against its source.


Generated by Claude Code

claude added 2 commits July 30, 2026 13:08
#137 — the advisory lane covers `@quarantine` + `@mockup`. With an empty flake
ledger and no `@quarantine` tag anywhere in the suite, it was spending ~3m14 on
every UI pull request to run five mockup tests. It now also requires
`advisory_ui_changed`: a mockup surface changed, or the flake ledger is
non-empty.
Reading the ledger rather than hard-coding "no quarantines exist" is the point —
the moment a test is quarantined the lane comes back on every UI PR without
anyone remembering to re-enable it. An unreadable ledger fails OPEN, because a
lane that silently never runs is the failure mode worth guarding against. Five
self-test cases cover mockup source, mockup component, a populated ledger, the
unreadable-ledger path, and the negative case.
#139 — `source_changed`, `workflow_changed` and `changed_files` were job outputs
consumed by nothing, which reads as live wiring and invites a future job to be
gated on a value no one maintains. They are no longer exported. The script still
computes all three; `docs_only` derives from the first two. The separate
coverage-breadth half of #139 is untouched and stays open.
Three of the five outstanding CI items resolved to "already done" or "measured
and rejected" rather than to code, and the measurements are worth more than the
changes would have been.
#135 (ui_changed over-fires on src/app/api) — already implemented on main, and
now validated. A naive grep suggested a coverage hole: 16 of 17 API route groups
appear in Playwright specs. But 92 of those references are `page.route(` /
`context.route(` stubs — the journeys mock the API layer, so an API handler
change cannot move them. The exclusion is correct. Recorded with the stop rule,
because the grep that suggested otherwise is the obvious thing to run again.
#136 (cold Next build cache) — built, benchmarked, rejected. `<distDir>/cache`
symlinked outside the run root keeps the runner's always-delete cleanup contract
intact, and that mechanism is proven. But cold compile 97s vs warm 63s is 34s
saved for a 804 MB cache, and a per-SHA key writes a fresh multi-hundred-MB entry
per commit against a 10 GB budget with LRU eviction — it would evict the browser
caches that save more. The symlink trick is reusable if the cache ever shrinks;
the arithmetic is what fails.
Shard imbalance — measured rather than guessed. `ui-phone-scroll.spec.ts` is 65%
of shard 1 (267s of 409s) at 4.77s/test, the worst rate of any large spec in it,
and it is also the file behind both #127 and #141. Splitting it is the only lever
that rebalances, since `--shard` cannot divide one file.
#127 — no recurrence across four post-#1427 runs, including the one whose shard 1
failed on a different test. That is the evidence its own next step asked for.
@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:17 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 185602df-d4cb-436f-902a-21e6d1f6f726

📥 Commits

Reviewing files that changed from the base of the PR and between 102bb1f and f5ead9e.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • docs/outstanding-issues.md
  • docs/process-hardening.md
  • scripts/ci-change-scope.mjs

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Jul 30, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@BigSimmo
BigSimmo marked this pull request as ready for review July 30, 2026 13:23
@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

@cursoragent resolve merge conflicts

@cursor

cursorBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Merge conflict on PR #1453 is resolved and pushed.

Conflict: only docs/outstanding-issues.md
Resolution: kept this branch’s measured #135#139 rows, took main’s #141#144, left #140 archived, next-id=145

  • Commit: 06ad4ff8merge origin/main into claude/ci-followups-outstanding
  • npm run check:outstanding-issues passed
  • Local git merge-tree clean; GitHub reports MERGEABLE (checks still running)

PR: #1453

Open in WebOpen in Cursor

@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:920616e881

ℹ️ 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 threadscripts/ci-change-scope.mjs Outdated
Resolve outstanding-issues conflict by keeping this branch's measured
#135#139 rows, taking main's #141#144, and leaving #140 archived.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@BigSimmo
BigSimmo enabled auto-merge (squash) July 30, 2026 13:38
BigSimmoand others added 3 commits July 30, 2026 21:38
Codex found a real hole in #137's gate, and it is wider than the single file
it named. `mockupPatterns` matched `-mockups.tsx` only, so eight singular
`-mockup.tsx` components missed it, and three of the five specs the advisory
project runs carry `@mockup` without "mockup" in the filename.
The failure mode is silent and total. Editing
`src/components/tools-page-mockups/task-directory-mockup.tsx` on its own gave
`ui_changed=true, advisory_ui_changed=false`: the production projects
`grepInvert` its `@mockup` tag, the advisory lane never starts, and
`ui-tools-task-directory.spec.ts` — that component's only browser coverage —
runs nowhere, on a green pull request.
Matched now by mockup component directory as well as singular/plural filename,
plus the three name-less advisory specs.
A second list is how this happened, so the second list is now checked against
the first. `assertMockupSpecParity` reads `mockupSpecPattern` out of
playwright.config.ts, expands the alternation, and asserts every advisory spec
matches `mockupPatterns`. It fails CLOSED on a lost anchor rather than quietly
becoming a guard that checks nothing. Both directions proven by mutation:
dropping the new spec pattern reports `tests/ui-tools.spec.ts` unrun, and
renaming the constant in playwright.config.ts reports the missing anchor.
Six self-test cases cover the singular filename, the mockup directory, both
name-less specs, and the negative case that the directory rule does not
swallow ordinary component paths.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHLPEV4o1rzipPDqshCSHY
@BigSimmo
BigSimmo merged commit 9e1d9cf into mainJul 30, 2026
23 checks passed
@BigSimmo
BigSimmo deleted the claude/ci-followups-outstanding branch July 30, 2026 13:56
BigSimmo pushed a commit that referenced this pull request Jul 30, 2026
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
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.

3 participants

@BigSimmo@claude@cursoragent