The predecessor AirGap Controller, Time host, and shared DAL sources are withdrawn by OK-5 under the approved posture split. Platform owns generic environment-state custody; the surviving policy/replication and OfflineKit time-verification libraries own interpretation and verification. This does not retire the exchange libraries or claim the separate live stop/schema window has run. Earlier host instructions below describe the predecessor. See the current split in docs-archive/modules/export-center/consolidation-design.md.

Microservice → database ownership matrix

Status: historical baseline with individually dated follow-up evidence + Accepted target (ADR-039 rev-13). Sources: devops/compose/docker-compose.stella-services.yml, live stellaops-postgres catalog (read-only), AddStartupMigrations sweep over src/, the data-and-stores role playbook (AGENTS.md — Memory). src/ wins over this document; rows marked (verify) lack a confirmed code anchor.

Source verification (2026-09-09)

Verified against 286391582b4d00782ae61002941079d40ef8c68e; current unresolved work is routed by the estate board. The older physical inventory and per-family rows below retain their own evidence dates. They are not a fresh census of the running estate.

SignalVerified resultRe-verify
Build-boundary exceptions31 pins across eight owning sprints, zero library impurities, no missing owner/seam/sunset fieldsParse build-boundary/legacy-edge-register.json
Generic base-compose connection0 environment assignments (2026-09-14, PLT-3); the only remaining textual hits are comments that say it is retired. Earlier: 32 textual lines; 25 environment assignments in 21 service definitions after YAML parsingrtk rg -n STELLAOPS_POSTGRES_CONNECTION devops/compose/docker-compose.stella-services.yml; parse service environment maps
stellaops-platform naming footprint299 tracked files at the verified commit, including 34 under src/devops/tools/.gitea after frozen roots are excluded. A text hit is not proof of runtime usertk git grep -l stellaops-platform 286391582b --
Central authority_app shellThe previously attributed Authority migration plugin was removed by 86056143f2; its dotted resource prefix never matched the migrations. This does not establish which historical process created the shell. 2026-09-14 (PLT-4): MigrationModulePlugins.cs is deleted; devops/compose/postgres-init/00-v1-baseline.sql now creates the authority / authority_app shells inside stellaops_authority and nothing elsertk rg -n 'authority_app' devops/compose/postgres-init/00-v1-baseline.sql

No deployed ownership or closure row was changed by this source verification.

1. Historical physical baseline (measured 2026-07-22)

Currency note (re-measured 2026-08-03, read-only, same lab database). These sizes move a lot: vuln 24 GB, concelier 1,551 MB, vex 3,968 kB, vexhub 680 kB ≈ 25.5 GB, scanner 2,701 MB. The lab was reset between the two readings, so the 2026-07-22 figures above are a fuller-corpus snapshot and neither reading is the production estate. Do not use either as a reclamation or headroom target — the SPRINT_20260722_008 VULN-G1 window measures the target database at step 0; see the G1 execution-window runbook §1–2.

Source boundary update (CM-6, 2026-08-26; verified against d3358c84319b0da835e944d70f360f7df526d9c5). Platform no longer reads RO topology tables or carries the RO migration plugin. It reads active tenant UUIDs from its own shared.tenants, then consumes the tenant-scoped RO owner API described in X22 below. Re-verify with rg -n "ReleaseOrchestratorMigrationModulePlugin|PostgresTopologySourceReader" src/Platform (the plugin name must return zero hits; the whole plugin mechanism was deleted 2026-09-14, PLT-4) and rg -n "topology-snapshot" src/Platform src/ReleaseOrchestrator. Platform’s own 098_v1_release_topology_inventory_projection.sql (Startup band) creates the release.topology_* projection tables in stellaops_platform.

2. Deployable → schema matrix

Replica sets (web/worker/init of one service) are one row — they legitimately share the owner database. Wave = adoption wave from ADR-039 (1 = vulnerability plane now, 2 = large/high-churn next, 3 = small batch moves). Risk = migration risk of the move itself.

The Cluster column is historical as of 2026-08-14 — read it as “which cluster this row was once planned for”, not as current topology. SPRINT_20260810_002 (PTC-1…PTC-5) consolidated the estate onto ONE PostgreSQL server per the ADR-039 P1 topology clarification: “own physical database” means a logical database + its own role on the shared installation cluster, and a dedicated server is an optional scaling choice, not the default. So a vulndata value below names a retired cluster (STOPPED 2026-08-14, volume kept as the repoint rollback), and every stellaops_* database in this table that exists today lives on stellaops-postgres / db.stella-ops.local. The boundary is the per-database role plus REVOKE CONNECT, not the container — see §5.4 for the measured census.

