Skip to content

ci: add dispatch-only live Web-Vitals workflow to discharge #017 - #1385

Merged
BigSimmo merged 21 commits into
mainfrom
claude/live-web-vitals-workflow
Jul 30, 2026
Merged

ci: add dispatch-only live Web-Vitals workflow to discharge #017#1385
BigSimmo merged 21 commits into
mainfrom
claude/live-web-vitals-workflow

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Ledger #017 gates seven client findings from the 2026-07-28 latency audit, plus #012/#013/#016. Nothing in the repo could produce the evidence it asks for — in-sandbox vitals are blocked because the production server hard-requires Supabase secrets, and local Playwright timings rank routes against each other without measuring the real origin. This adds the missing instrument.

Modelled on live-domain-monitor.yml: no secrets, no third-party actions, no providers beyond what an anonymous visitor triggers, LIVE_DOMAIN_URL repo-variable override, pinned action SHAs. Dispatch-only rather than scheduled — it is heavier than that monitor's plain GETs, and the decision it feeds is made a few times a year.

The decision rule is committed before any numbers are read, so the gate cannot be rationalised afterwards:

  • mobile LCP < 2500 ms and CLS < 0.1 on every route → close #017 as metrics-acceptable; the gated payload findings become WONTFIX
  • any breach → only that route's findings become actionable, ranked by measured contribution

Based on prior evidence I expect the first branch: #069 closed with hosted p90 175–243 ms and "plans are not the multi-second tail", and #017's own row records dev-mode CLS 0.00–0.04. This repo has a track record of inferred perf claims that measurement refuted — which is the point of measuring before optimising.

Why the summariser is a script, not inline YAML

scripts/summarise-web-vitals.mjs keeps the escaping out of YAML-inside-shell-inside-node quoting and makes the thresholds testable. It fails closed: a missing LCP or CLS counts as a breach, because an absent number is not evidence of passing — exactly the mistake #017 exists to prevent.

Lab metrics only. Lighthouse cannot measure INP in lab conditions (it is an interaction metric; TBT is the lab proxy), so the rule's INP clause is confirmed from CrUX field data and the pass verdict is stated as provisional on that.

Also records the PR #1376 prlanded verification in the branch review ledger.

Correction — a missing mobile run read as a pass (6972f367)

Review raised at P1, and it was correct. The workflow deliberately downgrades a per-route Lighthouse failure to a warning so one flaky route cannot discard the whole run. But the summariser enumerated only the reports that existed and rejected only an entirely empty directory. A directory holding just desktop-root.json therefore produced zero mobile breaches and rendered "Every mobile route is within…" — recommending #017 be closed and its seven gated findings marked WONTFIX on evidence that was never collected.

I reproduced it against the original function before changing anything:

