- 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.
120 lines
6.0 KiB
Markdown
120 lines
6.0 KiB
Markdown
# 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](https://github.com/BenMorris/NetArchTest) to enforce structural constraints at compile time. Rules are categorized into four areas:
|
||
|
||
1. **Lattice Engine Placement** – Ensures lattice/scoring logic stays in Scanner
|
||
2. **Module Dependencies** – Enforces proper layering between Core, Storage, WebServices, and Workers
|
||
3. **Forbidden Packages** – Blocks deprecated or non-compliant dependencies
|
||
4. **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
|
||
|
||
```bash
|
||
# 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
|
||
|
||
1. Open `tests/architecture/StellaOps.Architecture.Tests/`
|
||
2. Add test method to the appropriate `*RulesTests.cs` file
|
||
3. Use NetArchTest fluent API:
|
||
```csharp
|
||
[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");
|
||
}
|
||
```
|
||
4. Document the rule in this file
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
- [docs/07_HIGH_LEVEL_ARCHITECTURE.md](../07_HIGH_LEVEL_ARCHITECTURE.md) – High-level architecture overview
|
||
- [docs/modules/scanner/architecture.md](../modules/scanner/architecture.md) – Scanner module architecture (lattice engine details)
|
||
- [AGENTS.md](../../AGENTS.md) – Project-wide agent guidelines and module boundaries
|
||
- [NetArchTest Documentation](https://github.com/BenMorris/NetArchTest)
|
||
|
||
---
|
||
|
||
*Last updated: 2025-06-30 · Sprint 5100.0007.0007*
|