Deployable(s) (compose)Schemas written todayMigration assembly (AddStartupMigrations)CategoryProposed database / role (stellaops_*)ClusterWaveRisk
platformplatform, shared, release (19 live tables + the 9 topology_* projection tables 098 owns since 2026-09-14), catalog_replica, eventing (P6), crypto (P4) — the complete set; nothing else remains in stellaops_platform after 021 PLT-4’s four windows (981 MB, fresh-install-proven)Platform.Persistencecontrol planeplatform — DB name stays stellaops_platform(owner correction 2026-07-22; keeps shared.tenants while the resolver contract stands). End-state 2026-09-14 (021 PLT-4): 985 MB, down from 91 GB; residue left = policy 11 tables/41 rows (Policy’s ruling) and symbols/golden_sets/groundtruth seed rows (Vulnerabilities’ ruling).control— (stays)low
authorityauthority, authority_app, issuer, catalog_replica (P4), eventing (P6) — the complete set since 2026-09-14 (SPRINT_20260914_005 PEF-3 dropped the ten foreign shells the pre-prune init file had created inside stellaops_authority: advisoryai analytics attestor evidence_locker platform policy proofchain release release_app shared)Authority.Persistencecontrol planeauthority (done — the precedent)controldone—
concelier (+ concelier-jobs-init) → merges into StellaOps.Vulnerabilitiesconcelier (sources/DTOs/jobs), vuln canonical advisory tables + issue_observations/_evidence_refs/_linksets (global consensus)Concelier.Storage → Vulnerabilities.Persistencedata plane (hub)vuln(single hub DB stellaops_vuln, ADR-039 D2; fresh build, no data moved — D7)vulndata1rename + rebuild, no data migration
concelier (today’s serving role) — vuln.issue_gate_decisions + rebuild queueper-tenant gate projectionConcelier.Storage (today)control plane (tenant decisions)moves to policy— Policy-owned projection fed by hub events (ADR-039 D3)control1rebuilt by Policy projector
excititor-web + excititor-worker → merges into StellaOps.Vulnerabilitiesvex (claims, raw blobs/documents, product dedup, checkpoints, quarantine)Excititor.Persistence → Vulnerabilities.Persistencedata plane (hub)vulnvulndata1rename + rebuild
vexhub-web → merges into StellaOps.Vulnerabilitiesvexhub (statements, sources, provenance, conflicts) — rows currently written by Excititor’s sink, not by VexHubVexHub.Persistence → Vulnerabilities.Persistence (dist schema)data plane (distribution view, rebuildable)vulnvulndata1rename + rebuild
scanner-web + scanner-worker (+ cache-init)scanner + family-owned eventing on stellaops_scanner; legacy stellaops_platform.scanner and the mishomed signals objects are retained offline pending SCN-6StellaOps.Scanner.Persistence (sole scanner migration authority)data planescanner — live on stellaops_scanner since 2026-08-29control2 — DONEold-source retirement/drop remains SCN-6
advisory-ai-web + advisory-ai-worker (+ data-init) → consolidated as advisoryai-web + advisoryai-workeradvisoryaiStellaOps.AdvisoryAI.Persistence (sole family migration authority)data planeadvisoryai — live on stellaops_advisoryai since 2026-09-04 (AAI-9 window 20260904-aai9; 13,238 carried rows copied under a writer freeze, count + content-digest parity empty-diff on all 25 tables)control2 — DONEAAI-10 retired the predecessor path 2026-09-07 (compose keys deleted, host obsoleted). Legacy stellaops_platform.advisoryai is retained OFFLINE as the rollback input until its separately approved destructive drop
opsmemory-web → dissolved into advisoryai-web (in-process module)opsmemorymerged ↑ (StellaOps.AdvisoryAI.Persistence)control planeopsmemory — live on stellaops_advisoryai since 2026-09-04 (same AAI-9 window; decisions carried empty, write+recall proven on the consolidated host)control3 — DONEAAI-10 retired opsmemory-web 2026-09-07: compose key deleted, host frozen at src/__Obsoleted/AdvisoryAI/StellaOps.OpsMemory.WebService/, routes served in-process by advisoryai-web. Legacy stellaops_platform.opsmemory is retained OFFLINE as the rollback input until its separately approved destructive drop
timeline-webtimeline (+ timeline_app, + eventing) — MOVED: live on stellaops_timeline(role timeline; REVOKE CONNECT re-proven 2026-09-11 — CONNECT true for stellaops_timeline, false for all 21 sibling stellaops_* databases). The database was fresh-converged 2026-08-14 08:20 by PTC-4/Window B and the historical corpus was deliberately NOT carried (owner ruling C), so it holds events from 2026-08-14 onward — 452,007 at 2026-09-11. The legacy stellaops_platform.timeline schema was snapshotted and dropped 2026-08-24 under that ruling, and the 2026-08-24 partition bridge’s rollback heap was dropped 2026-09-11 under W3-01’s recorded approval. The vestigial timeline_app schema left on stellaops_platform (0 tables, 1 function, no policy callers — every platform RLS policy calls a schema-qualified require_current_tenant belonging to another *_app) is SPRINT_20260722_026’s to disposition, not Timeline’s. 2026-09-15 (SPRINT_20260911_001 TLC-1): a third schema, eventing, joins this database — the P4 reliability store (inbox, consumer_checkpoints, leases, outbox, stream_state, remote_stream_consumers) that StellaOps.Eventing.Reliability self-migrates under its own eventing.schema_migrations ledger, so the DC-29 findings.dispositions consumer can admit, insert into timeline.events and advance its checkpoint in ONE transaction. Per-host by rule, never shared; timeline.schema_migrations stays at the consolidated baseline’s four rows.Timeline persistenceevidence/read-modelstellaops_timeline — livecontrol2low
notify-web + notify-workernotify, notify_app, eventing, crypto, catalog_replicaNotify.Persistence.Consolidated + owner-scoped shared migrations; Notify.Persistence supplies repositories onlycontrol planenotify — live on stellaops_notifycontrol2 — database cutover completeleast-privilege owner; tenant-scoped operations
notifier-worker (retired 2026-09-04, NTF-10)notifier— the schema existed in none of the 22 databases on the cluster when the retirement was executed, so the planned drop was a measured no-op—control planefolded into notify on stellaops_notifycontrolDONEsources frozen at src/__Obsoleted/Notifier/ on 2026-09-08
policy-enginepolicy (+ policy_app); plus foreign writes: scheduler (via its scheduler connection), release.security_finding_projection (verify writer path); foreign read: vuln.issue_gate_decisionsPolicy.Persistencecontrol planepolicycontrol2medium (contracts, not size)
jobengine-web + jobengine-worker (+ jobengine-estate-worker)scheduler (+ scheduler_app), packs, eventing — MOVED 2026-09-12 (012 JOB-9, ADR-039 D14): live on stellaops_jobengine(role jobengine). Succeeds scheduler-web, packsregistry-web and packsregistry-worker, whose compose keys, publish keys and containers were all removed in the same window. The copy was a column-listed --data-only merge of the only three tables holding rows — scheduler.runs 1634, scheduler.schedules 11, packs.audit_log 1 — with exact count parity, an identical run-state histogram (running 1, completed 1127, error 506) and the live estate-doctor schedule row SKIPPED rather than overwritten so its estate_constraints survived. No BYPASSRLS grant was needed: both databases sit on the shared server, so the merge ran as the superuser and the boundary role’s attributes were never touched. Legacy stellaops_platform.scheduler/scheduler_app/packs DROPPED 2026-09-14 (021 PLT-4, 94f86d6cec; evidence docs/implplan/_evidence/20260914-plt4-closeout/); their last creator, the scheduler block of devops/compose/postgres-init/00-v1-baseline.sql, was removed the same day (SPRINT_20260914_003 JEF-1).StellaOps.JobEngine.Persistence (sole authority; both predecessor DALs absorbed byte-identically, and the two central-migrator plugins deleted)control planestellaops_jobengine — DONEcontrolDONE—
release-orchestrator (+ agent-ca-init)release, release_orchestrator, release_orchestrator_agent, scripts — scripts VERIFIED LIVE ON THE OWN DATABASE and its platform-side copy DROPPED 2026-09-13 (021 PLT-4): docker inspect stellaops-release-orchestrator shows BOTH the primary connection and the Scripts__Postgres__ConnectionString side channel resolving stellaops_release_orchestrator, so the 3-table stellaops_platform.scripts copy was residue (1 bookkeeping row, 0 domain rows), not a live read. SPRINT_20260913_001 ORP-4 had been filed claiming the opposite and was ABANDONED on this measurement. — MOVED 2026-08-22: live on stellaops_release_orchestrator(role release_orchestrator, REVOKE CONNECT proven by a 10×10 CONNECT probe: 10 own accepts, 90 sibling refusals; consolidated baseline converged 55 domain tables + one ledger; table-scoped data-only copy moved 2,953/2,953 rows across 54 source-present tables, per-table row/digest parity and 25 FK-orphan probes all green; tenant_deployment_settings is the one target-only table; doctor 11/11 post-cutover — D-RO5-9, SPRINT_20260722_018). Legacy stellaops_platform schemas retained for rollback; RO-6’s own destructive-drop approval has nothing to approve — the confirmed-DEAD drop set is measured EMPTY on this estate (RO-3 2026-08-06, re-corroborated 2026-08-17); DROP SCHEMA release stays forbidden (Platform owns 19 live tables in it)ReleaseOrchestrator(+.Environment)control planestellaops_release_orchestrator — DONEcontrol3medium (Platform/Policy seams)
agent-core(verify — likely none / registry cache volumes)—control plane——3low
evidence-web + evidence-workerevidence (+ carried attestor, proofchain, evidence_locker, evidence_locker_app) — MOVED 2026-09-05 (011 EVD-9): live on stellaops_evidence(role evidence, owner-only CONNECT with REVOKE CONNECT … FROM PUBLIC, 13/13 forced RLS asserted, parity 31/31 tables at count and canonical-row SHA-256, fk_artifacts_bundle added, 0 family sessions left on stellaops_platform). Legacy stellaops_platform schemas retained: the drop is a SEPARATE destructive approval and has NOT been taken — see EVD-10b.StellaOps.Evidence.Persistence.Consolidatedevidencestellaops_evidence — DONEcontrol3low
attestor — DELETED 2026-09-05 (011 EVD-8/EVD-10b)attestor, proofchain — MOVED 2026-09-05 with the family (row above). Compose key, container and image all gone; the host project has no Program.cs. The domain and application libraries are kept and live under src/Evidence/__Libraries/Attestor.Persistence (library only)evidencestellaops_evidence — DONEcontrol3low
attestor-tileproxy— (no database)—evidence———alive and unchanged; an Evidence-family member by ownership, keeping its own compose key
tsa— (no database; compose_tsa-data volume)—evidence———alive and unchanged; an Evidence-family member by ownership, keeping its own compose key and volume
signersigner — MOVED 2026-08-17 (019 SGN-5): now stellaops_signer, converged by StellaOps.Signer.Persistence. The former crypto entry here was WRONG and is deleted (D-SGN1-1, re-verified live 2026-08-18: stellaops_signer contains only public and signer; Signer’s 50-project closure holds no shared-persistence library, so it instantiates no crypto schema)Signer.Persistence (was Signer.KeyManagement)control planestellaops_signer — livecontrol3low
evidence-locker-web + worker — DELETED 2026-09-05 (011 EVD-8/EVD-10b)evidence_locker (+ _app) — MOVED 2026-09-05 with the family. The object store was carried, not recreated: compose_evidence-data keeps its physical name at the same in-container path. Blue/green siblings retired in the same changeEvidenceLocker persistence (libraries only)evidencestellaops_evidence — DONEcontrol3low
findings-ledger-webfindings — MOVED 2026-08-26 (010 FND-9): the consolidated findings-web/findings-worker family is live on stellaops_findings(role findings with BYPASSRLS+CREATEROLE; owner-only CONNECT; Findings 001–008 + Eventing 001–004 converged fresh at HEAD 3eeed9b925; table-scoped data-only copy 7/7 tables with row-count + content-hash parity — 533 security_finding_projection, 37 security_risk_snapshot, five singles incl. the byte-faithful seam cursor). The shared platform eventing schema was measured LIVE shared infrastructure and excluded from copy and revocation. Legacy stellaops_platform schemas retained: the drop is a SEPARATE destructive approval with its own window and has NOT been taken. Measured 2026-08-27 by exact count(*) (note pg_stat_user_tables reports 0 for all four and is stale): findings 35 tables / 2296 kB / 5 rows, findings_security 7 tables / 1408 kB / 575 rows (security_finding_projection 533, security_risk_snapshot 37, schema_migrations 5), analytics 12 tables / 1312 kB / 2 rows, riskengine 2 tables / 152 kB / 1 row. So the drop is not row-free — it discards 570 real projection rows.Findings ledger persistencedata planestellaops_findings — DONEcontrol3low
riskengine-web + worker — DELETED 2026-08-27 (010 FND-10)riskengine — MOVED 2026-08-26 with the family (row above); riskengine.risk_score_results measured 0 rows at source (the recorded worker-scoring fold gap), so the copy carried nothing. Both hosts and their source trees are now gone (owner ruling Q-2); the schema is kept and owned by the consolidated familyRiskEngine.Core + .Infrastructure (libraries only)data planestellaops_findings — DONEcontrol3low
findings-security-webfindings_security — MOVED 2026-08-26 with the family (row above); the consolidated security plane serves from Findings-owned local tables since the FND-X18-7 read cutover (2026-08-27); the typed 503 is now only the cold-start answer on an estate with no advisory generation — corrected 2026-09-16, SPRINT_20260914_001 VRP-6Findings.Security.Persistenceread-modelstellaops_findings — DONEcontrol3low
findings-vulncorrelation — DELETED 2026-08-27 (010 FND-10)analytics — MOVED 2026-08-26 with the family (row above); all source analytics tables were 0 rows at the move (2 bookkeeping rows at 2026-08-27). The host and its source tree are now gone; the Application/Persistence libraries are kept and remain unwired (the X18 re-home they were waiting on landed under FND-X18-5…7, so their disposition is now a separate call — noted 2026-09-16, SPRINT_20260914_001 VRP-6)Findings.VulnCorrelation.Persistenceread-modelstellaops_findings — DONEcontrol3low
signalssignals — MOVED: live on stellaops_signals(role signals, REVOKE CONNECT proven 2026-08-23; 23 tables + 2 matviews + 4 views, ledger = the two owned rows, zero rows in every table). Legacy stellaops_platform.signals retained for rollback and measured zero domain rows — a real count(*) sweep, not n_live_tupSignals.Persistencedata planestellaops_signals — DONEcontrol3low
sbomservicesbom — MOVED 2026-08-22: live on stellaops_sbomservice(role sbomservice, REVOKE CONNECT proven 2026-08-23; sbom 26 tables + an eventing schema of 7). Legacy stellaops_platform.sbom retained for rollback with its 6 non-ledger rows, all of which are present in the targetSbomService.Persistencedata planestellaops_sbomservice — DONEcontrol3low
graph-apigraph + reachgraph— measured LIVE 2026-09-07 (ROA-7, read-only): already on stellaops_graph. The container recreated 2026-09-07 carries STELLAOPS_POSTGRES_GRAPH_CONNECTION -> stellaops_graph as role graph, and that database holds BOTH schemas with exactly the two-row ledger SPRINT_20260722_023 GRA-9 specifies (001_graph_consolidated_baseline.sql, 002_force_reachgraph_tenant_rls.sql, applied 2026-09-02). All six CAS tables across both databases are 0 rows. This is currency, not a claim that GRA-9’s window closed — that row still owes its forcing functions and its own status.Graph.Persistence (consolidated baseline)data planestellaops_graphcontrol3low
reachgraph-web — SOURCE DELETED 2026-09-08 (023 GRA-10)reachgraph — the schema lives on in stellaops_graph beside graph, converged by StellaOps.Graph.Persistence’s consolidated baseline; what was deleted is the second HOST, not the data. GRA-10 removed src/ReachGraph/, the compose service block, the publish key and the services-matrix row, and moved the direct reachgraph.stella-ops.local alias onto graph-api. The last live measurement before deletion (GRA-9 window close, 2026-09-08) found the host DORMANT: no database connection at all, Router publication off since ROA-7, X20’s publisher unarmed, all three CAS tables 0 rows. The running container is not removed by that change — stopping stellaops-reachgraph-web and re-rendering the 17 recorded chains that still name it is GRA-10’s live half.— (was ReachGraph.Persistence)data planestellaops_graphcontrol3low
binaryindex-web (source retired, BIN-10)Predecessor binaries, binary_index; golden_sets retires to fixtures/bench per D-BIN3-6No current source migration ownerretained predecessor databinary knowledge → stellaops_vuln; no predecessor business payload to copy; DROP deferred—3low
symbols (source retired, BIN-10)Predecessor symbols; demo source/catalog seeds excluded from hubNo current source migration ownerretained predecessor datametadata → stellaops_vuln, blobs → configured CAS (§6.2); no predecessor manifests/blob bytes; DROP deferred—3low
export-web + export-worker — STOPPED 2026-09-13 (025 OK-10), frozen at src/__Obsoleted/ExportCenter/export_center (+ _app) — MOVED 2026-09-13 to stellaops_offlinekit, where it sits beside the new offlinekit schema (measured 2026-09-14: that database holds exactly export_center, export_center_app, offlinekit). The predecessor database stellaops_exportcenter still exists and still holds its own export_center/export_center_app copy — it is the window’s rollback, and dropping it plus revoking role exportcenter is a SEPARATE destructive approval that has not been takenStellaOps.OfflineKit.Persistence (was ExportCenter.Infrastructure)control planestellaops_offlinekit — DONEcontroldone—
replay-webreplayReplay.WebServiceevidencereplaycontrol3low
integrations-webintegrations — MOVED 2026-08-23: live on stellaops_integrations; the legacy shared schema was DROPPED 2026-09-14 (021 PLT-4, only migrator bookkeeping remained)StellaOps.Integrations.Persistence (single host-owned authority; Platform plugin retired 2026-08-24)control planestellaops_integrations — DONE; Policy gate decisions read through Policy’s owner API since 2026-09-14 (X23, ORP-3) — no cross-database read remainscontroldone—
packsregistry-web + worker (retired 2026-09-12, 012 JOB-9)packs — folded into the JobEngine family row abovemerged into StellaOps.JobEngine.Persistencecontrol planestellaops_jobengine — DONEcontrolDONEhosts frozen at src/__Obsoleted/JobEngine/
doctor-web (+ evidence-init) RETIRED 2026-08-17 (009 DOC-5 stage 4) — deployable, routes, image and job kind removed; the doctor SCHEMA survives pending the stage-5 destructive dropdoctorDoctor persistenceopsdoctorcontrol3low
issuer-directoryissuer — MOVED 2026-09-08 (016 AUTH-9, fourth window): live on stellaops_authority, served by the FOLDED Authority host rather than by its own deployable (S0 container-shape decision). The standalone stellaops-issuer-directory container was stopped in that window and the gateway route retargeted to authority.stella-ops.local. The copy carried nothing — source stellaops_platform.issuer measured 0 rows in all four domain tables at the fence, so the parity gate read NOOP_ALREADY_EQUAL; the schema was already converged in the target by migration 024 (AUTH-3). Owned by role authority_admin, which holds no CONNECT on any sibling database. The legacy stellaops_platform.issuer schema was DROPPED 2026-09-13 (021 PLT-4), exactly as 016 AUTH-10 handed it over measured: 0 rows in all five domain tables, the only content being 4 retired-migrator schema_migrations rows.IssuerDirectory persistence (folded into StellaOps.Authority)control planestellaops_authority — DONEcontroldone—
vexlens-webvexlens— schema DROPPED from stellaops_platform 2026-09-13 (021 PLT-4 destructive window): 7 tables, every domain table 0 rows, only 4 retired-migrator schema_migrations rows. The module was deleted at VULN-B1 and src/ contains zero vexlens. SQLVexLens persistence (retired)read-model—control3low
airgap-controller — RETIRED 2026-09-11 (025 OK-5), frozen at src/__Obsoleted/AirGap/StellaOps.AirGap.Controller/airgap— the schema does not exist on this estate. Measured 2026-09-14: every stellaops* database scanned for nspname='airgap' returned zero, and there is no stellaops_airgap database. It was never converged into a family database, so this row was allocating an owner to a schema with no rows and no home. Sealed-posture state lives in Platform’s ENVIRONMENT-scoped custodian platform.environment_state (present, 0 rows at the same measurement — no posture is declared on this estate)— (was AirGap persistence)control planenone — posture is Platform’s environment_state; there is no AirGap-family databasecontroln/a—
registry-web + registry-token (the Registry family, ADR-041 OD-2)registry (registry-web: repositories/manifests/blobs/tags + upload sessions), registry_token (registry-token: plan_rules/plan_audit)StellaOps.Registry.Persistence (schema registry); registry-token migrates registry_token in-service (RegistryTokenPersistenceExtensions.cs:37)control planestellaops_registry — DONE, both roles share the family database; REVOKE CONNECT applied, sibling roles refusedcontroldone—
unknowns-webunknowns + unknowns_app — MOVED 2026-08-22: live on stellaops_unknowns(role unknowns, REVOKE CONNECT proven 2026-08-23; RLS enabled AND FORCEd). There is no legacy schema to drop: unknowns/unknowns_app never existed in stellaops_platform (re-confirmed 2026-08-23) and the RC1 acceptance cluster that once held a converged empty copy is gone — no container, no volumeUnknowns.Persistencedata planestellaops_unknowns — DONEcontrol3low
workflow host (source retained/dormant; owner ruling 2026-08-19 keeps it for future use)workflow (10-table central-migration residue; fresh platform databases no longer converge it)Workflow.DataStore.PostgreSQLdormant control planeWorkflow-family database + role on activation (P4); no move nowcontrol3low
bench/tools (non-runtime)groundtruth, public; retired golden_sets reference data—fixtures/benchD-BIN3-6: golden sets never enter hub runtime migrations; predecessor sink seeds are preserved in committed source, no authored data to move—3low

