1"""ll-issues finalize-decomposition: close a decomposed parent + re-link its EPIC.

2

3 Backs ENH-1977 Fix 4. Invoked by ``rn-decompose``'s ``finalize_parent`` state once

4 children 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"""

7

8 from __future__ import annotations

9

10 import argparse

11 import sys

12 from pathlib import Path

13 from typing import TYPE_CHECKING

14

15 if TYPE_CHECKING:

16 from little_loops.config import BRConfig

17

18

19 def cmd_finalize_decomposition(config: BRConfig, args: argparse.Namespace) -> int:

20 """Close a decomposed parent and re-link children to the parent's EPIC.

21

22 Args:

23 config: Project configuration (provides the project root).

24 args: Parsed args with ``.parent``, ``.children``, ``.children_file``,

25 ``.issues_dir``, ``.no_move``.

26

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

31

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())

37

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

42

43 result = finalize_decomposed_parent(

44 args.parent,

45 child_ids,

46 issues_dir,

47 move_to_completed=not args.no_move,

48 )

49

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

53

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

62

63

64 def add_finalize_decomposition_parser(subs: argparse._SubParsersAction) -> None:

65 """Register the ``finalize-decomposition`` subparser."""

66 from little_loops.cli_args import add_config_arg

67

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)