ADR 015 Refreshable Preview E2E Runners
Context
Section titled “Context”The stage preview can pass backend Supabase/Restate smoke while the browser flow
still fails because /tmp Supabase Auth session files expire. Manual token
copying makes proof non-repeatable and hides whether the page, API, or auth
fixture is broken.
The reservation card flow also must not rely on frontend fixture JSON. The page has to load backend-owned mother booking operational context and then send the real booking workflow command.
Decision
Section titled “Decision”Preview E2E proof uses tracked Playwright runners:
pnpm playwright:reservation-card-flowpnpm playwright:operator-projection
Both runners load a session JSON file, refresh it through Supabase Auth when a refresh token and publishable/anon key are available, inject only browser-safe access-token cookies, and write redacted diagnostics to artifacts. They must not print access tokens, service-role keys, or refresh tokens.
Browser lifecycle is part of proof, not optional cleanup:
- Run one browser and one viewport sequence at a time.
- Bound each runner with a wall-clock timeout; terminate its process group with
SIGTERM, thenSIGKILLafter a short grace period. - Close page context and browser in
finally. - A worker using Playwright MCP must close the MCP browser before the worker is stopped. Do not terminate an agent while its browser is active.
- After every run, verify no reservation runner, Playwright daemon, headless
Chromium, or
playwright_chromiumdev_profile-*process remains. A run with a leaked process is failed evidence even when business assertions passed.
Reservation preview success requires browser evidence for the stage API calls:
POST /reservation-booking-workflow-starts = 202.GET /reservation-booking-workflow-requests/{requestId} = 200.GET /reservation-financial-lifecycles/{bookingId}/mother-visible-charge-summary = 200.
Settlement operator preview success requires backend-owned readiness, not local row counting:
settlementBoardState=ready.settlementDataRoomReadiness=branch_to_headquarters_settlement.GET /customer-service-balance-caregiver-account-disclosure-readiness/{bookingId}returnsready,not_required,grant_missing,instruction_missing, orinstruction_unavailable; the frontend calls the grant endpoint only after readiness isready.- Zero console errors and zero HTTP response errors.
Implementation Evidence
Section titled “Implementation Evidence”- Reservation Playwright runner
- Reservation settlement identifier gate tests
- Bounded Playwright process runner
- Bounded process-group cleanup tests
- Operator projection runner
- Shared refresh and redaction helper
implementation: implemented covers tracked runner/session-refresh mechanism,
not current preview health or deployment readiness.
Historical Runtime Evidence
Section titled “Historical Runtime Evidence”Following stage proof was recorded 2026-07-09 and was not rerun in this audit:
RESERVATION_FLOW_SUBMIT=1 pnpm playwright:reservation-card-flowsubmitted requestRBQ-e31f1bf8-3b04-4be4-846c-ee22de68b9e3, created bookingc1c75eca-9f6a-4072-87f2-ea8b054a5ce9, reachedprogressStatus=확정, showedpaymentWorkflowStatus=completed · 결제 진행 완료, and displayed1,681,519원as mother-visible payable.pnpm playwright:operator-projectionpassed on desktop and mobile for branch profile00000000-0000-0000-0000-000000000101.
Known fixture gap: when both access and refresh tokens are expired or revoked, an external fixture-generation command must seed or sign in the stage users again. That is fixture setup, not frontend business logic.
2026-07-09 follow-up: the backend and public contract now expose
/customer-service-balance-caregiver-account-disclosure-readiness/{reservationBookingId}
so the reservation-to-settlement preview can show post-payment account-disclosure
continuation readiness without treating 404 from the protected grant endpoint
as UI logic. ready is the only state where the frontend may call
/customer-service-balance-caregiver-account-disclosure-grants/{reservationBookingId};
all blocked states render backend blockingFacts and never expose raw account
fields.
2026-07-09 follow-up: zero service-balance due now returns
readinessStatus=not_required with empty blockingFacts, so preview UI can show
the settlement path as complete without fabricating an account-popup blocker.
Direct backend driver proof booking 27f69a4c-7600-4de1-b611-839d8e757b94
settled with caregiver payout 50000, branch settlement 100000, HQ settlement
50000, ledgerLineCount=3, and rawAccountFieldsVisible=false.
Stage runtime check after Mutagen flush and backend-dev restart returned
200 for
/customer-service-balance-caregiver-account-disclosure-readiness/a0d6ffbf-a83f-4458-9967-3fb27b5faa9f
with readinessStatus=instruction_missing,
blockingFacts=["customer_direct_service_balance_instruction_projection_missing"],
and rawAccountFieldsVisible=false; this is a valid blocked continuation
projection for demo, not a frontend-inferred missing-grant state.
2026-07-09 demo proof:
- Local proof plane used frontend preview
http://127.0.0.1:24161, backend smoke apphttp://127.0.0.1:24160, and proxyhttp://127.0.0.1:24162. The proxy routed mother context and account disclosure readiness to the stage backend while keeping booking and settlement smoke on the current backend branch. RESERVATION_FLOW_SUBMIT=1 RESERVATION_FLOW_SUBMIT_VIEWPORTS=all RESERVATION_FLOW_SMOKE_SETTLEMENT=1 pnpm playwright:reservation-card-flowpassed on desktop and mobile with zero console errors, zero failed requests, and zero bad responses.- Desktop booking
f4e2506c-2c29-419f-ab82-564ce487bad8and mobile booking33a62445-6ef2-4274-a067-c533a2def3ddboth reachedprogressStatus=확정,paymentWorkflowStatus=completed · 결제 진행 완료, and settlementfinancialLifecycleStage=settled. - Mother-visible payable stayed invariant before and after settlement:
1,681,519원 -> 1,681,519원. Settlement smoke exposed caregiver payout1,177,063원, branch settlement168,151원, HQ settlement252,227원, and policy versionreservation-settlement-smoke-v1. - Artifacts live under
frontend-repo/output/playwright/reservation-card-flow-demo-clean-readiness-20260709/.
2026-07-11 current proof and correction:
- Browser request
RBQ-78c9307d-0534-4d87-9f0e-90641786b683created bookingd0d51303-703e-40c0-8bcc-95dcc9e6ef88, reachedprogressStatus=확정, completed the payment workflow, and rendered backend payable1,681,519원. - The backend settlement driver reached
financial_stage=settled; a direct stage database read confirmed branch settlement63aeb220-f2ac-5d49-b49f-86770a516980. The runner still exited failed because test-data reaper references were written before their batch row. Backendf63af0b15f14e398c6c879bea595316b8afab0b0makes batch registration FK-safe and adds a colocated test. - Frontend
61c87af57522e1a466bd291d22371dc56c7bdc5brejects empty, placeholder, request-id, and malformed booking identifiers before settlement and surfaces the backend workflow failure instead of driving settlement with-. - A later browser attempt left Playwright/Chromium processes after worker
shutdown. Cleanup removed all browser and stale Serena processes; host memory
changed from
26 GiB used / 4.6 GiB available / 7.9 GiB swap usedto12 GiB used / 18 GiB available / 3.4 GiB swap used. This incident required the bounded process runner before another browser attempt. - First bounded attempt exposed obsolete Restate ingress shape: workflow
execution completed after the backend caller timed out. Backend
afd5fc7383d5cf4f907501dba189a42c52cd59cbswitched to official asynchronous/{workflow}/{key}/{handler}/send; live API smoke then completed in 2.35 seconds with invocationinv_198Joz6YUL3C5T51ASTErW3FWSSXtNW4qq. - Fresh bounded run submitted request
RBQ-fe3512da-50b4-43ac-a2c8-64d86a9d9fbf, created booking1b291ba5-17a2-4756-9767-1819846bdd6d, reachedprogressStatus=확정, and preserved mother-visible payable1,681,519원through settlement. - Backend driver recorded service delivery by branch operator, caregiver payout
1,581,519원, branch settlement100,000원, HQ settlement0원, three ledger lines, finalfinancialStage=settled, and account-disclosure readinessnot_requiredwithrawAccountFieldsVisible=false. - Authenticated branch projection rendered
settlementBoardState=readyandsettlementDataRoomReadiness=branch_to_headquarters_settlement. Browser proof had zero console events, failed requests, and HTTP error responses. - Runner exit was
0; post-run process check found zero reservation runner, Playwright, headless Chromium, and temporary Playwright profile processes. Available host memory remained15 GiBafter cleanup. - Backend
b0bbd21replaced source-name-specific, manually ordered cleanup with a resumable stage booking-reap preparation RPC. It verifies the expired batch and booking root, discovers direct booking children and dependency depth from Postgres foreign-key catalogs, then lets the guarded reaper delete child-first. - Guarded retry removed three expired booking batches and 90 referenced rows; the immediate dry-run result was zero expired batches and zero references.
- A subsequent non-browser reservation-to-settlement stage run passed for
booking
3ce972db-996b-4c8b-a3f2-b21fc04a9b5a, including delegated payment, service-delivery continuation, settlement completion, and the branch board.
Consequences
Section titled “Consequences”Preview evidence becomes reproducible, token-safe, and resource-bounded when fixtures remain refreshable and process cleanup passes. Expired/revoked fixture generation remains external setup, not frontend business logic.
Revision History
Section titled “Revision History”- 2026-07-09: accepted refreshable tracked preview runners.
- 2026-07-10: scoped implemented status to runner code and marked stage results historical.
- 2026-07-11: made browser/MCP process cleanup part of proof and recorded the booking-to-settled incident, bounded runner, Restate async fix, and fresh browser-to-settlement-projection success; added resumable FK-aware fixture cleanup and a fresh non-browser stage completion proof.