Skip to content

Commit 684dc10

Browse files
committed
Add maintained peer lab evidence gate
1 parent 5d17892 commit 684dc10

5 files changed

Lines changed: 182 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ The roadmap is intentionally conservative:
205205
3. Harden SCCP with external interoperability vectors and trace validation.
206206
4. Harden TCAP with external interoperability vectors and MAP profile validation.
207207

208-
Phase 27 tracks the maintained external peer lab contract, including package-neutral binding, host prerequisites, validated lab configuration, deterministic artifact planning, command planning, traffic vectors, and retained evidence gates.
208+
Phase 27 tracks the maintained external peer lab contract, including package-neutral binding, host prerequisites, validated lab configuration, deterministic artifact planning, command planning, traffic vectors, and digest-covered retained evidence gates.
209209
5. Harden MAP SMS with external interoperability vectors and operator-profile validation.
210210
6. Use completed interoperability tooling to run external lab validation, native SCTP verification, and release automation hardening.
211211
7. Complete commercial readiness gates and publish governed release candidates.

docs/PHASE27_MAINTAINED_EXTERNAL_PEER_LAB.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ The command plan is intentionally package-neutral. Real deployments can map `ext
8484

8585
The catalog flattens these vectors into an ordered expected message sequence so comparison reports can separate protocol mismatch from host or package setup issues.
8686

87+
## Unit 8 - Evidence Promotion Gate
88+
89+
`SigtranMaintainedPeerLabEvidenceReport` now separates planned execution from promotable evidence. Promotion requires:
90+
91+
- Host prerequisites ready.
92+
- Lab configuration valid.
93+
- Every required artifact retained.
94+
- Every retained artifact digest-covered.
95+
- Trace comparison passed.
96+
97+
This keeps commercial readiness honest: a command plan or artifact plan is not treated as proof until retained outputs and digests exist.
98+
8799
## Environment Contract
88100

89101
The default binding exposes these variables:
@@ -125,4 +137,4 @@ dotnet run --project src\Sigtran.NET.Tests\Sigtran.NET.Tests.csproj
125137
dotnet pack src\Sigtran.NET\Sigtran.NET.csproj -c Release
126138
```
127139

128-
The tests verify that the default binding satisfies the maintained peer selection policy, public binding summaries remain package-neutral, host prerequisite reports identify missing lab capabilities, environment-derived lab configuration is validated before use, the artifact plan covers every required retained evidence path, the command plan covers every required execution step, and the traffic vector catalog yields a comparable expected message sequence.
140+
The tests verify that the default binding satisfies the maintained peer selection policy, public binding summaries remain package-neutral, host prerequisite reports identify missing lab capabilities, environment-derived lab configuration is validated before use, the artifact plan covers every required retained evidence path, the command plan covers every required execution step, the traffic vector catalog yields a comparable expected message sequence, and evidence promotion is blocked without complete digest-covered artifacts.

docs/SDK_ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ Status: Phase 26 is foundation-complete for package-neutral commercial roadmap r
243243
- Define host prerequisites, peer config, artifact naming, command scripts, traffic vectors, evidence promotion, readiness, and commercial gate alignment.
244244
- Keep selected package details outside public SDK type names.
245245

246-
Status: Phase 27 has canonical `Sigtran.NET` naming, a package-neutral maintained peer lab binding catalog, host prerequisite readiness modeling, validated lab configuration contracts, deterministic retained artifact planning, an ordered command plan, and maintained peer traffic vectors. Commercial release remains blocked until the maintained peer lab produces passing digest-covered evidence.
246+
Status: Phase 27 has canonical `Sigtran.NET` naming, a package-neutral maintained peer lab binding catalog, host prerequisite readiness modeling, validated lab configuration contracts, deterministic retained artifact planning, an ordered command plan, maintained peer traffic vectors, and a digest-covered evidence promotion gate. Commercial release remains blocked until the maintained peer lab produces passing digest-covered evidence.
247247

248248
## Recommended First Deliverable
249249

src/Sigtran.NET.Tests/Program.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
Run("SIGTRAN maintained peer lab artifact plan covers retained evidence paths", SigtranMaintainedPeerLabArtifactPlanCoversRetainedEvidencePaths);
4343
Run("SIGTRAN maintained peer lab command plan covers execution steps", SigtranMaintainedPeerLabCommandPlanCoversExecutionSteps);
4444
Run("SIGTRAN maintained peer lab traffic vectors expose comparable sequence", SigtranMaintainedPeerLabTrafficVectorsExposeComparableSequence);
45+
Run("SIGTRAN maintained peer lab evidence gate requires complete retained artifacts", SigtranMaintainedPeerLabEvidenceGateRequiresCompleteRetainedArtifacts);
4546
Run("SIGTRAN trace comparison reports ordered mismatches", SigtranTraceComparisonReportsOrderedMismatches);
4647
Run("SIGTRAN interoperability evidence promotion requires passing lab run", SigtranInteropEvidencePromotionRequiresPassingLabRun);
4748
Run("SIGTRAN interoperability lab CI profile is opt-in", SigtranInteropLabCiProfileIsOptIn);
@@ -832,6 +833,38 @@ static void SigtranMaintainedPeerLabTrafficVectorsExposeComparableSequence()
832833
Assert(comparison.Passed, comparison.Describe());
833834
}
834835

