Files
git.stella-ops.org/docs-archived/implplan/AUDIT_20260225_module_consolidation_rationale.md

29 KiB

Module Consolidation Audit Document

Date: 2026-02-25 Prepared by: AI-assisted planning (Claude Opus 4.6) Update note (2026-02-25): Domain-first sprint rework and DB-merge planning updates were produced by Codex. See docs/implplan/AUDIT_20260225_cli_ui_module_reference_matrix.md and updated sprints 203, 204, 205, 206, 208, 211, 216, 218. Review note (2026-02-25): Post-review corrections applied by Claude Opus 4.6: fixed CryptoPro path, added Zastava to exclusion list, corrected module counts, added SbomService consolidation (Sprint 220), added compiled-model invalidation risk, rewrote execution order with coordination constraints, added standalone module rationale section. DB merge verdict (2026-02-25): Deep analysis of all 7 proposed DB merges completed. All services share one PostgreSQL database (stellaops_platform) with schema-level isolation. Verdicts: REJECT 4 merges (Advisory/203, Trust/204, Orchestration/208, Identity/216) as source-consolidation only; PROCEED 2 DbContext merges (VEX/205, Offline/211); PROCEED 1 empty placeholder deletion (Policy/206). Sprint files amended accordingly. See Section 9 for details. Scope: 22 sprint files (SPRINT_20260225_200 through SPRINT_20260225_221, including companion Sprint 219 for EF compiled models and Sprint 221 for Orchestrator rename) Sprint files location: docs/implplan/SPRINT_20260225_2*.md


1. Original Request

The owner reviewed the Stella Ops monorepo and identified that the repository had grown to ~60 module directories under src/ (plus infrastructure directories like __Libraries/, __Tests/, __Analyzers/). The request, in the owner's words:

"you have to think from stella ops architecture position. we have now 40+ modules. this is bit too much. ideally the number should be = purpose/schema + workers"

The guiding principle: one module = one distinct purpose or schema domain + its workers. Modules that share a schema, serve the same domain, or exist as thin deployment hosts for another module's libraries should be consolidated into their parent domain.

The owner explicitly excluded crypto modules from consolidation:

"i agree for all consolidation but the crypto one related. these modules are simulator smremote and cryptopro. so these needs to be kept as is."

The owner also requested:

"create sprints for the consolidation. one per domain. make sure to include cli, ui, documentation, tests and other related work after the core consolidation work. the sprints needs to be very detailed."


2. Analysis Method

The consolidation candidates were identified through:

  1. Dependency graph analysis — mapping every ProjectReference across all .csproj files in the repo to find which modules are consumed by which, and identifying single-consumer or zero-consumer modules.
  2. Domain alignment — evaluating whether two modules operate on the same schema, data domain, or workflow stage (e.g., Signer produces DSSE envelopes that Attestor logs — same trust domain).
  3. Deployment boundary audit — confirming that consolidation is organizational only (moving source code directories), NOT a service merge. Each absorbed module's WebService/Worker keeps its own Docker container and port.
  4. Consumer count — modules with zero external consumers are prime candidates. Modules with 1-2 consumers that are within the same domain are also candidates.
  5. Orphan library scan — searching every .csproj and .cs file for ProjectReference and using statements to confirm libraries with zero production consumers.

3. Consolidation Decisions — Sprint by Sprint

Sprint 200 — Delete src/Gateway/

Action: Delete (not absorb) Rationale: Gateway is dead code. The canonical StellaOps.Gateway.WebService already lives inside src/Router/StellaOps.Gateway.WebService/ with source comments confirming "now in same module." The src/Gateway/ directory is a leftover from a previous migration. Zero consumers reference it. The Router version is confirmed as a superset.


Sprint 201 — Scanner absorbs Cartographer

Action: Move src/Cartographer/ (1 csproj) → src/Scanner/ Rationale: Cartographer materializes SBOM graphs for indexing. SBOM processing is Scanner's domain. Cartographer has zero external consumers — it depends on Policy and Auth but nothing depends on it outside itself. Its AGENTS.md already points to the Graph module for required reading. Single-purpose library that belongs under its only consumer's domain.


Sprint 202 — BinaryIndex absorbs Symbols

Action: Move src/Symbols/ (6 csproj) → src/BinaryIndex/ Rationale: Symbols provides debug symbol storage and resolution. Its primary consumer is BinaryIndex.DeltaSig. The only other consumer is Cli.Plugins.Symbols (a thin CLI plugin loader). Same data domain — binary artifact analysis. Symbols.Server keeps its own container. The existing Symbols architecture doc was stale (described a monolithic layout while actual code has 5 projects), which further indicates this was an under-maintained satellite module.