OLD breaches: 0 -> verdict: PASS (close #017, 7 findings WONTFIX)

That is the exact failure mode #017 exists to prevent, in the script whose stated purpose is to fail closed — "an absent number is not evidence of passing" was implemented, but an absent run was not.

The fix tracks the requested route matrix rather than the surviving files. The workflow passes ROUTES into the summariser; expectedMobileRuns() derives the mobile-<slug> names using the same slug rule as the workflow's sed, and any expected run with no report is a breach. The summarise step also exits non-zero when reports are missing, so an incomplete run cannot read as a pass at the job level either. Zero mobile reports with no route list supplied is still a breach, so the guard cannot be bypassed by omitting the argument.

NEW breaches: 5 -> mobile-root (no Lighthouse report produced), mobile-therapy-compass (…), …

Verification

  • check:github-actions (pin check) — GitHub Actions pin check passed.
  • tests/summarise-web-vitals.test.ts — new. Test Files 1 passed (1) / Tests 8 passed (8). Covers the pass case, missing-metric, over-threshold, the desktop-root.json-only reproducer, and the no-route-list case. Each was checked to fail against the pre-fix shape rather than pass vacuously.
  • ESLint and Prettier clean on the changed files
  • No provider-backed gates run. Running the workflow itself is a live production action and needs explicit approval at dispatch time — this PR only adds the capability.

Risk and rollout

  • Risk: None to the product. Adds a workflow that only runs on manual dispatch and a script nothing else imports.
  • Rollback: Delete the files.
  • Provider or production effects: None until someone dispatches it. When dispatched it issues anonymous page loads against the public domain, the same class as the existing live-domain-monitor.yml.

Notes

Read the summary table in the run's step summary; full Lighthouse JSON is uploaded as a 30-day artifact. Record the #017 verdict in docs/outstanding-issues.md either way — a pass is as valuable as a breach here, because it closes seven findings. A run with any missing mobile report is not a verdict at all; rerun it first.

RAG impact: no retrieval behaviour change — adds a dispatch-only CI workflow, a standalone summariser script, and its unit test; no retrieval, ranking, selection, or answer-generation code is touched.

Summary by CodeRabbit

  • Monitoring
    • Added an on-demand “Live Web Vitals baseline” that measures selected routes on the live site using mobile and desktop Lighthouse and uploads per-route report artifacts.
    • Produces a graded summary and records the outcome in the Web Vitals baseline outstanding-issues ledger.
  • Bug Fixes
    • Strengthened the baseline evaluation to fail closed on incomplete, mismatched, redirected, or ambiguous measurements.
  • Documentation
    • Expanded the Web Vitals baseline ledger entry with the operational decision rule and sanity-check guidance.
    • Added a branch review ledger row for recent merges.
  • Tests
    • Expanded automated coverage for route normalization, evidence/threshold logic, origin gating, and report summarization edge cases.

Ledger #17 gates seven client findings from the 2026-07-28 latency audit plus
#12/#13/#16, and nothing in the repo could produce the evidence it asks for:
in-sandbox vitals are blocked because the production server hard-requires
Supabase secrets, and local Playwright timings rank routes against each other
without measuring the real origin.

Modelled on live-domain-monitor.yml — no secrets, no third-party actions, no
providers beyond what an anonymous visitor triggers, LIVE_DOMAIN_URL repo
variable override. Dispatch-only rather than scheduled: it is heavier than that
monitor's plain GETs, and the decision it feeds is made a few times a year.

The decision rule is committed BEFORE any numbers are read, so the gate cannot
be rationalised afterwards: mobile LCP < 2500ms and CLS < 0.1 on every route
closes #17 as metrics-acceptable and makes the gated payload findings WONTFIX;
any breach makes only that route's findings actionable.

The summariser is a real script rather than YAML-embedded node so the escaping
is not at the mercy of YAML-inside-shell-inside-node quoting, and so the
thresholds are testable. It fails closed: a missing LCP or CLS counts as a
breach, because an absent number is not evidence of passing — which is the
mistake #17 exists to prevent. Verified against four fixtures (breach, pass,
missing-metric, empty-directory).

Lab metrics only. Lighthouse cannot measure INP in lab conditions; the rule's
INP clause is confirmed from CrUX field data, so the pass verdict is stated as
provisional on that.

Also records the PR #1376 prlanded verification in the branch review ledger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a manually triggered Lighthouse workflow for configured live routes, a Node CLI that summarizes reports with fail-closed Web Vitals evidence checks and thresholds, expanded tests for evaluation helpers, and ledger updates.

Changes

Live Web Vitals Baseline

Layer / File(s) Summary
Report normalization and evidence evaluation
scripts/summarise-web-vitals.mjs, docs/outstanding-issues.md
Normalizes Lighthouse reports, validates origins, URLs, metrics, runtime errors, and evidence completeness, renders #017 verdicts, and documents the decision rule.
Evidence evaluation tests
tests/summarise-web-vitals.test.ts
Tests route naming, run completeness, threshold breaches, URL and origin validation, verdict gating, numeric normalization, and environment metadata.
Dispatch configuration and live-site preflight
.github/workflows/live-web-vitals.yml
Adds manual route inputs, execution settings, pinned Lighthouse and Node versions, and an HTTP 200 preflight check.
Route measurement and artifact publication
.github/workflows/live-web-vitals.yml
Runs mobile and desktop Lighthouse checks for each route, invokes the summarizer, and uploads reports regardless of earlier step outcomes.

Branch Review Ledger

Layer / File(s) Summary
Review record
docs/branch-review-ledger.md
Records verification details for two referenced pull request merges.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Workflow
  participant Lighthouse
  participant Summarizer
  participant ArtifactStore
  Workflow->>Lighthouse: Measure configured routes with mobile and desktop strategies
  Lighthouse-->>Workflow: Write JSON reports
  Workflow->>Summarizer: Normalize reports and evaluate `#017` evidence
  Workflow->>ArtifactStore: Upload web-vitals artifacts
Loading

