Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/ci/runtime_intelligence_gitlab_artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ When that report gate passes, its summary now emits a
context for the Lab-owned report, not an AIGuard-owned marker decision.

The CI artifact gate is implemented by `scripts/check_runtime_intelligence_ci_artifacts.py`. It runs in the deployment-risk stage and verifies that the collected optional GitLab artifacts include the manifest gate summary, AIGuard handoff alignment artifact, report gate summary, Runtime Intelligence Risk Summary report, portfolio demo status, and the validated contract markers from the bundle manifest gate. This keeps the final CI gate file-based and deterministic without turning GitLab into a runtime control plane.
The final `runtime_intelligence_ci_artifact_gate_summary.md` also preserves the
report gate's `Validated Duration Traceability` section. It repeats
`duration_handoff_alignment`,
`duration_source: source=entrypoint_requested_frames`,
`duration_scope_label: scope_label=source=entrypoint_requested_frames`, and the
`short 96-frame-class replay (96 frames)` label so CI reviewers can confirm
duration handoff/source/scope alignment from the compact deployment-risk
summary before opening the full Lab report.
The same CI artifact gate also checks the copied
`aiguard_edgeenv_handoff_alignment.json/.md` for Lab report marker context:
`lab_expected_report_markers` must match the Lab-owned Runtime Intelligence
Expand Down
33 changes: 28 additions & 5 deletions scripts/check_runtime_intelligence_ci_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@
"edgeenv-smoke-candidate",
"edgeenv-smoke-missing",
]
REQUIRED_DURATION_TRACEABILITY_SUMMARY_MARKERS = (
"## Validated Duration Traceability",
"duration_handoff_alignment: EdgeEnv/AIGuard report context preserved",
"duration_source: source=entrypoint_requested_frames",
"duration_scope_label: scope_label=source=entrypoint_requested_frames",
"duration_label: short 96-frame-class replay (96 frames)",
)


def _record(condition: bool, errors: list[str], message: str) -> None:
Expand Down Expand Up @@ -125,10 +132,18 @@ def _validate_required_files(report_dir: Path, errors: list[str]) -> None:
_record((report_dir / name).is_file(), errors, f"missing artifact: {name}")


def _validate_gate_summary(path: Path, errors: list[str], label: str) -> None:
def _validate_runtime_artifact_gate_summary(path: Path, errors: list[str]) -> None:
label = "Runtime Intelligence artifact gate summary"
text = _read_text(path, errors, label)
if text:
_record("- Status: passed" in text, errors, f"{label} must have passed status")
if not text:
return
_record("- Status: passed" in text, errors, f"{label} must have passed status")
for marker in REQUIRED_DURATION_TRACEABILITY_SUMMARY_MARKERS:
_record(
marker in text,
errors,
f"{label} missing duration traceability marker: {marker}",
)


def _validate_bundle_manifest_gate_summary(path: Path, errors: list[str]) -> None:
Expand Down Expand Up @@ -331,6 +346,15 @@ def _write_summary(path: Path, report_dir: Path, errors: list[str]) -> None:
lines.append("")
lines.extend(f"- {error}" for error in errors)
lines.append("")
else:
lines.append("## Validated Duration Traceability")
lines.append("")
lines.extend(
f"- {marker}"
for marker in REQUIRED_DURATION_TRACEABILITY_SUMMARY_MARKERS
if not marker.startswith("## ")
)
lines.append("")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("\n".join(lines), encoding="utf-8")

