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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions ops/dashboard/src/nanobot_ops_dashboard/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,32 @@ def _promotion_replay_readiness_from_promotions(promotions: list[dict] | None) -
readiness_checks = detail.get('readiness_checks') or detail.get('readinessChecks')
readiness_reasons = detail.get('readiness_reasons') or detail.get('readinessReasons') or []
missing_records = [name for name, value in {'decision_record': decision_record, 'accepted_record': accepted_record}.items() if _missing_record(value)]
accepted_lifecycle = (
row.get('lifecycle_phase') == 'accepted'
or row.get('status') == 'accept'
or review_packet_status == 'accepted'
or decision == 'accept'
)
if accepted_lifecycle and not _missing_record(decision_record) and not _missing_record(accepted_record):
return {
'schema_version': 'promotion-replay-readiness-v1',
'state': 'accepted',
'reason': 'promotion_candidate_accepted',
'promotion_id': row.get('identity_key') or row.get('title'),
'status': row.get('status'),
'review_status': review_status,
'decision': decision,
'review_packet_status': review_packet_status or 'accepted',
'decision_record': decision_record,
'accepted_record': accepted_record,
'missing_records': [],
'readiness_checks': readiness_checks,
'readiness_reasons': readiness_reasons,
'recommended_next_action': detail.get('recommended_next_action'),
'candidate_path': detail.get('candidate_path'),
'artifact_path': detail.get('artifact_path'),
'collected_at': row.get('collected_at'),
}
ready_for_policy_review = (
review_status == 'ready_for_policy_review'
or decision == 'ready_for_policy_review'
Expand Down
42 changes: 41 additions & 1 deletion ops/dashboard/tests/test_dashboard_truth_audit_gaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from wsgiref.util import setup_testing_defaults

from nanobot_ops_dashboard import app as dashboard_app
from nanobot_ops_dashboard.app import create_app, _dashboard_runtime_parity, _selected_hypothesis_terminal_evidence, _material_progress_summary, _approval_snapshot, _autonomy_verdict, _ambition_utilization_verdict, _experiment_snapshot_from_payload, _discover_subagent_requests
from nanobot_ops_dashboard.app import create_app, _dashboard_runtime_parity, _selected_hypothesis_terminal_evidence, _material_progress_summary, _approval_snapshot, _autonomy_verdict, _ambition_utilization_verdict, _experiment_snapshot_from_payload, _discover_subagent_requests, _promotion_replay_readiness_from_promotions
from nanobot_ops_dashboard.config import DashboardConfig
from nanobot_ops_dashboard.storage import init_db, insert_collection, upsert_event

Expand Down Expand Up @@ -76,6 +76,46 @@ def _seed_hypotheses_backlog(repo_root: Path, *, entry_count: int, selected_id:
return backlog


def test_promotion_replay_readiness_treats_accepted_promotion_as_resolved() -> None:
readiness = _promotion_replay_readiness_from_promotions([
{
'identity_key': 'promotion-a4c72dd77e02',
'status': 'accept',
'lifecycle_phase': 'accepted',
'replay_readiness': 'blocked',
'source': 'eeepc',
'collected_at': '2026-05-02T17:03:18.133759Z',
'detail': {
'candidate_path': '/var/lib/eeepc-agent/self-evolving-agent/state/promotions/promotion-a4c72dd77e02.json',
'artifact_path': '/var/lib/eeepc-agent/self-evolving-agent/state/improvements/materialized-cycle-bcb6c4e2203a.json',
'decision_record': '/var/lib/eeepc-agent/self-evolving-agent/state/promotions/decisions/promotion-a4c72dd77e02.json',
'accepted_record': '/var/lib/eeepc-agent/self-evolving-agent/state/promotions/accepted/promotion-a4c72dd77e02.json',
'readiness_blocker': None,
'recommended_next_action': None,
'governance_packet': {
'review_packet_status': 'accepted',
'review_status': 'reviewed',
'decision': 'accept',
'decision_record': '/var/lib/eeepc-agent/self-evolving-agent/state/promotions/decisions/promotion-a4c72dd77e02.json',
'accepted_record': '/var/lib/eeepc-agent/self-evolving-agent/state/promotions/accepted/promotion-a4c72dd77e02.json',
},
},
}
])

assert readiness is not None
assert readiness['promotion_id'] == 'promotion-a4c72dd77e02'
assert readiness['state'] == 'accepted'
assert readiness['reason'] == 'promotion_candidate_accepted'
assert readiness['status'] == 'accept'
assert readiness['review_packet_status'] == 'accepted'
assert readiness['decision'] == 'accept'
assert readiness['decision_record'].endswith('/decisions/promotion-a4c72dd77e02.json')
assert readiness['accepted_record'].endswith('/accepted/promotion-a4c72dd77e02.json')
assert readiness['missing_records'] == []
assert readiness['recommended_next_action'] is None


def test_material_progress_compacts_recursive_selfevo_lifecycle_evidence() -> None:
recursive_issue = {
'number': 82,
Expand Down
Loading