Skip to content

ADR 015 Refreshable Preview E2E Runners

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.

Preview E2E proof uses tracked Playwright runners:

  • pnpm playwright:reservation-card-flow
  • pnpm 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:

  1. Run one browser and one viewport sequence at a time.
  2. Bound each runner with a wall-clock timeout; terminate its process group with SIGTERM, then SIGKILL after a short grace period.
  3. Close page context and browser in finally.
  4. 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.
  5. 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:

  1. POST /reservation-booking-workflow-starts = 202.
  2. GET /reservation-booking-workflow-requests/{requestId} = 200.
  3. GET /reservation-financial-lifecycles/{bookingId}/mother-visible-charge-summary = 200.

Settlement operator preview success requires backend-owned readiness, not local row counting:

  1. settlementBoardState=ready.
  2. settlementDataRoomReadiness=branch_to_headquarters_settlement.
  3. GET /customer-service-balance-caregiver-account-disclosure-readiness/{bookingId} returns ready, not_required, grant_missing, instruction_missing, or instruction_unavailable; the frontend calls the grant endpoint only after readiness is ready.
  4. Zero console errors and zero HTTP response errors.

implementation: implemented covers tracked runner/session-refresh mechanism, not current preview health or deployment readiness.

Following stage proof was recorded 2026-07-09 and was not rerun in this audit:

  • RESERVATION_FLOW_SUBMIT=1 pnpm playwright:reservation-card-flow submitted request RBQ-e31f1bf8-3b04-4be4-846c-ee22de68b9e3, created booking c1c75eca-9f6a-4072-87f2-ea8b054a5ce9, reached progressStatus=확정, showed paymentWorkflowStatus=completed · 결제 진행 완료, and displayed 1,681,519원 as mother-visible payable.
  • pnpm playwright:operator-projection passed on desktop and mobile for branch profile 00000000-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 app http://127.0.0.1:24160, and proxy http://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-flow passed on desktop and mobile with zero console errors, zero failed requests, and zero bad responses.
  • Desktop booking f4e2506c-2c29-419f-ab82-564ce487bad8 and mobile booking 33a62445-6ef2-4274-a067-c533a2def3dd both reached progressStatus=확정, paymentWorkflowStatus=completed · 결제 진행 완료, and settlement financialLifecycleStage=settled.
  • Mother-visible payable stayed invariant before and after settlement: 1,681,519원 -> 1,681,519원. Settlement smoke exposed caregiver payout 1,177,063원, branch settlement 168,151원, HQ settlement 252,227원, and policy version reservation-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-90641786b683 created booking d0d51303-703e-40c0-8bcc-95dcc9e6ef88, reached progressStatus=확정, completed the payment workflow, and rendered backend payable 1,681,519원.
  • The backend settlement driver reached financial_stage=settled; a direct stage database read confirmed branch settlement 63aeb220-f2ac-5d49-b49f-86770a516980. The runner still exited failed because test-data reaper references were written before their batch row. Backend f63af0b15f14e398c6c879bea595316b8afab0b0 makes batch registration FK-safe and adds a colocated test.
  • Frontend 61c87af57522e1a466bd291d22371dc56c7bdc5b rejects 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 used to 12 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 afd5fc7383d5cf4f907501dba189a42c52cd59cb switched to official asynchronous /{workflow}/{key}/{handler}/send; live API smoke then completed in 2.35 seconds with invocation inv_198Joz6YUL3C5T51ASTErW3FWSSXtNW4qq.
  • Fresh bounded run submitted request RBQ-fe3512da-50b4-43ac-a2c8-64d86a9d9fbf, created booking 1b291ba5-17a2-4756-9767-1819846bdd6d, reached progressStatus=확정, and preserved mother-visible payable 1,681,519원 through settlement.
  • Backend driver recorded service delivery by branch operator, caregiver payout 1,581,519원, branch settlement 100,000원, HQ settlement 0원, three ledger lines, final financialStage=settled, and account-disclosure readiness not_required with rawAccountFieldsVisible=false.
  • Authenticated branch projection rendered settlementBoardState=ready and settlementDataRoomReadiness=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 remained 15 GiB after cleanup.
  • Backend b0bbd21 replaced 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.

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.

  • 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.