diff --git a/HANDOVER.md b/HANDOVER.md new file mode 100644 index 0000000..02bb7f1 --- /dev/null +++ b/HANDOVER.md @@ -0,0 +1,157 @@ +# Team Handover — Delivery Date & Time Shopify App + +**Date:** 2026-09-07 +**Branch:** `main` @ `f8dd5b2` +**Prepared for:** engineering team taking over active development + the test rollout + +Read this alongside: +- `PRODUCT_STRATEGY.md` — the *why* / feature rationale +- `IMPLEMENTATION_PLAN.md` — the phased build plan + architecture +- `IMPLEMENTATION_REVIEW_2026-09-04.md` — gap audit vs. the DS competitor study, and what was closed +- `CLAUDE.md` — the non-negotiables (server-side enforcement, DST-safe time, one scheduling service, multi-tenant, minimal scopes) + +--- + +## 1. Where the project is + +Greenfield build, Phases 0–8 substantially done (Phase 8 = billing + BfS hardening; a11y / perf / empty-state polish deferred to live testing). The app is deployed to a **real production server** (`metatron-delivery.thedomainnest.com`, PM2 + colocated Postgres/Redis) and is being tested against a password-protected dev store (Spark theme). + +**What works today (server-side, unit-tested):** the scheduling engine (templates / overrides / blackouts / cutoffs / lead times / capacity, DST-safe), slot-holds with an atomic Redis race test, multi-location + postal/radius/driving zones + nearest-location auto-assign, product rules, rate resolution, the dispatch dashboard, billing tiers + feature gating, GDPR handlers (wired, webhooks disabled — see §4), and the Cart/Checkout Validation Function. + +**156 unit tests pass** (`npm test`). Function WASM tests and integration (Redis/DB) tests run in CI only. + +--- + +## 2. What shipped in the last batch (2026-09-04) + +| Commit | What | +|---|---| +| `a2c78d7` | **DS study gap closure** — see `IMPLEMENTATION_REVIEW_2026-09-04.md` §"Implementation follow-up". Inventory-based location exclusion wired in; live slot re-validation at checkout via a shop-metafield capacity snapshot; scopable enforcement (`Shop.enforcementMode`); per-day order cap (`Location.dailyOrderCap`); product-rule slot blocking (`ProductRule.blockedStartMins`); product-page preview block; French locale; **new `payment-customization` Function**; **`checkout-datetime` Checkout UI Extension source restored**. | +| `3749b4d` | Widget decodes HTML entities in theme label attributes (`Couldn't…` → `Couldn't…`). | +| `d98b29e` | `UNLOCK_ALL_FEATURES` env flag — `getShopTier()` returns `"pro"` so all plan-gated screens open for testing. | +| `f8dd5b2` | Fixed inert Polaris `` inert without `value`+`onChange` | admin forms | Fixed for slots/blackouts/rates (`f8dd5b2`). **Audit rule for the team:** every Polaris `Select`/`TextField`/`Checkbox`/`ChoiceList` in a form must be controlled — this Polaris + App Bridge version freezes uncontrolled inputs (cf. commit `cc20f20`). | +| B4 | `checkout-datetime` extension ships `tsconfig` `checkJs:false` | `extensions/checkout-datetime/` | Source was restored from a `dist/`-only state and not yet reconciled against live `@shopify/ui-extensions` checkout types. Before shipping: `npm install` in the folder, flip `checkJs:true`, fix the `` component props, verify on a Plus dev store. Its README has the checklist. | +| B5 | `payment-customization` Function has no committed `schema.graphql` / `generated/` | `extensions/payment-customization/` | By design (gitignored, codegen'd). Run `npm --prefix extensions/payment-customization run typegen` then `shopify app deploy`; merchant activates it under Settings → Payments. Not in CI's `test:functions` yet — its pure logic *is* covered by `tests/unit/payment-customization.test.ts`. | +| B6 | GDPR compliance webhooks commented out | `shopify.app.toml` | Handlers exist and are wired (`webhooks.customers.data_request`, `.customers.redact`, `.shop.redact`). Blocked on Shopify granting **Protected Customer Data Access** (Partner Dashboard questionnaire). Re-enable the three `[[webhooks.subscriptions]]` blocks before any public/BfS submission. | +| B7 | `read_customers` scope intentionally dropped | `shopify.app.toml` | Needed only for the "recognize returning customers" parity item — add back with B6's approval when that feature is built. | +| B8 | Checkout snapshot 30-day horizon can go stale | `jobs/worker.ts` (TODO), `checkout-snapshot.server.ts` | Refreshed inline on order webhooks + slot/blackout/location/enforcement edits, which covers real usage. A periodic worker sweep to keep the rolling horizon advancing on a zero-activity store needs offline-session storage first (so the worker process can get an Admin client per shop). | +| B9 | CI `typecheck:checkout` step | `.github/workflows/ci.yml` | Was failing before this batch (the extension had no `package.json`). Now restored; will pass once deps install. Watch the first CI run after this handover. | + +--- + +## 5. Next-step implementation (prioritised) + +From `IMPLEMENTATION_REVIEW_2026-09-04.md` plus the deferred list. Rough order: + +### P1 — finish what's in flight +1. **Reconcile `checkout-datetime` with live types + test on Plus** (B4). The server routes it calls (`checkout.scheduling.*`) are already done and tested. +2. **Deploy + smoke `payment-customization`** on a dev store with a manual COD gateway (B5). +3. **Rates: decide display-only vs. real pricing.** Today `delivery-customization` only *renames* the delivery option to append a price label; it does not set the charged amount. If real location/method-based rates are required (DS's core value prop), that's a `delivery-customization` pricing operation or Shopify carrier-rate integration — scope it as its own task. + +### P2 — parity gaps still open +4. **Multi-pin pickup-location map** — the widget currently maps a single resolved location; DS shows a chooser across all pickup points. +5. **Returning-customer recognition** — needs B6/B7 (Protected Customer Data + `read_customers`). +6. **Cart Transform / deposits** — v2 per the plan (`write_cart_transforms` scope already requested). + +### P3 — BfS / launch readiness +7. **Re-enable GDPR webhooks** once access is granted (B6). +8. **Accessibility + performance pass, empty/loading/error states, onboarding polish** — the deferred Phase 8 items. Best done against the live store now that it exists. +9. **Deploy pipeline** (§6). +10. **Remove test toggles** — `UNLOCK_ALL_FEATURES`, confirm `automatically_update_urls_on_dev`, review scopes. + +### P4 — v1.x fast-follow (from `IMPLEMENTATION_PLAN.md` Phase 9) +Waitlists, self-service reschedule portal, email/SMS reminders (BullMQ + a `Notification` model), printable run sheets, holiday auto-import. + +--- + +## 6. Environment, infra, and gotchas + +### Deployment +- **Production server:** `metatron-delivery.thedomainnest.com`, Remix under **PM2** (`ecosystem.config.cjs`), Postgres + Redis colocated. `PORT=11002` (see server `.env`). +- **No deploy pipeline yet.** Current process is manual: `git pull` → `npm ci` → `npm run setup` (`prisma generate && prisma migrate deploy`) → `pm2 restart`. `npm run build` for the Remix admin build. **Automate this** — the missing `migrate deploy` step is exactly what caused the current breakage. +- **Extensions** (theme widget, Functions, POS, checkout) deploy separately via `shopify app deploy` — needs interactive Shopify CLI login (see below). + +### Shopify CLI +- `shopify app dev` / `deploy` / `config link` require **interactive login** — they won't run headless. `shopify app generate` and `function typegen` work fine non-interactively. +- Do **not** `shopify app dev` against this app config: `automatically_update_urls_on_dev = false` protects the live URL, but a dev session still can't serve the embedded admin through its tunnel — test extensions only, against the real domain. +- `shopify.app.toml` has a functionally-inert `[events]` placeholder subscription — required by the CLI schema for this org's Next-Gen Events preview enrolment. Leave it; it just logs and 200s. + +### Local dev +- **Node 22.23.2 via nvm-windows.** Global CLI tools (`shopify`, etc.) must be reinstalled per Node version after any `nvm use`. +- **Postgres, not SQLite** — the schema uses Prisma enums + scalar-list arrays. `docker compose up -d` starts local Postgres:5433 / Redis:6380 (see `.env.example`). The server's own DB is on 5432. +- `npm test` (Vitest unit) · `npm run test:integration` (needs Redis+DB) · `npm run test:functions` (needs Shopify CLI for the WASM build) · `npm run test:e2e` (Playwright) · `npm run build:widget` (esbuild, no deps). + +### Architecture invariants (from `CLAUDE.md` — enforce in review) +- **Enforcement is server-side.** The widget/POS/checkout extensions only *collect*; the `validation-slot` Function enforces. Anything that matters must go through it (now including the capacity snapshot). +- **One `resolveAvailabilityRequest` + one capacity pool** feeds web / checkout / POS. Never fork the logic per surface. +- **Pure scheduling core** in `app/services/*.server.ts` — inject data, no DB/Shopify I/O inside the math, so it stays unit-testable. `app/lib/time.ts` is safety-critical (DST); don't weaken its tests. +- **Every query scoped by `shopDomain`.** Multi-tenant. +- Polaris only in the admin UI (BfS). Controlled inputs only (B3). + +### Key files +| Area | File | +|---|---| +| Availability resolver (all surfaces) | `app/services/availability-request.server.ts` | +| Pure scheduling engine | `app/services/scheduling.server.ts` | +| Slot-holds (Redis, atomic) | `app/services/holds.server.ts`, `hold-request.server.ts` | +| Checkout capacity snapshot | `app/services/checkout-snapshot.server.ts` | +| Zones / geocoding / inventory exclusion | `app/services/zones.server.ts` | +| Product rules | `app/services/product-rules.server.ts` | +| Billing / tier gating | `app/services/billing.server.ts`, `app/lib/billing-plans.ts` | +| DST-safe date math | `app/lib/time.ts` | +| Storefront widget source | `widget-src/datetime-widget/datetime-widget.ts` → `extensions/datetime-widget/assets/datetime-widget.js` | +| Validation Function | `extensions/validation-slot/src/` | +| Delivery / payment customization Functions | `extensions/delivery-customization/src/`, `extensions/payment-customization/src/` | +| Checkout UI extension | `extensions/checkout-datetime/src/` | +| Order webhooks (booking + snapshot refresh) | `app/routes/webhooks.orders.*.tsx` |