Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
267 lines
15 KiB
Markdown
267 lines
15 KiB
Markdown
# Descrybe v2 — go-live checklist
|
||
|
||
**Date:** 2026-08-04
|
||
**Stack:** Go API + SvelteKit + PostgreSQL (`f:/laragon/www/_MY/descrybe-v2`)
|
||
**Legacy (still live):** Next.js + Clerk + MySQL (`f:/laragon/www/_MY/descrybe`)
|
||
|
||
**Verdict: NO-GO for production cutover.** Staging PG has live-migrated data; production DNS/switch stays blocked until emails, membership roles, and SMTP/set-password login are proven.
|
||
|
||
**Staging rehearsal:** **GO for login testing** after Clerk email patch (or synthetic-email awareness) + set-password invites / `-set-password` bootstrap. See [migration-run-log.md](migration-run-log.md).
|
||
|
||
**Demo account:** `demo@descrybe.test` / `DemoPass123!` — platform admin of **Platform Demo** only (not A1). A1 catalog stays on `a1-primary@descrybe.local`. Re-seed with `go run ./cmd/seed-demo`. Canonical: [demo-user.md](demo-user.md), [safe-test-fixtures.md](safe-test-fixtures.md).
|
||
|
||
Synthesized from [status-and-gaps.md](status-and-gaps.md), [design-gaps.md](design-gaps.md), [migration-readiness.md](migration-readiness.md), [cutover.md](cutover.md), [ops-runtime.md](ops-runtime.md), [schema-map.md](schema-map.md), `apps/api/internal/httpapi/server.go`, and `apps/api/cmd/migrator`.
|
||
|
||
---
|
||
|
||
## 1. Design parity status
|
||
|
||
### Cloned / aligned
|
||
|
||
| Area | Status |
|
||
|------|--------|
|
||
| Design tokens (seasalt / russian-violet / majorelle-blue) | Matched in `layout.css` from legacy globals |
|
||
| Shared UI kit (`$lib/components/ui`) + PageShell | Present |
|
||
| Core nav destinations | Dashboard, Products, Feeds (+ mapping), Export Feeds, Categories (+ title/description formula), Attributes, Standard Fields, Background Tasks, Billing, Settings, WooCommerce, Admin (+ subpages) |
|
||
| Auth shells | `/login`, `/register`, `/accept-invite` |
|
||
| Extra shells | `/plans`, `/structured-descriptions`, `/vector-categories` (routes exist; not all in nav) |
|
||
| Formula builders | Drag-reorder HTML5 DnD present; closer to legacy than status docs sometimes imply |
|
||
|
||
Nav order (v2): Dashboard → Products → Feeds → Export Feeds → Categories → Attributes → Standard Fields → Background Tasks → Usage & Billing → Settings → **WooCommerce** → **Admin**.
|
||
|
||
### Still imperfect (do not claim pixel / UX cutover)
|
||
|
||
From [design-gaps.md](design-gaps.md) — **P0 UI blockers** for daily-ops parity:
|
||
|
||
1. ~~Feed mapping — no live XML/CSV tree preview / schema extract~~ **DONE** (extract-schema + `FeedSourcePreview` / `MappingPreviewPanel`)
|
||
2. Export create/edit — thin dialog, not full `ExportFeedBuilder` routes — **WAIVED** (`DESIGN-P0-EXPORT-BUILDER-2026-08-08`; dialog covers daily ops)
|
||
3. Products “Export Selected” — client CSV blob, not export-feed picker
|
||
4. “Reset to Unprocessed” — stub toast
|
||
5. ~~Standard Fields — “API not available” banner~~ **DONE** (`/api/standard-fields` + field-groups CRUD mounted)
|
||
6. ~~Product Attributes tab — read-only~~ **DONE (MVP)** (editable key/value → PATCH `attributes`; Add Category Attribute deferred P1)
|
||
7. Attributes — no per-attribute Manage Categories tree dialog
|
||
8. Admin Settings / Admin home — `saveDemo` (UI demo, not persisted)
|
||
9. Admin Logs — no API
|
||
10. Admin always visible in customer sidebar (legacy keeps Admin separate)
|
||
11. Categories Bulk Formula Builder — stub button
|
||
12. ~~No floating TaskStatusIndicator in layout~~ **DONE** (`TaskStatusIndicator` in `+layout.svelte`)
|
||
|
||
**P1 shells with API banners:** plans, structured-descriptions, vector-categories (UI cloned; backends missing).
|
||
|
||
**IA differences:** WooCommerce is top-level in v2 (legacy: export-feed dialogs); Admin in customer Nav; path renames (`/register`, `/accept-invite`, `/processing`).
|
||
|
||
**Honest summary:** Visual shell and most route shells are cloned. Mapping preview, standard-fields API, product attribute edit (MVP), and floating task indicator are closed or waived. Remaining primary blockers for some tenants: export-selected→feed picker, reset-to-unprocessed, admin demos/logs, bulk formula, Manage Categories.
|
||
|
||
---
|
||
|
||
## 2. Backend / feature stubs blocking real use
|
||
|
||
Prefer [status-and-gaps.md](status-and-gaps.md) over [features.md](features.md) — the phase checklist is **stale** in places (still lists feed sync / export / AI / Woo / admin as stubs while code has moved on).
|
||
|
||
### Hard blockers for many production tenants
|
||
|
||
| Gap | Detail |
|
||
|-----|--------|
|
||
| Real emails | Most users have synthetic `…@legacy.local` — Clerk emails not in MySQL; patch before invites |
|
||
| Set-password delivery | Hooks tooling exists; SMTP + mailhooks **unproven** on staging |
|
||
| Membership roles | All imported as `role=member` (`profiles.role` absent) — promote via migrator `-list-member-memberships` / `-promote-company-admins` (`-dry-run` then `-confirm`; see [cutover.md](cutover.md)) |
|
||
| `company_plans` | 2 rows skipped (`plan_id=6` missing in `plans`) |
|
||
| API keys migration | `api_keys` / `descrybe_api_key` **not** migrated — clients must mint new keys |
|
||
| Company settings | Language / merge-by-GTIN **not** fully migrated (`company_settings`) |
|
||
| File blobs | Metadata only; no blob copy; `raw_products.file_id` unset |
|
||
|
||
### Product / API leftovers (in-use phases)
|
||
|
||
| Area | Works today | Still blocking / incomplete |
|
||
|------|-------------|-----------------------------|
|
||
| Feed sync | URL + CSV/XML → `raw_products` | FTP/FTPS, Excel, uploaded-file paths rejected |
|
||
| Export | Streaming generate + public XML/CSV | Schedule/cron; object-storage persist |
|
||
| Processing | OpenAI + heuristic + optional Pinecone; DB claim queue | River optional; prompt/index tuning |
|
||
| `/api/v1` | Mounted (Bearer / `X-API-Key`); OpenAPI YAML | Live API-key DB round-trip harness not run |
|
||
| WooCommerce | REST test + queued sync + 15m enqueue | Rich maps UI; live store E2E; set `CREDENTIALS_ENCRYPTION_KEY` |
|
||
| Catalog | CRUD, CSV, formulas, ListFilter | S3 (`UPLOAD_DIR` only); trees >2000; merge-by-GTIN beyond CSV |
|
||
| Structured descriptions / vector categories | — | **No backend routes** (UI 404 banners) |
|
||
| Standard fields | `/api/standard-fields`, `/api/field-groups` | **Mounted** (CRUD + bulk enable) |
|
||
| Job queue | DB claim + NOTIFY | River client deferred (`jobs/river.go`) |
|
||
| Org export/import, WebSockets | — | Missing vs legacy |
|
||
|
||
### Routes mounted (session API — excerpt)
|
||
|
||
From `server.go`: auth (register/login/logout/accept-invite/complete-set-password/me/set-password), admin (users/companies/jobs/plans/credits/emails), company/team/api-keys/billing, categories/attributes/variables/products, feeds (+ sync/mappings), export-feeds (+ generate), processing jobs, WooCommerce, public export XML/CSV, `/healthz` + `/readyz`, `/api/v1/*`.
|
||
|
||
**Not mounted:** `/api/structured-descriptions`, `/api/vector-categories/*`.
|
||
**Mounted:** `/api/standard-fields`, `/api/field-groups` (CRUD + bulk enable).
|
||
|
||
---
|
||
|
||
## 3. DB migration status & how to run
|
||
|
||
### Goose schema (Postgres)
|
||
|
||
| Item | Status |
|
||
|------|--------|
|
||
| Migrations `001`–`007` | Applied on staging PG (see [migration-run-log.md](migration-run-log.md)) |
|
||
| Apply tooling | `make migrate` / `.\scripts\migrate.ps1` / `scripts/migrate.sh` (pin goose if Go < 1.25.7) |
|
||
| Default local DB | Postgres 16 on host **5433** (`docker compose up -d`) |
|
||
| Default DSN | `postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable` |
|
||
|
||
```powershell
|
||
# Windows — apply schema + sqlc
|
||
$env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
|
||
.\scripts\migrate.ps1
|
||
```
|
||
|
||
```bash
|
||
export DATABASE_URL="postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
|
||
./scripts/migrate.sh
|
||
# or: make migrate
|
||
```
|
||
|
||
### MySQL → Postgres ETL (`cmd/migrator`)
|
||
|
||
| Item | Status |
|
||
|------|--------|
|
||
| Tooling / fixture dry-run | Ready |
|
||
| Live MySQL dry-run | **Done** (2026-08-03) — exit 0 |
|
||
| Live load + validation | **Done** on staging PG — orphans **16/16 pass**; see [migration-run-log.md](migration-run-log.md) |
|
||
| Staging data (written) | 27 companies, 21 users, 15 memberships, 8278 categories, **57630** attributes (unique collapse), **118784** raw products (+7732 GTIN-deduped), 7219 processed, 31 feeds, 8 export feeds |
|
||
| Coverage gaps | api_keys, full company_settings, billing_cycles, feed tags, Woo configs (unless domain `woo`), jobs/history (unless domain `jobs`; cutover default empty), file blob bytes |
|
||
| Auth design | UUID + `legacy_*` + `must_set_password=true` + `admin_users` → `is_platform_admin`; **no password hashes imported** |
|
||
| Post-import | This live run used `-skip-post-import` — re-issue invites when emails are real |
|
||
|
||
```bash
|
||
cd apps/api
|
||
|
||
# Offline smoke ONLY (not cutover proof)
|
||
go run ./cmd/migrator -dry-run -fixture ./cmd/migrator/testdata/fixture.json -maps-dir ../../artifacts
|
||
|
||
# Live dry-run / load (already completed on staging — re-run only if reloading)
|
||
go run ./cmd/migrator \
|
||
-mysql "$MIGRATE_MYSQL_DSN" \
|
||
-postgres "$DATABASE_URL" \
|
||
-dry-run \
|
||
-maps-dir ../../artifacts \
|
||
-id-map ../../artifacts/id-map.json
|
||
|
||
go run ./cmd/migrator \
|
||
-mysql "$MIGRATE_MYSQL_DSN" \
|
||
-postgres "$DATABASE_URL" \
|
||
-maps-dir ../../artifacts \
|
||
-id-map ../../artifacts/id-map.json
|
||
```
|
||
|
||
Do **not** invent DSNs. Do **not** commit `artifacts/` (id-map, validation report, set-password tokens).
|
||
|
||
Full matrix: [migration-readiness.md](migration-readiness.md). Runbook: [cutover.md](cutover.md). Evidence: [migration-run-log.md](migration-run-log.md).
|
||
|
||
---
|
||
|
||
## 4. Post-migrate auth (set password)
|
||
|
||
Migrated users get `password_hash = NULL` and `must_set_password = true`. Clerk sessions do not carry over.
|
||
|
||
### Operator sequence
|
||
|
||
1. Prefer real emails first (Clerk export → patch `users.email`); then issue invites (live load used `-skip-post-import`).
|
||
2. Confirm artifact: `<maps-dir>/set-password-hooks.json` (also `password_invites.json`).
|
||
3. Configure SMTP (`SMTP_ENABLED=true`, host/port/user/password/from, `WEB_ORIGIN`). See [ops-runtime.md](ops-runtime.md).
|
||
4. Send mail:
|
||
|
||
```bash
|
||
cd apps/api
|
||
# Under EMAIL_DRY_RUN (default true): omit -dry-run → exit 1; with -dry-run → subject-only smoke (no SMTP)
|
||
go run ./cmd/mailhooks -hooks ../../artifacts/set-password-hooks.json -dry-run # smoke
|
||
# Real send only after EMAIL_DRY_RUN=false + SMTP_ENABLED=true — see ops-runtime.md
|
||
go run ./cmd/mailhooks -hooks ../../artifacts/set-password-hooks.json # real send
|
||
```
|
||
|
||
5. User opens `/accept-invite?token=…` → sets password → login → session cookie.
|
||
6. Alternates: `POST /api/auth/complete-set-password`, admin `POST /api/admin/emails/set-password`, or migrator `-set-password` for local bootstrap.
|
||
7. **Gate:** do not flip DNS until at least one platform admin and one normal user can log in on v2.
|
||
8. Confirm `is_platform_admin` and `/admin` for migrated admins; promote tenant admins from `member` as needed (`-promote-company-admins -dry-run` then `-confirm`).
|
||
|
||
### Staging login rehearsal (exact commands)
|
||
|
||
Prefer the no-SMTP link-copy path first: [staging-auth-rehearsal.md](staging-auth-rehearsal.md) (`scripts/staging-auth-rehearsal.ps1` / `.sh`).
|
||
|
||
```bash
|
||
cd apps/api
|
||
export DATABASE_URL="postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
|
||
|
||
# Re-issue invites for must_set_password users (Postgres only)
|
||
go run ./cmd/migrator -issue-set-password-invites \
|
||
-postgres "$DATABASE_URL" \
|
||
-maps-dir ../../artifacts
|
||
|
||
# Dev-only: set one user's password directly (not for production cutover)
|
||
go run ./cmd/migrator -set-password "email@example.com:YourPass123" \
|
||
-postgres "$DATABASE_URL"
|
||
```
|
||
|
||
### Risks
|
||
|
||
- SMTP off → users locked out after cutover
|
||
- Synthetic `@legacy.local` emails until Clerk export
|
||
- Tenant admins may land as `member`
|
||
- API customers need **new** API keys after cutover
|
||
|
||
---
|
||
|
||
## 5. Go / no-go for cutover TODAY
|
||
|
||
### Checklist
|
||
|
||
| Gate | Status |
|
||
|------|--------|
|
||
| Goose schema applyable locally | **Done** on staging (001–007) |
|
||
| Staging stack runnable (API + worker + web) | Ready for **staging** use |
|
||
| UI visual shell cloned | Mostly yes |
|
||
| UI daily-ops parity (mapping, export builder, product attrs, …) | **Partial** — mapping/standard-fields/attr-edit/task indicator closed or waived; export-selected/reset/admin stubs remain |
|
||
| Feature parity for in-use phases | Partial — FTP/Excel, S3, scheduled export, several APIs missing |
|
||
| Live MySQL migrator dry-run | **Yes** (2026-08-03) |
|
||
| Staging live load + zero orphan FKs | **Yes** — 16/16 orphan checks pass |
|
||
| `migration-run-log.md` / artifacts present | **Yes** (log committed; artifacts gitignored) |
|
||
| Real emails (Clerk) | **No** — synthetic `@legacy.local` |
|
||
| Membership roles verified | **No** — all `member` |
|
||
| SMTP + set-password smoke | **No** (tooling only; post-import skipped this run) |
|
||
| `api_keys` / company_settings migration | **No** |
|
||
| Production secrets (`CREDENTIALS_ENCRYPTION_KEY`, SMTP, `TOKEN_SIGNING_SECRET`) | Operator-owned; not verified here |
|
||
| Cutover runbook executed | **No** |
|
||
| CI / GitHub Actions | Partial unit tests only |
|
||
|
||
### Recommendation
|
||
|
||
**NO-GO — do not cut over production traffic.**
|
||
|
||
Staging PG data load **succeeded**. Use staging for **login rehearsal** after set-password (and preferably after Clerk email patch). Production DNS switch stays blocked until at least:
|
||
|
||
1. Clerk (or other) email export → patch synthetic addresses; issue set-password invites; SMTP smoke for admin + member.
|
||
2. Explicit acceptance or fix of migration gaps: roles (`member` → admin), `plan_id=6` company_plans, api_keys re-issue plan, company_settings, file strategy.
|
||
3. P0 daily-ops UI/API gaps closed **or** signed waiver for tenants that do not need those paths.
|
||
- **Done (2026-08-08):** **P0-8** / **P0-9** remainder waived — `UX-P0-8-PROPOSAL-API-2026-08-08`, `UX-P0-9-SERVER-EMAIL-2026-08-08` in [ux-backlog.md](ux-backlog.md) (MVP Needs Review + local Alerts shipped).
|
||
4. Production secrets + `/readyz` deploy + freeze/rollback owners per [cutover.md](cutover.md).
|
||
|
||
Until then, keep legacy Descrybe as the live system.
|
||
|
||
### Suggested next actions (order)
|
||
|
||
1. Clerk email export → patch `users.email` → `-issue-set-password-invites` → SMTP/mailhooks → login smoke
|
||
2. Promote tenant admins; resolve skipped `plan_id=6` company_plans
|
||
3. Close or waive design P0s that match real tenant workflows — **P0-8 / P0-9 waived** (see [ux-backlog.md](ux-backlog.md))
|
||
4. Re-issue API key runbook for `/api/v1` customers
|
||
5. Execute [cutover.md](cutover.md) only after gates above are green
|
||
|
||
---
|
||
|
||
## Related docs
|
||
|
||
- [demo-user.md](demo-user.md) — staging demo login + company data counts
|
||
- [migration-run-log.md](migration-run-log.md) — live dry-run + load evidence
|
||
- [status-and-gaps.md](status-and-gaps.md) — product/backend gap analysis
|
||
- [design-gaps.md](design-gaps.md) — UI/UX parity audit
|
||
- [migration-readiness.md](migration-readiness.md) — ETL coverage & blockers
|
||
- [cutover.md](cutover.md) — freeze → migrate → DNS → Clerk decommission
|
||
- [ops-runtime.md](ops-runtime.md) — SMTP, sessions, Woo encryption
|
||
- [schema-map.md](schema-map.md) — ID remapping
|
||
- [features.md](features.md) — phase checkboxes (**may lag** status-and-gaps)
|