Close decision assistant H18 F19 G18 D20

This commit is contained in:
Codex
2026-05-13 11:22:59 +08:00
parent 849fc8c65c
commit 3c5ccbdf3e
5 changed files with 670 additions and 13 deletions
+18 -3
View File
@@ -2,7 +2,7 @@
## Current Goal
Automation execution-layer PXX remains closed and its known scope stays frozen. Under explicit user direction, the active product phase is a dense `Decision Assistant` operator workspace with grouped recall, broader replay baselines, note templates, quick-save shortcuts, and concrete OKX announcements/system-status read-only sources. This run closed H17/F18/G17/D19 by adding handoff resume history / stale-persistence cleanup, digest proof-resolution SLA / repeated-stale suppression, template recovery recommendations / audit filtering, and setup-family review-lane SLA / promotion backlog prioritization. Local validation passed, the current working tree was deployed, and remote smoke confirmed the latest Decision Assistant fields on the live host.
Automation execution-layer PXX remains closed and its known scope stays frozen. Under explicit user direction, the active product phase is a dense `Decision Assistant` operator workspace with grouped recall, broader replay baselines, note templates, quick-save shortcuts, and concrete OKX announcements/system-status read-only sources. This run closed H18/F19/G18/D20 by adding handoff cleanup-action readiness / resume backlog grouping, digest proof-SLA backlog routing / freshness-debt summary, template recovery shortcuts / audit lane grouping, and setup-family promotion evidence queue / backlog trend handoff. Local validation passed, the current working tree was deployed, and remote smoke confirmed the latest Decision Assistant fields on the live host.
## Current Branch
@@ -96,6 +96,10 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
- latest_decision_assistant_digest_proof_sla_slice: proof_resolution_sla_repeated_stale_suppression
- latest_decision_assistant_template_recovery_recommendation_slice: recovery_recommendations_audit_filtering
- latest_decision_assistant_replay_backlog_slice: review_lane_sla_promotion_backlog_priority
- latest_decision_assistant_handoff_backlog_slice: cleanup_action_readiness_resume_backlog_grouping
- latest_decision_assistant_digest_backlog_slice: proof_sla_backlog_freshness_debt_summary
- latest_decision_assistant_template_audit_lane_slice: recovery_shortcuts_audit_lane_grouping
- latest_decision_assistant_replay_evidence_backlog_slice: promotion_evidence_queue_backlog_trend_handoff
- latest_decision_assistant_summary_status: implemented
- latest_decision_assistant_change_summary_status: implemented
- latest_decision_assistant_evidence_status: implemented
@@ -176,10 +180,14 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
- latest_decision_assistant_digest_proof_sla_status: implemented
- latest_decision_assistant_template_recovery_recommendation_status: implemented
- latest_decision_assistant_replay_backlog_status: implemented
- latest_decision_assistant_handoff_backlog_status: implemented
- latest_decision_assistant_digest_backlog_status: implemented
- latest_decision_assistant_template_audit_lane_status: implemented
- latest_decision_assistant_replay_evidence_backlog_status: implemented
- latest_decision_assistant_workbench_status: visible_local_smoke
- latest_remote_decision_assistant_deploy_status: deployed_current_head
- latest_remote_decision_assistant_smoke_status: passed
- latest_remote_decision_assistant_review_packet_status: resume_history_proof_sla_template_filter_review_backlog_visible
- latest_remote_decision_assistant_review_packet_status: cleanup_backlog_proof_debt_template_lanes_promotion_evidence_visible
- latest_p7_selector_choice: continue_periodic_analysis_decision_monitoring_with_maturity_rollup
- latest_p14_slice: webhook_transport_retry
- latest_p15_pxx_tasklist_status: ready
@@ -258,6 +266,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
## Completed In This Run
- Added handoff cleanup-action readiness / resume backlog grouping fields (`cleanup_action_readiness`, `cleanup_action_ready_count`, `resume_backlog_group`, `resume_backlog_groups`, and `next_cleanup_action_prompt`) plus cockpit visibility.
- Added digest proof-SLA backlog routing / freshness-debt summary fields (`proof_sla_backlog_route`, `proof_sla_backlog_priority`, `freshness_debt_score`, `proof_sla_backlog_queue`, and `freshness_debt_summary`) plus cockpit visibility.
- Added template recovery shortcuts / audit lane grouping fields (`recovery_action_shortcuts`, `audit_lane_group`, `audit_lane_rank`, `template_audit_lane_groups`, and `next_recovery_action_shortcut`) plus cockpit visibility.
- Added setup-family promotion evidence queue / backlog trend handoff fields (`promotion_evidence_queue`, `promotion_evidence_queue_count`, `promotion_backlog_trend_status`, and `promotion_backlog_trend_handoff`) plus cockpit visibility.
- Passed focused validation: `unittest tests.unit.test_operator_workspace_service`, `node --check src/web/app.js`, `unittest tests.unit.test_operator_workspace_service tests.unit.test_api_static_server tests.unit.test_api_server`, full `unittest discover -s tests/unit` (713 tests), and `scripts/run_script_static_checks.py`.
- Deployed the current working tree to `http://192.168.3.90:8000`; deploy smoke passed 11 endpoints, `run_remote_ops_smoke.py` passed 9 checks, and remote field smoke confirmed cleanup backlog, proof debt, template audit lanes, and promotion evidence fields.
- Added handoff resume-history / stale-persistence cleanup fields (`resume_history`, `resume_history_count`, `stale_persistence_cleanup_queue`, `stale_persistence_cleanup_count`, and `next_stale_persistence_cleanup_prompt`) plus cockpit visibility.
- Added digest proof-resolution SLA / repeated-stale suppression fields (`proof_resolution_sla_status`, `proof_resolution_sla_seconds`, `proof_resolution_sla_queue`, `proof_resolution_sla_breach_count`, and `repeated_stale_proof_suppression_count`).
- Added template recovery recommendation / audit-filter fields (`preference_recovery_recommendation`, `preference_recovery_recommendations`, `guardrail_audit_filter`, `guardrail_audit_filter_counts`, and `guardrail_audit_filtered_trail`).
@@ -560,12 +574,13 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
- P24 remote ops smoke passed on `http://192.168.3.90:8000` with 9 passed checks, warnings=[], blockers=[].
- PXX final validation passed: 709 unit tests, 73 script static checks, `node --check src/web/app.js`, P24 remote ops smoke passed, and closure gate passed.
- Latest H16/F17/G16/D18 validation passed: 713 unit tests, 73 script static checks, `node --check src/web/app.js`, deploy smoke passed 11 endpoints, remote ops smoke passed 9 checks, and remote field smoke returned `status=passed`.
- Latest H18/F19/G18/D20 validation passed: 713 unit tests, 73 script static checks, `node --check src/web/app.js`, deploy smoke passed 11 endpoints, remote ops smoke passed 9 checks, and remote field smoke returned `status=passed`.
- Latest H17/F18/G17/D19 validation passed: 713 unit tests, 73 script static checks, `node --check src/web/app.js`, deploy smoke passed 11 endpoints, remote ops smoke passed 9 checks, and remote field smoke returned `status=passed`.
- Real-live order submission remains intentionally disabled because the OKX order adapter is still not a real-live submitter.
## Exact Next Step
Continue the Decision Assistant dense operator workspace with the next bounded slice: H18 handoff cleanup action readiness / resume backlog grouping, F19 digest proof SLA backlog routing / freshness-debt summary, G18 template recovery action shortcuts / audit lane grouping, and D20 setup-family promotion evidence queue / backlog trend handoff. PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
Continue the Decision Assistant dense operator workspace with the next bounded slice: H19 handoff cleanup execution log / grouped resume handoff summary, F20 digest freshness-debt trend / SLA recovery recommendations, G19 template audit-lane recommendations / shortcut outcome telemetry, and D21 setup-family promotion evidence aging / backlog comparison summary. PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
## Validation Commands Used Recently
+26 -5
View File
@@ -96,6 +96,10 @@
- [x] Decision Assistant F18 digest proof resolution SLA / repeated-stale proof suppression slice complete
- [x] Decision Assistant G17 template recovery recommendations / audit filtering slice complete
- [x] Decision Assistant D19 setup-family review-lane SLA / promotion backlog prioritization slice complete
- [x] Decision Assistant H18 handoff cleanup action readiness / resume backlog grouping slice complete
- [x] Decision Assistant F19 digest proof SLA backlog routing / freshness-debt summary slice complete
- [x] Decision Assistant G18 template recovery action shortcuts / audit lane grouping slice complete
- [x] Decision Assistant D20 setup-family promotion evidence queue / backlog trend handoff slice complete
## Next Up
@@ -123,10 +127,14 @@
- [x] F18 deepen digest recall with proof resolution SLA / repeated-stale proof suppression
- [x] G17 continue review template ergonomics with template recovery recommendations / audit filtering
- [x] D19 extend replay comparison with setup-family review-lane SLA / promotion backlog prioritization
- [ ] H18 continue compact operator workspace polish with handoff cleanup action readiness / resume backlog grouping
- [ ] F19 deepen digest recall with proof SLA backlog routing / freshness-debt summary
- [ ] G18 continue review template ergonomics with template recovery action shortcuts / audit lane grouping
- [ ] D20 extend replay comparison with setup-family promotion evidence queue / backlog trend handoff
- [x] H18 continue compact operator workspace polish with handoff cleanup action readiness / resume backlog grouping
- [x] F19 deepen digest recall with proof SLA backlog routing / freshness-debt summary
- [x] G18 continue review template ergonomics with template recovery action shortcuts / audit lane grouping
- [x] D20 extend replay comparison with setup-family promotion evidence queue / backlog trend handoff
- [ ] H19 continue compact operator workspace polish with handoff cleanup execution log / grouped resume handoff summary
- [ ] F20 deepen digest recall with freshness-debt trend / SLA recovery recommendations
- [ ] G19 continue review template ergonomics with template audit-lane recommendations / shortcut outcome telemetry
- [ ] D21 extend replay comparison with setup-family promotion evidence aging / backlog comparison summary
- [x] P8 local closure complete
- [x] P9 local closure complete
- [x] P10 local closure complete
@@ -230,6 +238,10 @@
- [x] latest_decision_assistant_digest_proof_sla_slice: proof_resolution_sla_repeated_stale_suppression
- [x] latest_decision_assistant_template_recovery_recommendation_slice: recovery_recommendations_audit_filtering
- [x] latest_decision_assistant_replay_backlog_slice: review_lane_sla_promotion_backlog_priority
- [x] latest_decision_assistant_handoff_backlog_slice: cleanup_action_readiness_resume_backlog_grouping
- [x] latest_decision_assistant_digest_backlog_slice: proof_sla_backlog_freshness_debt_summary
- [x] latest_decision_assistant_template_audit_lane_slice: recovery_shortcuts_audit_lane_grouping
- [x] latest_decision_assistant_replay_evidence_backlog_slice: promotion_evidence_queue_backlog_trend_handoff
- [x] latest_decision_assistant_summary_status: implemented
- [x] latest_decision_assistant_change_summary_status: implemented
- [x] latest_decision_assistant_evidence_status: implemented
@@ -307,10 +319,14 @@
- [x] latest_decision_assistant_digest_proof_sla_status: implemented
- [x] latest_decision_assistant_template_recovery_recommendation_status: implemented
- [x] latest_decision_assistant_replay_backlog_status: implemented
- [x] latest_decision_assistant_handoff_backlog_status: implemented
- [x] latest_decision_assistant_digest_backlog_status: implemented
- [x] latest_decision_assistant_template_audit_lane_status: implemented
- [x] latest_decision_assistant_replay_evidence_backlog_status: implemented
- [x] latest_decision_assistant_workbench_status: visible_local_smoke
- [x] latest_remote_decision_assistant_deploy_status: deployed_current_head
- [x] latest_remote_decision_assistant_smoke_status: passed
- [x] latest_remote_decision_assistant_review_packet_status: resume_history_proof_sla_template_filter_review_backlog_visible
- [x] latest_remote_decision_assistant_review_packet_status: cleanup_backlog_proof_debt_template_lanes_promotion_evidence_visible
- [x] latest_p7_selector_choice: continue_periodic_analysis_decision_monitoring_with_maturity_rollup
- [x] latest_p14_slice: webhook_transport_retry
- [x] latest_p15_pxx_tasklist_status: ready
@@ -388,6 +404,11 @@
## Newly Done
- [x] Added handoff cleanup-action readiness / resume backlog grouping fields and cockpit visibility
- [x] Added digest proof-SLA backlog routing / freshness-debt summary fields and cockpit visibility
- [x] Added template recovery action shortcuts / audit lane grouping fields and cockpit visibility
- [x] Added setup-family promotion evidence queue / backlog trend handoff fields and cockpit visibility
- [x] Passed focused validation, full unit discovery, script static checks, JS syntax check, deploy smoke, remote ops smoke, and remote field smoke for H18/F19/G18/D20
- [x] Added handoff lifecycle / operator resume queue fields and cockpit visibility
- [x] Added digest proof follow-up queue, resolved proof state counts, and group follow-up actions
- [x] Added template guardrail resolution and preferred-template decay fields
@@ -1095,6 +1095,9 @@ def _build_decision_assistant_review_entrypoints(
item["guardrail_audit_trail"] = outcome.get("guardrail_audit_trail") or []
item["guardrail_audit_trail_count"] = outcome.get("guardrail_audit_trail_count") or 0
item["preference_recovery_recommendation"] = outcome.get("preference_recovery_recommendation")
item["recovery_action_shortcuts"] = outcome.get("recovery_action_shortcuts") or []
item["audit_lane_group"] = outcome.get("audit_lane_group")
item["audit_lane_rank"] = outcome.get("audit_lane_rank")
item["guardrail_audit_filter"] = outcome.get("guardrail_audit_filter")
item["guardrail_audit_filter_reason"] = outcome.get("guardrail_audit_filter_reason")
item["guardrail_audit_filtered_trail"] = outcome.get("guardrail_audit_filtered_trail") or []
@@ -1429,6 +1432,18 @@ def _score_template_outcome(*, template_id: str, usage: dict, notes: list[dict])
guardrail_audit_trail=guardrail_audit_trail,
guardrail_audit_filter=guardrail_audit_filter,
)
preference_recovery_recommendation = _template_preference_recovery_recommendation(
template_id=template_id,
preference_recovery_status=preference_recovery_status,
negative_count=negative_count,
positive_count=positive_count,
actionable_count=action_count,
)
audit_lane_group = _template_audit_lane_group(
guardrail_audit_filter=guardrail_audit_filter,
preference_recovery_status=preference_recovery_status,
outcome_status=outcome_status,
)
return {
"template_id": template_id,
"reuse_count": reuse_count,
@@ -1470,13 +1485,14 @@ def _score_template_outcome(*, template_id: str, usage: dict, notes: list[dict])
),
"guardrail_audit_trail": guardrail_audit_trail[:5],
"guardrail_audit_trail_count": len(guardrail_audit_trail),
"preference_recovery_recommendation": _template_preference_recovery_recommendation(
"preference_recovery_recommendation": preference_recovery_recommendation,
"recovery_action_shortcuts": _template_recovery_action_shortcuts(
template_id=template_id,
preference_recovery_status=preference_recovery_status,
negative_count=negative_count,
positive_count=positive_count,
actionable_count=action_count,
recommendation=preference_recovery_recommendation,
audit_lane_group=audit_lane_group,
),
"audit_lane_group": audit_lane_group,
"audit_lane_rank": _template_audit_lane_rank(audit_lane_group),
"guardrail_audit_filter": guardrail_audit_filter,
"guardrail_audit_filter_reason": _template_guardrail_audit_filter_reason(guardrail_audit_filter),
"guardrail_audit_filtered_trail": guardrail_audit_filtered_trail[:5],
@@ -1650,6 +1666,77 @@ def _template_guardrail_audit_filtered_trail(
return guardrail_audit_trail[:2]
def _template_audit_lane_group(
*,
guardrail_audit_filter: str,
preference_recovery_status: str,
outcome_status: str,
) -> str:
if guardrail_audit_filter == "negative_guardrail_only" or preference_recovery_status == "recovery_required":
return "guardrail"
if preference_recovery_status == "recovery_in_progress":
return "recovery"
if outcome_status in {"needs_outcome_feedback", "unscored"} or preference_recovery_status == "awaiting_feedback":
return "capture"
return "stable"
def _template_audit_lane_rank(audit_lane_group: str) -> int:
return {
"guardrail": 1,
"recovery": 2,
"capture": 3,
"stable": 4,
}.get(str(audit_lane_group or "stable"), 4)
def _template_recovery_action_shortcuts(*, template_id: str, recommendation: dict, audit_lane_group: str) -> list[dict]:
action = str(recommendation.get("action") or "capture_first_outcome")
prompt = str(recommendation.get("prompt") or "")
priority = str(recommendation.get("priority") or "low")
template_label = template_id.replace("_", " ") or "template"
shortcuts = [
{
"action_id": f"{template_id}:{action}",
"label": _template_recovery_shortcut_label(action),
"priority": priority,
"audit_lane_group": audit_lane_group,
"prompt": prompt or f"{template_label}: capture recovery evidence.",
}
]
if audit_lane_group in {"guardrail", "recovery"}:
shortcuts.append(
{
"action_id": f"{template_id}:open_audit_lane",
"label": "Open Audit Lane",
"priority": "medium" if audit_lane_group == "recovery" else "high",
"audit_lane_group": audit_lane_group,
"prompt": f"Review {template_label} audit evidence before restoring preference.",
}
)
if audit_lane_group == "capture":
shortcuts.append(
{
"action_id": f"{template_id}:capture_outcome",
"label": "Capture Outcome",
"priority": "low",
"audit_lane_group": audit_lane_group,
"prompt": f"Save one outcome note for {template_label} before ranking it again.",
}
)
return shortcuts[:3]
def _template_recovery_shortcut_label(action: str) -> str:
if action == "collect_recovery_note":
return "Collect Recovery"
if action == "review_recovery_evidence":
return "Review Recovery"
if action == "capture_first_outcome":
return "Capture First Outcome"
return "Keep Stable"
def _build_template_feedback_loop(*, template_id: str, outcome_status: str, reuse_count: int) -> dict:
template_label = template_id.replace("_", " ") or "template"
if outcome_status == "review_before_reuse":
@@ -1757,10 +1844,23 @@ def _build_template_feedback_aggregation(
for item in template_outcomes.values()
if dict(item.get("preference_recovery_recommendation") or {}).get("action") != "keep_preferred_candidate"
]
recovery_action_shortcuts = [
dict(shortcut)
for item in template_outcomes.values()
for shortcut in list(item.get("recovery_action_shortcuts") or [])
]
recovery_action_shortcuts.sort(
key=lambda row: (
_template_audit_lane_rank(str(row.get("audit_lane_group") or "stable")),
{"high": 1, "medium": 2, "low": 3}.get(str(row.get("priority") or "low"), 3),
str(row.get("label") or ""),
)
)
guardrail_audit_filter_counts = {}
for item in template_outcomes.values():
audit_filter = str(item.get("guardrail_audit_filter") or "unknown")
guardrail_audit_filter_counts[audit_filter] = int(guardrail_audit_filter_counts.get(audit_filter) or 0) + 1
template_audit_lane_groups = _build_template_audit_lane_groups(template_outcomes)
rated_template_count = sum(
1
for item in template_outcomes.values()
@@ -1835,9 +1935,47 @@ def _build_template_feedback_aggregation(
"guardrail_audit_filter_counts": guardrail_audit_filter_counts,
"guardrail_audit_filtered_trail": guardrail_audit_filtered_trail[:5],
"guardrail_audit_filtered_count": len(guardrail_audit_filtered_trail),
"template_audit_lane_groups": template_audit_lane_groups,
"recovery_action_shortcuts": recovery_action_shortcuts[:6],
"recovery_action_shortcut_count": len(recovery_action_shortcuts),
"next_recovery_action_shortcut": recovery_action_shortcuts[0] if recovery_action_shortcuts else None,
}
def _build_template_audit_lane_groups(template_outcomes: dict[str, dict]) -> list[dict]:
lanes: dict[str, list[dict]] = {}
for item in template_outcomes.values():
lane = str(item.get("audit_lane_group") or "stable")
lanes.setdefault(lane, []).append(dict(item))
groups = []
for lane in ("guardrail", "recovery", "capture", "stable"):
items = lanes.get(lane) or []
if not items:
continue
ranked = sorted(items, key=lambda row: (int(row.get("audit_lane_rank") or 9), str(row.get("template_id") or "")))
groups.append(
{
"audit_lane_group": lane,
"label": _template_audit_lane_label(lane),
"count": len(items),
"template_ids": [str(item.get("template_id") or "") for item in ranked[:5]],
"next_template_id": str((ranked[0] or {}).get("template_id") or ""),
"next_prompt": (ranked[0] or {}).get("preference_recovery_prompt"),
}
)
return groups
def _template_audit_lane_label(lane: str) -> str:
if lane == "guardrail":
return "Guardrail"
if lane == "recovery":
return "Recovery"
if lane == "capture":
return "Capture"
return "Stable"
def _pick_best_template_outcome(template_outcomes: dict[str, dict]) -> str | None:
if not template_outcomes:
return None
@@ -2115,10 +2253,38 @@ def _build_decision_assistant_digest_history_summary(history: list[dict]) -> dic
"prompt": group.get("proof_resolution_sla_prompt"),
"age_seconds": group.get("proof_follow_up_age_seconds"),
"suppression": group.get("repeated_stale_proof_suppression"),
"backlog_route": group.get("proof_sla_backlog_route"),
"backlog_priority": group.get("proof_sla_backlog_priority"),
"freshness_debt_score": group.get("freshness_debt_score"),
}
for group in groups
if group.get("proof_resolution_sla_status") in {"breached", "due_soon", "missing_timestamp_sla"}
]
proof_sla_backlog_routes = {}
for group in groups:
route = str(group.get("proof_sla_backlog_route") or "unknown")
proof_sla_backlog_routes[route] = int(proof_sla_backlog_routes.get(route) or 0) + 1
proof_sla_backlog_queue = [
{
"kind": str(group.get("kind") or "history"),
"route": group.get("proof_sla_backlog_route"),
"priority": group.get("proof_sla_backlog_priority"),
"freshness_debt_score": group.get("freshness_debt_score"),
"sla_status": group.get("proof_resolution_sla_status"),
"prompt": group.get("proof_resolution_sla_prompt"),
"evidence_anchor": group.get("refresh_evidence_anchor"),
}
for group in groups
if group.get("proof_sla_backlog_route") not in {"resolved"}
]
proof_sla_backlog_queue.sort(
key=lambda row: (
{"high": 1, "medium": 2, "low": 3, "none": 4}.get(str(row.get("priority") or "none"), 4),
-int(row.get("freshness_debt_score") or 0),
str(row.get("kind") or ""),
)
)
freshness_debt_summary = _build_digest_history_freshness_debt_summary(groups)
repeated_stale_suppression_count = sum(
1
for group in groups
@@ -2195,6 +2361,11 @@ def _build_decision_assistant_digest_history_summary(history: list[dict]) -> dic
"repeated_stale_proof_suppression_count": repeated_stale_suppression_count,
"next_proof_resolution_sla_kind": proof_resolution_sla_queue[0].get("kind") if proof_resolution_sla_queue else None,
"next_proof_resolution_sla_prompt": proof_resolution_sla_queue[0].get("prompt") if proof_resolution_sla_queue else None,
"proof_sla_backlog_routes": proof_sla_backlog_routes,
"proof_sla_backlog_queue": proof_sla_backlog_queue[:5],
"proof_sla_backlog_count": len(proof_sla_backlog_queue),
"freshness_debt_summary": freshness_debt_summary,
"next_freshness_debt_prompt": freshness_debt_summary.get("next_prompt"),
}
@@ -2255,6 +2426,21 @@ def _build_decision_assistant_digest_history_groups(history: list[dict], *, reco
proof_follow_up_state=proof_follow_up_state,
proof_resolution_sla_seconds=proof_resolution_sla_seconds,
)
proof_sla_backlog_route = _digest_history_proof_sla_backlog_route(
proof_resolution_sla_status=proof_resolution_sla_status,
proof_follow_up_state=proof_follow_up_state,
freshness_proof_status=freshness_proof_status,
)
freshness_debt_score = _digest_history_freshness_debt_score(
age=age,
proof_resolution_sla_status=proof_resolution_sla_status,
proof_follow_up_state=proof_follow_up_state,
)
proof_sla_backlog_priority = _digest_history_proof_sla_backlog_priority(
route=proof_sla_backlog_route,
proof_resolution_sla_status=proof_resolution_sla_status,
freshness_debt_score=freshness_debt_score,
)
repeated_stale_proof_suppression = _digest_history_repeated_stale_proof_suppression(
item_count=len(items),
proof_follow_up_stale_queue=proof_follow_up_stale_queue,
@@ -2317,6 +2503,9 @@ def _build_decision_assistant_digest_history_groups(history: list[dict], *, reco
proof_resolution_sla_status=proof_resolution_sla_status,
proof_resolution_sla_seconds=proof_resolution_sla_seconds,
),
"proof_sla_backlog_route": proof_sla_backlog_route,
"proof_sla_backlog_priority": proof_sla_backlog_priority,
"freshness_debt_score": freshness_debt_score,
"repeated_stale_proof_suppression": repeated_stale_proof_suppression,
"refresh_confirmation_trail": refresh_confirmation_trail,
"latest_confirmation_trail_item": refresh_confirmation_trail[0] if refresh_confirmation_trail else None,
@@ -2648,6 +2837,124 @@ def _digest_history_proof_resolution_sla_prompt(
return f"{kind} proof is resolved; no proof SLA action is pending."
def _digest_history_proof_sla_backlog_route(
*,
proof_resolution_sla_status: str,
proof_follow_up_state: str,
freshness_proof_status: str,
) -> str:
if proof_resolution_sla_status == "breached":
return "resolve_now"
if proof_resolution_sla_status == "missing_timestamp_sla" or freshness_proof_status == "missing_timestamp_proof":
return "timestamp_proof"
if proof_resolution_sla_status in {"due_soon", "within_sla"} or proof_follow_up_state == "watch":
return "watch_queue"
return "resolved"
def _digest_history_proof_sla_backlog_priority(
*,
route: str,
proof_resolution_sla_status: str,
freshness_debt_score: int,
) -> str:
if route == "resolve_now" or proof_resolution_sla_status == "breached":
return "high"
if route == "timestamp_proof":
return "high" if int(freshness_debt_score or 0) >= 60 else "medium"
if route == "watch_queue":
return "medium" if int(freshness_debt_score or 0) >= 45 else "low"
return "none"
def _digest_history_freshness_debt_score(
*,
age: dict,
proof_resolution_sla_status: str,
proof_follow_up_state: str,
) -> int:
age_band = str(age.get("age_band") or "unknown_age")
base = {
"fresh_now": 0,
"monitor_window": 10,
"same_session": 35,
"stale": 75,
"untimed": 60,
"unknown_age": 60,
}.get(age_band, 40)
if proof_resolution_sla_status == "breached":
base += 25
elif proof_resolution_sla_status == "due_soon":
base += 15
elif proof_resolution_sla_status == "missing_timestamp_sla":
base += 20
elif proof_resolution_sla_status == "within_sla":
base += 10
if proof_follow_up_state == "follow_up_required":
base += 10
return max(0, min(100, int(base)))
def _build_digest_history_freshness_debt_summary(groups: list[dict]) -> dict:
if not groups:
return {
"status": "idle",
"debt_score_total": 0,
"high_debt_count": 0,
"route_counts": {},
"next_kind": None,
"next_prompt": None,
}
debt_score_total = sum(int(group.get("freshness_debt_score") or 0) for group in groups)
high_debt_groups = [
group
for group in groups
if int(group.get("freshness_debt_score") or 0) >= 70
]
route_counts = {}
for group in groups:
route = str(group.get("proof_sla_backlog_route") or "unknown")
route_counts[route] = int(route_counts.get(route) or 0) + 1
next_group = dict(
sorted(
groups,
key=lambda row: (
int(row.get("freshness_debt_score") or 0),
str(row.get("kind") or ""),
),
reverse=True,
)[0]
)
if high_debt_groups:
status = "high_debt"
elif any(group.get("proof_sla_backlog_route") in {"timestamp_proof", "watch_queue"} for group in groups):
status = "watch_debt"
else:
status = "clear"
return {
"status": status,
"debt_score_total": debt_score_total,
"average_debt_score": debt_score_total / len(groups) if groups else 0.0,
"high_debt_count": len(high_debt_groups),
"route_counts": route_counts,
"next_kind": next_group.get("kind"),
"next_debt_score": next_group.get("freshness_debt_score"),
"next_prompt": _digest_history_freshness_debt_prompt(next_group),
}
def _digest_history_freshness_debt_prompt(group: dict) -> str:
kind = str(group.get("kind") or "history")
route = str(group.get("proof_sla_backlog_route") or "resolved")
if route == "resolve_now":
return f"Resolve {kind} freshness debt now before reusing this digest group."
if route == "timestamp_proof":
return f"Attach timestamp proof for {kind} so the digest backlog can leave the debt queue."
if route == "watch_queue":
return f"Keep {kind} in the watch queue and refresh it before it drives the next note."
return f"{kind} freshness debt is clear."
def _digest_history_repeated_stale_proof_suppression(
*,
item_count: int,
@@ -3006,6 +3313,13 @@ def _build_decision_assistant_scan_handoff_summary(cards: list[dict]) -> dict:
title=str(item.get("title") or item.get("target_section") or "handoff"),
handoff_status=str(item.get("handoff_status") or ""),
),
"cleanup_action_readiness": _scan_handoff_cleanup_action_readiness(str(item.get("handoff_status") or "")),
"cleanup_action_readiness_reason": _scan_handoff_cleanup_action_readiness_reason(
title=str(item.get("title") or item.get("target_section") or "handoff"),
handoff_status=str(item.get("handoff_status") or ""),
),
"resume_backlog_group": _scan_handoff_resume_backlog_group(str(item.get("handoff_status") or "")),
"resume_backlog_priority": _scan_handoff_resume_backlog_priority(str(item.get("handoff_status") or "")),
}
for item in cards
if item.get("handoff_marker")
@@ -3025,6 +3339,20 @@ def _build_decision_assistant_scan_handoff_summary(cards: list[dict]) -> dict:
for row in list(item.get("resume_history") or [])
]
stale_persistence_cleanup_queue = _build_scan_handoff_stale_persistence_cleanup_queue(markers)
resume_backlog_groups = _build_scan_handoff_resume_backlog_groups(markers)
cleanup_action_queue = [
item
for item in sorted(
markers,
key=lambda row: (
int(row.get("resume_backlog_priority") or 99),
int(row.get("cleanup_priority") or 0) * -1,
str(row.get("title") or ""),
),
)
if item.get("cleanup_action_readiness") in {"ready_now", "ready_after_refresh", "ready_after_blocker", "monitor"}
]
next_cleanup_action = dict(cleanup_action_queue[0] or {}) if cleanup_action_queue else {}
next_unresolved_marker = next(
(
item
@@ -3084,6 +3412,10 @@ def _build_decision_assistant_scan_handoff_summary(cards: list[dict]) -> dict:
"next_stale_persistence_cleanup_prompt": (
stale_persistence_cleanup_queue[0].get("stale_persistence_cleanup_prompt") if stale_persistence_cleanup_queue else None
),
"resume_backlog_groups": resume_backlog_groups,
"cleanup_action_ready_count": len(cleanup_action_queue),
"next_cleanup_action_label": next_cleanup_action.get("cleanup_action_label"),
"next_cleanup_action_prompt": next_cleanup_action.get("cleanup_action_readiness_reason"),
"handoff_stale_policy": "Persisted handoffs older than their stale window should be reopened and refreshed before reuse.",
"handoff_cleanup_recommendation": _scan_handoff_cleanup_recommendation(markers),
"snooze_options": [
@@ -3273,6 +3605,103 @@ def _scan_handoff_stale_persistence_cleanup_prompt(*, title: str, handoff_status
return f"{title} does not need stale persistence cleanup."
def _scan_handoff_cleanup_action_readiness(handoff_status: str) -> str:
if handoff_status == "ready_handoff":
return "ready_now"
if handoff_status == "refresh_handoff":
return "ready_after_refresh"
if handoff_status == "blocked_handoff":
return "ready_after_blocker"
if handoff_status == "next_handoff":
return "monitor"
return "not_needed"
def _scan_handoff_cleanup_action_readiness_reason(*, title: str, handoff_status: str) -> str:
readiness = _scan_handoff_cleanup_action_readiness(handoff_status)
if readiness == "ready_now":
return f"{title} can be reopened and cleared from the persisted resume backlog."
if readiness == "ready_after_refresh":
return f"Refresh {title}, then clear the stale persisted handoff."
if readiness == "ready_after_blocker":
return f"Keep {title} in the cleanup backlog until its blocker is resolved."
if readiness == "monitor":
return f"{title} is the next handoff; monitor the stale window before cleanup."
return f"{title} does not need a cleanup action."
def _scan_handoff_resume_backlog_group(handoff_status: str) -> str:
if handoff_status == "next_handoff":
return "active_resume"
if handoff_status == "blocked_handoff":
return "blocked_cleanup"
if handoff_status == "refresh_handoff":
return "refresh_cleanup"
if handoff_status == "ready_handoff":
return "ready_cleanup"
return "carry_forward"
def _scan_handoff_resume_backlog_priority(handoff_status: str) -> int:
return {
"blocked_handoff": 1,
"next_handoff": 2,
"refresh_handoff": 3,
"ready_handoff": 4,
"context_ready": 5,
}.get(str(handoff_status or "context_ready"), 5)
def _build_scan_handoff_resume_backlog_groups(markers: list[dict]) -> list[dict]:
groups_by_name: dict[str, list[dict]] = {}
for marker in markers:
group = str(marker.get("resume_backlog_group") or "carry_forward")
groups_by_name.setdefault(group, []).append(marker)
ranked_groups = ["blocked_cleanup", "active_resume", "refresh_cleanup", "ready_cleanup", "carry_forward"]
groups = []
for group in ranked_groups:
items = groups_by_name.get(group) or []
if not items:
continue
ranked_items = sorted(
items,
key=lambda row: (
int(row.get("resume_backlog_priority") or 99),
str(row.get("title") or ""),
),
)
cleanup_ready_items = [
item
for item in ranked_items
if item.get("cleanup_action_readiness") in {"ready_now", "ready_after_refresh", "ready_after_blocker", "monitor"}
]
groups.append(
{
"resume_backlog_group": group,
"label": _scan_handoff_resume_backlog_group_label(group),
"count": len(ranked_items),
"cleanup_ready_count": len(cleanup_ready_items),
"titles": [str(item.get("title") or "handoff") for item in ranked_items[:4]],
"next_marker": (ranked_items[0] or {}).get("handoff_marker"),
"next_cleanup_action": (cleanup_ready_items[0] or {}).get("cleanup_action_label") if cleanup_ready_items else None,
"next_prompt": (ranked_items[0] or {}).get("cleanup_action_readiness_reason"),
}
)
return groups
def _scan_handoff_resume_backlog_group_label(group: str) -> str:
if group == "blocked_cleanup":
return "Blocked Cleanup"
if group == "active_resume":
return "Active Resume"
if group == "refresh_cleanup":
return "Refresh Cleanup"
if group == "ready_cleanup":
return "Ready Cleanup"
return "Carry Forward"
def _build_scan_handoff_stale_persistence_cleanup_queue(markers: list[dict]) -> list[dict]:
cleanup_status_rank = {
"cleanup_required": 1,
@@ -3287,6 +3716,10 @@ def _build_scan_handoff_stale_persistence_cleanup_queue(markers: list[dict]) ->
"stale_persistence_cleanup_status": item.get("stale_persistence_cleanup_status"),
"stale_persistence_cleanup_prompt": item.get("stale_persistence_cleanup_prompt"),
"resume_queue_rank": item.get("resume_queue_rank"),
"cleanup_action_readiness": item.get("cleanup_action_readiness"),
"cleanup_action_readiness_reason": item.get("cleanup_action_readiness_reason"),
"resume_backlog_group": item.get("resume_backlog_group"),
"resume_backlog_priority": item.get("resume_backlog_priority"),
}
for item in markers
if item.get("stale_persistence_cleanup_status") in cleanup_status_rank
@@ -3321,6 +3754,10 @@ def _build_scan_handoff_resume_queue(markers: list[dict]) -> list[dict]:
"resume_history_count": item.get("resume_history_count") or 0,
"stale_persistence_cleanup_status": item.get("stale_persistence_cleanup_status"),
"stale_persistence_cleanup_prompt": item.get("stale_persistence_cleanup_prompt"),
"cleanup_action_readiness": item.get("cleanup_action_readiness"),
"cleanup_action_readiness_reason": item.get("cleanup_action_readiness_reason"),
"resume_backlog_group": item.get("resume_backlog_group"),
"resume_backlog_priority": item.get("resume_backlog_priority"),
}
for item in markers
if item.get("lifecycle_state") != "carry_forward"
@@ -3691,6 +4128,19 @@ def _build_setup_replay_broader_baseline_summary(
"reason": "insufficient_samples",
"action": "Collect replay samples before promotion backlog review.",
},
"promotion_evidence_queue": [
{
"evidence_type": "sample_gap",
"status": "collecting",
"value": 5,
"summary": f"{setup_family} needs replay samples before promotion evidence can be reviewed.",
}
],
"promotion_evidence_queue_count": 1,
"promotion_backlog_trend_status": "stable_collecting",
"promotion_backlog_trend_handoff": (
f"Keep {setup_family} in analysis-only collection until replay evidence reaches the sample threshold."
),
"demotion_history": [],
"demotion_history_count": 0,
"focus_recommendation": f"Collect samples before changing focus for {setup_family}.",
@@ -3924,6 +4374,19 @@ def _build_setup_family_trend_delta(
"reason": "insufficient_samples",
"action": "Collect replay samples before promotion backlog review.",
},
"promotion_evidence_queue": [
{
"evidence_type": "sample_gap",
"status": "collecting",
"value": max(0, sample_threshold - sample_count),
"summary": f"{setup_family} needs more replay samples before promotion evidence can be reviewed.",
}
],
"promotion_evidence_queue_count": 1,
"promotion_backlog_trend_status": "stable_collecting",
"promotion_backlog_trend_handoff": (
f"Keep {setup_family} in analysis-only collection until replay evidence reaches the sample threshold."
),
"demotion_history": [],
"demotion_history_count": 0,
"focus_recommendation": f"Collect samples before changing focus for {setup_family}.",
@@ -4059,6 +4522,24 @@ def _build_setup_family_trend_delta(
promotion_backlog_rank = _setup_family_promotion_backlog_rank(
promotion_backlog_priority=promotion_backlog_priority,
)
promotion_evidence_queue = _setup_family_promotion_evidence_queue(
setup_family=setup_family,
trend_status=trend_status,
sample_count=sample_count,
sample_gap=sample_gap,
framework_delta=framework_delta,
avg_r_delta=avg_r_delta,
risk_delta=risk_delta,
promotion_blockers=promotion_blockers,
review_lane_sla_status=review_lane_sla_status,
promotion_backlog_priority=promotion_backlog_priority,
)
promotion_backlog_trend_status = _setup_family_promotion_backlog_trend_status(
promotion_ready=promotion_ready,
sample_gap=sample_gap,
review_lane_sla_status=review_lane_sla_status,
promotion_backlog_priority=promotion_backlog_priority,
)
return {
"status": "active",
"trend_status": trend_status,
@@ -4109,6 +4590,14 @@ def _build_setup_family_trend_delta(
promotion_backlog_reason=promotion_backlog_reason,
promotion_next_action=promotion_next_action,
),
"promotion_evidence_queue": promotion_evidence_queue[:6],
"promotion_evidence_queue_count": len(promotion_evidence_queue),
"promotion_backlog_trend_status": promotion_backlog_trend_status,
"promotion_backlog_trend_handoff": _setup_family_promotion_backlog_trend_handoff(
setup_family=setup_family,
promotion_backlog_trend_status=promotion_backlog_trend_status,
promotion_backlog_reason=promotion_backlog_reason,
),
"demotion_history": demotion_history[:5],
"demotion_history_count": len(demotion_history),
"focus_recommendation": focus_recommendation,
@@ -4402,6 +4891,89 @@ def _setup_family_promotion_backlog_item(
}
def _setup_family_promotion_evidence_queue(
*,
setup_family: str,
trend_status: str,
sample_count: int,
sample_gap: int,
framework_delta: float,
avg_r_delta: float,
risk_delta: float,
promotion_blockers: list[str],
review_lane_sla_status: str,
promotion_backlog_priority: str,
) -> list[dict]:
queue = [
{
"evidence_type": "sample_window",
"status": "ready" if sample_gap <= 0 else "collecting",
"value": sample_count,
"summary": f"{setup_family} has {sample_count} replay sample(s); sample gap is {sample_gap}.",
},
{
"evidence_type": "family_delta",
"status": trend_status,
"value": round(float(framework_delta or 0.0), 4),
"summary": (
f"Framework delta {framework_delta:.2f}, avg-R delta {avg_r_delta:.2f}, "
f"risk delta {risk_delta:.2f}."
),
},
{
"evidence_type": "review_lane_sla",
"status": review_lane_sla_status,
"value": promotion_backlog_priority,
"summary": f"Review lane SLA is {review_lane_sla_status}; backlog priority is {promotion_backlog_priority}.",
},
]
for blocker in promotion_blockers[:3]:
queue.append(
{
"evidence_type": "promotion_blocker",
"status": "blocking",
"value": blocker,
"summary": f"{setup_family} promotion evidence is blocked by {blocker}.",
}
)
return queue
def _setup_family_promotion_backlog_trend_status(
*,
promotion_ready: bool,
sample_gap: int,
review_lane_sla_status: str,
promotion_backlog_priority: str,
) -> str:
if promotion_ready:
return "ready_for_review"
if review_lane_sla_status == "breached" or promotion_backlog_priority == "urgent":
return "backlog_breached"
if review_lane_sla_status in {"due_now", "due_soon"} or promotion_backlog_priority in {"high", "medium"}:
return "backlog_due"
if sample_gap > 0:
return "stable_collecting"
return "evidence_watch"
def _setup_family_promotion_backlog_trend_handoff(
*,
setup_family: str,
promotion_backlog_trend_status: str,
promotion_backlog_reason: str,
) -> str:
if promotion_backlog_trend_status == "ready_for_review":
return f"Hand off {setup_family} for analysis-lane promotion review; keep execution permissions unchanged."
if promotion_backlog_trend_status == "backlog_breached":
return f"Review {setup_family} backlog immediately because {promotion_backlog_reason} is breached."
if promotion_backlog_trend_status == "backlog_due":
return f"Queue {setup_family} for the next operator review because {promotion_backlog_reason} is due."
if promotion_backlog_trend_status == "stable_collecting":
return f"Keep collecting {setup_family} replay evidence before promotion handoff."
return f"Keep {setup_family} in evidence watch until promotion backlog signals change."
def _rank_session_composite(rows: list[dict], *, session_code: str) -> int | None:
if not session_code:
return None
+22
View File
@@ -587,6 +587,10 @@ function renderDecisionAssistant(summary) {
['stale persistence cleanup', String(scanHandoffSummary.stale_persistence_cleanup_count ?? 0)],
['next persistence cleanup', scanHandoffSummary.next_stale_persistence_cleanup_key || 'n/a'],
['persistence cleanup prompt', scanHandoffSummary.next_stale_persistence_cleanup_prompt || 'n/a'],
['resume backlog groups', formatList((scanHandoffSummary.resume_backlog_groups || []).map((item) => `${item.label || item.resume_backlog_group}: ${item.count ?? 0}`))],
['cleanup action ready', String(scanHandoffSummary.cleanup_action_ready_count ?? 0)],
['next cleanup action', scanHandoffSummary.next_cleanup_action_label || 'n/a'],
['next cleanup action prompt', scanHandoffSummary.next_cleanup_action_prompt || 'n/a'],
['next resume action', scanHandoffSummary.next_resume_action_label || 'n/a'],
['next resume prompt', scanHandoffSummary.next_resume_prompt || 'n/a'],
['handoff cleanup recommendation', scanHandoffSummary.handoff_cleanup_recommendation || 'n/a'],
@@ -729,6 +733,11 @@ function renderDecisionAssistant(summary) {
['stale proof suppressions', String(digestSurface.history_summary?.repeated_stale_proof_suppression_count ?? 0)],
['next proof SLA kind', digestSurface.history_summary?.next_proof_resolution_sla_kind || 'n/a'],
['next proof SLA prompt', digestSurface.history_summary?.next_proof_resolution_sla_prompt || 'n/a'],
['proof SLA backlog', String(digestSurface.history_summary?.proof_sla_backlog_count ?? 0)],
['proof SLA routes', formatJsonInline(digestSurface.history_summary?.proof_sla_backlog_routes || {})],
['freshness debt', digestSurface.history_summary?.freshness_debt_summary?.status || 'n/a'],
['freshness debt score', String(digestSurface.history_summary?.freshness_debt_summary?.debt_score_total ?? 0)],
['next freshness debt', digestSurface.history_summary?.next_freshness_debt_prompt || 'n/a'],
['next proof follow-up', digestSurface.history_summary?.next_proof_follow_up_prompt || 'n/a'],
['confirmation trail items', String(digestSurface.history_summary?.refresh_confirmation_trail_count ?? 0)],
['confirmation trail', formatList((digestSurface.history_summary?.refresh_confirmation_trail || []).map((item) => `${item.kind}:${item.proof_label}`))],
@@ -826,6 +835,9 @@ function renderDecisionAssistant(summary) {
['proof SLA status', item.proof_resolution_sla_status || 'n/a'],
['proof SLA', formatDurationSeconds(item.proof_resolution_sla_seconds)],
['proof SLA prompt', item.proof_resolution_sla_prompt || 'n/a'],
['SLA backlog route', item.proof_sla_backlog_route || 'n/a'],
['SLA backlog priority', item.proof_sla_backlog_priority || 'n/a'],
['freshness debt', String(item.freshness_debt_score ?? 0)],
['stale suppression', item.repeated_stale_proof_suppression || 'n/a'],
['latest proof trail', item.latest_confirmation_trail_item?.proof_label || 'n/a'],
['proof trail items', formatList((item.refresh_confirmation_trail || []).map((row) => `${row.title || row.kind}: ${row.proof_label}`))],
@@ -894,6 +906,9 @@ function renderDecisionAssistant(summary) {
['next recovery prompt', reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.next_preference_recovery_prompt || 'n/a'],
['audit filters', formatJsonInline(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.guardrail_audit_filter_counts || {})],
['filtered audit trail', String(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.guardrail_audit_filtered_count ?? 0)],
['audit lane groups', formatList((reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.template_audit_lane_groups || []).map((item) => `${item.label || item.audit_lane_group}: ${item.count ?? 0}`))],
['recovery shortcuts', String(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.recovery_action_shortcut_count ?? 0)],
['next recovery shortcut', reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.next_recovery_action_shortcut?.label || 'n/a'],
['preferred template decay', reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.preferred_template_decay_status || 'n/a'],
['preferred decay templates', formatList(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.preferred_template_decay_template_ids || [])],
['max preference decay', formatPercent(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.preferred_template_decay_max)],
@@ -959,6 +974,9 @@ function renderDecisionAssistant(summary) {
['preference recovery', item.preference_recovery_status || 'n/a'],
['recovery prompt', item.preference_recovery_prompt || 'n/a'],
['recovery recommendation', item.preference_recovery_recommendation?.action || 'n/a'],
['audit lane', item.audit_lane_group || 'n/a'],
['audit lane rank', String(item.audit_lane_rank ?? 'n/a')],
['recovery shortcuts', formatList((item.recovery_action_shortcuts || []).map((row) => row.label || row.action_id))],
['audit filter', item.guardrail_audit_filter || 'n/a'],
['audit filter reason', item.guardrail_audit_filter_reason || 'n/a'],
['guardrail audit trail', formatList((item.guardrail_audit_trail || []).map((row) => `${row.label || row.signal}: ${row.resolution_status}`))],
@@ -1275,6 +1293,10 @@ function renderDecisionAssistant(summary) {
['promotion backlog priority', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_backlog_priority || 'n/a'],
['promotion backlog rank', String(setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_backlog_rank ?? 'n/a')],
['promotion backlog reason', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_backlog_reason || 'n/a'],
['promotion evidence queue', String(setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_evidence_queue_count ?? 0)],
['promotion evidence', formatList((setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_evidence_queue || []).map((item) => `${item.evidence_type}: ${item.status}`))],
['backlog trend', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_backlog_trend_status || 'n/a'],
['backlog handoff', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_backlog_trend_handoff || 'n/a'],
['demotion history', String(setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.demotion_history_count ?? 0)],
['demotion reasons', formatList((setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.demotion_history || []).map((item) => item.reason || item.event))],
['family sample threshold', String(setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.sample_threshold ?? 'n/a')],
@@ -214,6 +214,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("guardrail_audit_filter_counts", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_audit_filtered_trail", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_audit_filtered_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("template_audit_lane_groups", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("recovery_action_shortcuts", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("recovery_action_shortcut_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("next_recovery_action_shortcut", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertEqual(summary["review_entrypoints"]["note_templates"][0]["reuse_count"], 1)
self.assertEqual(summary["review_entrypoints"]["note_templates"][0]["reuse_status"], "saved_reuse")
self.assertEqual(summary["review_entrypoints"]["note_templates"][0]["outcome_status"], "useful_signal")
@@ -232,6 +236,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("guardrail_audit_trail", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_trail_count", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("preference_recovery_recommendation", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("recovery_action_shortcuts", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("audit_lane_group", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("audit_lane_rank", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_filter", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_filter_reason", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_filtered_trail", summary["review_entrypoints"]["note_templates"][0])
@@ -272,6 +279,11 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("proof_resolution_sla_queue_count", summary["digest_surface"]["history_summary"])
self.assertIn("repeated_stale_proof_suppression_count", summary["digest_surface"]["history_summary"])
self.assertIn("next_proof_resolution_sla_prompt", summary["digest_surface"]["history_summary"])
self.assertIn("proof_sla_backlog_routes", summary["digest_surface"]["history_summary"])
self.assertIn("proof_sla_backlog_queue", summary["digest_surface"]["history_summary"])
self.assertIn("proof_sla_backlog_count", summary["digest_surface"]["history_summary"])
self.assertIn("freshness_debt_summary", summary["digest_surface"]["history_summary"])
self.assertIn("next_freshness_debt_prompt", summary["digest_surface"]["history_summary"])
self.assertIn("refresh_prompts", summary["digest_surface"]["history_summary"])
self.assertIn("digest_history_groups", summary)
self.assertIn("group_status", summary["digest_history_groups"][0])
@@ -300,6 +312,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("proof_resolution_sla_seconds", summary["digest_history_groups"][0])
self.assertIn("proof_resolution_sla_status", summary["digest_history_groups"][0])
self.assertIn("proof_resolution_sla_prompt", summary["digest_history_groups"][0])
self.assertIn("proof_sla_backlog_route", summary["digest_history_groups"][0])
self.assertIn("proof_sla_backlog_priority", summary["digest_history_groups"][0])
self.assertIn("freshness_debt_score", summary["digest_history_groups"][0])
self.assertIn("repeated_stale_proof_suppression", summary["digest_history_groups"][0])
self.assertIn("refresh_confirmation_trail", summary["digest_history_groups"][0])
self.assertIn("latest_confirmation_trail_item", summary["digest_history_groups"][0])
@@ -355,6 +370,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("promotion_backlog_rank", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_reason", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_item", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_evidence_queue", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_evidence_queue_count", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_trend_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_trend_handoff", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("demotion_history", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("demotion_history_count", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("sample_threshold", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
@@ -407,6 +426,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("stale_persistence_cleanup_count", summary["scan_handoff_summary"])
self.assertIn("next_stale_persistence_cleanup_key", summary["scan_handoff_summary"])
self.assertIn("next_stale_persistence_cleanup_prompt", summary["scan_handoff_summary"])
self.assertIn("resume_backlog_groups", summary["scan_handoff_summary"])
self.assertIn("cleanup_action_ready_count", summary["scan_handoff_summary"])
self.assertIn("next_cleanup_action_label", summary["scan_handoff_summary"])
self.assertIn("next_cleanup_action_prompt", summary["scan_handoff_summary"])
self.assertIn("handoff_cleanup_recommendation", summary["scan_handoff_summary"])
self.assertTrue(summary["scan_handoff_summary"]["snooze_options"])
self.assertIn("snooze_seconds", summary["scan_handoff_summary"]["handoff_markers"][0])
@@ -424,6 +447,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("resume_history_count", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("stale_persistence_cleanup_status", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("stale_persistence_cleanup_prompt", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("cleanup_action_readiness", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("cleanup_action_readiness_reason", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_backlog_group", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_backlog_priority", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertTrue(summary["external_context_summary"]["operator_questions"])
self.assertTrue(summary["external_context_summary"]["source_cards"])
self.assertEqual(summary["external_context_summary"]["exchange_status"]["status"], "active")