From e4b7ba586671005f86b3bfe39552cbfad5b9a6e6 Mon Sep 17 00:00:00 2001 From: saagpatel Date: Wed, 5 Aug 2026 02:38:45 -0700 Subject: [PATCH] fix(portfolio): canonicalize collision warnings --- src/portfolio_truth_reconcile.py | 5 +++- tests/test_portfolio_truth.py | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/portfolio_truth_reconcile.py b/src/portfolio_truth_reconcile.py index 5ca7db6..763b478 100644 --- a/src/portfolio_truth_reconcile.py +++ b/src/portfolio_truth_reconcile.py @@ -509,7 +509,10 @@ def materialize_projects( catalog_errors=source_summary["catalog_errors"], catalog_warnings=source_summary["catalog_warnings"], unresolved_duplicates=source_summary["unresolved_duplicate_display_names"], - checkout_collisions=checkout_collisions, + # The summary owns the canonical origin ordering. Building the warning + # from discovery order makes the snapshot disagree with its own stored + # facts whenever filesystem order differs from origin order. + checkout_collisions=source_summary["checkout_collisions"]["groups"], ) snapshot = PortfolioTruthSnapshot( diff --git a/tests/test_portfolio_truth.py b/tests/test_portfolio_truth.py index 33ebcd9..94f0845 100644 --- a/tests/test_portfolio_truth.py +++ b/tests/test_portfolio_truth.py @@ -771,6 +771,55 @@ def test_current_prior_truth_failure_cannot_use_legacy_fallback( canonicalize_prior_security_truth_payload(current) +def test_collision_warning_uses_canonical_summary_order( + portfolio_workspace: Path, + portfolio_catalog: Path, + legacy_registry: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + collisions = [ + { + "origin": "owner/Zeta", + "selection": {"state": "unknown"}, + "full_clone_count": 1, + "discarded_checkouts": [], + }, + { + "origin": "owner/Alpha", + "selection": {"state": "unknown"}, + "full_clone_count": 1, + "discarded_checkouts": [], + }, + ] + + def discover_with_unsorted_collisions( + _workspace_root: Path, **kwargs: object + ) -> list[dict[str, object]]: + observed = kwargs["checkout_collisions"] + assert isinstance(observed, list) + observed.extend(collisions) + return [] + + monkeypatch.setattr( + "src.portfolio_truth_reconcile.discover_workspace_projects", + discover_with_unsorted_collisions, + ) + result = build_portfolio_truth_snapshot( + workspace_root=portfolio_workspace, + catalog_path=portfolio_catalog, + legacy_registry_path=legacy_registry, + include_notion=False, + now=datetime(2026, 8, 5, tzinfo=timezone.utc), + ) + + groups = result.snapshot.source_summary["checkout_collisions"]["groups"] + assert [group["origin"] for group in groups] == ["owner/Alpha", "owner/Zeta"] + assert result.snapshot.warnings[-1] == ( + "Checkout authority is UNKNOWN for same-origin checkout groups: " + "owner/Alpha, owner/Zeta" + ) + + def test_truth_snapshot_respects_declared_and_derived_fields( portfolio_workspace: Path, portfolio_catalog: Path,