Notes:

3. Cross-service SQL inventory (violations to burn down)

X1–X17 were re-verified by EST-2 on 2026-09-10 (Europe/Sofia): nine closures, one bar-dependent result and seven remaining source entries. The report carries current anchors, owning-row states, commit/test links and executed checks. It closes the verification task, not the estate-wide empty-register gate.

#Consumer → targetKindEvidenceDisposition (target, ADR-039 rev-2)
X1Excititor retention sweep → former vuln.* / vexhub.* targetsCLOSED: component retired with the plane, 2026-09-14 (SPRINT_20260914_001 VRP-4 chose the broader bar — component retirement — and it is met: VexRetentionSweepService and its options/metrics/tests have lived under src/__Obsoleted/Concelier/StellaOps.Excititor.Worker/Scheduling/ since VULN-B1 d80e7ce610 (2026-09-11, frozen per CoC 15.4); no live host composes it)Prior: BAR-DEPENDENT — VexRetentionSweepService retains only owned sweep targets; default disabled; VexRetentionSweepAuthorityTests 2/2 at the 2026-09-10 auditForeign-target authority is gone and pinned (1526a7f169); the sweep class itself remains. EST-2 records both interpretations rather than choosing the broader component-retirement bar. OWNER RE-TAGGED 2026-09-14 → SPRINT_20260914_001 (VRP-4): the sprint this row named is ARCHIVED, so the entry had no owner. A register tag is only an owner while its sprint is ACTIVE.
X2Excititor → vexhub.sources/statementsCLOSED: writer retired with the plane, 2026-09-14 (SPRINT_20260914_001 VRP-1: PostgresVexHubProjectionSink.cs has lived under src/__Obsoleted/Concelier/__Libraries/StellaOps.Excititor.Persistence/Postgres/VexHub/ since VULN-B1 d80e7ce610 (2026-09-11, frozen per CoC 15.4); no live project compiles it and no host writes vexhub.*)Prior: OPEN — PostgresVexHubProjectionSink.cs:166,336,366 still inserts into the old schemaThe hub successor does not remove this source anchor. 003/008 retain the legacy-source/federation disposition; no closure or active-runtime-write claim is inferred. OWNER RE-TAGGED 2026-09-14 → SPRINT_20260914_001 (VRP-1): the sprint this row named is ARCHIVED, so the entry had no owner. A register tag is only an owner while its sprint is ACTIVE.
X3Policy → vuln.issue_gate_decisions / vuln.advisory_affectedCLOSED: reader deleted 2026-09-14PostgresFindingsLookup.cs reads only policy.vuln_gate_current; pinned by ReleaseComponentsLookupIntegrationTests.SourceContract_HasOwnerClientAndNoForeignSql (rejects FROM/JOIN on vuln.* and the three retired tokens) and FindingsAdvisorySourceOptionsTests (options expose only OnUnavailable; a leftover AdvisorySource/ShadowCompare key fails startup naming the key and the row)SPRINT_20260914_001 VRP-2. Measured before deletion: the two tables existed in none of the 21 stellaops_* databases (dropped 2026-08-04, 008 G1), Policy connects only to stellaops_policy, the live host ran AdvisorySource=PolicyProjection, and selecting either retired source yielded 42P01 → deny. The six reads were dead rollback code with a dead target, so they were deleted rather than re-homed: LoadLegacyCvesForComponentsAsync, LoadGateDecisionCvesForComponentsAsync, RunAdvisoryQueryPassAsync, the parity classifier and the FindingsAdvisorySource enum are gone, and the compose / expectations / bundle keys with them. No DatabaseOwnershipConformanceTests or SharedCatalogReadConformanceTests pin ever named this entry (verified by grep 2026-09-14), so there is nothing to burn.
X4CLI compact export → vuln.issue_gate_decisions ⋈ vuln.issue_linksetsCLOSED: SQL branch retired 2026-09-14 (SPRINT_20260914_001 VRP-3, verify-and-close): PostgresCompactVulnDbCorpusSource.cs has lived under src/__Obsoleted/Cli/ since VULN-B1 5b4847c9a6 (2026-09-11); the live VulnDbCommandGroup.cs constructs only HubCompactVulnDbCorpusSource (:254) and names no Npgsql/connection string. Prior: OPEN — PostgresCompactVulnDbCorpusSource.cs:147-148; VulnDbCommandGroup.cs:289 still constructs it beside the Hub source008 VULN-G4 is DONE for signed Hub export/offline acceptance. X4 source retirement remains on the 003/008 and final PLT-4 register worklist; the completed API path is not an absence pin for SQL. OWNER RE-TAGGED 2026-09-14 → SPRINT_20260914_001 (VRP-3): the sprint this row named is ARCHIVED, so the entry had no owner. A register tag is only an owner while its sprint is ACTIVE.
X5CLI mirror seed / rebuild → Concelier persistenceCLOSED: operator SQL retired and the guard exception burned 2026-09-14 (SPRINT_20260914_001 VRP-3, verify-and-close): the live MirrorSeedCommandGroup.cs is 112 lines with no SQL or connection string (mirror seeding moved to the owner’s API at VULN-B1 5b4847c9a6), and DatabaseOwnershipConformanceTests.CliGenericConnectionExceptions is an empty register pinned Assert.Empty (:1117,1146). Prior: OPEN — MirrorSeedCommandGroup.cs:472,547; the CLI generic-connection guard still carries this exact exception026 CM-4 records the owner-side mirror-seed/federation prerequisite. The passing exception register prevents growth; it does not prove closure. OWNER RE-TAGGED 2026-09-14 → SPRINT_20260914_001 (VRP-3): the sprint this row named is ARCHIVED, so the entry had no owner. A register tag is only an owner while its sprint is ACTIVE.
X6Authority → Platform shared.tenants writerCLOSEDWriter/dependency deleted in 1265d4d13f; AuthorityPlatformDatabaseIsolationConformanceTests 3/3 plus folded-host mutation tests 2/2Platform converges its own catalog over its own connection. AUTH-25 header reconciled to DONE against all eight already-ticked criteria and the executed AUTH-9d receipt; no new live PATCH was run by EST-2.
X7Authority → Platform IdP importCLOSEDImporter/data-source removal 70fd9d4f7b; Authority own-connection and Platform-isolation conformance passed016 AUTH-4 DONE. The old generic/Platform connection cannot return through the guarded source/compose surfaces.
X8Policy → Scheduler persistenceCLOSEDPolicyOwnedGateEvaluationQueue (80756a1ef2); X8_PolicyCompilesNoJobEngineImplementation_AndTheRegisterAgrees passedQueue state belongs to Policy; archived POL-F3 and the 012 X8 disposition retain the implementation evidence. No JobEngine/Scheduler implementation pair remains in Policy.
X9Historical writer-less release.* security projectionsCLOSED: retired residue2026-09-10 read-only probe: all five tables absent in both Platform and RO databases; RO residue/baseline guards 3/3, including a present-with-rows controlRO-1 had already disproved the cited 574-row footprint for this estate. 865545d5da pins residue detection and excludes those tables from RO convergence. No new DROP or invented deletion commit.
X10Doctor central cross-schema migration checks / shared tenant catalogCLOSED0daa7e086c deleted the five checks, schema enumerator, ledger seam and shared-tenant reader; check-ID retirement 5/5, per-schema integration retirement 1/1009 DOC-4/DOC-5. Dedicated guards live under src/__Libraries/__Tests/StellaOps.Doctor*; the broader DOC-4b worker capability remains separate.
X11Findings.Security → Scanner SQLCLOSED: source boundaryOld store frozen by 13b0a644d7; active-generation composition and compiled foreign-schema prohibition passed (2/2 dedicated checks)Findings consumes owner facts locally. The SCN-7 seam pin b9a05a30df prevents returning Scanner SQL/DSN coupling; X18 activation/availability remains separate.
X23Integrations → Policy policy.gate_decisionsCLOSED: owner API (2026-09-14)PostgresRegistryImagePolicyDecisionProvider deleted; HttpRegistryImagePolicyDecisionProvider calls POST /api/v1/policy/gate/decisions/by-digest. Absence pinned by CanonicalCompose_ReadsPolicyThroughItsOwnerApi_AndPinsScannerProjectionAuth and by the empty generic-connection register.SPRINT_20260913_001 ORP-3. This entry is recorded CLOSED rather than omitted because the read was live and WRONG, not merely unlawful: it served stellaops_platform.policy, a copy frozen on 2026-08-15 (its vuln_gate_current sat at 10,503,548 rows for a month while Policy’s own database was at 11,144,755 and growing; the gate_decisions table the read actually hit holds 5 rows in the owner database), while Policy ran on stellaops_policy. Register ID corrected 2026-09-14 at landing: this row was filed as a second X21 while X21 (Findings.Security → sbom) already existed below; renumbered X23, the next free ID. The tenant half (a hand-rolled shared.tenants slug→UUID lookup, X14/ORF-2) was discharged by DELETION in the same change: Policy takes the tenant from the caller’s bearer and resolves it through its own replica, so the lookup did not move — it stopped existing.
X12Integrations → Scanner SQLCLOSED: source boundaryScannerProjectionService populates the local projection; ActiveConsumers_ReachScannerOnlyThroughOwnerSeams forbids Scanner SQL and DSN referencesb9a05a30df, archived 024/017. Runtime transport/lag evidence is distinct from this source closure.
X13Policy → Scanner SQLCLOSED: source boundaryIScannerDigestDetailClient is required and Scanner SQL/DSN references are forbidden by the same executed consumer-seam pinb9a05a30df, archived 007/017. The separate retained vulnerability SQL is still X3, not silently closed here.
X14Legacy shared-catalog readers → shared.tenants / shared.actor_identityPARTIAL (2026-09-12)The library anchor this row named — PostgresStellaOpsTenantResolver in IStellaOpsTenantResolver.cs — is GONE, deleted by 027 SCR-3 after measuring ZERO call sites, with SharedCatalogReadConformanceTests preventing its return. KnownDirectReaders is 7 entries, down from 14 (Policy, ReleaseOrchestrator and IssuerDirectory legacy resolvers retired; Platform’s three re-classified to the producer register because 016 AUTH-25 makes Platform the owner-converger of shared.tenants).027 ARCHIVED 2026-09-12 with all five rows DONE; 016 AUTH-10 closed 2026-09-11. Residual readers have a live owner in SPRINT_20260912_001 (ORF-1/2/4) — two of them are live defects, since RO’s own shared.tenants converged EMPTY. One entry is PERMANENT: an applied migration (ADR-004) that can never expire, so the register’s floor is one, not zero.
X15Platform central migration mechanismCLOSED: mechanism DELETED 2026-09-14IMigrationModulePlugin, MigrationModulePluginDiscovery, MigrationModulePlugins.cs, ReleaseMigrationRunner, the static registry and the CLI stella system migrations-* surface are deleted by 8c2ea1505c. Pinned by CentralMigratorMechanism_IsDeleted, CentralMigrator_HasNoCliFace and NoManualCategoryMigrationsTests.021 PLT-4 DC-26 closed the source mechanism. Per-service startup convergence and ADR-004 forward-only migrations remain required. Live schema retirement is separate from this source census.
X16RO Plugin Registry library instanceCLOSED: per-host homingParameterized schema repair cd25df804c; PluginRegistryPerHostSchemaTests 4/4 plus RO own-connection conformance019 SGN-2 and 018 RO-2/RO-5 DONE. A local platform schema in RO’s own database is a P4 library instance, not a connection to Platform. Platform’s remaining local migration plugin is X15.
X17Legacy Eventing producers/store → timeline.events/outboxCLOSED: owner named, cross-service write severed 2026-09-14 (0e26c27d37, TLC-1 owner half: timeline-web self-migrates timeline.events + hlc_state via 003_v1_timeline_eventing_adoption.sql, its Eventing connection is derived from the owner connection and fails closed on any other database; findings-web appends to its own P6 eventing.outbox on findings.dispositions.tenant.<hex>; timeline.outbox never existed live and its code is deleted). The Timeline CONSUMER of that stream is SPRINT_20260911_001 TLC-1’s feature work, not a register entry — delivered 2026-09-15: StellaOps.Timeline.DispositionProjection folds findings.dispositions into timeline.events through IInboxConsumer + a fenced lease, with its checkpoint in the eventing schema of stellaops_timeline; off by default behind FindingDispositionProjection:Enabled, and proved end to end on the reference estate 2026-09-15 (outbox seq 2 -> inbox -> timeline.events -> checkpoint -> the public HLC route; docs/implplan/_evidence/20260915-tlc1-consumer-window/). The row STAYS CLOSED: it closed on the owner ruling and the severed cross-service write, and the consumer adds a reader, not a second lineage. Prior: OPEN — EventingDbContext still defaults to timeline and maps both tables; EventingMigrationModulePlugin remains003 VULN-B4 delivered the reliable per-host mechanism (f8039fe1d7); Timeline’s consumer exit moved out of 020 W3-01 on 2026-09-11 (that row closed on the database move) into SPRINT_20260911_001 TLC-1, BLOCKED on the owner’s bounded consumer contract. Mechanism delivery does not close every producer/store exit.
X18Findings.Security → vuln.* (heavy) CLOSED: read cutover shipped and serving, 2026-09-16CLOSED (the cited joins are gone; what remains is one stale comment, now fixed)SecurityFindingProjectionStore.cs and its foreign-schema tests were frozen to src/__Obsoleted/Findings/ at FND-26 (2026-08-29); AggregatedRiskStatusService.cs named vuln.* only in a COMMENT, corrected 2026-09-16 with its duplicate in AggregatedRiskStatusModels.cs. findings-web composes AddActiveGenerationSecurityReadModel() (StellaOps.Findings.WebService/Program.cs:255-258); the live pin is FindingsSecurityPersistenceForeignSchemaTests (no tolerated foreign schema at all) plus ScannerConsolidationConformanceTests’ X18-7 composition assertions.SPRINT_20260914_001 VRP-6, verify-and-close. The prior body text was STALE, not merely unfinished: it said “FND-X18-5…7 remain, so current routes keep the typed 503”, but all three rows are Status: DONE in docs-archive/implplan/SPRINT_20260722_010_Findings_service_consolidation_program.md (:1071, :1094, :1129) and the runbook recorded the plane serving a live generation on 2026-08-27. The typed 503 is the COLD-START contract now, not the served state. Measured live 2026-09-16: findings_security.advisory_matcher_projection 40,253,283 rows, advisory_metadata_projection 1,365,172, advisory_exploit_projection 750,252, advisory_match_projection 3,700, advisory_corpus_section_import 9 over 3 generations — the consumer, import, matching and read cutover are all present and populated. Residual is ARMING, not this register’s: the shipped compose defaults for FINDINGS_ADVISORY_SBOM_PROJECTION_ENABLED / FINDINGS_SCANNER_SECURITY_PROJECTION_ENABLED are false while this estate runs them true, which is recorded in that sprint’s Decisions & Risks for a Findings owner. The FND-X18-7 STOP box is not open — an earlier draft of this row said it was, quoting the archived 010 rather than the live runbook; the warning was retired 2026-08-27 by running the full hold/rollback/restore/resume cycle, and the runbook’s own diagnostic on 2026-09-16 returns a single active generation with witness_current = t and no staging row.
X19SbomService → vuln.sbom_canonical_matchCLOSED: dead POST retired, 2026-09-16CLOSED — was an API call, never a direct write (corrected 2026-08-09)SbomLearnForwarder.cs is deleted with ConcelierLearnOptions, its DI, its HttpClient and its spec; SbomUploadService no longer forwards; every SbomService__Concelier__* key is gone from compose, the golden-path override and the regenerated bundle. Absence pinned by SbomServiceConsolidationConformanceTests.SbomService_ComposesNoConcelierLearnForwarder_AfterX19Retirement, which guards the code AND the config carriers (an unbound key binds silently).SPRINT_20260914_001 VRP-7. The precondition this row itself set is MET AND LIVE. The row required “a closed contract and P6 sbom.versions catch-up with transactional sbom.uploaded/sbom.version.retired, epoch/head/horizon, remote-consumer retention, and bounded snapshot+head bootstrap” before the POST could go. All of it shipped under FND-X18-3 (StellaOps.SbomService.Contracts, PostgresSbomLedgerRepository outbox append inside the upload transaction, migration 008_ledger_version_stream_fence.sql). Measured live 2026-09-16: eventing.outbox holds 52 sbom.uploaded + 1 sbom.version.retired on stream sbom.versions, sbom.ledger_versions carries 51 of 54 rows with a version_stream_seq, and eventing.remote_stream_consumers shows findings-advisory-sbom-v1 at seq 53 reporting 2026-09-16T14:07:32Z. Meanwhile the dead forwarder was still ARMED (LearnOnUpload shipped :-true) against a host frozen on 2026-09-11, so every BYOS upload made a round trip that could only 404. The 2026-08-04 G1 window had already ruled /api/v1/learn/sbom dies with the Concelier host (docs/implplan/_evidence/2026-08-04-sprint-008-vuln-g1-window.md:76). Scanner.Worker’s identical forwarder was retired in the same change (VRP-8).
X21Findings.Security → sbom.ledger_versionsRESOLVED 2026-08-10 (SPRINT_20260722_010)read (raw SQL LEFT JOIN LATERAL) — removedwas SecurityFindingProjectionStore.cs, resolving artifact_ref inside already-dead vuln.*/scanner.* statementsBoth joins remain deleted. FND-26 (2026-08-29) then froze the whole predecessor store to src/__Obsoleted/, and its SecurityFindingProjectionStoreForeignSchemaTests moved with it; the live pin is now FindingsSecurityPersistenceForeignSchemaTests, which asserts the SAME invariant over the live StellaOps.Findings.Security.Persistence assembly with no tolerated foreign schema at all. X21 does not reopen. Owner item 21(b)'s build-ready X18 design now supplies the deferred artifact identity through SbomService’s transactional version event + exact-version document seam into tenant-keyed Findings projections. FND-X18-3 and FND-X18-6 have since landed and the stream is live (see X19), so that identity now arrives through the owner seam. It was never permission for an HTTP lookup in a read/gate or a restored database join, and still is not.
X20Scanner.Worker → ReachGraphCLOSED: publisher retired (2026-09-14)HttpReachGraphPublisher, IReachGraphPublisher, ScannerWorkerOptions.Reachability.PublishToReachGraph / ReachGraphBaseUrl, the ReachGraphDigest analysis key and the Worker’s StellaOps.ReachGraph.Contracts reference are deleted; ReachabilityPublishStageExecutor keeps only the release-evidence publisher. Absence pinned by ScannerWorker_CompilesNoReachGraphProject_AfterX20Retirement.SPRINT_20260914_001 VRP-5. Measured 2026-09-14 before retiring: the live scanner worker carried no Scanner__Worker__Reachability__PublishToReachGraph (default false), no reachgraph container existed, and the target host was deleted at source on 2026-09-08 (023 GRA-10) — unarmed everywhere, target gone. The reachgraph.stella-ops.local alias and the api://reachgraph Authority audience are NOT removed here (Platform URL pointer / S001 baseline grant); they are residue, not a writer.
X22Platform topology synchronizer → RO-owned release.regions/environments/targets/agentsRESOLVED by 026 CM-6former raw SQL read — removedPostgresTopologySourceReader.cs was deleted. Platform now reads only active UUIDs from its own shared.tenants; HttpReleaseOrchestratorTopologySourceReader calls RO GET /internal/v1/release-orchestrator/topology-snapshot with a signed tenant-bound two-minute release:read envelope over the zero-reference producer contract. No Router route or SQL fallback exists.Registered owner read contract: schema v1; exact-tenant and cross-row partition checks; deterministic region/environment/target/agent ordering; Platform projection remains rebuildable. RO returns the union of built-in + extended agent capabilities and maps canonical Stale to wire offline.

