Skip to content

Commit b8860e2

Browse files
committed
Add commercial evidence run approval report writer
1 parent c1097cf commit b8860e2

7 files changed

Lines changed: 191 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ The first production milestone is M3UA over a transport abstraction. SCCP, TCAP,
7676
| Commercial evidence artifact intake | Phase 38 foundation-complete: artifact intake target identity, artifact source registration, SHA-256 digest coverage, redaction review, completeness evaluation, dossier reporting, promotion handoff, execution-to-dossier bridge, status reporting, and final validation are available, binding a stable intake id, reviewer identity, UTC receipt time, run-scoped dossier root, concrete source paths, unique retained dossier paths, retained digests, trace-bearing artifact approvals, explicit intake blockers, retained Markdown summary, digest-covered handoff, end-to-end intake assembly, and intake readiness status to a governed execution run; real artifact files are still required before commercial publication |
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 |
79-
| Approved commercial run publication handoff | Phase 41 in progress: approved run target identity, approval checklist, and reviewer approval manifest are available, 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, and checklist digest coverage; report writing, promotion package, publication handoff, blocker gate, audit trail, command materialization, status reporting, and final validation remain |
79+
| Approved commercial run publication handoff | Phase 41 in progress: approved run target identity, approval checklist, reviewer approval manifest, and approval report writing are available, 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, and report SHA-256 digest coverage; promotion package, publication handoff, blocker gate, audit trail, command materialization, status reporting, and final validation remain |
8080

8181
## Requirements
8282

docs/PHASE41_APPROVED_COMMERCIAL_RUN_PUBLICATION_HANDOFF.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ This unit turns commercial run approval into an explicit checklist with blocker
4444

4545
This unit makes commercial approval auditable and role-aware before any publication handoff is created.
4646

47+
## Unit 4 - Approval Report Writer
48+
49+
`SigtranCommercialEvidenceRunApprovalReportWriters` renders and retains a Markdown approval report:
50+
51+
- Run id, package version, source commit, and artifact root.
52+
- Checklist digest and manifest readiness.
53+
- Reviewer approval roles.
54+
- UTC write time.
55+
- SHA-256 digest for the retained report.
56+
57+
This unit turns approval data into a retained report artifact that later promotion packages and publication handoffs can reference.
58+
4759
## Validation
4860

4961
Each unit in this phase is validated with:

docs/PHASE41_SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ Phase 41 prepares the SDK to move from filesystem-backed evidence verification i
77
- Approved run target that binds package version, source commit, operator identity, UTC run timing, retained artifact root, and filesystem-backed promotion execution.
88
- Approval checklist that requires verified filesystem promotion, ready report/ledger/seal/attachments, redaction approval, promotion approval, and reviewer approval records.
99
- Reviewer approval manifest that records release, security, and operations approvals with UTC timestamps and a deterministic checklist digest.
10+
- Approval report writer that renders retained Markdown approval reports with SHA-256 digest coverage.
1011

1112
## Readiness Position
1213

13-
The phase is in progress. Approved run target identity, approval checklist, and reviewer approval manifest are available. Report writing, promotion package, publication handoff, blocker gate, audit trail, command materialization, status reporting, documentation, and final validation remain.
14+
The phase is in progress. Approved run target identity, approval checklist, reviewer approval manifest, and approval report writing are available. Promotion package, publication handoff, blocker gate, audit trail, command materialization, status reporting, documentation, and final validation remain.

docs/PHASE_INDEX.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ This index is the complete phase map for Sigtran.NET. It links each phase to the
4545
| 38 | Commercial evidence artifact intake | [Phase 38 Commercial Evidence Artifact Intake](PHASE38_COMMERCIAL_EVIDENCE_ARTIFACT_INTAKE.md), [Phase 38 Summary](PHASE38_SUMMARY.md) | Foundation complete; real artifact files still required |
4646
| 39 | Commercial evidence file verification | [Phase 39 Commercial Evidence File Verification](PHASE39_COMMERCIAL_EVIDENCE_FILE_VERIFICATION.md), [Phase 39 Summary](PHASE39_SUMMARY.md) | Foundation complete; real retained file evidence required |
4747
| 40 | Commercial evidence filesystem execution | [Phase 40 Commercial Evidence Filesystem Execution](PHASE40_COMMERCIAL_EVIDENCE_FILESYSTEM_EXECUTION.md), [Phase 40 Summary](PHASE40_SUMMARY.md) | Foundation complete; commercial publication requires real approved retained run |
48-
| 41 | Approved commercial run publication handoff | [Phase 41 Approved Commercial Run Publication Handoff](PHASE41_APPROVED_COMMERCIAL_RUN_PUBLICATION_HANDOFF.md), [Phase 41 Summary](PHASE41_SUMMARY.md) | In progress; reviewer approval manifest complete |
48+
| 41 | Approved commercial run publication handoff | [Phase 41 Approved Commercial Run Publication Handoff](PHASE41_APPROVED_COMMERCIAL_RUN_PUBLICATION_HANDOFF.md), [Phase 41 Summary](PHASE41_SUMMARY.md) | In progress; approval report writing complete |
4949

