Skip to content

Commit 22ad68b

Browse files
committed
Bind package publication artifacts
1 parent 9aa682b commit 22ad68b

5 files changed

Lines changed: 212 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ The first production milestone is M3UA over a transport abstraction. SCCP, TCAP,
7777
| Commercial evidence file verification | Phase 39 foundation-complete: retained file evidence item verification, retained file manifest coverage, file verification blocker reporting, retention ledger modeling, integrity sealing, publication attachment planning, verified promotion gating, command planning, status reporting, final validation, and documentation alignment are available, checking file existence, non-empty size, SHA-256 validity, digest match, UTC observation time, unique retained paths, promotion-required handoff coverage, explicit verification blockers, reviewer identity, immutable retention, UTC retention windows, minimum duration, deterministic aggregate ledger digests, sealed ledger attachment coverage, trace-bearing redaction approval, commercial readiness report presence, explicit promotion approval, workflow-ready verification command order, and readiness status separation; real retained file evidence remains required before commercial publication |
7878
| Commercial evidence filesystem execution | Phase 40 foundation-complete: filesystem observation, manifest execution, verification report execution, artifact writing, ledger execution, seal execution, publication attachment execution, promotion execution, command materialization, status reporting, documentation, and final validation are complete, reading retained files from disk, computing real SHA-256 digests, reporting file existence and size, supporting retained-path-to-local-path overrides, building retained file manifests from real observations, exposing blocker-aware retained file reports from filesystem evidence, writing retained Markdown/TSV verification artifacts, creating retention ledgers from filesystem-backed reports, sealing those ledgers with deterministic aggregate SHA-256 digests, creating release dossier attachments with redaction approval gates, evaluating reviewer-approved promotion gates, and writing ordered execution scripts; commercial publication still requires a real approved commercial run |
7979
| Approved commercial run publication handoff | Phase 41 foundation-complete: approved run target identity, approval checklist, reviewer approval manifest, approval report writing, promotion package, publication handoff, handoff gate, approval audit trail, command materialization, status reporting, documentation, and final validation are complete, binding package version, source commit, operator identity, UTC run timing, retained artifact root, filesystem-backed promotion execution, verified report, ready ledger/seal/attachments, redaction approval, promotion approval, release/security/operations reviewer approvals, UTC approval timestamps, checklist digest coverage, retained Markdown report output, report SHA-256 digest coverage, approved package references, requested publication channel, requester identity, UTC handoff time, explicit publish intent, channel version policy, handoff blocker reporting, digest-covered audit lifecycle events, and ordered approval workflow scripts; package publication still requires a real approved commercial run |
80-
| Commercial package publication gate integration | Phase 42 in progress: handoff-derived package publication request is available and binds package version, channel, requester identity, run id, promotion package id, UTC request time, and artifact-binding readiness; package publication remains blocked until artifact binding, credential readiness, evidence assembly, release guard, channel policy, gate execution, dry-run rehearsal, guarded commands, and status reporting are complete |
80+
| Commercial package publication gate integration | Phase 42 in progress: handoff-derived package publication request and digest-covered package artifact binding are available, binding package version, channel, requester identity, run id, promotion package id, UTC request time, nupkg/snupkg paths, retained sizes, SHA-256 digests, version-matched paths, and package integrity manifest projection; package publication remains blocked until credential readiness, evidence assembly, release guard, channel policy, gate execution, dry-run rehearsal, guarded commands, and status reporting are complete |
8181

8282
## Requirements
8383

docs/PHASE42_COMMERCIAL_PACKAGE_PUBLICATION_GATE_INTEGRATION.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Phase 42 connects an approved commercial evidence publication handoff to the pac
77
| Unit | Capability | Status |
88
| --- | --- | --- |
99
| 1 | Package publication request derived from approved handoff gate | Complete |
10-
| 2 | Package artifact binding with package and symbols digests | Pending |
10+
| 2 | Package artifact binding with package and symbols digests | Complete |
1111
| 3 | Credential readiness bridge for required publication secrets | Pending |
1212
| 4 | Publication evidence assembly from approved run and artifacts | Pending |
1313
| 5 | Release publish guard bridge | Pending |
@@ -23,6 +23,8 @@ Phase 42 connects an approved commercial evidence publication handoff to the pac
2323