Sprint 203 — Concelier absorbs Feedser + Excititor

Action: Move src/Feedser/ (2 csproj) + src/Excititor/ (17 csproj) + StellaOps.DistroIntel → src/Concelier/ Rationale:

  • Feedser is a backport evidence library. Its architecture doc explicitly states "Primary consumer: Concelier ProofService." Also consumed by Attestor.ProofChain and Scanner.PatchVerification, but the domain home is Concelier (advisory feed processing).
  • Excititor is the VEX feed collection service with connectors for Cisco, MSRC, Oracle, RedHat, SUSE, Ubuntu, OpenVEX, etc. Advisory ingestion is the same domain as Concelier (advisory feed curation). Excititor feeds VexHub which feeds VexLens — all VEX/advisory pipeline.
  • DistroIntel is a single-consumer library — only Concelier.BackportProof references it.
  • All three share the advisory/feed data domain. Excititor keeps its own WebService + Worker containers.

Sprint 204 — Attestor absorbs Signer + Provenance

Action: Move src/Signer/ (5 csproj) + src/Provenance/ (2 csproj) → src/Attestor/ Rationale: Same trust domain — keys, DSSE signing, transparency logs. Signer produces DSSE envelopes, Attestor logs them as attestations. Provenance is a thin attestation library + CLI forensic tool. The three together form the complete evidence trust chain: sign → attest → verify provenance. Signer's WebService keeps its own container. Provenance CLI tool may optionally move to Tools.


Sprint 205 — VexLens absorbs VexHub

Action: Move src/VexHub/ (3 csproj) → src/VexLens/ Rationale: Same VEX data domain. VexHub aggregates and validates VEX statements; VexLens adjudicates consensus over them. They operate on the same data (VEX documents) and the same workflow stage (VEX adjudication). VexHub keeps its own WebService container.


Sprint 206 — Policy absorbs Unknowns

Action: Move src/Unknowns/ (5 csproj) → src/Policy/ Rationale: Unknowns.Core already depends on Policy — it imports Policy types. Unknown component tracking is a policy governance concern (what is the policy for components whose provenance is unknown?). External consumers (Platform.Database, Scanner.Worker, Scanner.MaterialChanges) reference Unknowns.Core, but the domain home is Policy. Unknowns.WebService keeps its own container and EF Core migrations.


Sprint 207 — Findings absorbs RiskEngine + VulnExplorer

Action: Move src/RiskEngine/ (1 csproj) + src/VulnExplorer/ (1 csproj) → src/Findings/ Rationale: Both are single-csproj modules operating on the same data domain — vulnerability findings. RiskEngine computes risk scores over findings. VulnExplorer is the API surface for browsing findings. Both are thin enough (1 project each) that maintaining separate top-level directories is overhead. Low risk due to small scope.


Sprint 208 — Orchestrator absorbs Scheduler + TaskRunner + PacksRegistry

Action: Move src/Scheduler/ (8 csproj) + src/TaskRunner/ + src/PacksRegistry/ → src/Orchestrator/ Rationale: All three are "schedule and execute work" — same workflow lifecycle domain. Orchestrator owns the job lifecycle, Scheduler adds trigger/cron logic, TaskRunner adds DAG execution, PacksRegistry stores task pack definitions. They form a complete orchestration pipeline. All keep their deployable containers.


Sprint 209 — Notify absorbs Notifier

Action: Move src/Notifier/ (2 csproj) → src/Notify/ Rationale: Notifier is a thin deployment host for Notify libraries. The Notifier WebService and Worker only reference Notify libraries — they contain no unique logic. This was a 2025-11-02 separation decision that the architecture notes suggest should be revisited. The deployment hosts stay as separate containers, but the source code belongs with the libraries they host.


Sprint 210 — Timeline absorbs TimelineIndexer

Action: Move src/TimelineIndexer/ (4 csproj) → src/Timeline/ Rationale: CQRS split (read/write) is an internal architecture pattern, not a module boundary. Timeline and TimelineIndexer operate on the same schema domain (timeline events). TimelineIndexer is the write side (event ingestion and indexing); Timeline is the read side. ExportCenter references TimelineIndexer.Core — this cross-module reference path will be updated. TimelineIndexer Worker keeps its own container.


Sprint 211 — ExportCenter absorbs Mirror + AirGap

