Skip to content

diavola-88607: Phase 7 — Hardening for production - #8

Merged
snackman merged 4 commits into
mainfrom
diavola-88607-phase-7
Jun 5, 2026
Merged

diavola-88607: Phase 7 — Hardening for production#8
snackman merged 4 commits into
mainfrom
diavola-88607-phase-7

Conversation

@snackman

@snackman snackman commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Phase 7 — Hardening for Production

Makes the codebase production-trustworthy: a real automated test suite over the risk areas, observability scaffolding, an RLS isolation harness, training mode, and documented production readiness — all runnable now against the mock/pure logic with zero env vars. No live Supabase/Stripe/crypto/DoorDash wiring (that's the separate final phase).

Automated test suite (Vitest — 106 tests / 15 files, zero env)

Added Vitest (+ fake-indexeddb, jsdom); npm test / npm run test:run.

  • Money/pricing — half-and-half (left+right == whole, ceil-rounded, no leak), size deltas, discount-before-tax, integer-cents invariants
  • Platform fee — bps+flat, round-half-up, clamp-to-charge, card-only
  • Payment idempotency — same tender UUID → one tender (no double charge), order paid only when settled covers total, split → zero balance, crypto pending counts to balance but not paid → confirm settles, refund/void (full + partial)
  • Offline queue — idempotent upsert-by-UUID (enqueue + double-flush + reconnect retry never duplicates)
  • Delivery — out-of-zone + below-minimum rejected, fee/ETA quote, pickProvider selection
  • Scheduling — store-hours + prep gate (ASAP, scheduled lead/horizon, closed → rejected, midnight-wrap)
  • Entitlements — over-limit location blocked on Starter / allowed on Pro / unlimited Multi; online-ordering + advanced-reports gates; canceled → blocked
  • Reports — payment mix, tip de-dup (online order-level vs in-store tender), voids, failed-tender exclusion, per-location vs tenant rollup
  • Inventory — depletion on order + low-stock flag + clamp ≥ 0
  • Drawer/Z-report — over/short math + idempotent business-day close
  • App-layer tenant isolation — a tenant can't read another's orders/menu/inventory/reports/locations via the driver

RLS isolation harness

  • Expanded supabase/tests/rls_isolation.sql (no-orphan write, memberships + users isolation) + run-rls-isolation.sh one-command harness + README.
  • CI: required test job (npm run test:run, zero env) + optional non-blocking rls-isolation Postgres job (a live DB is never a required gate).

Observability (env-optional, no-op safe)

Structured JSON logger (LOG_LEVEL), request/trace ids (reuse x-request-id/traceparent, echoed on responses), error seam (captureError → Sentry only when SENTRY_DSN set, else log no-op). Wired into /api/orders; /api/health (liveness) + new /api/ready (readiness: probes driver, reports training mode, 503 on dep failure).

Training/demo mode

TRAINING_MODE flag (also implied by the mock driver) + banner via /api/ready.

Docs

docs/PRODUCTION_READINESS.md (tenant-isolation audit, idempotency guarantees, offline/store-and-forward risks, Connect/PCI posture — no PAN/SAQ-A, crypto finality, Supabase PITR, env inventory, go-live checklist) + docs/IDEMPOTENCY_REVIEW.md. No double-charge/dupe bugs found.

Local results (zero env vars)

  • npm run test:run106 passed / 15 files
  • npm run typecheck → clean · npm run lint → no warnings · npm run build → success (incl. /api/ready)

Preview URLs are SSO-protected (401 expected). Rely on gh pr checks Vercel status.

🤖 Generated with Claude Code

Add a real automated test suite, observability scaffolding, RLS harness,
training mode, and production-readiness docs — all runnable now against the
mock/pure logic with zero env vars. No live service wiring (final phase).

Tests (Vitest, 106 tests / 15 files, zero env):
- money/pricing incl. half-and-half (left+right==whole), size deltas, integer
  cents; platform fee (bps+flat, rounding, clamp, card-only)
- payment idempotency/no-double-charge, settled-vs-covered, split → zero,
  crypto pending→confirm, refund/void
- offline queue idempotent upsert-by-UUID (double-flush + reconnect retry)
- delivery zone gating + below-minimum + pickProvider; scheduling hours/prep
- entitlements/plan gating; reports aggregation (mix, tip de-dup, voids,
  rollup); inventory depletion + low-stock; drawer/Z-report + idempotent close
- app-layer tenant isolation (driver-scoped reads)

Observability: structured logger, request/trace ids, error seam (Sentry behind
SENTRY_DSN, no-op otherwise), /api/health + new /api/ready; wired into
/api/orders. Training/demo mode flag (TRAINING_MODE / mock driver).

RLS: expanded supabase/tests/rls_isolation.sql (memberships/users/no-orphan
write) + run-rls-isolation.sh harness + README; optional non-blocking Postgres
CI job. Added required vitest CI job.

Docs: docs/PRODUCTION_READINESS.md + docs/IDEMPOTENCY_REVIEW.md; env inventory
in .env.example. No double-charge/dupe bugs found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pos Ready Ready Preview, Comment Jun 5, 2026 3:22am

Request Review

The optional RLS-isolation job ran against vanilla Postgres, which lacks
Supabase's auth schema / auth.uid() the tenancy policies depend on. Apply
the auth_shim.sql before migrations (CI job + harness, skippable via
SKIP_AUTH_SHIM=1 for real Supabase) so the optional job exercises real RLS
and goes green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The RLS isolation test does `set local role authenticated`; vanilla Postgres
lacks Supabase's standard roles. Create them (no-login) in the shim so the
optional Postgres RLS job can switch roles and exercise the policies.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RLS gates rows but table GRANTs gate table access; the migration relied on
Supabase's implicit default privileges, so `authenticated` was denied on
vanilla Postgres. Grant the tenancy tables to `authenticated` (rows still
restricted by the policies) and only schema usage to `anon` — explicit,
portable, least-privilege. Makes the RLS isolation test pass off-platform.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@snackman
snackman marked this pull request as ready for review June 5, 2026 03:23
@snackman
snackman merged commit d3ff944 into main Jun 5, 2026
5 checks passed
@snackman
snackman deleted the diavola-88607-phase-7 branch June 5, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant