Skip to content

Commit 9ea9bd4

Browse files
committed
Add maintained peer lab runner command manifest
1 parent 8d4afec commit 9ea9bd4

5 files changed

Lines changed: 203 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ The first production milestone is M3UA over a transport abstraction. SCCP, TCAP,
6464
| Native SCTP implementation | Phase 8 foundation-ready: Linux SCTP probe, socket factory, endpoint planner, native adapter, connector, listener, lab profile, readiness report, and commercial gate integration; production readiness still requires Linux SCTP lab verification |
6565
| Interoperability lab | Phase 9 foundation-ready: lab scenario catalog, artifact manifests, run reports, external peer M3UA ASP-to-SG template, trace comparison, evidence promotion, opt-in CI profile, readiness report, and commercial gate integration added; production readiness still requires passing external lab evidence |
6666
| Maintained peer lab automation | Phase 28 foundation-ready: executable run manifest, deterministic environment file rendering, command script rendering, artifact digest manifests, comparison report, run report, evidence bundle, manual self-hosted workflow template, commercial readiness bridge, and automation status reporting are available; commercial release still requires real retained maintained-peer evidence |
67-
| Maintained peer lab runner | Phase 29 in progress: runner workspace, execution input bundle, output artifact materialization, and preflight contracts are available; commercial release still requires execution against a real maintained external peer |
67+
| Maintained peer lab runner | Phase 29 in progress: runner workspace, execution input bundle, output artifact materialization, preflight, and command manifest contracts are available; commercial release still requires execution against a real maintained external peer |
6868

6969
## Requirements
7070

@@ -213,7 +213,7 @@ The roadmap is intentionally conservative:
213213

214214
Phase 27 completed the maintained external peer lab foundation, including package-neutral binding, host prerequisites, validated lab configuration, deterministic artifact planning, command planning, traffic vectors, manual self-hosted CI policy, digest-covered retained evidence gates, and status reporting.
215215
Phase 28 completed the automation and evidence handoff layer above that foundation, including a single run manifest, deterministic environment file rendering, command script rendering, comparison reporting, run reporting, digest manifests, an evidence bundle, a manual self-hosted workflow template, a commercial readiness bridge, and automation status reporting for retained lab artifact handoff.
216-
Phase 29 materializes the maintained peer lab runner layer, starting with deterministic runner workspace paths, execution input bundles, output artifact mapping, and preflight checks for real lab execution.
216+
Phase 29 materializes the maintained peer lab runner layer, starting with deterministic runner workspace paths, execution input bundles, output artifact mapping, preflight checks, and command manifests for real lab execution.
217217
5. Harden MAP SMS with external interoperability vectors and operator-profile validation.
218218
6. Use completed interoperability tooling to run external lab validation, native SCTP verification, and release automation hardening.
219219
7. Complete commercial readiness gates and publish governed release candidates.

docs/PHASE29_MAINTAINED_PEER_LAB_RUNNER.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ This gives the runner a deterministic post-execution checklist before evidence c
5050

5151
The preflight report returns stable failed check identifiers so operators can fix the lab environment before opening SCTP traffic.
5252

53+
## Unit 5 - Command Manifest
54+
55+
`SigtranMaintainedPeerLabRunnerCommandManifest` now turns the command plan into an execution manifest. It records:
56+
57+
- One-based command sequence.
58+
- Command kind, name, and command line.
59+
- Expected output paths per command.
60+
- Preflight dependency.
61+
- Markdown rendering for retained run notes.
62+
63+
The manifest is execution-ready only when inputs, outputs, preflight checks, command sequencing, and expected artifact mappings are all valid.
64+
5365
## Validation
5466

5567
Each unit in this phase is validated with:

docs/SDK_ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ Status: Phase 28 is foundation-complete. It has a run manifest that aggregates b
259259
- Keep runner materialization package-neutral and separate from real retained lab evidence.
260260
- Prepare the SDK for a real maintained external peer lab run without manufacturing passing artifacts.
261261

262-
Status: Phase 29 has deterministic maintained peer lab runner workspace, execution input bundle, output artifact materialization, and preflight contracts.
262+
Status: Phase 29 has deterministic maintained peer lab runner workspace, execution input bundle, output artifact materialization, preflight, and command manifest contracts.
263263

