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
20 changes: 14 additions & 6 deletions src/portfolio_context_triage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# src/portfolio_context_triage.py
"""Context triage runner — B1 of Arc H operational stream."""

from __future__ import annotations

from dataclasses import dataclass
Expand All @@ -17,6 +18,9 @@ class FailureMode(str, Enum):
_DESCRIPTION_CONFIDENCE_WARN_BELOW = 0.5
_CATALOG_COMPLETENESS_WARN_BELOW = 0.6
_WEAK_CONTEXT_QUALITIES = {"none", "boilerplate"}
# Context quality measures "can someone resume this?" — irrelevant for repos
# that are intentionally not under active development, so don't flag them.
_CONTEXT_EXEMPT_LIFECYCLES = {"archived", "dormant"}


def assess_repo_failure_modes(repo: dict[str, Any]) -> list[FailureMode]:
Expand Down Expand Up @@ -45,12 +49,20 @@ def assess_repo_failure_modes(repo: dict[str, Any]) -> list[FailureMode]:
modes.append(FailureMode.CATALOG)

context_quality = _repo_context_quality(repo)
if context_quality in _WEAK_CONTEXT_QUALITIES:
if (
context_quality in _WEAK_CONTEXT_QUALITIES
and _repo_lifecycle_state(repo) not in _CONTEXT_EXEMPT_LIFECYCLES
):
modes.append(FailureMode.CONTEXT)

return modes


def _repo_lifecycle_state(repo: dict[str, Any]) -> str:
declared = repo.get("declared") if isinstance(repo.get("declared"), dict) else {}
return str(declared.get("lifecycle_state") or "").strip().lower()


@dataclass
class TriageEntry:
repo_name: str
Expand Down Expand Up @@ -95,8 +107,4 @@ def _repo_context_quality(repo: dict[str, Any]) -> str:

def run_triage(repos: list[dict[str, Any]]) -> list[TriageEntry]:
"""Return TriageEntry for every repo that has at least one failure mode."""
return [
TriageEntry.from_repo(repo)
for repo in repos
if assess_repo_failure_modes(repo)
]
return [TriageEntry.from_repo(repo) for repo in repos if assess_repo_failure_modes(repo)]
42 changes: 42 additions & 0 deletions tests/test_portfolio_context_triage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# tests/test_portfolio_context_triage.py
"""Tests for the context triage runner (Arc H B1)."""

import json

from src.portfolio_context_triage import (
Expand All @@ -15,6 +16,7 @@ def _entry(
readme_stale_by_age=False,
catalog_completeness=1.0,
context_quality="full",
lifecycle_state="active",
) -> dict:
return {
"name": "test-repo",
Expand All @@ -24,6 +26,7 @@ def _entry(
},
"catalog_completeness": catalog_completeness,
"context_quality": context_quality,
"declared": {"lifecycle_state": lifecycle_state},
}


Expand Down Expand Up @@ -62,6 +65,45 @@ def test_nested_portfolio_truth_context_quality_flagged():
assert FailureMode.CONTEXT in modes


# Context quality is a "can someone resume this?" signal — irrelevant for repos
# that are intentionally not under active development. Archived and dormant
# repos must not be context-flagged even when their docs are boilerplate.


def test_archived_repo_not_context_flagged():
modes = assess_repo_failure_modes(
_entry(context_quality="boilerplate", lifecycle_state="archived")
)
assert FailureMode.CONTEXT not in modes


def test_dormant_repo_not_context_flagged():
modes = assess_repo_failure_modes(_entry(context_quality="none", lifecycle_state="dormant"))
assert FailureMode.CONTEXT not in modes


def test_active_repo_with_weak_context_still_flagged():
modes = assess_repo_failure_modes(
_entry(context_quality="boilerplate", lifecycle_state="active")
)
assert FailureMode.CONTEXT in modes


def test_maintenance_repo_with_weak_context_still_flagged():
modes = assess_repo_failure_modes(
_entry(context_quality="boilerplate", lifecycle_state="maintenance")
)
assert FailureMode.CONTEXT in modes


def test_missing_lifecycle_state_defaults_to_flagged():
# no declared.lifecycle_state -> enforce context quality (do not suppress)
modes = assess_repo_failure_modes(
{"identity": {"display_name": "RepoB"}, "context_quality": "none"}
)
assert FailureMode.CONTEXT in modes


def test_severity_critical_when_multiple_failure_modes():
entry = _entry(
description_confidence=0.2,
Expand Down