2424
The request can only move to package artifact binding when the upstream handoff gate allows evaluation and the request time is normalized to UTC. This keeps package publication blocked when approval handoff evidence is incomplete.
2525

26+
`SigtranPackagePublicationArtifactSet` binds the request to the retained NuGet package and symbols package. It requires unique artifact kinds, package and symbols coverage, non-empty retained sizes, SHA-256 hex digests, and paths that match the requested package version. The artifact set can also project into the existing `SigtranPackageIntegrityManifest` contract used by the package publication gate.
27+
2628
## Commercial Gate Position
2729

28-
Phase 42 is still in progress. Unit 1 establishes the handoff-to-publication request boundary. Real package publication remains blocked until package artifacts, required credentials, retained evidence, release guard, channel policy, and the final publication gate all pass.
30+
Phase 42 is still in progress. Units 1 and 2 establish the handoff-to-publication request boundary and digest-covered package artifact binding. Real package publication remains blocked until required credentials, retained evidence, release guard, channel policy, and the final publication gate all pass.

docs/PHASE42_SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ Phase 42 is in progress. It connects approved commercial evidence handoff record
55
## Completed Capabilities
66

77
- Package publication request that derives package version, channel, requester identity, run id, promotion package id, UTC request time, and artifact-binding readiness from an approved handoff gate.
8+
- Package publication artifact set that binds the request to nupkg/snupkg paths, retained sizes, SHA-256 digests, requested package version matching, and the existing package integrity manifest.
89

910
## Readiness Position
1011

11-
Unit 1 is complete. The SDK can now represent the publication request boundary after commercial approval handoff. Package publication still requires artifact binding, credential readiness, evidence assembly, release guard evaluation, channel policy evaluation, final gate execution, dry-run rehearsal, guarded command materialization, status reporting, and retained real release evidence.
12+
Units 1 and 2 are complete. The SDK can now represent the publication request boundary after commercial approval handoff and bind digest-covered package artifacts to that request. Package publication still requires credential readiness, evidence assembly, release guard evaluation, channel policy evaluation, final gate execution, dry-run rehearsal, guarded command materialization, status reporting, and retained real release evidence.

src/Sigtran.NET.Tests/Program.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@
323323
Run("SIGTRAN commercial evidence publication handoff enforces channel version policy", SigtranCommercialEvidencePublicationHandoffEnforcesChannelVersionPolicy);
324324
Run("SIGTRAN commercial evidence publication handoff gate reports blockers", SigtranCommercialEvidencePublicationHandoffGateReportsBlockers);
325325
Run("SIGTRAN package publication request derives from approved handoff", SigtranPackagePublicationRequestDerivesFromApprovedHandoff);
326+
Run("SIGTRAN package publication artifacts bind package and symbols digests", SigtranPackagePublicationArtifactsBindPackageAndSymbolsDigests);
326327
Run("SIGTRAN commercial evidence approval audit trail covers lifecycle", SigtranCommercialEvidenceApprovalAuditTrailCoversLifecycle);
327328
Run("SIGTRAN commercial evidence approval command materializer writes script", SigtranCommercialEvidenceApprovalCommandMaterializerWritesScript);
328329
Run("SIGTRAN commercial evidence approval handoff status summarizes final validation", SigtranCommercialEvidenceApprovalHandoffStatusSummarizesFinalValidation);
@@ -5742,6 +5743,42 @@ static void SigtranPackagePublicationRequestDerivesFromApprovedHandoff()
57425743
}
57435744
}
57445745

