Spaces:
Sleeping
Sleeping
| # 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 | |