Approved cross-service SQL exception register (ADR-039 P5): the previously named X6, X8 and X9 transitional approvals have reached their closure conditions and are removed from this approval list by the 2026-09-10 verification. This does not approve any remaining source violation. The target SQL-exception register is empty; new raw-SQL approvals require an ADR-039 amendment. X22 is the intended replacement pattern: an explicit, producer-owned HTTP read contract into a consumer-owned rebuildable projection, not permission to connect to the owner database.

X10 REMOVED from the approved list 2026-08-17 (SPRINT_20260722_009 DOC-4/DOC-5 stage 4). It was approved “until D11 lands”; D11 has landed and the reach is gone, not merely dispositioned — the five cross-schema checks, the information_schema.tables enumerator and the eleven-schema expected set are all deleted (0daa7e086c). Its ROW above stays, annotated CLOSED, per the convention X7 and X21 use: the table is the inventory and keeps its history, this sentence is the standing APPROVAL and must not name a contract nobody holds.

X7 also removed here, because its own row already reads “CLOSED 2026-08-17 — the cited anchor no longer exists” with the compose evidence beside it; leaving it in the approved list contradicted the row. Flagged rather than silently taken: X7 belongs to the Authority/D10 lane, so if that closure is premature the sentence is where to put it back.

4. Shared-fallback burn-down — COMPLETE (2026-09-14)

The generic STELLAOPS_POSTGRES_CONNECTION has no live consumer anywhere in the estate, and the transitional pin register that tracked its retreat is EMPTY. ADR-039 P3 is discharged.

The burn-down ran 21 service definitions → 11 → 1 → 0. The last two steps are worth separating, because they were different defects wearing the same variable:

Two guards hold the floor, and they are different in kind:

One residue, out of scope and owned elsewhere: docker-compose.local-export-worker-rollback.yml still carries ConnectionStrings__Default: ${STELLAOPS_POSTGRES_CONNECTION}. It is the recorded rollback posture of the LIVE stellaops-export-worker container (RAR-7), so it is deliberately a frozen historical value rather than a live pointer — but it is a real estate-wide grep hit and it belongs to the OfflineKit consolidation lane, not here.

5. Full rename map — every container: current → target

Legend: unchanged = same name/src path. (verify) = project name not yet confirmed against a code anchor — the owning program’s S0 task confirms it. D12 DAL renames (…Storage/…Database → StellaOps.<Service>.Persistence) land with each service’s own wave, never out-of-band. “shared DB” = stellaops_platform (name kept — owner correction 2026-07-22). The container/instance half of that naming plan is WITHDRAWN: the owner DECLINED it on 2026-09-13 (SPRINT_20260722_021 PLT-2, ABANDONED). The bare stellaops name is NOT applied at the container level — the Platform service container stays stellaops-platform, which is what container_name: ${STELLAOPS_ESTATE_PREFIX:-stellaops}-platform already renders, in the same shape as every other service. Grounds are recorded in PLT-2’s status; the load-bearing one is that nothing in the estate resolves Platform by container name (every consumer uses the platform.stella-ops.local alias), so the rename bought no behaviour and would have made Platform the sole exception to the prefix convention.

5.1 Vulnerabilities program (sprints 003–008)