Expand All @@ -345,10 +369,9 @@ def main(report_dir: str, summary_out: str = "") -> int:
report_path / "runtime_intelligence_bundle_manifest_gate_summary.md",
errors,
)
_validate_gate_summary(
_validate_runtime_artifact_gate_summary(
report_path / "runtime_anomaly_gate_summary.md",
errors,
"Runtime Intelligence artifact gate summary",
)
_validate_aiguard_handoff_alignment(
report_path / "aiguard_edgeenv_handoff_alignment.json",
Expand Down
44 changes: 43 additions & 1 deletion tests/test_runtime_intelligence_ci_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
REPO_ROOT = Path(__file__).resolve().parents[1]
TEMPLATE = REPO_ROOT / "ci" / "gitlab" / "runtime-intelligence-artifacts.yml"
DOC = REPO_ROOT / "docs" / "ci" / "runtime_intelligence_gitlab_artifacts.md"
DURATION_TRACEABILITY_SUMMARY = "\n".join(
[
"- Status: passed",
"## Validated Duration Traceability",
"- duration_handoff_alignment: EdgeEnv/AIGuard report context preserved",
"- duration_source: source=entrypoint_requested_frames",
"- duration_scope_label: scope_label=source=entrypoint_requested_frames",
"- duration_label: short 96-frame-class replay (96 frames)",
]
) + "\n"


def test_runtime_intelligence_gitlab_template_preserves_roadmap_stages():
Expand Down Expand Up @@ -71,6 +81,9 @@ def test_runtime_intelligence_gitlab_doc_states_ownership_boundaries():
assert "lab_expected_report_markers" in text
assert "lab_report_contract_context" in text
assert "aiguard_validates_expected_report_markers" in text
assert "Validated Duration Traceability" in text
assert "duration_handoff_alignment" in text
assert "runtime_intelligence_ci_artifact_gate_summary.md" in text


def test_runtime_intelligence_ci_artifact_gate_passes_for_expected_outputs(tmp_path):
Expand Down Expand Up @@ -173,7 +186,7 @@ def test_runtime_intelligence_ci_artifact_gate_passes_for_expected_outputs(tmp_p
encoding="utf-8",
)
(report_dir / "runtime_anomaly_gate_summary.md").write_text(
"- Status: passed\n",
DURATION_TRACEABILITY_SUMMARY,
encoding="utf-8",
)
(report_dir / "aiguard_edgeenv_handoff_alignment.json").write_text(
Expand Down Expand Up @@ -241,6 +254,35 @@ def test_runtime_intelligence_ci_artifact_gate_passes_for_expected_outputs(tmp_p
assert result == 0
summary = summary_path.read_text(encoding="utf-8")
assert "- Status: passed" in summary
assert "## Validated Duration Traceability" in summary
assert (
"duration_handoff_alignment: EdgeEnv/AIGuard report context preserved"
in summary
)
assert "duration_source: source=entrypoint_requested_frames" in summary
assert (
"duration_scope_label: scope_label=source=entrypoint_requested_frames"
in summary
)
assert "duration_label: short 96-frame-class replay (96 frames)" in summary

(report_dir / "runtime_anomaly_gate_summary.md").write_text(
"- Status: passed\n",
encoding="utf-8",
)
missing_duration_summary = tmp_path / "ci_artifact_gate_missing_duration.md"

result = ci_artifact_gate(
report_dir=str(report_dir),
summary_out=str(missing_duration_summary),
)

assert result == 2
missing_summary = missing_duration_summary.read_text(encoding="utf-8")
assert (
"Runtime Intelligence artifact gate summary missing duration "
"traceability marker: ## Validated Duration Traceability"
) in missing_summary


def test_runtime_intelligence_ci_artifact_gate_fails_for_missing_risk_summary(
Expand Down
11 changes: 11 additions & 0 deletions tests/test_runtime_intelligence_smoke_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,14 @@ def test_runtime_intelligence_smoke_script_runs_artifact_chain(tmp_path):
output_dir / "runtime_intelligence_ci_artifact_gate_summary.md"
).read_text(encoding="utf-8")
assert "- Status: passed" in ci_summary
assert "## Validated Duration Traceability" in ci_summary
assert (
"duration_handoff_alignment: EdgeEnv/AIGuard report context preserved"
in ci_summary
)
assert "duration_source: source=entrypoint_requested_frames" in ci_summary
assert (
"duration_scope_label: scope_label=source=entrypoint_requested_frames"
in ci_summary
)
assert "duration_label: short 96-frame-class replay (96 frames)" in ci_summary
Loading