5050
## Current Commercial Gate
5151

docs/SDK_ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ Status: Phase 40 is foundation-complete. Filesystem observation reads retained f
363363
- Record approval checklist, reviewer approvals, retained reports, promotion package, publication handoff, blocker gates, and audit trail.
364364
- Keep approval handoff separate from commercial publication: the SDK can prepare approval records, but publication remains blocked until a real approved run is retained.
365365

366-
Status: Phase 41 is in progress. Approved run target identity now binds package version, source commit, operator identity, UTC run timing, retained artifact root, and filesystem-backed promotion execution. Approval checklist now requires verified filesystem promotion, ready report/ledger/seal/attachments, approved trace redaction, promotion approval, and reviewer approval records. Reviewer approval manifest now records release, security, and operations approvals with UTC timestamps and a deterministic checklist SHA-256 digest. Report writing, promotion package, publication handoff, blocker gate, audit trail, command materialization, status reporting, and final validation remain in progress.
366+
Status: Phase 41 is in progress. Approved run target identity now binds package version, source commit, operator identity, UTC run timing, retained artifact root, and filesystem-backed promotion execution. Approval checklist now requires verified filesystem promotion, ready report/ledger/seal/attachments, approved trace redaction, promotion approval, and reviewer approval records. Reviewer approval manifest now records release, security, and operations approvals with UTC timestamps and a deterministic checklist SHA-256 digest. Approval report writing now renders retained Markdown reports with run identity, checklist digest, reviewer roles, UTC write time, and report SHA-256 digest coverage. Promotion package, publication handoff, blocker gate, audit trail, command materialization, status reporting, and final validation remain in progress.
367367

368368
## Recommended First Deliverable
369369

src/Sigtran.NET.Tests/Program.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@
318318
Run("SIGTRAN commercial evidence approved run target binds filesystem promotion", SigtranCommercialEvidenceApprovedRunTargetBindsFileSystemPromotion);
319319
Run("SIGTRAN commercial evidence run approval checklist requires reviewer approval", SigtranCommercialEvidenceRunApprovalChecklistRequiresReviewerApproval);
320320
Run("SIGTRAN commercial evidence run approval manifest requires required roles", SigtranCommercialEvidenceRunApprovalManifestRequiresRequiredRoles);
321+
Run("SIGTRAN commercial evidence run approval report writer retains markdown", SigtranCommercialEvidenceRunApprovalReportWriterRetainsMarkdown);
321322
Run("SIGTRAN status capabilities use domain documentation labels", SigtranStatusCapabilitiesUseDomainDocumentationLabels);
322323
Run("Native SCTP platform probe reports socket creation capability", NativeSctpPlatformProbeReportsSocketCreationCapability);
323324
Run("Native SCTP socket factory creates or reports unsupported platform", NativeSctpSocketFactoryCreatesOrReportsUnsupportedPlatform);
@@ -5567,6 +5568,41 @@ static void SigtranCommercialEvidenceRunApprovalManifestRequiresRequiredRoles()
55675568
}
55685569
}
55695570

