Skip to content

Commit d6010e3

Browse files
committed
Add commercial evidence artifact digest manifest
1 parent 628b73b commit d6010e3

7 files changed

Lines changed: 181 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ The first production milestone is M3UA over a transport abstraction. SCCP, TCAP,
7373
| RC publish and commercial gate | Phase 35 complete: dry-run release rehearsal, gated NuGet prerelease publication, retained release notes, retained migration notes, final commercial readiness reporting, RC/stable decisioning, RC publication evidence manifest, release workflow channel wiring, status reporting, and final commercial gate report are available; stable publication remains blocked until commercial evidence is complete |
7474
| Commercial evidence readiness lockdown | Phase 36 foundation-complete: release target locking, protected secret readiness, evidence retention mapping, commercial evidence checklist, release preflight, protected release environments, dossier handoff, go/no-go gating, status reporting, and final validation are available; RC and stable publication remain blocked until retained commercial release evidence is complete |
7575
| Commercial evidence execution orchestration | Phase 37 foundation-complete: evidence execution run identity, stage catalog, operator command plan, execution environment contract, artifact collection manifest, digest/redaction verification, blocker classification, retry/resume policy, status reporting, and final validation are available; real retained execution artifacts are still required before commercial publication |
76-
| Commercial evidence artifact intake | Phase 38 in progress: artifact intake target identity and artifact source registration are available, binding a stable intake id, reviewer identity, UTC receipt time, run-scoped dossier root, concrete source paths, and unique retained dossier paths to a governed execution run; digest generation, redaction review, completeness evaluation, dossier reporting, promotion handoff, execution bridge, status reporting, and final validation remain |
76+
| Commercial evidence artifact intake | Phase 38 in progress: artifact intake target identity, artifact source registration, and SHA-256 digest coverage are available, binding a stable intake id, reviewer identity, UTC receipt time, run-scoped dossier root, concrete source paths, unique retained dossier paths, and retained digests to a governed execution run; redaction review, completeness evaluation, dossier reporting, promotion handoff, execution bridge, status reporting, and final validation remain |
7777

7878
## Requirements
7979

docs/PHASE38_COMMERCIAL_EVIDENCE_ARTIFACT_INTAKE.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ Floating dossier roots such as `artifacts/latest/...` are rejected because comme
2727

2828
The source manifest is the handoff from execution outputs into dossier intake. It prepares the next step, digest generation, without claiming that files are already verified.
2929

30+
## Unit 3 - Artifact Digest Manifest
31+
32+
`SigtranCommercialEvidenceArtifactDigests` records SHA-256 coverage for retained sources:
33+
34+
- Every registered source must have a digest entry.
35+
- Each digest entry keeps the stage id, artifact kind, source path, retained path, and SHA-256 value together.
36+
- SHA-256 values must be 64-character hexadecimal values.
37+
- Retained paths must remain unique.
38+
39+
The digest manifest is required before redaction review and promotion handoff because commercial evidence must remain tamper-evident after intake.
40+
3041
## Validation
3142

3243
Each unit in this phase is validated with:

docs/PHASE38_SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ Phase 38 prepares the SDK to receive real execution artifacts and convert them i
66

77
- Artifact intake target that binds a stable intake id, reviewer identity, UTC receipt time, and dossier root to a governed execution run.
88
- Artifact source manifest that maps every required expected execution artifact to a concrete source path and a unique retained dossier path.
9+
- Artifact digest manifest that requires SHA-256 coverage for every retained source.
910

1011
## Readiness Position
1112

12-
The phase is in progress. Artifact intake identity and source registration are available. Digest generation, redaction review, completeness evaluation, dossier reporting, promotion handoff, execution bridge, status reporting, and final validation remain.
13+
The phase is in progress. Artifact intake identity, source registration, and digest coverage are available. Redaction review, completeness evaluation, dossier reporting, promotion handoff, execution bridge, status reporting, and final validation remain.

