Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
63ea53f
test(specimens): add the visual-review workflow
BunsDev Aug 27, 2026
8ed042b
chore(ci): materialize the updated visual-review sources
BunsDev Aug 27, 2026
26235b3
test(specimens): add viewport visual receipts
github-actions[bot] Aug 27, 2026
b5d3d50
chore(ci): remove the visual-review materializer
BunsDev Aug 27, 2026
63c2344
chore(ci): sync responsive registry payloads for visual review
BunsDev Aug 27, 2026
6088155
chore(registry): sync responsive block payloads
github-actions[bot] Aug 27, 2026
99072c4
chore(ci): remove the responsive registry sync
BunsDev Aug 27, 2026
c20fb03
chore(stack): merge the repaired specimen shell lineage
BunsDev Aug 27, 2026
13d15a1
chore(ci): refine the visual clipping assertion
BunsDev Aug 27, 2026
92557fb
chore(ci): remove brittle self-modifying visual workflow
BunsDev Aug 30, 2026
822ee4f
fix(specimens): carry shell regression guards into visual review
BunsDev Aug 30, 2026
8377a5f
fix(specimens): preserve browser shortcuts in visual review stack
BunsDev Aug 30, 2026
a443768
test(contracts): keep visual-review stack cross-platform
BunsDev Aug 30, 2026
7ab0251
style(specimens): format regression guards
BunsDev Aug 30, 2026
6b3df62
fix(specimens): constrain assembled tabs at mobile width
BunsDev Aug 30, 2026
0fb6a95
noop
BunsDev Aug 30, 2026
419f383
chore: remove accidental sentinel file
BunsDev Aug 30, 2026
8378407
merge: synchronize visual review with browser shell
BunsDev Aug 30, 2026
3ce6d8f
fix(specimens): constrain assembled tab panels on mobile
BunsDev Aug 30, 2026
f5de416
merge: synchronize panel sizing fix with browser shell
BunsDev Aug 30, 2026
e57db6b
fix(specimens): make mobile assembled sizing border-box
BunsDev Aug 30, 2026
ff6be8e
merge: synchronize border-box fix with browser shell
BunsDev Aug 30, 2026
7473e98
chore(debug): add temporary mobile overflow diagnostic
BunsDev Aug 30, 2026
6ee4193
chore(debug): run temporary mobile overflow diagnostic
BunsDev Aug 30, 2026
f260519
chore(debug): inspect Base UI tab panel state
BunsDev Aug 30, 2026
2a580e6
chore(debug): rerun tab-panel state diagnostic
BunsDev Aug 30, 2026
cae3d40
fix(tabs): honor Base UI orientation contract
BunsDev Aug 30, 2026
2c009c4
fix(registry): publish corrected tabs orientation
BunsDev Aug 30, 2026
cb55eb4
test(tabs): lock Base UI orientation semantics
BunsDev Aug 30, 2026
9617424
chore(debug): remove mobile overflow diagnostic workflow
BunsDev Aug 30, 2026
ff7dd6a
chore(debug): remove mobile overflow diagnostic script
BunsDev Aug 30, 2026
8d66013
merge: synchronize visual review with corrected tabs primitive
BunsDev Aug 30, 2026
e38050e
test(specimens): add mobile quality browser gate
BunsDev Aug 30, 2026
f7b4c35
test(specimens): exercise mobile quality matrix
BunsDev Aug 30, 2026
ed2dafb
sync(specimens): inherit mobile card composition
BunsDev Aug 30, 2026
37ff414
sync(blocks): inherit mobile transcript contract
BunsDev Aug 30, 2026
496ed61
sync(blocks): inherit mobile session contract
BunsDev Aug 30, 2026
1ac5807
style(blocks): format responsive transcript contract
BunsDev Aug 30, 2026
0aef690
sync(registry): inherit responsive session header
BunsDev Aug 30, 2026
7ec9a04
sync(registry): inherit responsive transcript turn
BunsDev Aug 30, 2026
2852b36
sync(tests): inherit mobile block contracts
BunsDev Aug 30, 2026
fc30109
sync(registry): inherit responsive session description
BunsDev Aug 30, 2026
a257c64
sync(registry): keep stable session metadata
BunsDev Aug 30, 2026
eae3b50
sync(registry): keep stable session metadata
BunsDev Aug 30, 2026
ff9ebf7
style(specimens): format mobile quality gate
BunsDev Aug 30, 2026
3af1f5c
chore(ci): run one-shot mobile gate formatter
BunsDev Aug 30, 2026
099a1ac
fix(specimens): simplify mobile quality evaluation
BunsDev Aug 30, 2026
17071d1
chore(ci): remove one-shot formatter helper
BunsDev Aug 30, 2026
eecac17
chore(ci): capture exact mobile gate formatting
BunsDev Aug 30, 2026
466bc33
style(specimens): commit exact mobile gate formatting
BunsDev Aug 30, 2026
968a04d
sync(specimens): keep minimum viewport independent of text scale
BunsDev Aug 30, 2026
143fc1e
chore(ci): restore read-only mobile visual gate
BunsDev Aug 30, 2026
0466051
chore(stack): restack visual receipts on the reviewed shell
BunsDev Aug 30, 2026
4b1663b
chore(ci): pin visual receipt actions
BunsDev Aug 30, 2026
0b25257
test(specimens): harden mobile receipt runner
BunsDev Aug 30, 2026
1621ab9
docs(specimens): document the complete receipt matrix
BunsDev Aug 30, 2026
fc17cf9
fix(visual): avoid nested templates in browser probe
BunsDev Aug 30, 2026
4fdfe41
chore(ci): one-shot format mobile gate
BunsDev Aug 30, 2026
ed4b82e
style(visual): apply repository formatter
github-actions[bot] Aug 30, 2026
a08007d
chore(ci): trigger visual verification
BunsDev Aug 30, 2026
1c9d275
chore(ci): one-shot mobile harness cleanup
BunsDev Aug 30, 2026
24d11d9
chore(ci): make one-shot cleanup lint-safe
BunsDev Aug 30, 2026
f3a25ac
fix(visual): harden mobile harness cleanup
github-actions[bot] Aug 30, 2026
53a506a
chore(ci): verify hardened mobile harness
BunsDev Aug 30, 2026
8e2479b
chore(ci): verify text-zoom containment
BunsDev Aug 30, 2026
be43891
chore(ci): verify intrinsic mobile sizing
BunsDev Aug 30, 2026
1480c69
chore(stack): restack visual receipts on the final shell head
BunsDev Aug 30, 2026
a250e45
chore(stack): absorb latest responsive shell fixes
BunsDev Aug 30, 2026
15f0444
chore(ci): verify visual receipts against main
BunsDev Aug 31, 2026
9bef135
chore(ci): mark verified stacked PRs ready once
BunsDev Aug 31, 2026
e30e16a
chore(pr): mark stacked reviews ready
github-actions[bot] Aug 31, 2026
b4f05fc
chore(ci): certify ready visual-review head
BunsDev Aug 31, 2026
6e39a70
chore(ci): harden visual target startup once
BunsDev Aug 31, 2026
1518815
chore(ci): make visual startup hardening structural
BunsDev Aug 31, 2026
28d3f45
chore(ci): add one-shot PR 2 stack repair
BunsDev Aug 31, 2026
3aa7b55
chore(stack): rebuild visual receipts on verified shell
github-actions[bot] Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions .github/workflows/visual-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Visual review

