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

@ -260,7 +260,7 @@ class App:
if x.get("values") else f"<input name='p_{x['id']}' value='{e(x.get('default', ''))}'>")
+ f"</label><br><span class=note>{e(x.get('help', ''))} Never enter secrets.</span></p>"
for x in d.get("parameters", []))
return page(d["title"], f"<h1>{e(d['title'])}</h1><p class=pill>{e(d['exercise_status'])}</p>"
return page(d["title"], f"<h1>{e(d['title'])}</h1><p>{UP.maturity_pill(p)}</p>"
f"<p>{e(d.get('outcome', ''))}</p><p class=note>{e(score_line(R.scorecard(p)))}</p>"
f"{UP.maturity_block(self, i, p)}{self.runs_list(i, p)}<h2>Prerequisites</h2><ul>{prereq}</ul>"
f"<form method=post action='/run/new/{i}'>{self.hidden()}{fields}"
@ -302,17 +302,6 @@ class App:
if outcome:
note = f" - {e(r.data['outcome_note'])}" if r.data.get("outcome_note") else ""
box = f"<p><b>Run finished: {e(outcome)}</b>{note}</p>{self.locked_note(r)}"
if outcome == "succeeded":
if r.data.get("exercise_recorded"):
box += f"<p>Pack recorded as <b>{e(r.data['exercise_recorded'])}</b>. Commit the change in the owning repository.</p>"
else:
old, new = R.exercise_update(r)
line = new.split("exercise_status:", 1)[1].split("\n", 1)[0].strip()
box += (f"<p>Current pack status: <span class=pill>{e(p.data['exercise_status'])}</span>. "
f"Recording this run would set it to <b>{e(line)}</b> in <code>{e(p.path)}</code> "
"(a reviewable edit; commit it in the owning repository).</p>"
f"<form method=post action='/run/{i}/{e(rid)}/exercise'>{self.hidden()}"
"<button>Record pack as exercised</button></form>")
return f"<div id=close class=step><h2>Close this run</h2>{box}</div>"
states = [v["state"] for v in r.data["steps"].values()]
btns = []
@ -383,7 +372,7 @@ class App:
where = (f" · project <a href='/project/{e(ctx['project'])}'>{e(ctx['project'])}</a>"
f"{' @' + e(ctx['target']) if ctx.get('target') else ''} · {e(ctx.get('environment', ''))}"
f"{' · part of <a href=' + chr(39) + '/project-run/' + e(ctx['project_run']) + chr(39) + '>project run ' + e(ctx['project_run']) + '</a>' if ctx.get('project_run') else ''}")
out = [f"<h1>{e(d['title'])}</h1><p class=pill>{e(d['exercise_status'])}</p>"
out = [f"<h1>{e(d['title'])}</h1><p>{UP.maturity_pill(p)}</p>"
f"<p class=note>run {e(rid)} · operator {e(r.data['operator'])}{where}</p>"
f"<p class=note>{UI2.times_line(r)}</p>"]
step_secs = R.run_times(r)["step_seconds"]
@ -607,15 +596,10 @@ def make_handler(app: App, port_holder: list):
i, rid = int(parts[1]), parts[2]
R.add_feedback(R.load(app.packs[i], rid), form.get("rating", ""), form.get("comment", ""))
return self._redirect(f"/run/{i}/{rid}#feedback")
if len(parts) == 4 and parts[0] == "run" and parts[3] in ("finalize", "exercise"):
if len(parts) == 4 and parts[0] == "run" and parts[3] == "finalize":
i, rid = int(parts[1]), parts[2]
r = R.load(app.packs[i], rid)
if parts[3] == "finalize":
R.finalize(r, form.get("outcome", ""), form.get("note", ""), rating=form.get("rating") or None,
comment=form.get("fb_comment", ""))
else:
R.apply_exercise(r)
app.packs[i] = P.load(app.packs[i].path) # pick up the new exercise status
R.finalize(R.load(app.packs[i], rid), form.get("outcome", ""), form.get("note", ""),
rating=form.get("rating") or None, comment=form.get("fb_comment", ""))
return self._redirect(f"/run/{i}/{rid}#close")
i, rid, sid, action = int(parts[1]), parts[2], parts[3], parts[4]
p = app.packs[i]; r = R.load(p, rid)