diff --git a/scripts/openspec_change.py b/scripts/openspec_change.py index e5dae737..3bfb71b2 100644 --- a/scripts/openspec_change.py +++ b/scripts/openspec_change.py @@ -45,7 +45,6 @@ def _active_openspec_changes() -> list[str] | None: changes = payload.get("changes") if isinstance(payload, dict) else payload if not isinstance(changes, list): return None - parsed: list[str] = [] in_progress: list[str] = [] for entry in changes: if not isinstance(entry, dict): @@ -53,10 +52,9 @@ def _active_openspec_changes() -> list[str] | None: name = entry.get("name") if not isinstance(name, str) or not name: return None - parsed.append(name) if entry.get("status") != "complete": in_progress.append(name) - return in_progress if in_progress else parsed + return in_progress except Exception: return None diff --git a/tests/test_verify_portable.py b/tests/test_verify_portable.py index 8f874f06..36cda927 100644 --- a/tests/test_verify_portable.py +++ b/tests/test_verify_portable.py @@ -356,6 +356,47 @@ def test_active_changes_empty_list_parses_to_noop(monkeypatch: pytest.MonkeyPatc assert verify_portable.validate_openspec_change(None) == 0 +def test_active_changes_all_complete_parses_to_none_noop(monkeypatch: pytest.MonkeyPatch) -> None: + import json as _json + completed_payload = { + "changes": [ + {"name": "change-1", "status": "complete", "completedTasks": 5, "totalTasks": 5}, + {"name": "change-2", "status": "complete", "completedTasks": 3, "totalTasks": 3}, + ], + "root": {}, + } + monkeypatch.setattr( + verify_portable.subprocess, "run", + lambda *a, **k: subprocess.CompletedProcess(a, 0, _json.dumps(completed_payload), ""), + ) + assert verify_portable._active_openspec_changes() == [] + resolution = verify_portable.resolve_openspec_change(None) + assert resolution.state == "none" + assert resolution.name is None + assert resolution.names == [] + assert verify_portable.validate_openspec_change(None) == 0 + + +def test_active_changes_filters_out_completed_when_in_progress_exists(monkeypatch: pytest.MonkeyPatch) -> None: + import json as _json + mixed_payload = { + "changes": [ + {"name": "change-complete", "status": "complete"}, + {"name": "change-active", "status": "in-progress"}, + ], + "root": {}, + } + monkeypatch.setattr( + verify_portable.subprocess, "run", + lambda *a, **k: subprocess.CompletedProcess(a, 0, _json.dumps(mixed_payload), ""), + ) + assert verify_portable._active_openspec_changes() == ["change-active"] + resolution = verify_portable.resolve_openspec_change(None) + assert resolution.state == "active" + assert resolution.name == "change-active" + assert resolution.names == [] + + def test_verify_portable_modular_decomposition_and_backwards_compatibility() -> None: vp_path = ROOT / "scripts" / "verify-portable.py" sc_path = ROOT / "scripts" / "select_checks.py"