Possibly related PRs

  • BigSimmo/Database#1043: Both changes update docs/outstanding-issues.md with Web Vitals baseline ledger details.

Suggested reviewers: cursoragent

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The changes do not address the linked issue to bump @types/node from 25.9.1 to 25.9.2. Update the PR to include the @types/node version bump, or retarget/remove the linked issue if this PR is meant for the Web Vitals workflow.
Out of Scope Changes check ⚠️ Warning The workflow, script, tests, and docs are unrelated to the linked @types/node dependency bump. Remove the Web Vitals workflow/script/doc changes from this PR or split them into a separate PR for the unrelated work.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: a manual-dispatch Web Vitals workflow for #017, though it omits the supporting script/test updates.
Description check ✅ Passed The PR description covers Summary, Verification, Risk and rollout, and Notes with specific implementation and test details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

@supabase

supabase Bot commented Jul 29, 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 ↗︎.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: eab2183503

ℹ️ 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 thread .github/workflows/live-web-vitals.yml
claude added 2 commits July 29, 2026 13:36
The workflow deliberately downgrades a per-route Lighthouse failure to a
warning, but the summariser enumerated only the reports that existed and
rejected only an entirely empty directory. A directory holding just
desktop-root.json therefore produced zero mobile breaches and rendered
"Every mobile route is within..." — recommending #17 be closed and its
seven gated findings marked WONTFIX on evidence that was never collected.

Track the requested route-by-strategy matrix instead: the workflow passes
ROUTES through, and any expected mobile run with no report is a breach.
The step also exits non-zero when evidence is incomplete, so a partially
failed run cannot be read as a pass. An absent number was already treated
as a breach; an absent run now is too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 6972f3679f

ℹ️ 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 thread .github/workflows/live-web-vitals.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/summarise-web-vitals.test.ts (1)

47-53: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a missingReport/missingMetric assertion here.

This test confirms mobile-dsm is flagged as a breach when its LCP is null, but doesn't assert what missingReport is set to. Given the gap flagged in scripts/summarise-web-vitals.mjs (Line 137-141) where only missingReport gates the process exit, a test asserting the flag value here would have caught that the fail-closed exit doesn't currently cover this case.

     const breaches = mobileBreaches(rows, DEFAULT_ROUTES);
     expect(breaches.map((breach) => breach.run)).toEqual(["mobile-dsm"]);