Action: Move src/Mirror/ (1 csproj) + src/AirGap/ → src/ExportCenter/ Rationale:

  • Mirror creates offline mirror bundles — ExportCenter's domain. Mirror's shell scripts already reference ExportCenter. Zero external consumers. Single csproj.
  • AirGap handles offline bundle creation, sync, and policy. Natural fit with ExportCenter — both deal with offline/air-gap distribution. AirGap components keep their project names for offline kit identity.

Sprint 212 — Tools absorbs Bench + Verifier + Sdk + DevPortal

Action: Move src/Bench/ (5 csproj) + src/Verifier/ (1 csproj) + src/Sdk/ (2 csproj) + src/DevPortal/ → src/Tools/ Rationale: All four are non-service, developer-facing tooling with no production deployment. Bench runs benchmarks, Verifier is a CLI bundle verifier, Sdk contains a code generator + release tool, DevPortal is the developer portal. None have Docker services. None are consumed by production modules. Tools already has 7+ csproj — these are natural additions. Low risk.


Sprint 213 — AdvisoryAI absorbs OpsMemory

Action: Move src/OpsMemory/ (2 csproj) → src/AdvisoryAI/ Rationale: OpsMemory is the AI's operational memory / RAG vector store. It is only consumed by AdvisoryAI. AdvisoryAI currently references OpsMemory via ProjectReference. Same domain — AI knowledge management. OpsMemory WebService keeps its own container.


Sprint 214 — Integrations absorbs Extensions

Action: Move src/Extensions/ (VS Code + JetBrains plugins) → src/Integrations/__Extensions/ Rationale: Extensions are developer-facing IDE plugins that consume the same Orchestrator/Router APIs as other integrations. They are logically part of the Integrations domain (toolchain integrations). Note: these are non-.NET (TypeScript/Kotlin) — no .csproj files. Zero external consumers. No Docker service. Placed under __Extensions/ (not __Plugins/) to avoid confusion with the Integrations plugin framework.


Sprint 215 — Signals absorbs RuntimeInstrumentation

Action: Move src/RuntimeInstrumentation/ → src/Signals/ Rationale: RuntimeInstrumentation provides eBPF/Tetragon event adapters that feed into Signals. Same domain — runtime observability. Critical finding: RuntimeInstrumentation has no .csproj files — source code exists (12 .cs files) but lacks build integration. Zero external consumers (impossible to reference without .csproj). Signals already has StellaOps.Signals.Ebpf which may overlap. The sprint includes an audit task to determine integration strategy.


Sprint 216 — Authority absorbs IssuerDirectory

Action: Move src/IssuerDirectory/ (6 csproj + client lib) → src/Authority/ Rationale: IssuerDirectory manages issuer metadata, keys, and trust — a natural subdomain of Authority (identity and trust). IssuerDirectory depends on Authority for authentication. Only 2 external consumers (Excititor, DeltaVerdict) via a client library. IssuerDirectory has its own PostgreSQL schema (issuer_directory) which remains unchanged. IssuerDirectory WebService keeps its own container.


Sprint 217 — Orphan Library Cleanup

Action: Archive StellaOps.AdvisoryLens + StellaOps.Resolver to src/__Libraries/_archived/ Rationale:

  • AdvisoryLens — zero production consumers. Not in main solution file. Has tests but nothing imports it. Appears to be intended for a feature not yet implemented.
  • Resolver — zero production consumers. In main solution file but nothing imports it. Research/PoC code for deterministic verdict resolution with extensive SOLID review documentation.
  • SettingsStore was initially suspected but confirmed active — used by ReleaseOrchestrator, Platform, Cli, and AdvisoryAI. Removed from cleanup scope.
  • Archive (not delete) preserves code history and enables reactivation.

Sprint 218 — Final Documentation Consolidation

Action: Update all docs to mirror new src/ structure Rationale: This is the cleanup sweep that runs LAST, after all source moves (200-220) are complete. It updates CLAUDE.md, docs/INDEX.md, docs/07_HIGH_LEVEL_ARCHITECTURE.md, validates all cross-references, and ensures zero broken links to absorbed module docs. Depends on all other sprints being DONE.


Sprint 219 — EF Compiled Model & Migration Consistency (companion sprint, DONE)

