Coverage for little_loops / cli / issues / check_readiness.py: 22%
27 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-05-22 16:19 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-05-22 16:19 -0500
1"""ll-issues check-readiness: Exit 0 if an issue meets readiness thresholds."""
3from __future__ import annotations
5import argparse
6import json
7import sys
8from typing import TYPE_CHECKING
10if TYPE_CHECKING:
11 from little_loops.config import BRConfig
14def cmd_check_readiness(config: BRConfig, args: argparse.Namespace) -> int:
15 """Exit 0 if the issue's confidence and outcome scores meet thresholds.
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.
21 Args:
22 config: Project configuration
23 args: Parsed arguments with .issue_id, .readiness, .outcome
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
31 default_readiness: int = args.readiness
32 default_outcome: int = args.outcome
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
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
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)
53 return 0 if (confidence >= readiness and outcome_val >= outcome) else 1