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

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

2 

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

7 

8from __future__ import annotations 

9 

10import argparse 

11import sys 

12from pathlib import Path 

13from typing import TYPE_CHECKING 

14 

15if TYPE_CHECKING: 

16 from little_loops.config import BRConfig 

17 

18 

19def 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 

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 

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)