create-ailk 0.24.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -20
- package/component-catalog.json +3 -3
- package/component-catalog.md +3 -3
- package/dist/add-module.js +4 -2
- package/dist/add-route.d.ts +12 -1
- package/dist/add-route.js +31 -3
- package/dist/cli.js +12 -7
- package/dist/derive-architecture.d.ts +3 -4
- package/dist/derive-architecture.js +65 -29
- package/dist/lib/design-checks.d.ts +9 -4
- package/dist/lib/design-checks.js +13 -7
- package/dist/lib/extract-module-bundle.js +29 -2
- package/dist/lib/fetch-template-snapshot.d.ts +1 -1
- package/dist/lib/fetch-template-snapshot.js +7 -3
- package/dist/lib/import-graph.d.ts +1 -1
- package/dist/lib/import-graph.js +4 -4
- package/dist/lib/module-license-gate.d.ts +12 -1
- package/dist/lib/module-license-gate.js +34 -12
- package/dist/module-architecture.d.ts +20 -3
- package/dist/module-architecture.js +117 -550
- package/dist/paid-sections.d.ts +25 -1
- package/dist/paid-sections.js +88 -3
- package/dist/surfaces.js +10 -2
- package/dist/template-record.d.ts +14 -4
- package/dist/template-record.js +23 -5
- package/dist/topology.d.ts +16 -3
- package/dist/topology.js +31 -19
- package/dist/update.d.ts +10 -1
- package/dist/update.js +540 -16
- package/package.json +3 -3
- package/release-scope.json +1 -1
- package/templates/.claude/cloudparity-repo-hooks.json +5 -0
- package/templates/.claude/hooks/template-guard.sh +137 -0
- package/templates/.claude/rules/extending.md +106 -0
- package/templates/.claude/rules/loop-contract.md +2 -2
- package/templates/.claude/settings.json +11 -0
- package/templates/.env.example +5 -5
- package/templates/.github/workflows/ci.yml +7 -4
- package/templates/CLAUDE.md +11 -1
- package/templates/CONVENTIONS.md +8 -10
- package/templates/README.md +14 -13
- package/templates/apps/api/.env.example +2 -2
- package/templates/apps/api/CLAUDE.md +102 -76
- package/templates/apps/api/jest.config.cjs +15 -5
- package/templates/apps/api/package.json +16 -20
- package/templates/apps/api/src/__tests__/trusted-origins.server.test.ts +2 -2
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +4 -4
- package/templates/apps/api/src/index.ts +1 -1
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -3
- package/templates/apps/api/src/modules/commerce.ts +10 -0
- package/templates/apps/api/src/modules/content-write.ts +10 -0
- package/templates/apps/api/src/modules/multi-tenant.ts +10 -0
- package/templates/apps/api/src/modules/scheduling.ts +10 -0
- package/templates/apps/api/src/modules/signal-collectors.ts +10 -0
- package/templates/apps/api/src/modules/workspace.ts +10 -0
- package/templates/apps/api/src/server.ts +117 -1882
- package/templates/apps/api/vercel.json +2 -2
- package/templates/apps/mcp/CLAUDE.md +67 -20
- package/templates/apps/mcp/__tests__/app-entry.test.ts +4 -254
- package/templates/apps/mcp/__tests__/boot-file.test.ts +92 -0
- package/templates/apps/mcp/jest.config.cjs +6 -1
- package/templates/apps/mcp/package.json +5 -13
- package/templates/apps/mcp/src/app.ts +8 -7
- package/templates/apps/mcp/src/index.ts +6 -34
- package/templates/apps/mcp/src/lib/paid-tools.generated.ts +10 -0
- package/templates/apps/mcp/src/lib/topology.generated.ts +2 -2
- package/templates/apps/mcp/src/site.ts +12 -0
- package/templates/apps/web/.env.example +1 -1
- package/templates/apps/web/CLAUDE.md +10 -3
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
- package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +22 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +7 -3
- package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +43 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +7 -3
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +222 -0
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +10 -3
- package/templates/apps/web/app/api/revalidate/route.ts +3 -3
- package/templates/apps/web/auth.ts +2 -2
- package/templates/apps/web/jest.config.cjs +1 -0
- package/templates/apps/web/lib/auth-email.ts +1 -1
- package/templates/apps/web/lib/relay.ts +2 -2
- package/templates/apps/web/lib/release-scope.ts +47 -0
- package/templates/apps/web/lib/server-api.ts +1 -1
- package/templates/apps/web/src/lib/commerce-claim.ts +1 -1
- package/templates/customizations.yaml +37 -0
- package/templates/database/prisma.config.ts +10 -14
- package/templates/database/seed-test.ts +2 -2
- package/templates/database/seed.ts +5 -5
- package/templates/package.json +12 -6
- package/templates/pnpm-workspace.yaml +0 -1
- package/templates/project.yaml +16 -8
- package/templates/tools/jest/reporters.cjs +65 -0
- package/templates/tools/lint-architecture.ts +553 -7
- package/templates/tsconfig.json +1 -2
- package/versions.json +48 -35
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +0 -409
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +0 -102
- package/templates/apps/api/src/__tests__/factories/content-fixture.ts +0 -138
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +0 -441
- package/templates/apps/api/src/__tests__/factories/index.ts +0 -70
- package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +0 -82
- package/templates/apps/api/src/__tests__/leads-forwarded-rate-limit.test.ts +0 -135
- package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +0 -126
- package/templates/apps/api/src/__tests__/stripe-boot.test.ts +0 -132
- package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +0 -362
- package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +0 -184
- package/templates/apps/api/src/bin/abandon-sweep.ts +0 -42
- package/templates/apps/api/src/bin/rate-limit-probe.ts +0 -53
- package/templates/apps/api/src/bin/spokes.ts +0 -198
- package/templates/apps/api/src/bin/stripe-connections-rekey.ts +0 -45
- package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +0 -196
- package/templates/apps/api/src/config/__tests__/index.test.ts +0 -233
- package/templates/apps/api/src/config/__tests__/modules.test.ts +0 -76
- package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +0 -185
- package/templates/apps/api/src/config/__tests__/trusted-origins.test.ts +0 -238
- package/templates/apps/api/src/config/index.ts +0 -596
- package/templates/apps/api/src/config/modules.ts +0 -83
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +0 -581
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +0 -143
- package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +0 -237
- package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +0 -353
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +0 -201
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +0 -224
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +0 -120
- package/templates/apps/api/src/lib/__tests__/errors.test.ts +0 -94
- package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +0 -144
- package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +0 -8
- package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +0 -149
- package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +0 -116
- package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +0 -209
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +0 -257
- package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +0 -96
- package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +0 -125
- package/templates/apps/api/src/lib/__tests__/prisma.test.ts +0 -165
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +0 -317
- package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +0 -129
- package/templates/apps/api/src/lib/__tests__/site.test.ts +0 -308
- package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +0 -89
- package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +0 -202
- package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +0 -217
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -82
- package/templates/apps/api/src/lib/agent-store.ts +0 -613
- package/templates/apps/api/src/lib/auth-email-budget.ts +0 -161
- package/templates/apps/api/src/lib/consent-store.ts +0 -363
- package/templates/apps/api/src/lib/content-adapter.ts +0 -112
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +0 -96
- package/templates/apps/api/src/lib/error-serializer.ts +0 -164
- package/templates/apps/api/src/lib/errors.ts +0 -95
- package/templates/apps/api/src/lib/file-fallback.ts +0 -87
- package/templates/apps/api/src/lib/flow-completion-store.ts +0 -136
- package/templates/apps/api/src/lib/hub-sign-in-store.ts +0 -455
- package/templates/apps/api/src/lib/lead-capture-store.ts +0 -122
- package/templates/apps/api/src/lib/lead-delivery.ts +0 -52
- package/templates/apps/api/src/lib/license-key-store.ts +0 -45
- package/templates/apps/api/src/lib/locales.ts +0 -201
- package/templates/apps/api/src/lib/outbound-http.ts +0 -127
- package/templates/apps/api/src/lib/prisma.ts +0 -21
- package/templates/apps/api/src/lib/proposal-paid-store.ts +0 -66
- package/templates/apps/api/src/lib/rate-limit-probe.ts +0 -165
- package/templates/apps/api/src/lib/rate-limit-store.ts +0 -142
- package/templates/apps/api/src/lib/require-commerce-claim.ts +0 -250
- package/templates/apps/api/src/lib/require-paid-tier.ts +0 -53
- package/templates/apps/api/src/lib/site.ts +0 -257
- package/templates/apps/api/src/lib/slice-load.ts +0 -90
- package/templates/apps/api/src/lib/spoke-store.ts +0 -180
- package/templates/apps/api/src/lib/stripe.ts +0 -321
- package/templates/apps/api/src/lib/webhook-idempotency.ts +0 -156
- package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +0 -111
- package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +0 -172
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +0 -119
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +0 -138
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-trusted-origins.test.ts +0 -142
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +0 -155
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +0 -139
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +0 -562
- package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +0 -76
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +0 -220
- package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +0 -97
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +0 -557
- package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +0 -72
- package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +0 -140
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +0 -721
- package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +0 -220
- package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +0 -430
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +0 -1409
- package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +0 -174
- package/templates/apps/api/src/middleware/auth.ts +0 -864
- package/templates/apps/api/src/middleware/default-tenant.ts +0 -166
- package/templates/apps/api/src/middleware/rate-limit.ts +0 -983
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +0 -391
- package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +0 -182
- package/templates/apps/api/src/openapi/index.ts +0 -18
- package/templates/apps/api/src/openapi/spec.ts +0 -2148
- package/templates/apps/api/src/openapi/surface-drift.ts +0 -195
- package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +0 -175
- package/templates/apps/api/src/routes/aeo/index.ts +0 -107
- package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +0 -196
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +0 -249
- package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +0 -532
- package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +0 -313
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +0 -413
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +0 -609
- package/templates/apps/api/src/routes/auth/hub-sign-in.ts +0 -242
- package/templates/apps/api/src/routes/auth/tokens.ts +0 -211
- package/templates/apps/api/src/routes/auth/transactional-email.ts +0 -301
- package/templates/apps/api/src/routes/auth/user.ts +0 -305
- package/templates/apps/api/src/routes/billing/plan-checkout.ts +0 -332
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +0 -178
- package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +0 -1426
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +0 -448
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -1742
- package/templates/apps/api/src/routes/checkout/index.ts +0 -34
- package/templates/apps/api/src/routes/checkout/session.ts +0 -171
- package/templates/apps/api/src/routes/checkout/sessions.ts +0 -540
- package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +0 -386
- package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +0 -160
- package/templates/apps/api/src/routes/consent/account.ts +0 -268
- package/templates/apps/api/src/routes/consent/cookie.ts +0 -90
- package/templates/apps/api/src/routes/consent/index.ts +0 -26
- package/templates/apps/api/src/routes/consent/unsubscribe.ts +0 -127
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +0 -853
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +0 -230
- package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +0 -44
- package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +0 -118
- package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +0 -731
- package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +0 -753
- package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +0 -51
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +0 -450
- package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +0 -762
- package/templates/apps/api/src/routes/content/get-page.ts +0 -183
- package/templates/apps/api/src/routes/content/index.ts +0 -49
- package/templates/apps/api/src/routes/content/list-pages.ts +0 -74
- package/templates/apps/api/src/routes/content/list-types.ts +0 -19
- package/templates/apps/api/src/routes/content/offerings.ts +0 -184
- package/templates/apps/api/src/routes/content/pricing.ts +0 -176
- package/templates/apps/api/src/routes/content/rate-limit.ts +0 -22
- package/templates/apps/api/src/routes/content/search.ts +0 -100
- package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
- package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +0 -88
- package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +0 -171
- package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +0 -515
- package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +0 -392
- package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +0 -165
- package/templates/apps/api/src/routes/leads/index.ts +0 -639
- package/templates/apps/api/src/routes/leads/list.ts +0 -104
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +0 -626
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +0 -1268
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +0 -847
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +0 -491
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -476
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +0 -872
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +0 -1022
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +0 -484
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +0 -535
- package/templates/apps/api/src/routes/project-listings/index.ts +0 -164
- package/templates/apps/api/src/routes/project-listings/me.ts +0 -87
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +0 -224
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +0 -102
- package/templates/apps/api/src/routes/project-listings/public.ts +0 -98
- package/templates/apps/api/src/routes/project-listings/respond.ts +0 -48
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +0 -27
- package/templates/apps/api/src/routes/project-listings/start.ts +0 -140
- package/templates/apps/api/src/routes/project-listings/submit.ts +0 -130
- package/templates/apps/api/src/routes/project-listings/verify-email.ts +0 -150
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +0 -686
- package/templates/apps/api/src/routes/requests/confirm.ts +0 -601
- package/templates/apps/api/src/routes/requests/index.ts +0 -54
- package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +0 -306
- package/templates/apps/api/src/routes/test/auth/session.ts +0 -143
- package/templates/apps/api/src/routes/webhooks/README.md +0 -398
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +0 -649
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +0 -749
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -449
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -264
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +0 -159
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +0 -158
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +0 -814
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -301
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-proposal-paid.test.ts +0 -369
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -215
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +0 -301
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +0 -809
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -1155
- package/templates/apps/api/src/routes/webhooks/index.ts +0 -141
- package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +0 -384
- package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -2028
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -645
- package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +0 -132
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +0 -1028
- package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +0 -313
- package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +0 -503
- package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +0 -65
- package/templates/apps/api/src/services/__tests__/consent.test.ts +0 -299
- package/templates/apps/api/src/services/__tests__/default-tenant.test.ts +0 -147
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +0 -877
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +0 -379
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +0 -368
- package/templates/apps/api/src/services/__tests__/entitlements.test.ts +0 -142
- package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +0 -854
- package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +0 -234
- package/templates/apps/api/src/services/__tests__/lead-accept-user.test.ts +0 -251
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +0 -2660
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +0 -166
- package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +0 -389
- package/templates/apps/api/src/services/__tests__/person.test.ts +0 -1204
- package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -450
- package/templates/apps/api/src/services/__tests__/project-listings.counts.test.ts +0 -202
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -151
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +0 -137
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +0 -531
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +0 -372
- package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +0 -348
- package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +0 -660
- package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +0 -941
- package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +0 -588
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +0 -78
- package/templates/apps/api/src/services/abandon-email.ts +0 -336
- package/templates/apps/api/src/services/aeo-score.ts +0 -325
- package/templates/apps/api/src/services/answer-distributions.ts +0 -138
- package/templates/apps/api/src/services/audit-log.ts +0 -961
- package/templates/apps/api/src/services/checkout-buyer.ts +0 -168
- package/templates/apps/api/src/services/consent.ts +0 -834
- package/templates/apps/api/src/services/default-tenant.ts +0 -416
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +0 -750
- package/templates/apps/api/src/services/dunning-policy.ts +0 -294
- package/templates/apps/api/src/services/enrichment-shim.ts +0 -53
- package/templates/apps/api/src/services/entitlements.ts +0 -338
- package/templates/apps/api/src/services/hub-sign-in.ts +0 -472
- package/templates/apps/api/src/services/lead-accept-user.ts +0 -126
- package/templates/apps/api/src/services/lead-promotion.ts +0 -160
- package/templates/apps/api/src/services/lead-routing.ts +0 -1372
- package/templates/apps/api/src/services/leads-inbox.ts +0 -312
- package/templates/apps/api/src/services/listing-config.ts +0 -178
- package/templates/apps/api/src/services/magnet-completion.ts +0 -242
- package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +0 -348
- package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +0 -268
- package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +0 -975
- package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +0 -416
- package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +0 -400
- package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -685
- package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
- package/templates/apps/api/src/services/offerings/catalog.ts +0 -318
- package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -403
- package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
- package/templates/apps/api/src/services/offerings/connection.ts +0 -1083
- package/templates/apps/api/src/services/offerings/index.ts +0 -614
- package/templates/apps/api/src/services/offerings/source.ts +0 -140
- package/templates/apps/api/src/services/offerings/store.ts +0 -150
- package/templates/apps/api/src/services/owner-workspaces.ts +0 -132
- package/templates/apps/api/src/services/person.ts +0 -1211
- package/templates/apps/api/src/services/plan-offerings.ts +0 -125
- package/templates/apps/api/src/services/project-listing-resume-email.ts +0 -326
- package/templates/apps/api/src/services/project-listings.ts +0 -2085
- package/templates/apps/api/src/services/public-site-resolver.ts +0 -396
- package/templates/apps/api/src/services/request-confirm.ts +0 -698
- package/templates/apps/api/src/services/revalidation.ts +0 -235
- package/templates/apps/api/src/services/slack-notify.ts +0 -291
- package/templates/apps/api/src/services/usage-aggregation.ts +0 -516
- package/templates/apps/api/src/services/usage-credits.ts +0 -340
- package/templates/apps/api/src/services/verified-email.ts +0 -71
- package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +0 -52
- package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +0 -46
- package/templates/apps/mcp/__tests__/abuse/guard.test.ts +0 -198
- package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +0 -36
- package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +0 -54
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +0 -116
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +0 -299
- package/templates/apps/mcp/__tests__/boot.test.ts +0 -280
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +0 -359
- package/templates/apps/mcp/__tests__/config/modules.test.ts +0 -78
- package/templates/apps/mcp/__tests__/get_business_hours.test.ts +0 -82
- package/templates/apps/mcp/__tests__/get_faq.test.ts +0 -80
- package/templates/apps/mcp/__tests__/get_locations.test.ts +0 -65
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +0 -557
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +0 -524
- package/templates/apps/mcp/__tests__/get_site_info.test.ts +0 -81
- package/templates/apps/mcp/__tests__/http-transport.test.ts +0 -1232
- package/templates/apps/mcp/__tests__/index.test.ts +0 -195
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +0 -191
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +0 -531
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +0 -70
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -162
- package/templates/apps/mcp/__tests__/lib/content-root.test.ts +0 -93
- package/templates/apps/mcp/__tests__/lib/errors.test.ts +0 -75
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +0 -180
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
- package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -268
- package/templates/apps/mcp/__tests__/locale.test.ts +0 -120
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +0 -175
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +0 -101
- package/templates/apps/mcp/__tests__/rate-limit-write.test.ts +0 -189
- package/templates/apps/mcp/__tests__/read_content.test.ts +0 -511
- package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -160
- package/templates/apps/mcp/__tests__/search_content.test.ts +0 -271
- package/templates/apps/mcp/__tests__/server.test.ts +0 -388
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +0 -448
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +0 -222
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -152
- package/templates/apps/mcp/__tests__/tools/create_project.test.ts +0 -243
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +0 -451
- package/templates/apps/mcp/src/abuse/daily-cap.ts +0 -47
- package/templates/apps/mcp/src/abuse/email-validator.ts +0 -25
- package/templates/apps/mcp/src/abuse/honeypot.ts +0 -21
- package/templates/apps/mcp/src/abuse/index.ts +0 -349
- package/templates/apps/mcp/src/abuse/user-agent-deny.ts +0 -58
- package/templates/apps/mcp/src/boot.ts +0 -167
- package/templates/apps/mcp/src/config/modules.ts +0 -43
- package/templates/apps/mcp/src/http-transport.ts +0 -564
- package/templates/apps/mcp/src/lib/audit-single-page.ts +0 -308
- package/templates/apps/mcp/src/lib/commerce-claim.ts +0 -263
- package/templates/apps/mcp/src/lib/content-adapter.ts +0 -116
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -140
- package/templates/apps/mcp/src/lib/errors.ts +0 -61
- package/templates/apps/mcp/src/lib/license-key-store.ts +0 -45
- package/templates/apps/mcp/src/lib/locales.ts +0 -171
- package/templates/apps/mcp/src/lib/prisma.ts +0 -39
- package/templates/apps/mcp/src/lib/public-workspace.ts +0 -133
- package/templates/apps/mcp/src/lib/resolve-locale.ts +0 -30
- package/templates/apps/mcp/src/lib/shown-offerings.ts +0 -105
- package/templates/apps/mcp/src/lib/site-config.ts +0 -53
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +0 -59
- package/templates/apps/mcp/src/request-context.ts +0 -52
- package/templates/apps/mcp/src/server.ts +0 -343
- package/templates/apps/mcp/src/serverless-app.ts +0 -88
- package/templates/apps/mcp/src/tools/audit.ts +0 -127
- package/templates/apps/mcp/src/tools/capture_lead.ts +0 -66
- package/templates/apps/mcp/src/tools/create_project.ts +0 -134
- package/templates/apps/mcp/src/tools/get_business_hours.ts +0 -68
- package/templates/apps/mcp/src/tools/get_faq.ts +0 -71
- package/templates/apps/mcp/src/tools/get_locations.ts +0 -88
- package/templates/apps/mcp/src/tools/get_offerings.ts +0 -96
- package/templates/apps/mcp/src/tools/get_pricing.ts +0 -94
- package/templates/apps/mcp/src/tools/get_site_info.ts +0 -86
- package/templates/apps/mcp/src/tools/index.ts +0 -138
- package/templates/apps/mcp/src/tools/list_pages.ts +0 -68
- package/templates/apps/mcp/src/tools/person_notes.ts +0 -25
- package/templates/apps/mcp/src/tools/proposals.ts +0 -27
- package/templates/apps/mcp/src/tools/purchase.ts +0 -205
- package/templates/apps/mcp/src/tools/read_content.ts +0 -81
- package/templates/apps/mcp/src/tools/request_callback.ts +0 -52
- package/templates/apps/mcp/src/tools/search_content.ts +0 -63
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -54
- package/templates/apps/mcp/src/workspace-server.ts +0 -224
- package/templates/database/CHANGELOG.md +0 -981
- package/templates/database/LICENSE +0 -202
- package/templates/database/NOTICE +0 -2
- package/templates/database/README.md +0 -243
- package/templates/database/__tests__/agents.db.test.ts +0 -320
- package/templates/database/__tests__/appointment.db.test.ts +0 -106
- package/templates/database/__tests__/artifact-content-store.db.test.ts +0 -316
- package/templates/database/__tests__/composite-fence.db.test.ts +0 -206
- package/templates/database/__tests__/consent-ledger.db.test.ts +0 -345
- package/templates/database/__tests__/consent-source-checkout-migration.test.ts +0 -79
- package/templates/database/__tests__/fence-fan-out.db.test.ts +0 -715
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +0 -79
- package/templates/database/__tests__/fixtures/person-backfill.json +0 -264
- package/templates/database/__tests__/fixtures/person-contact-backfill.json +0 -182
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +0 -100
- package/templates/database/__tests__/flow-completion-migration.test.ts +0 -115
- package/templates/database/__tests__/flow-completion.db.test.ts +0 -136
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +0 -263
- package/templates/database/__tests__/flows-listing-diagnostic.test.ts +0 -392
- package/templates/database/__tests__/flows-presets.test.ts +0 -173
- package/templates/database/__tests__/in-page-magnets-migration.test.ts +0 -161
- package/templates/database/__tests__/lead-idempotency-key.db.test.ts +0 -92
- package/templates/database/__tests__/member-area.db.test.ts +0 -257
- package/templates/database/__tests__/member-contract-migration.test.ts +0 -85
- package/templates/database/__tests__/member-contract.db.test.ts +0 -413
- package/templates/database/__tests__/offerings-checkout-session.db.test.ts +0 -270
- package/templates/database/__tests__/offerings-migration.test.ts +0 -197
- package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +0 -86
- package/templates/database/__tests__/offerings.db.test.ts +0 -303
- package/templates/database/__tests__/outbound-message-action.db.test.ts +0 -80
- package/templates/database/__tests__/person-contact.db.test.ts +0 -563
- package/templates/database/__tests__/person-notes.db.test.ts +0 -249
- package/templates/database/__tests__/person.db.test.ts +0 -586
- package/templates/database/__tests__/piece-kind-migration.test.ts +0 -67
- package/templates/database/__tests__/piece-kind.db.test.ts +0 -208
- package/templates/database/__tests__/prefixed-ids.db.test.ts +0 -222
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +0 -111
- package/templates/database/__tests__/seed.test.ts +0 -272
- package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
- package/templates/database/__tests__/tenancy.test.ts +0 -141
- package/templates/database/__tests__/trial-magnet-migration.test.ts +0 -186
- package/templates/database/__tests__/user-cascade.test.ts +0 -140
- package/templates/database/_gitignore +0 -8
- package/templates/database/auth/.gitkeep +0 -0
- package/templates/database/auth/schema.prisma +0 -641
- package/templates/database/config/.gitkeep +0 -0
- package/templates/database/config/schema.prisma +0 -441
- package/templates/database/content/.gitkeep +0 -0
- package/templates/database/content/schema.prisma +0 -339
- package/templates/database/eslint.config.mjs +0 -69
- package/templates/database/flows/listing-diagnostic.ts +0 -182
- package/templates/database/flows/presets.ts +0 -315
- package/templates/database/inbox/.gitkeep +0 -0
- package/templates/database/inbox/README.md +0 -3
- package/templates/database/inbox/__tests__/schema.test.ts +0 -44
- package/templates/database/inbox/schema.prisma +0 -1447
- package/templates/database/jest.config.cjs +0 -36
- package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +0 -328
- package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +0 -100
- package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +0 -28
- package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +0 -25
- package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +0 -42
- package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +0 -67
- package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +0 -21
- package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +0 -201
- package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +0 -67
- package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +0 -46
- package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +0 -25
- package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +0 -49
- package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +0 -197
- package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +0 -32
- package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +0 -22
- package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +0 -151
- package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +0 -223
- package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +0 -22
- package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +0 -32
- package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +0 -40
- package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +0 -4
- package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +0 -2
- package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +0 -2
- package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +0 -29
- package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +0 -6
- package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +0 -39
- package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +0 -24
- package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +0 -3
- package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +0 -33
- package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +0 -30
- package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +0 -47
- package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +0 -16
- package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +0 -47
- package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +0 -10
- package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +0 -26
- package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +0 -90
- package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +0 -174
- package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +0 -14
- package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +0 -25
- package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +0 -32
- package/templates/database/migrations/20260911180000_consent_grants/migration.sql +0 -71
- package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +0 -30
- package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +0 -49
- package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +0 -123
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +0 -15
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +0 -158
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +0 -40
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +0 -67
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +0 -70
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +0 -181
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +0 -172
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +0 -41
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +0 -185
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +0 -70
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +0 -57
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +0 -68
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +0 -1067
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +0 -210
- package/templates/database/migrations/20261003120000_spoke_registry/migration.sql +0 -80
- package/templates/database/migrations/20261003130000_scheduling_connection/migration.sql +0 -49
- package/templates/database/migrations/20261005120000_agents/migration.sql +0 -84
- package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +0 -35
- package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +0 -20
- package/templates/database/migrations/20261009120000_agent_grants/migration.sql +0 -33
- package/templates/database/migrations/20261010120000_person/migration.sql +0 -106
- package/templates/database/migrations/20261010121000_person_backfill/migration.sql +0 -132
- package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +0 -20
- package/templates/database/migrations/20261011120000_member_area/migration.sql +0 -158
- package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +0 -21
- package/templates/database/migrations/20261012120000_member_contract/migration.sql +0 -192
- package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +0 -20
- package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +0 -122
- package/templates/database/migrations/20261014120000_person_contact/migration.sql +0 -231
- package/templates/database/migrations/20261015120000_person_notes/migration.sql +0 -77
- package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +0 -43
- package/templates/database/migrations/20261016120000_piece_kind/migration.sql +0 -95
- package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +0 -30
- package/templates/database/migrations/20261018120000_flow_completion/migration.sql +0 -52
- package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +0 -27
- package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +0 -146
- package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +0 -77
- package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +0 -45
- package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +0 -47
- package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +0 -16
- package/templates/database/migrations/20261025120000_trial_magnet/migration.sql +0 -28
- package/templates/database/migrations/20261026120000_trial_magnet_proof_line/migration.sql +0 -23
- package/templates/database/migrations/migration_lock.toml +0 -3
- package/templates/database/ops/.gitkeep +0 -0
- package/templates/database/ops/schema.prisma +0 -43
- package/templates/database/package.json +0 -89
- package/templates/database/schema.prisma +0 -71
- package/templates/database/scripts/db-generate-locked.sh +0 -80
- package/templates/database/scripts/person-backfill.sql +0 -132
- package/templates/database/tenancy.ts +0 -183
- package/templates/database/tsconfig.build.json +0 -16
- package/templates/database/tsconfig.flows.json +0 -15
- package/templates/database/tsconfig.json +0 -16
- package/templates/database/tsconfig.tenancy.json +0 -14
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
-- #6155 — FlowDefinition and FlowSession gain a workspace isolation key.
|
|
2
|
-
--
|
|
3
|
-
-- The ownership brief (docs/internal/decisions/workspace-owns-data-surfaces-render.md)
|
|
4
|
-
-- and AD-2 (docs/internal/decisions/flow-engine-admin-surface.md): a workspace
|
|
5
|
-
-- owns data, and the flow engine's tenant-scope fence grows to cover it.
|
|
6
|
-
-- `siteId` stays a legacy alias for the public path (unchanged); `workspaceId`
|
|
7
|
-
-- is the isolation key every authenticated read/write will go through
|
|
8
|
-
-- (apps/api/src/lib/tenant-db.ts's new `flows`/`flowSessions` accessors).
|
|
9
|
-
--
|
|
10
|
-
-- NULLABLE, no default (spec D3) — a row whose `siteId` matches no workspace
|
|
11
|
-
-- is left UNSET rather than guessed, and stays invisible to every accessor
|
|
12
|
-
-- rather than visible to the wrong tenant (the same "fails empty, never
|
|
13
|
-
-- wide" posture apps/api/src/services/leads-inbox.ts already takes).
|
|
14
|
-
--
|
|
15
|
-
-- RE-RUNNABLE (NFR3): `ADD COLUMN IF NOT EXISTS` / `CREATE INDEX IF NOT
|
|
16
|
-
-- EXISTS` cover the column and index. Postgres has no
|
|
17
|
-
-- `ADD CONSTRAINT IF NOT EXISTS`, so each format CHECK is wrapped in its own
|
|
18
|
-
-- existence-guarded `DO $$` block — the stated exception to NFR3's "in the
|
|
19
|
-
-- house style of 20260918120000_asset_metadata_columns" note: that
|
|
20
|
-
-- precedent (and 20260713192429_prefixed_uuid_entity_ids's own CHECK
|
|
21
|
-
-- additions) rely on `prisma migrate`'s per-migration transaction and
|
|
22
|
-
-- ledger, not on idempotent DDL — the guard below is this migration's own
|
|
23
|
-
-- addition.
|
|
24
|
-
--
|
|
25
|
-
-- Rollback: DROP INDEX "flow_definitions_workspace_id_idx",
|
|
26
|
-
-- "flow_sessions_workspace_id_idx"; ALTER TABLE "flow_definitions" DROP
|
|
27
|
-
-- CONSTRAINT "flow_definitions_workspace_id_format", DROP COLUMN
|
|
28
|
-
-- "workspace_id"; ALTER TABLE "flow_sessions" DROP CONSTRAINT
|
|
29
|
-
-- "flow_sessions_workspace_id_format", DROP COLUMN "workspace_id". Safe
|
|
30
|
-
-- unconditionally at this pin — nothing reads the column until #6156+.
|
|
31
|
-
|
|
32
|
-
-- The census the post-assertions compare against. ON COMMIT DROP: `prisma
|
|
33
|
-
-- migrate` runs each migration in one transaction, so this disappears with it.
|
|
34
|
-
CREATE TEMP TABLE "_6155_flow_census" ON COMMIT DROP AS
|
|
35
|
-
SELECT (SELECT count(*) FROM "flow_definitions") AS d,
|
|
36
|
-
(SELECT count(*) FROM "flow_sessions") AS s;
|
|
37
|
-
|
|
38
|
-
-- AlterTable
|
|
39
|
-
ALTER TABLE "flow_definitions" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
|
|
40
|
-
ALTER TABLE "flow_sessions" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
|
|
41
|
-
|
|
42
|
-
-- Format CHECKs — NULL-permitting, in the exact shape of the CHECKs
|
|
43
|
-
-- 20260713192429_prefixed_uuid_entity_ids/migration.sql:170-197 already
|
|
44
|
-
-- applies to every other `workspace_id` column. Existence-guarded because
|
|
45
|
-
-- Postgres has no `ADD CONSTRAINT IF NOT EXISTS`.
|
|
46
|
-
DO $$ BEGIN
|
|
47
|
-
IF NOT EXISTS (
|
|
48
|
-
SELECT 1 FROM pg_constraint WHERE conname = 'flow_definitions_workspace_id_format'
|
|
49
|
-
) THEN
|
|
50
|
-
ALTER TABLE "flow_definitions" ADD CONSTRAINT "flow_definitions_workspace_id_format"
|
|
51
|
-
CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
|
|
52
|
-
END IF;
|
|
53
|
-
END $$;
|
|
54
|
-
|
|
55
|
-
DO $$ BEGIN
|
|
56
|
-
IF NOT EXISTS (
|
|
57
|
-
SELECT 1 FROM pg_constraint WHERE conname = 'flow_sessions_workspace_id_format'
|
|
58
|
-
) THEN
|
|
59
|
-
ALTER TABLE "flow_sessions" ADD CONSTRAINT "flow_sessions_workspace_id_format"
|
|
60
|
-
CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
|
|
61
|
-
END IF;
|
|
62
|
-
END $$;
|
|
63
|
-
|
|
64
|
-
-- CreateIndex
|
|
65
|
-
CREATE INDEX IF NOT EXISTS "flow_definitions_workspace_id_idx" ON "flow_definitions"("workspace_id");
|
|
66
|
-
CREATE INDEX IF NOT EXISTS "flow_sessions_workspace_id_idx" ON "flow_sessions"("workspace_id");
|
|
67
|
-
|
|
68
|
-
-- ── Backfill (spec D2) ──────────────────────────────────────────────────────
|
|
69
|
-
-- Both spellings of the alias, one statement per table. The literal alias
|
|
70
|
-
-- "default" resolves to the seeded sentinel workspace
|
|
71
|
-
-- (database/tenancy.ts DEFAULT_WORKSPACE_ID); every other alias resolves
|
|
72
|
-
-- only by exact workspaces.id equality. An inner join to "workspaces" means a
|
|
73
|
-
-- row whose alias resolves to nothing is simply not updated and stays NULL
|
|
74
|
-
-- — the correct, stated behavior (D3), not an error.
|
|
75
|
-
--
|
|
76
|
-
-- The sentinel id is written as a literal because SQL cannot import
|
|
77
|
-
-- database/tenancy.ts; database/__tests__/tenancy.test.ts pins the same
|
|
78
|
-
-- literal against the exported constant so the two cannot drift.
|
|
79
|
-
UPDATE "flow_definitions" f
|
|
80
|
-
SET "workspace_id" = w."id"
|
|
81
|
-
FROM "workspaces" w
|
|
82
|
-
WHERE f."workspace_id" IS NULL
|
|
83
|
-
AND w."id" = CASE WHEN f."site_id" = 'default'
|
|
84
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
85
|
-
ELSE f."site_id" END;
|
|
86
|
-
|
|
87
|
-
UPDATE "flow_sessions" f
|
|
88
|
-
SET "workspace_id" = w."id"
|
|
89
|
-
FROM "workspaces" w
|
|
90
|
-
WHERE f."workspace_id" IS NULL
|
|
91
|
-
AND w."id" = CASE WHEN f."site_id" = 'default'
|
|
92
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
93
|
-
ELSE f."site_id" END;
|
|
94
|
-
|
|
95
|
-
-- ── POST-ASSERTIONS ─────────────────────────────────────────────────────────
|
|
96
|
-
-- AC-1's own enforcement: nothing lost, nothing guessed, nothing missed that
|
|
97
|
-
-- was resolvable. The fourth check is a NOTICE, not an exception — an
|
|
98
|
-
-- unpinned/unseeded deployment leaving rows NULL is the expected, documented
|
|
99
|
-
-- case (spec § 1.3), never an error this migration should refuse to run over.
|
|
100
|
-
DO $$
|
|
101
|
-
DECLARE
|
|
102
|
-
before_d bigint;
|
|
103
|
-
before_s bigint;
|
|
104
|
-
after_d bigint;
|
|
105
|
-
after_s bigint;
|
|
106
|
-
guessed bigint;
|
|
107
|
-
missed bigint;
|
|
108
|
-
null_d bigint;
|
|
109
|
-
null_s bigint;
|
|
110
|
-
BEGIN
|
|
111
|
-
SELECT d, s INTO before_d, before_s FROM "_6155_flow_census";
|
|
112
|
-
SELECT count(*) INTO after_d FROM "flow_definitions";
|
|
113
|
-
SELECT count(*) INTO after_s FROM "flow_sessions";
|
|
114
|
-
|
|
115
|
-
-- 1. Nothing lost: row counts unchanged on both tables.
|
|
116
|
-
IF before_d <> after_d OR before_s <> after_s THEN
|
|
117
|
-
RAISE EXCEPTION
|
|
118
|
-
'#6155 post-assertion: row count changed across a purely additive migration (flow_definitions % -> %, flow_sessions % -> %)',
|
|
119
|
-
before_d, after_d, before_s, after_s;
|
|
120
|
-
END IF;
|
|
121
|
-
|
|
122
|
-
-- 2. Nothing guessed: no row's workspace_id was set to a value absent
|
|
123
|
-
-- from "workspaces" (the inner-join backfill above cannot produce this
|
|
124
|
-
-- on its own, but a re-run against a database missing the sentinel row
|
|
125
|
-
-- must never leave a dangling reference).
|
|
126
|
-
SELECT count(*) INTO guessed FROM "flow_definitions" f
|
|
127
|
-
WHERE f."workspace_id" IS NOT NULL
|
|
128
|
-
AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = f."workspace_id");
|
|
129
|
-
IF guessed <> 0 THEN
|
|
130
|
-
RAISE EXCEPTION
|
|
131
|
-
'#6155 post-assertion: % flow_definitions row(s) carry a workspace_id absent from workspaces',
|
|
132
|
-
guessed;
|
|
133
|
-
END IF;
|
|
134
|
-
SELECT count(*) INTO guessed FROM "flow_sessions" f
|
|
135
|
-
WHERE f."workspace_id" IS NOT NULL
|
|
136
|
-
AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = f."workspace_id");
|
|
137
|
-
IF guessed <> 0 THEN
|
|
138
|
-
RAISE EXCEPTION
|
|
139
|
-
'#6155 post-assertion: % flow_sessions row(s) carry a workspace_id absent from workspaces',
|
|
140
|
-
guessed;
|
|
141
|
-
END IF;
|
|
142
|
-
|
|
143
|
-
-- 3. Nothing missed: no row whose site_id names a workspace that actually
|
|
144
|
-
-- exists came out of the backfill with workspace_id still NULL.
|
|
145
|
-
SELECT count(*) INTO missed FROM "flow_definitions" f
|
|
146
|
-
WHERE f."workspace_id" IS NULL
|
|
147
|
-
AND EXISTS (
|
|
148
|
-
SELECT 1 FROM "workspaces" w
|
|
149
|
-
WHERE w."id" = CASE WHEN f."site_id" = 'default'
|
|
150
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
151
|
-
ELSE f."site_id" END
|
|
152
|
-
);
|
|
153
|
-
IF missed <> 0 THEN
|
|
154
|
-
RAISE EXCEPTION
|
|
155
|
-
'#6155 post-assertion: % flow_definitions row(s) had a resolvable site_id but came out of the backfill NULL',
|
|
156
|
-
missed;
|
|
157
|
-
END IF;
|
|
158
|
-
SELECT count(*) INTO missed FROM "flow_sessions" f
|
|
159
|
-
WHERE f."workspace_id" IS NULL
|
|
160
|
-
AND EXISTS (
|
|
161
|
-
SELECT 1 FROM "workspaces" w
|
|
162
|
-
WHERE w."id" = CASE WHEN f."site_id" = 'default'
|
|
163
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
164
|
-
ELSE f."site_id" END
|
|
165
|
-
);
|
|
166
|
-
IF missed <> 0 THEN
|
|
167
|
-
RAISE EXCEPTION
|
|
168
|
-
'#6155 post-assertion: % flow_sessions row(s) had a resolvable site_id but came out of the backfill NULL',
|
|
169
|
-
missed;
|
|
170
|
-
END IF;
|
|
171
|
-
|
|
172
|
-
-- 4. A NOTICE, never an exception: an unpinned/unseeded deployment leaving
|
|
173
|
-
-- rows NULL is the expected, documented case (spec § 1.3).
|
|
174
|
-
SELECT count(*) INTO null_d FROM "flow_definitions" WHERE "workspace_id" IS NULL;
|
|
175
|
-
SELECT count(*) INTO null_s FROM "flow_sessions" WHERE "workspace_id" IS NULL;
|
|
176
|
-
IF null_d <> 0 OR null_s <> 0 THEN
|
|
177
|
-
RAISE NOTICE
|
|
178
|
-
'#6155: % flow_definitions row(s) and % flow_sessions row(s) left workspace_id unset — their site_id did not resolve to a workspace this database holds',
|
|
179
|
-
null_d, null_s;
|
|
180
|
-
END IF;
|
|
181
|
-
END $$;
|
package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
-- #6156 — S5b: a magnet is a flow row. FlowDefinition gains four
|
|
2
|
-
-- operator-facing columns — kind, title, landingPage, status — spec D1.
|
|
3
|
-
--
|
|
4
|
-
-- STRICTLY ADDITIVE. Nothing dropped, renamed or relaxed; @@unique
|
|
5
|
-
-- ([siteId, slug]), the `flow_`+UUIDv7 id default, and `listing_id` are all
|
|
6
|
-
-- untouched. The visitor-facing `config` column is byte-identical across
|
|
7
|
-
-- this migration (FR4, asserted below) — these four columns are how the
|
|
8
|
-
-- operator MANAGES a magnet, never what a visitor's session SEES
|
|
9
|
-
-- (AD-3, spec D1/§1.2).
|
|
10
|
-
--
|
|
11
|
-
-- `status` is backfilled 'live' (every row that exists today is already
|
|
12
|
-
-- serving — D1's availability half) and carries NO column default (D1's
|
|
13
|
-
-- publication-safety half, SEC-08): a DEFAULT 'live' would make
|
|
14
|
-
-- PUBLICATION the thing that happens when a future writer says nothing,
|
|
15
|
-
-- which is the exact asymmetry the brief's "a magnet created from the
|
|
16
|
-
-- Admin opens as a draft" commitment forbids. So `status` is added
|
|
17
|
-
-- nullable, backfilled, then SET NOT NULL with no default — every future
|
|
18
|
-
-- writer must name a status or fail to compile.
|
|
19
|
-
--
|
|
20
|
-
-- `title` is NOT NULL with no column default, by the same three-step, for
|
|
21
|
-
-- the positive reason: a nameless magnet must never enter the table
|
|
22
|
-
-- silently. The slug backfills every existing row's title — an honest
|
|
23
|
-
-- placeholder the operator renames.
|
|
24
|
-
--
|
|
25
|
-
-- `kind` KEEPS its column default ('contact') — unlike `status`, a
|
|
26
|
-
-- mis-classified magnet is a mislabel in a list, not a publication or
|
|
27
|
-
-- security control (D1). The backfill below reclassifies the rows D3's
|
|
28
|
-
-- structural rule actually matches; every other row stays the default.
|
|
29
|
-
--
|
|
30
|
-
-- `landing_page` is nullable with NO backfill (D1): a magnet has THREE
|
|
31
|
-
-- public addresses (the indexed landing route, the walk route one segment
|
|
32
|
-
-- deeper, and the results route), so seeding a derived guess would freeze
|
|
33
|
-
-- a route as if it were an operator decision. NULL means "no
|
|
34
|
-
-- operator-declared landing page; the canonical route serves it" — true of
|
|
35
|
-
-- every existing row.
|
|
36
|
-
--
|
|
37
|
-
-- `flow_definitions_status_domain` (D2) is a closed two-value CHECK,
|
|
38
|
-
-- deliberately narrower than the precedent this namespace otherwise follows
|
|
39
|
-
-- (WaitlistExperiment.status, FlowCheckout.status, ProjectListing.status —
|
|
40
|
-
-- all plain String with no CHECK): `status` is what makes D10's
|
|
41
|
-
-- deny-list gate in `resolveSession` SAFE rather than merely convenient —
|
|
42
|
-
-- an unstorable unknown value means "not 'draft'" and "'live'" are the same
|
|
43
|
-
-- set at the database level. No CHECK on `kind`: its vocabulary is six
|
|
44
|
-
-- values and growing (Epic #6025), so a CHECK there would make every new
|
|
45
|
-
-- kind a migration that RELAXES a constraint.
|
|
46
|
-
--
|
|
47
|
-
-- Statement order (D9) is fixed and load-bearing, not incidental: the
|
|
48
|
-
-- `ADD COLUMN` runs FIRST, acquiring ACCESS EXCLUSIVE on `flow_definitions`
|
|
49
|
-
-- before the census is taken, so no concurrent committed write (a listing
|
|
50
|
-
-- approval via `upsertFlowDefinition`, a waitlist-register materialize) can
|
|
51
|
-
-- land between the census and the assertions and falsely trip them. The
|
|
52
|
-
-- whole sequence blocks every read AND write to `flow_definitions` for its
|
|
53
|
-
-- duration — acceptable only because the table is tiny today (§1.4's
|
|
54
|
-
-- census: four seeded presets plus two generated families); a later unit
|
|
55
|
-
-- that grows this table materially owes a re-read of this decision rather
|
|
56
|
-
-- than an inherited assumption.
|
|
57
|
-
--
|
|
58
|
-
-- Not re-runnable by `IF NOT EXISTS` guards (unlike #6155's
|
|
59
|
-
-- 20260919120000_flow_workspace_key, whose NFR3 needed it for a reason
|
|
60
|
-
-- specific to that migration): `prisma migrate`'s per-migration transaction
|
|
61
|
-
-- + the `_prisma_migrations` ledger already make re-runnability a
|
|
62
|
-
-- non-requirement here.
|
|
63
|
-
--
|
|
64
|
-
-- Rollback: ALTER TABLE "flow_definitions" DROP COLUMN "kind", DROP COLUMN
|
|
65
|
-
-- "title", DROP COLUMN "landing_page", DROP COLUMN "status". Safe
|
|
66
|
-
-- unconditionally — dropping "status" drops "flow_definitions_status_domain"
|
|
67
|
-
-- with it, the four columns carry no other constraint, no index and no
|
|
68
|
-
-- relation, and nothing outside this diff references them.
|
|
69
|
-
|
|
70
|
-
-- AlterTable — all four, one statement, taking ACCESS EXCLUSIVE before
|
|
71
|
-
-- anything else in this transaction reads the table (D9).
|
|
72
|
-
ALTER TABLE "flow_definitions"
|
|
73
|
-
ADD COLUMN "kind" TEXT NOT NULL DEFAULT 'contact',
|
|
74
|
-
ADD COLUMN "title" TEXT,
|
|
75
|
-
ADD COLUMN "landing_page" TEXT,
|
|
76
|
-
ADD COLUMN "status" TEXT;
|
|
77
|
-
|
|
78
|
-
-- The census the post-assertions compare against — taken SECOND, under the
|
|
79
|
-
-- lock the ADD COLUMN above already holds, so it is a stable baseline for
|
|
80
|
-
-- every assertion below (D9 / SEC-07). ON COMMIT DROP: `prisma migrate`
|
|
81
|
-
-- runs each migration in one transaction, so this disappears with it.
|
|
82
|
-
CREATE TEMP TABLE "_6156_flow_def_census" ON COMMIT DROP AS
|
|
83
|
-
SELECT count(*) AS n,
|
|
84
|
-
md5(string_agg("config"::text, '' ORDER BY "id")) AS config_hash
|
|
85
|
-
FROM "flow_definitions";
|
|
86
|
-
|
|
87
|
-
-- ── Backfill (D3 / D1) ───────────────────────────────────────────────────
|
|
88
|
-
-- 1. `kind` — D3's structural rule, first match wins. Every row not
|
|
89
|
-
-- matched by a branch below keeps the column default written above.
|
|
90
|
-
UPDATE "flow_definitions"
|
|
91
|
-
SET "kind" = CASE
|
|
92
|
-
WHEN "listing_id" IS NOT NULL THEN 'diagnostic'
|
|
93
|
-
WHEN "config"->>'mode' = 'recommending' THEN 'diagnostic'
|
|
94
|
-
WHEN "config" ? 'deliverables' THEN 'special-report'
|
|
95
|
-
ELSE 'contact'
|
|
96
|
-
END;
|
|
97
|
-
|
|
98
|
-
-- 2. `title` = the slug (an honest placeholder); `status` = 'live' (every
|
|
99
|
-
-- row that exists today is already serving — D1's availability half).
|
|
100
|
-
UPDATE "flow_definitions"
|
|
101
|
-
SET "title" = "slug",
|
|
102
|
-
"status" = 'live';
|
|
103
|
-
|
|
104
|
-
-- SET NOT NULL + the status domain CHECK — AFTER the backfill, so neither
|
|
105
|
-
-- constraint is ever evaluated against a NULL this migration itself wrote.
|
|
106
|
-
ALTER TABLE "flow_definitions" ALTER COLUMN "title" SET NOT NULL;
|
|
107
|
-
ALTER TABLE "flow_definitions" ALTER COLUMN "status" SET NOT NULL;
|
|
108
|
-
ALTER TABLE "flow_definitions"
|
|
109
|
-
ADD CONSTRAINT "flow_definitions_status_domain"
|
|
110
|
-
CHECK ("status" IN ('draft', 'live'));
|
|
111
|
-
|
|
112
|
-
-- ── POST-ASSERTIONS (FR2/FR3/FR4 — AC-1) ────────────────────────────────
|
|
113
|
-
-- Asserted against the rows the target database actually holds, not a
|
|
114
|
-
-- fixture's. A raised exception here rolls the whole migration back
|
|
115
|
-
-- (`prisma migrate`'s per-migration transaction).
|
|
116
|
-
DO $$
|
|
117
|
-
DECLARE
|
|
118
|
-
before_n bigint;
|
|
119
|
-
before_hash text;
|
|
120
|
-
after_n bigint;
|
|
121
|
-
after_hash text;
|
|
122
|
-
null_cols bigint;
|
|
123
|
-
wrong_kind bigint;
|
|
124
|
-
BEGIN
|
|
125
|
-
SELECT n, config_hash INTO before_n, before_hash FROM "_6156_flow_def_census";
|
|
126
|
-
SELECT count(*), md5(string_agg("config"::text, '' ORDER BY "id"))
|
|
127
|
-
INTO after_n, after_hash
|
|
128
|
-
FROM "flow_definitions";
|
|
129
|
-
|
|
130
|
-
-- 1. Nothing lost or gained: row count unchanged across a purely
|
|
131
|
-
-- additive-plus-backfill migration.
|
|
132
|
-
IF before_n <> after_n THEN
|
|
133
|
-
RAISE EXCEPTION
|
|
134
|
-
'#6156 post-assertion: flow_definitions row count changed (% -> %)',
|
|
135
|
-
before_n, after_n;
|
|
136
|
-
END IF;
|
|
137
|
-
|
|
138
|
-
-- 2. `config` is byte-identical (FR4) — this migration writes NO column
|
|
139
|
-
-- other than kind/title/landing_page/status.
|
|
140
|
-
IF before_hash IS DISTINCT FROM after_hash THEN
|
|
141
|
-
RAISE EXCEPTION
|
|
142
|
-
'#6156 post-assertion: flow_definitions.config changed across a migration that must not touch it';
|
|
143
|
-
END IF;
|
|
144
|
-
|
|
145
|
-
-- 3. Every pre-existing row leaves with a non-NULL status/title/kind
|
|
146
|
-
-- (landing_page is NOT asserted — D1: it is deliberately unbackfilled).
|
|
147
|
-
SELECT count(*) INTO null_cols FROM "flow_definitions"
|
|
148
|
-
WHERE "status" IS NULL OR "title" IS NULL OR "kind" IS NULL;
|
|
149
|
-
IF null_cols <> 0 THEN
|
|
150
|
-
RAISE EXCEPTION
|
|
151
|
-
'#6156 post-assertion: % flow_definitions row(s) came out of the backfill with a NULL status/title/kind',
|
|
152
|
-
null_cols;
|
|
153
|
-
END IF;
|
|
154
|
-
|
|
155
|
-
-- 4. Backfill rule correctness (D3) — every listing-projected row, and
|
|
156
|
-
-- every recommending-mode row, classified 'diagnostic'.
|
|
157
|
-
SELECT count(*) INTO wrong_kind FROM "flow_definitions"
|
|
158
|
-
WHERE "listing_id" IS NOT NULL AND "kind" <> 'diagnostic';
|
|
159
|
-
IF wrong_kind <> 0 THEN
|
|
160
|
-
RAISE EXCEPTION
|
|
161
|
-
'#6156 post-assertion: % flow_definitions row(s) with listing_id set did not classify as diagnostic',
|
|
162
|
-
wrong_kind;
|
|
163
|
-
END IF;
|
|
164
|
-
|
|
165
|
-
SELECT count(*) INTO wrong_kind FROM "flow_definitions"
|
|
166
|
-
WHERE "config"->>'mode' = 'recommending' AND "kind" <> 'diagnostic';
|
|
167
|
-
IF wrong_kind <> 0 THEN
|
|
168
|
-
RAISE EXCEPTION
|
|
169
|
-
'#6156 post-assertion: % flow_definitions row(s) with config.mode = recommending did not classify as diagnostic',
|
|
170
|
-
wrong_kind;
|
|
171
|
-
END IF;
|
|
172
|
-
END $$;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
-- #6183 (S9b) — the WorkspaceMeetings model: the operator's enabled-slug
|
|
2
|
-
-- gate for meeting types (spec docs/specs/6183-s9b-enabled-meeting-slugs-live-spec.md
|
|
3
|
-
-- D1, D11). A fourth workspace site-config blob, structurally identical to
|
|
4
|
-
-- its three siblings (workspace_themes / workspace_site_identities /
|
|
5
|
-
-- workspace_seos) — same `@unique(workspace_id)` key, same Json-column
|
|
6
|
-
-- convention, validation happens at the API layer.
|
|
7
|
-
--
|
|
8
|
-
-- One new table, one unique index, in the house style of
|
|
9
|
-
-- 20260918120000_add_appointment (CreateTable + CreateIndex immediately
|
|
10
|
-
-- followed by the ADR-0031 rule-3 id-format completion, in ONE migration
|
|
11
|
-
-- since the table is new and needs no retrofit). No `workspace_id_format`
|
|
12
|
-
-- CHECK, matching that same precedent — the baseline migration's 12
|
|
13
|
-
-- relation-free scope checks are pinned to it alone
|
|
14
|
-
-- (packages/ids/src/__tests__/migration-sync.test.ts), and no per-entity
|
|
15
|
-
-- migration since has added one.
|
|
16
|
-
--
|
|
17
|
-
-- No backfill, no constraint relaxed, no existing table touched.
|
|
18
|
-
--
|
|
19
|
-
-- Rollback: DROP TABLE "workspace_meetings". Safe unconditionally while
|
|
20
|
-
-- nothing but #6183's own code reads it.
|
|
21
|
-
|
|
22
|
-
-- CreateTable
|
|
23
|
-
CREATE TABLE "workspace_meetings" (
|
|
24
|
-
"id" TEXT NOT NULL DEFAULT ('meet_'::text || (uuidv7())::text),
|
|
25
|
-
"workspace_id" TEXT NOT NULL,
|
|
26
|
-
"data" JSONB NOT NULL,
|
|
27
|
-
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
28
|
-
"updated_at" TIMESTAMPTZ NOT NULL,
|
|
29
|
-
|
|
30
|
-
CONSTRAINT "workspace_meetings_pkey" PRIMARY KEY ("id")
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
-- CreateIndex
|
|
34
|
-
CREATE UNIQUE INDEX "workspace_meetings_workspace_id_key" ON "workspace_meetings"("workspace_id");
|
|
35
|
-
|
|
36
|
-
-- ADR-0031 rule 3 — workspace_meetings: meet_ (registered in
|
|
37
|
-
-- packages/ids/src/registry.ts ENTITY_PREFIX.workspaceMeetings; this
|
|
38
|
-
-- migration's path is added to migration-sync.test.ts's PK_CHECK_MIGRATIONS).
|
|
39
|
-
ALTER TABLE "workspace_meetings" ALTER COLUMN "id" TYPE text COLLATE "C";
|
|
40
|
-
ALTER TABLE "workspace_meetings" ALTER COLUMN "id" SET DEFAULT ('meet_'::text || (uuidv7())::text);
|
|
41
|
-
ALTER TABLE "workspace_meetings" ADD CONSTRAINT "workspace_meetings_id_format" CHECK ("id" ~ '^meet_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
-- #6203 — ProjectListing and Lead gain a workspace isolation key.
|
|
2
|
-
--
|
|
3
|
-
-- The ownership brief (docs/internal/decisions/workspace-owns-data-surfaces-render.md)
|
|
4
|
-
-- and AD-4 (docs/internal/decisions/flow-engine-admin-surface.md): a workspace
|
|
5
|
-
-- owns data, and the Inbox's tenant-scope fence grows to cover the two
|
|
6
|
-
-- models it reads. `siteId` stays a legacy alias for the anonymous write
|
|
7
|
-
-- paths (unchanged); `workspaceId` is the isolation key every Admin read
|
|
8
|
-
-- will go through (apps/api/src/lib/tenant-db.ts's new
|
|
9
|
-
-- `projectListings`/`leads` accessors). Mirrors
|
|
10
|
-
-- database/migrations/20260919120000_flow_workspace_key step for step
|
|
11
|
-
-- (#6155's precedent for FlowDefinition/FlowSession).
|
|
12
|
-
--
|
|
13
|
-
-- NULLABLE, no default (spec D2) — a row whose `siteId` matches no workspace
|
|
14
|
-
-- is left UNSET rather than guessed, and stays invisible to every accessor
|
|
15
|
-
-- rather than visible to the wrong tenant (the same "fails empty, never
|
|
16
|
-
-- wide" posture apps/api/src/services/leads-inbox.ts already takes).
|
|
17
|
-
--
|
|
18
|
-
-- RE-RUNNABLE (NFR3): `ADD COLUMN IF NOT EXISTS` / `CREATE INDEX IF NOT
|
|
19
|
-
-- EXISTS` cover the column and index. Postgres has no
|
|
20
|
-
-- `ADD CONSTRAINT IF NOT EXISTS`, so each format CHECK is wrapped in its own
|
|
21
|
-
-- existence-guarded `DO $$` block, exactly as the #6155 precedent does.
|
|
22
|
-
--
|
|
23
|
-
-- Rollback: DROP INDEX "project_listings_workspace_id_idx",
|
|
24
|
-
-- "leads_workspace_id_idx"; ALTER TABLE "project_listings" DROP
|
|
25
|
-
-- CONSTRAINT "project_listings_workspace_id_format", DROP COLUMN
|
|
26
|
-
-- "workspace_id"; ALTER TABLE "leads" DROP CONSTRAINT
|
|
27
|
-
-- "leads_workspace_id_format", DROP COLUMN "workspace_id". Safe
|
|
28
|
-
-- unconditionally at this pin — nothing reads the column until #6204+.
|
|
29
|
-
|
|
30
|
-
-- The census the post-assertions compare against. ON COMMIT DROP: `prisma
|
|
31
|
-
-- migrate` runs each migration in one transaction, so this disappears with it.
|
|
32
|
-
CREATE TEMP TABLE "_6203_inbox_census" ON COMMIT DROP AS
|
|
33
|
-
SELECT (SELECT count(*) FROM "project_listings") AS p,
|
|
34
|
-
(SELECT count(*) FROM "leads") AS l;
|
|
35
|
-
|
|
36
|
-
-- AlterTable
|
|
37
|
-
ALTER TABLE "project_listings" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
|
|
38
|
-
ALTER TABLE "leads" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
|
|
39
|
-
|
|
40
|
-
-- Format CHECKs — NULL-permitting, in the exact shape of the CHECKs
|
|
41
|
-
-- 20260713192429_prefixed_uuid_entity_ids/migration.sql already applies to
|
|
42
|
-
-- every other `workspace_id` column. Existence-guarded because Postgres has
|
|
43
|
-
-- no `ADD CONSTRAINT IF NOT EXISTS`. This is what makes the Lead.siteId
|
|
44
|
-
-- provenance gap (spec §1.3, §9 SEC-1) non-exploitable as an id-SHAPE
|
|
45
|
-
-- attack: a site_id that is not a well-formed wksp_<uuidv7> cannot land in
|
|
46
|
-
-- this column at all.
|
|
47
|
-
DO $$ BEGIN
|
|
48
|
-
IF NOT EXISTS (
|
|
49
|
-
SELECT 1 FROM pg_constraint WHERE conname = 'project_listings_workspace_id_format'
|
|
50
|
-
) THEN
|
|
51
|
-
ALTER TABLE "project_listings" ADD CONSTRAINT "project_listings_workspace_id_format"
|
|
52
|
-
CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
|
|
53
|
-
END IF;
|
|
54
|
-
END $$;
|
|
55
|
-
|
|
56
|
-
DO $$ BEGIN
|
|
57
|
-
IF NOT EXISTS (
|
|
58
|
-
SELECT 1 FROM pg_constraint WHERE conname = 'leads_workspace_id_format'
|
|
59
|
-
) THEN
|
|
60
|
-
ALTER TABLE "leads" ADD CONSTRAINT "leads_workspace_id_format"
|
|
61
|
-
CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
|
|
62
|
-
END IF;
|
|
63
|
-
END $$;
|
|
64
|
-
|
|
65
|
-
-- CreateIndex
|
|
66
|
-
CREATE INDEX IF NOT EXISTS "project_listings_workspace_id_idx" ON "project_listings"("workspace_id");
|
|
67
|
-
CREATE INDEX IF NOT EXISTS "leads_workspace_id_idx" ON "leads"("workspace_id");
|
|
68
|
-
|
|
69
|
-
-- ── Backfill (spec D2, §5.4) ────────────────────────────────────────────────
|
|
70
|
-
-- Both spellings of the alias, one statement per table. The literal alias
|
|
71
|
-
-- "default" resolves to the seeded sentinel workspace
|
|
72
|
-
-- (database/tenancy.ts DEFAULT_WORKSPACE_ID); every other alias resolves
|
|
73
|
-
-- only by exact workspaces.id equality. An inner join to "workspaces" means a
|
|
74
|
-
-- row whose alias resolves to nothing is simply not updated and stays NULL
|
|
75
|
-
-- — the correct, stated behavior (D2), not an error.
|
|
76
|
-
--
|
|
77
|
-
-- The sentinel id is a literal because SQL cannot import
|
|
78
|
-
-- database/tenancy.ts. It is ADR 0031's reserved-range sentinel and changes
|
|
79
|
-
-- only with that ADR — no test pins it against SQL (#6155's own migration
|
|
80
|
-
-- carries the identical correction: the sibling tenancy.test.ts compares
|
|
81
|
-
-- only the imported constant, not this SQL literal).
|
|
82
|
-
UPDATE "project_listings" p
|
|
83
|
-
SET "workspace_id" = w."id"
|
|
84
|
-
FROM "workspaces" w
|
|
85
|
-
WHERE p."workspace_id" IS NULL
|
|
86
|
-
AND w."id" = CASE WHEN p."site_id" = 'default'
|
|
87
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
88
|
-
ELSE p."site_id" END;
|
|
89
|
-
|
|
90
|
-
UPDATE "leads" l
|
|
91
|
-
SET "workspace_id" = w."id"
|
|
92
|
-
FROM "workspaces" w
|
|
93
|
-
WHERE l."workspace_id" IS NULL
|
|
94
|
-
AND w."id" = CASE WHEN l."site_id" = 'default'
|
|
95
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
96
|
-
ELSE l."site_id" END;
|
|
97
|
-
|
|
98
|
-
-- ── POST-ASSERTIONS ─────────────────────────────────────────────────────────
|
|
99
|
-
-- AC-1's own enforcement: nothing lost, nothing guessed, nothing missed that
|
|
100
|
-
-- was resolvable. The fourth check is a NOTICE, not an exception — an
|
|
101
|
-
-- unpinned/unseeded deployment leaving rows NULL is the expected, documented
|
|
102
|
-
-- case (spec §1.9, NFR4), never an error this migration should refuse to
|
|
103
|
-
-- run over.
|
|
104
|
-
DO $$
|
|
105
|
-
DECLARE
|
|
106
|
-
before_p bigint;
|
|
107
|
-
before_l bigint;
|
|
108
|
-
after_p bigint;
|
|
109
|
-
after_l bigint;
|
|
110
|
-
guessed bigint;
|
|
111
|
-
missed bigint;
|
|
112
|
-
null_p bigint;
|
|
113
|
-
null_l bigint;
|
|
114
|
-
BEGIN
|
|
115
|
-
SELECT p, l INTO before_p, before_l FROM "_6203_inbox_census";
|
|
116
|
-
SELECT count(*) INTO after_p FROM "project_listings";
|
|
117
|
-
SELECT count(*) INTO after_l FROM "leads";
|
|
118
|
-
|
|
119
|
-
-- 1. Nothing lost: row counts unchanged on both tables.
|
|
120
|
-
IF before_p <> after_p OR before_l <> after_l THEN
|
|
121
|
-
RAISE EXCEPTION
|
|
122
|
-
'#6203 post-assertion: row count changed across a purely additive migration (project_listings % -> %, leads % -> %)',
|
|
123
|
-
before_p, after_p, before_l, after_l;
|
|
124
|
-
END IF;
|
|
125
|
-
|
|
126
|
-
-- 2. Nothing guessed: no row's workspace_id was set to a value absent
|
|
127
|
-
-- from "workspaces" (the inner-join backfill above cannot produce this
|
|
128
|
-
-- on its own, but a re-run against a database missing the sentinel row
|
|
129
|
-
-- must never leave a dangling reference).
|
|
130
|
-
SELECT count(*) INTO guessed FROM "project_listings" p
|
|
131
|
-
WHERE p."workspace_id" IS NOT NULL
|
|
132
|
-
AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = p."workspace_id");
|
|
133
|
-
IF guessed <> 0 THEN
|
|
134
|
-
RAISE EXCEPTION
|
|
135
|
-
'#6203 post-assertion: % project_listings row(s) carry a workspace_id absent from workspaces',
|
|
136
|
-
guessed;
|
|
137
|
-
END IF;
|
|
138
|
-
SELECT count(*) INTO guessed FROM "leads" l
|
|
139
|
-
WHERE l."workspace_id" IS NOT NULL
|
|
140
|
-
AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = l."workspace_id");
|
|
141
|
-
IF guessed <> 0 THEN
|
|
142
|
-
RAISE EXCEPTION
|
|
143
|
-
'#6203 post-assertion: % leads row(s) carry a workspace_id absent from workspaces',
|
|
144
|
-
guessed;
|
|
145
|
-
END IF;
|
|
146
|
-
|
|
147
|
-
-- 3. Nothing missed: no row whose site_id names a workspace that actually
|
|
148
|
-
-- exists came out of the backfill with workspace_id still NULL.
|
|
149
|
-
SELECT count(*) INTO missed FROM "project_listings" p
|
|
150
|
-
WHERE p."workspace_id" IS NULL
|
|
151
|
-
AND EXISTS (
|
|
152
|
-
SELECT 1 FROM "workspaces" w
|
|
153
|
-
WHERE w."id" = CASE WHEN p."site_id" = 'default'
|
|
154
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
155
|
-
ELSE p."site_id" END
|
|
156
|
-
);
|
|
157
|
-
IF missed <> 0 THEN
|
|
158
|
-
RAISE EXCEPTION
|
|
159
|
-
'#6203 post-assertion: % project_listings row(s) had a resolvable site_id but came out of the backfill NULL',
|
|
160
|
-
missed;
|
|
161
|
-
END IF;
|
|
162
|
-
SELECT count(*) INTO missed FROM "leads" l
|
|
163
|
-
WHERE l."workspace_id" IS NULL
|
|
164
|
-
AND EXISTS (
|
|
165
|
-
SELECT 1 FROM "workspaces" w
|
|
166
|
-
WHERE w."id" = CASE WHEN l."site_id" = 'default'
|
|
167
|
-
THEN 'wksp_00000000-0000-7000-8000-000000000001'
|
|
168
|
-
ELSE l."site_id" END
|
|
169
|
-
);
|
|
170
|
-
IF missed <> 0 THEN
|
|
171
|
-
RAISE EXCEPTION
|
|
172
|
-
'#6203 post-assertion: % leads row(s) had a resolvable site_id but came out of the backfill NULL',
|
|
173
|
-
missed;
|
|
174
|
-
END IF;
|
|
175
|
-
|
|
176
|
-
-- 4. A NOTICE, never an exception: an unpinned/unseeded deployment leaving
|
|
177
|
-
-- rows NULL is the expected, documented case (spec §1.9).
|
|
178
|
-
SELECT count(*) INTO null_p FROM "project_listings" WHERE "workspace_id" IS NULL;
|
|
179
|
-
SELECT count(*) INTO null_l FROM "leads" WHERE "workspace_id" IS NULL;
|
|
180
|
-
IF null_p <> 0 OR null_l <> 0 THEN
|
|
181
|
-
RAISE NOTICE
|
|
182
|
-
'#6203: % project_listings row(s) and % leads row(s) left workspace_id unset — their site_id did not resolve to a workspace this database holds',
|
|
183
|
-
null_p, null_l;
|
|
184
|
-
END IF;
|
|
185
|
-
END $$;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
-- #6204 — Lead gains a nullable triage stamp.
|
|
2
|
-
--
|
|
3
|
-
-- Operator decision Q1(a) (#6026, 2026-09-18): a message is a lead that needs
|
|
4
|
-
-- an answer — same capture path, same routing, same tenancy, different
|
|
5
|
-
-- follow-up. One nullable column beats a second entity, and
|
|
6
|
-
-- docs/internal/decisions/flow-engine-admin-surface.md AD-4 stays true: the
|
|
7
|
-
-- Inbox is a READ PROJECTION over tables that already hold decidable state,
|
|
8
|
-
-- never a new decision table.
|
|
9
|
-
--
|
|
10
|
-
-- PURELY ADDITIVE (spec NFR1): two ADD COLUMNs, one CREATE INDEX. No UPDATE, no
|
|
11
|
-
-- backfill, no DEFAULT, no NOT NULL — every existing row keeps NULL, which is
|
|
12
|
-
-- exactly "unhandled" (spec D5, AC-1). There is no data transformation here, so
|
|
13
|
-
-- this migration carries none of the census/post-assertion apparatus
|
|
14
|
-
-- 20260919140000_inbox_workspace_key needed to prove ITS backfill correct.
|
|
15
|
-
--
|
|
16
|
-
-- RE-RUNNABLE (spec NFR2): IF NOT EXISTS on every statement, the house style of
|
|
17
|
-
-- 20260919140000_inbox_workspace_key/migration.sql.
|
|
18
|
-
--
|
|
19
|
-
-- ORDER-SAFE (spec NFR2, security finding S-9): the index references
|
|
20
|
-
-- "workspace_id", which 20260919140000_inbox_workspace_key adds — a migration
|
|
21
|
-
-- this one sorts strictly after (20260920 > 20260919), so in the normal case
|
|
22
|
-
-- the guard below is a formality. It stays a guard rather than an assumption:
|
|
23
|
-
-- `IF NOT EXISTS` guards the INDEX's own name, not the column it references,
|
|
24
|
-
-- so an unguarded CREATE INDEX would abort inside prisma migrate's
|
|
25
|
-
-- per-migration transaction and wedge the ledger for the whole deployment if
|
|
26
|
-
-- that migration were ever skipped, renamed, or rolled back on a given
|
|
27
|
-
-- database. The two ADD COLUMNs above it are unconditional because they
|
|
28
|
-
-- reference nothing another migration owns.
|
|
29
|
-
--
|
|
30
|
-
-- CREATE INDEX is NOT CONCURRENT (security finding S-11), and cannot be:
|
|
31
|
-
-- CONCURRENTLY may not run inside prisma migrate's per-migration transaction.
|
|
32
|
-
-- It therefore takes a lock on "leads" for its duration, which blocks the three
|
|
33
|
-
-- anonymous capture routes (a write failure there surfaces as 503
|
|
34
|
-
-- persist_failed, routes/leads/index.ts), and every subsequent insert
|
|
35
|
-
-- pays a fourth index write. Accepted: "leads" carries four indexes already
|
|
36
|
-
-- (siteId, createdAt, email, workspaceId) and is small at this pin. A
|
|
37
|
-
-- deployment with a large leads table should create this index out-of-band
|
|
38
|
-
-- with CONCURRENTLY first — the IF NOT EXISTS guard then makes this
|
|
39
|
-
-- statement a no-op.
|
|
40
|
-
--
|
|
41
|
-
-- Rollback: DROP INDEX "leads_workspace_id_handled_at_idx";
|
|
42
|
-
-- ALTER TABLE "leads" DROP COLUMN "handled_at", DROP COLUMN "handled_by".
|
|
43
|
-
-- Safe ONLY at this pin, and the qualifier is load-bearing: once the Needs-you
|
|
44
|
-
-- section ships, dropping these columns destroys operator triage state. After
|
|
45
|
-
-- that point the rollback is a forward migration, not a DROP.
|
|
46
|
-
|
|
47
|
-
-- AlterTable
|
|
48
|
-
ALTER TABLE "leads" ADD COLUMN IF NOT EXISTS "handled_at" TIMESTAMP(3);
|
|
49
|
-
-- The actor (spec D4, security finding S-7) — the sibling of
|
|
50
|
-
-- project_listings."decided_by". Plain TEXT, no FK:
|
|
51
|
-
-- ADR 0009 D2 forbids a relation crossing from inbox into auth.
|
|
52
|
-
ALTER TABLE "leads" ADD COLUMN IF NOT EXISTS "handled_by" TEXT;
|
|
53
|
-
|
|
54
|
-
-- CreateIndex — the read is
|
|
55
|
-
-- workspace_id = $1 AND handled_at IS NULL AND source = ANY($2)
|
|
56
|
-
-- ordered by created_at desc. A two-column (workspace_id, handled_at) index is
|
|
57
|
-
-- the one that serves it: it narrows to the tenant and then to the unhandled
|
|
58
|
-
-- partition, which is the small side and the side that shrinks as an operator
|
|
59
|
-
-- works the queue. `source` is deliberately NOT a third column (spec D10).
|
|
60
|
-
DO $$ BEGIN
|
|
61
|
-
IF EXISTS (
|
|
62
|
-
SELECT 1 FROM information_schema.columns
|
|
63
|
-
WHERE table_name = 'leads' AND column_name = 'workspace_id'
|
|
64
|
-
) THEN
|
|
65
|
-
CREATE INDEX IF NOT EXISTS "leads_workspace_id_handled_at_idx"
|
|
66
|
-
ON "leads"("workspace_id", "handled_at");
|
|
67
|
-
ELSE
|
|
68
|
-
RAISE NOTICE '#6204: leads.workspace_id absent — index skipped. 20260919140000_inbox_workspace_key has not run against this database; the Needs-you read cannot be served until it does (spec § 10.1).';
|
|
69
|
-
END IF;
|
|
70
|
-
END $$;
|