from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Any, Dict, Sequence
ROOT = Path(__file__).resolve().parents[1]
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Write the Iridium appliance release closeout artifacts"
)
parser.add_argument("--report-dir", default="artifacts")
parser.add_argument("--report-prefix", default="appliance_release_closeout")
parser.add_argument(
"--python-bin",
default=os.environ.get("PYTHON_BIN", sys.executable),
help="Python interpreter to use for Python-backed reporting stages",
)
parser.add_argument(
"--llvm-prefix",
default=os.environ.get(
"MLIR_SYS_220_PREFIX",
os.environ.get("MLIR_SYS_210_PREFIX", "/opt/homebrew/opt/llvm"),
),
help="LLVM/MLIR prefix used by Plexus-backed cargo commands",
)
parser.add_argument(
"--plexus-build-dir",
default=os.environ.get(
"PLEXUS_BUILD_DIR",
str((ROOT.parent / "plexus" / "build").resolve()),
),
help="Path to the built Plexus C++/MLIR artifacts",
)
parser.add_argument(
"--library-path",
default=os.environ.get(
"LIBRARY_PATH", "/opt/homebrew/lib:/opt/homebrew/opt/llvm/lib"
),
help="Library path used by Plexus-backed cargo link steps",
)
return parser.parse_args(argv)
def run_command(command: list[str], env: dict[str, str] | None = None) -> None:
subprocess.run(
command,
cwd=ROOT,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True,
env=env,
)
def load_json(path: Path) -> Dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))
def write_markdown(path: Path, payload: Dict[str, Any]) -> None:
lines = [
"# Appliance Release Closeout",
"",
f"- schema: `{payload['schema']}`",
f"- release_decision: `{payload['release_decision']['decision']}`",
f"- iridium_scope_complete: `{str(payload['release_decision']['iridium_scope_complete']).lower()}`",
f"- package_target: `{payload['release_packet']['package_target']}`",
"",
"## Release Gates",
]
for name, passed in payload["release_gates"].items():
lines.append(f"- `{name}`: `{str(passed).lower()}`")
lines.extend(
[
"",
"## External Inputs",
]
)
for item in payload["external_inputs"]:
lines.append(f"- `{item['producer']}`: {item['status']} - {item['surface']}")
lines.extend(
[
"",
"## Residual Risks",
]
)
for risk in payload["residual_risks"]:
lines.append(f"- `{risk['risk_id']}`: {risk['summary']}")
lines.extend(
[
"",
"## Deferrals",
]
)
if payload["deferrals"]:
for item in payload["deferrals"]:
lines.append(f"- `{item['deferral_id']}`: {item['summary']}")
else:
lines.append("- none")
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
def write_packet_markdown(path: Path, payload: Dict[str, Any]) -> None:
lines = [
"# Appliance Release Packet",
"",
f"- schema: `{payload['schema']}`",
f"- package_target: `{payload['package_target']}`",
f"- packet_ready: `{str(payload['packet_ready']).lower()}`",
"",
"## Attached Artifacts",
]
for artifact in payload["attached_artifacts"]:
lines.append(
f"- `{artifact['artifact_id']}`: path=`{artifact['path']}` role=`{artifact['role']}`"
)
lines.extend(
[
"",
"## Reproduction Commands",
]
)
for command in payload["reproduction_commands"]:
lines.append(f"- `{command}`")
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
def main(argv: Sequence[str]) -> int:
args = parse_args(argv)
report_dir = Path(args.report_dir)
report_dir.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env["PYTHON_BIN"] = args.python_bin
env["TABLEGEN_210_PREFIX"] = args.llvm_prefix
env["MLIR_SYS_210_PREFIX"] = args.llvm_prefix
env["LLVM_SYS_210_PREFIX"] = args.llvm_prefix
env["TABLEGEN_220_PREFIX"] = args.llvm_prefix
env["MLIR_SYS_220_PREFIX"] = args.llvm_prefix
env["LLVM_SYS_220_PREFIX"] = args.llvm_prefix
env["PLEXUS_BUILD_DIR"] = args.plexus_build_dir
env["LIBRARY_PATH"] = args.library_path
run_command(
[
args.python_bin,
str(ROOT / "scripts" / "appliance_lifecycle_report.py"),
"--python-bin",
args.python_bin,
"--llvm-prefix",
args.llvm_prefix,
"--plexus-build-dir",
args.plexus_build_dir,
"--library-path",
args.library_path,
"--report-dir",
str(report_dir),
],
env=env,
)
flagship = load_json(report_dir / "flagship_release_report.json")
appliance_profile = load_json(report_dir / "appliance_profile_report.json")
appliance_lifecycle = load_json(report_dir / "appliance_lifecycle_report.json")
release_gates = {
"flagship_release_ready": bool(flagship["release_gate"]["release_ready"]),
"appliance_profile_ready": bool(appliance_profile["appliance_gate"]["release_ready"]),
"appliance_lifecycle_ready": bool(
appliance_lifecycle["lifecycle_gate"]["release_ready"]
),
}
iridium_scope_complete = all(release_gates.values())
external_inputs = [
{
"producer": "alloy",
"surface": "final Phase 4 release metadata and support-boundary templates",
"status": "required-upstream",
},
{
"producer": "rhodium",
"surface": "final appliance acceleration evidence and operations posture",
"status": "required-for-program-release",
},
{
"producer": "strontium",
"surface": "final scenario pack and certification evidence workflows",
"status": "required-for-program-release",
},
{
"producer": "plexus",
"surface": "final certification reporting artifacts",
"status": "required-for-program-release",
},
{
"producer": "palladium",
"surface": "final support-band and control-plane inputs",
"status": "required-for-program-release",
},
]
payload = {
"schema": "iridium.appliance-release-closeout.v1",
"package_identity": appliance_lifecycle["package_identity"],
"release_decision": {
"decision": (
"iridium-scope-complete-awaiting-program-release"
if iridium_scope_complete
else "iridium-scope-incomplete"
),
"iridium_scope_complete": iridium_scope_complete,
"statement": (
"Iridium-owned appliance execution and lifecycle scope is complete; "
"program-wide Phase 4 release still depends on external project inputs."
if iridium_scope_complete
else "Iridium-owned appliance release scope is not yet complete."
),
},
"release_gates": release_gates,
"external_inputs": external_inputs,
"residual_risks": [
{
"risk_id": "program-release-external-dependencies",
"summary": "Program-wide Phase 4 release remains dependent on non-Iridium Sprint 9 deliverables.",
},
{
"risk_id": "shared-metadata-drift",
"summary": "Final Alloy metadata and support-boundary templates may still tighten naming or packet structure.",
},
],
"deferrals": [],
"release_packet": {
"package_target": "iridium-appliance-release-candidate",
"packet_ready": iridium_scope_complete,
},
"artifact_set": {
"flagship_release_report": "flagship_release_report.json",
"appliance_profile_report": "appliance_profile_report.json",
"appliance_profile_handoff": "appliance_profile_handoff.json",
"appliance_lifecycle_report": "appliance_lifecycle_report.json",
"appliance_lifecycle_handoff": "appliance_lifecycle_handoff.json",
"appliance_support_snapshot": "appliance_support_snapshot.json",
"appliance_diagnostics_manifest": "appliance_diagnostics_manifest.json",
},
}
packet = {
"schema": "iridium.appliance-release-packet.v1",
"package_target": "alloy-phase4-appliance-slot",
"packet_ready": iridium_scope_complete,
"attached_artifacts": [
{
"artifact_id": "iridium-flagship-release-report",
"path": "artifacts/flagship_release_report.json",
"role": "release-baseline",
},
{
"artifact_id": "iridium-appliance-profile-report",
"path": "artifacts/appliance_profile_report.json",
"role": "appliance-execution-evidence",
},
{
"artifact_id": "iridium-appliance-profile-handoff",
"path": "artifacts/appliance_profile_handoff.json",
"role": "appliance-packaging-input",
},
{
"artifact_id": "iridium-appliance-lifecycle-report",
"path": "artifacts/appliance_lifecycle_report.json",
"role": "lifecycle-evidence",
},
{
"artifact_id": "iridium-appliance-lifecycle-handoff",
"path": "artifacts/appliance_lifecycle_handoff.json",
"role": "support-boundary-input",
},
{
"artifact_id": "iridium-appliance-support-snapshot",
"path": "artifacts/appliance_support_snapshot.json",
"role": "support-snapshot",
},
{
"artifact_id": "iridium-appliance-diagnostics-manifest",
"path": "artifacts/appliance_diagnostics_manifest.json",
"role": "diagnostics-evidence",
},
],
"reproduction_commands": [
f"{args.python_bin} scripts/flagship_release_report.py --python-bin {args.python_bin} --report-dir artifacts",
f"{args.python_bin} scripts/appliance_profile_report.py --python-bin {args.python_bin} --report-dir artifacts",
f"{args.python_bin} scripts/appliance_lifecycle_report.py --python-bin {args.python_bin} --report-dir artifacts",
f"{args.python_bin} scripts/appliance_release_closeout.py --python-bin {args.python_bin} --report-dir artifacts",
],
}
json_path = report_dir / f"{args.report_prefix}_report.json"
md_path = report_dir / f"{args.report_prefix}_report.md"
packet_json = report_dir / f"{args.report_prefix}_packet.json"
packet_md = report_dir / f"{args.report_prefix}_packet.md"
json_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
write_markdown(md_path, payload)
packet_json.write_text(json.dumps(packet, indent=2), encoding="utf-8")
write_packet_markdown(packet_md, packet)
print(f"wrote: {json_path}")
print(f"wrote: {md_path}")
print(f"wrote: {packet_json}")
print(f"wrote: {packet_md}")
return 0 if iridium_scope_complete else 1
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))