Action: Generate EF Core compiled models for 5 remaining real DbContexts; convert code-first migrations to raw SQL Rationale: Foundation sprint completed before consolidation execution. Ensures all real DbContexts have compiled models, factory infrastructure, and guard tests. Covers ExportCenter, Triage, ProofService, Integration, and Provcache contexts. Three stub contexts (SbomService, PacksRegistry, TaskRunner) were deferred — SbomServiceDbContext stub has since been deleted, and PacksRegistry/TaskRunner will be addressed during Sprint 208 orchestration domain merge. Status: All 6 tasks DONE as of 2026-02-25.

Note: Source moves in domain sprints (203, 206, 208, etc.) will require updating <Compile Remove> paths for compiled model assembly attributes in moved .csproj files. This is a non-breaking path fixup (builds produce a duplicate attribute warning, not a failure) but should be included in each sprint's source-move verification step.


Sprint 220 — Scanner absorbs SbomService

Action: Move src/SbomService/ â†' src/Scanner/ Rationale: SbomService generates and processes SBOMs from scanned artifacts — this is squarely within Scanner's domain (scan â†' produce SBOM â†' index). The SbomServiceDbContext stub was already deleted in a prior session, removing the persistence complication. SbomService has its own WebService which keeps its own container. Low consumer count — primarily consumed by Scanner and Orchestrator pipelines. Moving it under Scanner follows the same “one module = one domain” principle applied throughout this consolidation.


Sprint 221 â€" Rename Orchestrator domain

Action: Rename src/Orchestrator/ â†' src/<NewName>/ (name TBD in TASK-221-001) Rationale: Orchestrator creates persistent confusion with ReleaseOrchestrator (the core product feature â€" release promotion pipeline). After Sprint 208 consolidates Scheduler/TaskRunner/PacksRegistry under Orchestrator, the rename gives the domain an unambiguous identity. Scope: 3,268 namespace references, 336 C# files, 36 external ProjectReferences, Docker images, Helm, API routes, authority scopes, 40+ TypeScript files. PostgreSQL schema name orchestrator is preserved for data continuity. Pre-alpha with zero clients makes this the last low-cost window.


4. What Does NOT Change

  • Deployment boundaries — every absorbed module's WebService/Worker keeps its own Docker container, port, and image name. This is organizational consolidation, not service merge.
  • Project names — cross-module libraries keep their original names to avoid breaking downstream references. Only Cartographer and Symbols get renamed (they have zero or contained consumers).
  • Database schemas — all schemas and migrations are preserved. No schema renames.
  • Crypto modules — src/SmRemote/, src/Zastava/, and src/Cryptography/ (which contains CryptoPro as a plugin at src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/) are untouched per owner's explicit instruction. Note: there is no top-level src/CryptoPro/ directory — CryptoPro is a plugin within the Cryptography module.
  • Core standalone modules — Platform, Router, Web, Cli, Doctor, Telemetry, EvidenceLocker, Graph, ReachGraph, Plugin, Registry, ReleaseOrchestrator, Remediation, and Replay remain as independent top-level directories. See Section 8 for rationale on each.

5. Expected Outcome

Metric Before After
Top-level src/ module directories ~60 ~33
Infrastructure directories (__Libraries/, __Tests/, __Analyzers/) 3 3 (unchanged)
Modules with 0 external consumers 9 0 (absorbed or archived)
Single-consumer satellite modules 7 0 (absorbed into parent)
Thin deployment hosts as separate modules 3 0 (moved to library host)
Orphan libraries 3 0 (1 confirmed active, 2 archived)
Modules absorbed or deleted â€" 27
Crypto modules (untouched) 3 3 (SmRemote, Zastava, Cryptography)

6. Risk Summary

Risk Mitigation
Namespace renames may break serialized type names Grep for typeof(), nameof(), JSON $type discriminators before renaming
Cross-module ProjectReference paths break during move Update all consumer .csproj paths atomically; verify with dotnet build StellaOps.sln
EF Core compiled models / migration identity Preserve migration file names and schema names unchanged
Compiled model path invalidation after source moves Sprint 219 (DONE) generated compiled models whose .csproj files contain <Compile Remove> for assembly attributes. Domain sprints that move these projects (203, 206, 208) must update these paths as part of the source-move verification. Builds emit a duplicate attribute warning (not failure) if missed, but should be fixed proactively.
RuntimeInstrumentation has no .csproj Sprint includes audit task to create build integration or merge into existing Signals.Ebpf
Excititor has 17 csproj (largest move) Move in batches: core+persistence first, then connectors, then deployables
Authority grows to 35 csproj after absorbing IssuerDirectory Justified — all are identity/trust domain. Authority already well-structured with __Libraries/ and __Tests/ conventions
Parallel sprint coordination risk Sprints 203/204 and 203/205 touch overlapping cross-module references (Feedser/Provenance, Excititor/VexHub). Must be serialized or carefully coordinated — see Section 7 execution tiers.

