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
71 changes: 71 additions & 0 deletions ops/dashboard/src/nanobot_ops_dashboard/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,76 @@ def _promotion_replay_readiness_from_promotions(promotions: list[dict] | None) -
return {'schema_version': 'promotion-replay-readiness-v1', 'state': 'ready', 'reason': 'no_blocked_promotions'}


def _promotion_source_commit_blocker_resolved(promotion_readiness: dict | None) -> bool:
if not isinstance(promotion_readiness, dict):
return False
checks = promotion_readiness.get('readiness_checks') if isinstance(promotion_readiness.get('readiness_checks'), dict) else {}
missing_inputs = checks.get('missing_inputs') if isinstance(checks.get('missing_inputs'), list) else []
readiness_reasons = promotion_readiness.get('readiness_reasons') if isinstance(promotion_readiness.get('readiness_reasons'), list) else []
return bool(
checks.get('provenance_complete') is True
and 'source_commit' not in {str(item) for item in missing_inputs}
and 'source_commit_missing' not in {str(item) for item in readiness_reasons}
)


def _source_commit_blocker(value: dict | None) -> bool:
if not isinstance(value, dict):
return False
markers = {
value.get('failure_class'),
value.get('reason'),
value.get('blocked_next_step'),
value.get('recommended_next_action'),
}
return any(str(item) in {'source_commit_missing', 'supply_source_commit_or_policy_override'} for item in markers if item is not None)


def _demote_resolved_source_commit_blocker(current_blocker: dict | None, control_plane: dict | None, promotion_readiness: dict | None) -> tuple[dict | None, dict | None]:
if not _promotion_source_commit_blocker_resolved(promotion_readiness):
return current_blocker, control_plane
next_action = promotion_readiness.get('recommended_next_action') or 'review_promotion_candidate'
blocker = dict(current_blocker) if isinstance(current_blocker, dict) else current_blocker
if _source_commit_blocker(blocker):
blocker = dict(blocker)
blocker.update({
'kind': 'unknown',
'failure_class': None,
'reason': 'none',
'blocked_next_step': next_action,
'recommended_next_action': next_action,
'resolved_stale_blocker': 'source_commit_missing',
'resolution_source': 'promotion_replay_readiness',
})
plane = dict(control_plane) if isinstance(control_plane, dict) else control_plane
if isinstance(plane, dict):
control_blocker = plane.get('current_blocker')
if _source_commit_blocker(control_blocker):
control_blocker = dict(control_blocker)
control_blocker.update({
'kind': 'unknown',
'failure_class': None,
'reason': 'none',
'blocked_next_step': next_action,
'recommended_next_action': next_action,
'resolved_stale_blocker': 'source_commit_missing',
'resolution_source': 'promotion_replay_readiness',
})
plane['current_blocker'] = control_blocker
blocker_summary = plane.get('blocker_summary')
if _source_commit_blocker(blocker_summary):
blocker_summary = dict(blocker_summary)
blocker_summary.update({
'state': 'clear',
'reason': 'none',
'recommended_next_action': next_action,
'resolved_stale_blocker': 'source_commit_missing',
'resolution_source': 'promotion_replay_readiness',
})
plane['blocker_summary'] = blocker_summary
return blocker, plane


def _env(cfg: DashboardConfig) -> Environment:
templates = cfg.project_root / 'src' / 'nanobot_ops_dashboard' / 'templates'
return Environment(
Expand Down Expand Up @@ -3915,6 +3985,7 @@ def app(environ, start_response):
)
if promotion_replay_readiness is not None:
control_plane['promotion_replay_readiness'] = promotion_replay_readiness
current_blocker, control_plane = _demote_resolved_source_commit_blocker(current_blocker, control_plane, promotion_replay_readiness)
overview_subagent_cycle_id = None
if subagent_latest_event and isinstance(subagent_latest_event.get('detail'), dict):
detail = subagent_latest_event['detail']
Expand Down
61 changes: 61 additions & 0 deletions ops/dashboard/tests/test_dashboard_truth_audit_gaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3022,6 +3022,67 @@ def test_api_system_hydrates_unknown_blocker_from_autonomy_verdict(tmp_path: Pat
assert control['blocker_summary']['source'] == 'autonomy_verdict'


def test_api_demotes_stale_source_commit_blocker_when_promotion_provenance_is_complete(tmp_path: Path) -> None:
project_root = tmp_path / 'dashboard'
repo_root = tmp_path / 'nanobot'
db = tmp_path / 'dashboard.sqlite3'
init_db(db)
eeepc_raw = {
'outbox': {
'process_reflection': {
'failure_class': 'source_commit_missing',
'improvement_score': None,
},
'goal': {
'follow_through': {
'blocked_next_step': 'supply_source_commit_or_policy_override',
},
},
},
'reachability': {'reachable': True},
}
insert_collection(db, {
'collected_at': '2999-05-02T11:07:00Z',
'source': 'eeepc',
'status': 'PASS',
'active_goal': 'goal-bootstrap',
'current_task': 'Synthesize one new bounded improvement candidate from retired lanes',
'raw_json': json.dumps(eeepc_raw),
})
upsert_event(db, {
'collected_at': '2999-05-02T11:07:00Z',
'source': 'eeepc',
'event_type': 'promotion',
'identity_key': 'promotion-provenance-complete',
'title': 'promotion-provenance-complete | not_ready_for_policy_review | not_ready_for_policy_review',
'status': 'not_ready_for_policy_review',
'detail_json': json.dumps({
'candidate_path': '/var/lib/eeepc-agent/self-evolving-agent/state/promotions/promotion-provenance-complete.json',
'artifact_path': '/var/lib/eeepc-agent/self-evolving-agent/state/improvements/materialized-cycle.json',
'decision_record': 'blocked_not_ready',
'accepted_record': 'not_created_not_ready',
'readiness_checks': {'schema_version': 'promotion-readiness-inputs-v1', 'artifact_present': True, 'evidence_refs_present': True, 'provenance_complete': True, 'missing_inputs': []},
'readiness_reasons': [],
'recommended_next_action': 'ready_for_policy_review',
'governance_packet': {'review_packet_status': 'blocked_not_ready', 'review_status': 'not_ready_for_policy_review', 'decision': 'not_ready_for_policy_review'},
}),
})
cfg = DashboardConfig(project_root=project_root, nanobot_repo_root=repo_root, db_path=db, eeepc_ssh_host='eeepc', eeepc_ssh_key=tmp_path / 'missing-key', eeepc_state_root='/var/lib/eeepc-agent/self-evolving-agent/state')
app = create_app(cfg)

system = _call_json(app, '/api/system')
mission = _call_json(app, '/api/mission-control')

readiness = system['control_plane']['promotion_replay_readiness']
assert readiness['readiness_checks']['provenance_complete'] is True
assert readiness['readiness_checks']['missing_inputs'] == []
assert readiness['readiness_reasons'] == []
assert system['control_plane']['current_blocker'].get('failure_class') != 'source_commit_missing'
assert mission['current_blocker']['reason'] != 'source_commit_missing'
assert 'source_commit_missing' not in mission['headline']



def test_subagent_visibility_preserves_generation_scoped_identity(tmp_path: Path):
repo = tmp_path / 'repo'
state = repo / 'workspace' / 'state'
Expand Down
Loading