5571+
static void SigtranCommercialEvidenceRunApprovalReportWriterRetainsMarkdown()
5572+
{
5573+
string tempRoot = Path.Combine(Path.GetTempPath(), "sigtran-commercial-evidence-" + Guid.NewGuid().ToString("N"));
5574+
Directory.CreateDirectory(tempRoot);
5575+
5576+
try
5577+
{
5578+
SigtranCommercialEvidenceRunApprovalManifest manifest = CreateReadyCommercialEvidenceRunApprovalManifest(tempRoot);
5579+
5580+
SigtranCommercialEvidenceRunApprovalReportWriteResult result = SigtranCommercialEvidenceRunApprovalReportWriters.WriteReport(
5581+
manifest,
5582+
Path.Combine(tempRoot, "approval"),
5583+
DateTimeOffset.UtcNow);
5584+
string report = File.ReadAllText(result.ReportPath);
5585+
5586+
Assert(result.IsReady, result.Describe());
5587+
Assert(result.ReportExists, "approval report should exist");
5588+
Assert(result.ReportSizeBytes > 0, "approval report should be non-empty");
5589+
Assert(result.HasValidReportDigest, "approval report should have a valid digest");
5590+
Assert(report.Contains(manifest.Checklist.Target.RunId, StringComparison.Ordinal), "approval report should include run id");
5591+
Assert(report.Contains(manifest.ChecklistSha256, StringComparison.Ordinal), "approval report should include checklist digest");
5592+
}
5593+
finally
5594+
{
5595+
DeleteTempEvidenceRoot(tempRoot);
5596+
}
5597+
}
5598+
5599+
static SigtranCommercialEvidenceRunApprovalManifest CreateReadyCommercialEvidenceRunApprovalManifest(string tempRoot)
5600+
{
5601+
return SigtranCommercialEvidenceRunApprovalManifests.CreateDefault(
5602+
CreateReadyCommercialEvidenceRunApprovalChecklist(tempRoot),
5603+
DateTimeOffset.UtcNow);
5604+
}
5605+
55705606
static SigtranCommercialEvidenceRunApprovalChecklist CreateReadyCommercialEvidenceRunApprovalChecklist(string tempRoot)
55715607
{
55725608
return SigtranCommercialEvidenceRunApprovalChecklists.CreateDefault(CreateReadyCommercialEvidenceApprovedRunTarget(tempRoot));
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using System.Globalization;
2+
using System.Security.Cryptography;
3+
using System.Text;
4+
5+
namespace Sigtran.NET.Core.Utilities;
6+
7+
/// <summary>
8+
/// Describes a retained commercial evidence run approval report.
9+
/// </summary>
10+
public sealed class SigtranCommercialEvidenceRunApprovalReportWriteResult
11+
{
12+
/// <summary>Creates a retained commercial evidence run approval report result.</summary>
13+
/// <param name="manifest">The approval manifest rendered into the report.</param>
14+
/// <param name="reportPath">The retained report path.</param>
15+
/// <param name="reportSha256">The retained report SHA-256 digest.</param>
16+
/// <param name="writtenAtUtc">The UTC report write time.</param>
17+
public SigtranCommercialEvidenceRunApprovalReportWriteResult(
18+
SigtranCommercialEvidenceRunApprovalManifest manifest,
19+
string reportPath,
20+
string reportSha256,
21+
DateTimeOffset writtenAtUtc)
22+
{
23+
Manifest = manifest ?? throw new ArgumentNullException(nameof(manifest));
24+
ReportPath = string.IsNullOrWhiteSpace(reportPath) ? throw new ArgumentException("Report path is required.", nameof(reportPath)) : reportPath;
25+
ReportSha256 = string.IsNullOrWhiteSpace(reportSha256) ? throw new ArgumentException("Report SHA-256 digest is required.", nameof(reportSha256)) : reportSha256;
26+
WrittenAtUtc = writtenAtUtc.Offset == TimeSpan.Zero ? writtenAtUtc : writtenAtUtc.ToUniversalTime();
27+
}
28+
29+
/// <summary>The approval manifest rendered into the report.</summary>
30+
public SigtranCommercialEvidenceRunApprovalManifest Manifest { get; }
31+
32+
/// <summary>The retained report path.</summary>
33+
public string ReportPath { get; }
34+
35+
/// <summary>The retained report SHA-256 digest.</summary>
36+
public string ReportSha256 { get; }
37+
38+
/// <summary>The UTC report write time.</summary>
39+
public DateTimeOffset WrittenAtUtc { get; }
40+
41+
/// <summary>Whether the report exists on disk.</summary>
42+
public bool ReportExists => File.Exists(ReportPath);
43+
44+
/// <summary>The retained report size in bytes.</summary>
45+
public long ReportSizeBytes => ReportExists ? new FileInfo(ReportPath).Length : 0;
46+
47+
/// <summary>Whether the report digest is a valid SHA-256 hex value.</summary>
48+
public bool HasValidReportDigest => ReportSha256.Length == 64
49+
&& ReportSha256.All(Uri.IsHexDigit);
50+
51+
/// <summary>Whether the report write time is normalized to UTC.</summary>
52+
public bool HasUtcWriteTime => WrittenAtUtc.Offset == TimeSpan.Zero;
53+
54+
/// <summary>Whether the retained report is ready for promotion package creation.</summary>
55+
public bool IsReady => Manifest.IsReady
56+
&& ReportExists
57+
&& ReportSizeBytes > 0
58+
&& HasValidReportDigest
59+
&& HasUtcWriteTime;
60+
61+
/// <summary>Formats a compact approval report write summary.</summary>
62+
/// <returns>The approval report write summary.</returns>
63+
public string Describe()
64+
{
65+
return $"commercialEvidenceRunApprovalReportReady={IsReady} report={ReportPath}";
66+
}
67+
}
68+
69+
/// <summary>
70+
/// Provides retained commercial evidence run approval report helpers.
71+
/// </summary>
72+
public static class SigtranCommercialEvidenceRunApprovalReportWriters
73+
{
74+
/// <summary>Writes a retained Markdown approval report.</summary>
75+
/// <param name="manifest">The approval manifest to render.</param>
76+
/// <param name="outputDirectory">The output directory.</param>
77+
/// <param name="writtenAtUtc">The UTC report write time.</param>
78+
/// <returns>The retained approval report write result.</returns>
79+
public static SigtranCommercialEvidenceRunApprovalReportWriteResult WriteReport(
80+
SigtranCommercialEvidenceRunApprovalManifest manifest,
81+
string outputDirectory,
82+
DateTimeOffset writtenAtUtc)
83+
{
84+
ArgumentNullException.ThrowIfNull(manifest);
85+
string directory = string.IsNullOrWhiteSpace(outputDirectory) ? throw new ArgumentException("Output directory is required.", nameof(outputDirectory)) : outputDirectory;
86+
Directory.CreateDirectory(directory);
87+
88+
string reportPath = Path.Combine(directory, "commercial-evidence-run-approval-report.md");
89+
string report = RenderMarkdown(manifest, writtenAtUtc);
90+
File.WriteAllText(reportPath, report, Encoding.UTF8);
91+
string digest = ComputeSha256(File.ReadAllBytes(reportPath));
92+
93+
return new(manifest, reportPath, digest, writtenAtUtc);
94+
}
95+
96+
/// <summary>Renders a Markdown approval report.</summary>
97+
/// <param name="manifest">The approval manifest to render.</param>
98+
/// <param name="writtenAtUtc">The UTC report write time.</param>
99+
/// <returns>The rendered Markdown report.</returns>
100+
public static string RenderMarkdown(
101+
SigtranCommercialEvidenceRunApprovalManifest manifest,
102+
DateTimeOffset writtenAtUtc)
103+
{
104+
ArgumentNullException.ThrowIfNull(manifest);
105+
DateTimeOffset writtenAt = writtenAtUtc.Offset == TimeSpan.Zero ? writtenAtUtc : writtenAtUtc.ToUniversalTime();
106+
SigtranCommercialEvidenceApprovedRunTarget target = manifest.Checklist.Target;
107+
StringBuilder builder = new();
108+
109+
builder.AppendLine("# Commercial Evidence Run Approval Report");
110+
builder.AppendLine();
111+
builder.Append("- Run id: `").Append(target.RunId).AppendLine("`");
112+
builder.Append("- Package version: `").Append(target.PackageVersion).AppendLine("`");
113+
builder.Append("- Source commit: `").Append(target.SourceCommit).AppendLine("`");
114+
builder.Append("- Artifact root: `").Append(target.ArtifactRoot).AppendLine("`");
115+
builder.Append("- Checklist digest: `").Append(manifest.ChecklistSha256).AppendLine("`");
116+
builder.Append("- Manifest ready: `").Append(manifest.IsReady).AppendLine("`");
117+
builder.Append("- Written at UTC: `").Append(writtenAt.ToString("O", CultureInfo.InvariantCulture)).AppendLine("`");
118+
builder.AppendLine();
119+
builder.AppendLine("## Reviewer Approvals");
120+
builder.AppendLine();
121+
122+
foreach (SigtranCommercialEvidenceReviewerApproval approval in manifest.Approvals)
123+
{
124+
builder.Append("- `").Append(approval.Role).Append("`: `")
125+
.Append(approval.Approved)
126+
.Append("` by `")
127+
.Append(approval.ReviewerName)
128+
.AppendLine("`");
129+
}
130+
131+
return builder.ToString();
132+
}
133+
134+
private static string ComputeSha256(byte[] content)
135+
{
136+
return Convert.ToHexString(SHA256.HashData(content)).ToLowerInvariant();
137+
}
138+
}

0 commit comments

Comments
 (0)