264264
## Recommended First Deliverable
265265

src/Sigtran.NET.Tests/Program.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
Run("SIGTRAN maintained peer lab runner inputs render environment and script", SigtranMaintainedPeerLabRunnerInputsRenderEnvironmentAndScript);
6060
Run("SIGTRAN maintained peer lab runner artifacts map outputs to producers", SigtranMaintainedPeerLabRunnerArtifactsMapOutputsToProducers);
6161
Run("SIGTRAN maintained peer lab runner preflight gates execution", SigtranMaintainedPeerLabRunnerPreflightGatesExecution);
62+
Run("SIGTRAN maintained peer lab runner command manifest orders execution", SigtranMaintainedPeerLabRunnerCommandManifestOrdersExecution);
6263
Run("SIGTRAN trace comparison reports ordered mismatches", SigtranTraceComparisonReportsOrderedMismatches);
6364
Run("SIGTRAN interoperability evidence promotion requires passing lab run", SigtranInteropEvidencePromotionRequiresPassingLabRun);
6465
Run("SIGTRAN interoperability lab CI profile is opt-in", SigtranInteropLabCiProfileIsOptIn);
@@ -1179,6 +1180,25 @@ static void SigtranMaintainedPeerLabRunnerPreflightGatesExecution()
11791180
Assert(blocked.FailedRequiredCheckIds.Contains("host-prerequisites-ready"), blocked.Describe());
11801181
}
11811182