docs/PHASE_INDEX.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ This index is the complete phase map for Sigtran.NET. It links each phase to the
4242
| 35 | RC publish and commercial gate | [Phase 35 RC Publish And Commercial Gate](PHASE35_RC_PUBLISH_COMMERCIAL_GATE.md), [Phase 35 Summary](PHASE35_SUMMARY.md), [Phase 35 Commercial Gate Report](PHASE35_COMMERCIAL_GATE_REPORT.md) | RC gate foundation complete; stable publication blocked |
4343
| 36 | Commercial evidence readiness lockdown | [Phase 36 Commercial Evidence Readiness Lockdown](PHASE36_COMMERCIAL_EVIDENCE_READINESS_LOCKDOWN.md), [Phase 36 Summary](PHASE36_SUMMARY.md) | Foundation complete; real commercial evidence still required |
4444
| 37 | Commercial evidence execution orchestration | [Phase 37 Commercial Evidence Execution Orchestration](PHASE37_COMMERCIAL_EVIDENCE_EXECUTION_ORCHESTRATION.md), [Phase 37 Summary](PHASE37_SUMMARY.md) | Foundation complete; real execution artifacts still required |
45-
| 38 | Commercial evidence artifact intake | [Phase 38 Commercial Evidence Artifact Intake](PHASE38_COMMERCIAL_EVIDENCE_ARTIFACT_INTAKE.md), [Phase 38 Summary](PHASE38_SUMMARY.md) | In progress; source registration complete |
45+
| 38 | Commercial evidence artifact intake | [Phase 38 Commercial Evidence Artifact Intake](PHASE38_COMMERCIAL_EVIDENCE_ARTIFACT_INTAKE.md), [Phase 38 Summary](PHASE38_SUMMARY.md) | In progress; digest coverage complete |
4646

4747
## Current Commercial Gate
4848

docs/SDK_ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ Status: Phase 37 is foundation-complete. Evidence execution run identity binds a
339339
- Register artifact sources, digests, redaction reviews, completeness results, dossier reports, promotion handoff, and execution-to-dossier bridging.
340340
- Keep intake foundation separate from commercial publication: intake can prove that artifacts are ready for review, but publication remains blocked until all retained evidence is complete and approved.
341341

342-
Status: Phase 38 is in progress. Artifact intake target identity now binds a stable intake id, reviewer identity, UTC receipt time, and run-scoped dossier root to a governed commercial evidence execution run. Artifact source registration now maps every required expected execution artifact to a concrete source path and unique retained dossier path while rejecting floating `artifacts/latest` aliases. Digest generation, redaction review, completeness evaluation, dossier reporting, promotion handoff, execution bridge, status reporting, and final validation remain in progress.
342+
Status: Phase 38 is in progress. Artifact intake target identity now binds a stable intake id, reviewer identity, UTC receipt time, and run-scoped dossier root to a governed commercial evidence execution run. Artifact source registration now maps every required expected execution artifact to a concrete source path and unique retained dossier path while rejecting floating `artifacts/latest` aliases. Digest coverage now records SHA-256 values for every retained source and blocks invalid digest values. Redaction review, completeness evaluation, dossier reporting, promotion handoff, execution bridge, status reporting, and final validation remain in progress.
343343

344344
## Recommended First Deliverable
345345

src/Sigtran.NET.Tests/Program.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@
289289
Run("SIGTRAN commercial evidence execution status summarizes orchestration readiness", SigtranCommercialEvidenceExecutionStatusSummarizesOrchestrationReadiness);
290290
Run("SIGTRAN commercial evidence artifact intake target binds to execution run", SigtranCommercialEvidenceArtifactIntakeTargetBindsToExecutionRun);
291291
Run("SIGTRAN commercial evidence artifact sources cover expected execution artifacts", SigtranCommercialEvidenceArtifactSourcesCoverExpectedExecutionArtifacts);
292+
Run("SIGTRAN commercial evidence artifact digests cover retained sources", SigtranCommercialEvidenceArtifactDigestsCoverRetainedSources);
292293
Run("SIGTRAN status capabilities use domain documentation labels", SigtranStatusCapabilitiesUseDomainDocumentationLabels);
293294
Run("Native SCTP platform probe reports socket creation capability", NativeSctpPlatformProbeReportsSocketCreationCapability);
294295
Run("Native SCTP socket factory creates or reports unsupported platform", NativeSctpSocketFactoryCreatesOrReportsUnsupportedPlatform);
@@ -4844,6 +4845,42 @@ static void SigtranCommercialEvidenceArtifactSourcesCoverExpectedExecutionArtifa
48444845
Assert(!floatingManifest.IsReady, "floating source paths should not be source-manifest ready");
48454846
}
48464847

