Files
descrybe/docs/go-live-checklist.md
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

15 KiB
Raw Permalink Blame History

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 → WooCommerceAdmin.

Still imperfect (do not claim pixel / UX cutover)

From design-gaps.mdP0 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 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 001007 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_usersis_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

  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.
  4. 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
  1. User opens /accept-invite?token=… → sets password → login → session cookie.
  2. Alternates: POST /api/auth/complete-set-password, admin POST /api/admin/emails/set-password, or migrator -set-password for local bootstrap.
  3. Gate: do not flip DNS until at least one platform admin and one normal user can log in on v2.
  4. 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 (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.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 (001007)
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 (MVP Needs Review + local Alerts shipped).
  4. Production secrets + /readyz deploy + freeze/rollback owners per 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)
  4. Re-issue API key runbook for /api/v1 customers
  5. Execute cutover.md only after gates above are green