1183+
static void SigtranMaintainedPeerLabRunnerCommandManifestOrdersExecution()
1184+
{
1185+
SigtranMaintainedPeerLabRunManifest runManifest = SigtranMaintainedPeerLabRunManifests.CreateDefault("phase29-unit5");
1186+
SigtranMaintainedPeerLabRunnerInputBundle inputs = SigtranMaintainedPeerLabRunnerInputs.CreateDefault(runManifest);
1187+
SigtranMaintainedPeerLabRunnerArtifactMaterializationPlan artifacts = SigtranMaintainedPeerLabRunnerArtifacts.CreateDefault(inputs.Workspace);
1188+
IReadOnlyList<string> allPrerequisites = SigtranMaintainedPeerLabPrerequisites.GetDefault()
1189+
.Select(static prerequisite => prerequisite.Id)
1190+
.ToArray();
1191+
SigtranMaintainedPeerLabRunnerPreflightReport preflight = SigtranMaintainedPeerLabRunnerPreflight.Evaluate(inputs, artifacts, allPrerequisites);
1192+
SigtranMaintainedPeerLabRunnerCommandManifest manifest = SigtranMaintainedPeerLabRunnerCommandManifests.Create(inputs, artifacts, preflight);
1193+
1194+
Assert(manifest.IsExecutionReady, manifest.Describe());
1195+
AssertEqual(6, manifest.Commands.Count, "maintained peer lab runner command manifest command count");
1196+
Assert(manifest.HasContiguousSequence, manifest.Describe());
1197+
Assert(manifest.CoversExpectedArtifacts, manifest.Describe());
1198+
AssertEqual(SigtranMaintainedPeerLabCommandKind.Prepare, manifest.Commands[0].Command.Kind, "maintained peer lab runner first command");
1199+
Assert(manifest.RenderMarkdown().Contains("Ready: `True`", StringComparison.Ordinal), manifest.RenderMarkdown());
1200+
}
1201+
11821202
static void SigtranTraceComparisonReportsOrderedMismatches()
11831203
{
11841204
SigtranInteropLabTemplate template = SigtranInteropPeerProfiles.CreateExternalPeerM3uaAspToSgTemplate();
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
using System.Text;
2+
3+
namespace Sigtran.NET.Core.Utilities;
4+
5+
/// <summary>
6+
/// Describes one maintained external peer lab runner command manifest entry.
7+
/// </summary>
8+
public sealed class SigtranMaintainedPeerLabRunnerCommandEntry
9+
{
10+
private readonly string[] _expectedOutputPaths;
11+
12+
/// <summary>Creates a maintained peer lab runner command entry.</summary>
13+
/// <param name="sequence">The one-based execution sequence.</param>
14+
/// <param name="command">The command contract.</param>
15+
/// <param name="expectedOutputPaths">The expected output paths.</param>
16+
public SigtranMaintainedPeerLabRunnerCommandEntry(
17+
int sequence,
18+
SigtranMaintainedPeerLabCommand command,
19+
IReadOnlyList<string> expectedOutputPaths)
20+
{
21+
ArgumentNullException.ThrowIfNull(command);
22+
ArgumentNullException.ThrowIfNull(expectedOutputPaths);
23+
Sequence = sequence <= 0 ? throw new ArgumentOutOfRangeException(nameof(sequence)) : sequence;
24+
Command = command;
25+
_expectedOutputPaths = expectedOutputPaths.ToArray();
26+
}
27+
28+
/// <summary>The one-based execution sequence.</summary>
29+
public int Sequence { get; }
30+
31+
/// <summary>The command contract.</summary>
32+
public SigtranMaintainedPeerLabCommand Command { get; }
33+
34+
/// <summary>The expected output paths.</summary>
35+
public IReadOnlyList<string> ExpectedOutputPaths => _expectedOutputPaths.ToArray();
36+
37+
/// <summary>Whether this entry has output mappings for every expected artifact kind.</summary>
38+
public bool CoversExpectedArtifacts => Command.ExpectedArtifactKinds.Count == _expectedOutputPaths.Length;
39+
40+
/// <summary>Formats a compact command entry summary.</summary>
41+
/// <returns>The command entry summary.</returns>
42+
public string Describe()
43+
{
44+
return $"sequence={Sequence} kind={Command.Kind} outputs={_expectedOutputPaths.Length}";
45+
}
46+
}
47+
48+
/// <summary>
49+
/// Describes an executable command manifest for a maintained external peer lab runner.
50+
/// </summary>
51+
public sealed class SigtranMaintainedPeerLabRunnerCommandManifest
52+
{
53+
private readonly SigtranMaintainedPeerLabRunnerCommandEntry[] _commands;
54+
55+
/// <summary>Creates a maintained peer lab runner command manifest.</summary>
56+
/// <param name="inputBundle">The runner input bundle.</param>
57+
/// <param name="artifactPlan">The runner artifact materialization plan.</param>
58+
/// <param name="preflightReport">The runner preflight report.</param>
59+
/// <param name="commands">The ordered command entries.</param>
60+
public SigtranMaintainedPeerLabRunnerCommandManifest(
61+
SigtranMaintainedPeerLabRunnerInputBundle inputBundle,
62+
SigtranMaintainedPeerLabRunnerArtifactMaterializationPlan artifactPlan,
63+
SigtranMaintainedPeerLabRunnerPreflightReport preflightReport,
64+
IReadOnlyList<SigtranMaintainedPeerLabRunnerCommandEntry> commands)
65+
{
66+
ArgumentNullException.ThrowIfNull(inputBundle);
67+
ArgumentNullException.ThrowIfNull(artifactPlan);
68+
ArgumentNullException.ThrowIfNull(preflightReport);
69+
ArgumentNullException.ThrowIfNull(commands);
70+
71+
InputBundle = inputBundle;
72+
ArtifactPlan = artifactPlan;
73+
PreflightReport = preflightReport;
74+
_commands = commands.Count == 0 ? throw new ArgumentException("At least one runner command is required.", nameof(commands)) : commands.ToArray();
75+
}
76+
77+
/// <summary>The runner input bundle.</summary>
78+
public SigtranMaintainedPeerLabRunnerInputBundle InputBundle { get; }
79+
80+
/// <summary>The runner artifact materialization plan.</summary>
81+
public SigtranMaintainedPeerLabRunnerArtifactMaterializationPlan ArtifactPlan { get; }
82+
83+
/// <summary>The runner preflight report.</summary>
84+
public SigtranMaintainedPeerLabRunnerPreflightReport PreflightReport { get; }
85+
86+
/// <summary>The ordered command entries.</summary>
87+
public IReadOnlyList<SigtranMaintainedPeerLabRunnerCommandEntry> Commands => _commands.ToArray();
88+
89+
/// <summary>Whether command entries follow a contiguous one-based sequence.</summary>
90+
public bool HasContiguousSequence => _commands.Select(static command => command.Sequence).Order().SequenceEqual(Enumerable.Range(1, _commands.Length));
91+
92+
/// <summary>Whether every command entry has expected artifact mappings.</summary>
93+
public bool CoversExpectedArtifacts => _commands.All(static command => command.CoversExpectedArtifacts);
94+
95+
/// <summary>Whether the command manifest is ready for runner execution.</summary>
96+
public bool IsExecutionReady => InputBundle.IsMaterializationReady
97+
&& ArtifactPlan.IsMaterializationReady
98+
&& PreflightReport.Ready
99+
&& HasContiguousSequence
100+
&& CoversExpectedArtifacts;
101+
102+
/// <summary>Renders a Markdown command manifest.</summary>
103+
/// <returns>The Markdown command manifest.</returns>
104+
public string RenderMarkdown()
105+
{
106+
StringBuilder builder = new();
107+
builder.AppendLine("# Maintained Peer Lab Runner Command Manifest");
108+
builder.AppendLine();
109+
builder.AppendLine($"Run: `{InputBundle.Workspace.RunManifest.RunId}`");
110+
builder.AppendLine($"Ready: `{IsExecutionReady}`");
111+
builder.AppendLine();
112+
builder.AppendLine("## Commands");
113+
114+
foreach (SigtranMaintainedPeerLabRunnerCommandEntry entry in _commands.OrderBy(static command => command.Sequence))
115+
{
116+
builder.Append("- ");
117+
builder.Append(entry.Sequence);
118+
builder.Append(". ");
119+
builder.Append(entry.Command.Name);
120+
builder.Append(" -> ");
121+
builder.AppendLine(string.Join(", ", entry.ExpectedOutputPaths));
122+
}
123+
124+
return builder.ToString();
125+
}
126+
127+
/// <summary>Formats a compact command manifest summary.</summary>
128+
/// <returns>The command manifest summary.</returns>
129+
public string Describe()
130+
{
131+
return $"run={InputBundle.Workspace.RunManifest.RunId} commands={_commands.Length} sequence={HasContiguousSequence} artifacts={CoversExpectedArtifacts} ready={IsExecutionReady}";
132+
}
133+
}
134+
135+
/// <summary>
136+
/// Provides maintained external peer lab runner command manifest helpers.
137+
/// </summary>
138+
public static class SigtranMaintainedPeerLabRunnerCommandManifests
139+
{
140+
/// <summary>Creates a command manifest from runner input, artifact, and preflight contracts.</summary>
141+
/// <param name="inputBundle">The runner input bundle.</param>
142+
/// <param name="artifactPlan">The runner artifact materialization plan.</param>
143+
/// <param name="preflightReport">The runner preflight report.</param>
144+
/// <returns>The runner command manifest.</returns>
145+
public static SigtranMaintainedPeerLabRunnerCommandManifest Create(
146+
SigtranMaintainedPeerLabRunnerInputBundle inputBundle,
147+
SigtranMaintainedPeerLabRunnerArtifactMaterializationPlan artifactPlan,
148+
SigtranMaintainedPeerLabRunnerPreflightReport preflightReport)
149+
{
150+
ArgumentNullException.ThrowIfNull(inputBundle);
151+
ArgumentNullException.ThrowIfNull(artifactPlan);
152+
ArgumentNullException.ThrowIfNull(preflightReport);
153+
154+
int sequence = 1;
155+
List<SigtranMaintainedPeerLabRunnerCommandEntry> commands = [];
156+
foreach (SigtranMaintainedPeerLabCommand command in inputBundle.Workspace.RunManifest.CommandPlan.Commands)
157+
{
158+
IReadOnlyList<string> outputs = artifactPlan.Outputs
159+
.Where(output => command.ExpectedArtifactKinds.Contains(output.Kind))
160+
.Select(static output => output.Path)
161+
.ToArray();
162+
commands.Add(new(sequence, command, outputs));
163+
sequence++;
164+
}
165+
166+
return new(inputBundle, artifactPlan, preflightReport, commands);
167+
}
168+
}

0 commit comments

Comments
 (0)