5746+
static void SigtranPackagePublicationArtifactsBindPackageAndSymbolsDigests()
5747+
{
5748+
string tempRoot = Path.Combine(Path.GetTempPath(), "sigtran-commercial-evidence-" + Guid.NewGuid().ToString("N"));
5749+
Directory.CreateDirectory(tempRoot);
5750+
5751+
try
5752+
{
5753+
SigtranPackagePublicationRequest request = CreateReadyPackagePublicationRequest(tempRoot);
5754+
5755+
SigtranPackagePublicationArtifactSet artifacts = CreateReadyPackagePublicationArtifactSet(request);
5756+
SigtranPackagePublicationArtifactSet blocked = SigtranPackagePublicationArtifacts.Create(
5757+
request,
5758+
"Sigtran.NET",
5759+
[
5760+
new(SigtranPackageArtifactKind.Package, "artifacts/Sigtran.NET.1.0.0-rc.1.nupkg", "not-a-digest", 1024, required: true),
5761+
new(SigtranPackageArtifactKind.SymbolPackage, "artifacts/Sigtran.NET.1.0.0-rc.1.snupkg", new string('b', 64), 512, required: true)
5762+
]);
5763+
5764+
SigtranPackageIntegrityManifest integrity = artifacts.ToIntegrityManifest();
5765+
5766+
Assert(artifacts.IsReadyForCredentialEvaluation, artifacts.Describe());
5767+
Assert(artifacts.IncludesPackage, "publication artifacts should include nupkg");
5768+
Assert(artifacts.IncludesSymbolPackage, "publication artifacts should include snupkg");
5769+
Assert(artifacts.UsesUniqueArtifactKinds, "publication artifacts should use unique kinds");
5770+
Assert(artifacts.RequiredArtifactsAreReady, "publication artifacts should have digest-covered required artifacts");
5771+
Assert(artifacts.PathsMatchRequestedVersion, "publication artifact paths should match request version");
5772+
Assert(integrity.IsComplete, "publication artifacts should project to a complete integrity manifest");
5773+
Assert(!blocked.IsReadyForCredentialEvaluation, "invalid package digest should block publication artifact readiness");
5774+
Assert(!blocked.RequiredArtifactsAreReady, "blocked publication artifacts should expose digest failure");
5775+
}
5776+
finally
5777+
{
5778+
DeleteTempEvidenceRoot(tempRoot);
5779+
}
5780+
}
5781+
57455782
static void SigtranCommercialEvidenceApprovalAuditTrailCoversLifecycle()
57465783
{
57475784
string tempRoot = Path.Combine(Path.GetTempPath(), "sigtran-commercial-evidence-" + Guid.NewGuid().ToString("N"));
@@ -5826,6 +5863,24 @@ static SigtranCommercialEvidencePublicationHandoffGateResult CreateReadyCommerci
58265863
commercialReadinessApproved: false);
58275864
}
58285865

