docs: add team handover — state, deploy steps, open bugs, next-step plan
Some checks failed
CI / Lint, Unit & Integration Tests (push) Has been cancelled

Consolidates the current situation for the team taking over: what shipped
2026-09-04, the blocking migration + app-proxy items, the known bug list
(B1-B9), a prioritised next-step plan, and the infra/gotcha notes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
metatroncubeswdev 2026-09-07 01:21:38 -04:00
parent f8dd5b2586
commit 8650b1cb0c

157
HANDOVER.md Normal file
View File

@ -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 08 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&#39;t…``Couldn't…`). |
| `d98b29e` | `UNLOCK_ALL_FEATURES` env flag — `getShopTier()` returns `"pro"` so all plan-gated screens open for testing. |
| `f8dd5b2` | Fixed inert Polaris `<Select>` dropdowns (Weekly slots, Blackout dates, Rates) — they need `value`+`onChange` in this Polaris/App-Bridge version. |
**New DB migration:** `prisma/migrations/20260904120000_review_gaps/` — adds `Location.dailyOrderCap`, `ProductRule.blockedStartMins`, `Shop.enforcementMode`, `Shop.enforcementTag`. **Not yet applied on the test server — this is the #1 action item below.**
---
## 3. Immediate action items (do these first)
### 3.1 Apply the migration on the test server — BLOCKING
The regenerated Prisma client now `SELECT`s the new columns. Every loader that does `db.location.findMany()` / `findFirst()` without an explicit `select` (Weekly slots, Blackout dates, Checkout enforcement pages) **and the storefront `/apps/scheduling/availability` endpoint** will throw `column ... does not exist` until the migration runs. This is almost certainly why the cart widget currently shows *"Couldn't load available dates"* and why the admin slot/blackout pages misbehave.
```bash
cd <app dir on server>
git pull
npx prisma migrate deploy # applies 20260904120000_review_gaps
npx prisma generate
pm2 restart <process> # or: pm2 restart ecosystem.config.cjs
pm2 logs # confirm no "column X does not exist"
```
Fold `npx prisma migrate deploy` into the deploy pipeline (there isn't one yet — see §6).
### 3.2 Redeploy the admin app + widget bundle
`f8dd5b2` and `3749b4d` change Remix routes and the widget asset (`extensions/datetime-widget/assets/datetime-widget.js`, already rebuilt & committed). Restart the Remix server for the route fixes; run `shopify app deploy` to push the theme app extension (widget) to the store.
### 3.3 Verify the storefront widget path end-to-end
On the cart page console:
```js
fetch('/apps/scheduling/availability?method=SHIPPING&days=14').then(r=>r.text()).then(console.log)
```
- **nginx 403** → the app-proxy backend is blocking `/apps/scheduling/*`. Check `pm2 status` and the nginx site config forwards it. (A raw `403 Forbidden / nginx` was seen during testing — unresolved, needs a look at the server's nginx rules.)
- **Shopify 404** → App Proxy not registered. Partner Dashboard → App setup → App proxy: prefix `apps`, subpath `scheduling`, URL `https://metatron-delivery.thedomainnest.com/apps/scheduling`, then `shopify app deploy`.
- **`{"error":"No active location configured"}`** → add a Location + weekly slot templates in the admin.
- **`{... "dates":{}}`** → reachable but no matching slots (weekday/method/cutoff/lead filtering everything out).
### 3.4 Test-mode toggles (remove before any real launch)
- `.env` on the server: `UNLOCK_ALL_FEATURES=true` → all plan gates open. Restart PM2.
- `automatically_update_urls_on_dev = false` in `shopify.app.toml` is deliberate — do **not** run `shopify app dev` against this app config or it overwrites the live URL with a tunnel. Test extensions against the real domain.
---
## 4. Known bugs / open issues
| # | Issue | Where | Notes / next step |
|---|---|---|---|
| B1 | Storefront widget: *"Couldn't load available dates"* | cart page | Most likely §3.1 (migration) + §3.3 (app-proxy 403). Re-test after both. |
| B2 | App-proxy backend returned **403 Forbidden / nginx** during testing | `metatron-delivery.thedomainnest.com/apps/scheduling/*` | Inspect the server's nginx config — is `/apps/scheduling/` proxied to the Remix port, any `deny` / auth / IP allowlist? |
| B3 | Polaris `<Select>` 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 `<s-*>` 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` |