StellaOps.Determinism.Analyzers
Roslyn analyzers enforcing determinism patterns in StellaOps codebase.
Diagnostics
| ID | Severity | Description |
|---|---|---|
| STELLA0100 | Warning | Non-canonical JSON serialization at resolver boundary |
| STELLA0101 | Info | Unicode string not NFC normalized before hashing |
| STELLA0102 | Warning | Non-deterministic collection iteration in digest context |
STELLA0100: Canonicalization Boundary Violation
Triggers when: JsonSerializer.Serialize() is used in methods that compute digests, create attestations, or are marked with resolver boundary attributes.
Why it matters: Non-canonical JSON produces different byte representations on different platforms, breaking signature verification and replay guarantees.
Fix: Use Rfc8785JsonCanonicalizer.Canonicalize() instead of JsonSerializer.Serialize().
Example
// ❌ STELLA0100: Non-canonical JSON serialization
public string ComputeDigest(object data)
{
var json = JsonSerializer.Serialize(data); // Warning here
return SHA256.HashData(Encoding.UTF8.GetBytes(json)).ToHexString();
}
// ✅ Correct: Use canonicalizer
public string ComputeDigest(object data)
{
var canonicalizer = new Rfc8785JsonCanonicalizer();
var canonical = canonicalizer.Canonicalize(data);
return SHA256.HashData(Encoding.UTF8.GetBytes(canonical)).ToHexString();
}
STELLA0102: Non-Deterministic Collection Iteration
Triggers when: foreach iterates over Dictionary or HashSet in resolver boundary methods without explicit ordering.
Why it matters: Dictionary/HashSet iteration order is not guaranteed across runs or platforms.
Fix: Use OrderBy() before iteration, or use FrozenDictionary/SortedDictionary.
Example
// ❌ STELLA0102: Non-deterministic iteration
public void ProcessItems(Dictionary<string, Item> items)
{
foreach (var item in items) // Warning here
{
AppendToDigest(item.Key);
}
}
// ✅ Correct: Order before iteration
public void ProcessItems(Dictionary<string, Item> items)
{
foreach (var item in items.OrderBy(x => x.Key, StringComparer.Ordinal))
{
AppendToDigest(item.Key);
}
}
Configuration
Add the analyzer to your project:
<ProjectReference Include="path/to/StellaOps.Determinism.Analyzers.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
Suppression
When intentionally using non-canonical serialization (e.g., for human-readable output):
#pragma warning disable STELLA0100 // Intentional: human-readable log output
var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
#pragma warning restore STELLA0100