on:
pull_request:
paths:
- ".github/workflows/visual-review.yml"
- "apps/specimens/**"
- "packages/ui/**"
- "scripts/visual-review.mjs"
- "scripts/mobile-quality-review.mjs"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: visual-review-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
specimens:
name: Specimen browser
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Check out repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Set up pnpm
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
version: 10.17.1

- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build package and specimen app
run: pnpm build

- name: Set up Chrome for Testing
id: chrome
uses: browser-actions/setup-chrome@2e1d749697dd1612b833dba4a722266286fbefcd # v2.1.2
with:
chrome-version: stable

- name: Capture visual and mobile-quality receipts
env:
CHROME_PATH: ${{ steps.chrome.outputs.chrome-path }}
run: |
set -euo pipefail

mkdir -p artifacts/visual-review artifacts/mobile-quality
preview_log="$RUNNER_TEMP/specimens-preview.log"

pnpm --filter @opencoven/specimens preview \
--host 127.0.0.1 \
--port 4173 >"$preview_log" 2>&1 &
preview_pid=$!

cleanup() {
kill "$preview_pid" 2>/dev/null || true
wait "$preview_pid" 2>/dev/null || true
}
trap cleanup EXIT

ready=false
for _ in $(seq 1 100); do
if curl --fail --silent --show-error http://127.0.0.1:4173/ >/dev/null; then
ready=true
break
fi
sleep 0.1
done

