Files
git.stella-ops.org/docs/replay/replay-manifest-v2-acceptance.md
StellaOps Bot 6e45066e37
Some checks failed
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
up
2025-12-13 09:37:15 +02:00

7.5 KiB

Replay Manifest v2 Acceptance Contract

Last updated: 2025-12-13. Owner: BE-Base Platform Guild.

This document defines the acceptance criteria and test vectors for replay manifest v2, enabling Task 19 (GAP-REP-004) to proceed with implementation.


1. Overview

Replay manifest v2 introduces:

  • BLAKE3 graph hashes: Primary hash algorithm for reachability graphs
  • Sorted CAS entries: Deterministic ordering of all CAS references
  • hashAlg fields: Explicit algorithm declarations for forward compatibility
  • code_id coverage: Coverage metrics for stripped binary handling

2. Schema Changes (v1 → v2)

2.1 Version Field

{
  "schemaVersion": "2.0",
  ...
}

2.2 Hash Algorithm Declaration

All hash fields now include explicit algorithm:

{
  "reachability": {
    "graphs": [
      {
        "hash": "blake3:a1b2c3d4e5f6...",
        "hashAlg": "blake3-256",
        "casUri": "cas://reachability/graphs/blake3:a1b2c3d4..."
      }
    ],
    "runtimeTraces": [
      {
        "hash": "sha256:feedface...",
        "hashAlg": "sha256",
        "casUri": "cas://reachability/runtime/sha256:feedface..."
      }
    ]
  }
}

2.3 Sorted CAS Entries

All arrays must be sorted by deterministic key:

Array Sort Key
reachability.graphs[] casUri (lexicographic)
reachability.runtimeTraces[] casUri (lexicographic)
inputs.feeds[] name (lexicographic)
inputs.tools[] name (lexicographic)

2.4 Code ID Coverage

New field for stripped binary support:

{
  "reachability": {
    "code_id_coverage": {
      "total_nodes": 1247,
      "nodes_with_symbol_id": 1189,
      "nodes_with_code_id": 58,
      "coverage_percent": 100.0
    }
  }
}

3. CAS Registration Gates

3.1 Required Registration

All referenced artifacts must be registered in CAS before manifest finalization:

Artifact Type CAS Path Pattern Required
Graph body cas://reachability/graphs/{hash} Yes
Graph DSSE cas://reachability/graphs/{hash}.dsse Yes
Runtime trace cas://reachability/runtime/{hash} Conditional
Edge bundle cas://reachability/edges/{graph_hash}/{bundle_id} Conditional

3.2 Registration Validation

Before signing a replay manifest:

  1. Verify all casUri references resolve to existing CAS objects
  2. Verify hash matches CAS content
  3. Verify DSSE envelope exists for all graph references
  4. Fail manifest creation if any reference is missing

3.3 Validation API

public interface ICasValidator
{
    Task<CasValidationResult> ValidateAsync(string casUri, string expectedHash);
    Task<CasValidationResult> ValidateBatchAsync(IEnumerable<CasReference> refs);
}

public record CasValidationResult(
    bool IsValid,
    string? ActualHash,
    string? Error
);

4. Acceptance Test Vectors

4.1 Minimal Valid Manifest v2

{
  "schemaVersion": "2.0",
  "scan": {
    "id": "scan-test-001",
    "time": "2025-12-13T10:00:00Z",
    "mode": "record",
    "scannerVersion": "10.2.0"
  },
  "subject": {
    "ociDigest": "sha256:abc123..."
  },
  "inputs": {
    "feeds": [],
    "tools": []
  },
  "reachability": {
    "graphs": [
      {
        "kind": "static",
        "analyzer": "scanner.java@10.2.0",
        "hash": "blake3:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
        "hashAlg": "blake3-256",
        "casUri": "cas://reachability/graphs/blake3:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
      }
    ],
    "runtimeTraces": [],
    "code_id_coverage": {
      "total_nodes": 100,
      "nodes_with_symbol_id": 100,
      "nodes_with_code_id": 0,
      "coverage_percent": 100.0
    }
  },
  "outputs": {},
  "provenance": {}
}