Current microserviceSrc pathDB project(s) usedDB usedNew microserviceNew src pathNew DB projectNew DB
concelier (+ concelier-jobs-init), retired VULN-G6src/__Obsoleted/Concelier/StellaOps.Concelier.WebServiceStellaOps.Concelier.Persistence (historical)retired schemas (concelier, vuln)StellaOps.Vulnerabilities (vulnerabilities-web/-worker)src/Vulnerabilities/StellaOps.Vulnerabilities.Persistencestellaops_vuln
excititor-web + excititor-workersrc/Concelier/StellaOps.Excititor.WebService / .WorkerStellaOps.Excititor.Persistenceshared DB (vex)merged ↑src/Vulnerabilities/merged ↑stellaops_vuln
vexhub-websrc/VexHub/StellaOps.VexHub.Persistenceshared DB (vexhub)merged ↑ (dist schema)src/Vulnerabilities/merged ↑stellaops_vuln
vexlens-websrc/VexLens/(module deleted, VULN-B1)—shared DB (vexlens) — schema DROPPED 2026-09-13 (021 PLT-4)folded into Vulnerabilitiessrc/Vulnerabilities/merged ↑stellaops_vuln
— (EPSS data only)src/Scanner/ EPSS connector tablesStellaOps.Scanner.Storageshared DB (scanner.epss_*, ~2.05 GB)data relocates to the hub (P14); Scanner keeps no EPSS tables—StellaOps.Vulnerabilities.Persistencestellaops_vuln

5.2 Family merge programs (sprints 010–016)

Current microserviceSrc pathDB project(s) usedDB usedNew microserviceNew src pathNew DB projectNew DB
findings-ledger-web (withdrawn 2026-08-28, FND-20 — src/__Obsoleted/)src/Findings/StellaOps.Findings.Ledger.WebServiceStellaOps.Findings.Ledger (library w/ migrations, KEPT)shared DB (findings)StellaOps.Findings (findings-web/-worker)src/Findings/StellaOps.Findings.WebService/.WorkerStellaOps.Findings.Persistencestellaops_findings
riskengine-web + riskengine-worker (deleted 2026-08-27, FND-10)src/Findings/StellaOps.RiskEngine.WebService/.WorkerStellaOps.RiskEngine.Core + .Infrastructure (kept as libraries)shared DB (riskengine)merged ↑ (scoring = worker role)↑merged ↑stellaops_findings
findings-security-web (withdrawn 2026-08-28, FND-20 — src/__Obsoleted/)src/Findings/StellaOps.Findings.Security.WebServiceStellaOps.Findings.Security.Persistence (KEPT)shared DB (findings_security)merged ↑↑merged ↑stellaops_findings
findings-vulncorrelation (deleted 2026-08-27, FND-10)src/Findings/StellaOps.Findings.VulnCorrelation.WebServiceStellaOps.Findings.VulnCorrelation.Application/.Persistence (kept as libraries)shared DB (analytics)merged ↑↑merged ↑stellaops_findings
attestor (deleted 2026-09-05, EVD-8/EVD-10b)src/Attestor/StellaOps.Attestor.WebService— no Program.cs; src/Attestor/ survives only for the StellaOps.Signer.* librariesAttestor.Persistence (ProofChainDbContext), kept as a library under src/Evidence/__Libraries/shared DB (attestor, proofchain)StellaOps.Evidence (evidence-web/evidence-worker) — DONE 2026-09-05src/Evidence/StellaOps.Evidence.Persistence.Consolidated (name corrected 2026-08-10 — see note)stellaops_evidence
attestor-tileproxysrc/Attestor/StellaOps.Attestor.TileProxy——Evidence-family member by ownership since 2026-09-05 (EVD-8); compose key and container deliberately unchanged↑——
tsainfra container from devops/docker/tsa/ (openssl RFC3161 — no .NET host)—tsa-data volume, carried by staying attached to this unchanged key as compose_tsa-dataEvidence-family infra member by ownership since 2026-09-05 (EVD-8); not re-keyeddevops/docker/tsa/——
evidence-locker-web + worker (deleted 2026-09-05, EVD-8/EVD-10b)src/EvidenceLocker/StellaOps.EvidenceLocker— the WebService project has no csproj; the libraries live under src/Evidence/__Libraries/migrations superseded by the consolidated baselineshared DB (evidence_locker)merged into StellaOps.Evidence (011) — DONE 2026-09-05src/Evidence/merged ↑stellaops_evidence
scheduler-web (retired 2026-09-12, 012 JOB-9)src/__Obsoleted/JobEngine/StellaOps.Scheduler.WebService (frozen; Worker.Host retires with JOB-10)StellaOps.Scheduler.Persistence (migrations frozen; the DAL was absorbed byte-identically)shared DB (scheduler) → dropped from stellaops_platform 2026-09-14 (021 PLT-4)StellaOps.JobEngine (jobengine-web/-worker) — DONE 2026-09-12src/JobEngine/StellaOps.JobEngine.WebService/.WorkerStellaOps.JobEngine.Persistencestellaops_jobengine
packsregistry-web + packsregistry-worker (retired 2026-09-12, 012 JOB-9)src/__Obsoleted/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.WebService (frozen; the .Worker project retires with JOB-10)StellaOps.PacksRegistry.Persistence (migrations frozen; the DAL was absorbed byte-identically)shared DB (packs) → dropped from stellaops_platform 2026-09-14 (021 PLT-4)merged ↑ — DONE 2026-09-12↑merged ↑stellaops_jobengine
advisory-ai-web + advisory-ai-worker (compose keys deleted 2026-09-07, AAI-10)src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/.Worker — the same projects, renamed keysStellaOps.AdvisoryAI.Persistence (sole authority)shared DB (advisoryai)StellaOps.AdvisoryAI (advisoryai-web/-worker) — DONE 2026-09-04same module, consolidated hostsStellaOps.AdvisoryAI.Persistencestellaops_advisoryai
opsmemory-web (withdrawn 2026-09-07, AAI-10 — src/__Obsoleted/)src/AdvisoryAI/StellaOps.OpsMemory.WebService— the HTTP surface lives at __Libraries/StellaOps.OpsMemory.Application and is mapped by advisoryai-webmigrations superseded by the consolidated baselineshared DB (opsmemory)merged ↑ (module) — DONE 2026-09-04↑merged ↑stellaops_advisoryai
binaryindex-web (retired source)src/__Obsoleted/BinaryIndex/frozen predecessor persistenceretained predecessor schemas; DROP deferredHub-native binary facts and generation; standing retirement completed 2026-09-12src/Vulnerabilities/hub persistencestellaops_vuln
symbols (retired source)src/__Obsoleted/BinaryIndex/StellaOps.Symbols.Server/frozen predecessor persistenceretained predecessor schema; DROP deferredHub-owned global manifest/source/catalog metadata and CAS contentsrc/Vulnerabilities/hub persistence and configured CASstellaops_vuln
notify-websrc/Notify/StellaOps.Notify.WebServiceStellaOps.Notify.Persistence repositories + .Consolidated migrationsstellaops_notify (live)StellaOps.Notify (notify-web/-worker)src/Notify/StellaOps.Notify.Persistence (kept) + .Consolidated migration authoritystellaops_notify
notifier-worker (withdrawn 2026-09-08, NTF-10 — src/__Obsoleted/)src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService + .Worker— the delivery domain lives at src/Notify/__Libraries/StellaOps.Notify.Delivery and is composed by notify-workerown schema live-empty; hosted a crypto CredentialStore instanceshared DB (notifier, empty)merged ↑ (worker role) — DONE 2026-09-04, deployable retired on a constructed consumer.role=notify-worker forcing functionsrc/Notify/merged ↑stellaops_notify
issuer-directory (withdrawn 2026-09-11, AUTH-10 — src/__Obsoleted/Authority/)src/Authority/StellaOps.IssuerDirectory/StellaOps.IssuerDirectory.WebService— the issuer surface lives at src/Authority/__Libraries/StellaOps.IssuerDirectory.Api and is composed by authorityIssuerDirectory.Infrastructureissuer schema moved INTO stellaops_authority at AUTH-9’s fold window (2026-09-08, D-AUTH3-1)merged ↑ (role of StellaOps.Authority, S0 decision on container shape) — DONE: deployable retired once the fold was live, the container stopped and the gateway routed the prefix to authoritysrc/Authority/StellaOps.Authority.Persistencestellaops_authority
authoritysrc/Authority/StellaOps.AuthorityStellaOps.Authority.Persistencestellaops_authority (already own DB)unchanged + D10 (tenants/IdP sole owner; zero platform-DB access)unchangedunchangedstellaops_authority

D-EVD3-14 — the Evidence DAL name (corrected 2026-08-10, EVD-3 Stage B). The attestor row above originally read StellaOps.Evidence.Persistence. That assembly name is already taken by src/__Libraries/StellaOps.Evidence.Persistence, an unrelated shared library owning a different evidence schema (generic evidence records, EF Core, once migrated centrally by platform-web’s EvidenceMigrationModulePlugin — deleted with the plugin mechanism 2026-09-14, PLT-4) and compiled by Platform.Database. Two projects cannot share an assembly name, so the merged DAL is StellaOps.Evidence.Persistence.Consolidated, following the Notify / Timeline / ReleaseOrchestrator / Integrations / CredentialStore precedent. The two evidence schemas never collide — one lives in the shared platform database, the other in stellaops_evidence, where it holds the migration ledger pair plus the Evidence-owned Timestamp Assurance state from migrations 002/003. Applies to the evidence-locker-web + worker row too, which points at the attestor row via “merged ↑”.

5.3 Same service, new database (Waves 2–3; D12 DAL rename where deviating)

Owning programs: Scanner → SPRINT_20260722_017; ReleaseOrchestrator → 018; Signer → 019; Platform → 021 (endgame); Graph+ReachGraph → 023; Integrations → 024; the Registry family (registry-web + registry-token) → SPRINT_20260803_004 (ADR-041 OD-2 removed registry-token from 024’s scope); EvidenceLocker → 011; doctor-web → retirement via 009; remediation → archived; ExportCenter + AirGap (+ Mirror.Creator) → 025 (OfflineKit, owner-approved); every other row → SPRINT_20260722_020 (batch).