836+
static void SigtranMaintainedPeerLabEvidenceGateRequiresCompleteRetainedArtifacts()
837+
{
838+
SigtranMaintainedPeerLabConfiguration configuration = SigtranMaintainedPeerLabConfigurations.CreateDefault();
839+
SigtranMaintainedPeerLabArtifactPlan artifactPlan = SigtranMaintainedPeerLabArtifactPlans.CreateDefault(configuration, "phase27-unit8");
840+
IReadOnlyList<string> allPrerequisites = SigtranMaintainedPeerLabPrerequisites.GetDefault()
841+
.Select(static prerequisite => prerequisite.Id)
842+
.ToArray();
843+
844+
SigtranMaintainedPeerLabEvidenceReport ready = new(
845+
artifactPlan,
846+
SigtranMaintainedPeerLabPrerequisites.Evaluate(allPrerequisites),
847+
configuration.Validate(),
848+
SigtranMaintainedPeerLabEvidence.CreateDigestCoveredArtifacts(artifactPlan, "0123456789abcdef"),
849+
comparisonPassed: true);
850+
851+
Assert(ready.PromotionReady, ready.Describe());
852+
Assert(ready.HasRequiredArtifacts, ready.Describe());
853+
Assert(ready.HasDigestCoverage, ready.Describe());
854+
855+
SigtranMaintainedPeerLabEvidenceReport missingDigest = new(
856+
artifactPlan,
857+
SigtranMaintainedPeerLabPrerequisites.Evaluate(allPrerequisites),
858+
configuration.Validate(),
859+
[new(SigtranMaintainedPeerLabArtifactKind.PacketCapture, "artifacts/external-peer/maintained/pcap/missing.pcap", retained: true)],
860+
comparisonPassed: true);
861+
862+
Assert(!missingDigest.PromotionReady, missingDigest.Describe());
863+
Assert(!missingDigest.HasRequiredArtifacts, missingDigest.Describe());
864+
Assert(!missingDigest.HasDigestCoverage, missingDigest.Describe());
865+
Assert(missingDigest.Describe().Contains("promotion=False", StringComparison.Ordinal), missingDigest.Describe());
866+
}
867+
835868
static void SigtranTraceComparisonReportsOrderedMismatches()
836869
{
837870
SigtranInteropLabTemplate template = SigtranInteropPeerProfiles.CreateExternalPeerM3uaAspToSgTemplate();
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
namespace Sigtran.NET.Core.Utilities;
2+
3+
/// <summary>
4+
/// Describes one retained maintained external peer lab evidence artifact.
5+
/// </summary>
6+
public sealed class SigtranMaintainedPeerLabEvidenceArtifact
7+
{
8+
/// <summary>Creates a retained maintained peer lab evidence artifact.</summary>
9+
/// <param name="kind">The artifact kind.</param>
10+
/// <param name="path">The retained artifact path.</param>
11+
/// <param name="retained">Whether the artifact is retained.</param>
12+
/// <param name="sha256">The optional SHA-256 digest.</param>
13+
public SigtranMaintainedPeerLabEvidenceArtifact(
14+
SigtranMaintainedPeerLabArtifactKind kind,
15+
string path,
16+
bool retained,
17+
string? sha256 = null)
18+
{
19+
Kind = kind;
20+
Path = string.IsNullOrWhiteSpace(path) ? throw new ArgumentException("Artifact path is required.", nameof(path)) : path;
21+
Retained = retained;
22+
Sha256 = string.IsNullOrWhiteSpace(sha256) ? null : sha256;
23+
}
24+
25+
/// <summary>The artifact kind.</summary>
26+
public SigtranMaintainedPeerLabArtifactKind Kind { get; }
27+
28+
/// <summary>The retained artifact path.</summary>
29+
public string Path { get; }
30+
31+
/// <summary>Whether the artifact is retained.</summary>
32+
public bool Retained { get; }
33+
34+
/// <summary>The optional SHA-256 digest.</summary>
35+
public string? Sha256 { get; }
36+
37+
/// <summary>Whether the artifact is retained and digest-covered.</summary>
38+
public bool IsPromotionReady => Retained && Sha256 is not null;
39+
}
40+
41+
/// <summary>
42+
/// Describes maintained external peer lab evidence promotion output.
43+
/// </summary>
44+
public sealed class SigtranMaintainedPeerLabEvidenceReport
45+
{
46+
private readonly SigtranMaintainedPeerLabEvidenceArtifact[] _artifacts;
47+
48+
/// <summary>Creates a maintained peer lab evidence report.</summary>
49+
/// <param name="artifactPlan">The artifact plan.</param>
50+
/// <param name="prerequisiteReport">The prerequisite report.</param>
51+
/// <param name="configurationValidation">The configuration validation report.</param>
52+
/// <param name="artifacts">The retained evidence artifacts.</param>
53+
/// <param name="comparisonPassed">Whether trace comparison passed.</param>
54+
public SigtranMaintainedPeerLabEvidenceReport(
55+
SigtranMaintainedPeerLabArtifactPlan artifactPlan,
56+
SigtranMaintainedPeerLabPrerequisiteReport prerequisiteReport,
57+
SigtranMaintainedPeerLabConfigurationValidation configurationValidation,
58+
IReadOnlyList<SigtranMaintainedPeerLabEvidenceArtifact> artifacts,
59+
bool comparisonPassed)
60+
{
61+
ArgumentNullException.ThrowIfNull(artifactPlan);
62+
ArgumentNullException.ThrowIfNull(prerequisiteReport);
63+
ArgumentNullException.ThrowIfNull(configurationValidation);
64+
ArgumentNullException.ThrowIfNull(artifacts);
65+
66+
ArtifactPlan = artifactPlan;
67+
PrerequisiteReport = prerequisiteReport;
68+
ConfigurationValidation = configurationValidation;
69+
_artifacts = artifacts.ToArray();
70+
ComparisonPassed = comparisonPassed;
71+
}
72+
73+
/// <summary>The artifact plan.</summary>
74+
public SigtranMaintainedPeerLabArtifactPlan ArtifactPlan { get; }
75+
76+
/// <summary>The prerequisite report.</summary>
77+
public SigtranMaintainedPeerLabPrerequisiteReport PrerequisiteReport { get; }
78+
79+
/// <summary>The configuration validation report.</summary>
80+
public SigtranMaintainedPeerLabConfigurationValidation ConfigurationValidation { get; }
81+
82+
/// <summary>The retained evidence artifacts.</summary>
83+
public IReadOnlyList<SigtranMaintainedPeerLabEvidenceArtifact> Artifacts => _artifacts.ToArray();
84+
85+
/// <summary>Whether trace comparison passed.</summary>
86+
public bool ComparisonPassed { get; }
87+
88+
/// <summary>Whether every required planned artifact is retained.</summary>
89+
public bool HasRequiredArtifacts => ArtifactPlan.Items
90+
.Where(static item => item.Required)
91+
.All(item => _artifacts.Any(artifact => artifact.Kind == item.Kind && artifact.Retained));
92+
93+
/// <summary>Whether every retained artifact has digest coverage.</summary>
94+
public bool HasDigestCoverage => _artifacts.Length > 0 && _artifacts.All(static artifact => artifact.IsPromotionReady);
95+
96+
/// <summary>Whether the maintained peer lab evidence is ready for commercial promotion.</summary>
97+
public bool PromotionReady => PrerequisiteReport.Ready
98+
&& ConfigurationValidation.IsValid
99+
&& HasRequiredArtifacts
100+
&& HasDigestCoverage
101+
&& ComparisonPassed;
102+
103+
/// <summary>Formats a compact evidence report summary.</summary>
104+
/// <returns>The evidence report summary.</returns>
105+
public string Describe()
106+
{
107+
return $"run={ArtifactPlan.RunId} artifacts={_artifacts.Length} required={HasRequiredArtifacts} digests={HasDigestCoverage} comparison={ComparisonPassed} promotion={PromotionReady}";
108+
}
109+
}
110+
111+
/// <summary>
112+
/// Provides maintained external peer lab evidence helpers.
113+
/// </summary>
114+
public static class SigtranMaintainedPeerLabEvidence
115+
{
116+
/// <summary>Creates a retained evidence artifact set from an artifact plan using one digest placeholder.</summary>
117+
/// <param name="artifactPlan">The artifact plan.</param>
118+
/// <param name="sha256">The digest used for all planned artifacts.</param>
119+
/// <returns>The retained evidence artifacts.</returns>
120+
public static IReadOnlyList<SigtranMaintainedPeerLabEvidenceArtifact> CreateDigestCoveredArtifacts(
121+
SigtranMaintainedPeerLabArtifactPlan artifactPlan,
122+
string sha256)
123+
{
124+
ArgumentNullException.ThrowIfNull(artifactPlan);
125+
if (string.IsNullOrWhiteSpace(sha256))
126+
{
127+
throw new ArgumentException("Digest is required.", nameof(sha256));
128+
}
129+
130+
return artifactPlan.Items
131+
.Select(item => new SigtranMaintainedPeerLabEvidenceArtifact(item.Kind, item.Path, retained: true, sha256))
132+
.ToArray();
133+
}
134+
}

0 commit comments

Comments
 (0)