Close decision assistant H19 F20 G19 D21
This commit is contained in:
+18
-3
@@ -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 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.
|
||||
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 H19/F20/G19/D21 by adding handoff cleanup execution logs / grouped resume summaries, digest freshness-debt trend / SLA recovery recommendations, template audit-lane recommendations / shortcut telemetry, and setup-family promotion evidence aging / backlog comparison summaries. Local validation passed, the current working tree was deployed, and remote smoke confirmed the latest Decision Assistant fields on the live host.
|
||||
|
||||
## Current Branch
|
||||
|
||||
@@ -100,6 +100,10 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- 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_handoff_execution_slice: cleanup_execution_log_grouped_resume_handoff_summary
|
||||
- latest_decision_assistant_digest_debt_trend_slice: freshness_debt_trend_sla_recovery_recommendations
|
||||
- latest_decision_assistant_template_shortcut_telemetry_slice: audit_lane_recommendations_shortcut_outcome_telemetry
|
||||
- latest_decision_assistant_replay_evidence_aging_slice: promotion_evidence_aging_backlog_comparison_summary
|
||||
- latest_decision_assistant_summary_status: implemented
|
||||
- latest_decision_assistant_change_summary_status: implemented
|
||||
- latest_decision_assistant_evidence_status: implemented
|
||||
@@ -184,10 +188,14 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- 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_handoff_execution_status: implemented
|
||||
- latest_decision_assistant_digest_debt_trend_status: implemented
|
||||
- latest_decision_assistant_template_shortcut_telemetry_status: implemented
|
||||
- latest_decision_assistant_replay_evidence_aging_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: cleanup_backlog_proof_debt_template_lanes_promotion_evidence_visible
|
||||
- latest_remote_decision_assistant_review_packet_status: cleanup_execution_debt_recovery_shortcut_telemetry_evidence_aging_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
|
||||
@@ -266,6 +274,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
|
||||
## Completed In This Run
|
||||
|
||||
- Added handoff cleanup execution log / grouped resume handoff summary fields (`cleanup_execution_log`, `grouped_resume_handoff_summary`, and `next_cleanup_execution_prompt`) plus cockpit visibility.
|
||||
- Added digest freshness-debt trend / SLA recovery recommendation fields (`freshness_debt_trend`, `freshness_debt_trend_counts`, `sla_recovery_recommendations`, and `next_sla_recovery_prompt`) plus cockpit visibility.
|
||||
- Added template audit-lane recommendation / shortcut outcome telemetry fields (`audit_lane_recommendation`, `shortcut_outcome_telemetry`, `template_audit_lane_recommendations`, and `shortcut_outcome_status_counts`) plus cockpit visibility.
|
||||
- Added setup-family promotion evidence aging / backlog comparison fields (`promotion_evidence_aging_status`, `promotion_evidence_aging_queue`, `promotion_backlog_comparison_status`, and `promotion_backlog_comparison_summary`) 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 execution, freshness-debt trend, shortcut telemetry, and promotion evidence aging fields.
|
||||
- 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.
|
||||
@@ -576,11 +590,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- 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`.
|
||||
- Latest H19/F20/G19/D21 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 confirmed cleanup execution, debt trend, shortcut telemetry, and evidence aging fields.
|
||||
- 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: 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.
|
||||
Continue the Decision Assistant dense operator workspace with the next bounded product slice from the remaining non-trading assistant gaps: E5 external read-only macro/news calendar and event-window summary, W1 pre/intraday/post daily workflow rhythm checklist, D22 replay failure/missed-opportunity attribution rollup, and F21 personalized digest high-value filter / importance templates. PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
|
||||
|
||||
## Validation Commands Used Recently
|
||||
|
||||
|
||||
@@ -131,10 +131,14 @@
|
||||
- [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] H19 continue compact operator workspace polish with handoff cleanup execution log / grouped resume handoff summary
|
||||
- [x] F20 deepen digest recall with freshness-debt trend / SLA recovery recommendations
|
||||
- [x] G19 continue review template ergonomics with template audit-lane recommendations / shortcut outcome telemetry
|
||||
- [x] D21 extend replay comparison with setup-family promotion evidence aging / backlog comparison summary
|
||||
- [ ] E5 external read-only macro/news calendar and event-window summary
|
||||
- [ ] W1 pre/intraday/post workflow rhythm checklist
|
||||
- [ ] D22 replay learning failure/missed-opportunity attribution rollup
|
||||
- [ ] F21 personalized digest high-value filter / importance template
|
||||
- [x] P8 local closure complete
|
||||
- [x] P9 local closure complete
|
||||
- [x] P10 local closure complete
|
||||
|
||||
@@ -1098,6 +1098,9 @@ def _build_decision_assistant_review_entrypoints(
|
||||
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["audit_lane_recommendation"] = outcome.get("audit_lane_recommendation")
|
||||
item["shortcut_outcome_telemetry"] = outcome.get("shortcut_outcome_telemetry") or []
|
||||
item["shortcut_outcome_telemetry_count"] = outcome.get("shortcut_outcome_telemetry_count") or 0
|
||||
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 []
|
||||
@@ -1444,6 +1447,22 @@ def _score_template_outcome(*, template_id: str, usage: dict, notes: list[dict])
|
||||
preference_recovery_status=preference_recovery_status,
|
||||
outcome_status=outcome_status,
|
||||
)
|
||||
recovery_action_shortcuts = _template_recovery_action_shortcuts(
|
||||
template_id=template_id,
|
||||
recommendation=preference_recovery_recommendation,
|
||||
audit_lane_group=audit_lane_group,
|
||||
)
|
||||
audit_lane_recommendation = _template_audit_lane_recommendation(
|
||||
template_id=template_id,
|
||||
audit_lane_group=audit_lane_group,
|
||||
recommendation=preference_recovery_recommendation,
|
||||
)
|
||||
shortcut_outcome_telemetry = _template_shortcut_outcome_telemetry(
|
||||
template_id=template_id,
|
||||
shortcuts=recovery_action_shortcuts,
|
||||
audit_lane_group=audit_lane_group,
|
||||
outcome_status=outcome_status,
|
||||
)
|
||||
return {
|
||||
"template_id": template_id,
|
||||
"reuse_count": reuse_count,
|
||||
@@ -1486,13 +1505,12 @@ 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": preference_recovery_recommendation,
|
||||
"recovery_action_shortcuts": _template_recovery_action_shortcuts(
|
||||
template_id=template_id,
|
||||
recommendation=preference_recovery_recommendation,
|
||||
audit_lane_group=audit_lane_group,
|
||||
),
|
||||
"recovery_action_shortcuts": recovery_action_shortcuts,
|
||||
"audit_lane_group": audit_lane_group,
|
||||
"audit_lane_rank": _template_audit_lane_rank(audit_lane_group),
|
||||
"audit_lane_recommendation": audit_lane_recommendation,
|
||||
"shortcut_outcome_telemetry": shortcut_outcome_telemetry,
|
||||
"shortcut_outcome_telemetry_count": len(shortcut_outcome_telemetry),
|
||||
"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],
|
||||
@@ -1737,6 +1755,71 @@ def _template_recovery_shortcut_label(action: str) -> str:
|
||||
return "Keep Stable"
|
||||
|
||||
|
||||
def _template_audit_lane_recommendation(*, template_id: str, audit_lane_group: str, recommendation: dict) -> dict:
|
||||
action = str(recommendation.get("action") or "capture_first_outcome")
|
||||
priority = str(recommendation.get("priority") or "low")
|
||||
template_label = template_id.replace("_", " ") or "template"
|
||||
lane = str(audit_lane_group or "stable")
|
||||
if lane == "guardrail":
|
||||
lane_action = "resolve_guardrail"
|
||||
prompt = f"Keep {template_label} in the guardrail lane until recovery evidence is captured."
|
||||
elif lane == "recovery":
|
||||
lane_action = "review_recovery"
|
||||
prompt = f"Review recovery evidence for {template_label} before restoring preference."
|
||||
elif lane == "capture":
|
||||
lane_action = "capture_outcome"
|
||||
prompt = f"Capture one outcome note for {template_label} before ranking it again."
|
||||
else:
|
||||
lane_action = "keep_stable"
|
||||
prompt = f"{template_label} is stable; keep telemetry on future shortcut use."
|
||||
return {
|
||||
"template_id": template_id,
|
||||
"audit_lane_group": lane,
|
||||
"audit_lane_rank": _template_audit_lane_rank(lane),
|
||||
"action": lane_action,
|
||||
"source_action": action,
|
||||
"priority": priority,
|
||||
"prompt": prompt,
|
||||
}
|
||||
|
||||
|
||||
def _template_shortcut_outcome_telemetry(
|
||||
*,
|
||||
template_id: str,
|
||||
shortcuts: list[dict],
|
||||
audit_lane_group: str,
|
||||
outcome_status: str,
|
||||
) -> list[dict]:
|
||||
telemetry = []
|
||||
for index, shortcut in enumerate(shortcuts[:4], start=1):
|
||||
action_id = str(shortcut.get("action_id") or f"{template_id}:shortcut:{index}")
|
||||
telemetry.append(
|
||||
{
|
||||
"template_id": template_id,
|
||||
"action_id": action_id,
|
||||
"label": shortcut.get("label"),
|
||||
"audit_lane_group": audit_lane_group,
|
||||
"outcome_status": outcome_status,
|
||||
"telemetry_status": _template_shortcut_telemetry_status(
|
||||
audit_lane_group=audit_lane_group,
|
||||
outcome_status=outcome_status,
|
||||
),
|
||||
"rank": index,
|
||||
}
|
||||
)
|
||||
return telemetry
|
||||
|
||||
|
||||
def _template_shortcut_telemetry_status(*, audit_lane_group: str, outcome_status: str) -> str:
|
||||
if audit_lane_group == "guardrail" or outcome_status == "review_before_reuse":
|
||||
return "guardrail_shortcut_watch"
|
||||
if audit_lane_group == "recovery":
|
||||
return "recovery_shortcut_watch"
|
||||
if audit_lane_group == "capture":
|
||||
return "capture_shortcut_needed"
|
||||
return "stable_shortcut"
|
||||
|
||||
|
||||
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":
|
||||
@@ -1856,6 +1939,27 @@ def _build_template_feedback_aggregation(
|
||||
str(row.get("label") or ""),
|
||||
)
|
||||
)
|
||||
template_audit_lane_recommendations = [
|
||||
dict(item.get("audit_lane_recommendation") or {})
|
||||
for item in template_outcomes.values()
|
||||
if dict(item.get("audit_lane_recommendation") or {})
|
||||
]
|
||||
template_audit_lane_recommendations.sort(
|
||||
key=lambda row: (
|
||||
int(row.get("audit_lane_rank") or 9),
|
||||
{"high": 1, "medium": 2, "low": 3, "none": 4}.get(str(row.get("priority") or "low"), 3),
|
||||
str(row.get("template_id") or ""),
|
||||
)
|
||||
)
|
||||
shortcut_outcome_telemetry = [
|
||||
dict(row)
|
||||
for item in template_outcomes.values()
|
||||
for row in list(item.get("shortcut_outcome_telemetry") or [])
|
||||
]
|
||||
shortcut_outcome_status_counts = {}
|
||||
for row in shortcut_outcome_telemetry:
|
||||
status = str(row.get("telemetry_status") or "unknown")
|
||||
shortcut_outcome_status_counts[status] = int(shortcut_outcome_status_counts.get(status) or 0) + 1
|
||||
guardrail_audit_filter_counts = {}
|
||||
for item in template_outcomes.values():
|
||||
audit_filter = str(item.get("guardrail_audit_filter") or "unknown")
|
||||
@@ -1939,6 +2043,14 @@ def _build_template_feedback_aggregation(
|
||||
"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,
|
||||
"template_audit_lane_recommendations": template_audit_lane_recommendations[:6],
|
||||
"template_audit_lane_recommendation_count": len(template_audit_lane_recommendations),
|
||||
"shortcut_outcome_telemetry": shortcut_outcome_telemetry[:8],
|
||||
"shortcut_outcome_telemetry_count": len(shortcut_outcome_telemetry),
|
||||
"shortcut_outcome_status_counts": shortcut_outcome_status_counts,
|
||||
"next_shortcut_outcome_prompt": (
|
||||
template_audit_lane_recommendations[0].get("prompt") if template_audit_lane_recommendations else None
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -2285,6 +2397,11 @@ def _build_decision_assistant_digest_history_summary(history: list[dict]) -> dic
|
||||
)
|
||||
)
|
||||
freshness_debt_summary = _build_digest_history_freshness_debt_summary(groups)
|
||||
freshness_debt_trend_counts = {}
|
||||
for group in groups:
|
||||
trend = str(group.get("freshness_debt_trend") or "unknown")
|
||||
freshness_debt_trend_counts[trend] = int(freshness_debt_trend_counts.get(trend) or 0) + 1
|
||||
sla_recovery_recommendations = _build_digest_history_sla_recovery_recommendations(groups)
|
||||
repeated_stale_suppression_count = sum(
|
||||
1
|
||||
for group in groups
|
||||
@@ -2366,6 +2483,15 @@ def _build_decision_assistant_digest_history_summary(history: list[dict]) -> dic
|
||||
"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"),
|
||||
"freshness_debt_trend_counts": freshness_debt_trend_counts,
|
||||
"sla_recovery_recommendations": sla_recovery_recommendations[:5],
|
||||
"sla_recovery_recommendation_count": len(sla_recovery_recommendations),
|
||||
"next_sla_recovery_recommendation": (
|
||||
sla_recovery_recommendations[0] if sla_recovery_recommendations else None
|
||||
),
|
||||
"next_sla_recovery_prompt": (
|
||||
sla_recovery_recommendations[0].get("prompt") if sla_recovery_recommendations else None
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -2441,6 +2567,18 @@ def _build_decision_assistant_digest_history_groups(history: list[dict], *, reco
|
||||
proof_resolution_sla_status=proof_resolution_sla_status,
|
||||
freshness_debt_score=freshness_debt_score,
|
||||
)
|
||||
freshness_debt_trend = _digest_history_freshness_debt_trend(
|
||||
freshness_debt_score=freshness_debt_score,
|
||||
proof_sla_backlog_route=proof_sla_backlog_route,
|
||||
proof_resolution_sla_status=proof_resolution_sla_status,
|
||||
)
|
||||
sla_recovery_recommendation = _digest_history_sla_recovery_recommendation(
|
||||
kind=kind,
|
||||
proof_sla_backlog_route=proof_sla_backlog_route,
|
||||
proof_sla_backlog_priority=proof_sla_backlog_priority,
|
||||
freshness_debt_trend=freshness_debt_trend,
|
||||
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,
|
||||
@@ -2506,6 +2644,8 @@ def _build_decision_assistant_digest_history_groups(history: list[dict], *, reco
|
||||
"proof_sla_backlog_route": proof_sla_backlog_route,
|
||||
"proof_sla_backlog_priority": proof_sla_backlog_priority,
|
||||
"freshness_debt_score": freshness_debt_score,
|
||||
"freshness_debt_trend": freshness_debt_trend,
|
||||
"sla_recovery_recommendation": sla_recovery_recommendation,
|
||||
"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,
|
||||
@@ -2955,6 +3095,84 @@ def _digest_history_freshness_debt_prompt(group: dict) -> str:
|
||||
return f"{kind} freshness debt is clear."
|
||||
|
||||
|
||||
def _digest_history_freshness_debt_trend(
|
||||
*,
|
||||
freshness_debt_score: int,
|
||||
proof_sla_backlog_route: str,
|
||||
proof_resolution_sla_status: str,
|
||||
) -> str:
|
||||
score = int(freshness_debt_score or 0)
|
||||
route = str(proof_sla_backlog_route or "resolved")
|
||||
sla_status = str(proof_resolution_sla_status or "resolved")
|
||||
if score >= 80 or route == "resolve_now" or sla_status == "breached":
|
||||
return "debt_expanding"
|
||||
if route == "timestamp_proof" or sla_status == "missing_timestamp_sla":
|
||||
return "timestamp_gap"
|
||||
if score >= 40 or route == "watch_queue":
|
||||
return "debt_watch"
|
||||
return "debt_clear"
|
||||
|
||||
|
||||
def _digest_history_sla_recovery_recommendation(
|
||||
*,
|
||||
kind: str,
|
||||
proof_sla_backlog_route: str,
|
||||
proof_sla_backlog_priority: str,
|
||||
freshness_debt_trend: str,
|
||||
freshness_debt_score: int,
|
||||
) -> dict:
|
||||
route = str(proof_sla_backlog_route or "resolved")
|
||||
priority = str(proof_sla_backlog_priority or "none")
|
||||
trend = str(freshness_debt_trend or "debt_clear")
|
||||
if route == "resolve_now":
|
||||
action = "resolve_now"
|
||||
label = "Resolve SLA"
|
||||
prompt = f"Resolve {kind} proof SLA before this digest group is reused."
|
||||
elif route == "timestamp_proof":
|
||||
action = "attach_timestamp_proof"
|
||||
label = "Attach Timestamp"
|
||||
prompt = f"Attach timestamp proof for {kind} to reduce freshness debt."
|
||||
elif route == "watch_queue":
|
||||
action = "refresh_watch_queue"
|
||||
label = "Refresh Watch"
|
||||
prompt = f"Refresh or recheck {kind} before it drives the next operator note."
|
||||
else:
|
||||
action = "confirm_clear"
|
||||
label = "Confirmed Clear"
|
||||
prompt = f"{kind} has no SLA recovery action pending."
|
||||
return {
|
||||
"kind": kind,
|
||||
"action": action,
|
||||
"label": label,
|
||||
"priority": priority,
|
||||
"freshness_debt_trend": trend,
|
||||
"freshness_debt_score": int(freshness_debt_score or 0),
|
||||
"prompt": prompt,
|
||||
}
|
||||
|
||||
|
||||
def _build_digest_history_sla_recovery_recommendations(groups: list[dict]) -> list[dict]:
|
||||
recommendations = [
|
||||
dict(group.get("sla_recovery_recommendation") or {})
|
||||
for group in groups
|
||||
if dict(group.get("sla_recovery_recommendation") or {}).get("action") != "confirm_clear"
|
||||
]
|
||||
recommendations.sort(
|
||||
key=lambda row: (
|
||||
{"high": 1, "medium": 2, "low": 3, "none": 4}.get(str(row.get("priority") or "none"), 4),
|
||||
{
|
||||
"debt_expanding": 1,
|
||||
"timestamp_gap": 2,
|
||||
"debt_watch": 3,
|
||||
"debt_clear": 4,
|
||||
}.get(str(row.get("freshness_debt_trend") or "debt_clear"), 4),
|
||||
-int(row.get("freshness_debt_score") or 0),
|
||||
str(row.get("kind") or ""),
|
||||
)
|
||||
)
|
||||
return recommendations
|
||||
|
||||
|
||||
def _digest_history_repeated_stale_proof_suppression(
|
||||
*,
|
||||
item_count: int,
|
||||
@@ -3320,6 +3538,18 @@ def _build_decision_assistant_scan_handoff_summary(cards: list[dict]) -> dict:
|
||||
),
|
||||
"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 "")),
|
||||
"cleanup_execution_log": _scan_handoff_cleanup_execution_log(
|
||||
title=str(item.get("title") or item.get("target_section") or "handoff"),
|
||||
handoff_status=str(item.get("handoff_status") or ""),
|
||||
persistence_key=str(item.get("handoff_persistence_key") or item.get("handoff_marker") or ""),
|
||||
),
|
||||
"cleanup_execution_log_count": len(
|
||||
_scan_handoff_cleanup_execution_log(
|
||||
title=str(item.get("title") or item.get("target_section") or "handoff"),
|
||||
handoff_status=str(item.get("handoff_status") or ""),
|
||||
persistence_key=str(item.get("handoff_persistence_key") or item.get("handoff_marker") or ""),
|
||||
)
|
||||
),
|
||||
}
|
||||
for item in cards
|
||||
if item.get("handoff_marker")
|
||||
@@ -3353,6 +3583,11 @@ def _build_decision_assistant_scan_handoff_summary(cards: list[dict]) -> dict:
|
||||
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 {}
|
||||
cleanup_execution_log = [
|
||||
row
|
||||
for item in cleanup_action_queue
|
||||
for row in list(item.get("cleanup_execution_log") or [])
|
||||
]
|
||||
next_unresolved_marker = next(
|
||||
(
|
||||
item
|
||||
@@ -3416,6 +3651,10 @@ def _build_decision_assistant_scan_handoff_summary(cards: list[dict]) -> dict:
|
||||
"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"),
|
||||
"cleanup_execution_log": cleanup_execution_log[:8],
|
||||
"cleanup_execution_log_count": len(cleanup_execution_log),
|
||||
"grouped_resume_handoff_summary": _build_scan_handoff_grouped_resume_handoff_summary(resume_backlog_groups),
|
||||
"next_cleanup_execution_prompt": _scan_handoff_next_cleanup_execution_prompt(next_cleanup_action),
|
||||
"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": [
|
||||
@@ -3702,6 +3941,94 @@ def _scan_handoff_resume_backlog_group_label(group: str) -> str:
|
||||
return "Carry Forward"
|
||||
|
||||
|
||||
def _scan_handoff_cleanup_execution_action_label(handoff_status: str) -> str:
|
||||
if handoff_status == "blocked_handoff":
|
||||
return "Hold Blocker"
|
||||
if handoff_status == "refresh_handoff":
|
||||
return "Refresh Then Clear"
|
||||
if handoff_status == "ready_handoff":
|
||||
return "Clear Resume State"
|
||||
if handoff_status == "next_handoff":
|
||||
return "Monitor Window"
|
||||
return "Carry Forward"
|
||||
|
||||
|
||||
def _scan_handoff_cleanup_execution_log(*, title: str, handoff_status: str, persistence_key: str) -> list[dict]:
|
||||
key = str(persistence_key or title or "handoff").strip() or "handoff"
|
||||
readiness = _scan_handoff_cleanup_action_readiness(handoff_status)
|
||||
backlog_group = _scan_handoff_resume_backlog_group(handoff_status)
|
||||
log = [
|
||||
{
|
||||
"event": "cleanup_readiness_checked",
|
||||
"title": title,
|
||||
"status": readiness,
|
||||
"persistence_key": key,
|
||||
"resume_backlog_group": backlog_group,
|
||||
},
|
||||
{
|
||||
"event": "cleanup_action_selected",
|
||||
"title": title,
|
||||
"status": _scan_handoff_stale_persistence_cleanup_status(handoff_status),
|
||||
"persistence_key": key,
|
||||
"action_label": _scan_handoff_cleanup_execution_action_label(handoff_status),
|
||||
},
|
||||
{
|
||||
"event": "grouped_resume_handoff",
|
||||
"title": title,
|
||||
"status": backlog_group,
|
||||
"persistence_key": key,
|
||||
"resume_backlog_priority": _scan_handoff_resume_backlog_priority(handoff_status),
|
||||
},
|
||||
]
|
||||
if readiness in {"ready_now", "ready_after_refresh", "ready_after_blocker", "monitor"}:
|
||||
log.append(
|
||||
{
|
||||
"event": "operator_cleanup_handoff_ready",
|
||||
"title": title,
|
||||
"status": readiness,
|
||||
"persistence_key": key,
|
||||
"prompt": _scan_handoff_cleanup_action_readiness_reason(
|
||||
title=title,
|
||||
handoff_status=handoff_status,
|
||||
),
|
||||
}
|
||||
)
|
||||
return log
|
||||
|
||||
|
||||
def _build_scan_handoff_grouped_resume_handoff_summary(resume_backlog_groups: list[dict]) -> list[dict]:
|
||||
summary = []
|
||||
for group in resume_backlog_groups:
|
||||
label = str(group.get("label") or group.get("resume_backlog_group") or "handoff")
|
||||
count = int(group.get("count") or 0)
|
||||
cleanup_ready_count = int(group.get("cleanup_ready_count") or 0)
|
||||
summary.append(
|
||||
{
|
||||
"resume_backlog_group": group.get("resume_backlog_group"),
|
||||
"label": label,
|
||||
"count": count,
|
||||
"cleanup_ready_count": cleanup_ready_count,
|
||||
"next_marker": group.get("next_marker"),
|
||||
"next_cleanup_action": group.get("next_cleanup_action"),
|
||||
"summary": f"{label} has {count} lane(s), {cleanup_ready_count} cleanup-ready.",
|
||||
}
|
||||
)
|
||||
return summary
|
||||
|
||||
|
||||
def _scan_handoff_next_cleanup_execution_prompt(next_cleanup_action: dict) -> str:
|
||||
if not next_cleanup_action:
|
||||
return "No cleanup execution is queued."
|
||||
title = str(next_cleanup_action.get("title") or next_cleanup_action.get("target_section") or "handoff")
|
||||
readiness_reason = str(next_cleanup_action.get("cleanup_action_readiness_reason") or "")
|
||||
action_label = str(next_cleanup_action.get("cleanup_action_label") or "")
|
||||
if readiness_reason:
|
||||
return readiness_reason
|
||||
if action_label:
|
||||
return f"{title}: execute {action_label} when the operator resumes this handoff."
|
||||
return f"{title}: review cleanup execution before carrying the handoff forward."
|
||||
|
||||
|
||||
def _build_scan_handoff_stale_persistence_cleanup_queue(markers: list[dict]) -> list[dict]:
|
||||
cleanup_status_rank = {
|
||||
"cleanup_required": 1,
|
||||
@@ -4141,6 +4468,28 @@ def _build_setup_replay_broader_baseline_summary(
|
||||
"promotion_backlog_trend_handoff": (
|
||||
f"Keep {setup_family} in analysis-only collection until replay evidence reaches the sample threshold."
|
||||
),
|
||||
"promotion_evidence_aging_status": "collecting_samples",
|
||||
"promotion_evidence_aging_prompt": (
|
||||
f"{setup_family} promotion evidence is still collecting; add replay samples before review."
|
||||
),
|
||||
"promotion_evidence_aging_queue": [
|
||||
{
|
||||
"evidence_type": "sample_gap",
|
||||
"aging_status": "collecting_samples",
|
||||
"aging_rank": 1,
|
||||
"summary": f"{setup_family} needs replay samples before promotion evidence can age into review.",
|
||||
}
|
||||
],
|
||||
"promotion_backlog_comparison_status": "collecting_samples",
|
||||
"promotion_backlog_comparison_summary": {
|
||||
"status": "collecting_samples",
|
||||
"priority": "low",
|
||||
"rank": 5,
|
||||
"reason": "insufficient_samples",
|
||||
"sample_gap": 5,
|
||||
"evidence_queue_count": 1,
|
||||
"summary": f"{setup_family} backlog remains in sample collection before comparison.",
|
||||
},
|
||||
"demotion_history": [],
|
||||
"demotion_history_count": 0,
|
||||
"focus_recommendation": f"Collect samples before changing focus for {setup_family}.",
|
||||
@@ -4327,12 +4676,35 @@ def _build_setup_family_trend_delta(
|
||||
baseline_rows = peer_sessions or fallback_rows
|
||||
if sample_count <= 0 or not baseline_rows:
|
||||
sample_threshold = 5
|
||||
sample_gap = max(0, sample_threshold - sample_count)
|
||||
promotion_evidence_queue = [
|
||||
{
|
||||
"evidence_type": "sample_gap",
|
||||
"status": "collecting",
|
||||
"value": sample_gap,
|
||||
"summary": f"{setup_family} needs more replay samples before promotion evidence can be reviewed.",
|
||||
}
|
||||
]
|
||||
promotion_evidence_aging_status = _setup_family_promotion_evidence_aging_status(
|
||||
sample_gap=sample_gap,
|
||||
review_lane_sla_status="collecting_samples",
|
||||
promotion_backlog_trend_status="stable_collecting",
|
||||
)
|
||||
promotion_backlog_comparison_summary = _setup_family_promotion_backlog_comparison_summary(
|
||||
setup_family=setup_family,
|
||||
promotion_backlog_priority="low",
|
||||
promotion_backlog_rank=5,
|
||||
promotion_backlog_reason="insufficient_samples",
|
||||
promotion_backlog_trend_status="stable_collecting",
|
||||
sample_gap=sample_gap,
|
||||
promotion_evidence_queue_count=len(promotion_evidence_queue),
|
||||
)
|
||||
return {
|
||||
"status": "idle",
|
||||
"trend_status": "insufficient_samples",
|
||||
"sample_count": sample_count,
|
||||
"sample_threshold": sample_threshold,
|
||||
"sample_gap": max(0, sample_threshold - sample_count),
|
||||
"sample_gap": sample_gap,
|
||||
"baseline_count": len(baseline_rows),
|
||||
"framework_delta": 0.0,
|
||||
"avg_r_delta": 0.0,
|
||||
@@ -4374,19 +4746,24 @@ 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_evidence_queue": promotion_evidence_queue,
|
||||
"promotion_evidence_queue_count": len(promotion_evidence_queue),
|
||||
"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."
|
||||
),
|
||||
"promotion_evidence_aging_status": promotion_evidence_aging_status,
|
||||
"promotion_evidence_aging_prompt": _setup_family_promotion_evidence_aging_prompt(
|
||||
setup_family=setup_family,
|
||||
promotion_evidence_aging_status=promotion_evidence_aging_status,
|
||||
),
|
||||
"promotion_evidence_aging_queue": _setup_family_promotion_evidence_aging_queue(
|
||||
setup_family=setup_family,
|
||||
promotion_evidence_queue=promotion_evidence_queue,
|
||||
promotion_evidence_aging_status=promotion_evidence_aging_status,
|
||||
),
|
||||
"promotion_backlog_comparison_status": promotion_backlog_comparison_summary.get("status"),
|
||||
"promotion_backlog_comparison_summary": promotion_backlog_comparison_summary,
|
||||
"demotion_history": [],
|
||||
"demotion_history_count": 0,
|
||||
"focus_recommendation": f"Collect samples before changing focus for {setup_family}.",
|
||||
@@ -4540,6 +4917,25 @@ def _build_setup_family_trend_delta(
|
||||
review_lane_sla_status=review_lane_sla_status,
|
||||
promotion_backlog_priority=promotion_backlog_priority,
|
||||
)
|
||||
promotion_evidence_aging_status = _setup_family_promotion_evidence_aging_status(
|
||||
sample_gap=sample_gap,
|
||||
review_lane_sla_status=review_lane_sla_status,
|
||||
promotion_backlog_trend_status=promotion_backlog_trend_status,
|
||||
)
|
||||
promotion_evidence_aging_queue = _setup_family_promotion_evidence_aging_queue(
|
||||
setup_family=setup_family,
|
||||
promotion_evidence_queue=promotion_evidence_queue,
|
||||
promotion_evidence_aging_status=promotion_evidence_aging_status,
|
||||
)
|
||||
promotion_backlog_comparison_summary = _setup_family_promotion_backlog_comparison_summary(
|
||||
setup_family=setup_family,
|
||||
promotion_backlog_priority=promotion_backlog_priority,
|
||||
promotion_backlog_rank=promotion_backlog_rank,
|
||||
promotion_backlog_reason=promotion_backlog_reason,
|
||||
promotion_backlog_trend_status=promotion_backlog_trend_status,
|
||||
sample_gap=sample_gap,
|
||||
promotion_evidence_queue_count=len(promotion_evidence_queue),
|
||||
)
|
||||
return {
|
||||
"status": "active",
|
||||
"trend_status": trend_status,
|
||||
@@ -4598,6 +4994,14 @@ def _build_setup_family_trend_delta(
|
||||
promotion_backlog_trend_status=promotion_backlog_trend_status,
|
||||
promotion_backlog_reason=promotion_backlog_reason,
|
||||
),
|
||||
"promotion_evidence_aging_status": promotion_evidence_aging_status,
|
||||
"promotion_evidence_aging_prompt": _setup_family_promotion_evidence_aging_prompt(
|
||||
setup_family=setup_family,
|
||||
promotion_evidence_aging_status=promotion_evidence_aging_status,
|
||||
),
|
||||
"promotion_evidence_aging_queue": promotion_evidence_aging_queue[:6],
|
||||
"promotion_backlog_comparison_status": promotion_backlog_comparison_summary.get("status"),
|
||||
"promotion_backlog_comparison_summary": promotion_backlog_comparison_summary,
|
||||
"demotion_history": demotion_history[:5],
|
||||
"demotion_history_count": len(demotion_history),
|
||||
"focus_recommendation": focus_recommendation,
|
||||
@@ -4974,6 +5378,109 @@ def _setup_family_promotion_backlog_trend_handoff(
|
||||
return f"Keep {setup_family} in evidence watch until promotion backlog signals change."
|
||||
|
||||
|
||||
def _setup_family_promotion_evidence_aging_status(
|
||||
*,
|
||||
sample_gap: int,
|
||||
review_lane_sla_status: str,
|
||||
promotion_backlog_trend_status: str,
|
||||
) -> str:
|
||||
sla_status = str(review_lane_sla_status or "within_sla")
|
||||
trend_status = str(promotion_backlog_trend_status or "evidence_watch")
|
||||
if sla_status == "breached" or trend_status == "backlog_breached":
|
||||
return "aging_overdue"
|
||||
if sla_status in {"due_now", "due_soon"} or trend_status == "backlog_due":
|
||||
return "aging_due"
|
||||
if int(sample_gap or 0) > 0:
|
||||
return "collecting_samples"
|
||||
if trend_status == "ready_for_review":
|
||||
return "ready_now"
|
||||
return "watching_evidence"
|
||||
|
||||
|
||||
def _setup_family_promotion_evidence_aging_prompt(*, setup_family: str, promotion_evidence_aging_status: str) -> str:
|
||||
status = str(promotion_evidence_aging_status or "watching_evidence")
|
||||
if status == "aging_overdue":
|
||||
return f"{setup_family} promotion evidence is overdue; review the backlog before carrying the lane forward."
|
||||
if status == "aging_due":
|
||||
return f"{setup_family} promotion evidence is due soon; compare backlog signals in the next review pass."
|
||||
if status == "collecting_samples":
|
||||
return f"{setup_family} promotion evidence is still collecting; add replay samples before review."
|
||||
if status == "ready_now":
|
||||
return f"{setup_family} promotion evidence is ready for analysis-lane review; execution permissions stay unchanged."
|
||||
return f"{setup_family} promotion evidence remains on watch until backlog signals change."
|
||||
|
||||
|
||||
def _setup_family_promotion_evidence_aging_queue(
|
||||
*,
|
||||
setup_family: str,
|
||||
promotion_evidence_queue: list[dict],
|
||||
promotion_evidence_aging_status: str,
|
||||
) -> list[dict]:
|
||||
queue = []
|
||||
for index, item in enumerate(promotion_evidence_queue[:6], start=1):
|
||||
evidence_type = str(item.get("evidence_type") or "promotion_evidence")
|
||||
queue.append(
|
||||
{
|
||||
"evidence_type": evidence_type,
|
||||
"status": item.get("status"),
|
||||
"aging_status": promotion_evidence_aging_status,
|
||||
"aging_rank": index,
|
||||
"summary": item.get("summary")
|
||||
or f"{setup_family} {evidence_type} is part of the promotion evidence aging queue.",
|
||||
}
|
||||
)
|
||||
return queue
|
||||
|
||||
|
||||
def _setup_family_promotion_backlog_comparison_summary(
|
||||
*,
|
||||
setup_family: str,
|
||||
promotion_backlog_priority: str,
|
||||
promotion_backlog_rank: int,
|
||||
promotion_backlog_reason: str,
|
||||
promotion_backlog_trend_status: str,
|
||||
sample_gap: int,
|
||||
promotion_evidence_queue_count: int,
|
||||
) -> dict:
|
||||
status = _setup_family_promotion_backlog_comparison_status(
|
||||
promotion_backlog_priority=promotion_backlog_priority,
|
||||
promotion_backlog_trend_status=promotion_backlog_trend_status,
|
||||
sample_gap=sample_gap,
|
||||
)
|
||||
return {
|
||||
"status": status,
|
||||
"priority": promotion_backlog_priority,
|
||||
"rank": int(promotion_backlog_rank or 0),
|
||||
"reason": promotion_backlog_reason,
|
||||
"trend_status": promotion_backlog_trend_status,
|
||||
"sample_gap": int(sample_gap or 0),
|
||||
"evidence_queue_count": int(promotion_evidence_queue_count or 0),
|
||||
"summary": (
|
||||
f"{setup_family} backlog comparison is {status}: priority {promotion_backlog_priority}, "
|
||||
f"reason {promotion_backlog_reason}, sample gap {int(sample_gap or 0)}."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _setup_family_promotion_backlog_comparison_status(
|
||||
*,
|
||||
promotion_backlog_priority: str,
|
||||
promotion_backlog_trend_status: str,
|
||||
sample_gap: int,
|
||||
) -> str:
|
||||
priority = str(promotion_backlog_priority or "low")
|
||||
trend_status = str(promotion_backlog_trend_status or "evidence_watch")
|
||||
if priority == "urgent" or trend_status == "backlog_breached":
|
||||
return "urgent_gap"
|
||||
if priority in {"high", "medium"} or trend_status == "backlog_due":
|
||||
return "due_gap"
|
||||
if int(sample_gap or 0) > 0:
|
||||
return "collecting_samples"
|
||||
if trend_status == "ready_for_review":
|
||||
return "ready_for_review"
|
||||
return "watching"
|
||||
|
||||
|
||||
def _rank_session_composite(rows: list[dict], *, session_code: str) -> int | None:
|
||||
if not session_code:
|
||||
return None
|
||||
|
||||
@@ -588,9 +588,13 @@ function renderDecisionAssistant(summary) {
|
||||
['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}`))],
|
||||
['grouped resume summary', formatList((scanHandoffSummary.grouped_resume_handoff_summary || []).map((item) => item.summary || `${item.label}: ${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'],
|
||||
['cleanup execution log', String(scanHandoffSummary.cleanup_execution_log_count ?? 0)],
|
||||
['cleanup execution events', formatList((scanHandoffSummary.cleanup_execution_log || []).map((item) => `${item.event}: ${item.status}`))],
|
||||
['next cleanup execution', scanHandoffSummary.next_cleanup_execution_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'],
|
||||
@@ -738,6 +742,10 @@ function renderDecisionAssistant(summary) {
|
||||
['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'],
|
||||
['freshness debt trends', formatJsonInline(digestSurface.history_summary?.freshness_debt_trend_counts || {})],
|
||||
['SLA recovery recs', String(digestSurface.history_summary?.sla_recovery_recommendation_count ?? 0)],
|
||||
['next SLA recovery', digestSurface.history_summary?.next_sla_recovery_recommendation?.label || 'n/a'],
|
||||
['next SLA recovery prompt', digestSurface.history_summary?.next_sla_recovery_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}`))],
|
||||
@@ -838,6 +846,9 @@ function renderDecisionAssistant(summary) {
|
||||
['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)],
|
||||
['freshness debt trend', item.freshness_debt_trend || 'n/a'],
|
||||
['SLA recovery', item.sla_recovery_recommendation?.label || 'n/a'],
|
||||
['SLA recovery prompt', item.sla_recovery_recommendation?.prompt || 'n/a'],
|
||||
['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}`))],
|
||||
@@ -909,6 +920,10 @@ function renderDecisionAssistant(summary) {
|
||||
['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'],
|
||||
['audit lane recommendations', String(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.template_audit_lane_recommendation_count ?? 0)],
|
||||
['next audit lane prompt', reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.next_shortcut_outcome_prompt || 'n/a'],
|
||||
['shortcut telemetry', String(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.shortcut_outcome_telemetry_count ?? 0)],
|
||||
['shortcut telemetry states', formatJsonInline(reviewEntrypoints.template_reuse_metrics?.template_feedback_aggregation?.shortcut_outcome_status_counts || {})],
|
||||
['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)],
|
||||
@@ -976,7 +991,10 @@ function renderDecisionAssistant(summary) {
|
||||
['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')],
|
||||
['audit lane recommendation', item.audit_lane_recommendation?.action || 'n/a'],
|
||||
['audit lane prompt', item.audit_lane_recommendation?.prompt || 'n/a'],
|
||||
['recovery shortcuts', formatList((item.recovery_action_shortcuts || []).map((row) => row.label || row.action_id))],
|
||||
['shortcut telemetry', formatList((item.shortcut_outcome_telemetry || []).map((row) => `${row.label || row.action_id}: ${row.telemetry_status}`))],
|
||||
['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}`))],
|
||||
@@ -1297,6 +1315,11 @@ function renderDecisionAssistant(summary) {
|
||||
['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'],
|
||||
['evidence aging', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_evidence_aging_status || 'n/a'],
|
||||
['evidence aging prompt', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_evidence_aging_prompt || 'n/a'],
|
||||
['evidence aging queue', formatList((setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_evidence_aging_queue || []).map((item) => `${item.evidence_type}: ${item.aging_status}`))],
|
||||
['backlog comparison', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_backlog_comparison_status || 'n/a'],
|
||||
['backlog comparison summary', setupLearningRollup.replay_statistics?.broader_baseline_summary?.setup_family_trend_delta?.promotion_backlog_comparison_summary?.summary || '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')],
|
||||
|
||||
@@ -218,6 +218,12 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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.assertIn("template_audit_lane_recommendations", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
|
||||
self.assertIn("template_audit_lane_recommendation_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
|
||||
self.assertIn("shortcut_outcome_telemetry", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
|
||||
self.assertIn("shortcut_outcome_telemetry_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
|
||||
self.assertIn("shortcut_outcome_status_counts", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
|
||||
self.assertIn("next_shortcut_outcome_prompt", 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")
|
||||
@@ -239,6 +245,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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("audit_lane_recommendation", summary["review_entrypoints"]["note_templates"][0])
|
||||
self.assertIn("shortcut_outcome_telemetry", summary["review_entrypoints"]["note_templates"][0])
|
||||
self.assertIn("shortcut_outcome_telemetry_count", 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])
|
||||
@@ -284,6 +293,11 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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("freshness_debt_trend_counts", summary["digest_surface"]["history_summary"])
|
||||
self.assertIn("sla_recovery_recommendations", summary["digest_surface"]["history_summary"])
|
||||
self.assertIn("sla_recovery_recommendation_count", summary["digest_surface"]["history_summary"])
|
||||
self.assertIn("next_sla_recovery_recommendation", summary["digest_surface"]["history_summary"])
|
||||
self.assertIn("next_sla_recovery_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])
|
||||
@@ -315,6 +329,8 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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("freshness_debt_trend", summary["digest_history_groups"][0])
|
||||
self.assertIn("sla_recovery_recommendation", 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])
|
||||
@@ -374,6 +390,11 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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("promotion_evidence_aging_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
|
||||
self.assertIn("promotion_evidence_aging_prompt", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
|
||||
self.assertIn("promotion_evidence_aging_queue", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
|
||||
self.assertIn("promotion_backlog_comparison_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
|
||||
self.assertIn("promotion_backlog_comparison_summary", 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"])
|
||||
@@ -430,6 +451,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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("cleanup_execution_log", summary["scan_handoff_summary"])
|
||||
self.assertIn("cleanup_execution_log_count", summary["scan_handoff_summary"])
|
||||
self.assertIn("grouped_resume_handoff_summary", summary["scan_handoff_summary"])
|
||||
self.assertIn("next_cleanup_execution_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])
|
||||
@@ -451,6 +476,8 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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.assertIn("cleanup_execution_log", summary["scan_handoff_summary"]["handoff_markers"][0])
|
||||
self.assertIn("cleanup_execution_log_count", 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")
|
||||
|
||||
Reference in New Issue
Block a user