Coverage for little_loops / cli / issues / finalize_decomposition.py: 0%
35 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-08 15:34 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-08 15:34 -0500
1"""ll-issues finalize-decomposition: close a decomposed parent + re-link its EPIC.
3Backs ENH-1977 Fix 4. Invoked by ``rn-decompose``'s ``finalize_parent`` state once
4children have been enqueued. Children may be passed positionally or via
5``--children-file`` (one ID per line — the loop's ``children_<id>.txt`` artifact).
6"""
8from __future__ import annotations
10import argparse
11import sys
12from pathlib import Path
13from typing import TYPE_CHECKING
15if TYPE_CHECKING:
16 from little_loops.config import BRConfig
19def cmd_finalize_decomposition(config: BRConfig, args: argparse.Namespace) -> int:
20 """Close a decomposed parent and re-link children to the parent's EPIC.
22 Args:
23 config: Project configuration (provides the project root).
24 args: Parsed args with ``.parent``, ``.children``, ``.children_file``,
25 ``.issues_dir``, ``.no_move``.
27 Returns:
28 Exit code (0 on success, 1 if the parent could not be found).
29 """
30 from little_loops.recursive_finalize import finalize_decomposed_parent
32 child_ids: list[str] = list(args.children or [])
33 if args.children_file:
34 cf = Path(args.children_file)
35 if cf.exists():
36 child_ids.extend(line.strip() for line in cf.read_text().splitlines() if line.strip())
38 project_root = args.config or Path.cwd()
39 issues_dir = Path(args.issues_dir)
40 if not issues_dir.is_absolute():
41 issues_dir = Path(project_root) / issues_dir
43 result = finalize_decomposed_parent(
44 args.parent,
45 child_ids,
46 issues_dir,
47 move_to_completed=not args.no_move,
48 )
50 if result["warnings"] and "parent file not found" in result["warnings"][0]:
51 print(f"Error: {result['warnings'][0]}", file=sys.stderr)
52 return 1
54 epic = result["epic"] or "(none)"
55 print(
56 f"Finalized {result['parent']}: status=done, "
57 f"moved={result['moved']}, epic={epic}, children={len(result['children'])}"
58 )
59 for warning in result["warnings"]:
60 print(f" warning: {warning}", file=sys.stderr)
61 return 0
64def add_finalize_decomposition_parser(subs: argparse._SubParsersAction) -> None:
65 """Register the ``finalize-decomposition`` subparser."""
66 from little_loops.cli_args import add_config_arg
68 fd = subs.add_parser(
69 "finalize-decomposition",
70 aliases=["fd"],
71 help="Close a decomposed parent and re-link its children to the parent's EPIC",
72 )
73 fd.set_defaults(command="finalize-decomposition")
74 fd.add_argument("parent", help="Decomposed parent issue ID (e.g., ENH-123)")
75 fd.add_argument("children", nargs="*", help="Child issue IDs (or use --children-file)")
76 fd.add_argument(
77 "--children-file",
78 dest="children_file",
79 default=None,
80 metavar="PATH",
81 help="File with one child ID per line (e.g., run_dir/children_<id>.txt)",
82 )
83 fd.add_argument(
84 "--issues-dir",
85 dest="issues_dir",
86 default=".issues",
87 metavar="DIR",
88 help="Issues base directory (default: .issues)",
89 )
90 fd.add_argument(
91 "--no-move",
92 action="store_true",
93 dest="no_move",
94 help="Do not move the closed parent into completed/ (status-only close)",
95 )
96 add_config_arg(fd)