7. Sprint Execution Order

Completed

  • Sprint 219 (EF Compiled Models) — DONE. Foundation work completed before consolidation.

Tier 1 — Independent, no cross-sprint conflicts (safe to run in parallel)

These sprints touch isolated modules with no overlapping cross-references:

  • Sprint 200 — Gateway deletion (dead code, zero risk)
  • Sprint 201 — Scanner absorbs Cartographer
  • Sprint 202 — BinaryIndex absorbs Symbols
  • Sprint 207 — Findings absorbs RiskEngine + VulnExplorer
  • Sprint 209 — Notify absorbs Notifier
  • Sprint 210 — Timeline absorbs TimelineIndexer
  • Sprint 212 — Tools absorbs Bench + Verifier + Sdk + DevPortal
  • Sprint 213 — AdvisoryAI absorbs OpsMemory
  • Sprint 214 — Integrations absorbs Extensions
  • Sprint 215 — Signals absorbs RuntimeInstrumentation
  • Sprint 217 — Orphan Library Cleanup
  • Sprint 220 — Scanner absorbs SbomService (can run in parallel with 201 if source moves don't overlap; serialize if both touch Scanner solution file simultaneously)

Tier 2 — Coordination required (serialize within group)

These sprints have overlapping cross-module references and must be executed in the order shown:

Group A — Advisory/VEX pipeline (serialize):

  1. Sprint 203 — Concelier absorbs Feedser + Excititor (moves Excititor, which feeds VexHub)
  2. Sprint 205 — VexLens absorbs VexHub (depends on Excititor's new path from Sprint 203)

Group B — Advisory/Trust cross-references (serialize):

  1. Sprint 203 — Concelier absorbs Feedser + Excititor (moves Feedser, referenced by Attestor)
  2. Sprint 204 — Attestor absorbs Signer + Provenance (updates Feedser references to new paths)

Group C — Orchestration domain (serialize within):

  1. Sprint 208 — Orchestrator absorbs Scheduler + TaskRunner + PacksRegistry

Group D — Identity cross-references (coordinate with Group A):

  1. Sprint 216 — Authority absorbs IssuerDirectory (IssuerDirectory client used by Excititor — coordinate with Sprint 203 if running close together)

Group E — Offline domain (independent of other groups):

  1. Sprint 211 — ExportCenter absorbs Mirror + AirGap

Group F — Policy domain (independent of other groups):

  1. Sprint 206 — Policy absorbs Unknowns

Practical serialization: Run Sprint 203 first among Tier 2 groups since it is the largest move (17 csproj) and unblocks both Group A and Group B. After 203 completes, Sprints 204, 205, and 216 can proceed. Sprints 206, 208, and 211 can run any time.

Tier 2.5 — Post-consolidation rename (depends on Sprint 208 being DONE)

  • Sprint 221 — Rename Orchestrator domain to resolve ReleaseOrchestrator naming collision (3,268 namespace references, 336 C# files, Docker/Helm/API routes/authority scopes). PostgreSQL schema name preserved for data continuity.

Tier 3 — Final sweep (depends on all Tier 1 + Tier 2 + Tier 2.5 being DONE)

  • Sprint 218 — Final Documentation Consolidation

8. Standalone Module Rationale

The following modules remain as independent top-level directories after consolidation. This section documents why each was not absorbed into another domain, to preempt future “why didn't we consolidate X?” questions.

Module Why standalone
Platform Cross-cutting infrastructure host (health checks, config distribution, service discovery). Not domain-specific — consumed by all modules.
Router API gateway / reverse proxy. Already absorbed Gateway (Sprint 200). Sits at the network edge, not inside any domain.
Web Angular SPA. Single UI project, not a backend domain.
Cli CLI tool. Cross-cutting client, not a backend domain.
Doctor Diagnostics and health monitoring. Cross-cutting operational concern, not tied to a single domain.
Telemetry Shared observability library (metrics, tracing, logging). Consumed by nearly every module — shared infrastructure, not a domain.
EvidenceLocker Evidence storage. Could be argued as part of the trust domain (Attestor), but it is a storage service consumed by multiple domains (Policy, Orchestrator, Attestor, Scanner) for different evidence types. Multi-consumer shared service.
Graph Graph data structures and algorithms library. Shared infrastructure consumed by ReachGraph, Scanner/Cartographer, and others. Not a domain-specific service.
ReachGraph Reachability analysis service. Uses Graph but serves a specific security analysis purpose (dependency reachability for vulnerability assessment). Distinct enough from Scanner (which uses reachability results but doesn't compute them).
Plugin Plugin framework and hosting infrastructure. Cross-cutting extension mechanism, not domain-specific.
Registry Container/artifact registry service. Manages artifact storage and distribution — its own distinct concern separate from scanning (Scanner analyzes) or orchestration (Orchestrator schedules).
ReleaseOrchestrator Release promotion workflow engine. The core “promote through environments” logic. Could be argued as part of Orchestrator, but ReleaseOrchestrator is the business-critical release pipeline while Orchestrator is the generic job/schedule engine. Different concerns despite similar names.
Remediation Remediation workflow engine. Produces actionable fix plans from findings. Could be argued as part of Findings, but remediation is an active response domain (suggest fixes, track remediation progress) while Findings is a passive data domain (store and query vulnerability data).
Replay Deterministic evidence replay for verification. Could be argued as part of the trust domain (Attestor), but Replay serves an independent verification purpose (re-derive any past decision from evidence). Used by compliance auditors, not just trust infrastructure.
SmRemote HSM/crypto remote signing bridge. Excluded from consolidation per owner instruction (crypto module).
Zastava Crypto signing service (HSM bridge, regional crypto). Excluded from consolidation per owner instruction (crypto module).
Cryptography Crypto plugin framework (contains CryptoPro, GOST, SM plugins). Excluded from consolidation per owner instruction (crypto module).

9. DB Merge Verdicts (2026-02-25)

Context

All Stella Ops services share a single PostgreSQL database (stellaops_platform at db.stella-ops.local:5432). Domain isolation is achieved through PostgreSQL schemas, not separate databases. The original consolidation sprints proposed "DB merges" for 7 domain consolidations. After deep analysis, the proposed merges are really about merging EF Core DbContexts (code-only) or merging PostgreSQL schemas (data migration). Since all data is already in one database, schema merges provide marginal operational benefit at significant code risk.

Verdict summary

Sprint Domain Verdict Rationale Task impact
203 Advisory (Concelier) REJECT 49 entities across 5 schemas (vuln, feedser, vex, proofchain, advisory_raw). Distinct lifecycles. Coupling risk too high. 8 tasks reduced to 4 (source move only)
204 Trust (Attestor) REJECT Security boundary between signer key material and attestation evidence is a deliberate feature. Merging widens credential compromise blast radius. 8 tasks reduced to 4 (source move only)
205 VEX (VexLens) PROCEED (DbContext merge) 9 entities, zero name collisions. Low-risk DbContext consolidation. Schemas stay separate. 5 tasks reduced to 4 (no dual-write/backfill)
206 Policy PROCEED (delete placeholder) UnknownsDbContext has 0 entities, 0 tables. Just delete the empty class. 6 tasks reduced to 4 (no data migration)
208 Orchestration REJECT 39 + 11 entities with Jobs/JobHistory name collisions. Fundamentally different job models (pipeline runs vs. cron). 8 tasks reduced to 3 (source move only)
211 Offline (ExportCenter) PROCEED (DbContext merge) 7 entities, zero name collisions. Low-risk DbContext consolidation. Schemas stay separate. 5 tasks reduced to 4 (no dual-write/backfill)
216 Identity (Authority) REJECT Most security-critical domain. Merging IssuerDirectory into AuthorityDbContext would give any issuer-metadata code path access to authentication internals. 6 tasks reduced to 4 (source move only)

Impact

The verdicts eliminated ~42 data migration task phases (expand, dual-write, backfill, cutover, rollback) across 7 sprints, replacing them with either:

  • Source move only (4 rejected domains): no schema changes, no data migration, no runtime risk.
  • DbContext-level merge (2 proceeding domains): code-only change, compiled model regeneration, targeted integration tests.
  • Empty placeholder deletion (1 domain): trivial cleanup with zero data risk.

Architectural rule established

The analysis prompted the addition of Section 16 (Domain Ownership and Boundary Rules) to docs/code-of-conduct/CODE_OF_CONDUCT.md, codifying:

  • Single database / schema isolation as an architectural invariant
  • DbContext ownership rules per domain
  • Cross-domain dependency rules (no direct persistence references)
  • Schema migration ownership rules