4848+
static void SigtranCommercialEvidenceArtifactDigestsCoverRetainedSources()
4849+
{
4850+
SigtranCommercialEvidenceArtifactSourceManifest sources = CreateDefaultCommercialEvidenceArtifactSourceManifest();
4851+
SigtranCommercialEvidenceArtifactDigestManifest digests = SigtranCommercialEvidenceArtifactDigests.CreateCovered(sources, new string('a', 64));
4852+
SigtranCommercialEvidenceArtifactDigestManifest invalidDigests = SigtranCommercialEvidenceArtifactDigests.CreateCovered(sources, "abc");
4853+
4854+
AssertEqual(sources.Sources.Count, digests.Digests.Count, "artifact digest count");
4855+
Assert(digests.IsReady, digests.Describe());
4856+
Assert(digests.CoversSources, "artifact digests should cover every retained source");
4857+
Assert(digests.HasValidDigests, "artifact digests should use SHA-256 values");
4858+
Assert(digests.UsesUniqueRetainedPaths, "artifact digest retained paths should be unique");
4859+
Assert(!invalidDigests.IsReady, "invalid SHA-256 values should block digest readiness");
4860+
}
4861+
4862+
static SigtranCommercialEvidenceArtifactSourceManifest CreateDefaultCommercialEvidenceArtifactSourceManifest()
4863+
{
4864+
SigtranCommercialEvidenceExecutionRun run = SigtranCommercialEvidenceExecutionRuns.CreateReleaseCandidateRun(
4865+
"1.0.0-rc.1",
4866+
"abcdef123456",
4867+
"run-20260622-001",
4868+
"release-automation",
4869+
DateTimeOffset.UtcNow);
4870+
SigtranCommercialEvidenceExecutionStageCatalog catalog = SigtranCommercialEvidenceExecutionStages.CreateDefault(run);
4871+
SigtranCommercialEvidenceExecutionArtifactManifest expected = SigtranCommercialEvidenceExecutionArtifacts.CreateDefault(catalog);
4872+
SigtranCommercialEvidenceArtifactIntakeTarget target = SigtranCommercialEvidenceArtifactIntakes.CreateDefault(
4873+
run,
4874+
"intake-20260622-001",
4875+
"release-review",
4876+
DateTimeOffset.UtcNow);
4877+
4878+
return SigtranCommercialEvidenceArtifactSources.CreateDefault(
4879+
target,
4880+
expected,
4881+
$"{run.RunArtifactRoot}/incoming/intake-20260622-001");
4882+
}
4883+
48474884
static void SigtranStatusCapabilitiesUseDomainDocumentationLabels()
48484885
{
48494886
IReadOnlyList<string>[] statusCapabilities =
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
namespace Sigtran.NET.Core.Utilities;
2+
3+
/// <summary>
4+
/// Describes one retained commercial evidence artifact digest.
5+
/// </summary>
6+
public sealed class SigtranCommercialEvidenceArtifactDigest
7+
{
8+
/// <summary>Creates a retained commercial evidence artifact digest.</summary>
9+
/// <param name="stageId">The execution stage that produced the artifact.</param>
10+
/// <param name="kind">The checklist artifact kind.</param>
11+
/// <param name="sourcePath">The received artifact source path.</param>
12+
/// <param name="retainedPath">The retained dossier artifact path.</param>
13+
/// <param name="sha256">The SHA-256 digest.</param>
14+
public SigtranCommercialEvidenceArtifactDigest(
15+
string stageId,
16+
SigtranCommercialEvidenceChecklistKind kind,
17+
string sourcePath,
18+
string retainedPath,
19+
string sha256)
20+
{
21+
StageId = string.IsNullOrWhiteSpace(stageId) ? throw new ArgumentException("Stage id is required.", nameof(stageId)) : stageId;
22+
Kind = kind;
23+
SourcePath = string.IsNullOrWhiteSpace(sourcePath) ? throw new ArgumentException("Source path is required.", nameof(sourcePath)) : sourcePath;
24+
RetainedPath = string.IsNullOrWhiteSpace(retainedPath) ? throw new ArgumentException("Retained path is required.", nameof(retainedPath)) : retainedPath;
25+
Sha256 = string.IsNullOrWhiteSpace(sha256) ? throw new ArgumentException("SHA-256 digest is required.", nameof(sha256)) : sha256;
26+
}
27+
28+
/// <summary>The execution stage that produced the artifact.</summary>
29+
public string StageId { get; }
30+
31+
/// <summary>The checklist artifact kind.</summary>
32+
public SigtranCommercialEvidenceChecklistKind Kind { get; }
33+
34+
/// <summary>The received artifact source path.</summary>
35+
public string SourcePath { get; }
36+
37+
/// <summary>The retained dossier artifact path.</summary>
38+
public string RetainedPath { get; }
39+
40+
/// <summary>The SHA-256 digest.</summary>
41+
public string Sha256 { get; }
42+
43+
/// <summary>Whether the digest is a valid SHA-256 hex value.</summary>
44+
public bool HasValidSha256 => Sha256.Length == 64
45+
&& Sha256.All(Uri.IsHexDigit);
46+
}
47+
48+
/// <summary>
49+
/// Describes the digest manifest for retained commercial evidence artifacts.
50+
/// </summary>
51+
public sealed class SigtranCommercialEvidenceArtifactDigestManifest
52+
{
53+
/// <summary>Creates a retained commercial evidence artifact digest manifest.</summary>
54+
/// <param name="sourceManifest">The artifact source manifest.</param>
55+
/// <param name="digests">The artifact digest entries.</param>
56+
public SigtranCommercialEvidenceArtifactDigestManifest(
57+
SigtranCommercialEvidenceArtifactSourceManifest sourceManifest,
58+
IReadOnlyList<SigtranCommercialEvidenceArtifactDigest> digests)
59+
{
60+
SourceManifest = sourceManifest ?? throw new ArgumentNullException(nameof(sourceManifest));
61+
ArgumentNullException.ThrowIfNull(digests);
62+
Digests = digests.Count == 0 ? throw new ArgumentException("At least one artifact digest is required.", nameof(digests)) : digests.ToArray();
63+
}
64+
65+
/// <summary>The artifact source manifest.</summary>
66+
public SigtranCommercialEvidenceArtifactSourceManifest SourceManifest { get; }
67+
68+
/// <summary>The artifact digest entries.</summary>
69+
public IReadOnlyList<SigtranCommercialEvidenceArtifactDigest> Digests { get; }
70+
71+
/// <summary>Whether every source has a matching digest entry.</summary>
72+
public bool CoversSources => SourceManifest.Sources.All(source => Digests.Any(digest =>
73+
digest.StageId == source.StageId
74+
&& digest.Kind == source.Kind
75+
&& digest.SourcePath == source.SourcePath
76+
&& digest.RetainedPath == source.RetainedPath));
77+
78+
/// <summary>Whether every digest entry contains a valid SHA-256 value.</summary>
79+
public bool HasValidDigests => Digests.All(static digest => digest.HasValidSha256);
80+
81+
/// <summary>Whether retained digest paths are unique.</summary>
82+
public bool UsesUniqueRetainedPaths => Digests.Select(static digest => digest.RetainedPath).Distinct(StringComparer.OrdinalIgnoreCase).Count() == Digests.Count;
83+
84+
/// <summary>Whether the digest manifest is ready for redaction review.</summary>
85+
public bool IsReady => SourceManifest.IsReady
86+
&& CoversSources
87+
&& HasValidDigests
88+
&& UsesUniqueRetainedPaths;
89+
90+
/// <summary>Formats a compact artifact digest manifest summary.</summary>
91+
/// <returns>The artifact digest manifest summary.</returns>
92+
public string Describe()
93+
{
94+
return $"commercialEvidenceArtifactDigestsReady={IsReady} digests={Digests.Count} intake={SourceManifest.Target.IntakeId}";
95+
}
96+
}
97+
98+
/// <summary>
99+
/// Provides commercial evidence artifact digest manifest helpers.
100+
/// </summary>
101+
public static class SigtranCommercialEvidenceArtifactDigests
102+
{
103+
/// <summary>Creates a digest manifest using the same digest value for every source.</summary>
104+
/// <param name="sourceManifest">The artifact source manifest.</param>
105+
/// <param name="sha256">The SHA-256 digest assigned to every artifact.</param>
106+
/// <returns>The digest manifest.</returns>
107+
public static SigtranCommercialEvidenceArtifactDigestManifest CreateCovered(
108+
SigtranCommercialEvidenceArtifactSourceManifest sourceManifest,
109+
string sha256)
110+
{
111+
ArgumentNullException.ThrowIfNull(sourceManifest);
112+
if (string.IsNullOrWhiteSpace(sha256))
113+
{
114+
throw new ArgumentException("SHA-256 digest is required.", nameof(sha256));
115+
}
116+
117+
return new(
118+
sourceManifest,
119+
sourceManifest.Sources
120+
.Select(source => new SigtranCommercialEvidenceArtifactDigest(
121+
source.StageId,
122+
source.Kind,
123+
source.SourcePath,
124+
source.RetainedPath,
125+
sha256))
126+
.ToArray());
127+
}
128+
}

0 commit comments

Comments
 (0)