Retire exercise_status: maturity is the only notion; add CLI tests (RBT-WP-0009-T01)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 295952@bnt-lap001
Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
This commit is contained in:
tegwick 2026-09-29 21:28:56 +02:00
parent b89e8eaa74
commit 6d3414d8b7
29 changed files with 444 additions and 218 deletions

View file

@ -5,6 +5,7 @@ import argparse
import sys
from pathlib import Path
from . import maturity as M
from . import pack as P
@ -47,19 +48,12 @@ def _run(pk, a) -> int:
R.skip(r, a.step, a.note); return 0
if a.action == "finalize":
R.finalize(r, a.outcome or "", a.note)
if r.data["outcome"] == "succeeded" and a.apply:
print("recorded:", R.apply_exercise(r))
print(r.data["outcome"]); return 0
if a.action == "capture":
R.add_capture(r, a.step, sys.stdin.read(), a.command); print("captured"); return 0
if a.action == "feedback":
R.add_feedback(r, a.rating or "", a.note); print("recorded"); return 0
old, new = R.exercise_update(r)
import difflib
print("".join(difflib.unified_diff(old.splitlines(True), new.splitlines(True), "pack.yaml", "pack.yaml")))
if a.apply:
pk.path.write_text(new); print("written")
return 0
raise ValueError(f"unknown action {a.action}")
except (R.RunError, KeyError, ValueError) as e:
print(f"error: {e}", file=sys.stderr)
return 1
@ -125,7 +119,7 @@ def main(argv: list[str] | None = None) -> int:
sp = sub.add_parser(name)
sp.add_argument("root", nargs="?", default=".")
sp = sub.add_parser("run", help="record a run of a native pack")
sp.add_argument("action", choices=["new", "list", "status", "cmd", "start", "verify", "done", "fail", "retry", "skip", "finish", "feedback", "finalize", "capture"])
sp.add_argument("action", choices=["new", "list", "status", "cmd", "start", "verify", "done", "fail", "retry", "skip", "feedback", "finalize", "capture"])
sp.add_argument("manifest")
sp.add_argument("run_id", nargs="?")
sp.add_argument("step", nargs="?")
@ -138,7 +132,6 @@ def main(argv: list[str] | None = None) -> int:
sp.add_argument("--rating", choices=["good", "neutral", "poor"], help="feedback: how the runbook felt")
sp.add_argument("--yes", action="store_true", help="confirm running a verify command")
sp.add_argument("--pass", dest="passed", action="store_true", help="manual check: operator confirms it passed")
sp.add_argument("--apply", action="store_true", help="finish: write the exercise-status change")
sp = sub.add_parser("ui", help="serve the local UI on 127.0.0.1")
sp.add_argument("roots", nargs="*", default=["."])
sp.add_argument("--port", type=int, default=8877)
@ -182,7 +175,6 @@ def main(argv: list[str] | None = None) -> int:
if a.cmd == "project":
return _project(a)
if a.cmd in ("maturity", "review"):
from . import maturity as M
pk = P.load(Path(a.manifest))
if not pk.ok:
print("\n".join(pk.errors), file=sys.stderr); return 1
@ -202,7 +194,7 @@ def main(argv: list[str] | None = None) -> int:
for p in _packs(a.root):
if p.ok and p.data.get("engine") == "native" and p.spec == "runbook-pack/v0.1":
c = R.scorecard(p)
print(f"{p.id}\ttried {c['tried']}\tsucceeded {c['succeeded']}\tfailed {c['failed']}\tabandoned {c['abandoned']}\tstalled {c['stalled']}\t[{p.data['exercise_status']}]")
print(f"{p.id}\ttried {c['tried']}\tsucceeded {c['succeeded']}\tfailed {c['failed']}\tabandoned {c['abandoned']}\tstalled {c['stalled']}\t[{M.maturity(p)['level']}]")
return 0
if a.cmd in ("list", "validate"):
packs = _packs(a.root)
@ -213,7 +205,8 @@ def main(argv: list[str] | None = None) -> int:
for p in packs:
d = p.data
if a.cmd == "list":
print(f"{p.id}\t{d.get('engine')}\t{d.get('exercise_status')}\t{d.get('title')}")
level = M.maturity(p)["level"] if p.ok and d.get("engine") == "native" else "-"
print(f"{p.id}\t{d.get('engine')}\t{level}\t{d.get('title')}")
else:
print(f"{'ok ' if p.ok else 'FAIL'} {p.id}")
for e in p.errors:
@ -246,7 +239,7 @@ def main(argv: list[str] | None = None) -> int:
return 0
if a.cmd == "show":
d = pk.data
print(f"{d['title']} [{d['exercise_status']}] engine={d['engine']}")
print(f"{d['title']} [{M.maturity(pk)['level']}] engine={d['engine']}")
for i, s in enumerate(d.get("steps", []), 1):
print(f"{i}. [{s['owner']}] {s['title']} ({s.get('risk', 'read-only')})")
print(f" done when: {s['verify']['done_when']}")