Current microserviceSrc pathDB project(s) usedDB usedNew microserviceNew src pathNew DB projectNew DB
platformsrc/Platform/StellaOps.Platform.Persistence — D12 rename LANDED 2026-08-18 (021 PLT-1); the old StellaOps.Platform.Database name is gone from src/shared DB (platform, shared)unchangedunchangedStellaOps.Platform.Persistence (D12)stellaops_platform(name kept — owner correction). Container name also kept: PLT-2’s stellaops-platform → stellaops rename was DECLINED by the owner 2026-09-13 and the row is ABANDONED
policy-enginesrc/Policy/StellaOps.Policy.EngineStellaOps.Policy.Persistenceshared DB (policy)unchanged + gains vuln gate projection (D3, sprint 007); publishes POST /api/v1/policy/gate/decisions/by-digest (policy:read, tenant from the bearer) for Integrations since 2026-09-14 (X23)unchangedunchangedstellaops_policy; the platform copy of policy was dropped 2026-09-14 except 11 pre-move tables (41 rows) awaiting Policy’s carry-or-discard ruling
scanner-web + scanner-worker (+ cache-init)src/Scanner/StellaOps.Scanner.Persistence is the sole migration authority; retained StellaOps.Scanner.Storage adapters run without migrations pending SCN-6shared DB (scanner, minus EPSS) → stellaops_scanner, MOVED 2026-08-29; source scanner/signals objects retained offline for rollback and a separately confirmed SCN-6 dropunchangedunchangedStellaOps.Scanner.Persistence (D12)stellaops_scanner — DONE
release-orchestrator (+ agent-ca-init)src/ReleaseOrchestrator/StellaOps.ReleaseOrchestrator.Persistence (+ Environment data source in WebApi)shared DB (release, release_orchestrator, release_orchestrator_agent, scripts) → stellaops_release_orchestrator, MOVED 2026-08-22 (D-RO5-9: 55-table consolidated baseline + one ledger; table-scoped data-only copy, 2,953/2,953 rows across 54 source-present tables; doctor 11/11); legacy stellaops_platform schemas retained for rollback — old-schema drop approval recorded as nothing-to-approve, the drop set is empty on this estateunchangedunchangedconsolidated StellaOps.ReleaseOrchestrator.Persistencestellaops_release_orchestrator — DONE
signersrc/Attestor/StellaOps.SignerSigner.Persistence (consolidated baseline is the live migration authority since 2026-08-17)shared DB (signer, crypto) → stellaops_signer, MOVED 2026-08-17 by SGN-5 (no crypto schema — D-SGN1-1)unchanged — stays separate (key custody, D14)unchangedStellaOps.Signer.Persistence (D12)stellaops_signer — DONE; old stellaops_platform.signer deliberately retained, drop approval DEFERRED to owner (019 SGN-6)
evidence-locker-web + worker— row moved to §5.2 (Evidence merge, 011) —
timeline-websrc/Timeline/StellaOps.Timeline.Persistence.Consolidated — the live startup authority since the 2026-08-24 window; ledger order 000 → 001_v1_timeline_consolidated_baseline → 002 all appliedshared DB (timeline) → stellaops_timeline, live (M4 2026-08-10 onto a dedicated cluster, then PTC-4/Window B moved it to the shared server 2026-08-14 and fresh-converged it; the corpus was deliberately not carried, owner ruling C). Legacy stellaops_platform.timeline snapshotted and dropped 2026-08-24 under that rulingunchanged; audit-event partitioning (review §3) DELIVERED — timeline.unified_audit_events is relkind='p' with monthly children 2026_06…2026_12 + DEFAULT and 452,007 rows (measured 2026-09-11); retention is partition maintenance, and the bridge’s rollback heap was dropped 2026-09-11unchangedper D12 — livestellaops_timeline — DONE (W3-01 closed 2026-09-11; doctor /doctor/checks 6/6 healthy)
export-web + export-worker → offlinekit-web + offlinekit-worker, LANDED 2026-09-13src/ExportCenter/StellaOps.ExportCenter/*→ src/OfflineKit/ (the predecessor tree is frozen at src/__Obsoleted/ExportCenter/)ExportCenter.Infrastructure → StellaOps.OfflineKit.Persistenceshared DB (export_center, 15 domain tables + a private migration ledger export_schema_version, verified OK-3 2026-08-04) → stellaops_offlinekit, MOVED 2026-09-13 (025 OK-10; six rows copied with parity, tenants replica converged at sequence 9). Source stellaops_exportcenter retained as the rollback — its drop and the exportcenter role revoke are a separate destructive window, not takenStellaOps.OfflineKit (owner-approved EVL-3, 025)src/OfflineKit/StellaOps.OfflineKit.Persistencestellaops_offlinekit — DONE
replay-websrc/Replay/StellaOps.Replay.Persistence(D12 extraction DONE 2026-08-09, W3-04)shared DB (replay) — repoint staged, window-gatedunchangedunchangedStellaOps.Replay.Persistencestellaops_replay
integrations-websrc/Integrations/StellaOps.Integrations.Persistence (IntegrationDbContext + canonical embedded baseline)shared DB (integrations) → stellaops_integrations, MOVED 2026-08-23; legacy shared schema retained for rollback and no destructive drop approvedStellaOps.Integrations (owner-approved, 024) — + registry-token role dropped: ADR-041 OD-2 supersedes the EVL-5 fold, the token service stays in the Registry familysrc/Integrations/StellaOps.Integrations.Persistence — single authority; Platform plugin/reference and staged .Consolidated project retired 2026-08-24stellaops_integrations — DONE
signalssrc/Signals/StellaOps.Signals.Persistenceshared DB (signals) → stellaops_signals, MOVED 2026-08-10 (re-converged on the shared server 2026-08-14 by PTC-3/Window A; zero rows on both sides throughout, so nothing was carried and nothing was lost); legacy stellaops_platform.signals retained for rollback and measured zero domain rowsunchangedunchangedunchangedstellaops_signals — DONE; the pre-window connection keys were retired from the resolver at M5 (2026-08-23), old schema drop DEFERRED to owner
sbomservicesrc/SbomService/StellaOps.SbomService.Persistenceshared DB (sbom) → stellaops_sbomservice, MOVED 2026-08-22; legacy stellaops_platform.sbom retained for rollback pending M5SBOM system of record (DC-34): absorbs vuln.sbom_registry + Scanner’s SBOM registry; blobs → object store; producers publish, consumers subscribeunchangedunchangedstellaops_sbomservice — DONE
graph-apisrc/Graph/StellaOps.Graph.Indexer.Persistenceshared DB (graph)StellaOps.Graph (owner-approved merge, 023)src/Graph/StellaOps.Graph.Persistencestellaops_graph
reachgraph-websrc/ReachGraph/— DELETED 2026-09-08 (023 GRA-10)ReachGraph.Persistenceshared DB (reachgraph)merged into StellaOps.Graph (023; CAS role)src/Graph/merged ↑stellaops_graph
doctor-web (+ evidence-init)src/Doctor/Doctor persistence (verify)shared DB (doctor)retired (owner-approved, 009 DOC-5) — module survives as plugin SDK + CLI; registry lives in Platform——— (schema dispositioned at retirement)
airgap-controller (+ airgap-time)src/__Obsoleted/AirGap/ (frozen OK-5; libraries relocated OK-14)StellaOps.AirGap.Persistence (one 32 kB state table)shared DB (airgap)split decided (owner EVL-3, 025): posture → Platform (seal/status/time-anchor; both deployables retire); Importer/Bundle/Sync + Mirror.Creator → OfflineKitposture src/Platform/; exchange src/OfflineKit/Platform.Persistence / OfflineKit.Persistenceposture → platform DB; exchange → stellaops_offlinekit
registry-web + registry-tokensrc/Registry/ (StellaOps.Registry.WebService + StellaOps.Registry.TokenService)StellaOps.Registry.Persistence (schema registry); token service in-service (RegistryTokenPersistenceExtensions.cs:37, schema registry_token)shared DB (registry_token) → stellaops_registry, MOVED — both roles live thererole of StellaOps.Integrations (owner-approved, 024) → SUPERSEDED by ADR-041 OD-2 (owner-confirmed change of an owner ruling, 2026-08-03): the token service stays in the Registry family, so the EVL-5 fold into Integrations does not happensrc/Registry/StellaOps.Registry.Persistence + in-service token ledgerstellaops_registry — DONE
unknowns-websrc/Unknowns/StellaOps.Unknowns.Persistenceshared DB (unknowns + unknowns_app) → stellaops_unknowns, MOVED 2026-08-22 by fresh convergence — there was never a copy in stellaops_platform to move, and the RC1 acceptance cluster that held one is goneunchangedunchangedunchangedstellaops_unknowns — DONE; no drop approval is owed — no legacy schema, database, role or volume exists to drop
remediationsrc/__Obsoleted/Remediation/ (archived 2026-08-19, 020 W3-14; central-migrator plugin deleted, so fresh DBs no longer receive the schema)— (no live migrator)nonearchived — executed (owner re-confirmed 2026-08-19; AdvisoryAI autofix is in-module; the four Console /security/remediation* routes retired in the same commit)———
workflow host (source retained/dormant; owner ruling 2026-08-19 keeps it for future use)src/Workflow/StellaOps.Workflow.DataStore.PostgreSQLshared-DB workflow residue; no deployed hostfuture Workflow-family service (new P4 activation sprint)src/Workflow/StellaOps.Workflow.DataStore.PostgreSQLWorkflow-family database on the shared PostgreSQL installation

Evaluation families (VexLens fold, Reachability, Offline/exchange): SPRINT_20260722_022.

5.4 No database / infrastructure (unchanged unless noted)

ContainerSrc pathDBChange
router-gatewaysrc/Router/StellaOps.Gateway.WebService— (Valkey)route table updates only (sprints 006 + per-program S5)
stellaops (console) + frontdoor + console-buildersrc/Web/StellaOps.Web—API base-map repoints only (S6 stages)
stellaops-cli (stella)src/Cli/— (was direct SQL)X4/X5 die: corpus/mirror via hub APIs
agent-coreRO agent host (verify)— (verify)unchanged
airgap-timeAirGap module—unchanged
init jobs (scanner-cache-init, doctor-evidence-init, advisory-ai-data-init, release-orchestrator-agent-ca-init, concelier-jobs-init)with their services—follow their owning service’s program (concelier-jobs-init → Vulnerabilities)
stellaops-postgres (postgres:18.1)—hosts control-plane DBs and every consolidated per-service DB — measured 2026-08-14 at the PTC-6 close-out: 7 stellaops_* databases (stellaops_platform 11 GB, stellaops_vuln 1398 MB, stellaops_authority 189 MB, stellaops_policy 39 MB, stellaops_signals 9790 kB, stellaops_timeline 9750 kB, stellaops_replay 7854 kB) + the postgres maintenance DB. CURRENCY NOTE — the 7-database census is a frozen 2026-08-14 measurement and is left as recorded. Re-measured 2026-08-23: the live count is 12 — stellaops_authority, stellaops_platform, stellaops_policy, stellaops_registry, stellaops_release_orchestrator, stellaops_replay, stellaops_sbomservice, stellaops_signals, stellaops_signer, stellaops_timeline, stellaops_unknowns, stellaops_vuln. (The 2026-08-18 note said 9; stellaops_release_orchestrator, stellaops_sbomservice and stellaops_unknowns have landed since.) Do not quote any of these three numbers — re-measure with SELECT datname FROM pg_database WHERE datname LIKE 'stellaops%', which is why the query is herestays; the only PostgreSQL server on the installation. Whole-estate sizing handed over from the retired vulndata cluster at PTC-5 (shared_buffers=4096MB, effective_cache_size=16GB, maintenance_work_mem=512MB, read back from pg_settings).
stellaops-postgres-vulndata—hosts stellaops_vulnRETIRED 2026-08-14 (SPRINT_20260810_002 PTC-5): stellaops_vuln moved onto the shared server per the ADR-039 P1 topology clarification. Container is STOPPED, not removed — its volume compose_vulndata-postgres-data (67.36 GB) is the repoint rollback.
stellaops-postgres-signals—hosts stellaops_signalsRETIRED 2026-08-14 (PTC-3, Window A). STOPPED exit 0; volume compose_signals-postgres-data (67.47 MB) kept as the repoint rollback.
stellaops-postgres-replay—hosts stellaops_replayRETIRED 2026-08-14 (PTC-3, Window A). STOPPED exit 0; volume compose_replay-postgres-data (65.4 MB) kept as the repoint rollback.
stellaops-postgres-timeline—hosts stellaops_timelineRETIRED 2026-08-14 (PTC-4, Window B). STOPPED exit 0; volume compose_timeline-postgres-data (264.1 MB) kept as the repoint rollback — it still holds the 105,895 audit events ruling C deliberately did not carry.
valkey / rustfs (seaweedfs) / registry (zot)——unchanged

Bench/tools data (groundtruth, public; analytics is VulnCorrelation) is non-runtime. The predecessor BinaryIndex host did migrate/serve golden_sets, but its approved retirement disposition is fixtures/bench (014 D-BIN3-6/A6), never a hub runtime schema. BIN-9 verified that the only predecessor golden-set rows are exact committed reference seeds, with no authored definitions, targets or audit records. The original schema remains pending separately authorized DROP; retaining it does not require creating a replacement runtime database.

5.5 Measured role boundary (census 2026-08-14, SPRINT_20260810_002 PTC-6)

The ownership boundary on a shared server is the per-database role plus REVOKE CONNECT, so it has to be demonstrated, not asserted. Re-measure with bash tools/scripts/deploy/postgres/probe-database-isolation.sh (read-only; SELECT 1 per cell).

Six login roles on stellaops-postgres: stellaops (superuser), policy, replay, signals, timeline, vuln. Six further *_admin roles (authority_admin, findings_ledger_admin, notify_admin, policy_admin, scheduler_admin, vex_admin) are NOLOGIN and carry no session.

A *_admin role is provisioned by the install path, never by a migration — a migration runs as the service’s own NOCREATEROLE role and cannot create one. The requirement, the from-scratch failure it prevents (42501 permission denied to create role) and the exact statement to run on an operator-managed cluster are stated once, in INSTALL_GUIDE.md - PostgreSQL roles the install path must create. Currency, 2026-09-14 (SPRINT_20260914_003 JEF-1): 05-service-admin-roles.sql provisions authority_admin (AUTH-26, 2026-09-09) and scheduler_admin (moved there when the platform scheduler block left 00-v1-baseline.sql; the JobEngine 001 baseline guards on that role). The existing 00-v1-baseline.sql still provisions findings_ledger_admin. The remaining Notify, Policy and VEX admin-role requirements and the installer verification command are recorded in the install guide above.

Isolation matrix: 25/25 PASS (5 service roles × 5 service databases) — each role reaches exactly its own database; all 20 cross-database attempts refused.

Currency (re-measured 2026-08-23, read-only). The census above is the frozen PTC-6 reading. The live login-role set is now eleven: stellaops (superuser) plus policy, registry, release_orchestrator, replay, sbomservice, signals, signer, timeline, unknowns, vuln. The four wave-3 roles this sprint owns were re-probed against all twelve stellaops_* databases with has_database_privilege(<role>, <db>, 'CONNECT'): timeline, signals, sbomservice and unknowns each return true for exactly their own database and false for the other eleven — a 4 × 12 slice that is exactly diagonal, 44 refusals and 4 grants. Note platform and authority still connect as the superuser and therefore sit outside this boundary by design, as PTC-6 recorded.

Two honesty caveats a reader must not gloss:

Two-consumer trap when re-running the probe: it derives pairs from STELLAOPS_POSTGRES_<SVC>_CONNECTION lines in devops/compose/.env, but the vulnerability hub’s connection is assembled in docker-compose.vulnerabilities.yml and has no .env presence at all since PTC-6 dropped VULN_POSTGRES_HOST. A plain run therefore silently reports a 4×4 matrix and omits vuln. Feed it an env file built from .env plus the value read from the live container (live truth, not a hand-copied duplicate that can drift), and delete that file afterwards:

cp devops/compose/.env /tmp/probe.env
docker inspect stellaops-vulnerabilities-web --format '{{range .Config.Env}}{{println .}}{{end}}' \
  | grep '^STELLAOPS_POSTGRES_VULN_CONNECTION=' >> /tmp/probe.env
bash tools/scripts/deploy/postgres/probe-database-isolation.sh --env-file /tmp/probe.env && rm /tmp/probe.env

6. Non-database owned state (P16 / DC-27 / DC-28)

Databases are not the only shared state: Valkey keyspaces/streams and object-store buckets/prefixes have named owners, a prefix convention, and P7 budgets. The FULL inventory is SPRINT_20260722_028’s deliverable — the rows below are the ones their owning task has already landed, so nothing waits on that sweep to be recorded.

6.1 Valkey keyspaces / streams

Live inventory 2026-08-02 (SPRINT_20260722_028 NPS-1: read-only SCAN + MEMORY USAGE against the lab stack — 882 keys, 19.3 MB total; re-verify against a production-shaped estate before treating counts as budgets). Convention: keys under {service}:, streams under the existing stream:{name} form; cross-service consumption of another owner’s stream is a DC-06/DC-07 envelope contract, never an ad-hoc key read.

Prefix / streamOwnerPurposeMeasured (lab)Budget / retention
router:requests:{service}, router:responses, router:cancels:{service}:{id}Router/gateway (messaging transport)per-service request queues, shared response queue, cancellation markers~404 keystransport-transient; leases + timeouts already bound growth (Router__Messaging__*)
notify:events:idemp:{producer}|{tenant}|{kind}|{id}Notifyevent-delivery idempotency dedup keys471 keys, ~184 B each, TTL ≈ 95 min observedTTL-bounded by construction; budget = rate × TTL
scanner:jobs, scanner:jobs:deadScannerscan job queue + dead letter2 streamsqueue depth is the operational alarm; DLQ needs an explicit drain runbook (flagged). Disposition (P16, 2026-09-14): unbudgeted by design — a queue is bounded by its producers, not by a byte cap; the DLQ drain runbook is Scanner’s (owner: Scanner family; carrier: SPRINT_20260722_028 NPS-3’s measurer work names it). No measurer exists for Valkey anywhere in the estate; that is recorded here rather than silently missing.
scheduler:runner, scheduler:plannerJobEngine (Scheduler)run/planner queues2 streamsas above. Disposition (P16, 2026-09-14): unbudgeted by design (bounded by producers); owner JobEngine; the 012 programme that used to carry it is archived, so the carrier is SPRINT_20260722_028 NPS-3 (Valkey measurer) — nothing else waits on it.
stream:orchestrator:eventsScanner (scanner-web) — owner corrected 2026-09-15orchestrator event stream (stream:{name} convention; the stream: prefix is applied centrally by ValkeyEventStream.cs:53, so configured names are bare)39 entries, all deadDisposition (P16, 2026-09-15, NPS-3 — supersedes the 2026-09-14 “owner JobEngine” disposition, which was wrong on every reading. JobEngine does not write this stream: GraphJobEventPublisher.cs:78 defaults to stella.events, and no stream:stella.events key exists in the live estate. The only LIVE configured producer is scanner-web — SCANNER_SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-orchestrator:events}" (docker-compose.stella-services.yml:1050, docker-compose.scanner.yml:295) with a real publisher registered at Scanner.WebService/Program.cs:638. Ownership follows the writer, so the owner is Scanner. Live probe 2026-09-15: the key is the estate’s ONLY stream:* key; XLEN 39; XINFO GROUPS returns zero consumer groups. All 39 entries are kind=CONSENSUS, spanning 2026-07-08…2026-07-11, written by VexLens through Eventing__StreamBridge__StreamName: "orchestrator:events" (docker-compose.cons-c1a-enable.override.yml:44). VexLens was retired 2026-08-04 (SPRINT_20260722_008 VULN-G1) and its tree deleted (SPRINT_20260722_003 VULN-B1), and that override no longer renders. So the content is residue from a deleted service, read by nobody, while scanner-web has emitted nothing here in over two months. Two consequences, both un-owned and neither a trimming policy: (a) the residue is §2.11 dead state — dropping it is a destructive action needing its own window, NOT a startup action; (b) the Findings consumers (ConsensusChangeIngestionOptions.cs:25,29 and two siblings) are configured against a producer that no longer exists — FND-21 ruled exactly this inert on 2026-08-28. Trimming policy + measurer: SPRINT_20260915_001 OSM-1/OSM-2.
timeline.eventsTimelinethe estate event stream (consumer group timeline-indexer, 12 consumers)XLEN 0 at scan (indexer keeps it drained)convention outlier flagged: dot-form name predates the {service}: rule; renaming is a coordinated producer+consumer change — record as accepted legacy or fold into a 012/Timeline move, do NOT rename ad hoc Disposition (P16, 2026-09-14): owner Timeline; the rename and the trimming policy ride SPRINT_20260911_001 TLC-1 (Timeline becomes a DC-29 stream consumer) — the same owner decision DC-26’s last half waits on; “fold into a 012/Timeline move” is retired wording, 012 is archived.

Cross-service ad-hoc key reads found: none. Every prefix has exactly one writing owner; the only cross-service touch points are the router transport queues (that is their job) and consumer-group reads on the streams, which is the sanctioned contract. Consumer groups in compose: 40 distinct Router__Messaging__ConsumerGroup values, one per service — matching the convention.

6.3 Named volumes (round-22 scope: ALL persistent non-Postgres state)

Live docker volume ls sweep 2026-08-02 (lab; compose project prefix stripped; scratch/QA projects excluded). Rule: every named volume has an owner and a class; *-plugin-scratch volumes (11 found: advisoryai, authority, concelier, doctor, excititor, integrations, notify, policy-messaging, scanner, scheduler, +) are ephemeral by contract (tmpfs-mode plugin staging) and carry a standing infra-exempt note as a family.

Method correction 2026-09-15 (NPS-3) — read this before trusting the section’s completeness. The inventory above was built from a live sweep of one lab, so it could only ever cover what that lab happened to be running on 2026-08-02. A declaration-side sweep — every top-level volumes: entry across devops/compose/*.yml, diffed against this table — had never been run. Running it found fourteen shipped states with no row at all, including two custody surfaces (stellaops-cryptopro-keys, openbao-data), three volumes live in the estate right now (findings-advisory-corpus, vulnerabilities-corpus-publications, packsregistry-upload-data), and one object-store bucket missing from §6.2 (registry-content) whose own compose comment calls it P16-owned. Every overlay that was switched off in the lab — openbao, cryptopro, rekor-tiles, tile-proxy, consul — was invisible to the original method by construction. Those rows are added below and in §6.2.

The consequence for anyone auditing P16: SPRINT_20260722_021 PLT-4 ticked its P16 audit criterion on “All 15 §6.1/§6.2 rows name an owner”. That counted rows in this table, not stores in the estate, so it could not have detected an absent row. A future audit must diff declarations against this table, not read the table back to itself. The mechanical form of that check belongs in tools/scripts/validate/ with a --self-test (AGENTS.md §2.13), and is carried by SPRINT_20260915_001 OSM-9.

VolumeOwnerClass / note
postgres-dataPlatform cluster (shared DB — shrinks as ADR-039 lands)source-of-truth; the 008 window drops ~91 GB of vuln-plane data from it
valkey-dataValkey (transport/queues above)transient-rebuildable (AOF persistence for restart continuity, not durability)
rustfs-dataobject store (see §6.2)source-of-truth for evidence/exports; per-prefix budgets in §6.2
evidence-data, attestor-proofchain-cas, scanner-poe-cas-dataEvidenceLocker / Attestor / Scannercontent-addressed evidence — custody surfaces, append-only
doctor-evidenceDoctor (retires with 009 DOC-5 → JobEngine run records)migrates, then dies
tsa-dataTSA infra container (011 family)infra; key custody noted
stellaops-vault-dataVault (KEK backend)custody surface — never infra-exempt
sm-remote-keys (compose file docker-compose.sm-remote.yml)SmRemotecustody surface (regional key material)
console-distConsole builder → gatewayrebuildable (refreshed on console image up)
scanner-cache-data, scanner-surface-cache, agent-core-registry-cacheScanner / agent-corecaches, rebuildable, size-budget candidates for NPS-3’s doctor check
offlinekit-dataOfflineKitcustody surface — carrier CAS (cas/), plus transient quarantine/ and multipart-buffer/. BUDGETED since SPRINT_20260722_025 OK-CAP (028 NPS-3): offlinekit-web declares owned state volume:offlinekit-import-cas measuring cas/ ONLY, and OfflineKitImportStorage.PromoteAsync carries the fail-closed P7 pre-write pause. Budget key Doctor:State:volume:offlinekit-import-cas:BudgetBytes, shell-safe alias OFFLINEKIT_IMPORT_CAS_BUDGET_BYTES. A missing or unreadable tree reports offlinekit-import-cas-measurement-unavailable rather than zero. Live red/green PASSED 2026-09-15 (028 NPS-3 window): Info/healthy unbudgeted at 918,047 bytes (independent du agreeing), a control carrier admitted 202 taking it to 975,834, then under a 1-byte budget Critical/unhealthy and a real import refused HTTP 507 CAS_WRITE_PAUSEDwith bytes unchanged 975,834 -> 975,834; budget removed, the same refused carrier then admitted to exactly 1,033,621 bytes — the figure the refusal projected, proving the guard’s arithmetic and not merely its verdict. Still open: the budget has no VALUE — OFFLINEKIT_IMPORT_CAS_BUDGET_BYTES is an empty-default pass-through in every compose file and in the live container, so the P7 guard ships OFF (SPRINT_20260915_001 OSM-6); and measure -> compare -> write is not an atomic cross-replica reservation (the same bound Scanner’s row records). Procedure: docs/modules/offlinekit/operations/import-cas-capacity-guard.md.
exportcenter-object-store-dataOfflineKitexport-run artifacts, audit bundles, NIS2/assurance bundles and risk bundles under /var/lib/stella/exportcenter. Mounted by BOTH offlinekit roles since OK-12 — the worker had no mount at all before that, so its artifacts landed in the container layer. No budget or measurer yet: the stella-exports/ and mirror/ prefixes in §6.2 name it, and NPS-3’s check has not been extended to it. Disposition (P16, 2026-09-14): owner OfflineKit; no budget or measurer yet — extend the OK-CAP IOwnedStateMeasurer to this volume under SPRINT_20260722_028 NPS-3; recorded rather than silently missing.
concelier-jobs, advisory-ai-{outputs,plans,queue}, agent-core-deployments, agent-ca-data, art-var, stellaops-registry-data, stellaops-tester-seed, regauth-basicowning service per name (Concelier / AdvisoryAI / agent-core / registry / tester)working state; owners recorded, budgets with NPS-3
telemetry overlay (prometheus/tempo/loki/grafana in docker-compose.telemetry.yml)Telemetry overlayinfra-exempt (observability retention governs), noted per round-22
stellaops-cryptopro-keysCryptoPro provider overlay (docker-compose.crypto-provider.cryptopro.yml:162)ADDED 2026-09-15 (NPS-3) — CUSTODY SURFACE, never infra-exempt. Mounted at /var/opt/cprocsp/keys under the comment “Optional: Mount key containers” (:86). This is regional crypto key material and belongs in the same class as stellaops-vault-data and sm-remote-keys; it was missing for the reason given in the section note below. No budget, no measurer; the custody question (who may read it, how it is rotated) matters more here than the byte count.
openbao-dataOpenBao / Vault secret backend (docker-compose.openbao.yml:55, mounted :82)ADDED 2026-09-15 (NPS-3) — CUSTODY SURFACE, never infra-exempt. The KEK/secret backend’s storage. In the documented LOCAL dev/CI path the server runs ephemeral -dev mode (in-memory, auto-unsealed) and this volume stays empty, but the real-server path (:41) persists sealed material here. Distinct from the already-listed stellaops-vault-data (docker-compose.integrations.yml:58) — the matrix carried one and not the other.
findings-advisory-corpusfindings-web (docker-compose.findings.yml:361)ADDED 2026-09-15 (NPS-3). Advisory-corpus cache root plus two sub-roots the same volume carries — Findings__ScannerSecurityProjection__CacheRoot (:337) and Findings__Nis2IncidentLedgerConsumer__CacheRoot (:351). Rebuildable cache; unbudgeted, no measurer. Live in the estate today (compose_findings-advisory-corpus).
vulnerabilities-corpus-publicationsvulnerabilities-web (docker-compose.vulnerabilities.yml:289)ADDED 2026-09-15 (NPS-3). Corpus publication artifacts; written by the publisher role (:257) and mounted read-only by the consumer (:191). Relates to the vuln/corpus/<generation>/ row in §6.2 and inherits its last-N windowing, but is a separate physical state with no budget or measurer. Live in the estate today.
packsregistry-upload-dataJobEngine (docker-compose.stella-services.yml:172; mounted read-only at docker-compose.jobengine.yml:174)ADDED 2026-09-15 (NPS-3). Packs-registry upload staging under /var/lib/stellaops/packsregistry/uploads. An append-side growth surface with no retention statement and no measurer — the shape P16 exists to police. Live in the estate today. Note JobEngine already returns 507 on a per-request artifact byte cap (PacksRegistryComposition.cs:642,662), which is a request-size limit, not a state budget.
rekor-tiles-datarekor-v2 transparency log (docker-compose.stella-infra.yml:46, mounted :201)ADDED 2026-09-15 (NPS-3). Append-only transparency-log tiles; grows monotonically by design, so “unbudgeted” needs an explicit retention/rotation statement rather than silence. No measurer.
stellaops-customer-registry-dataintegrations overlay fixture (docker-compose.integrations.yml:62, mounted :326)ADDED 2026-09-15 (NPS-3), then classed out of P16 scope in the same pass. Present in the live estate (container stellaops-customer-registry) and absent from this table. Traced to a stock OCI registry at /var/lib/registry — a customer registry under test, the same family as the gitea/gitlab/nexus fixtures below, not state a Stella Ops service owns. Distinct from stellaops-registry-data (the estate’s own registry). The compliance golden-path overlay re-declares it under a per-estate prefix (${CIL2_ESTATE_PREFIX}-customer-registry-data), which is a copy of the same fixture, not a second owner.
consul-data (docker-compose.integrations.yml), tile-cache + tuf-cache (docker-compose.tile-proxy.yml), corpus-data (docker-compose.corpus.yml), stella-cli-state (docker-compose.cli.yml), otel-data + promtail-data (docker-compose.telemetry-offline.yml)owning overlay per nameADDED 2026-09-15 (NPS-3) as a group: each is a declared, shipped overlay volume with no row. otel-data/promtail-data extend the telemetry family exemption above (which named only prometheus/tempo/loki/grafana); the rest are working state, unbudgeted, no measurer.
Third-party systems under test — gitea-*, gitlab-*, jenkins-data, nexus-data, minio-data (console-audit + integrations overlays), bsim-data + ghidra-* (docker-compose.bsim.yml)integration-fixture overlays, not Stella Ops servicesDispositioned 2026-09-15 (NPS-3) as a family: out of P16 scope — these are external systems the estate integrates with or analyses under test, not state a Stella Ops service owns. Recorded so the sweep’s silence about them is a decision rather than an omission.

6.2 Object-store buckets / prefixes

Live inventory 2026-08-02 (SPRINT_20260722_028 NPS-2: lab filer lists ONE provisioned bucket at 33.6 MB total store; the rest are configured-but-unprovisioned env keys that materialize on first write). Rule: cross-service object access goes through the owner’s API or the digest contract (ADR-033/035) — never raw paths. Sweep result: zero cross-owner raw-path consumers — every bucket’s S3 access lives in its owning family’s tree.

PrefixOwnerContentsRetention classBudget / knob
scanner-artifacts/ (LIVE)Scannerscan artifacts/CAS payloadsworking setScanner family only. SCN-CAS source partial (017 / 028 D-NPS3-1, 2026-08-23): S3 now measures real paginated bucket/prefix bytes; scanner-web declares doctor state bucket:scanner-artifacts; both Scanner roles receive the fail-closed P7 pre-write pause. Budget key Doctor:State:bucket:scanner-artifacts:BudgetBytes. Live red/green PASSED 2026-08-23 (corrected 2026-09-15 — this row said “has not run”, which was already false when written): the clean scanner-web image reported Info/healthy unbudgeted at 142,432,843 bytes, a 1-byte budget produced Critical/unhealthy, a tenant-valid scanner:write upload was refused with scanner-cas-write-paused leaving bytes 142,432,843 -> 142,432,843, and removing the override restored Info/healthy (SPRINT_20260722_017 SCN-CAS; SPRINT_20260722_028 Execution Log). Still open: the native RustFS API has no evidenced list/size route and fails explicitly rather than returning zero; measure -> compare -> write is not an atomic cross-replica quota reservation; and the budget has no VALUE — SCANNER_CAS_BUDGET_BYTES is an empty-default pass-through in every compose file and in the live container, so the P7 guard is off (CapacityGuardedArtifactObjectStore.cs:42-46). Procedure: docs/modules/scanner/operations/cas-capacity-guard.md.
stella-exports/{tenant}/{run-id}/OfflineKit (was ExportCenter until SPRINT_20260722_025 OK-10, 2026-09-13)export-run outputs, tenant-prefixed, immutable retention per profilewindowed (retention scheduler purges expired runs)profile retention config. Materialised as a VOLUME, not a bucket (exportcenter-object-store-data, §6.3): OK-12’s export-run executor writes run artifacts to the filesystem under Export:Runs:StorageRoot and records absolute paths in the distribution ledger. No owned-state budget yet — OK-CAP budgeted the IMPORT CAS, which is a different volume. Disposition (P16, 2026-09-14): windowed retention bounds it in time, not in bytes; owner OfflineKit; the byte budget is the OK-CAP measurer extended to this prefix (SPRINT_20260722_028 NPS-3 extension, recorded 2026-09-13 as un-owned by OK-10 — now owned by OfflineKit on this row).
oci-cache/ReleaseOrchestrator — owner corrected 2026-09-15orchestrator-held L2 content cache (digest-keyed, content-addressed) behind /api/v1/release-orchestrator/deployments/{id}/registry/content/{kind}/{digest}rebuildable cacheenv …BUCKET:-oci-cache. Disposition (P16, 2026-09-15, NPS-3 — supersedes the 2026-09-14 “owner Scanner” disposition. oci-cache has zero occurrences anywhere in src/Scanner. The constant is ReleaseOrchestrator’s: ContentByDigestServiceCollectionExtensions.cs:34 (L2Prefix = "oci-cache"), and compose configures it on that service alone — docker-compose.release-orchestrator.yml:437, docker-compose.stella-services.yml:1993, whose comment reads “bucket segregated to oci-cache. The orchestrator ALONE holds this key.” Root cause of the mis-attribution, stated in source: RustFsObjectStoreOptions.cs:11-16 records that RO “used to reach into StellaOps.Scanner.Storage … against its own separate oci-cache bucket — a build-time dependency on Scanner’s implementation for data Scanner never owned”, severed by SPRINT_20260722_017 SCN-6. The NPS-2 inventory recorded the borrowed driver as ownership of the data. Rebuildable, so unbudgeted remains acceptable; a measurer belongs to ReleaseOrchestrator (a doctor adopter — ReleaseOrchestrator.WebApi/Program.cs:1758), not to an extension of Scanner’s IOwnedStateMeasurer. Carrier: SPRINT_20260915_001 (OSM-7 for the doctor adoption this bucket needs first).
surface-cache/Scanner (surface)surface analysis cacherebuildable cachenaming spread flagged: surface-cache / stellaops-surface / surface-artifacts / surface-bucket all appear across code+tests — converge on ONE name before provisioning production (17 in-family references) Disposition (P16, 2026-09-14): rebuildable cache, unbudgeted acceptable; owner Scanner; the name convergence is Scanner’s before any production provisioning (recorded, no carrier row yet — Scanner files it).
mirror/OfflineKit (was Mirror/ExportCenter until SPRINT_20260722_025 OK-10, 2026-09-13)mirror bundle partsper bundle contractprovisioned on first mirror export Disposition (P16, 2026-09-14): per-bundle contract, materialises on first export; owner OfflineKit; budget rides OfflineKit’s existing measurer (OK-CAP) once the prefix is provisioned — carrier SPRINT_20260722_028 NPS-3 extension.
stellaops-rekor-devAttestor (dev rekor)dev transparency-log statedev-onlynever in production estates
evidence/timestamp-assurance/objects/<sha256>StellaOps.EvidenceExact RFC 3161 tokens/responses, certificate chains, signed TL/LOTL/OCSP/CRL objects and the authentication/policy/agreement material actually relied upon. The database keeps immutable digest/pointer metadata, CHECK-caps each text-rich row at 64 KiB aggregate logical metadata, and refuses keys outside this content-addressed prefix. The successor-token writer composed by TA-7 is the first and so far only writer, and it writes only under evidence/timestamp-assurance/tokens/<sha256>.source-of-truth, retained for R; shared-object deletion requires reference count and active-hold count both zeroWriter budget (TA-7, SPRINT_20260819_001): at most one object per gated remediation execution, and executions are themselves capped by the durable per-action rate window (Evidence:TimestampAssurance:Remediation:MaxExecutionsPerWindow, default 4/hour/tenant). Each object is CHECK-refused above 256 KiB before the first byte is written, and the write is refused outright when the configured capacity headroom is unavailable — fail-closed, discharging the obligation this cell previously recorded as owed; database cardinality is deduplicated by (tenant_id, sha256) and restricted to actual result dependencies, never every global snapshot.
vuln/symbols/<sha256>vulnerabilities-web (hub)Symbol blob CAS — the debuginfo/PDB payloads behind symbols.manifest. Folded in by DC-33 (SPRINT_20260722_014 BIN-3): the retiring symbols deployable stored these bytes in the database as symbols.blobs.content_bytes; P16 moves them here and the hub keeps only the catalog (symbols.blob_catalog: sha256, size, media type, object key). Content-addressed and tenant-free — a debug-id resolves to the same bytes for every estate.windowed online (LRU working set; a missing blob re-fetches from its configured symbol source) / source-of-truth in air-gap, where the pinned pack set has no upstream to re-fetch fromWorking-set cap driven by symbols.blob_catalog.last_read_at; per-estate size budget lands with NPS-3’s doctor check. Air-gap estates size the prefix to their pinned packs and must exclude it from eviction.
vuln/corpus/<generation>/vulnerabilities-web (hub)Sectioned corpus artifact per COMPLETED generation (DC-38): one generation manifest plus its content-addressed parts — matcher-rows, consensus-inputs, exploit-evidence, reachability-sinks. Generation identity = the manifest digest, carried in corpus.generation.completed (DC-06).windowed — producer keeps a last-N working set; a generation leaving the window is deleted, never rewritten (content-addressed parts are immutable while retained)Vulnerabilities:Corpus:WorkingSetGenerations (producer) and the consumer-side CorpusGenerationStore working set, default 2 (active + previous, so an A/B swap can roll back and replay against the previous generation). Per-host disk is capped by section subsetting: consumers fetch only the sections they declare (CLI/Integrations skip consensus-inputs; Policy takes all).
registry-content (bucket, prefix registry/)Registry (stella-registry)OCI registry content storage — the blobs and manifests the estate’s own registry servessource-of-truthADDED 2026-09-15 (NPS-3). This bucket was absent from §6.2 entirely, although its own compose comment declares it in-scope: “the Registry-OWNED bucket (OD-3, P16). No other service reads or writes this bucket. registry-content is not a prefix inside someone else’s bucket on purpose: P16 ownership is about the bucket, and sharing one would make the claim unverifiable” (docker-compose.registry.yml:148-152; also docker-compose.stella-services.yml:3227). No budget and no measurer, and unlike every other §6.2 owner, Registry is not a doctor adopter at all — it has no AddServiceDoctorChecks call — so wiring a measurer here means adopting doctor first. Carrier: SPRINT_20260915_001 (OSM-7 for the doctor adoption this bucket needs first).

Explicitly NOT decided here (owner R3): long-term generation retention for verdict replay — i.e. whether a verdict pins its corpus generation in the evidence plane so it stays replayable after the working set has rotated. The last-N working set above is a matcher-host guarantee, not an evidence guarantee; CorpusGenerationStore.ReadRetained says so in its failure message rather than implying replay works forever. Recorded by SPRINT_20260722_005 VULN-D4 (2026-08-02).