1"""ll-issues check-readiness: Exit 0 if an issue meets readiness thresholds."""

2

3 from __future__ import annotations

4

5 import argparse

6 import json

7 import sys

8 from typing import TYPE_CHECKING

9

10 if TYPE_CHECKING:

11 from little_loops.config import BRConfig

12

13

14 def cmd_check_readiness(config: BRConfig, args: argparse.Namespace) -> int:

15 """Exit 0 if the issue's confidence and outcome scores meet thresholds.

16

17 Reads thresholds from ll-config.json (commands.confidence_gate), falling

18 back to the values supplied via --readiness / --outcome CLI args so callers

19 can pass loop-context defaults without special-casing the config file.

20

21 Args:

22 config: Project configuration

23 args: Parsed arguments with .issue_id, .readiness, .outcome

24

25 Returns:

26 0 if both thresholds are met, 1 otherwise

27 """

28 from little_loops.cli.issues.show import _resolve_issue_id

29 from little_loops.frontmatter import parse_frontmatter

30

31 default_readiness: int = args.readiness

32 default_outcome: int = args.outcome

33

34 config_path = config.project_root / ".ll" / "ll-config.json"

35 try:

36 raw = json.loads(config_path.read_text())

37 cg = raw.get("commands", {}).get("confidence_gate", {})

38 readiness = cg.get("readiness_threshold", default_readiness)

39 outcome = cg.get("outcome_threshold", default_outcome)

40 except Exception:

41 readiness = default_readiness

42 outcome = default_outcome

43

44 path = _resolve_issue_id(config, args.issue_id)

45 if path is None:

46 print(f"Error: Issue '{args.issue_id}' not found.", file=sys.stderr)

47 return 1

48

49 fm = parse_frontmatter(path.read_text(), coerce_types=True)

50 confidence = int(fm.get("confidence_score") or 0)

51 outcome_val = int(fm.get("outcome_confidence") or 0)

52

53 return 0 if (confidence >= readiness and outcome_val >= outcome) else 1