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
35 changes: 35 additions & 0 deletions src/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2213,6 +2213,37 @@ def _write_control_center_artifacts(
return json_path, md_path, weekly_json, weekly_md, payload


def _enrich_control_center_snapshot_from_report(
report_data: dict,
snapshot: dict,
args,
) -> dict:
report = _report_from_dict(
{
**report_data,
"operator_summary": snapshot.get("operator_summary", {}),
"operator_queue": snapshot.get("operator_queue", []),
}
)
if any(audit.portfolio_catalog for audit in report.audits):
audit_lookup = {audit.metadata.name: audit.portfolio_catalog for audit in report.audits}
for item in report.operator_queue:
repo_name = str(item.get("repo") or item.get("repo_name") or "").strip()
catalog_entry = audit_lookup.get(repo_name, {})
if catalog_entry:
item["portfolio_catalog"] = dict(catalog_entry)
item["catalog_line"] = catalog_entry.get("catalog_line", "")
item["intent_alignment"] = catalog_entry.get("intent_alignment", "missing-contract")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recompute intent alignment for refreshed queues

When standalone --control-center rebuilds a snapshot whose queue differs from the stored report queue (for example a lane-filtered view or local acknowledgment/approval state), build_operator_snapshot has already attached intent alignment from the current queue item, but this line overwrites it with the audit row's older intent_alignment. A maintain/finish repo that is now urgent or blocked can therefore keep an aligned value, and _apply_operating_paths below will treat the path as healthier than the refreshed operator evidence supports.

Useful? React with 👍 / 👎.

item["intent_alignment_reason"] = catalog_entry.get("intent_alignment_reason", "")
else:
report = _apply_portfolio_catalog(report, args)
report = _apply_scorecards(report, args)
report = _apply_operating_paths(report)
snapshot["operator_summary"] = report.operator_summary
snapshot["operator_queue"] = report.operator_queue
return snapshot


def _write_approval_receipt(
output_dir: Path,
username: str,
Expand Down Expand Up @@ -2385,6 +2416,7 @@ def _run_control_center_mode(args, parser) -> None:
output_dir=output_dir,
triage_view=args.triage_view,
)
snapshot = _enrich_control_center_snapshot_from_report(normalized, snapshot, args)
artifact_generated_at = _report_artifact_datetime(
report_path,
_parse_iso_dt(normalized.get("generated_at")) or datetime.now(timezone.utc),
Expand Down Expand Up @@ -2584,6 +2616,7 @@ def _run_acknowledgment_capture_mode(args, parser) -> None:
build_acknowledgment_record,
find_matching_change,
find_sibling_changes,
load_acknowledgments,
save_acknowledgment,
)
from src.recurring_review import MATERIALITY_THRESHOLDS, evaluate_material_changes
Expand Down Expand Up @@ -2615,10 +2648,12 @@ def _run_acknowledgment_capture_mode(args, parser) -> None:
diff_data=diff_dict,
thresholds=MATERIALITY_THRESHOLDS["standard"],
)
acknowledgments = load_acknowledgments(output_dir, args.username)
matched = find_matching_change(
repo_name=args.acknowledge_target,
change_kind=args.acknowledge_kind,
material_changes=material_changes,
acknowledgments=acknowledgments,
)
if not matched:
parser.error(
Expand Down
3 changes: 3 additions & 0 deletions src/operator_acknowledgments.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,15 @@ def find_matching_change(
repo_name: str,
change_kind: str,
material_changes: list[dict],
acknowledgments: list[dict] | None = None,
) -> dict | None:
for change in material_changes:
if change.get("change_type") != change_kind:
continue
if change.get("repo_name") != repo_name:
continue
if is_change_acknowledged(change, acknowledgments or []):
continue
return change
return None

Expand Down
85 changes: 85 additions & 0 deletions tests/test_cli_hardening.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,91 @@ def test_apply_scorecards_populates_report(monkeypatch, sample_metadata, tmp_pat
assert updated.operator_queue[0]["scorecard_line"].startswith("Scorecard: Maintain")


def test_control_center_snapshot_rehydrates_portfolio_context(
tmp_path,
sample_metadata,
) -> None:
catalog_path = tmp_path / "portfolio-catalog.yaml"
catalog_path.write_text(
"""
repos:
test-repo:
owner: d
lifecycle_state: active
review_cadence: monthly
intended_disposition: maintain
maturity_program: maintain
target_maturity: operating
"""
)
scorecards_path = tmp_path / "scorecards.yaml"
scorecards_path.write_text(
"""
programs:
maintain:
label: Maintain
target_maturity: operating
rules:
- key: testing
label: Testing
check: dimension_at_least
dimension: testing
threshold: 0.80
partial_threshold: 0.60
weight: 1.0
"""
)
audit = RepoAudit(
metadata=sample_metadata,
analyzer_results=[AnalyzerResult("testing", 1.0, 1.0, [], {})],
overall_score=0.9,
completeness_tier="shipped",
flags=[],
lenses={"ship_readiness": {"score": 0.9}},
security_posture={"score": 1.0},
portfolio_catalog={
"has_explicit_entry": True,
"intended_disposition": "maintain",
"maturity_program": "maintain",
"target_maturity": "operating",
"operating_path": "maintain",
"path_confidence": "high",
"intent_alignment": "aligned",
"intent_alignment_reason": "The repo is holding a maintain posture.",
"catalog_line": "d | lifecycle active | disposition maintain | program maintain",
},
)
report = AuditReport.from_audits("testuser", [audit], [], 1)
report.operator_summary = {"headline": "Review current movement."}
report.operator_queue = [
{
"repo": "test-repo",
"lane": "urgent",
"title": "Review test-repo",
"portfolio_catalog": {
"path_confidence": "low",
"intent_alignment": "needs-review",
},
}
]
snapshot = {
"operator_summary": dict(report.operator_summary),
"operator_queue": [dict(report.operator_queue[0])],
}

updated = cli._enrich_control_center_snapshot_from_report(
report.to_dict(),
snapshot,
_make_args(catalog=catalog_path, scorecards=scorecards_path),
)

item = updated["operator_queue"][0]
assert item["portfolio_catalog"]["intent_alignment"] == "aligned"
assert item["portfolio_catalog"]["path_confidence"] == "high"
assert item["portfolio_catalog"]["operating_path"] == "maintain"
assert item["scorecard"]["status"] == "on-track"


def test_main_doctor_writes_artifact_and_exits_cleanly(monkeypatch, tmp_path, capsys):
args = _make_args(doctor=True, output_dir=str(tmp_path))
artifact_path = tmp_path / "diagnostics-testuser-2026-03-29.json"
Expand Down
26 changes: 26 additions & 0 deletions tests/test_operator_acknowledgments.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,32 @@ def test_is_change_acknowledged_rejects_unrelated_change():
assert is_change_acknowledged(other, [ack]) is False


def test_find_matching_change_skips_already_acknowledged_change():
first = _make_change(
change_type="lens-delta",
title="RepoA shifted on maintenance risk",
details={"lens": "maintenance_risk", "delta": -0.1},
)
second = {
**_make_change(
change_type="lens-delta",
title="RepoA shifted on momentum",
details={"lens": "momentum", "delta": 0.1},
),
"change_key": "key-lens-delta-RepoA-momentum",
}
ack = build_acknowledgment_record(first, reviewer="alice", note="reviewed")

match = find_matching_change(
repo_name="RepoA",
change_kind="lens-delta",
material_changes=[first, second],
acknowledgments=[ack],
)

assert match == second


def test_is_change_acknowledged_handles_empty_acknowledgments():
change = _make_change()
assert is_change_acknowledged(change, []) is False
Expand Down