# Real scheduling E2E

This harness drives Chromium against the real PHP API and Next UI. It never intercepts or fulfills API responses, mocks clock time, or writes production application/backend code.

## Run

From the `alqove-web` root (requires existing npm/Composer dependencies and PHP >=8.4):

```sh
./node_modules/.bin/playwright install chromium  # one-time browser runtime
python3 -m unittest discover -s e2e-scheduling-real -p 'test_isolation.py' -v
python3 e2e-scheduling-real/run.py
# One scenario, still using a fresh isolated DB:
python3 e2e-scheduling-real/run.py --grep 'real API payroll'
```

The runner refuses occupied ports. It provisions fresh fixtures, launches only its own API at `127.0.0.1:8105` and Next at `http://localhost:3100`, health-checks both, runs Playwright, shuts down its own process groups, and removes its exact database/credential files. It never starts another Next instance alongside an existing listener. A stopped server's TCP TIME_WAIT does not count as an occupied listener.

For iterative development:

```sh
python3 e2e-scheduling-real/run.py --serve
# Another terminal: use the exact run directory printed by that command.
python3 e2e-scheduling-real/test-running.py .cache/scheduling-qa/E2E-<printed-marker> --grep 'test name'
# Ctrl-C / SIGTERM the runner to stop servers and perform cleanup.
```

Do not rerun a mutating case against the same fixture DB or reuse a DB after new migrations land. Use a fresh `run.py` invocation instead. Concurrent production source edits trigger Next Fast Refresh and can reset open forms; stable-source browser evidence is required before crediting a release verdict. `snapshot-start.json`, `snapshot-end.json`, and `freshness.json` bind source hashes to each run. Exit3 means tests passed but source changed during execution; rerun after implementation settles.

## Isolation

- Private artifacts live in existing-gitignored `.cache/scheduling-qa/E2E-<random>/`; run directories0700, files0600.
- The PHP environment is an explicit allowlist with no inherited service credentials. Root API `.env` is neither read nor modified. Private empty environment and storage paths replace root environment/storage; config/route/event cache paths point at nonexistent private files.
- Mail=array, cache/session=array, Scout=null, queue=sync, broadcast=null. No Stripe IDs or enabled payments, no search/email/broadcast/payment calls required, no Redis dependency.
- Bootstrap checks the exact directory marker, exact configured SQLite path, and actual PDO `PRAGMA database_list` path before migration/seeding and on every HTTP request. It refuses a nonempty DB for fixture seeding. Only `migrate` is used—never `migrate:fresh`.
- Factories create four base synthetic `.test` users, two stores, explicit owner/manager/staff memberships, StoreSettings, and initial wage history. Invitation fixtures add two synthetic users through the same guarded environment. All tested scheduling/timekeeping/payroll mutations use actual API requests or real UI buttons.
- Readbacks open SQLite read-only, allow only four evidence tables, enforce exact fixture store IDs, and cap collected rows. Cleanup checks fixture cardinality and markers, deletes only an exact filename allowlist, and writes `cleanup.json` proving database/credential absence. Logs, CSVs and failure traces remain private; **traces may contain synthetic bearer credentials and must not be committed or shared publicly**.
- Initial cleaned evidence originally created under project `.hermes/qa` was moved into `.cache/scheduling-qa`; no protected agent configuration was changed. Root `.gitignore` is unchanged.

## Coverage

- Browser login, membership discovery, store switching and persistence.
- Real API manager/staff capability boundaries, cross-store child IDs, opaque stale revisions, DST-preserving copy preview/commit and atomic overlap rejection.
- Owner browser assigned/open shift creation, draft/publication readbacks, edit-to-draft, copy preview/commit and tenant switching.
- Staff browser draft exclusion, published own/team schedule, refresh and switching.
- Staff browser clock in → unpaid break → end break → clock out; server-derived timestamps and replay of the original real client command.
- Manager browser punch correction, exact120-minute recalculation, approval; owner browser CSV export/download and exact API replay, immutable history guards.
- Real API stranded-clock approval/export guards, independently of UI readiness.
- Real API payroll correction/approval/export, CSV SHA256, replay after settings mutation, same-key/different-input409, money authorization and exported-punch immutability.

- Real invitation email-token capture/scrubbing, login continuation, explicit acceptance, membership discovery and persisted store selection; wrong-account and revoked-invitation rejection. Notification delivery into the pre-hydration browser URL is synthetic; the application flow and backend service are real.

See `STATUS.md` for executed verdicts and boundaries. API-only passes do not substitute for failed browser cases. The final unfiltered run includes all scheduling, payroll and invitation cases.
