Removes `continue-on-error: true` from `.github/workflows/visual-diff.yml`'s
`Capture screenshots (PR)` step. Visual drift is now a real merge gate
on UI-touching PRs.
Brief 2/2 of the `harden-visual-diff-gate` convoy. PR #138 shipped the
seed workflow (Brief 1); PR #139 (`54495fe`) landed the fresh Linux
baseline regenerated against post-glass-redesign main on CT 111. With a
known-good baseline committed, the gate can flip without false-failing
every UI-touching PR.
## What changes
- `.github/workflows/visual-diff.yml` — drop the
`continue-on-error: true` flag; add an inline rationale block linking
to the convoy + the operator runbook for both intentional changes
(dispatch seed workflow → manually open PR → merge → re-run) and
unintentional regressions (inspect artifact diff → fix → push).
- `.github/workflows/ci.yml` — add 9th `forbidden-patterns` check that
greps `visual-diff.yml` for `^\s*continue-on-error:\s*true` and fails
the build if it returns. Risk #3 of the convoy made concrete: prevents
silent re-introduction via template revert. Scoped narrowly to that
one file; other workflows (`seed-visual-baselines.yml`'s PR-open
step, etc.) legitimately use the flag. Job name bumped from
"Forbidden patterns (8 checks)" → "(9 checks)". All `Check N/8`
group labels renumbered to `N/9`.
- `AGENTS.md` — § Testing § Visual baselines rewritten to drop the
"Known staleness as of 2026-06-12" callout (resolved by PR #139);
§ Testing § Screenshot diff rewritten to lead with "hard merge gate",
document the intentional-change runbook, reference the new ci.yml
check, and explicitly mention the org-setting caveat for the seed
workflow's auto-PR step.
- `tests/visual/homepage.spec.ts` — module docblock rewritten to match
the AGENTS.md runbook: drops the "advisory, not gating" language;
promotes the seed-visual-baselines workflow as the primary
re-seeding path; demotes the Playwright Docker image to the offline
fallback.
- `.github/workflows/seed-visual-baselines.yml` — patches the
`peter-evans/create-pull-request@v6` PR-open failure case discovered
during Brief 1's first dispatch (run 27454132468). The PR-open step
is now `continue-on-error: true` (narrowly scoped, with an inline
rationale callout distinguishing it from the just-removed
`visual-diff.yml` flag — that one silently hid real UI regressions;
this one fronts a known org-level "Allow GitHub Actions to create
and approve pull requests" limitation with a loud failure notice).
New steps disambiguate the three possible outcomes (no-changes /
pr-opened / branch-pushed-pr-blocked) via a `git ls-remote` check on
the bot branch and exit non-zero on the blocked-PR case so the
workflow run shows red and the operator gets the exact `gh pr create`
command in the run logs.
- `.convoys/harden-visual-diff-gate.md` — status: shipping; Step 2
marked SHIPPED; Decision D4 ratified (chose option C: accept org
setting, document manual `gh pr create` fallback). Inline links to
PR #139 + PR #140.
## Test plan
- [x] `npm run lint` — clean (1 pre-existing unrelated warning)
- [x] `npm run test:run` — 24 files / 118 tests pass
- [ ] CI on this PR: 9th forbidden-patterns check passes; visual-diff
job passes against the fresh baseline; convoy-metrics-gate passes
(2 new rows added by this commit)
- [ ] After merge: smoke test the 9th check by opening a throwaway PR
that re-adds `continue-on-error: true` to `visual-diff.yml`; confirm
it red-X's. (Skip if confident in the grep.)
## Convoy state
- Brief 1: SHIPPED (PR #138, `c100c5f`, 2026-06-13)
- Baseline refresh: SHIPPED (PR #139, `54495fe`, 2026-06-13)
- Brief 2 (this PR): shipping
- Convoy closeout: this PR's merge
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds a workflow_dispatch-triggered job on the self-hosted axiom runner
that captures fresh `tests/visual/__screenshots__/*.png` against a
caller-provided URL and opens a `chore(visual): refresh baselines from
<url>` PR via peter-evans/create-pull-request@v6.
This is brief 1 of 2 of the harden-visual-diff-gate convoy. The
workflow exists but is not invoked by this PR — operator dispatches via
the GitHub UI or `gh workflow run seed-visual-baselines.yml` once they
want a fresh baseline against post-glass-redesign main.
Brief 2 (flip continue-on-error: true off visual-diff.yml, add 9th
forbidden-patterns check) is unblocked once a fresh baseline lands via
this workflow's auto-PR.
Convoy decision D1 ratified: Option B (workflow_dispatch + auto-PR)
chosen over Option A (ad-hoc SSH-into-CT-111 + manual commit). The
workflow gives the baseline regeneration a reviewable Git-native shape;
the auto-PR carries a checklist for visual sanity-check before merge.
Workflow shape:
- Inputs: base_url (required, no default to avoid wrong-target
accidents), reason (optional, used in PR body).
- Permissions: contents: write + pull-requests: write — sufficient for
default GITHUB_TOKEN; no PAT needed.
- Caches: shares the node_modules + Playwright browser caches with
visual-diff.yml so the byte-equivalence guarantee holds without
cache miss overhead.
- Idempotent: peter-evans/create-pull-request short-circuits to a
::notice:: annotation if the captures match the existing committed
baselines (no PR opened).
Metrics: logged role-conductor + role-architect + role-implementer
events for this convoy in .convoys/.metrics.jsonl. Satisfies the
convoy-metrics-gate (PR #134, 9eef8d9) that fires on convoy:-titled
PRs requiring at least one new metrics row.
Co-authored-by: Cursor <cursoragent@cursor.com>
PR #58 (83a358b, 2026-06-02) committed the first Linux visual baseline,
resolving the seed-visual-baselines-on-linux convoy. But the cleanup
sweep across docs that referenced the convoy as "queued / not yet done"
never landed. Three files still describe the world as if PR #58 hadn't
happened, which confuses any agent reading the codebase to understand
the visual-diff pipeline:
1. tests/visual/homepage.spec.ts module docblock — described "FIRST
RUN (no committed baseline yet)" and "SEEDING THE BASELINE
(post-merge follow-up)" as the active state.
2. playwright.config.js snapshotPathTemplate comment — said
"Per Decision 4, we don't commit baselines this convoy" and
pointed at the queued seed convoy as future work.
3. AGENTS.md § Testing § Visual baselines + § CI behavior §
Screenshot diff — claimed "none committed yet" and that the first
visual-diff run "will fail at the test step because no baseline
exists yet."
Sweeping all three to describe the current reality. The Mac-vs-Linux
platform footgun (snapshotPathTemplate has no {platform} token) is
still live, so that warning stays — just rephrased from "don't have
baselines yet" to "don't regenerate them on a Mac."
Also surfaces a separate finding the seed work left behind:
visual-diff.yml's screenshot capture step still carries
`continue-on-error: true`, making the diff advisory rather than gating.
Flipping it requires re-seeding the baseline against post-glass-redesign
main first (the PR #58 baseline predates unify-glass-panel-surfaces +
cleanup-card-item-list-and-share-modal-palette +
migrate-button-input-mobilenav-to-glass-primitive). New convoy seed at
.convoys/harden-visual-diff-gate.md captures the two-step shape (re-seed
baseline, then flip the gate) plus the recommended workflow_dispatch
approach for repeatable re-seeding on CT 111.
No code behavior changes. Documentation + .md convoy seed only.
Co-authored-by: Cursor <cursoragent@cursor.com>