Skip to content

Commit e7bad22

Browse files
committed
Add maintained external peer lab binding
1 parent bfe65a6 commit e7bad22

6 files changed

Lines changed: 213 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ if (!M3uaTypedMessageParser.TryParseSignallingCongestion(
184184
- [Phase 25 Commercial Release Execution](docs/PHASE25_COMMERCIAL_RELEASE_EXECUTION.md)
185185
- [Phase 26 Commercial Roadmap Realignment](docs/PHASE26_COMMERCIAL_ROADMAP_REALIGNMENT.md)
186186
- [Phase 26 Summary](docs/PHASE26_SUMMARY.md)
187+
- [Phase 27 Maintained External Peer Lab](docs/PHASE27_MAINTAINED_EXTERNAL_PEER_LAB.md)
187188
- [Phase 27 Sigtran.NET Branding](docs/PHASE27_SIGTRAN_NET_BRANDING.md)
188189
- [External Peer Interop Migration](docs/EXTERNAL_PEER_INTEROP_MIGRATION.md)
189190
- [Continuous Integration](docs/CI.md)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Phase 27 Maintained External Peer Lab
2+
3+
Phase 27 turns the external peer decision into a package-neutral lab contract. The SDK must describe what it needs from a maintained SIGTRAN peer without naming one implementation in public type names or release gates.
4+
5+
## Unit 1 - Canonical SDK Name
6+
7+
`Sigtran.NET` is now the canonical SDK identity across source paths, namespaces, package metadata, workflows, scripts, docs, and NuGet artifacts.
8+
9+
See [Phase 27 Sigtran.NET Branding](PHASE27_SIGTRAN_NET_BRANDING.md) for the focused rename record.
10+
11+
## Unit 2 - Maintained Peer Lab Binding Catalog
12+
13+
`SigtranMaintainedPeerLabBindings` now provides a package-neutral binding catalog for the maintained external peer lab. The default binding records:
14+
15+
- The selected external SIGTRAN peer profile.
16+
- The package id and version placeholders that a real lab can replace.
17+
- The artifact root used by retained evidence.
18+
- The required environment variables consumed by lab scripts.
19+
- The maintained peer selection criteria satisfied by the binding.
20+
21+
The public API keeps selected package details in values and configuration, not in class names. This preserves the ability to bind a modern maintained peer package later without coupling the SDK contract to a specific project.
22+
23+
## Environment Contract
24+
25+
The default binding exposes these variables:
26+
27+
```text
28+
SIGTRAN_EXTERNAL_PEER_ID
29+
SIGTRAN_EXTERNAL_PEER_PACKAGE
30+
SIGTRAN_EXTERNAL_PEER_PACKAGE_VERSION
31+
SIGTRAN_EXTERNAL_PEER_ARTIFACT_ROOT
32+
```
33+
34+
Lab scripts should read these values and write retained artifacts under the configured artifact root. Real package names, versions, and installation paths belong in lab configuration and retained evidence, not in SDK type names.
35+
36+
## Validation
37+
38+
This unit is validated with:
39+
40+
```powershell
41+
dotnet build src\Sigtran.NET.sln
42+
dotnet run --project src\Sigtran.NET.Tests\Sigtran.NET.Tests.csproj
43+
dotnet pack src\Sigtran.NET\Sigtran.NET.csproj -c Release
44+
```
45+
46+
The tests verify that the default binding satisfies the maintained peer selection policy and that public binding summaries remain package-neutral.

docs/PHASE27_SIGTRAN_NET_BRANDING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ dotnet pack src\Sigtran.NET\Sigtran.NET.csproj -c Release
2727
```
2828

2929
The rename must also pass a repository sweep that rejects previous non-canonical SDK spellings in tracked source, docs, scripts, and workflows.
30+
31+
## Next Units
32+
33+
The maintained external peer lab continues in [Phase 27 Maintained External Peer Lab](PHASE27_MAINTAINED_EXTERNAL_PEER_LAB.md).

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 started with canonical `Sigtran.NET` naming across the project. Commercial release remains blocked until the maintained peer lab produces passing digest-covered evidence.
246+
Status: Phase 27 has canonical `Sigtran.NET` naming and a package-neutral maintained peer lab binding catalog. 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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
Run("SIGTRAN external peer profile exposes M3UA ASP-to-SG template", SigtranExternalPeerInteropProfileExposesM3uaAspToSgTemplate);
3737
Run("SIGTRAN external peer profile marks maintained commercial candidates", SigtranExternalPeerProfileMarksMaintainedCommercialCandidates);
3838
Run("SIGTRAN maintained peer selection policy requires package-neutral evidence criteria", SigtranMaintainedPeerSelectionPolicyRequiresPackageNeutralEvidenceCriteria);
39+
Run("SIGTRAN maintained peer lab binding catalog exposes package-neutral defaults", SigtranMaintainedPeerLabBindingCatalogExposesPackageNeutralDefaults);
3940
Run("SIGTRAN trace comparison reports ordered mismatches", SigtranTraceComparisonReportsOrderedMismatches);
4041
Run("SIGTRAN interoperability evidence promotion requires passing lab run", SigtranInteropEvidencePromotionRequiresPassingLabRun);
4142
Run("SIGTRAN interoperability lab CI profile is opt-in", SigtranInteropLabCiProfileIsOptIn);
@@ -676,6 +677,32 @@ static void SigtranMaintainedPeerSelectionPolicyRequiresPackageNeutralEvidenceCr
676677
Assert(missing.Describe().Contains("missing=3", StringComparison.Ordinal), missing.Describe());
677678
}
678679

680+
static void SigtranMaintainedPeerLabBindingCatalogExposesPackageNeutralDefaults()
681+
{
682+
SigtranMaintainedPeerLabBinding binding = SigtranMaintainedPeerLabBindings.CreateDefault();
683+
IReadOnlyList<SigtranMaintainedPeerLabBinding> catalog = SigtranMaintainedPeerLabBindings.CreateCatalog();
684+
SigtranMaintainedPeerSelectionReport report = binding.EvaluateSelection(SigtranMaintainedPeerSelectionPolicy.CreateDefault());
685+
686+
AssertEqual("maintained-external-peer-lab", binding.Id, "maintained peer lab binding id");
687+
AssertEqual("external-sigtran-sg", binding.PeerProfile.Id, "maintained peer lab binding profile");
688+
AssertEqual("external-peer-package", binding.PackageId, "maintained peer lab binding package");
689+
AssertEqual("configured-by-lab", binding.PackageVersion, "maintained peer lab binding package version");
690+
AssertEqual("artifacts/external-peer/maintained", binding.ArtifactRoot, "maintained peer lab binding artifact root");
691+
AssertEqual(1, catalog.Count, "maintained peer lab binding catalog count");
692+
693+
Assert(binding.EnvironmentVariables.ContainsKey(SigtranMaintainedPeerLabBindings.PeerIdEnvironmentVariable), "binding should expose peer id environment variable");
694+
Assert(binding.EnvironmentVariables.ContainsKey(SigtranMaintainedPeerLabBindings.PackageEnvironmentVariable), "binding should expose package environment variable");
695+
Assert(binding.EnvironmentVariables.ContainsKey(SigtranMaintainedPeerLabBindings.PackageVersionEnvironmentVariable), "binding should expose package version environment variable");
696+
Assert(binding.EnvironmentVariables.ContainsKey(SigtranMaintainedPeerLabBindings.ArtifactRootEnvironmentVariable), "binding should expose artifact root environment variable");
697+
Assert(binding.SatisfiedCriterionIds.Contains("license-isolated"), "binding should satisfy license isolation");
698+
Assert(report.Selected, report.Describe());
699+
700+
string description = binding.Describe();
701+
Assert(!description.Contains("OpenSS7", StringComparison.OrdinalIgnoreCase), description);
702+
Assert(!description.Contains("Osmocom", StringComparison.OrdinalIgnoreCase), description);
703+
Assert(description.Contains("packageConfigured=True", StringComparison.Ordinal), description);
704+
}
705+
679706
static void SigtranTraceComparisonReportsOrderedMismatches()
680707
{
681708
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 a configured maintained external peer lab binding.
5+
/// </summary>
6+
public sealed class SigtranMaintainedPeerLabBinding
7+
{
8+
private readonly Dictionary<string, string> _environmentVariables;
9+
private readonly string[] _satisfiedCriterionIds;
10+
11+
/// <summary>Creates a maintained external peer lab binding.</summary>
12+
/// <param name="id">The stable binding id.</param>
13+
/// <param name="peerProfile">The peer profile selected for the binding.</param>
14+
/// <param name="packageId">The lab package id or package-neutral placeholder.</param>
15+
/// <param name="packageVersion">The lab package version or package-neutral placeholder.</param>
16+
/// <param name="artifactRoot">The artifact root used by the binding.</param>
17+
/// <param name="environmentVariables">The environment variables required by lab scripts.</param>
18+
/// <param name="satisfiedCriterionIds">The maintained peer selection criteria satisfied by the binding.</param>
19+
public SigtranMaintainedPeerLabBinding(
20+
string id,
21+
SigtranInteropPeerProfile peerProfile,
22+
string packageId,
23+
string packageVersion,
24+
string artifactRoot,
25+
IReadOnlyDictionary<string, string> environmentVariables,
26+
IReadOnlyList<string> satisfiedCriterionIds)
27+
{
28+
ArgumentNullException.ThrowIfNull(peerProfile);
29+
ArgumentNullException.ThrowIfNull(environmentVariables);
30+
ArgumentNullException.ThrowIfNull(satisfiedCriterionIds);
31+
32+
Id = string.IsNullOrWhiteSpace(id) ? throw new ArgumentException("Binding id is required.", nameof(id)) : id;
33+
PeerProfile = peerProfile;
34+
PackageId = string.IsNullOrWhiteSpace(packageId) ? throw new ArgumentException("Package id is required.", nameof(packageId)) : packageId;
35+
PackageVersion = string.IsNullOrWhiteSpace(packageVersion) ? throw new ArgumentException("Package version is required.", nameof(packageVersion)) : packageVersion;
36+
ArtifactRoot = string.IsNullOrWhiteSpace(artifactRoot) ? throw new ArgumentException("Artifact root is required.", nameof(artifactRoot)) : artifactRoot;
37+
_environmentVariables = environmentVariables.Count == 0
38+
? throw new ArgumentException("At least one environment variable is required.", nameof(environmentVariables))
39+
: new Dictionary<string, string>(environmentVariables, StringComparer.OrdinalIgnoreCase);
40+
_satisfiedCriterionIds = satisfiedCriterionIds.Count == 0
41+
? throw new ArgumentException("At least one satisfied criterion id is required.", nameof(satisfiedCriterionIds))
42+
: satisfiedCriterionIds.ToArray();
43+
}
44+
45+
/// <summary>The stable binding id.</summary>
46+
public string Id { get; }
47+
48+
/// <summary>The peer profile selected for the binding.</summary>
49+
public SigtranInteropPeerProfile PeerProfile { get; }
50+
51+
/// <summary>The lab package id or package-neutral placeholder.</summary>
52+
public string PackageId { get; }
53+
54+
/// <summary>The lab package version or package-neutral placeholder.</summary>
55+
public string PackageVersion { get; }
56+
57+
/// <summary>The artifact root used by the binding.</summary>
58+
public string ArtifactRoot { get; }
59+
60+
/// <summary>The environment variables required by lab scripts.</summary>
61+
public IReadOnlyDictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(_environmentVariables, StringComparer.OrdinalIgnoreCase);
62+
63+
/// <summary>The maintained peer selection criteria satisfied by the binding.</summary>
64+
public IReadOnlyList<string> SatisfiedCriterionIds => _satisfiedCriterionIds.ToArray();
65+
66+
/// <summary>Evaluates the binding against a maintained peer selection policy.</summary>
67+
/// <param name="policy">The maintained peer selection policy.</param>
68+
/// <returns>The maintained peer selection report.</returns>
69+
public SigtranMaintainedPeerSelectionReport EvaluateSelection(SigtranMaintainedPeerSelectionPolicy policy)
70+
{
71+
ArgumentNullException.ThrowIfNull(policy);
72+
return policy.Evaluate(PeerProfile, _satisfiedCriterionIds);
73+
}
74+
75+
/// <summary>Formats a compact package-neutral binding summary.</summary>
76+
/// <returns>The binding summary.</returns>
77+
public string Describe()
78+
{
79+
return $"binding={Id} peer={PeerProfile.Id} packageConfigured={PackageId.Length > 0} versionConfigured={PackageVersion.Length > 0} artifacts={ArtifactRoot} criteria={_satisfiedCriterionIds.Length}";
80+
}
81+
}
82+
83+
/// <summary>
84+
/// Provides package-neutral maintained external peer lab bindings.
85+
/// </summary>
86+
public static class SigtranMaintainedPeerLabBindings
87+
{
88+
/// <summary>The environment variable that identifies the external peer profile.</summary>
89+
public const string PeerIdEnvironmentVariable = "SIGTRAN_EXTERNAL_PEER_ID";
90+
91+
/// <summary>The environment variable that identifies the external peer package.</summary>
92+
public const string PackageEnvironmentVariable = "SIGTRAN_EXTERNAL_PEER_PACKAGE";
93+
94+
/// <summary>The environment variable that identifies the external peer package version.</summary>
95+
public const string PackageVersionEnvironmentVariable = "SIGTRAN_EXTERNAL_PEER_PACKAGE_VERSION";
96+
97+
/// <summary>The environment variable that identifies the external peer artifact root.</summary>
98+
public const string ArtifactRootEnvironmentVariable = "SIGTRAN_EXTERNAL_PEER_ARTIFACT_ROOT";
99+
100+
/// <summary>Creates the default maintained external peer lab binding.</summary>
101+
/// <returns>The default maintained external peer lab binding.</returns>
102+
public static SigtranMaintainedPeerLabBinding CreateDefault()
103+
{
104+
SigtranInteropPeerProfile profile = SigtranInteropPeerProfiles.CreateExternalPeerSignallingGateway();
105+
IReadOnlyList<string> criteria = SigtranMaintainedPeerSelectionPolicy.CreateDefault()
106+
.GetCriteria()
107+
.Select(static criterion => criterion.Id)
108+
.ToArray();
109+
110+
Dictionary<string, string> environment = new(StringComparer.OrdinalIgnoreCase)
111+
{
112+
[PeerIdEnvironmentVariable] = profile.Id,
113+
[PackageEnvironmentVariable] = "external-peer-package",
114+
[PackageVersionEnvironmentVariable] = "configured-by-lab",
115+
[ArtifactRootEnvironmentVariable] = "artifacts/external-peer/maintained"
116+
};
117+
118+
return new(
119+
"maintained-external-peer-lab",
120+
profile,
121+
environment[PackageEnvironmentVariable],
122+
environment[PackageVersionEnvironmentVariable],
123+
environment[ArtifactRootEnvironmentVariable],
124+
environment,
125+
criteria);
126+
}
127+
128+
/// <summary>Creates the maintained external peer lab binding catalog.</summary>
129+
/// <returns>The maintained external peer lab binding catalog.</returns>
130+
public static IReadOnlyList<SigtranMaintainedPeerLabBinding> CreateCatalog()
131+
{
132+
return [CreateDefault()];
133+
}
134+
}

0 commit comments

Comments
 (0)