5866+
static SigtranPackagePublicationRequest CreateReadyPackagePublicationRequest(string tempRoot)
5867+
{
5868+
return SigtranPackagePublicationRequests.Create(
5869+
CreateReadyCommercialEvidencePublicationHandoffGateResult(tempRoot),
5870+
DateTimeOffset.UtcNow);
5871+
}
5872+
5873+
static SigtranPackagePublicationArtifactSet CreateReadyPackagePublicationArtifactSet(SigtranPackagePublicationRequest request)
5874+
{
5875+
return SigtranPackagePublicationArtifacts.Create(
5876+
request,
5877+
"Sigtran.NET",
5878+
[
5879+
new(SigtranPackageArtifactKind.Package, "artifacts/Sigtran.NET.1.0.0-rc.1.nupkg", new string('a', 64), 1024, required: true),
5880+
new(SigtranPackageArtifactKind.SymbolPackage, "artifacts/Sigtran.NET.1.0.0-rc.1.snupkg", new string('b', 64), 512, required: true)
5881+
]);
5882+
}
5883+
58295884
static SigtranCommercialEvidenceApprovedRunPromotionPackage CreateReadyCommercialEvidenceApprovedRunPromotionPackage(string tempRoot)
58305885
{
58315886
return SigtranCommercialEvidenceApprovedRunPromotionPackages.CreateDefault(
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
namespace Sigtran.NET.Core.Utilities;
2+
3+
/// <summary>
4+
/// Describes a package artifact that can participate in publication evaluation.
5+
/// </summary>
6+
public sealed class SigtranPackagePublicationArtifact
7+
{
8+
/// <summary>Creates a package publication artifact reference.</summary>
9+
/// <param name="kind">The package artifact kind.</param>
10+
/// <param name="path">The retained artifact path.</param>
11+
/// <param name="sha256">The retained artifact SHA-256 digest.</param>
12+
/// <param name="sizeBytes">The retained artifact size in bytes.</param>
13+
/// <param name="required">Whether the artifact is required for publication.</param>
14+
public SigtranPackagePublicationArtifact(
15+
SigtranPackageArtifactKind kind,
16+
string path,
17+
string sha256,
18+
long sizeBytes,
19+
bool required)
20+
{
21+
Kind = kind;
22+
Path = string.IsNullOrWhiteSpace(path) ? throw new ArgumentException("Artifact path is required.", nameof(path)) : path;
23+
Sha256 = string.IsNullOrWhiteSpace(sha256) ? throw new ArgumentException("SHA-256 digest is required.", nameof(sha256)) : sha256;
24+
SizeBytes = sizeBytes >= 0 ? sizeBytes : throw new ArgumentOutOfRangeException(nameof(sizeBytes));
25+
Required = required;
26+
}
27+
28+
/// <summary>The package artifact kind.</summary>
29+
public SigtranPackageArtifactKind Kind { get; }
30+
31+
/// <summary>The retained artifact path.</summary>
32+
public string Path { get; }
33+
34+
/// <summary>The retained artifact SHA-256 digest.</summary>
35+
public string Sha256 { get; }
36+
37+
/// <summary>The retained artifact size in bytes.</summary>
38+
public long SizeBytes { get; }
39+
40+
/// <summary>Whether the artifact is required for publication.</summary>
41+
public bool Required { get; }
42+
43+
/// <summary>Whether the artifact digest is a valid SHA-256 hex value.</summary>
44+
public bool HasValidDigest => Sha256.Length == 64
45+
&& Sha256.All(Uri.IsHexDigit);
46+
47+
/// <summary>Whether the artifact has a non-empty retained size.</summary>
48+
public bool HasContent => SizeBytes > 0;
49+
50+
/// <summary>Whether the artifact can participate in publication evaluation.</summary>
51+
public bool IsReady => !Required || (HasValidDigest && HasContent);
52+
}
53+
54+
/// <summary>
55+
/// Binds package artifacts to a package publication request.
56+
/// </summary>
57+
public sealed class SigtranPackagePublicationArtifactSet
58+
{
59+
/// <summary>Creates a package publication artifact set.</summary>
60+
/// <param name="request">The package publication request.</param>
61+
/// <param name="packageId">The package identifier.</param>
62+
/// <param name="artifacts">The retained package artifacts.</param>
63+
public SigtranPackagePublicationArtifactSet(
64+
SigtranPackagePublicationRequest request,
65+
string packageId,
66+
IReadOnlyList<SigtranPackagePublicationArtifact> artifacts)
67+
{
68+
Request = request ?? throw new ArgumentNullException(nameof(request));
69+
PackageId = string.IsNullOrWhiteSpace(packageId) ? throw new ArgumentException("Package id is required.", nameof(packageId)) : packageId;
70+
ArgumentNullException.ThrowIfNull(artifacts);
71+
Artifacts = artifacts.Count == 0 ? throw new ArgumentException("At least one package artifact is required.", nameof(artifacts)) : artifacts.ToArray();
72+
}
73+
74+
/// <summary>The package publication request.</summary>
75+
public SigtranPackagePublicationRequest Request { get; }
76+
77+
/// <summary>The package identifier.</summary>
78+
public string PackageId { get; }
79+
80+
/// <summary>The retained package artifacts.</summary>
81+
public IReadOnlyList<SigtranPackagePublicationArtifact> Artifacts { get; }
82+
83+
/// <summary>Whether the artifact set includes a NuGet package.</summary>
84+
public bool IncludesPackage => Artifacts.Any(static artifact => artifact.Kind == SigtranPackageArtifactKind.Package);
85+
86+
/// <summary>Whether the artifact set includes a symbols package.</summary>
87+
public bool IncludesSymbolPackage => Artifacts.Any(static artifact => artifact.Kind == SigtranPackageArtifactKind.SymbolPackage);
88+
89+
/// <summary>Whether each artifact kind appears only once.</summary>
90+
public bool UsesUniqueArtifactKinds => Artifacts
91+
.Select(static artifact => artifact.Kind)
92+
.Distinct()
93+
.Count() == Artifacts.Count;
94+
95+
/// <summary>Whether required artifacts have valid digests and retained content.</summary>
96+
public bool RequiredArtifactsAreReady => Artifacts
97+
.Where(static artifact => artifact.Required)
98+
.All(static artifact => artifact.IsReady);
99+
100+
/// <summary>Whether artifact paths match the requested package version.</summary>
101+
public bool PathsMatchRequestedVersion => Artifacts.All(artifact =>
102+
artifact.Path.Contains($"{PackageId}.{Request.PackageVersion}", StringComparison.OrdinalIgnoreCase));
103+
104+
/// <summary>Whether the artifacts are ready for credential evaluation.</summary>
105+
public bool IsReadyForCredentialEvaluation => Request.IsReadyForArtifactBinding
106+
&& IncludesPackage
107+
&& IncludesSymbolPackage
108+
&& UsesUniqueArtifactKinds
109+
&& RequiredArtifactsAreReady
110+
&& PathsMatchRequestedVersion;
111+
112+
/// <summary>Creates a package integrity manifest from the bound publication artifacts.</summary>
113+
/// <returns>The package integrity manifest.</returns>
114+
public SigtranPackageIntegrityManifest ToIntegrityManifest()
115+
{
116+
SigtranPackageIntegrityManifest manifest = new();
117+
foreach (SigtranPackagePublicationArtifact artifact in Artifacts)
118+
{
119+
manifest.Add(new SigtranPackageIntegrityEntry(artifact.Kind, artifact.Path, artifact.Sha256));
120+
}
121+
122+
return manifest;
123+
}
124+
125+
/// <summary>Formats a compact package publication artifact set summary.</summary>
126+
/// <returns>The package publication artifact set summary.</returns>
127+
public string Describe()
128+
{
129+
return $"packagePublicationArtifactsReady={IsReadyForCredentialEvaluation} packageId={PackageId} artifacts={Artifacts.Count}";
130+
}
131+
}
132+
133+
/// <summary>
134+
/// Provides package publication artifact set helpers.
135+
/// </summary>
136+
public static class SigtranPackagePublicationArtifacts
137+
{
138+
/// <summary>Creates a package publication artifact set.</summary>
139+
/// <param name="request">The package publication request.</param>
140+
/// <param name="packageId">The package identifier.</param>
141+
/// <param name="artifacts">The retained package artifacts.</param>
142+
/// <returns>The package publication artifact set.</returns>
143+
public static SigtranPackagePublicationArtifactSet Create(
144+
SigtranPackagePublicationRequest request,
145+
string packageId,
146+
IReadOnlyList<SigtranPackagePublicationArtifact> artifacts)
147+
{
148+
return new(request, packageId, artifacts);
149+
}
150+
}

0 commit comments

Comments
 (0)