feat(evidence-locker): Decision Capsule sealing pipeline
Builds the previously-aspirational Capsule create/seal/verify/export/replay
pipeline. Unblocks the former CAPSULE-001 task that lived (BLOCKED) in
SPRINT_20260408_005; carried over as CAPSULE-AUDIT-001 inside the new
SPRINT_20260422_002 (created + archived in same pass).
Pipeline:
- CapsuleManifest record: deterministic SBOM+feeds+reachability+policy+VEX
content-address bundle.
- CapsuleManifestCanonicalizer: mirrors AUDIT-007 algorithm byte-for-byte
(ordinal-sorted UTF-8 JSON via JsonDocument round-trip).
- ICapsuleSigner + EcdsaCapsuleSigner + NullCapsuleSigner: DSSE PAE
contract, DSSE payload type application/vnd.stellaops.decision-capsule+json.
Pattern-identical to IAuditBundleManifestSigner; defined locally rather
than cross-referencing IExportAttestationSigner (which lives inside
ExportCenter.WebService, not a shared library — future cleanup noted).
- CapsuleService: create / seal / verify / export (zip) / replay.
- PostgresCapsuleRepository (Dapper) with tenant RLS hookup.
Endpoints (all tenant-scoped, POST):
- POST /api/v1/evidence/capsules
- POST /api/v1/evidence/capsules/{id}/seal
- POST /api/v1/evidence/capsules/{id}/verify
- POST /api/v1/evidence/capsules/{id}/export (application/zip)
- POST /api/v1/evidence/capsules/{id}/replay
Storage: embedded migration 005_decision_capsules.sql creates
evidence_locker.decision_capsules (RLS-enforced) + indexes + CHECK
constraints. Auto-applied by existing EvidenceLockerMigrationRunner.
Audit (CAPSULE-AUDIT-001):
- 5 new AuditActions.Evidence constants (CreateCapsule/Seal/Verify/Export/Replay)
- Each endpoint chained with .Audited(AuditModules.Evidence, ...)
- contentHash surfaced on responses so AuditActionFilter propagates it
into details_jsonb.
Tests: 9 focused tests (determinism x3, sign+verify+tamper x3, null-signer
graceful degradation, pipeline round-trip, 404 on missing). Full
EvidenceLocker namespace sweep: 141/141, 0 failures.
Docs: docs/modules/evidence-locker/architecture.md §9bis (manifest schema,
DSSE payload type, storage, API surface, relationship to
release.run_capsule_replay_linkage).
Runtime curl+Timeline assertion deferred — running container image
predates these changes; rebuild pending. Structural wiring identical to
runtime-verified VerdictEndpoints (AUDIT-002 precedent).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>