diff --git a/src/repo_registry/web_ui/views.py b/src/repo_registry/web_ui/views.py index a7c6b52..3e5f3e4 100644 --- a/src/repo_registry/web_ui/views.py +++ b/src/repo_registry/web_ui/views.py @@ -2232,6 +2232,7 @@ def filter_element_rows( str(row.get("target_id", "")), str(row.get("reference_kind", "")), str(row.get("reference_id", "")), + support_orientation_label(row), source_refs_text(row["source_refs"]), ] ).lower() @@ -2291,11 +2292,45 @@ def render_element_source_detail(row: dict) -> str: return ( f'

supports {target}{f" #{target_id}" if target_id else ""}' f' references {reference_kind}{f" #{reference_id}" if reference_id else ""}

' + f'

{render_support_orientation(row)}

' f'{render_sources(row["source_refs"])}' ) return render_sources(row["source_refs"]) +def support_orientation_label(row: dict) -> str: + if row.get("item_kind") != "evidence": + return "" + target_kind = str(row.get("target_kind") or "capability") + reference_kind = str(row.get("reference_kind") or "source") + levels = { + "scope": 0, + "ability": 1, + "capability": 2, + "feature": 3, + "fact": 4, + "source": 4, + "content": 4, + "chunk": 4, + } + target_level = levels.get(target_kind) + reference_level = levels.get(reference_kind) + if target_level is None or reference_level is None: + return "unclassified support" + if reference_level > target_level: + return "downward support" + if reference_level == target_level: + return "same-level support review" + return "upward support review" + + +def render_support_orientation(row: dict) -> str: + label = support_orientation_label(row) + if not label: + return "" + return f'{escape(label)}' + + def render_element_actions( row: dict, repository_id: int, @@ -3020,6 +3055,7 @@ def render_approved_evidence(evidence: dict, repository_id: int) -> str: {escape(evidence["strength"])} supports {target_kind}{f' #{target_id}' if target_id else ''} references {reference_kind}{f' #{reference_id}' if reference_id else ''} + {render_support_orientation(evidence | {"item_kind": "evidence"})} {escape(evidence["reference"])} {render_sources(evidence.get("source_refs", []))}
diff --git a/tests/test_web_api.py b/tests/test_web_api.py index bd99eca..2ac55b7 100644 --- a/tests/test_web_api.py +++ b/tests/test_web_api.py @@ -1722,6 +1722,7 @@ def test_ui_manual_registry_entry_loop(tmp_path): assert "README.md" in detail_response.text assert "supports capability" in detail_response.text assert "references source" in detail_response.text + assert "downward support" in detail_response.text assert "ID " in detail_response.text assert "Save Ability" in detail_response.text @@ -1786,6 +1787,7 @@ def test_ui_manual_registry_entry_loop(tmp_path): assert "Edited Manual API" in detail_response.text assert "tests/test_manual.py" in detail_response.text assert f"references feature #{feature_id}" in detail_response.text + assert "downward support" in detail_response.text delete_feature_response = client.post( f"{repository_path}/features/{feature_id}/delete", diff --git a/workplans/RREG-WP-0003-automatic-repository-exploration.md b/workplans/RREG-WP-0003-automatic-repository-exploration.md index 568b903..a995749 100644 --- a/workplans/RREG-WP-0003-automatic-repository-exploration.md +++ b/workplans/RREG-WP-0003-automatic-repository-exploration.md @@ -247,3 +247,8 @@ from the UI and service layer. Accepting support promotes the parent ability and capability context as needed, records a review decision, marks the candidate support approved, and maps capability support targets to the approved capability ID. + +Implementation note 2026-04-29: support rows now show an orientation label based +on target/reference abstraction levels. Downward support is normal, same-level +support is marked for review, and upward support is marked for review because it +usually indicates an abstraction or organization problem.