Files
git.stella-ops.org/src/__Analyzers/StellaOps.Determinism.Analyzers

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