Expected canonical hash: sha256:e7f8a9b0... (computed from canonical JSON)

4.2 Manifest with Runtime Traces

{
  "schemaVersion": "2.0",
  "scan": {
    "id": "scan-test-002",
    "time": "2025-12-13T11:00:00Z",
    "mode": "record",
    "scannerVersion": "10.2.0"
  },
  "reachability": {
    "graphs": [
      {
        "kind": "static",
        "analyzer": "scanner.java@10.2.0",
        "hash": "blake3:1111111111111111111111111111111111111111111111111111111111111111",
        "hashAlg": "blake3-256",
        "casUri": "cas://reachability/graphs/blake3:1111111111111111111111111111111111111111111111111111111111111111"
      }
    ],
    "runtimeTraces": [
      {
        "source": "eventpipe",
        "hash": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
        "hashAlg": "sha256",
        "casUri": "cas://reachability/runtime/sha256:2222222222222222222222222222222222222222222222222222222222222222",
        "recordedAt": "2025-12-13T10:30:00Z"
      }
    ]
  }
}

4.3 Sorting Validation Vector

Input (unsorted):

{
  "reachability": {
    "graphs": [
      {"casUri": "cas://reachability/graphs/blake3:zzzz...", "kind": "framework"},
      {"casUri": "cas://reachability/graphs/blake3:aaaa...", "kind": "static"}
    ]
  }
}

Expected output (sorted):

{
  "reachability": {
    "graphs": [
      {"casUri": "cas://reachability/graphs/blake3:aaaa...", "kind": "static"},
      {"casUri": "cas://reachability/graphs/blake3:zzzz...", "kind": "framework"}
    ]
  }
}

4.4 Invalid Manifest Vectors

Test Case Input Expected Error
Missing schemaVersion {} REPLAY_MANIFEST_MISSING_VERSION
Invalid version {"schemaVersion": "1.0"} REPLAY_MANIFEST_VERSION_MISMATCH (when v2 required)
Missing hashAlg {"hash": "blake3:..."} REPLAY_MANIFEST_MISSING_HASH_ALG
Unsorted graphs See 4.3 input REPLAY_MANIFEST_UNSORTED_ENTRIES
Missing CAS reference {"casUri": "cas://missing/..."} REPLAY_MANIFEST_CAS_NOT_FOUND
Hash mismatch CAS content differs REPLAY_MANIFEST_HASH_MISMATCH

5. Migration Path

5.1 v1 → v2 Upgrade

public static ReplayManifest UpgradeToV2(ReplayManifest v1)
{
    return v1 with
    {
        SchemaVersion = "2.0",
        Reachability = v1.Reachability with
        {
            Graphs = v1.Reachability.Graphs
                .Select(g => g with { HashAlg = InferHashAlg(g.Hash) })
                .OrderBy(g => g.CasUri)
                .ToList(),
            RuntimeTraces = v1.Reachability.RuntimeTraces
                .Select(t => t with { HashAlg = InferHashAlg(t.Hash) })
                .OrderBy(t => t.CasUri)
                .ToList()
        }
    };
}

5.2 Backward Compatibility

  • v2 readers MUST accept v1 manifests with warning
  • v2 writers MUST always emit v2 format
  • v1 writers deprecated after 2026-03-01

6. Test Fixture Locations

tests/Replay/
  fixtures/
    manifest-v2-minimal.json
    manifest-v2-with-runtime.json
    manifest-v2-sorted.json
    manifest-v2-code-id-coverage.json
  invalid/
    manifest-missing-version.json
    manifest-unsorted.json
    manifest-missing-hashalg.json
  golden/
    manifest-v2-canonical.golden.json
    manifest-v2-hash.golden.txt

7. Implementation Checklist

  • Update ReplayManifest record with v2 fields
  • Add hashAlg to all hash-bearing types
  • Implement sorting in ReachabilityReplayWriter
  • Add CAS registration validation
  • Create test fixtures
  • Update DETERMINISTIC_REPLAY.md section 3
  • Wire into RecordModeService

Last updated: 2025-12-13. See Sprint 0401 GAP-REP-004 for implementation.