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
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Inherits root `CLAUDE.md`. This file adds API-specific rules.
|
|
4
4
|
|
|
5
|
+
**The API is packages since #9564 and #9565 (#9563 D8).** `@working-theory/api`
|
|
6
|
+
(`packages/api/`) carries the base routes, services, middleware and stores and
|
|
7
|
+
exports `buildServer({ modules, topology, config })`; the module routes and
|
|
8
|
+
services are the six paid `@working-theory/api-<module>` packages
|
|
9
|
+
(`packages-paid/api-{multi-tenant,workspace,scheduling,signal-collectors,commerce,content-write}/`),
|
|
10
|
+
each exporting one module object whose `readClaim()` reads the licence through
|
|
11
|
+
`checkLicense()`. This app is the boot — `src/server.ts` loads the six objects
|
|
12
|
+
through `src/modules/<name>.ts` (one-statement re-exports) and hands them to
|
|
13
|
+
the package, which registers the rows of the modules `AILK_ENABLED_MODULES`
|
|
14
|
+
selects whose claim answered `claimed` (a selected, unclaimed module's routes
|
|
15
|
+
answer 403 `feature_required`). A `src/…` path cited below resolves under
|
|
16
|
+
`packages/api/src` for a base file and `packages-paid/api-<module>/src` for a
|
|
17
|
+
module file; the registration table (`API_MODULE_TABLE`,
|
|
18
|
+
`packages/api/src/config/modules.ts`) is the one list of which module registers
|
|
19
|
+
what, under which predicate.
|
|
20
|
+
|
|
5
21
|
## Stack
|
|
6
22
|
|
|
7
23
|
- Fastify, TypeScript
|
|
@@ -10,7 +26,7 @@ Inherits root `CLAUDE.md`. This file adds API-specific rules.
|
|
|
10
26
|
|
|
11
27
|
## Deploy path
|
|
12
28
|
|
|
13
|
-
**The supported deploy path is plain `tsc` output, traced and compiled by `@vercel/node`.** `vercel.json` sets `framework: null` and routes every request to `/api/index.ts`; `build:vercel` runs `prisma generate` and
|
|
29
|
+
**The supported deploy path is plain `tsc` output, traced and compiled by `@vercel/node`.** `vercel.json` sets `framework: null` and routes every request to `/api/index.ts`; `build:vercel` runs `prisma generate` and the package builds — `@working-theory/validation`, then `@working-theory/api` (#9564: the package's `dist` must exist before the app's, in the monorepo; a scaffold, which carries no `packages/api`, uses the installed `dist`) — and does NOT compile `apps/api` — Vercel's own builder does, through `nodeFileTrace`, tracing through the workspace link into `packages/api/dist`. That tracer is what decides which files ship.
|
|
14
30
|
|
|
15
31
|
**Bundling `apps/api` is NOT a supported deploy path** (#5334). This is a property of the analysers, not a policy:
|
|
16
32
|
|
|
@@ -20,7 +36,7 @@ Inherits root `CLAUDE.md`. This file adds API-specific rules.
|
|
|
20
36
|
|
|
21
37
|
If a dependency is forcing you toward a bundler (an extensionless relative import in a published `dist`, say), fix the dependency's packaging — don't bundle `apps/api`. If you bundle anyway, `loadSlice()` (`src/lib/slice-load.ts`) makes the failure say so at boot instead of failing mute; it does not make bundling work.
|
|
22
38
|
|
|
23
|
-
Three gates hold this together. Run them from the repo root, with `apps/api/dist`
|
|
39
|
+
Three gates hold this together. Run them from the repo root, with `packages/api/dist` built (`pnpm --filter @working-theory/api build`) and then `apps/api/dist` (`pnpm --filter @working-theory/app-api exec tsc`):
|
|
24
40
|
|
|
25
41
|
```bash
|
|
26
42
|
pnpm slice-reachability:check # every slice specifier is reachable to @vercel/nft
|
|
@@ -36,7 +52,7 @@ Every endpoint here has a 1:1 MCP tool in `apps/mcp`. When you add, change, or r
|
|
|
36
52
|
|
|
37
53
|
The root index sits outside `/v1/*`:
|
|
38
54
|
|
|
39
|
-
- `/` (GET) — the anonymous root index (`src/server.ts`): `{ name, version, description, modules, endpoints }`. `name` and `description` introduce the site from `_site.mdx` (#5307); `endpoints` is derived at request time from the routes this process registered (#5314). **`modules` (#7501)** is the sorted list of optional modules this process registered routes for — `servedModules()` filtering the `OPTIONAL_MODULES` vocabulary (`commerce`, `content-write`, `multi-tenant`, `scheduling`, `signal-collectors`, `workspace`) against `AILK_ENABLED_MODULES`, both in `src/config/modules.ts`. It is computed once in `build()` (at boot), before any module route registers, so it reports what registered rather than re-reading the env per request; filtering to the vocabulary keeps the anonymous index from echoing whatever an operator typed into the env var. `docs` is deliberately absent from the vocabulary: no apps/api route registers under it, so reporting it would break "modules this process registered routes for". `content-write` is reported only alongside `workspace` or `multi-tenant` — the predicate `server.ts` registers the editor's content routes under; it
|
|
55
|
+
- `/` (GET) — the anonymous root index (`src/server.ts`): `{ name, version, description, modules, endpoints }`. `name` and `description` introduce the site from `_site.mdx` (#5307); `endpoints` is derived at request time from the routes this process registered (#5314). **`modules` (#7501)** is the sorted list of optional modules this process registered routes for — `servedModules()` filtering the `OPTIONAL_MODULES` vocabulary (`commerce`, `content-write`, `multi-tenant`, `scheduling`, `signal-collectors`, `workspace`) against `AILK_ENABLED_MODULES`, both in `src/config/modules.ts`. It is computed once in `build()` (at boot), before any module route registers, so it reports what registered rather than re-reading the env per request; filtering to the vocabulary keeps the anonymous index from echoing whatever an operator typed into the env var. `docs` is deliberately absent from the vocabulary: no apps/api route registers under it, so reporting it would break "modules this process registered routes for". `content-write` is reported only alongside `workspace` or `multi-tenant` — the predicate `server.ts` registers the editor's content routes under; it is the `workspaceContentRoutes` row's predicate in `API_MODULE_TABLE`, is repeated in `servedModules()`, and `root-modules.test.ts` (`packages-paid/api-multi-tenant/src/__tests__/`) asserts the two agree. The Admin reads `modules` to hide the surfaces whose module is off; a failed read, or an older API with no `modules` field, hides nothing. No MCP tool mirrors this index.
|
|
40
56
|
|
|
41
57
|
Routes are versioned under `/v1/*`:
|
|
42
58
|
|
|
@@ -52,10 +68,10 @@ Routes are versioned under `/v1/*`:
|
|
|
52
68
|
- `/v1/deliverables/open/:slug` (GET) — an UNGATED file deliverable's bytes, keyed by the flow slug (#8848 D5, `deliverableOpenRoute` in `src/routes/deliverables/index.ts`). Anonymous; the workspace is the caller's (`resolveCallerWorkspace`, keyless through the one-workspace predicate as the flows bullet says; a refused spoke key or an ambiguous deployment `503 tenant_unresolved`), never a request value; no `DATABASE_URL` is `503 deliverables_not_configured`. The flow must be live and carry `deliverable` with `kind: "file"` and `gated: false`; the bytes are its `deliverables` default entry's `Asset`, through `lib/deliverable-asset-store.ts` (a client's file refused), never the legacy store, never a `playbook:` key; served with the grant route's `asset_` headers (`attachment`, pinned extension, `nosniff`, the sandbox CSP, `private, no-store`). Every miss is the file's one uniform 404. Registered in its own encapsulated `deliverableopen` rate-limit scope (60 a minute) with the visitor forward-key digests, and on `FORWARDED_VISITOR_ROUTES`, so a site's proxied reads (`/reports/<slug>/file`) are counted per visitor; the grant route's own budget is unchanged. Module: `signal-collectors`. No MCP twin — a byte download on the grant route's footing, recorded in `tests/parity/README.md`.
|
|
53
69
|
- `/v1/content/{types,search,offerings,pricing,shown-offerings,:type,:type/:slug}` — public content reads. `GET /v1/content/search`, `GET /v1/content/:type` and `GET /v1/content/:type/:slug` are `search_content`'s, `list_pages`' and `read_content`'s twins (#7167, #7168, #7169): each parses its request with the `@working-theory/validation` exports in `content-reads.ts` (`contentSearchQuerySchema`; `contentListParamsSchema` + `contentListQuerySchema`; `contentPageParamsSchema` + `contentPageQuerySchema`) and answers through the shared builder in `@working-theory/content-adapters` the tool also calls (`searchContentItemList`, `listPagesItemList`, `readContentDocument`), so the pair takes the same arguments and returns the same result — the tool adopted the API; the `/v1` wire shapes are unchanged. `GET /v1/content/offerings` (`?type=&locale=`) is `get_offerings`' twin (#7177), answering `{ ok: true, data: { offerings } }` through `listOfferings` in `@working-theory/content-adapters`, the seam the tool also calls. `GET /v1/content/pricing` (`?slug=&locale=`) is `get_pricing`'s twin (#7178), answering `{ ok: true, data: { tiers } | null }` through `getPricingTiers`. `GET /v1/content/shown-offerings` is the `/pricing` page's Offerings read (#8924), answering `{ ok: true, data: { status: "shown", shownSet } | { status: "unconnected" } | { status: "empty" } }` behind the `commerce` claim, refreshing the snapshot on the bounded non-forced path, with no MCP twin. On a connected workspace with a current shown set, `GET /v1/content/pricing` (the `pricing` page) and `GET /v1/content/offerings` (unless `type=service`) answer that set through `readShownOfferings` (exported from `shown-offerings.ts`, the one read all three share, registered with one `shownSetRouteOptions` in `server.ts`) and `@working-theory/content-adapters`' shown-set mapping (`pricingTiersFromShownSet`, `listOfferingsWithShownSet`), with major-unit prices, `interval` and the full `lookupKey` (offerings: the Service pages, then the shown Products in place of the Product pages, each with `prices`); every other case — no connection, no claim, an expired or empty set, a failed read or mapping (one `warn { outcome: "shown_set_failed", errName }`) — answers page content as before; their MCP twins read the same answer through `apiClient.offerings.shown` (#8933).
|
|
54
70
|
- `/v1/auth/tokens`, `/v1/user/{profile,data/:key}` — local auth + user data
|
|
55
|
-
- `/v1/hub-sign-in/codes` (POST), `/v1/hub-sign-in/token` (POST), `/v1/hub-sign-in/revoke` (POST), `/v1/hub-sign-in/me` (GET) — the hub signs in its spokes (#7894, `src/routes/auth/hub-sign-in.ts` over `src/services/hub-sign-in.ts`; spec `docs/specs/7894-the-hub-signs-in-its-spec.md`): an authorization code with PKCE between a spoke (a confidential client whose `OAuthClient.clientId` IS its `Spoke.id`) and this hub. `codes` is in `authScope` and answers a COOKIE session only (a personal `ApiToken` is 403, an agent key 403 at the preHandler, a hub credential 404): it mints a single-use 60 s code only for an email-verified person and a callback in the client's registered `redirectUris` (exact match), 30 a user per 10 minutes. `token` and `revoke` are in their OWN scope outside `authScope` (per-IP `rateLimitMiddleware`, scope `hubsignin`, 60/min; plus a per-client bucket of 120 exchanges a minute keyed on the VERIFIED client): the caller authenticates with `Authorization: Basic clientId:secret` AND `x-ailk-spoke-key`, which must resolve to the unrevoked spoke whose id is that client — either alone is `401 invalid_client`. `token` consumes the code first and exactly once, then checks the client, the callback and the PKCE verifier (`400 invalid_grant` for any failure, the code gone either way) and issues `ailk_hst_…`, a 24 h credential stored only as a SHA-256 digest in `OAuthAuthorization` (one row per person and spoke, so a new sign-in rotates it); it writes no `Session` row and sets no cookie. `revoke` ends a credential or burns an unexpired code of that client (`{ revoked }`, never an error). `me` answers the credential's person, read live, and is reachable only by that credential. **The credential is admitted by one branch of `middleware/auth.ts`** — only beside the same spoke's key, only while its grant is unrevoked and unexpired, its client enabled and its person's email verified, and only on `HUB_SESSION_ROUTES`, an exact list of 26 method + registered-URL strings, never a prefix (#8220): `GET /v1/hub-sign-in/me`, #7877's three member reads, #8169's eight member reads, #8170's six member writes, #8435's two contact routes, #8438's two consent routes, #8765's account-agreements pair and #8515's onboarding read and write; every other route answers `404` with no handler run (the status the account area's `read()` loads as "nothing for this caller"), and the list is extended only by a reviewed edit. **The per-person budget (#8220 D2, SEC-8169-4):** each person at each spoke gets 60 requests and 10 writes a minute and 30 writes an hour (a fixed hour window from the first admitted write, so up to twice that across a window edge), keyed on the VERIFIED spoke and person and charged only after admission (a refused credential or an off-list route never gets a bucket); over budget is `429 rate_limited` with `Retry-After`. The write limits refuse only writes, and a refused request is not counted. Every line the request logger writes for an admitted request after admission carries `spokeId` (never the key or the credential); a helper logging through the module-level `logger` carries no request binding. The server-wide per-IP limiter still counts all of one spoke's members together — a named residual whose follow-up (the visitor forward key on member calls) must land before Phase 3 (#8163) ships to a live spoke. A bearer write skips the origin check, so the cross-site-request protection for the member writes is the spoke's: its server actions that send a sealed credential carry it (Phase 3, #8163; SEC-8220-11). A completed password reset (`apps/web/auth.ts` `onPasswordReset`) revokes every credential of that person. The operator registers a spoke's client with `spokes sign-in-client --id <spoke_id> --redirect-uri <url>` (`bin/spokes.ts`; `--disable` switches it off) — `lib/hub-sign-in-store.ts` is the only file that names `prisma.oAuthClient` and `prisma.oAuthAuthorization`. No MCP twin for any of the four — a credential exchange between a site and its hub is not an agent-facing call (#3591 exception, the `/v1/auth/tokens` footing). The browser step lives on the hub's web app: `apps/web/app/api/auth/hub/authorize/route.ts`.
|
|
71
|
+
- `/v1/hub-sign-in/codes` (POST), `/v1/hub-sign-in/token` (POST), `/v1/hub-sign-in/revoke` (POST), `/v1/hub-sign-in/me` (GET) — the hub signs in its spokes (#7894, `packages-paid/api-hub/src/routes/auth/hub-sign-in.ts` over `packages-paid/api-hub/src/services/hub-sign-in.ts` — `@working-theory/api-hub` since #9571, which owns the spoke registry and the sign-in trio and hands them to `buildServer({ hub })` through `packages/api/src/lib/hub.ts`; spec `docs/specs/7894-the-hub-signs-in-its-spec.md`): an authorization code with PKCE between a spoke (a confidential client whose `OAuthClient.clientId` IS its `Spoke.id`) and this hub. `codes` is in `authScope` and answers a COOKIE session only (a personal `ApiToken` is 403, an agent key 403 at the preHandler, a hub credential 404): it mints a single-use 60 s code only for an email-verified person and a callback in the client's registered `redirectUris` (exact match), 30 a user per 10 minutes. `token` and `revoke` are in their OWN scope outside `authScope` (per-IP `rateLimitMiddleware`, scope `hubsignin`, 60/min; plus a per-client bucket of 120 exchanges a minute keyed on the VERIFIED client): the caller authenticates with `Authorization: Basic clientId:secret` AND `x-ailk-spoke-key`, which must resolve to the unrevoked spoke whose id is that client — either alone is `401 invalid_client`. `token` consumes the code first and exactly once, then checks the client, the callback and the PKCE verifier (`400 invalid_grant` for any failure, the code gone either way) and issues `ailk_hst_…`, a 24 h credential stored only as a SHA-256 digest in `OAuthAuthorization` (one row per person and spoke, so a new sign-in rotates it); it writes no `Session` row and sets no cookie. `revoke` ends a credential or burns an unexpired code of that client (`{ revoked }`, never an error). `me` answers the credential's person, read live, and is reachable only by that credential. **The credential is admitted by one branch of `middleware/auth.ts`** — only beside the same spoke's key, only while its grant is unrevoked and unexpired, its client enabled and its person's email verified, and only on `HUB_SESSION_ROUTES`, an exact list of 26 method + registered-URL strings, never a prefix (#8220): `GET /v1/hub-sign-in/me`, #7877's three member reads, #8169's eight member reads, #8170's six member writes, #8435's two contact routes, #8438's two consent routes, #8765's account-agreements pair and #8515's onboarding read and write; every other route answers `404` with no handler run (the status the account area's `read()` loads as "nothing for this caller"), and the list is extended only by a reviewed edit. **The per-person budget (#8220 D2, SEC-8169-4):** each person at each spoke gets 60 requests and 10 writes a minute and 30 writes an hour (a fixed hour window from the first admitted write, so up to twice that across a window edge), keyed on the VERIFIED spoke and person and charged only after admission (a refused credential or an off-list route never gets a bucket); over budget is `429 rate_limited` with `Retry-After`. The write limits refuse only writes, and a refused request is not counted. Every line the request logger writes for an admitted request after admission carries `spokeId` (never the key or the credential); a helper logging through the module-level `logger` carries no request binding. The server-wide per-IP limiter still counts all of one spoke's members together — a named residual whose follow-up (the visitor forward key on member calls) must land before Phase 3 (#8163) ships to a live spoke. A bearer write skips the origin check, so the cross-site-request protection for the member writes is the spoke's: its server actions that send a sealed credential carry it (Phase 3, #8163; SEC-8220-11). A completed password reset (`apps/web/auth.ts` `onPasswordReset`) revokes every credential of that person. The operator registers a spoke's client with `spokes sign-in-client --id <spoke_id> --redirect-uri <url>` (`packages-paid/api-hub/src/bin/spokes.ts`; `--disable` switches it off) — `packages-paid/api-hub/src/lib/hub-sign-in-store.ts` is the only file that names `prisma.oAuthClient` and `prisma.oAuthAuthorization`. **The `hub` claim (#9571 D4; #9568 D5):** read only inside `@working-theory/api-hub` (`src/claim.ts`), through `checkLicense()`, registry first — with no `DATABASE_URL` or no registered spoke the gate answers `not-serving` and never reads the licence; with a spoke and the claim, `claimed`; with a spoke and no claim, `unclaimed`, and `codes`, `token`, `me` (a verified credential) and `clients/:clientId` answer `403 feature_required` naming `hub`; an unreadable licence is `undetermined`, `503 license_undetermined` with `Retry-After: 300`. `revoke` is never gated; a definite answer is held 300 s, `not-serving` 30 s, `undetermined` never; `spokes register` refuses without the claim (`hub-claim-missing`). Lead capture, the resolver and magnet completion never read it, and a boot without the package answers as an empty registry (the four routes `404`). No MCP twin for any of the four — a credential exchange between a site and its hub is not an agent-facing call (#3591 exception, the `/v1/auth/tokens` footing). The browser step lives on the hub's web app: `apps/web/app/api/auth/hub/authorize/route.ts`.
|
|
56
72
|
- `/v1/checkout/sessions` — start a Stripe checkout. **Two paths (#8931, `src/routes/checkout/sessions.ts` over `src/services/offerings/checkout-authorization.ts`; spec `docs/specs/8931-offerings-checkout-authorization-server-side-spec.md`; contract A1–A11, A13).** The path comes from `resolvePublicWorkspace()` and the workspace's own Organization alone — no body field names either: a resolved workspace whose Organization has a Stripe connection takes the **Offerings path**; a presented `x-ailk-spoke-key` the resolver refuses is `503 { error: "tenant_unresolved" }` with no Stripe call; a corrupt connection, or a database read that fails while choosing, is `502 checkout-unavailable`; everything else (keyless, `ambiguous`, off the database adapter, no Organization, no connection) takes the **env path**, today's code. **The env path is the hub's own-products path only, never a spoke's (#9142):** a spoke sells only through its `apps/web` Offerings buy route, which calls this route only for a lookup key its connected workspace's shown set holds, so a spoke's buy always takes the Offerings path; the spoke's own env-key buy route and Stripe webhook are retired. The Offerings path charges `chargeWriteBudget` scope `offeringscheckout` before the body is read — 10 a minute per `request.ip` and 60 per workspace (`OFFERINGS_CHECKOUT_PER_WORKSPACE_PER_MINUTE`), charged one at a time with the IP first, so a request its IP bucket refuses never spends the workspace's, whatever the process's mode — answering `429 { error: "rate_limited", retryAfterSeconds }` with `Retry-After`. It parses `productCode` (the full lookup key) by the one grammar first, builds the connection's own client (`stripeClientForOrganization`), reads org-enabled ∩ workspace-shown from the rows (`getSellableProductIds`, never the snapshot), looks the key up live on that client, and sells only when the Price's own Product is in the set — and, for a workspace saved per Price (#9070 D5), the Price itself is one of its stored shown Prices (`getSellableProductIds`' `priceIds`; `null` for a row saved per Product) — and `normalizeCatalog` keeps that Price under that key and period. Every miss — ungrammatical, not enabled, not shown, moved, archived, nonexistent — is the one `404 { error: "unknown-product-code" }`, never echoing the key; Stripe, the rows or the connection failing is `502`, never the snapshot or the env key. The session is anonymous (no `customer`, no signed-in buyer, so no `ailk_user_id`), sells the authorized Price id, allows promotion codes, `customer_creation: "always"` in payment mode, and is stamped by the server `metadata` and `subscription_data.metadata` `{ site: <workspaceId>, ailk_offering: <lookup key> }`; it is recorded through `recordCheckoutSession` before the route answers, and a failed record expires it on the same client and answers `502`. **The env path refuses an Offerings product (A9):** `createCheckoutSession` gets `{ refuseProduct: isOfferingsProduct }`, so a Price whose Product carries `metadata.ailk_group` (or whose Product did not expand) is the same `404`. **The Offerings path refuses the hub's own plans (#9338 DD5):** right after the grammar parse, a key whose plan is `ailk-pro` or `ailk-pro-plus` (`isPlanPurchaseKey`, any period) is the same `404`, before any read or Stripe call, for every Organization — such a sale would stamp `ailk_offering`, which no billing path mints a licence for. A hub plan under another slug is not covered (the named residual). The request schema and the `201 { sessionId, url }` body are unchanged, so the `start_purchase` parity row is too. **`GET /v1/checkout/sessions/:id` (A13):** when `DATABASE_URL` is set, a session `readCheckoutSessionRecord` holds is read with that record's Organization's own client, only in the record's mode; its line-item names pass the catalog's T1 `normalizeText`; a record that cannot be read, or a connection gone, unusable or in another mode, is `502`, never the env path; no record is today's env read. Logs carry the workspace or Organization, an 8-character session prefix and a fixed reason only.
|
|
57
73
|
- `/v1/workspaces/:workspaceId/{theme,identity,seo}` (GET/PUT) — per-workspace site config (multi-tenant module). PUT needs `workspace-settings` (owner, admin — `@working-theory/validation`'s permission table (#7636 D2)), checked before the body parse; GET stays open to every member.
|
|
58
|
-
- `/v1/workspaces/:workspaceId/meetings` (GET/PUT) — the operator's enabled-slug gate for meeting types (#6183 S9b, `src/routes/workspaces/meetings.ts`). Beside the three siblings above in URL shape and tenant-isolation posture, but registered in its OWN scope, gated on `isModuleEnabled("scheduling")` ALONE — not the `workspace`/`multi-tenant` scope its siblings share, because its GET reaches `services/scheduling/` (see below), which a scaffold without that module does not carry (
|
|
74
|
+
- `/v1/workspaces/:workspaceId/meetings` (GET/PUT) — the operator's enabled-slug gate for meeting types (#6183 S9b, `src/routes/workspaces/meetings.ts`). Beside the three siblings above in URL shape and tenant-isolation posture, but registered in its OWN scope, gated on `isModuleEnabled("scheduling")` ALONE — not the `workspace`/`multi-tenant` scope its siblings share, because its GET reaches `services/scheduling/` (see below), which a scaffold without that module does not carry (#5701; since #9565 the package boundary — `@working-theory/api-scheduling` owns both files). PUT needs `meeting-types-manage` (owner, admin — `@working-theory/validation`'s permission table (#7636 D2)) because this blob decides which pages the public site serves. GET answers `meeting-types-manage` with the full join and `meeting-types-read` (every role, a member included) with the ENABLED rows only (#7636 D6) — since it does NOT just return the stored blob, it echoes the deployment's whole provider catalog, including the rows the operator chose not to publish (security review, 2026-09-19), which a member never sees. GET is the one segment in this file's family that does NOT just return the stored blob: it ALSO lists the scheduling provider's own bookable event types (`listEventTypes`, #6182), joined with `enabled: boolean`; an unconfigured/unreachable provider answers the same `SchedulingErrorKind` outcome `/v1/schedule/*` uses, distinct from an empty list. **`customerSlugs` (#8171 D5)** — who may book from the member area, independent of `enabledSlugs`: `GET ?include=customers` adds `customer: boolean` per row, and without it the body is unchanged; the PUT carries `customerSlugs`, and a PUT without it keeps the stored list (a read then a write, last-writer-wins), so the public toggle never erases it. `enabledSlugs` keeps its whole-list meaning, so a writer of `customerSlugs` sends the `enabledSlugs` it read. **`include` is a comma list (#9128)** — `?include=customers,private`, in either order, or a repeated `include` key; an unknown value is ignored, so `?include=customers` alone reads as before. **`private` adds `private: boolean` per row** — membership of the stored `privateSlugs` (#8843 D2), the Admin meeting page's Private switch — on the `meeting-types-manage` full join only: a `meeting-types-read` caller never receives it, whatever it asks. The PUT carries `privateSlugs` whole-list and keeps a stored list a body leaves out, as it does `customerSlugs`. An agent's PUT is audited `meetings.set_customers` when it carries `customerSlugs`, else `meetings.set_enabled`. No MCP twin — identity-bound tenant route (#3591 exception).
|
|
59
75
|
- `/v1/workspaces/:workspaceId/meetings/connection` (GET, PUT, DELETE) — the workspace's own scheduling connection (#7533, `src/routes/workspaces/meetings.ts`; data access and the cipher in `src/services/scheduling/connections.ts`, the only file that touches `prisma.schedulingConnection`). Same registration, scope and tenant guard as the meetings route above. GET, PUT and DELETE all need `scheduling-connection` (owner, admin — `@working-theory/validation`'s permission table (#7636 D2), the operator's 2026-10-04 decision overturning SEC-7533-11's owner-only write). GET answers `null` or a summary — `provider`, `baseUrl`, `webhookPath`, `createdAt`, `updatedAt` — with no credential, secret, ciphertext or hint of one. PUT takes a strict `{ provider, apiKey, baseUrl? }`; create and replace are one operation, and every PUT mints a new webhook token and secret, returning the secret this once (`Cache-Control: no-store`) so the operator can paste it into the provider — a replace cuts off the previous entry point. `baseUrl` must be `https://api.cal.com` or the deployment's own `CAL_API_BASE_URL` origin, with no path, query or userinfo (`400 base_url_not_allowed`); only the bare origin is stored. The credential and the secret are AES-256-GCM ciphertext under `SCHEDULING_CREDENTIAL_KEY`, bound to the workspace and the column; with the key unset, PUT answers `503 credential_key_not_configured` before any write. DELETE is idempotent (`{ revoked }`) and unconfigures the workspace at once. Because a connection replaces the account every provider call uses, every successful PUT, and every DELETE that revoked a row, writes one `recordAgentAction` audit row (`scheduling.connection.{connect,replace,remove}`; a PUT is `replace` whenever the workspace was served by any provider before it — its own row, or the env credential on a one-workspace deployment) and emails every OTHER owner of the organization through `@working-theory/email`'s `sendSchedulingConnectionChangedEmail` (#7636 D7). Neither step can fail the write: a failed audit is logged at `error`, and the notify step runs in its own `try`, so the one-time secret is always returned; with no sender address or no Resend key it is skipped with a `warn`. Logs carry the recipient count, never an address. The meetings GET above, and the three anonymous `/v1/schedule/*` provider routes below, resolve their provider through `src/services/scheduling/resolve.ts`: a workspace's own row always wins; `CAL_API_KEY` serves a workspace only on a one-workspace deployment; otherwise `not_configured`. No MCP twin — identity-bound tenant routes (#3591 exception).
|
|
60
76
|
- `/v1/workspaces/:workspaceId/general` (GET/PUT) — the workspace's own display name, the Pro `workspace` module's general-settings surface (#5371). Registered under `isModuleEnabled("workspace") || isModuleEnabled("multi-tenant")` alongside the site-config routes above, behind the same tenant guard; the scope is always `request.tenant.workspaceId`, never the path param (the guard 403s a mismatch before any handler runs). PUT takes a strict `{ name }` body (1–200 chars, trimmed) and answers `{ name }`; GET answers `{ name, emailFrom }` (#7519 D6), where `emailFrom` is the deployment's sender (`deliverableSenderAddress()` — `AILK_DELIVERABLE_EMAIL_FROM ?? AILK_LEAD_NOTIFICATION_FROM`, the resolver the fulfillment service sends with) or `null` when neither is set, so the Admin's magnet wizard can show it read-only. Role floor on PUT: `workspace-settings` (owner, admin — `@working-theory/validation`'s permission table (#7636 D2)) — renaming is an org-level settings write; GET stays open to every member. No MCP twin — identity-bound tenant route (#3591 exception, same shape as `/v1/billing/*`).
|
|
61
77
|
- `/v1/workspaces/:workspaceId/messages` (GET, `?limit=`), `/v1/workspaces/:workspaceId/messages/:leadId` (GET, PATCH) — the Inbox message API (#6216 S7e, `src/routes/workspaces/messages.ts`): the workspace's unhandled messages newest first, the item read (resolves a message handled or not, serving `handledAt`), and the one-way idempotent handled stamp over #6204's `markMessageHandled`. Registered under `isModuleEnabled("workspace") || isModuleEnabled("multi-tenant")`, the same scope as the two routes above. A message is a `Lead` whose `source` is in #6204's closed `MESSAGE_LEAD_SOURCE_LIST` allowlist and that is not an order (#9170, the orders bullet below) — both checked only in `tenantDb(tenant).leads`, never in this file; a non-message lead is a 404 on the item read and the PATCH and absent from the list, the SAME 404 body as an absent id, another workspace's id, or an unbackfilled `workspaceId`. **One served row across all four routes** — `id`, `name`, `email`, `source`, `createdAt`, `handledAt`, `personId`, `body`, `excerpt` — and nothing else (#9349, superseding the 2026-09-19 six-key ruling): `personId` is the lead's Person or `null`; `body` is `payload.message` alone — cut to 16384 code units, line breaks normalized, control, bidi, zero-width and tag characters stripped (`\t` and `\n` kept), trimmed and cut at 4096 code points (`inbox-messages.ts`'s `readMessageBody`), else `null`; `excerpt` is its first line, cut at 140 code points. `payload` itself and every other payload key never cross, and every success body is re-parsed against `inboxMessageSchema` (`inboxMessageListSchema` for the list) — a mismatch is `500 internal_error` logged with issue codes and paths only. `leads-inbox.ts`'s `GET /v1/leads` row is unchanged. PATCH takes `{ "handled": true }` only, `.strict()`; there is no un-handle path (#6204 D4), so `{ "handled": false }` is a 400. The stamp records the actor via `tenant.userId` in `Lead.handledBy` — written, not served. Role floor: `inbox-read` on the GETs and `inbox-act` on PATCH and accept (`@working-theory/validation`'s permission table (#7636 D2) — every role, a member included), the SAME rows `services/inbox-messages.ts` re-checks, checked before any read or write. The role is not self-servable: since #7523 `multi-tenant`'s null-binding fallback grants the default tenant only to the pinned, verified operator, the same check the base `workspace`-only guard applies (see the `AILK_OPERATOR_EMAIL` paragraph below). No anonymous MCP twin — identity-bound tenant routes, and the anonymous `/mcp` carries no caller identity. Each route's twin is a signed-in tool on `/mcp/workspace` (#3591 D13), which forwards the caller's agent key here: `list_inbox_messages` (list), `get_inbox_message` (item read), `mark_inbox_message_done` (PATCH) and `accept_inbox_message` (`POST …/:leadId/accept`).
|
|
@@ -132,8 +148,18 @@ These own every billing-ledger mutation — don't write `UsageEvent`/`UsagePerio
|
|
|
132
148
|
|
|
133
149
|
Stateless, idempotent bins an external scheduler runs (Vercel Cron / GitHub
|
|
134
150
|
Actions cron / plain crontab) — this repo has no queue or in-process timer
|
|
135
|
-
(D1). Dev: `pnpm --filter @working-theory/api <script
|
|
136
|
-
|
|
151
|
+
(D1). Dev: `pnpm --filter @working-theory/api-<module> <script>` for a module
|
|
152
|
+
bin (#9565 D2 — eight are `@working-theory/api-signal-collectors`'s,
|
|
153
|
+
`member-request:match` is `@working-theory/api-workspace`'s; each package's
|
|
154
|
+
`src/bin/` holds its source and its `package.json` the script) and
|
|
155
|
+
`pnpm --filter @working-theory/api <script>` for a base bin (`abandon:sweep`,
|
|
156
|
+
`rate-limit:probe`, `spokes`, `stripe-connections:rekey` — their source is
|
|
157
|
+
`packages/api/src/bin/`, and the app's script of the same name runs the
|
|
158
|
+
installed package's compiled bin, so the one command works in the monorepo
|
|
159
|
+
and in a scaffold, #9564 D9). Production: `node
|
|
160
|
+
packages-paid/api-<module>/dist/bin/<name>.js` for a module bin (each package
|
|
161
|
+
builds `src/` to `dist/`) and `node node_modules/@working-theory/api/dist/bin/<name>.js`
|
|
162
|
+
for a base bin, on whatever cadence the deployment chooses;
|
|
137
163
|
a SEQUENTIAL re-run is always a no-op for already-processed rows, via each
|
|
138
164
|
sweep's own DB constraint (never a caller-side lock).
|
|
139
165
|
|
|
@@ -189,59 +215,59 @@ sweep's own DB constraint (never a caller-side lock).
|
|
|
189
215
|
- Don't put session state in `apps/api`. It's stateless.
|
|
190
216
|
- Don't bundle `apps/api` (esbuild, rollup, webpack). Slice specifiers cannot survive it — see `## Deploy path`.
|
|
191
217
|
|
|
192
|
-
Last updated: 2026-10-20 — Inbox Reply records the message (#9348): the submissions bullet names `personId` on the item read and PATCH, and the appointments bullet names the ninth field — verified against `apps/api/src/services/{inbox-submissions,scheduling/appointments}.ts`, `
|
|
218
|
+
Last updated: 2026-10-20 — Inbox Reply records the message (#9348): the submissions bullet names `personId` on the item read and PATCH, and the appointments bullet names the ninth field — verified against `apps/api/src/services/{inbox-submissions,scheduling/appointments}.ts`, `packages-paid/api-workspace/src/routes/workspaces/submissions.ts`, `packages/api/src/lib/tenant-db.ts` and `packages/validation/src/schemas/{inbox,scheduling}.ts`.
|
|
193
219
|
|
|
194
220
|
Prior: 2026-10-20 — the Inbox message row serves its body and Person (#9349): the messages bullet names the nine-key row, `readMessageBody` and the strict re-parse; the People bullet names `stripeCustomerUrl` and the `billing-read` withholding — verified against `apps/api/src/routes/workspaces/{messages,people}.ts`, `apps/api/src/services/{inbox-messages,person}.ts` and `packages/validation/src/schemas/{inbox,people}.ts`.
|
|
195
221
|
|
|
196
|
-
Prior: 2026-10-20 — the hub's plans become Offerings plans (#9338): the plan-checkout sentence names `ailk-pro_monthly` / `ailk-pro-plus_monthly` and the plan-only `refuseProduct` (DD4), and the checkout bullet adds the Offerings path's hub-plan refusal beside A9 (DD5) — verified against `
|
|
222
|
+
Prior: 2026-10-20 — the hub's plans become Offerings plans (#9338): the plan-checkout sentence names `ailk-pro_monthly` / `ailk-pro-plus_monthly` and the plan-only `refuseProduct` (DD4), and the checkout bullet adds the Offerings path's hub-plan refusal beside A9 (DD5) — verified against `packages-paid/api-multi-tenant/src/routes/billing/plan-checkout.ts`, `apps/api/src/services/offerings/{catalog,checkout-authorization}.ts` and `packages/validation/src/schemas/{billing,offerings}.ts`.
|
|
197
223
|
|
|
198
|
-
Prior: 2026-10-20 — Start trial becomes a hosted Checkout trial (#9342): the trial bullet names the three Stripe calls, the session's parameters, the completed-session guard, the email, the `{ checkout }` answer, the two new refusals and the `{ emailed }` audit summary, and Get and Extend's §3.3 limit — verified against `
|
|
224
|
+
Prior: 2026-10-20 — Start trial becomes a hosted Checkout trial (#9342): the trial bullet names the three Stripe calls, the session's parameters, the completed-session guard, the email, the `{ checkout }` answer, the two new refusals and the `{ emailed }` audit summary, and Get and Extend's §3.3 limit — verified against `packages-paid/api-workspace/src/services/trial.ts`, `packages-paid/api-workspace/src/routes/workspaces/trial.ts`, `packages/stripe-client/src/checkout.ts` and `packages/validation/src/schemas/trial.ts`.
|
|
199
225
|
|
|
200
|
-
Prior: 2026-10-20 — the lead writes become parity twins (#7171, #7172, #7173): the `/v1/leads` bullet names the three registrations, their body exports, the body gate and the unchanged raw-body persistence — verified against `
|
|
226
|
+
Prior: 2026-10-20 — the lead writes become parity twins (#7171, #7172, #7173): the `/v1/leads` bullet names the three registrations, their body exports, the body gate and the unchanged raw-body persistence — verified against `packages/api/src/routes/leads/index.ts`, `packages/validation/src/schemas/leads.ts` and `tests/parity/api-parity/kinds/site.ts`.
|
|
201
227
|
|
|
202
228
|
Prior: 2026-10-20 — the content reads become parity twins (#7167, #7168, #7169): the public content reads bullet names the three routes as `search_content`'s, `list_pages`' and `read_content`'s twins through the validation exports and the shared builders — verified against `apps/api/src/routes/content/{search,list-pages,get-page}.ts`, `packages/content-adapters/src/content-reads.ts` and `tests/parity/api-parity/kinds/site.ts`.
|
|
203
229
|
|
|
204
|
-
Prior: 2026-10-18 — the Inbox orders read (#9170): added the `/v1/workspaces/:workspaceId/orders` bullet and named the order exclusion in the messages bullet — verified against `
|
|
230
|
+
Prior: 2026-10-18 — the Inbox orders read (#9170): added the `/v1/workspaces/:workspaceId/orders` bullet and named the order exclusion in the messages bullet — verified against `packages-paid/api-workspace/src/routes/workspaces/orders.ts`, `packages-paid/api-workspace/src/services/inbox-orders.ts`, `packages/api/src/lib/tenant-db.ts` and `packages/api/src/server.ts`.
|
|
205
231
|
|
|
206
232
|
Prior: 2026-10-18 — the env path is the hub's own-products path only, never a spoke's (#9142): the `/v1/checkout/sessions` bullet and § BYO Stripe say a spoke sells through its Offerings buy route — verified against `src/routes/checkout/sessions.ts` and `apps/web/app/api/offerings/checkout/[lookupKey]/route.ts`.
|
|
207
233
|
|
|
208
|
-
Prior: 2026-10-18 — one keyless-workspace predicate (#9145): the flows bullet names how `resolveCallerWorkspace` resolves a keyless call (the one-workspace predicate, not the alias) and the open deliverable bullet follows it — verified against `
|
|
234
|
+
Prior: 2026-10-18 — one keyless-workspace predicate (#9145): the flows bullet names how `resolveCallerWorkspace` resolves a keyless call (the one-workspace predicate, not the alias) and the open deliverable bullet follows it — verified against `packages/api/src/services/public-site-resolver.ts` and `packages-paid/api-hub/src/services/__tests__/keyless-workspace-predicate.test.ts`.
|
|
209
235
|
|
|
210
|
-
Prior: 2026-10-18 — the meetings-list assembly moves to the service tier (#9129): the meetings-read bullet names `listWorkspaceMeetings` and the member read's handle-less call — verified against `
|
|
236
|
+
Prior: 2026-10-18 — the meetings-list assembly moves to the service tier (#9129): the meetings-read bullet names `listWorkspaceMeetings` and the member read's handle-less call — verified against `packages-paid/api-scheduling/src/services/scheduling/meetings.ts`, `packages-paid/api-scheduling/src/routes/schedule/index.ts` and `packages-paid/api-scheduling/src/routes/member/meeting-types.ts`.
|
|
211
237
|
|
|
212
|
-
Prior: 2026-10-18 — the Admin's Private switch (#9128): the workspace meetings bullet names `include` as a comma list and the manage-tier-only `private` row field — verified against `
|
|
238
|
+
Prior: 2026-10-18 — the Admin's Private switch (#9128): the workspace meetings bullet names `include` as a comma list and the manage-tier-only `private` row field — verified against `packages-paid/api-scheduling/src/routes/workspaces/meetings.ts` and `packages/site-config/src/schema/meetings-schema.ts`.
|
|
213
239
|
|
|
214
|
-
Prior: 2026-10-18 — a job application carries terms acceptance (#9130): the leads bullet names the consent-source set, `isConsentLeadSource`, and the replay comparison's exemption — verified against `
|
|
240
|
+
Prior: 2026-10-18 — a job application carries terms acceptance (#9130): the leads bullet names the consent-source set, `isConsentLeadSource`, and the replay comparison's exemption — verified against `packages/api/src/routes/leads/index.ts`, `packages/api/src/services/lead-routing.ts` and `packages/validation/src/schemas/leads.ts`.
|
|
215
241
|
|
|
216
|
-
Prior: 2026-10-18 — the standard recommender (#9071): the recommender bullet names the `offerings_offer_refused` refusal of an offer carrying `lookupKey`; the magnets bullet names `newsletter` and `recommender` in `slug_fixed`/`slug_reserved` — verified against `
|
|
242
|
+
Prior: 2026-10-18 — the standard recommender (#9071): the recommender bullet names the `offerings_offer_refused` refusal of an offer carrying `lookupKey`; the magnets bullet names `newsletter` and `recommender` in `slug_fixed`/`slug_reserved` — verified against `packages-paid/api-commerce/src/routes/flows/recommender.ts`, `packages-paid/api-signal-collectors/src/routes/magnets/index.ts` and `packages/validation/src/schemas/flow.ts`.
|
|
217
243
|
|
|
218
|
-
Prior: 2026-10-18 — the gated-deliverable shape (#8848): the flows bullet names the `deliverable` block, its served projection and the completion key; added the `/v1/deliverables/open/:slug` bullet — verified against `apps/api/src/services/{flow-engine,deliverable-fulfillment}.ts`, `apps/api/src/routes/{flows,deliverables}/index.ts`, `
|
|
244
|
+
Prior: 2026-10-18 — the gated-deliverable shape (#8848): the flows bullet names the `deliverable` block, its served projection and the completion key; added the `/v1/deliverables/open/:slug` bullet — verified against `apps/api/src/services/{flow-engine,deliverable-fulfillment}.ts`, `apps/api/src/routes/{flows,deliverables}/index.ts`, `packages/api/src/middleware/rate-limit.ts` and `packages/api/src/server.ts`.
|
|
219
245
|
|
|
220
|
-
Prior: 2026-10-18 — the Billing tab's plan card (#9067): the `/v1/billing` bullet names `?include=billing`, `details`, its `billing-manage` gate and cache, `POST /v1/billing/plan-checkout` with its check order, and the webhook's proof-based Organization binding — verified against `apps/api/src/routes/billing/{read,plan-checkout,index}.ts`, `
|
|
246
|
+
Prior: 2026-10-18 — the Billing tab's plan card (#9067): the `/v1/billing` bullet names `?include=billing`, `details`, its `billing-manage` gate and cache, `POST /v1/billing/plan-checkout` with its check order, and the webhook's proof-based Organization binding — verified against `apps/api/src/routes/billing/{read,plan-checkout,index}.ts`, `packages/api/src/services/entitlements.ts`, `packages/api/src/lib/stripe.ts` and `packages/api/src/routes/webhooks/stripe.ts`.
|
|
221
247
|
|
|
222
|
-
Prior: 2026-10-18 — the workspace shown set per Stripe Price (#9070): the `/v1/workspaces/:workspaceId/offerings` bullet names the one-Price body, the `shown` answer, `price_not_offered`, the Recommended kept or cleared and never set, the Price-counted audit and `shownPriceIdsFor`; the `/v1/checkout/sessions` bullet adds the shown-Price check — verified against `
|
|
248
|
+
Prior: 2026-10-18 — the workspace shown set per Stripe Price (#9070): the `/v1/workspaces/:workspaceId/offerings` bullet names the one-Price body, the `shown` answer, `price_not_offered`, the Recommended kept or cleared and never set, the Price-counted audit and `shownPriceIdsFor`; the `/v1/checkout/sessions` bullet adds the shown-Price check — verified against `packages-paid/api-multi-tenant/src/routes/workspaces/offerings.ts`, `apps/api/src/services/offerings/{index,store,checkout-authorization}.ts` and `packages/validation/src/schemas/offerings.ts`.
|
|
223
249
|
|
|
224
|
-
Prior: 2026-10-18 — the in-page form shape (#8847): the magnets bullet names the Newsletter seed, its migration and the `newsletter` slug in `slug_fixed`/`slug_reserved`; the definition bullet adds `unsubscribe` to the seedable keys and the reserved slugs — verified against `
|
|
250
|
+
Prior: 2026-10-18 — the in-page form shape (#8847): the magnets bullet names the Newsletter seed, its migration and the `newsletter` slug in `slug_fixed`/`slug_reserved`; the definition bullet adds `unsubscribe` to the seedable keys and the reserved slugs — verified against `packages/api/src/lib/tenant-db.ts`, `apps/api/src/routes/{workspaces,magnets}/index.ts`, `packages-paid/api-signal-collectors/src/services/flow-engine.ts` and `database/migrations/20261022120000_in_page_magnets/migration.sql`.
|
|
225
251
|
|
|
226
|
-
Prior: 2026-10-18 — the booking pages (#8843 PR B): the meeting-types bullet no longer names a web-side env fallback, since the pages read `/v1/schedule/meetings` — verified against `apps/web/app/[locale]/meet/` and `
|
|
252
|
+
Prior: 2026-10-18 — the booking pages (#8843 PR B): the meeting-types bullet no longer names a web-side env fallback, since the pages read `/v1/schedule/meetings` — verified against `apps/web/app/[locale]/meet/` and `packages-paid/api-scheduling/src/routes/schedule/meeting-types.ts`.
|
|
227
253
|
|
|
228
|
-
Prior: 2026-10-18 — a default full-screen magnet (#8840): the Job application bullet names the two-step seed, the apply walks, `payload.name` and the hidden value's slug rule; the definition bullet adds `ending` and `landing` to the seedable keys — verified against `
|
|
254
|
+
Prior: 2026-10-18 — a default full-screen magnet (#8840): the Job application bullet names the two-step seed, the apply walks, `payload.name` and the hidden value's slug rule; the definition bullet adds `ending` and `landing` to the seedable keys — verified against `packages-paid/api-signal-collectors/src/services/flow-engine.ts` and `packages/validation/src/schemas/flow.ts`.
|
|
229
255
|
|
|
230
|
-
Prior: 2026-10-18 — the Offerings checkout authorization (#8931): the `/v1/checkout/sessions` bullet names the two paths, the `404`/`502`/`503`/`429` rules, the stamped and recorded session, A9's env-path refusal and A13's session read; BYO Stripe names the env key's Products: Read need — verified against `apps/api/src/routes/checkout/{sessions,session}.ts`, `
|
|
256
|
+
Prior: 2026-10-18 — the Offerings checkout authorization (#8931): the `/v1/checkout/sessions` bullet names the two paths, the `404`/`502`/`503`/`429` rules, the stamped and recorded session, A9's env-path refusal and A13's session read; BYO Stripe names the env key's Products: Read need — verified against `apps/api/src/routes/checkout/{sessions,session}.ts`, `packages/api/src/services/offerings/checkout-authorization.ts` and `packages/stripe-client/src/checkout.ts`.
|
|
231
257
|
|
|
232
|
-
Prior: 2026-10-18 — the agent pricing tools read the Offerings shown set (#8933): the public content reads bullet names the twins' shown-set branch, `readShownOfferings`, the shared mapping, the shared route options and the content fallback — verified against `apps/api/src/routes/content/{pricing,offerings,shown-offerings}.ts`, `
|
|
258
|
+
Prior: 2026-10-18 — the agent pricing tools read the Offerings shown set (#8933): the public content reads bullet names the twins' shown-set branch, `readShownOfferings`, the shared mapping, the shared route options and the content fallback — verified against `apps/api/src/routes/content/{pricing,offerings,shown-offerings}.ts`, `packages/api/src/server.ts` and `packages/content-adapters/src/shown-offerings.ts`. Also 2026-10-18 — the workspace Offerings routes (#8926): added the `/v1/workspaces/:workspaceId/offerings` bullet (the availability answers, the enabled catalog with the shown set and Recommended, the PUT's check order, the write budget, the selection rules, the store writer, the audit row and the forced refresh) — verified against `packages-paid/api-multi-tenant/src/routes/workspaces/offerings.ts`, `packages/api/src/services/offerings/store.ts`, `packages/api/src/server.ts` and `architecture.yaml`.
|
|
233
259
|
|
|
234
|
-
Prior: 2026-10-18 — the per-Organization Stripe webhook (#8932): added the `POST /v1/webhooks/stripe/:connectionToken` bullet (the token, the key-less verify, the record attribution, the Price check, the one lead on the full key held for a live session, the legacy `offerings_path` step-aside, the dedup split, the log and path redaction) and named the Offerings webhook as the marker's second writer in the member reads bullet — verified against `apps/api/src/routes/webhooks/{stripe-offerings,stripe,index}.ts`, `
|
|
260
|
+
Prior: 2026-10-18 — the per-Organization Stripe webhook (#8932): added the `POST /v1/webhooks/stripe/:connectionToken` bullet (the token, the key-less verify, the record attribution, the Price check, the one lead on the full key held for a live session, the legacy `offerings_path` step-aside, the dedup split, the log and path redaction) and named the Offerings webhook as the marker's second writer in the member reads bullet — verified against `apps/api/src/routes/webhooks/{stripe-offerings,stripe,index}.ts`, `packages/api/src/services/lead-routing.ts`, `apps/api/src/lib/{webhook-idempotency,error-serializer}.ts` and `packages/stripe-client/src/webhook.ts`.
|
|
235
261
|
|
|
236
|
-
Prior: 2026-10-18 — the may-sell product list (#8922): added the `/v1/billing/stripe-products` bullet (the two routes, their permissions and check order, the entry shape, the refusals, the store writer, the catalog source and its bounds, the audit row) — verified against `
|
|
262
|
+
Prior: 2026-10-18 — the may-sell product list (#8922): added the `/v1/billing/stripe-products` bullet (the two routes, their permissions and check order, the entry shape, the refusals, the store writer, the catalog source and its bounds, the audit row) — verified against `packages-paid/api-multi-tenant/src/routes/billing/stripe-products.ts`, `apps/api/src/services/offerings/{index,store,source}.ts` and `architecture.yaml`.
|
|
237
263
|
|
|
238
|
-
Prior: 2026-10-18 — the Organization Stripe connection (#8907): added the `/v1/billing/stripe-connection` bullet (the six routes, their permissions and check order, the cipher and its one file, the probes, the write budget, the audit and email, the rekey bin) and the `/v1/billing` read's `commerce` field — verified against `apps/api/src/routes/billing/{stripe-connection,read}.ts`, `
|
|
264
|
+
Prior: 2026-10-18 — the Organization Stripe connection (#8907): added the `/v1/billing/stripe-connection` bullet (the six routes, their permissions and check order, the cipher and its one file, the probes, the write budget, the audit and email, the rekey bin) and the `/v1/billing` read's `commerce` field — verified against `apps/api/src/routes/billing/{stripe-connection,read}.ts`, `packages/api/src/services/offerings/connection.ts`, `packages/api/src/lib/stripe.ts` and `packages/api/src/middleware/rate-limit.ts`.
|
|
239
265
|
|
|
240
|
-
Prior: 2026-10-18 — the plan offerings re-sourced from the shown set (#8934): the member-area bullet's `GET …/plan-offerings` sentence names both sources, the claim gate and the failed reads — verified against `
|
|
266
|
+
Prior: 2026-10-18 — the plan offerings re-sourced from the shown set (#8934): the member-area bullet's `GET …/plan-offerings` sentence names both sources, the claim gate and the failed reads — verified against `packages/api/src/services/plan-offerings.ts` and `packages-paid/api-workspace/src/routes/workspaces/plan.ts`.
|
|
241
267
|
|
|
242
|
-
Prior: 2026-10-18 — the anonymous `/pricing` read (#8924): the public content reads bullet names `shown-offerings`, its three answers, the `commerce` claim, the bounded refresh and its missing MCP twin — verified against `apps/api/src/routes/content/{index,shown-offerings}.ts`, `
|
|
268
|
+
Prior: 2026-10-18 — the anonymous `/pricing` read (#8924): the public content reads bullet names `shown-offerings`, its three answers, the `commerce` claim, the bounded refresh and its missing MCP twin — verified against `apps/api/src/routes/content/{index,shown-offerings}.ts`, `packages/api/src/server.ts` and `packages/api/src/services/offerings/index.ts`.
|
|
243
269
|
|
|
244
|
-
Prior: 2026-10-18 — the meetings read (#8843 PR A): added the `/v1/schedule/meetings` bullet (the Live set, the row order, the bookable rule, `privateSlugs` and its unlisted-not-secret meaning, the re-parse, the `list_meetings` twin) — verified against `
|
|
270
|
+
Prior: 2026-10-18 — the meetings read (#8843 PR A): added the `/v1/schedule/meetings` bullet (the Live set, the row order, the bookable rule, `privateSlugs` and its unlisted-not-secret meaning, the re-parse, the `list_meetings` twin) — verified against `packages-paid/api-scheduling/src/routes/schedule/index.ts`, `apps/api/src/lib/{public-meetings-config,tenant-db}.ts` and `packages/site-config/src/schema/meetings-schema.ts`.
|
|
245
271
|
|
|
246
272
|
Prior: 2026-10-18 — an HTTP twin for `get_pricing` (#7178): the public content reads bullet names `pricing`, and `GET /v1/content/pricing` as the tool's twin through `getPricingTiers` — verified against `apps/api/src/routes/content/{index,pricing}.ts` and `packages/content-adapters/src/pricing.ts`.
|
|
247
273
|
|
|
@@ -249,100 +275,100 @@ Prior: 2026-10-18 — an HTTP twin for `get_offerings` (#7177): the public conte
|
|
|
249
275
|
|
|
250
276
|
Prior: 2026-10-16 — the checkout plan is held until a verified first link (#8793): the member-area bullet's checkout plan rule names the hold, the release in `resolveMemberPersonId` on a fresh `stamped` link, the agreement rule and the guarded write — verified against `apps/api/src/services/{lead-routing,person}.ts`.
|
|
251
277
|
|
|
252
|
-
Prior: 2026-10-16 — a magnet rename sets unclaimed leads aside (#8734): the magnets bullet names the holder read first, the `unclaimed:` re-stamp in place of the lead refusal, the race's `409 conflict` and the audit summary's `unclaimed` — verified against `
|
|
278
|
+
Prior: 2026-10-16 — a magnet rename sets unclaimed leads aside (#8734): the magnets bullet names the holder read first, the `unclaimed:` re-stamp in place of the lead refusal, the race's `409 conflict` and the audit summary's `unclaimed` — verified against `packages/api/src/lib/tenant-db.ts` and `packages-paid/api-signal-collectors/src/routes/magnets/index.ts`.
|
|
253
279
|
|
|
254
|
-
Prior: 2026-10-16 — the sign-up evidence read is budgeted per site key (#8805): the consent bullet names the per-key bucket, its default and override, and the server-wide ceiling's exemption — verified against `
|
|
280
|
+
Prior: 2026-10-16 — the sign-up evidence read is budgeted per site key (#8805): the consent bullet names the per-key bucket, its default and override, and the server-wide ceiling's exemption — verified against `packages/api/src/middleware/rate-limit.ts` and `packages/api/src/server.ts`.
|
|
255
281
|
|
|
256
282
|
Prior: 2026-10-16 — a spoke purchase's checkout grant (#8778): the consent bullet names the spokeCheckout write point's grant from `payload.termsAccepted` — verified against `apps/api/src/services/{lead-routing,consent}.ts`.
|
|
257
283
|
|
|
258
|
-
Prior: 2026-10-16 — the checkout grant (#8765 PR B): the consent bullet names `checkout` as the eighth, unverified source and the webhook's grant (`recordCheckoutConsent`, `checkoutBuyerPerson`, the stamp and outcome rules); BYO Stripe names the terms box and its Dashboard precondition — verified against `
|
|
284
|
+
Prior: 2026-10-16 — the checkout grant (#8765 PR B): the consent bullet names `checkout` as the eighth, unverified source and the webhook's grant (`recordCheckoutConsent`, `checkoutBuyerPerson`, the stamp and outcome rules); BYO Stripe names the terms box and its Dashboard precondition — verified against `packages/api/src/routes/webhooks/stripe.ts`, `apps/api/src/services/{consent,checkout-buyer}.ts`, `packages/api/src/lib/consent-store.ts` and `database/migrations/20261019120000_consent_source_checkout/migration.sql`.
|
|
259
285
|
|
|
260
|
-
Prior: 2026-10-15 — sign-up refuses without the agreements (#8765): the consent bullet names the evidence route, the account-agreements pair, the second ledger writer (the web sign-up hook), the account-level `user`-subject reading of a sign-up agreement and the member pair's change; `HUB_SESSION_ROUTES` is 26 entries — verified against `
|
|
286
|
+
Prior: 2026-10-15 — sign-up refuses without the agreements (#8765): the consent bullet names the evidence route, the account-agreements pair, the second ledger writer (the web sign-up hook), the account-level `user`-subject reading of a sign-up agreement and the member pair's change; `HUB_SESSION_ROUTES` is 26 entries — verified against `packages/api/src/routes/consent/account.ts`, `packages-paid/api-workspace/src/routes/member/consents.ts`, `packages/api/src/middleware/auth.ts` and `packages/api/src/server.ts`.
|
|
261
287
|
|
|
262
|
-
Prior: 2026-10-15 — a per-magnet completion setting (#8757): the leads bullet names the `next` key and the invite email; the magnets bullet names the completion pair, its PATCH rules and refusals — verified against `apps/api/src/routes/{leads,magnets}/index.ts`, `apps/api/src/services/{magnet-completion,plan-offerings}.ts` and `
|
|
288
|
+
Prior: 2026-10-15 — a per-magnet completion setting (#8757): the leads bullet names the `next` key and the invite email; the magnets bullet names the completion pair, its PATCH rules and refusals — verified against `apps/api/src/routes/{leads,magnets}/index.ts`, `apps/api/src/services/{magnet-completion,plan-offerings}.ts` and `packages/api/src/lib/flow-completion-store.ts`.
|
|
263
289
|
|
|
264
|
-
Prior: 2026-10-14 — the Contact form magnet (#8661): the magnets bullet names the Contact form seed, its callers and the `contact` slug's place in `slug_fixed`/`slug_reserved`; the definition bullet names the `contact` refusal and the `upsertFlowDefinition` guard — verified against `
|
|
290
|
+
Prior: 2026-10-14 — the Contact form magnet (#8661): the magnets bullet names the Contact form seed, its callers and the `contact` slug's place in `slug_fixed`/`slug_reserved`; the definition bullet names the `contact` refusal and the `upsertFlowDefinition` guard — verified against `packages/api/src/lib/tenant-db.ts`, `apps/api/src/routes/{workspaces,magnets}/index.ts`, `apps/api/src/services/{flow-engine,project-listings}.ts` and `packages-paid/api-signal-collectors/src/bin/seed-presets.ts`.
|
|
265
291
|
|
|
266
|
-
Prior: 2026-10-14 — magnets editable after creation (#8641): the magnets bullet names the slug rename, its four `409` refusals and its audit row in place of the old immutable-slug refusal — verified against `
|
|
292
|
+
Prior: 2026-10-14 — magnets editable after creation (#8641): the magnets bullet names the slug rename, its four `409` refusals and its audit row in place of the old immutable-slug refusal — verified against `packages-paid/api-signal-collectors/src/routes/magnets/index.ts` and `packages/api/src/lib/tenant-db.ts`.
|
|
267
293
|
|
|
268
|
-
Prior: 2026-10-14 — the standard client onboarding diagnostic (#8515): the magnets bullet names the standard onboarding seed and its two callers; the flow-definition seed refuses the `onboarding` slug; the member-area bullet names Start onboarding and the outbound action; the member reads and writes bullets name the onboarding pair and the message `action`; `HUB_SESSION_ROUTES` is 24 entries — verified against `apps/api/src/lib/{tenant-db,member-store}.ts`, `apps/api/src/routes/{member/onboarding,workspaces/messages-out,workspaces/index}.ts`, `
|
|
294
|
+
Prior: 2026-10-14 — the standard client onboarding diagnostic (#8515): the magnets bullet names the standard onboarding seed and its two callers; the flow-definition seed refuses the `onboarding` slug; the member-area bullet names Start onboarding and the outbound action; the member reads and writes bullets name the onboarding pair and the message `action`; `HUB_SESSION_ROUTES` is 24 entries — verified against `apps/api/src/lib/{tenant-db,member-store}.ts`, `apps/api/src/routes/{member/onboarding,workspaces/messages-out,workspaces/index}.ts`, `packages-paid/api-signal-collectors/src/services/flow-engine.ts`, `packages-paid/api-signal-collectors/src/bin/seed-presets.ts` and `packages/api/src/middleware/auth.ts`.
|
|
269
295
|
|
|
270
|
-
Prior: 2026-10-14 — proposals as an artifact kind (#8516): the member-area bullet names the piece `kind`, the proposals pair, its two MCP twins and the money gate (C4); the member-writes bullet names a proposal's artifact keys, the client review audit row and the pay route with its same-site and sandbox rule; the BYO Stripe bullet names the webhook's proposal stamp — verified against `
|
|
296
|
+
Prior: 2026-10-14 — proposals as an artifact kind (#8516): the member-area bullet names the piece `kind`, the proposals pair, its two MCP twins and the money gate (C4); the member-writes bullet names a proposal's artifact keys, the client review audit row and the pay route with its same-site and sandbox rule; the BYO Stripe bullet names the webhook's proposal stamp — verified against `packages-paid/api-workspace/src/routes/workspaces/pieces.ts`, `apps/api/src/routes/member/{artifacts,artifact-review}.ts`, `packages-paid/api-workspace/src/services/proposal-payments.ts`, `apps/api/src/lib/{tenant-db,member-store,proposal-paid-store}.ts` and `packages/api/src/routes/webhooks/stripe.ts`.
|
|
271
297
|
|
|
272
|
-
Prior: 2026-10-13 — the Person record redesign (#8514): the People bullet names members on the three client rows, the agent-withheld rule and its fail-closed marker, and the private notes pair with its two MCP twins; the member-area bullet's floors name members — verified against `packages/validation/src/schemas/permissions.ts`, `
|
|
298
|
+
Prior: 2026-10-13 — the Person record redesign (#8514): the People bullet names members on the three client rows, the agent-withheld rule and its fail-closed marker, and the private notes pair with its two MCP twins; the member-area bullet's floors name members — verified against `packages/validation/src/schemas/permissions.ts`, `packages/api/src/services/default-tenant.ts`, `apps/api/src/middleware/{tenant,default-tenant}.ts`, `apps/api/src/routes/workspaces/{people,plan}.ts` and `packages/api/src/lib/tenant-db.ts`. Before that, 2026-10-13 — the contact details on the client record (#8435): the People bullet names the contact columns, the capture fill rule, the operator PATCH and the subscriber/callback links; the member-writes bullet names the two contact routes; `HUB_SESSION_ROUTES` is 20 entries. Before that, 2026-10-11 — the spoke member admission (#8220): the hub-sign-in bullet names `HUB_SESSION_ROUTES`' 18 entries by group, the per-(person, spoke) budget, the `spokeId` log binding, the per-IP residual and the spoke's cross-site-request obligation; the #8169 and #8170 bullets say their routes are admitted, and the #8169 bullet states the one-owner-per-organization precondition — verified against `packages/api/src/middleware/auth.ts`, `packages/api/src/lib/member-store.ts`, `packages-paid/api-workspace/src/routes/member/billing.ts` and `packages-paid/api-multi-tenant/src/__tests__/member-isolation.test.ts`.
|
|
273
299
|
|
|
274
|
-
Prior: 2026-10-12 — the hub's restricted live Stripe key (#8450): the BYO Stripe bullet on the boot admission, the admitted-key guard on the portal session and the checkout start and read, the three routes that stay sandbox-only, the live-only checkout bucket and the webhook's livemode drop — verified against `
|
|
300
|
+
Prior: 2026-10-12 — the hub's restricted live Stripe key (#8450): the BYO Stripe bullet on the boot admission, the admitted-key guard on the portal session and the checkout start and read, the three routes that stay sandbox-only, the live-only checkout bucket and the webhook's livemode drop — verified against `packages/api/src/lib/stripe.ts`, `packages/api/src/server.ts`, `apps/api/src/routes/{subscription/portal,checkout/sessions,checkout/session,webhooks/stripe}.ts` and `architecture.yaml`.
|
|
275
301
|
|
|
276
|
-
Prior: 2026-10-11 — the spoke member admission (#8220): the hub-sign-in bullet names `HUB_SESSION_ROUTES`' 18 entries by group, the per-(person, spoke) budget, the `spokeId` log binding, the per-IP residual and the spoke's cross-site-request obligation; the #8169 and #8170 bullets say their routes are admitted, and the #8169 bullet states the one-owner-per-organization precondition — verified against `
|
|
302
|
+
Prior: 2026-10-11 — the spoke member admission (#8220): the hub-sign-in bullet names `HUB_SESSION_ROUTES`' 18 entries by group, the per-(person, spoke) budget, the `spokeId` log binding, the per-IP residual and the spoke's cross-site-request obligation; the #8169 and #8170 bullets say their routes are admitted, and the #8169 bullet states the one-owner-per-organization precondition — verified against `packages/api/src/middleware/auth.ts`, `packages/api/src/lib/member-store.ts`, `packages-paid/api-workspace/src/routes/member/billing.ts` and `packages-paid/api-multi-tenant/src/__tests__/member-isolation.test.ts`.
|
|
277
303
|
|
|
278
|
-
Prior: 2026-10-11 — the provider routes (#8171): added the bullet for the Requests read and Deliver a version, and extended the People (`request` and `version` timeline kinds), meetings (`customerSlugs`), magnets (`customersEnabled`), deliverables and asset-store (a version's file) bullets and added a `/v1/subscription/me` note (`?include=billing`) — verified against `apps/api/src/routes/workspaces/{requests,pieces,meetings}.ts`, `
|
|
304
|
+
Prior: 2026-10-11 — the provider routes (#8171): added the bullet for the Requests read and Deliver a version, and extended the People (`request` and `version` timeline kinds), meetings (`customerSlugs`), magnets (`customersEnabled`), deliverables and asset-store (a version's file) bullets and added a `/v1/subscription/me` note (`?include=billing`) — verified against `apps/api/src/routes/workspaces/{requests,pieces,meetings}.ts`, `packages-paid/api-signal-collectors/src/routes/magnets/index.ts`, `packages-paid/api-commerce/src/routes/subscription/me.ts`, `packages-paid/api-commerce/src/services/subscription.ts` and `apps/api/src/lib/{tenant-db,asset-store,deliverable-asset-store,stripe}.ts`.
|
|
279
305
|
|
|
280
|
-
Prior: 2026-10-11 — the Phase 1 member writes (#8170): added the bullet for the six `/v1/member/*` writes (the inbound Interaction rows, the hourly cap under the Person lock, the review under the Person then piece lock, the booking bound, the deletion destination, the log rule) — verified against `apps/api/src/routes/member/*.ts`, `
|
|
306
|
+
Prior: 2026-10-11 — the Phase 1 member writes (#8170): added the bullet for the six `/v1/member/*` writes (the inbound Interaction rows, the hourly cap under the Person lock, the review under the Person then piece lock, the booking bound, the deletion destination, the log rule) — verified against `apps/api/src/routes/member/*.ts`, `packages/api/src/lib/member-store.ts`, `packages/api/src/services/member-area.ts` and `packages/api/src/server.ts`.
|
|
281
307
|
|
|
282
|
-
Prior: 2026-10-11 — the root index's `modules` report (#7546, after #7501): added the `/` (GET) bullet at the head of Endpoint conventions — the field, its `servedModules()` / `OPTIONAL_MODULES` source, `docs` absent from the vocabulary, `content-write` reported only alongside `workspace` or `multi-tenant` — verified against `
|
|
308
|
+
Prior: 2026-10-11 — the root index's `modules` report (#7546, after #7501): added the `/` (GET) bullet at the head of Endpoint conventions — the field, its `servedModules()` / `OPTIONAL_MODULES` source, `docs` absent from the vocabulary, `content-write` reported only alongside `workspace` or `multi-tenant` — verified against `packages/api/src/config/modules.ts`, `packages/api/src/server.ts` and `packages-paid/api-multi-tenant/src/__tests__/root-modules.test.ts`.
|
|
283
309
|
|
|
284
|
-
Prior: 2026-10-10 — the Phase 1 member reads (#8169): added the bullet for the eight `/v1/member/*` GETs (the organization grain of purchases and billing, the hub-credential-only billing stance, the scheduling-gated pair) — verified against `apps/api/src/routes/member/*.ts`, `
|
|
310
|
+
Prior: 2026-10-10 — the Phase 1 member reads (#8169): added the bullet for the eight `/v1/member/*` GETs (the organization grain of purchases and billing, the hub-credential-only billing stance, the scheduling-gated pair) — verified against `apps/api/src/routes/member/*.ts`, `packages/api/src/lib/member-store.ts` and `packages/api/src/server.ts`.
|
|
285
311
|
|
|
286
312
|
Prior: 2026-10-09 — an agent key may be granted `member-pieces-manage` (#7878): the member-area bullet's four writes read it through `allows`, and the agent-seat bullet lists seven grantable permissions — verified against `apps/api/src/routes/workspaces/{pieces,plan,messages-out}.ts` and `packages/validation/src/schemas/permissions.ts`.
|
|
287
313
|
|
|
288
|
-
Prior: 2026-10-09 — a spoke's schedule calls bind the event owner to its workspace (#7944): the `/v1/schedule/*` bullet gains the spoke gate and the accepted shared MCP schedule budget — verified against `
|
|
314
|
+
Prior: 2026-10-09 — a spoke's schedule calls bind the event owner to its workspace (#7944): the `/v1/schedule/*` bullet gains the spoke gate and the accepted shared MCP schedule budget — verified against `packages-paid/api-scheduling/src/routes/schedule/index.ts`, `apps/api/src/services/scheduling/{types,cal-provider}.ts` and `packages/api/src/middleware/rate-limit.ts`.
|
|
289
315
|
|
|
290
|
-
Prior: 2026-10-09 — the member area contract (#7877): added the operator-route bullet (pieces, piece stages, plan, outbound messages; `people-write`; the bounds, refusals and audit; the checkout plan rule; the no-twin reason) and the member-read bullet (`/v1/member/*`, the caller seam, the refusals, the never-served fields, the file headers), the timeline's `message` kind on the People bullet, and the private-asset rule on the asset-store bullet — verified against `apps/api/src/routes/{workspaces/{pieces,plan,messages-out},member/{pieces,messages}}.ts`, `apps/api/src/services/{member-area,person,lead-routing}.ts`, `apps/api/src/lib/{member-store,tenant-db,asset-store}.ts` and `
|
|
316
|
+
Prior: 2026-10-09 — the member area contract (#7877): added the operator-route bullet (pieces, piece stages, plan, outbound messages; `people-write`; the bounds, refusals and audit; the checkout plan rule; the no-twin reason) and the member-read bullet (`/v1/member/*`, the caller seam, the refusals, the never-served fields, the file headers), the timeline's `message` kind on the People bullet, and the private-asset rule on the asset-store bullet — verified against `apps/api/src/routes/{workspaces/{pieces,plan,messages-out},member/{pieces,messages}}.ts`, `apps/api/src/services/{member-area,person,lead-routing}.ts`, `apps/api/src/lib/{member-store,tenant-db,asset-store}.ts` and `packages/api/src/middleware/auth.ts`.
|
|
291
317
|
|
|
292
|
-
Prior: 2026-10-09 — Person, the per-workspace client record (#852): added the `/v1/workspaces/:workspaceId/people` bullet (the two reads, `people-read`, the grant, the bounds, the no-twin reason, the never-served columns) — verified against `
|
|
318
|
+
Prior: 2026-10-09 — Person, the per-workspace client record (#852): added the `/v1/workspaces/:workspaceId/people` bullet (the two reads, `people-read`, the grant, the bounds, the no-twin reason, the never-served columns) — verified against `packages-paid/api-workspace/src/routes/workspaces/people.ts`, `packages/api/src/lib/tenant-db.ts` and `packages/api/src/services/person.ts`.
|
|
293
319
|
|
|
294
|
-
Prior: 2026-10-09 — the hub signs in its spokes (#7894): the `/v1/hub-sign-in/*` bullet in Endpoint conventions — verified against `
|
|
320
|
+
Prior: 2026-10-09 — the hub signs in its spokes (#7894): the `/v1/hub-sign-in/*` bullet in Endpoint conventions — verified against `packages-paid/api-hub/src/routes/auth/hub-sign-in.ts`, `packages-paid/api-hub/src/services/hub-sign-in.ts`, `packages-paid/api-hub/src/lib/hub-sign-in-store.ts`, `packages/api/src/middleware/auth.ts` and `packages-paid/api-hub/src/bin/spokes.ts`.
|
|
295
321
|
|
|
296
|
-
Prior: 2026-10-08 — agent key grants (#7864): the agent-seat bullet names `PUT /v1/workspaces/:workspaceId/agents/:agentId/grants`, the five grantable permissions and the person clamp — verified against `
|
|
322
|
+
Prior: 2026-10-08 — agent key grants (#7864): the agent-seat bullet names `PUT /v1/workspaces/:workspaceId/agents/:agentId/grants`, the five grantable permissions and the person clamp — verified against `packages-paid/api-workspace/src/routes/workspaces/agents.ts`, `packages/api/src/services/default-tenant.ts` and `packages/validation/src/schemas/permissions.ts`.
|
|
297
323
|
|
|
298
|
-
Prior: 2026-10-05 — doc-sync: added the agent seat routes (#3591, #7678) to Endpoint conventions — verified against `
|
|
324
|
+
Prior: 2026-10-05 — doc-sync: added the agent seat routes (#3591, #7678) to Endpoint conventions — verified against `packages-paid/api-workspace/src/routes/workspaces/agents.ts`.
|
|
299
325
|
|
|
300
326
|
Prior: 2026-10-04 — doc-sync: the agent seat, step E (#7684) — the messages and submissions bullets now name each route's signed-in MCP twin on `/mcp/workspace` (#3591 D13) in place of the retired no-twin exception — verified against `apps/api/src/routes/workspaces/{messages,submissions}.ts`, `apps/mcp/src/workspace-server.ts` and `tests/parity/api-parity/kinds/site.ts`.
|
|
301
327
|
|
|
302
|
-
Prior: 2026-10-04 — doc-sync: roles by job (#7636 PR-A, #7653) — every role floor on the routes above now names its row of `@working-theory/validation`'s permission table (`can(tenant.role, "<permission>")`, no route-local role constant): a member reads and acts on the inbox family, appointments, the waitlist dashboard and listing decisions, reads live-only docs, magnets and meeting types and the asset list, and is refused draft content, workspace settings (the config PUT gained its first check) and agent sessions; an admin manages the calendar connection (audited, other owners emailed) and is refused the billing portal — verified against `apps/api/src/routes/{workspaces,magnets,assets,billing,waitlist-experiments,project-listings}/*.ts`, `
|
|
328
|
+
Prior: 2026-10-04 — doc-sync: roles by job (#7636 PR-A, #7653) — every role floor on the routes above now names its row of `@working-theory/validation`'s permission table (`can(tenant.role, "<permission>")`, no route-local role constant): a member reads and acts on the inbox family, appointments, the waitlist dashboard and listing decisions, reads live-only docs, magnets and meeting types and the asset list, and is refused draft content, workspace settings (the config PUT gained its first check) and agent sessions; an admin manages the calendar connection (audited, other owners emailed) and is refused the billing portal — verified against `apps/api/src/routes/{workspaces,magnets,assets,billing,waitlist-experiments,project-listings}/*.ts`, `packages-paid/api-scheduling/src/routes/schedule/appointments.ts` and `apps/api/src/services/{leads-inbox,inbox-messages,inbox-submissions,waitlist-dashboard,waitlist-operator-reads,tenant-context}.ts`.
|
|
303
329
|
|
|
304
|
-
Prior: 2026-10-03 — doc-sync: the Inbox submissions and subscribers reads (#7535) — added the `/v1/workspaces/:workspaceId/submissions` and `/v1/workspaces/:workspaceId/subscribers` bullets — verified against `apps/api/src/routes/workspaces/{submissions,subscribers}.ts`, `
|
|
330
|
+
Prior: 2026-10-03 — doc-sync: the Inbox submissions and subscribers reads (#7535) — added the `/v1/workspaces/:workspaceId/submissions` and `/v1/workspaces/:workspaceId/subscribers` bullets — verified against `apps/api/src/routes/workspaces/{submissions,subscribers}.ts`, `packages-paid/api-workspace/src/services/inbox-submissions.ts`, `packages/api/src/lib/tenant-db.ts` and `packages/api/src/server.ts`.
|
|
305
331
|
|
|
306
|
-
Prior: 2026-10-03 — doc-sync: a scheduling connection per workspace (#7533) — added the `/v1/workspaces/:workspaceId/meetings/connection` and `POST /v1/webhooks/calcom/:connectionToken` bullets and the workspace-aware resolution note on `/v1/schedule/*` — verified against `
|
|
332
|
+
Prior: 2026-10-03 — doc-sync: a scheduling connection per workspace (#7533) — added the `/v1/workspaces/:workspaceId/meetings/connection` and `POST /v1/webhooks/calcom/:connectionToken` bullets and the workspace-aware resolution note on `/v1/schedule/*` — verified against `packages-paid/api-scheduling/src/routes/workspaces/meetings.ts`, `apps/api/src/routes/webhooks/{calcom,index}.ts`, `packages-paid/api-scheduling/src/routes/schedule/index.ts` and `apps/api/src/services/scheduling/{connections,resolve}.ts`.
|
|
307
333
|
|
|
308
|
-
Prior: 2026-10-03 — doc-sync: the multi-tenant null-binding fallback and the platform-operator scope both require the caller to BE the verified `AILK_OPERATOR_EMAIL` pin (#7523 D1, D3) — corrected the Inbox message role-floor note, the `/v1/waitlist-experiments/*` row and the `AILK_OPERATOR_EMAIL` paragraph — verified against `
|
|
334
|
+
Prior: 2026-10-03 — doc-sync: the multi-tenant null-binding fallback and the platform-operator scope both require the caller to BE the verified `AILK_OPERATOR_EMAIL` pin (#7523 D1, D3) — corrected the Inbox message role-floor note, the `/v1/waitlist-experiments/*` row and the `AILK_OPERATOR_EMAIL` paragraph — verified against `packages/api/src/services/tenant-context.ts`, `packages/api/src/services/default-tenant.ts` and `packages-paid/api-signal-collectors/src/services/waitlist-operator-reads.ts`.
|
|
309
335
|
|
|
310
|
-
Prior: 2026-10-03 — doc-sync: the general GET gains `emailFrom`, and `GET /v1/flows/:slug` serves the config minus `completionEmail` (#7519 D5, D6) — verified against `
|
|
336
|
+
Prior: 2026-10-03 — doc-sync: the general GET gains `emailFrom`, and `GET /v1/flows/:slug` serves the config minus `completionEmail` (#7519 D5, D6) — verified against `packages-paid/api-workspace/src/routes/workspaces/general.ts`, `packages-paid/api-signal-collectors/src/services/flow-engine.ts` and `packages/api/src/services/deliverable-fulfillment.ts`.
|
|
311
337
|
|
|
312
|
-
Prior: 2026-09-21 — doc-sync: re-pointed magnet deliverables at the workspace-scoped `Asset` store (#6310) — extended the `/v1/deliverables/:grantId` row with the new `asset_` grant class (the named boundary it reads through, its serve-time tenancy re-check, its hardened attachment+CSP+no-store serve for every content type, and its uniform-404 fold), recording the rejected redirect-to-`/v1/assets/:assetId` mechanism and why — verified against `
|
|
338
|
+
Prior: 2026-09-21 — doc-sync: re-pointed magnet deliverables at the workspace-scoped `Asset` store (#6310) — extended the `/v1/deliverables/:grantId` row with the new `asset_` grant class (the named boundary it reads through, its serve-time tenancy re-check, its hardened attachment+CSP+no-store serve for every content type, and its uniform-404 fold), recording the rejected redirect-to-`/v1/assets/:assetId` mechanism and why — verified against `packages-paid/api-signal-collectors/src/routes/deliverables/index.ts`, `packages/api/src/lib/deliverable-asset-store.ts` and `packages/api/src/services/deliverable-fulfillment.ts`.
|
|
313
339
|
|
|
314
|
-
Prior: 2026-09-19 — doc-sync: added the three per-magnet captured-lead list routes (#6246 S6h) — `GET /v1/workspaces/:workspaceId/magnets/:id/{signups,subscribers,messages}` — to the magnets bullet, recording the `Lead.source = 'flow:' + slug` join, the new `tenantDb(tenant).leads.listBySource` accessor, the new `(workspaceId, source)` index, the wrong-kind/absent-magnet shared 404, the four/five-key wire shapes, the bounded `?limit=`, and the no-MCP-twin decision — verified against `
|
|
340
|
+
Prior: 2026-09-19 — doc-sync: added the three per-magnet captured-lead list routes (#6246 S6h) — `GET /v1/workspaces/:workspaceId/magnets/:id/{signups,subscribers,messages}` — to the magnets bullet, recording the `Lead.source = 'flow:' + slug` join, the new `tenantDb(tenant).leads.listBySource` accessor, the new `(workspaceId, source)` index, the wrong-kind/absent-magnet shared 404, the four/five-key wire shapes, the bounded `?limit=`, and the no-MCP-twin decision — verified against `packages-paid/api-signal-collectors/src/routes/magnets/index.ts`, `packages/api/src/lib/tenant-db.ts` and `database/inbox/schema.prisma`.
|
|
315
341
|
|
|
316
|
-
Prior: 2026-09-19 — doc-sync: added the Inbox message API (#6216 S7e) — `GET /v1/workspaces/:workspaceId/messages` and `GET`/`PATCH /v1/workspaces/:workspaceId/messages/:leadId` — to Endpoint conventions, recording the six-key served row, the message-source allowlist confinement, the shared 404 for four causes, the one-way idempotent PATCH, the `owner|admin` role floor and its TOFU precondition, and the no-MCP-twin decision — verified against `
|
|
342
|
+
Prior: 2026-09-19 — doc-sync: added the Inbox message API (#6216 S7e) — `GET /v1/workspaces/:workspaceId/messages` and `GET`/`PATCH /v1/workspaces/:workspaceId/messages/:leadId` — to Endpoint conventions, recording the six-key served row, the message-source allowlist confinement, the shared 404 for four causes, the one-way idempotent PATCH, the `owner|admin` role floor and its TOFU precondition, and the no-MCP-twin decision — verified against `packages-paid/api-workspace/src/routes/workspaces/messages.ts`, `packages-paid/api-workspace/src/services/inbox-messages.ts`, `packages/api/src/lib/tenant-db.ts` and `packages/api/src/server.ts`.
|
|
317
343
|
|
|
318
|
-
Prior: 2026-09-19 — doc-sync: added the tenant-scoped magnet Admin authoring surface (#6157 S5c) — `GET`/`POST /v1/workspaces/:workspaceId/magnets` and `GET`/`PATCH /v1/workspaces/:workspaceId/magnets/:id` — to Endpoint conventions, recording its `tenantDb`-only data path, its `owner|admin` role floor, the `multi-tenant || signal-collectors` gate, the config-URL origin gate and the no-MCP-twin parity decision — verified against `
|
|
344
|
+
Prior: 2026-09-19 — doc-sync: added the tenant-scoped magnet Admin authoring surface (#6157 S5c) — `GET`/`POST /v1/workspaces/:workspaceId/magnets` and `GET`/`PATCH /v1/workspaces/:workspaceId/magnets/:id` — to Endpoint conventions, recording its `tenantDb`-only data path, its `owner|admin` role floor, the `multi-tenant || signal-collectors` gate, the config-URL origin gate and the no-MCP-twin parity decision — verified against `packages-paid/api-signal-collectors/src/routes/magnets/index.ts`, `packages/api/src/lib/tenant-db.ts` and `packages/api/src/server.ts`.
|
|
319
345
|
|
|
320
|
-
Prior: 2026-09-19 — doc-sync: the enabled-meeting-slugs gate (#6183 S9b) — added the `/v1/workspaces/:workspaceId/meetings` bullet (its own scope, gated on `scheduling` alone, the D24 `listEventTypes` join) and the `/v1/schedule/meeting-types` bullet (the public read, its own boundary file) — verified against `
|
|
346
|
+
Prior: 2026-09-19 — doc-sync: the enabled-meeting-slugs gate (#6183 S9b) — added the `/v1/workspaces/:workspaceId/meetings` bullet (its own scope, gated on `scheduling` alone, the D24 `listEventTypes` join) and the `/v1/schedule/meeting-types` bullet (the public read, its own boundary file) — verified against `packages-paid/api-scheduling/src/routes/workspaces/meetings.ts`, `packages-paid/api-scheduling/src/routes/schedule/meeting-types.ts`, `packages-paid/api-scheduling/src/lib/public-meetings-config.ts` and `packages/api/src/server.ts`.
|
|
321
347
|
|
|
322
|
-
Prior: 2026-09-18 — doc-sync: the Docs list gains tags and a closed kind, and the asset store admits documents and gains list, metadata update and delete (#6093) — completed the `/v1/workspaces/:workspaceId/content` bullet with the page/asset union (`tags`, derived `kind`, `?tag=` filter), and added the `/v1/workspaces/:workspaceId/assets` + `/v1/assets/:assetId` family bullet (list/PATCH/DELETE, the widened upload allowlist admitting `image/svg+xml` and `application/pdf`, and the public read's raster-inline/non-raster-attachment+CSP serve split) — verified against `
|
|
348
|
+
Prior: 2026-09-18 — doc-sync: the Docs list gains tags and a closed kind, and the asset store admits documents and gains list, metadata update and delete (#6093) — completed the `/v1/workspaces/:workspaceId/content` bullet with the page/asset union (`tags`, derived `kind`, `?tag=` filter), and added the `/v1/workspaces/:workspaceId/assets` + `/v1/assets/:assetId` family bullet (list/PATCH/DELETE, the widened upload allowlist admitting `image/svg+xml` and `application/pdf`, and the public read's raster-inline/non-raster-attachment+CSP serve split) — verified against `packages-paid/api-content-write/src/routes/workspaces/content.ts`, `apps/api/src/routes/assets/{index,upload,read,manage}.ts` and `packages/api/src/lib/tenant-db.ts`.
|
|
323
349
|
|
|
324
|
-
Prior: 2026-09-16 — doc-sync: added `GET /v1/leads` (#5678), the owner-scoped leads inbox read, to Endpoint conventions, recording its unconditional registration, its five-field row, its `resolveOwnerWorkspaces` scoping and its no-MCP-twin parity decision — verified against `
|
|
350
|
+
Prior: 2026-09-16 — doc-sync: added `GET /v1/leads` (#5678), the owner-scoped leads inbox read, to Endpoint conventions, recording its unconditional registration, its five-field row, its `resolveOwnerWorkspaces` scoping and its no-MCP-twin parity decision — verified against `packages/api/src/routes/leads/list.ts`, `packages/api/src/services/leads-inbox.ts`, `packages/api/src/services/owner-workspaces.ts` and `packages/api/src/server.ts`.
|
|
325
351
|
|
|
326
|
-
Prior: 2026-09-14 — doc-sync: signal-collectors module extraction (D-48, #5366) — added the `module: signal-collectors` note to the flows/waitlist-signups-me/project-listings-me endpoints, added the previously-undocumented `/v1/deliverables/:grantId`, `/v1/project-listings/{start,submit,:id,public}` and `/v1/slack/actions` rows — verified against `
|
|
352
|
+
Prior: 2026-09-14 — doc-sync: signal-collectors module extraction (D-48, #5366) — added the `module: signal-collectors` note to the flows/waitlist-signups-me/project-listings-me endpoints, added the previously-undocumented `/v1/deliverables/:grantId`, `/v1/project-listings/{start,submit,:id,public}` and `/v1/slack/actions` rows — verified against `packages/api/src/server.ts` and each route's own header.
|
|
327
353
|
|
|
328
|
-
Prior: 2026-09-12 — doc-sync: added the `## Deploy path` section and the matching `## What NOT to do` line (#5334), recording that bundling `apps/api` is not a supported deploy path and naming the three gates — verified against `apps/api/vercel.json`, `
|
|
354
|
+
Prior: 2026-09-12 — doc-sync: added the `## Deploy path` section and the matching `## What NOT to do` line (#5334), recording that bundling `apps/api` is not a supported deploy path and naming the three gates — verified against `apps/api/vercel.json`, `packages/api/src/lib/slice-load.ts` and `scripts/check-bundled-build-diagnoses.ts`.
|
|
329
355
|
|
|
330
|
-
Prior: 2026-09-12 — doc-sync: added `GET /v1/project-listings/me` (#5295), the auth-scoped founder read joined on the owner link, to Endpoint conventions — verified against `
|
|
356
|
+
Prior: 2026-09-12 — doc-sync: added `GET /v1/project-listings/me` (#5295), the auth-scoped founder read joined on the owner link, to Endpoint conventions — verified against `packages-paid/api-signal-collectors/src/routes/project-listings/me.ts` and `packages/api/src/server.ts`.
|
|
331
357
|
|
|
332
|
-
Prior: 2026-09-07 — doc-sync: added `listing-csv:sweep` (#5001), the weekly founder-CSV sweep, to `## Scheduled sweeps (external cron)` — verified against `
|
|
358
|
+
Prior: 2026-09-07 — doc-sync: added `listing-csv:sweep` (#5001), the weekly founder-CSV sweep, to `## Scheduled sweeps (external cron)` — verified against `packages-paid/api-signal-collectors/src/bin/listing-csv-sweep.ts` and `apps/api/package.json`.
|
|
333
359
|
|
|
334
360
|
Prior: 2026-09-04 — doc-sync: added the `## Scheduled sweeps (external cron)` section documenting `abandon:sweep` and `followup:sweep` (#4732 AC-6) — verified against `apps/api/package.json` and `apps/api/src/bin/`.
|
|
335
361
|
|
|
336
|
-
Prior: 2026-09-04 — doc-sync: added `GET /v1/waitlist-signups/me` (#4731), the auth-scoped respondent read, to Endpoint conventions — verified against `
|
|
362
|
+
Prior: 2026-09-04 — doc-sync: added `GET /v1/waitlist-signups/me` (#4731), the auth-scoped respondent read, to Endpoint conventions — verified against `packages-paid/api-signal-collectors/src/routes/waitlist-signups/index.ts` and `packages/api/src/server.ts`.
|
|
337
363
|
|
|
338
|
-
Prior: 2026-09-02 — doc-sync: noted the `AILK_OPERATOR_EMAIL` fail-closed precondition on the platform-operator scope in the `/v1/waitlist-experiments/*` row (PR #4687 security review, High finding) — verified against `
|
|
364
|
+
Prior: 2026-09-02 — doc-sync: noted the `AILK_OPERATOR_EMAIL` fail-closed precondition on the platform-operator scope in the `/v1/waitlist-experiments/*` row (PR #4687 security review, High finding) — verified against `packages-paid/api-signal-collectors/src/services/waitlist-dashboard.ts`.
|
|
339
365
|
|
|
340
|
-
Prior: 2026-09-02 — doc-sync: added `/v1/waitlist-experiments/*` routes (#4665 D7, #4671) to Endpoint conventions — verified against `
|
|
366
|
+
Prior: 2026-09-02 — doc-sync: added `/v1/waitlist-experiments/*` routes (#4665 D7, #4671) to Endpoint conventions — verified against `packages-paid/api-signal-collectors/src/routes/waitlist-experiments/index.ts`.
|
|
341
367
|
|
|
342
|
-
Prior: 2026-08-31 — doc-sync: added `GET /v1/schedule/event-meta` (#4633), the meta-only scheduling read, to the `/v1/schedule/*` row — verified against `
|
|
368
|
+
Prior: 2026-08-31 — doc-sync: added `GET /v1/schedule/event-meta` (#4633), the meta-only scheduling read, to the `/v1/schedule/*` row — verified against `packages-paid/api-scheduling/src/routes/schedule/index.ts`.
|
|
343
369
|
|
|
344
|
-
Prior: 2026-08-26 — doc-sync: added `/v1/schedule/*` routes (#4416) to Endpoint conventions — verified against `
|
|
370
|
+
Prior: 2026-08-26 — doc-sync: added `/v1/schedule/*` routes (#4416) to Endpoint conventions — verified against `packages-paid/api-scheduling/src/routes/schedule/index.ts`.
|
|
345
371
|
|
|
346
|
-
Prior: 2026-08-10 — doc-sync: added `/v1/flows/*` routes (#3956) to Endpoint conventions — verified against `
|
|
372
|
+
Prior: 2026-08-10 — doc-sync: added `/v1/flows/*` routes (#3956) to Endpoint conventions — verified against `packages-paid/api-signal-collectors/src/routes/flows/index.ts`.
|
|
347
373
|
|
|
348
|
-
Prior: 2026-07-15 — doc-sync: added `/v1/billing/*` routes (#3701) to Endpoint conventions; added the `## Services (billing-ledger)` section documenting `usage-metering.ts` / `usage-aggregation.ts` / `usage-credits.ts` — verified against `
|
|
374
|
+
Prior: 2026-07-15 — doc-sync: added `/v1/billing/*` routes (#3701) to Endpoint conventions; added the `## Services (billing-ledger)` section documenting `usage-metering.ts` / `usage-aggregation.ts` / `usage-credits.ts` — verified against `packages-paid/api-multi-tenant/src/routes/billing/index.ts` and `apps/api/src/services/`.
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
preset: 'ts-jest',
|
|
3
3
|
globalSetup: require.resolve('../../tools/jest/pin-timezone.cjs'),
|
|
4
|
+
reporters: require('../../tools/jest/reporters.cjs'),
|
|
4
5
|
testEnvironment: 'node',
|
|
5
6
|
roots: ['<rootDir>/src'],
|
|
6
7
|
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'],
|
|
7
8
|
testPathIgnorePatterns: ['/node_modules/', '.*\\.spec\\.ts$'],
|
|
8
9
|
// Ignore node_modules except jose (ESM-only, loaded transitively via @working-theory/license → @working-theory/schema-pack-registry → @working-theory/schema).
|
|
9
10
|
transformIgnorePatterns: ['/node_modules/(?!(\\.pnpm/)?(jose@?[^/]*?|cookie@?[^/]*?|@vercel(?:\\+analytics@[^/]*?|/analytics)|@working-theory(?:\\+[^/]*?@?[^/]*?|/[^/]+))(/|$))'],
|
|
10
|
-
|
|
11
|
+
// `tsx` so the mapped @working-theory/email source can resolve its React
|
|
12
|
+
// Email templates (`./templates/*.js` → `*.tsx`) — #9446.
|
|
13
|
+
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
|
14
|
+
// #9564 D7: the shared test setup moved to the package with the base code.
|
|
11
15
|
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts'],
|
|
12
16
|
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts', '!src/__tests__/**'],
|
|
13
17
|
// Use v8 coverage provider for better async code tracking (route handlers via Fastify inject)
|
|
@@ -23,6 +27,8 @@ module.exports = {
|
|
|
23
27
|
moduleNameMapper: {
|
|
24
28
|
// @working-theory/* resolve from the installed packages; their ESM
|
|
25
29
|
// dist is converted by the transform below.
|
|
30
|
+
'^@working-theory/api/routes/webhooks/stripe\\.js$':
|
|
31
|
+
'<rootDir>/node_modules/@working-theory/api/dist/routes/webhooks/stripe.js',
|
|
26
32
|
// Map Prisma client import for testing (must come before the generic .js stripper).
|
|
27
33
|
// Phase C (#710): the v7 prisma-client generator emits TypeScript source files
|
|
28
34
|
// (client.ts) that use `import.meta.url`, which Jest's CommonJS preset can't
|
|
@@ -33,14 +39,14 @@ module.exports = {
|
|
|
33
39
|
// The tenancy constants subpath (#3627) — real TS source, not a mock: the
|
|
34
40
|
// `database/tenancy.ts` module has no external boundary to fake, so map
|
|
35
41
|
// straight to source, matching the workspace-package-to-src pattern below.
|
|
36
|
-
'^@working-theory/database/tenancy$': '<rootDir
|
|
42
|
+
'^@working-theory/database/tenancy$': '<rootDir>/node_modules/@working-theory/database/dist/tenancy.js',
|
|
37
43
|
// The flow-presets data subpath (#3958) — same real-TS-source rationale
|
|
38
44
|
// as tenancy above: FLOW_PRESETS is plain data with no external boundary
|
|
39
45
|
// to fake, so map straight to source rather than requiring a package build.
|
|
40
|
-
'^@working-theory/database/flows/presets$': '<rootDir
|
|
46
|
+
'^@working-theory/database/flows/presets$': '<rootDir>/node_modules/@working-theory/database/dist/flows/presets.js',
|
|
41
47
|
// The shared listing-diagnostic scoring register (#5001) — same
|
|
42
48
|
// real-TS-source rationale as flows/presets above.
|
|
43
|
-
'^@working-theory/database/flows/listing-diagnostic$': '<rootDir
|
|
49
|
+
'^@working-theory/database/flows/listing-diagnostic$': '<rootDir>/node_modules/@working-theory/database/dist/flows/listing-diagnostic.js',
|
|
44
50
|
'^@working-theory/analytics/server$': '<rootDir>/node_modules/@working-theory/analytics/dist/server/index.js',
|
|
45
51
|
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
46
52
|
},
|
|
@@ -59,10 +65,14 @@ module.exports = {
|
|
|
59
65
|
skipLibCheck: true,
|
|
60
66
|
ignoreDeprecations: '6.0',
|
|
61
67
|
forceConsistentCasingInFileNames: true,
|
|
68
|
+
jsx: 'react-jsx', // matches packages/email/tsconfig.json (#9446)
|
|
62
69
|
},
|
|
63
70
|
// Allow import.meta syntax to be parsed (source is ESM, but we compile to CommonJS)
|
|
64
71
|
diagnostics: {
|
|
65
|
-
|
|
72
|
+
// 1343: "import.meta only allowed in ESM modules"; 2823: import attributes
|
|
73
|
+
// under CJS — the composition's `with: { type: "json" }` manifest probe
|
|
74
|
+
// (#9571 D5), which the CJS emit drops and `require` serves.
|
|
75
|
+
ignoreCodes: [1343, 2823],
|
|
66
76
|
},
|
|
67
77
|
},
|
|
68
78
|
],
|