if [[ "$ready" != "true" ]]; then
cp "$preview_log" artifacts/visual-review/preview.log
echo "Specimen preview did not become ready." >&2
exit 1
fi

set +e
node scripts/visual-review.mjs
visual_status=$?
node scripts/mobile-quality-review.mjs
mobile_status=$?
set -e

cp "$preview_log" artifacts/visual-review/preview.log

if [[ "$visual_status" -ne 0 || "$mobile_status" -ne 0 ]]; then
exit 1
fi

- name: Upload visual-review artifact
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: specimen-visual-review-${{ github.event.pull_request.number || github.run_number }}
path: artifacts/visual-review
if-no-files-found: error
retention-days: 14

- name: Upload mobile-quality artifact
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: specimen-mobile-quality-${{ github.event.pull_request.number || github.run_number }}
path: artifacts/mobile-quality
if-no-files-found: error
retention-days: 14
77 changes: 77 additions & 0 deletions handoffs/visual-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Specimen visual review

The specimen browser produces reviewable viewport receipts for changes that can
alter its presentation. These checks are render and responsive-contract smoke
tests, not a pixel-perfect golden-image suite.

## What the workflow proves

For each run, `scripts/visual-review.mjs` drives Chrome through the DevTools
Protocol without adding a browser-testing dependency to the package graph. It
checks:

- the top bar, responsive rail, and main landmark are visible;
- the page has no horizontal overflow and key assembled surfaces have no hidden internal clipping;
- light/dark scheme and cozy/compact density persist through reload;
- the library renders all 16 specimens in its three task groups;
- the assembled lab renders five tabs;
- no uncaught exception or `console.error` is emitted.

The visual runner captures these viewport receipts:

| Surface | Viewport | Scheme | Density |
|---|---:|---|---|
| Library | 1440×1000 | dark | cozy |
| Library | 390×844 | dark | cozy |
| Library | 1440×1000 | light | compact |
| Assembled lab | 1440×1000 | dark | cozy |
| Assembled lab | 390×844 | dark | compact |

`scripts/mobile-quality-review.mjs` adds a stricter library-surface matrix. It
checks the 16-card catalog at 320, 375, 390, and 430 px; light and dark schemes;
cozy and compact density; RTL direction; reduced-motion behavior; and a 200%
root-text-size simulation. It also verifies that every card retains its tab
root, tab list, active panel, full-width stacked layout, and at least a 44 px tab
target. Session and transcript blocks must remain unellipsized and free of
internal overflow.

The 200% case is a deterministic text-resizing stress case, not a claim that it
emulates every browser zoom or operating-system accessibility implementation.
Its purpose is to catch rem-scaled viewport floors, fixed-size controls, and
other layout assumptions that make enlarged text force page-level horizontal
scrolling.

Every run uploads both receipt sets as PNGs with `summary.json` and Markdown
summaries. The visual artifact also includes the Vite preview log, and each
runner writes a bounded Chrome log when its capture process fails. Artifacts are
retained for 14 days.

## Local use

Build and start the specimen preview first:

```bash
pnpm build
pnpm --filter @opencoven/specimens preview --host 127.0.0.1 --port 4173
```

Then, from another shell:

```bash
CHROME_PATH=/path/to/chrome node scripts/visual-review.mjs
CHROME_PATH=/path/to/chrome node scripts/mobile-quality-review.mjs
```

Set `BASE_URL` when the preview is not on `http://127.0.0.1:4173`. Set
`VISUAL_OUTPUT_DIR` or `MOBILE_OUTPUT_DIR` to change the corresponding receipt
directory. `CHROME_DEBUGGING_PORT` and `MOBILE_CHROME_PORT` may be overridden
when the default local ports are occupied.

## Review policy

A green result proves the shell rendered, stayed within the requested viewport,
kept key surfaces free of hidden internal clipping, preserved the named
structural contracts, and emitted no observed runtime error. It does not prove
subjective visual quality. Reviewers should still open the PNG receipts when
hierarchy, spacing, typography, responsive behavior, or component composition
changed.
Loading