+    expect(breaches[0].missingReport).toBe(false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/summarise-web-vitals.test.ts` around lines 47 - 53, Extend the test for
the null LCP case in mobileBreaches to assert the resulting breach’s
missingMetric or missingReport flag, using the expected fail-closed value. Keep
the existing run-name assertion and ensure the new assertion verifies the flag
needed by the process-exit logic.
.github/workflows/live-web-vitals.yml (1)

88-94: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Pin Lighthouse to an exact patch version

npx --yes lighthouse@12 can drift across v12 patch/minor releases, which makes these decision-gate measurements less reproducible; pin an exact version such as 12.8.2 instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/live-web-vitals.yml around lines 88 - 94, Update the
Lighthouse invocation in the workflow to use an exact v12 patch version, such as
lighthouse@12.8.2, instead of the floating lighthouse@12 reference; preserve all
existing options and failure handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/live-web-vitals.yml:
- Around line 54-55: Update the checkout step using actions/checkout in the live
web vitals workflow to set persist-credentials to false, while preserving the
existing pinned action reference and read-only permissions.

In `@scripts/summarise-web-vitals.mjs`:
- Around line 66-88: Update mobileBreaches so rows with null lcpMs or cls are
marked as fail-closed missing-metric breaches, distinct from ordinary threshold
failures, and update main()’s exit gate to treat both missingReport and
missing-metric breaches as fatal. Preserve the existing handling for genuinely
over-threshold metrics and completely missing mobile reports.

---

Nitpick comments:
In @.github/workflows/live-web-vitals.yml:
- Around line 88-94: Update the Lighthouse invocation in the workflow to use an
exact v12 patch version, such as lighthouse@12.8.2, instead of the floating
lighthouse@12 reference; preserve all existing options and failure handling.

In `@tests/summarise-web-vitals.test.ts`:
- Around line 47-53: Extend the test for the null LCP case in mobileBreaches to
assert the resulting breach’s missingMetric or missingReport flag, using the
expected fail-closed value. Keep the existing run-name assertion and ensure the
new assertion verifies the flag needed by the process-exit logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 01611a64-5733-483b-aa05-ff909d283cac

📥 Commits

Reviewing files that changed from the base of the PR and between 8605cf7 and 0d88075.

📒 Files selected for processing (4)
  • .github/workflows/live-web-vitals.yml
  • docs/branch-review-ledger.md
  • scripts/summarise-web-vitals.mjs
  • tests/summarise-web-vitals.test.ts

Comment thread .github/workflows/live-web-vitals.yml
Comment thread scripts/summarise-web-vitals.mjs
claude added 2 commits July 29, 2026 13:55
…uments

The documents segment has no page.tsx — it holds only search/, source/ and
[id]/ — and there is no rewrite for the bare path, so the default dispatch
input would have profiled the Next.js 404 document. docs/site-map.md lists
/documents/search as the canonical documents-mode route.

This matters more now that a missing mobile report is a hard breach: a route
that cannot produce a usable report would block the #17 verdict rather than
quietly skew it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: a626ef0801

ℹ️ 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 thread scripts/summarise-web-vitals.mjs Outdated
@BigSimmo

Copy link
Copy Markdown
Owner Author

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

  • Added requested-run matrix validation for both mobile and desktop Lighthouse strategies, so missing desktop reports are treated as incomplete evidence. scripts/summarise-web-vitals.mjsL49-L65
  • Prevented incomplete runs from rendering the metrics-acceptable #017 verdict and now identify the exact missing Lighthouse runs before exiting non-zero. scripts/summarise-web-vitals.mjsL101-L128 scripts/summarise-web-vitals.mjsL147-L158
  • Updated the workflow commentary to document that every requested route must produce evidence for both strategies. .github/workflows/live-web-vitals.ymlL103-L107
  • Added regression tests proving that a mobile-only result set is incomplete and that a complete mobile/desktop matrix passes validation. tests/summarise-web-vitals.test.tsL45-L65
  • Created commit 95dead431188aa32efda43dcd43fcd6574d76ac3 (fix(web-vitals): require desktop baseline reports) and prepared the required follow-up PR metadata through make_pr.
  • The unresolved GitHub thread could not be replied to or closed: this environment has no gh executable or GitHub credentials/API tool. Per the trigger, I did not emit the codex-thread-disposition:resolved marker outside the actual thread; it remains open for a human to post the disposition.

Testing

  • npx vitest run tests/summarise-web-vitals.test.ts --reporter=verbose1 test file passed; 10 tests passed.
  • npm run check:github-actions — decisive output: GitHub Actions pin check passed.
  • npx prettier --check scripts/summarise-web-vitals.mjs tests/summarise-web-vitals.test.ts .github/workflows/live-web-vitals.yml — all matched files use Prettier formatting.
  • git diff --check
  • ⚠️ npm run test — environment limitation: the repository-local node_modules/vitest/vitest.mjs installation is missing; the targeted suite passed using npx vitest.
  • ⚠️ gh api repos/BigSimmo/Database/pulls/1385/comments ... — environment limitation: /bin/bash: gh: command not found.

View task →

Two holes in the fail-closed gate, both of which exited 0 and let a verdict be
recorded on evidence that was not collected.

Desktop was never validated. #17 asks for reproducible mobile AND desktop
evidence, but the gate only asked mobileBreaches about missing mobile reports.
A complete mobile sweep with zero desktop reports printed the
metrics-acceptable verdict and exited 0. missingRuns() now covers the full
route-by-strategy matrix.

A present report with a null LCP/CLS did not fail the step. Those rows are
classified under failed with missingReport false, so the gate skipped them and
they surfaced only in the table - contradicting the documented guarantee that a
missing metric fails closed. They now carry missingMetric and are fatal.

An over-threshold number is deliberately still not 'incomplete': that is a real
measurement and a real verdict, and the distinction is pinned by a test.

Also sets persist-credentials: false on checkout (zizmor artipacked); the job
only reads files.

12 tests pass; both gaps were reproduced against the previous gate first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: d860e6320a

ℹ️ 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 thread scripts/summarise-web-vitals.mjs Outdated
Comment thread scripts/summarise-web-vitals.mjs
Two more holes, both the same root cause: incompleteEvidence() delegated to
mobileBreaches(), which is mobile-only, so anything it did not look at was
treated as evidence.

A desktop report with a null LCP/CLS passed. missingRuns() only checked that the
filename existed, and metric validity was only ever checked on mobile, so a
complete sweep with desktop-forms.json carrying null metrics returned no
incomplete evidence and exited 0.

Colliding route slugs silently dropped a route. /a/b and /a-b both slug to a-b,
so the second Lighthouse run overwrites the first, and the surviving file then
satisfies the expected-run check for both requested routes. The filename scheme
cannot represent both, so the evidence can never be complete; this is now fatal.

Rather than patch the third instance, incompleteEvidence() now walks the
expected route-by-strategy matrix directly and requires each run to carry both
graded numbers. Completeness is a property of the whole matrix, so it is
computed over the whole matrix.

15 tests pass; both gaps reproduced against the previous code first, each
returning [] and exiting 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 46c85c42f3

ℹ️ 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 thread .github/workflows/live-web-vitals.yml
Comment thread .github/workflows/live-web-vitals.yml Outdated
LIVE_DOMAIN_URL can point the workflow at a staging cutover. The summariser
still printed 'closes #17 as metrics-acceptable', so a staging dry run read as
production evidence for a ledger item that asks for psychiatry.tools
specifically.

The origin is read from each report's own final URL rather than from
LIVE_DOMAIN_URL, so a redirect to another host is caught as well as a
deliberate override. A non-canonical origin still gets its table and its
threshold summary - it is a useful dry run - but the closure sentence is
replaced with an explicit non-verdict note naming the origin measured.

18 tests pass; reproduced against the previous code, which emitted the closure
sentence for a staging origin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 07d0fdbbdc

ℹ️ 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 thread scripts/summarise-web-vitals.mjs Outdated
The #17 rule has three clauses - LCP, CLS and INP < 200ms - and Lighthouse
cannot measure INP in lab conditions at all. The summariser nonetheless printed
'closes #17 as metrics-acceptable' in bold on LCP and CLS alone and merely
asked the operator to confirm INP afterward, so an unavailable or >=200ms CrUX
value would have invalidated a closure already announced. The pass branch now
states that LCP and CLS are within threshold and that this is explicitly NOT
yet an #17 closure until INP is obtained from CrUX. The script can satisfy at
most two of the three clauses and no longer claims otherwise.

npx --yes lighthouse@12 resolves the range >=12.0.0 <13.0.0-0, so a patch
published between a baseline and its follow-up could change metric collection
independently of the application and decalibrate the comparison this workflow
exists to make. Pinned to 12.8.2 exactly, and summary.json now records the
resolved version so a baseline states its own tooling.

18 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 8b3904d500

ℹ️ 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 thread scripts/summarise-web-vitals.mjs
The origin check passed any report served from psychiatry.tools, but a route
that redirects to /login, to a same-origin error page, or to any other path
still produces clean metrics for the WRONG page, and the filename cannot reveal
it. Reproduced: with every expected run landing on /login, isProductionVerdict
returned true and incompleteEvidence returned [].

summariseReport now keeps requestedUrl and runtimeError alongside the final URL,
and a run counts as evidence only if the final path matches the requested path
(trailing slash normalised) and Lighthouse reported no runtime error. Same input
now flags all 10 runs.

21 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 44a4221f3b

ℹ️ 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 thread scripts/summarise-web-vitals.mjs Outdated
The path-only comparison accepted a redirect that dropped or rewrote the query,
so a dispatch of /documents/search?q=depression that finalised at
/documents/search passed as the requested page - presenting an empty-state
render as evidence for a results page. Compares normalised path AND query, with
params sorted so a reordering redirect is not a false rejection.

23 tests pass, including a dropped-query rejection and a reordered-params
acceptance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 93a6955628

ℹ️ 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 thread scripts/summarise-web-vitals.mjs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 75a2ed8355

ℹ️ 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 thread .github/workflows/live-web-vitals.yml
Comment thread .github/workflows/live-web-vitals.yml

Copy link
Copy Markdown
Owner Author

Resolving the position I left this PR in.

I said earlier that after eight rounds of findings I would not self-certify this one, and I meant it — each fix was locally correct and globally incomplete, which is a pattern, not a run of bad luck. What I got wrong was the conclusion I drew from it: I treated "I am no longer confident in my grading of the script" as equivalent to "this PR is not safe to merge". Those are different claims, and on re-examination only the first is true.

What merging this actually changes. Nothing runs. live-web-vitals.yml is workflow_dispatch only, holds permissions: contents: read, takes no secrets, and summarise-web-vitals.mjs is imported by nothing but its own test. Merged, the repository gains a capability and zero behaviour. There is no product surface, no provider call, no scheduled job, no clinical path.

Where the residual risk actually lands. It is realised at dispatch time, not merge time: someone runs the workflow and reads a verdict the summariser produced. That is already an explicitly approval-gated action, and #017's row is written to be recorded by a human either way. So the failure mode is "a human reads a mis-graded table", not "the product degrades". The fail-closed direction is also the right one — a missing run, a null metric, a slug collision and a redirected or query-dropped measurement are all breaches, so the script errs toward withholding the pass that would close seven findings, which is the direction that costs a rerun rather than a wrong closure.

I am merging on that basis, and recording the reservation rather than dropping it: the caveat is that this instrument's grading logic needed eight corrections, so the first dispatch should be read as a measurement to be sanity-checked, not as an oracle. I will file that in docs/outstanding-issues.md against #017 so it reaches whoever dispatches it, months from now, instead of living in this thread.

State on 2ba5abcd: merged origin/main (now carrying #1374 and the #1377 latency work). All 12 review threads resolved. verify:cheap exit 0 — Test Files 430 passed (430), Tests 4426 passed | 4 skipped (4430); tests/summarise-web-vitals.test.ts 23 passed within that. Branch review ledger guard passed with no duplicate rows from the union merge. No provider-backed gate was run, and the workflow itself has not been dispatched.


Generated by Claude Code

The reservation raised during this PR's review belongs where whoever
dispatches the workflow will find it, months from now, rather than in a
review thread. Records what the instrument is, that the decision rule was
committed before any numbers were read, that it fails closed, and that its
grading logic took eight rounds of corrections — so the first dispatch is a
measurement to sanity-check against the raw artifact, not an oracle, most of
all on a PASS that would close seven findings at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 7ed97850fe

ℹ️ 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 thread scripts/summarise-web-vitals.mjs
… one

Rounds 9 and 11 found the same root cause from two directions, and both were
right. `breaches` and `productionVerdict` chose the verdict sentence, while
`incomplete` was appended afterwards as a separate paragraph — so a summary
could assert "Every mobile route is within LCP < 2500ms and CLS < 0.1" in bold
and then admit two paragraphs later that the evidence was incomplete, or call a
staging breach's findings "actionable" for a production ledger item. Whichever
line a reader took away, one of them was false, and this text is written to the
step summary before the process exits.

Rather than gate the two sentences individually — which is what the last two
rounds of this kind did, and why a third instance existed — the disqualifiers
are now resolved BEFORE any verdict prose. Incomplete evidence and a
non-canonical measured origin are collected together, and if either holds the
function returns after one non-verdict statement plus the measurements marked
"for reference only". The threshold and actionability sentences are unreachable
unless every precondition holds, so a fourth instance of this class cannot be
written by editing one branch.

Round 10's Chrome finding is also right: Chrome ships with the ubuntu-24.04
runner image, not with the pinned LIGHTHOUSE_VERSION, so it moves underneath a
baseline and a metric shift can originate in the browser rather than the
application. `summariseReport` now keeps `environment.hostUserAgent` — what
actually drove the run, rather than what the runner claims — and `main()`
records the distinct values in summary.json next to `lighthouseVersion`.
Disagreement across reports in one run is itself visible. This is the
record-and-compare half of that finding; pinning Chrome outright is not
available on a GitHub-hosted runner without a container.

Five new assertions, all verified red against the previous shape:
  × suppresses the threshold claim when a run measured the wrong page
  × does not call a staging breach actionable
  × records the Chrome build that produced the report
  (+2)

verify:cheap exit 0 — Test Files 430 passed (430), Tests 4431 passed | 4 skipped.
check:github-actions — GitHub Actions pin check passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU8z73P6TXUXoYBqN5K1P

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: b345746e5c

ℹ️ 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 thread scripts/summarise-web-vitals.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/summarise-web-vitals.test.ts (1)

320-338: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Do not encode a full user agent as chromeVersion.

This test freezes the entire hostUserAgent string under a field whose name implies a parsed browser version. Rename the field to hostUserAgent, or extract Chrome/<version> and keep the chromeVersion name so downstream consumers cannot misinterpret the summary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/summarise-web-vitals.test.ts` around lines 320 - 338, The
measurement-environment summary currently stores the full host user agent in
chromeVersion. Update summariseReport and its tests to either expose this value
as hostUserAgent or parse only the Chrome version while retaining chromeVersion;
ensure the missing-report case remains null and downstream consumers receive a
semantically accurate field.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/outstanding-issues.md`:
- Line 111: Update the decision rule in the `#017` entry and its reference to `#016`
so a passing mobile LCP/CLS measurement only closes the explicitly measured
payload findings. Keep `#016`’s motion, CSS, client-waterfall, caching, and
dynamic-import items open unless separate evidence validates them, and adjust
the stated closure scope accordingly.

---

Nitpick comments:
In `@tests/summarise-web-vitals.test.ts`:
- Around line 320-338: The measurement-environment summary currently stores the
full host user agent in chromeVersion. Update summariseReport and its tests to
either expose this value as hostUserAgent or parse only the Chrome version while
retaining chromeVersion; ensure the missing-report case remains null and
downstream consumers receive a semantically accurate field.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c8aea2b2-3497-47d2-92b9-d8f9bf488300

📥 Commits

Reviewing files that changed from the base of the PR and between 93a6955 and b345746.

📒 Files selected for processing (4)
  • docs/branch-review-ledger.md
  • docs/outstanding-issues.md
  • scripts/summarise-web-vitals.mjs
  • tests/summarise-web-vitals.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/branch-review-ledger.md
  • scripts/summarise-web-vitals.mjs

Comment thread docs/outstanding-issues.md Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 5516e634b4

ℹ️ 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 thread .github/workflows/live-web-vitals.yml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/outstanding-issues.md (1)

161-161: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Restore the missing Resolved table cell.

This row has four cells instead of the archive table’s required five, so the resolution date is missing and markdownlint reports MD056.

Proposed fix
-| `#111` | issue | `ui-overlap` phone-inset case still flakes under load          | RESOLVED 2026-07-29 (PR `#1391`). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR `#1375` applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR `#1375` flake triage; session 2026-07-29                                                       |
+| `#111` | issue | `ui-overlap` phone-inset case still flakes under load          | RESOLVED 2026-07-29 (PR `#1391`). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR `#1375` applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR `#1375` flake triage; session 2026-07-29                                                       | 2026-07-29 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/outstanding-issues.md` at line 161, Update the `#111` row in the
outstanding-issues archive table to restore the missing Resolved status cell,
ensuring the row has the required five cells and includes the resolution date
consistently with the existing resolution text.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docs/outstanding-issues.md`:
- Line 161: Update the `#111` row in the outstanding-issues archive table to
restore the missing Resolved status cell, ensuring the row has the required five
cells and includes the resolution date consistently with the existing resolution
text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f95759c1-14e1-4ee4-a0e7-6d8f4e46c00b

📥 Commits

Reviewing files that changed from the base of the PR and between b345746 and 5516e63.

📒 Files selected for processing (2)
  • docs/branch-review-ledger.md
  • docs/outstanding-issues.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/branch-review-ledger.md

@BigSimmo
BigSimmo enabled auto-merge (squash) July 30, 2026 02:01
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

⚠️ Branch updated during autofix.

The branch was updated while autofix was in progress. Please try again.

@BigSimmo

Copy link
Copy Markdown
Owner Author

@cursoragent fix merge conflict and all comments resolve

@cursor

cursor Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Merge conflict cleared and all open review threads resolved on PR #1385.

Merge: Merged origin/main (#1390 ModeNav). GitHub now reports MERGEABLE (was CONFLICTING).

Fixes in 662c3329:

  • Encode query params in normalisedTarget so %26/%3D cannot alias a different page
  • Strip trailing slash from LIVE_DOMAIN_URL before Lighthouse
  • Narrow #017 so LCP/CLS cannot WONTFIX all of #016
  • Restore missing #111 archive Resolved cell

Verification: tests/summarise-web-vitals.test.ts — 30 passed; check:github-actions — pin check passed.

Threads: 19/19 resolved (0 open). PR head: 662c3329.

Open in Web Open in Cursor 

cursoragent and others added 2 commits July 30, 2026 02:04
…s-workflow

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Encode query params when comparing measured vs requested URLs so an
encoded separator cannot alias a different page; strip a trailing slash
from LIVE_DOMAIN_URL before route append; narrow the #17 closure rule so
LCP/CLS cannot WONTFIX all of #16; restore the #111 archive Resolved cell.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Sign up for free to 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