github-actions[bot]
Deploy 7688ef1
13d4e44
Raw
History Blame Contribute Delete
9.92 kB
# Parity checks between the React frontend and standalone_interface.html,
# plus the demo-scenario replay suite (config_small_grid coverage).
#
# See scripts/PARITY_README.md for the four-layer parity design and
# scripts/parity_e2e/DEMO_REPLAY_README.md for the demo-replay layers.
# Summary:
# Layer 1 — static inventory (events, API paths, settings, schema drift)
# Layer 2 — session-reload fidelity (save-vs-restore symmetry)
# Layer 3a — gesture-sequence static proxy (ordered event emission)
# Layer 3b — behavioural E2E + demo-replay (real DOM, mocked backend):
# * `e2e_parity.spec.ts` — React ↔ standalone parity
# * `demo_replay.spec.ts` — fiche-as-data scenario walker
# * `demo_visual_snapshots.spec.ts` — normalised SVG/HTML diffs
# * `demo_meta_invariants.spec.ts` — console errors, empty
# text, pin-count sanity
# Layer 4 — user-observable invariants (static source patterns)
#
# Layers 1 + 2 + 3a + 4 run on every PR (fast, deterministic, no browser).
# The cheap `demo_meta_invariants` slice runs on every PR too (~1 min
# Chromium install + ~20 s test) — it catches console errors and
# undefined-id leaks that no static pattern can see.
# The full Layer 3b suite (parity + replay + snapshots) runs nightly +
# on PRs labelled `e2e`. ~3 min per run.
name: Parity
on:
push:
branches: [main]
# Run on every PR regardless of its base branch (e.g. PRs stacked onto a
# `claude/*` working branch, not just those targeting main).
pull_request:
branches: ['**']
schedule:
# Nightly at 02:30 UTC — catches drift introduced between PRs
# (e.g. docs/features/interaction-logging.md updates without a code change).
- cron: '30 2 * * *'
# Always allow a re-run to supersede an in-flight one for the same PR.
concurrency:
group: parity-${{ github.ref }}
cancel-in-progress: true
jobs:
# -------------------------------------------------------------------
# Layer 1 — static inventory parity (events, API paths, settings,
# three-way spec diff). No backend, no browser.
# -------------------------------------------------------------------
layer1-static-inventory:
name: 'Layer 1 · static inventory parity'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
# The parity check needs an up-to-date standalone bundle to
# diff against. `frontend/dist-standalone/` is gitignored, so
# in CI we have to (re)build it from source — otherwise the
# script falls back to the frozen `standalone_interface_legacy.html`
# which lags behind the React source by design.
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Build standalone bundle
working-directory: frontend
run: |
npm ci
npm run build:standalone
- name: Run parity check
run: python scripts/check_standalone_parity.py
- name: Emit Markdown summary
if: always()
run: |
python scripts/check_standalone_parity.py --emit-markdown \
>> "$GITHUB_STEP_SUMMARY" || true
# -------------------------------------------------------------------
# Layer 2 — session-reload fidelity (save/restore symmetry).
# -------------------------------------------------------------------
layer2-session-fidelity:
name: 'Layer 2 · session-reload fidelity'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Build standalone bundle
working-directory: frontend
run: |
npm ci
npm run build:standalone
- name: Run session-fidelity check
run: python scripts/check_session_fidelity.py
# -------------------------------------------------------------------
# Layer 3a — gesture-sequence static proxy. Lightweight sequence-
# aware check; complements Layer 1's set-based diff.
# -------------------------------------------------------------------
layer3a-gesture-sequence:
name: 'Layer 3a · gesture-sequence static proxy'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Build standalone bundle
working-directory: frontend
run: |
npm ci
npm run build:standalone
- name: Run gesture-sequence check
run: python scripts/check_gesture_sequence.py
# -------------------------------------------------------------------
# Layer 4 — user-observable invariants. Static source-pattern
# assertions for the six bug classes Layers 1-3a cannot catch by
# construction (visual thresholds, conditional rendering,
# field semantics, auto-effects, loading-state, performance).
# -------------------------------------------------------------------
layer4-invariants:
name: 'Layer 4 · user-observable invariants'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Build standalone bundle
working-directory: frontend
run: |
npm ci
npm run build:standalone
- name: Run invariants check
run: python scripts/check_invariants.py
# -------------------------------------------------------------------
# Demo meta-invariants — cheap Playwright slice that catches console
# errors, empty visible text, undefined-id leaks and pin-count
# inconsistencies on the small_grid demo flow. Runs on every PR
# because the signal-to-cost ratio is high (~1 min total: Chromium
# install + ~20 s test). The full Layer 3b suite still gates on
# the `e2e` label / nightly schedule.
# -------------------------------------------------------------------
demo-meta-invariants:
name: 'Demo · meta-invariants (Playwright, fast)'
runs-on: ubuntu-latest
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: |
frontend/package-lock.json
scripts/parity_e2e/package-lock.json
- name: Install frontend deps + build
working-directory: frontend
run: |
npm ci
npm run build
- name: Install E2E deps
working-directory: scripts/parity_e2e
run: npm ci
- name: Install Playwright Chromium
working-directory: scripts/parity_e2e
run: npx playwright install --with-deps chromium
- name: Run demo meta-invariants only
working-directory: scripts/parity_e2e
run: npx playwright test demo_meta_invariants.spec.ts
- name: Upload Playwright report on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-demo-meta-report
path: scripts/parity_e2e/playwright-report/
if-no-files-found: ignore
retention-days: 7
# -------------------------------------------------------------------
# Layer 3b — behavioural E2E + demo-replay full suite with
# Playwright. Runs every spec matched by `playwright.config.ts`'s
# testMatch (`*parity.spec.ts` + `demo_*.spec.ts`): the React ↔
# standalone parity check, the fiche-as-data scenario walker, the
# normalised SVG/HTML snapshot diffs, and the meta-invariants
# battery. Browser install + 4 specs ≈ 3 min per run, so this job
# only fires nightly or on PRs carrying the `e2e` label.
# -------------------------------------------------------------------
layer3b-behavioural-e2e:
name: 'Layer 3b · behavioural E2E + demo replay (Playwright)'
runs-on: ubuntu-latest
if: |
github.event_name == 'schedule'
|| (github.event_name == 'pull_request'
&& contains(github.event.pull_request.labels.*.name, 'e2e'))
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: |
frontend/package-lock.json
scripts/parity_e2e/package-lock.json
- name: Install frontend deps + build
working-directory: frontend
run: |
npm ci
npm run build
- name: Install E2E deps
working-directory: scripts/parity_e2e
run: npm ci
- name: Install Playwright browsers
working-directory: scripts/parity_e2e
run: npx playwright install --with-deps chromium
- name: Run E2E parity spec
working-directory: scripts/parity_e2e
run: npx playwright test
- name: Upload Playwright report + artefacts
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
scripts/parity_e2e/playwright-report/
scripts/parity_e2e/artefacts.json
if-no-files-found: ignore
retention-days: 14