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
37 changes: 37 additions & 0 deletions src/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2218,13 +2218,50 @@ def _enrich_control_center_snapshot_from_report(
snapshot: dict,
args,
) -> dict:
from src.portfolio_catalog import (
DEFAULT_CATALOG_PATH,
build_catalog_line,
catalog_entry_for_repo,
evaluate_intent_alignment,
load_portfolio_catalog,
)
from src.report_enrichment import build_operator_focus

report = _report_from_dict(
{
**report_data,
"operator_summary": snapshot.get("operator_summary", {}),
"operator_queue": snapshot.get("operator_queue", []),
}
)
catalog_path = getattr(args, "catalog", None) or DEFAULT_CATALOG_PATH
catalog_data = load_portfolio_catalog(Path(catalog_path))
queue_by_repo = {
str(item.get("repo") or item.get("repo_name") or "").strip(): item
for item in report.operator_queue
if str(item.get("repo") or item.get("repo_name") or "").strip()
}
for audit in report.audits:
if (audit.portfolio_catalog or {}).get("has_explicit_entry"):
continue
base_entry = catalog_entry_for_repo(audit.metadata.to_dict(), catalog_data)
if not base_entry.get("has_explicit_entry"):
continue
operator_focus = build_operator_focus(queue_by_repo.get(audit.metadata.name, {}))
intent_alignment, intent_alignment_reason = evaluate_intent_alignment(
base_entry,
completeness_tier=audit.completeness_tier,
archived=audit.metadata.archived,
operator_focus=operator_focus,
)
audit.portfolio_catalog = {
**base_entry,
"catalog_line": build_catalog_line(base_entry),
"intent_alignment": intent_alignment,
"intent_alignment_reason": intent_alignment_reason,
"intent_alignment_line": f"{intent_alignment}: {intent_alignment_reason}",
"operator_focus": operator_focus,
}
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:
Expand Down
15 changes: 15 additions & 0 deletions src/portfolio_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,21 @@ def catalog_entry_for_repo(
"matched_by": "bare-name",
}

basename_matches = [
entry
for key, entry in repos.items()
if "/" in key and key.rsplit("/", 1)[-1] == normalized_repo_name
]
if normalized_repo_name and len(basename_matches) == 1:
Comment on lines +373 to +375

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not basename-match when a workspace path is present

When callers already provide metadata['path'] and that exact path failed to match, this basename fallback can attach a different path-qualified catalog contract to the project. portfolio_truth_reconcile._build_truth_project passes the real relative path into catalog_entry_for_repo (src/portfolio_truth_reconcile.py:433-440), so a project at Other/StatusPage would inherit MoneyPRJsViaGPT/StatusPage if that is the only catalog entry with the same basename, corrupting lifecycle/disposition in the canonical portfolio truth. Please restrict this fallback to truly pathless metadata, or otherwise require the provided path to be compatible before using a basename match.

Useful? React with 👍 / 👎.

return {
**basename_matches[0],
"repo": repo_name,
"repo_full_name": full_name,
"catalog_default_maturity_program": default_program,
"catalog_default_target_maturity": default_target,
"matched_by": "path-basename",
}

return {
"repo": repo_name,
"repo_full_name": full_name,
Expand Down
57 changes: 57 additions & 0 deletions tests/test_cli_hardening.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,63 @@ def test_control_center_snapshot_rehydrates_portfolio_context(
assert item["scorecard"]["status"] == "on-track"


def test_control_center_snapshot_repairs_missing_path_basename_catalog_context(
tmp_path,
sample_metadata,
) -> None:
catalog_path = tmp_path / "portfolio-catalog.yaml"
catalog_path.write_text(
"""
repos:
_machine/test-repo:
owner: d
lifecycle_state: active
criticality: high
review_cadence: weekly
intended_disposition: maintain
maturity_program: maintain
target_maturity: operating
category: infrastructure
"""
)
audit = RepoAudit(
metadata=sample_metadata,
analyzer_results=[AnalyzerResult("testing", 0.7, 1.0, [], {})],
overall_score=0.7,
completeness_tier="functional",
flags=[],
lenses={"ship_readiness": {"score": 0.7}},
security_posture={"score": 0.8},
portfolio_catalog={
"has_explicit_entry": False,
"catalog_line": "No portfolio catalog contract is recorded yet.",
"intent_alignment": "missing-contract",
},
)
report = AuditReport.from_audits("testuser", [audit], [], 1)
report.operator_queue = [
{
"repo": "test-repo",
"lane": "urgent",
"title": "Review test-repo",
"portfolio_catalog": dict(audit.portfolio_catalog),
}
]
snapshot = {"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),
)

item = updated["operator_queue"][0]
assert item["portfolio_catalog"]["catalog_key"] == "_machine/test-repo"
assert item["portfolio_catalog"]["matched_by"] == "path-basename"
assert item["portfolio_catalog"]["has_explicit_entry"] is True
assert item["intent_alignment"] != "missing-contract"


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
48 changes: 48 additions & 0 deletions tests/test_portfolio_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,54 @@ def test_catalog_entry_matches_path_before_full_name_and_bare_name():
assert entry["matched_by"] == "path"


def test_catalog_entry_matches_unambiguous_path_basename():
catalog = {
"repos": {
"_machine/machine-control-tower": {
"owner": "d",
"catalog_key": "_machine/machine-control-tower",
"matched_by": "full-name",
"has_explicit_entry": True,
},
}
}

entry = catalog_entry_for_repo(
{"name": "machine-control-tower", "full_name": "user/machine-control-tower"},
catalog,
)

assert entry["owner"] == "d"
assert entry["matched_by"] == "path-basename"


def test_catalog_entry_does_not_guess_ambiguous_path_basename():
catalog = {
"repos": {
"alpha/shared": {
"owner": "alpha",
"catalog_key": "alpha/shared",
"matched_by": "full-name",
"has_explicit_entry": True,
},
"beta/shared": {
"owner": "beta",
"catalog_key": "beta/shared",
"matched_by": "full-name",
"has_explicit_entry": True,
},
}
}

entry = catalog_entry_for_repo(
{"name": "shared", "full_name": "user/shared"},
catalog,
)

assert entry["has_explicit_entry"] is False
assert entry["matched_by"] == ""


def test_group_entry_matches_path_prefix():
catalog = {
"groups": {
Expand Down