Determinism Gates
Audience: engineers writing or reviewing unit tests that assert reproducible output for Stella Ops artifacts.
Determinism is a core principle of Stella Ops: all artifact generation (SBOM, VEX, policy verdicts, attestations) must be reproducible. This guide shows how to assert determinism at the unit level using the DeterminismGate helpers and the deterministic time/random utilities in the TestKit. For the manifest-and-baseline workflow that gates artifact drift in CI, see Determinism Verification; for the end-to-end pipeline reproducibility suite, see E2E Reproducibility.
Why Determinism Matters
- Reproducible builds: Same input → same output, always
- Cryptographic verification: Hash-based integrity depends on byte-for-byte reproducibility
- Audit trails: Deterministic timestamps and ordering for compliance
- Offline operation: No reliance on external randomness or timestamps
Using Determinism Gates
Reference the StellaOps.Testing.Determinism library from your test project and use the DeterminismGate static class. Tag determinism tests with the canonical Category trait (TestCategories.Determinism) so they route under the determinism CI lane — there are no [UnitTest] / [DeterminismTest] attribute types; categorization is always via [Trait("Category", ...)].
using StellaOps.Testing.Determinism;
using StellaOps.TestKit; // TestCategories
using Xunit;
public class SbomGeneratorTests
{
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void SbomGenerationIsDeterministic()
{
// Verify that calling the function 3 times produces identical output
DeterminismGate.AssertDeterministic(() =>
{
return GenerateSbom();
}, iterations: 3);
}
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void SbomBinaryIsDeterministic()
{
// Verify binary reproducibility
DeterminismGate.AssertDeterministic(() =>
{
return GenerateSbomBytes();
}, iterations: 3);
}
}
JSON Determinism
JSON output must have:
- Stable property ordering (alphabetical or schema-defined)
- Consistent whitespace/formatting
- No random IDs or timestamps (unless explicitly from deterministic clock)
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void VexDocumentJsonIsDeterministic()
{
// Verifies JSON canonicalization and property ordering
DeterminismGate.AssertJsonDeterministic(() =>
{
var vex = GenerateVexDocument();
return JsonSerializer.Serialize(vex);
});
}
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void VerdictObjectIsDeterministic()
{
// Verifies object serialization is deterministic
DeterminismGate.AssertJsonDeterministic(() =>
{
return GenerateVerdict();
});
}
Canonical Equality
Compare two objects for canonical equivalence:
[Fact]
[Trait("Category", TestCategories.Unit)]
public void VerdictFromDifferentPathsAreCanonicallyEqual()
{
var verdict1 = GenerateVerdictFromSbom();
var verdict2 = GenerateVerdictFromCache();
// Asserts that both produce identical canonical JSON
DeterminismGate.AssertCanonicallyEqual(verdict1, verdict2);
}
Hash-Based Regression Testing
Compute stable hashes for regression detection:
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void SbomHashMatchesBaseline()
{
var sbom = GenerateSbom();
var hash = DeterminismGate.ComputeHash(sbom);
// This hash should NEVER change unless SBOM format changes intentionally
const string expectedHash = "abc123...";
Assert.Equal(expectedHash, hash);
}
Path Ordering
File paths in manifests must be sorted:
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void SbomFilePathsAreSorted()
{
var sbom = GenerateSbom();
var filePaths = ExtractFilePaths(sbom);
// Asserts paths are in deterministic (lexicographic) order
DeterminismGate.AssertSortedPaths(filePaths);
}
Timestamp Validation
All timestamps must be UTC ISO 8601:
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void AttestationTimestampIsUtcIso8601()
{
var attestation = GenerateAttestation();
// Asserts timestamp is UTC with 'Z' suffix
DeterminismGate.AssertUtcIso8601(attestation.Timestamp);
}
Deterministic Time in Tests
Use DeterministicTime for reproducible timestamps. It is constructed from a fixed UTC instant and exposes a stable UtcNow:
using StellaOps.TestKit.Deterministic;
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void AttestationWithDeterministicTime()
{
using var clock = new DeterministicTime(
new DateTime(2026, 1, 15, 10, 30, 0, DateTimeKind.Utc));
// All operations reading this clock get the same time
var attestation1 = GenerateAttestation(clock.UtcNow);
var attestation2 = GenerateAttestation(clock.UtcNow);
Assert.Equal(attestation1.Timestamp, attestation2.Timestamp);
}
Deterministic Random in Tests
Use DeterministicRandom for reproducible randomness. The same seed always produces the same sequence:
using StellaOps.TestKit.Deterministic;
[Fact]
[Trait("Category", TestCategories.Determinism)]
public void GeneratedIdsAreReproducible()
{
var rng1 = new DeterministicRandom(seed: 42);
var id1 = GenerateId(rng1);
var rng2 = new DeterministicRandom(seed: 42);
var id2 = GenerateId(rng2);
// Same seed → same output
Assert.Equal(id1, id2);
}
Module-Specific Gates
Scanner Determinism
- SBOM file path ordering
- Component hash stability
- Dependency graph ordering
Concelier Determinism
- Advisory normalization (same advisory → same canonical form)
- Vulnerability merge determinism
- No lattice ordering dependencies
Excititor Determinism
- VEX document format stability
- Preserve/prune decision ordering
- No lattice dependencies
Policy Determinism
- Verdict reproducibility (same inputs → same verdict)
- Policy evaluation ordering
- Unknown budget calculations
Attestor Determinism
- DSSE envelope canonical bytes
- Signature ordering (multiple signers)
- Rekor receipt stability
Common Determinism Violations
❌ Timestamps from system clock
// Bad: Uses system time
var timestamp = DateTimeOffset.UtcNow.ToString("o");
// Good: Uses injected clock
var timestamp = clock.UtcNow.ToString("o");
❌ Random GUIDs
// Bad: Non-deterministic
var id = Guid.NewGuid().ToString();
// Good: Deterministic or content-addressed
var id = ComputeContentHash(data);
❌ Unordered collections
// Bad: Dictionary iteration order is undefined
foreach (var (key, value) in dict) { ... }
// Good: Explicit ordering
foreach (var (key, value) in dict.OrderBy(x => x.Key)) { ... }
❌ Floating-point comparisons
// Bad: Floating-point can differ across platforms
var score = 0.1 + 0.2; // Might not equal 0.3 exactly
// Good: Use fixed-point or integers
var scoreInt = (int)((0.1 + 0.2) * 1000);
❌ Non-UTC timestamps
// Bad: Timezone-dependent
var timestamp = DateTime.Now.ToString();
// Good: Always UTC with 'Z'
var timestamp = DateTimeOffset.UtcNow.ToString("o");
Determinism Test Checklist
When writing determinism tests, verify:
- [ ] Multiple invocations produce identical output
- [ ] JSON has stable property ordering
- [ ] File paths are sorted lexicographically
- [ ] Timestamps are UTC ISO 8601 with ‘Z’ suffix
- [ ] No random GUIDs (use content-addressing)
- [ ] Collections are explicitly ordered
- [ ] No system time/random usage (use
DeterministicTime/DeterministicRandom)
Related Documentation
- Determinism Verification — manifest format, CI drift gate, baseline updates
- E2E Reproducibility — full-pipeline and cross-platform reproducibility
- Testing Strategy Models
- Test category constants:
src/__Libraries/StellaOps.TestKit/TestCategories.cs - TestKit README:
src/__Libraries/StellaOps.TestKit/README.md
