Lägger docs/production-readiness.md och siax/production-gaps.json enligt SIAX Master Definition of Done. Underlag: git-, kod- och live-evidens 2026-11-11. Nivån är evidensbaserad, inte ett leveransgodkännande.
The `resolveStoreMode` kill switch (Master-DoD §3/§10) covered the newer
`src/repositories/*` stores but not two older paths, both of which are
live production execution paths today.
1. LEGACY NEXT.JS STORES. `lib/orders-store.ts` and
`lib/subscriptions-store.ts` predate `src/platform/dependency-mode.ts`
and called `getSql()` directly, silently falling back to module-global
`Map`s when no database is configured. For orders, payments and
subscriptions that is not a degraded mode, it is invisible loss of
money-bearing state: the API returns 201 and the row evaporates on the
next restart or on any other replica. The live 0pt deployment
(Coolify app 54) runs NODE_ENV=production with DATABASE_URL,
POSTGRES_URL and POSTGRES_PRISMA_URL all unset — so this is the
behavior in production right now, across 7 API routes.
These now go through `getSqlForDurableStore()`, which reuses the SAME
`resolveStoreMode` decision every `src/repositories/*` store already
uses rather than re-implementing a second production check. The reuse
is load-bearing: `isProductionRuntime` deliberately exempts
`NEXT_PHASE=phase-production-build`, because `next build` runs
page-data collection under NODE_ENV=production while serving nothing.
A naive `process.env.NODE_ENV === "production"` test here would have
failed the BUILD instead of protecting a request path.
2. LOCALBLOBSTORE. It writes document bytes to a container-local disk
while `document-capture-service` registers an artifact pointer
claiming the bytes are stored — durable object storage silently
swapped for ephemeral disk. It is the default blob store for both the
capture and extraction services. In production it now raises
DEPENDENCY_UNAVAILABLE (capability blocked: no ST0RE bytes path
exists yet) instead of fabricating success. Asserted per-operation,
not in the constructor, so merely wiring the services is not fatal —
only actually persisting or reading bytes is.
Evidence: full suite 956 passed / 184 skipped, typecheck clean,
dependency-cruiser 0 errors, lint identical to main's pre-existing
baseline (17 problems, none in changed files). Both guards asserted in
both directions and mutation-tested: bypassing resolveStoreMode -> 2
failed; dropping the write assert -> 2 failed; dropping the read assert
-> 1 failed; all restored -> 8 passed. 7 non-test call sites.
DEPLOYMENT NOTE: merging changes no runtime (no Gitea webhook exists on
any repo; deploys are manual). Deploying this build REQUIRES
provisioning DATABASE_URL for 0pt first, or the order/subscription
routes will correctly return DEPENDENCY_UNAVAILABLE instead of silently
accepting orders into memory. That provisioning is a Simon action.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- tests/integration/: 10 tests against REAL OpenSearch 2.11 (tenant
isolation, purpose/rights filters, generation rebuild/rollback,
projection-disaster rebuild §68) + REAL ClickHouse 24.8 (tenant-isolated
metric DSL, TTL schema, injection-surface refusals). Honest skip without
env - and CI fails hard if the suites skip instead of running.
- .gitea/workflows/ci.yml: new blocking 'integration' job with
self-identifying service containers (st0re ci.yml's measured
alias-collision discipline: run-unique cluster.name / credentials,
getent-enumerated IP selection, no-match = hard fail).
- STATUS.md: full 8-level Master-DoD classification per capability incl.
the honest gaps blocking PRODUCTION_READY.
- dependency-mode: NEXT_PHASE=phase-production-build is not a serving
runtime (next build page-data collection must compile; guard re-arms at
boot) - pinned by contract test.
- docs/02-architecture.md: superseded the accurate-but-now-fixed 'every
client runs against its mock' paragraph; .env.example: client URLs now
actually read from env + new P0 backend vars.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
38 tests: recording provider double answers with the sibling services' real
response shapes; each suite asserts the exact request wire shape (path,
method, tenant/actor/idempotency headers, camelCase vs snake_case body) so a
regression to the pre-rewrite invented shapes fails loudly. Includes the
fail-closed policy cases (missing decision => deny, CL0UD down => typed
error, never implicit allow) and the production factory guard for all four
clients.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>