- Created `StellaOps.TestKit.Tests` project for unit tests related to determinism. - Implemented `DeterminismManifestTests` to validate deterministic output for canonical bytes and strings, file read/write operations, and error handling for invalid schema versions. - Added `SbomDeterminismTests` to ensure identical inputs produce consistent SBOMs across SPDX 3.0.1 and CycloneDX 1.6/1.7 formats, including parallel execution tests. - Updated project references in `StellaOps.Integration.Determinism` to include the new determinism testing library.
6.0 KiB
Architecture Enforcement Rules
This document describes the automated architecture rules enforced by tests/architecture/StellaOps.Architecture.Tests. These rules run on every PR and gate merges, ensuring consistent adherence to StellaOps architectural boundaries.
Overview
Architecture tests use NetArchTest.Rules to enforce structural constraints at compile time. Rules are categorized into four areas:
- Lattice Engine Placement – Ensures lattice/scoring logic stays in Scanner
- Module Dependencies – Enforces proper layering between Core, Storage, WebServices, and Workers
- Forbidden Packages – Blocks deprecated or non-compliant dependencies
- Naming Conventions – Ensures consistent project/assembly naming
1. Lattice Engine Placement Rules
Purpose: The lattice engine computes vulnerability scoring, VEX decisions, and reachability proofs. These computations must remain in Scanner to preserve "prune at source" semantics—no other module should re-derive decisions.
| Rule ID | Description | Assemblies Affected | Enforcement |
|---|---|---|---|
Lattice_Concelier_NoReference |
Concelier assemblies must NOT reference Scanner lattice engine | StellaOps.Concelier.* |
Fail if any reference to StellaOps.Scanner.Lattice |
Lattice_Excititor_NoReference |
Excititor assemblies must NOT reference Scanner lattice engine | StellaOps.Excititor.* |
Fail if any reference to StellaOps.Scanner.Lattice |
Lattice_Scanner_MayReference |
Scanner.WebService MAY reference Scanner lattice engine | StellaOps.Scanner.WebService |
Allowed (no constraint) |
Lattice_PreservePruneSource |
Excititor does not compute lattice decisions (verified via type search) | StellaOps.Excititor.* |
Fail if types named *LatticeEngine*, *VexDecision*, or *ScoreCalculator* exist |
Rationale: If Excititor or Concelier computed their own lattice decisions, findings could drift from Scanner's authoritative scoring. Downstream consumers must accept pre-computed verdicts.
2. Module Dependency Rules
Purpose: Enforce clean architecture layering. Core business logic must not depend on infrastructure; services must not cross-call each other.
| Rule ID | Description | Source | Forbidden Target |
|---|---|---|---|
Dependency_Core_NoInfrastructure |
Core libraries must not depend on infrastructure | *.Core |
*.Storage.*, *.Postgres, *.WebService |
Dependency_WebService_NoWebService |
WebServices may not depend on other WebServices | *.WebService |
Other *.WebService assemblies |
Dependency_Worker_NoWebService |
Workers must not depend directly on WebServices | *.Worker |
*.WebService |
Rationale:
- Core libraries define contracts and business rules; they must remain portable.
- WebServices should communicate via HTTP/gRPC, not direct assembly references.
- Workers may share Core and Storage, but reaching into another service's WebService layer violates service boundaries.
3. Forbidden Package Rules
Purpose: Block usage of deprecated, non-compliant, or strategically-replaced dependencies.
| Rule ID | Description | Forbidden Namespace/Type | Rationale |
|---|---|---|---|
Forbidden_Redis |
No direct Redis library usage | StackExchange.Redis, ServiceStack.Redis |
StellaOps uses Valkey; Redis clients may introduce incompatible commands |
Forbidden_MongoDB |
No MongoDB usage | MongoDB.Driver, MongoDB.Bson |
MongoDB storage was deprecated in Sprint 4400; all persistence is PostgreSQL |
Forbidden_BouncyCastle_Core |
No direct BouncyCastle in core assemblies | Org.BouncyCastle.* |
Cryptography must be plugin-based (StellaOps.Cryptography.Plugin.*); core assemblies reference only StellaOps.Cryptography.Abstractions |
Exception: StellaOps.Cryptography.Plugin.BouncyCastle is the designated wrapper and may reference BouncyCastle directly.
4. Naming Convention Rules
Purpose: Ensure consistent assembly naming for discoverability and tooling.
| Rule ID | Pattern | Enforcement |
|---|---|---|
Naming_TestProjects |
Test projects must end with .Tests |
Assemblies matching StellaOps.*Tests* must end with .Tests |
Naming_Plugins |
Plugins must follow StellaOps.<Module>.Plugin.* or StellaOps.<Module>.Connector.* |
Assemblies with "Plugin" or "Connector" in name must match pattern |
Rationale: Consistent naming enables CI glob patterns (**/*.Tests.csproj) and plugin discovery (Assembly.Load("StellaOps.*.Plugin.*")).
Running Architecture Tests
# From repository root
dotnet test tests/architecture/StellaOps.Architecture.Tests --logger "console;verbosity=detailed"
CI Integration: Architecture tests run in the Unit test lane on every PR. They are PR-gating—failures block merge.
Adding New Rules
- Open
tests/architecture/StellaOps.Architecture.Tests/ - Add test method to the appropriate
*RulesTests.csfile - Use NetArchTest fluent API:
[Fact] public void NewRule_Description() { var result = Types.InAssembly(typeof(SomeType).Assembly) .That() .HaveDependencyOn("Forbidden.Namespace") .Should() .NotExist() .GetResult(); result.IsSuccessful.Should().BeTrue( "Assemblies should not reference Forbidden.Namespace"); } - Document the rule in this file
References
- docs/07_HIGH_LEVEL_ARCHITECTURE.md – High-level architecture overview
- docs/modules/scanner/architecture.md – Scanner module architecture (lattice engine details)
- AGENTS.md – Project-wide agent guidelines and module boundaries
- NetArchTest Documentation
Last updated: 2025-06-30 · Sprint 5100.0007.0007