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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,13 @@ If AIGuard preserves EdgeEnv/Orchestrator `candidate_context.producer` lineage,

If EdgeEnv preserves an Orchestrator `operation_risk_summary`, Lab shows the compact queue-pressure, max-pressure task, worker-health, and producer/device-local event markers as navigation context in the Runtime Intelligence Risk Summary. These markers help reviewers find the relevant operation evidence, but they do not become EdgeEnv regression deltas, comparability fields, or a deployment decision override.

When EdgeEnv/Orchestrator context includes reviewer-facing duration metadata,
Lab renders a `Runtime replay duration scope` row with `duration_label`,
`duration_class`, and frame count. This helps reviewers distinguish short
96-frame replay, 5-minute-class sustained replay, and quick starter smoke
without changing Lab deployment policy or treating replay duration as a
production readiness claim.

When the EdgeEnv preservation path is present, Lab also renders a `Lab EdgeEnv
preservation context` row with `lab_report_preservation_context_present=True`,
`lab_preservation=present`, and `lab_context=present`. This keeps Lab's
Expand Down
76 changes: 76 additions & 0 deletions inferedgelab/report/runtime_intelligence.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ def _append_telemetry_context_rows(
)
)

replay_scope_labels = _runtime_replay_scope_labels(telemetry_context)
if replay_scope_labels:
rows.append(
(
"Runtime replay duration scope",
"; ".join(replay_scope_labels),
"Duration metadata helps reviewers choose the right replay bundle; it is navigation context and does not change Lab deployment policy.",
)
)

coverage_labels = _runtime_telemetry_coverage_labels(telemetry_context)
if coverage_labels:
rows.append(
Expand Down Expand Up @@ -397,6 +407,72 @@ def _operation_risk_summary_parts(summary: dict[str, Any]) -> list[str]:
return parts


def _runtime_replay_scope_labels(context: dict[str, Any]) -> list[str]:
labels: list[str] = []
for run_label in ("baseline", "candidate"):
run_context = context.get(run_label)
if not isinstance(run_context, dict):
continue
label = _runtime_replay_scope_label(run_label, run_context)
if label:
labels.append(label)
return labels


def _runtime_replay_scope_label(run_label: str, run_context: dict[str, Any]) -> str:
operation_context = run_context.get("orchestrator_operation_context")
if not isinstance(operation_context, dict):
operation_context = {}
candidate_context = operation_context.get("candidate_context")
if not isinstance(candidate_context, dict):
candidate_context = {}
producer = candidate_context.get("producer")
if not isinstance(producer, dict):
producer = {}
operation_summary = operation_context.get("operation_risk_summary")
if not isinstance(operation_summary, dict):
operation_summary = {}

payloads = [
run_context,
operation_context,
candidate_context,
producer,
operation_summary,
]
duration_label = _first_payload_value(payloads, "duration_label")
duration_class = _first_payload_value(payloads, "duration_class")
frames = _first_payload_value(payloads, "frames")
if frames is None:
frames = _first_payload_value(payloads, "requested_frames")
if frames is None:
frames = _first_payload_value(payloads, "frame_count")

parts: list[str] = []
if duration_label is not None:
parts.append(f"label={duration_label}")
if duration_class is not None:
parts.append(f"class={duration_class}")
if frames is not None:
parts.append(f"frames={_format_compact_value(frames)}")
if not parts:
return ""
return f"{run_label}: " + ", ".join(parts)


def _first_payload_value(payloads: list[dict[str, Any]], field: str) -> Any:
for payload in payloads:
value = payload.get(field)
if value not in (None, ""):
return value
run_summary = payload.get("run_summary")
if isinstance(run_summary, dict):
value = run_summary.get(field)
if value not in (None, ""):
return value
return None


def _edgeenv_preservation_run_labels(context: dict[str, Any]) -> list[str]:
labels: list[str] = []
for run_label in ("baseline", "candidate"):
Expand Down
10 changes: 10 additions & 0 deletions tests/test_report_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@ def make_edgeenv_regression_with_orchestrator_context() -> dict:
context = regression["runtime_telemetry_context"]
context["history"]["summary"]["orchestrator_feed_runs"] = 1
context["candidate"]["orchestrator_context_present"] = True
context["candidate"]["frames"] = 96
context["candidate"]["duration_class"] = "short_96_frame_class"
context["candidate"]["duration_label"] = "short 96-frame-class replay (96 frames)"
context["candidate"]["orchestrator_operation_context"] = {
"schema_version": "inferedge-orchestrator-edgeenv-runtime-telemetry-feed-v1",
"role": "orchestrator_operation_context_for_edgeenv",
Expand Down Expand Up @@ -829,6 +832,11 @@ def test_generate_compare_markdown_summarizes_orchestrator_context_risk():
"health=worker_health_degraded, device_local_events=15, "
"producer_events=7, degraded_workers=vision_agent |"
) in text
assert (
"| Runtime replay duration scope | candidate: "
"label=short 96-frame-class replay (96 frames), "
"class=short_96_frame_class, frames=96 |"
) in text
assert (
"| Jetson/device-local EdgeEnv preservation run | candidate: "
"identity=jetson_device_local_preservation, run=candidate |"
Expand Down Expand Up @@ -894,6 +902,8 @@ def test_generate_compare_html_summarizes_operation_risk_summary():
)

assert "Runtime Intelligence Risk Summary" in html
assert "Runtime replay duration scope" in html
assert "short 96-frame-class replay (96 frames)" in html
assert "Orchestrator operation risk summary" in html
assert "Orchestrator task event rollup" in html
assert "vision_agent(delay=1,miss=1,max_delay_cycles=3,max_wait_ms=15)" in html
Expand Down
Loading