///|
pub(all) enum ReleaseGate {
ProductStatusReady
RehearsalReady
SuitePackComplete
HistoricalRecordsAvailable
LegacyAppArchived
ExportEvidenceReady
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) enum ReleaseStatus {
ReleasePass
ReleaseBlocked
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct ReleaseFinding {
gate : ReleaseGate
status : ReleaseStatus
evidence_path : String
message : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct ReleaseCertificateInput {
product_status : @product.MoonfishProductStatus
rehearsal_report : @product.ProductRehearsalReport
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct ReleaseCertificate {
ready : Bool
run_id : String
release_path : String
pass_count : Int
blocked_count : Int
findings : Array[ReleaseFinding]
summary : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn release_certificate_input(
product_status : @product.MoonfishProductStatus,
rehearsal_report : @product.ProductRehearsalReport,
) -> ReleaseCertificateInput {
{ product_status, rehearsal_report }
}
///|
pub fn release_finding(
gate : ReleaseGate,
status : ReleaseStatus,
evidence_path : String,
message : String,
) -> ReleaseFinding {
{ gate, status, evidence_path, message }
}
///|
fn tool_declared(
status : @product.MoonfishProductStatus,
name : String,
) -> Bool {
status.pack_manifest.tools.any(fn(tool) { tool.name == name })
}
///|
fn schema_declared(
status : @product.MoonfishProductStatus,
id : String,
) -> Bool {
status.pack_manifest.schemas.any(fn(schema) { schema.id == id })
}
///|
fn rehearsal_pass(
report : @product.ProductRehearsalReport,
check : @product.ProductRehearsalCheck,
) -> Bool {
report.findings.any(fn(finding) {
finding.check == check && finding.status is RehearsalPass
})
}
///|
fn decommission_pass(
status : @product.MoonfishProductStatus,
gate : @decommission.DecommissionGate,
) -> Bool {
status.decommission_report.findings.any(fn(finding) {
finding.gate == gate && finding.status is DecommissionPass
})
}
///|
fn product_status_finding(
status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
if status.acceptance_report.passed &&
status.readiness_report.ready &&
status.cutover_plan.ready_to_cutover &&
status.handoff_report.ready &&
status.decommission_report.ready {
release_finding(
ProductStatusReady,
ReleasePass,
"records/product-status/\{status.workspace.bundle.request.run_id}.json",
"product status proves acceptance, readiness, cutover, handoff, and decommission gates",
)
} else {
release_finding(
ProductStatusReady,
ReleaseBlocked,
"product-status",
"product status still has blocked launch gates",
)
}
}
///|
fn rehearsal_finding(
report : @product.ProductRehearsalReport,
) -> ReleaseFinding {
if report.ready &&
rehearsal_pass(report, DailyWorkflowExecutable) &&
rehearsal_pass(report, LegacyReadOnlyBoundary) &&
rehearsal_pass(report, ExportReplayEvidence) {
release_finding(
RehearsalReady,
ReleasePass,
"records/rehearsals/\{report.run_id}.json",
"daily workflow, export/replay, and legacy read-only rehearsal checks pass",
)
} else {
release_finding(
RehearsalReady,
ReleaseBlocked,
"product-rehearsal-report",
"product rehearsal is incomplete",
)
}
}
///|
fn suite_pack_finding(
status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
if status.pack_manifest.id == "price-action" &&
schema_declared(status, "release-certificate-input") &&
schema_declared(status, "release-certificate") &&
tool_declared(status, "prepare_release_certificate") &&
tool_declared(status, "prepare_product_rehearsal_report") &&
tool_declared(status, "prepare_moonfish_product_status") {
release_finding(
SuitePackComplete,
ReleasePass,
"pack-manifest",
"suite pack declares product status, rehearsal, and release certificate contracts",
)
} else {
release_finding(
SuitePackComplete,
ReleaseBlocked,
"pack-manifest",
"suite pack is missing release-time schemas or tools",
)
}
}
///|
fn historical_records_finding(
status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
if status.import_projection.record_count > 0 &&
status.import_projection.review_count == 0 &&
status.import_projection.records.all(fn(record) {
record.replay_anchor_path != ""
}) &&
status.workspace.history.total_count > 0 &&
status.workspace.replay_plan.can_replay {
release_finding(
HistoricalRecordsAvailable,
ReleasePass,
"records/imports/\{status.import_projection.batch_id}.json",
"imported records and current workspace history are inspectable and replayable",
)
} else {
release_finding(
HistoricalRecordsAvailable,
ReleaseBlocked,
"legacy-import-projection",
"historical records or replay anchors are missing",
)
}
}
///|
fn legacy_archive_finding(
status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
if status.decommission_report.legacy_app_ref == "../paa" &&
decommission_pass(status, LegacyReadOnly) &&
decommission_pass(status, ArchiveManifestReady) &&
status.decommission_report.archive_items.any(fn(item) {
item.kind is LegacySourceReference && item.retained
}) {
release_finding(
LegacyAppArchived,
ReleasePass,
"../paa",
"PA Agent is archived as a read-only rollback and audit reference, not an active runtime dependency",
)
} else {
release_finding(
LegacyAppArchived,
ReleaseBlocked,
"../paa",
"legacy PA Agent archive or read-only boundary is incomplete",
)
}
}
///|
fn export_evidence_finding(
status : @product.MoonfishProductStatus,
report : @product.ProductRehearsalReport,
) -> ReleaseFinding {
if status.workspace.export_manifest.item_count > 0 &&
status.workspace.export_manifest.destination_path != "" &&
!status.workspace.export_manifest.items.any(fn(item) {
item.required && item.path == ""
}) &&
rehearsal_pass(report, ExportReplayEvidence) {
release_finding(
ExportEvidenceReady,
ReleasePass,
status.workspace.export_manifest.destination_path,
"launch evidence can be exported with required replay and operator artifacts",
)
} else {
release_finding(
ExportEvidenceReady,
ReleaseBlocked,
"export-manifest",
"release export evidence is incomplete",
)
}
}
///|
fn count_status(
findings : Array[ReleaseFinding],
status : ReleaseStatus,
) -> Int {
findings.fold(init=0, fn(count, finding) {
if finding.status == status {
count + 1
} else {
count
}
})
}
///|
pub fn prepare_release_certificate(
input : ReleaseCertificateInput,
) -> ReleaseCertificate {
let status = input.product_status
let rehearsal = input.rehearsal_report
let findings = [
product_status_finding(status),
rehearsal_finding(rehearsal),
suite_pack_finding(status),
historical_records_finding(status),
legacy_archive_finding(status),
export_evidence_finding(status, rehearsal),
]
let blocked_count = count_status(findings, ReleaseBlocked)
let pass_count = count_status(findings, ReleasePass)
let ready = blocked_count == 0
let run_id = status.workspace.bundle.request.run_id
{
ready,
run_id,
release_path: "records/releases/\{run_id}.json",
pass_count,
blocked_count,
findings,
summary: if ready {
"release certificate ready with \{pass_count}/\{findings.length()} gate(s); Moonfish is the standalone primary workflow"
} else {
"release certificate blocked by \{blocked_count} gate(s)"
},
}
}