Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
15 KiB
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.
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, safe-test-fixtures.md.
Synthesized from status-and-gaps.md, design-gaps.md, migration-readiness.md, cutover.md, ops-runtime.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 — P0 UI blockers for daily-ops parity:
Feed mapping — no live XML/CSV tree preview / schema extractDONE (extract-schema +FeedSourcePreview/MappingPreviewPanel)- Export create/edit — thin dialog, not full
ExportFeedBuilderroutes — WAIVED (DESIGN-P0-EXPORT-BUILDER-2026-08-08; dialog covers daily ops) - Products “Export Selected” — client CSV blob, not export-feed picker
- “Reset to Unprocessed” — stub toast
Standard Fields — “API not available” bannerDONE (/api/standard-fields+ field-groups CRUD mounted)Product Attributes tab — read-onlyDONE (MVP) (editable key/value → PATCHattributes; Add Category Attribute deferred P1)- Attributes — no per-attribute Manage Categories tree dialog
- Admin Settings / Admin home —
saveDemo(UI demo, not persisted) - Admin Logs — no API
- Admin always visible in customer sidebar (legacy keeps Admin separate)
- Categories Bulk Formula Builder — stub button
No floating TaskStatusIndicator in layoutDONE (TaskStatusIndicatorin+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 over 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) |
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) |
| 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 |
# Windows — apply schema + sqlc
$env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
.\scripts\migrate.ps1
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 |
| 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 |
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. Runbook: cutover.md. Evidence: 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
- Prefer real emails first (Clerk export → patch
users.email); then issue invites (live load used-skip-post-import). - Confirm artifact:
<maps-dir>/set-password-hooks.json(alsopassword_invites.json). - Configure SMTP (
SMTP_ENABLED=true, host/port/user/password/from,WEB_ORIGIN). See ops-runtime.md. - Send mail:
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
- User opens
/accept-invite?token=…→ sets password → login → session cookie. - Alternates:
POST /api/auth/complete-set-password, adminPOST /api/admin/emails/set-password, or migrator-set-passwordfor local bootstrap. - Gate: do not flip DNS until at least one platform admin and one normal user can log in on v2.
- Confirm
is_platform_adminand/adminfor migrated admins; promote tenant admins frommemberas needed (-promote-company-admins -dry-runthen-confirm).
Staging login rehearsal (exact commands)
Prefer the no-SMTP link-copy path first: staging-auth-rehearsal.md (scripts/staging-auth-rehearsal.ps1 / .sh).
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.localemails 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:
- Clerk (or other) email export → patch synthetic addresses; issue set-password invites; SMTP smoke for admin + member.
- Explicit acceptance or fix of migration gaps: roles (
member→ admin),plan_id=6company_plans, api_keys re-issue plan, company_settings, file strategy. - 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-08in ux-backlog.md (MVP Needs Review + local Alerts shipped).
- Done (2026-08-08): P0-8 / P0-9 remainder waived —
- Production secrets +
/readyzdeploy + freeze/rollback owners per cutover.md.
Until then, keep legacy Descrybe as the live system.
Suggested next actions (order)
- Clerk email export → patch
users.email→-issue-set-password-invites→ SMTP/mailhooks → login smoke - Promote tenant admins; resolve skipped
plan_id=6company_plans - Close or waive design P0s that match real tenant workflows — P0-8 / P0-9 waived (see ux-backlog.md)
- Re-issue API key runbook for
/api/v1customers - Execute cutover.md only after gates above are green
Related docs
- demo-user.md — staging demo login + company data counts
- migration-run-log.md — live dry-run + load evidence
- status-and-gaps.md — product/backend gap analysis
- design-gaps.md — UI/UX parity audit
- migration-readiness.md — ETL coverage & blockers
- cutover.md — freeze → migrate → DNS → Clerk decommission
- ops-runtime.md — SMTP, sessions, Woo encryption
- schema-map.md — ID remapping
- features.md — phase checkboxes (may lag status-and-gaps)