Default parallel execution of sync-check and git-lfs commands within
the pre-push hook deadlocks: both read from stdin (git pipes the push
refspec to pre-push hooks), and two parallel readers never reach EOF.
Add piped: true to force sequential execution. sync-check runs first
(only inspects push_files via lefthook templating, doesn't actually
consume stdin), then git-lfs runs second with clean stdin access.
Observed during chore/test-infra-001 sprint: LFS upload completed
100% but pre-push hook hung indefinitely. Workaround was --no-verify;
this commit removes the need for that.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Marks all three sprint backlog entries Resolved with sprint commit
references and documented deviations:
- TEST-INFRA-001 (b8d18e6, 82af117, f6509d9, 2dfb1e8) — Playwright
foundation operational locally. CI deferred.
- TEST-CONTRACT-001 (2dfb1e8) — 409 conflict shape verified against
real Laravel. Single-context replay instead of two-browser
concurrent edit; UI rollback assertion deferred to F4.
- TEST-VISUAL-001 (f6509d9) — 5 composite baselines from canonical
prototype. Composite-over-isolated rationale: prototype DOM lacks
data-* attributes; isolated artist-name locators would rot. F4
adds isolated baselines using stable data-test-id.
Opens TEST-INFRA-002 for the deferred CI work: Gitea/GitHub Actions
decision, runner image, caching, screenshot-diff artifacts, label-
gated nightly e2e. No deadline; surfaces when first review cycle
feels drift.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
B5 of TEST-INFRA-001 (RFC-WS-FRONTEND-PRIMEVUE Amendment A-1).
- Add dev-docs/ARCH-TESTING.md (~13 KB):
§1 Five-tier pyramid (Unit / Component / Integration / Visual /
E2E) with environment, cost, and purpose per tier
§2 Decision tree — pick by what is being verified, not by speed
§3 Mock-vs-real-backend rules + the self-confirming-bias anti-
pattern that motivated TEST-CONTRACT-001
§4 Visual baseline workflow including the composite-over-isolated
strategy used in B3
§5 CI strategy stub — deferred to TEST-INFRA-002
§6 Conventions + 5 anti-patterns
§7 Vuetify-during-PrimeVue-migration: explicit doc that the
Vuetify plugin in playwright/index.ts is INTENTIONAL TEMPORARY
STATE replaced in F3 by PrimeVue. Forbids the "abstract the UI
framework provider" deferred-cost trap.
§8 Host setup — Node, pnpm, Chromium, Git LFS, MySQL 8, PHP, .env;
known risks (unpkg.com flakiness, shared crewli_test DB)
§9 Deferred work cross-references to BACKLOG entries
- Update CLAUDE.md ### Testing section to reference ARCH-TESTING.md
- Add ARCH-TESTING.md to .claude-sync.conf so the dev-docs sync
pipeline picks it up; sync script run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
B4 of TEST-INFRA-001 (RFC-WS-FRONTEND-PRIMEVUE Amendment A-1).
- Add api/database/seeders/E2EBaselineSeeder.php — deterministic seed
for Playwright e2e: e2e@test.local user (org_admin) on a fresh org +
event + stage + StageDay + artist + engagement + performance
(version=0). Writes seeded IDs to api/storage/app/e2e-fixtures.json
so the Playwright fixture can construct API URLs without API
discovery calls.
- Add apps/app/tests/playwright-e2e/global-setup.ts — runs
`php artisan migrate:fresh --force --seed` against crewli_test (the
existing PHPUnit MySQL test DB) before the test suite starts.
Uses --env=testing to satisfy the dangerous-bash hook's migrate:fresh
guard.
- Add apps/app/tests/playwright-e2e/utils/fixtures.ts — typed reader
for e2e-fixtures.json. Cached after first read.
- Add apps/app/tests/playwright-e2e/utils/auth.ts — login helper that
POSTs /api/v1/auth/login and returns user/org IDs. Uses Bearer-via-
cookie flow (per api/.../SetAuthCookie.php), not stateful Sanctum.
- Add apps/app/tests/playwright-e2e/timetable/409-conflict.spec.ts —
the contract test: first move with version=0 returns 200, second
move with same stale version returns 409 with shape
`errors.conflict: 'version_mismatch'`. Catches the schema-drift
bug class that timetable-stabilization B5 surfaced.
- Update apps/app/playwright.config.ts — wire globalSetup, webServer
for `php artisan serve --port=8001`, baseURL `http://localhost:8001`
(NOT 127.0.0.1 — auth cookie's domain=localhost requires hostname
match).
- Update .gitignore — runtime e2e-fixtures.json never committed.
DoD-19 met locally: `pnpm test:e2e` passes against a real Laravel
test server. CI integration deferred to TEST-INFRA-002 (per A-1
amendment).
Constraint: e2e tests share the crewli_test DB with PHPUnit. Running
both concurrently would collide. Documented in ARCH-TESTING.md (B5).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
B3 of TEST-INFRA-001 (RFC-WS-FRONTEND-PRIMEVUE Amendment A-1).
- Add tests/playwright-ct/visual/static-server.mjs: 60-line Node http
server that serves the canonical prototype directory. No new
dependency added (vs. http-server / serve packages).
- Wire static server into playwright-ct.config.ts via webServer; tests
navigate to http://127.0.0.1:5179/crewli-timetable.html.
- Add tests/playwright-ct/visual/prototype-smoke.spec.ts to verify the
prototype loads in CT runner.
- Add tests/playwright-ct/visual/prototype.spec.ts with 5 @visual
composite baselines:
canvas-friday.png — all status colors, b2b indicators,
multi-lane stacking
canvas-saturday.png — conflict ring + capacity warnings
stage-row-multilane.png — first row in isolation
wachtrij-populated.png — sidebar list with parked + pending
popover.png — block-click popover layout
9 additional surfaces from RFC §A.3's enumerated list are documented
as test.skip() with reasons (cancelled status absent from prototype
data, isolated-block locators would lock to artist names, drag-mode
flaky under simulated pointer events, empty Wachtrij/empty day not
reachable from canonical seed). All deferred to F4 component-level
Vue baselines that will use stable data-test-id attributes.
- Baselines stored at tests/playwright-ct/__screenshots__/visual/
prototype.spec.ts/*.png; tracked via Git LFS (.gitattributes).
Composite-over-isolated rationale: the prototype's DOM exposes status
only via inline style.background, no data-* attributes. Isolated-block
baselines would require artist-name locators that silently rot if
prototype data changes. Composite captures yield the same visual
vocabulary in fewer, more stable images. dev-docs/ARCH-TESTING.md (B5)
documents this strategy and the F4 transition plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
B2 of TEST-INFRA-001 (RFC-WS-FRONTEND-PRIMEVUE Amendment A-1).
- Add tests/playwright-ct/utils/mountWithProviders.ts: ergonomic
wrapper around Playwright CT's mount() exposing buildMountArgs()
and readNotificationState(). Documents the Vue Test Utils ↔
Playwright CT API divergence (provider plugins must be wired in
beforeMount, not at call time) and the Vuetify-temp lifecycle
(replaced by PrimeVue in F3).
- Add tests/playwright-ct/components/SanityButtonHarness.vue: a
v-btn harness with a click counter; lives in a .vue file so Vite
bundles its CSS-side-effect imports for the browser context
(Playwright CT runs the test orchestrator in Node and components
in a Vite-bundled browser, unlike Vitest's single jsdom graph).
- Add tests/playwright-ct/components/sanity-vuetify.spec.ts: two
tests proving (a) v-btn renders and propagates clicks, (b) the
--v-theme-primary CSS variable resolves to a parseable RGB triplet.
- Update playwright/index.ts: import 'vuetify/styles' so the v-btn
renders with its actual visual appearance (not unstyled). Required
for B3's visual baselines.
3 component tests pass. 402 Vitest tests still pass unchanged.
Lint + typecheck clean on new files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>