diff --git a/desloppify/app/cli_support/parser_groups_plan_impl_sections_cluster.py b/desloppify/app/cli_support/parser_groups_plan_impl_sections_cluster.py index dea394f5..def2a8bd 100644 --- a/desloppify/app/cli_support/parser_groups_plan_impl_sections_cluster.py +++ b/desloppify/app/cli_support/parser_groups_plan_impl_sections_cluster.py @@ -105,8 +105,20 @@ def _add_cluster_subparser(plan_sub) -> None: p_cu.add_argument("--effort", type=str, default=None, choices=["trivial", "small", "medium", "large"], help="Effort tag for --add-step or --update-step") - p_cu.add_argument("--depends-on", nargs="+", default=None, metavar="CLUSTER", - help="Cluster(s) this cluster depends on") + dependency_options = p_cu.add_mutually_exclusive_group() + dependency_options.add_argument( + "--depends-on", + nargs="+", + default=None, + metavar="CLUSTER", + help="Cluster(s) this cluster depends on", + ) + dependency_options.add_argument( + "--clear-depends-on", + action="store_true", + default=False, + help="Clear all cluster dependencies", + ) p_cu.add_argument("--issue-refs", nargs="+", default=None, metavar="REF", help="Issue refs for --add-step or --update-step") diff --git a/desloppify/app/commands/plan/cluster/update_flow.py b/desloppify/app/commands/plan/cluster/update_flow.py index 80d3d92d..7cea5994 100644 --- a/desloppify/app/commands/plan/cluster/update_flow.py +++ b/desloppify/app/commands/plan/cluster/update_flow.py @@ -131,7 +131,11 @@ def build_request(args) -> ClusterUpdateRequest: undone_step=getattr(args, "undone_step", None), priority=getattr(args, "priority", None), effort=getattr(args, "effort", None), - depends_on=getattr(args, "depends_on", None), + depends_on=( + [] + if getattr(args, "clear_depends_on", False) + else getattr(args, "depends_on", None) + ), issue_refs=getattr(args, "issue_refs", None), ) @@ -196,7 +200,10 @@ def _apply_cluster_metadata( print(services.colorize_fn(f" Unknown cluster(s): {', '.join(bad)}", "red")) return False cluster["depends_on_clusters"] = request.depends_on - print(services.colorize_fn(f" Dependencies set: {', '.join(request.depends_on)}", "dim")) + if request.depends_on: + print(services.colorize_fn(f" Dependencies set: {', '.join(request.depends_on)}", "dim")) + else: + print(services.colorize_fn(" Dependencies cleared.", "dim")) return True diff --git a/desloppify/tests/commands/plan/test_cluster_ops_direct.py b/desloppify/tests/commands/plan/test_cluster_ops_direct.py index 0ed4c6f9..031f4db8 100644 --- a/desloppify/tests/commands/plan/test_cluster_ops_direct.py +++ b/desloppify/tests/commands/plan/test_cluster_ops_direct.py @@ -449,6 +449,41 @@ def test_cluster_update_direct_paths(capsys) -> None: assert "Nothing to update" in out2 +def test_cluster_update_clears_dependencies(capsys) -> None: + plan = { + "clusters": { + "alpha": { + "issue_ids": [], + "action_steps": [], + "depends_on_clusters": ["beta"], + }, + "beta": {"issue_ids": []}, + } + } + saved: list[dict] = [] + args = argparse.Namespace(cluster_name="alpha", clear_depends_on=True) + services = cluster_update_flow_mod.ClusterUpdateServices( + load_plan_fn=lambda: plan, + save_plan_fn=lambda payload: saved.append(payload), + append_log_entry_fn=lambda *_a, **_k: None, + parse_steps_file_fn=lambda _text: [], + normalize_step_fn=lambda step: {"title": str(step)}, + step_summary_fn=lambda step: str(step), + utc_now_fn=lambda: "2026-03-09T00:00:00+00:00", + colorize_fn=lambda text, _tone: text, + ) + + cluster_update_mod.cmd_cluster_update( + args, + services=services, + plan_lock_fn=lambda: nullcontext(), + ) + + assert plan["clusters"]["alpha"]["depends_on_clusters"] == [] + assert saved == [plan] + assert "Dependencies cleared." in capsys.readouterr().out + + def test_cluster_update_steps_file_parse_failure_raises_command_error(tmp_path) -> None: plan = {"clusters": {"alpha": {"issue_ids": [], "action_steps": []}}} steps_file = tmp_path / "steps.md" diff --git a/desloppify/tests/commands/test_parser_groups_option_sections_direct.py b/desloppify/tests/commands/test_parser_groups_option_sections_direct.py index 1e61c9ec..643aa00e 100644 --- a/desloppify/tests/commands/test_parser_groups_option_sections_direct.py +++ b/desloppify/tests/commands/test_parser_groups_option_sections_direct.py @@ -4,6 +4,8 @@ import argparse +import pytest + from desloppify.app.cli_support.parser_groups_admin_review_options_batch import ( _add_batch_execution_options, ) @@ -272,6 +274,31 @@ def test_plan_cluster_triage_commit_and_scan_gate_subparsers() -> None: assert cluster_update.depends_on == ["base", "parser"] assert cluster_update.issue_refs == ["test_coverage::a", "test_coverage::b"] + clear_dependencies = parser.parse_args( + [ + "plan", + "cluster", + "update", + "auto/test_coverage", + "--clear-depends-on", + ] + ) + assert clear_dependencies.clear_depends_on is True + assert clear_dependencies.depends_on is None + + with pytest.raises(SystemExit): + parser.parse_args( + [ + "plan", + "cluster", + "update", + "auto/test_coverage", + "--depends-on", + "base", + "--clear-depends-on", + ] + ) + triage_args = parser.parse_args( [ "plan",