Rename open to stalled and autopilot to auto-confirm; add pushed state; declare idempotency; propose maturity and projects model (RBT-WP-0008)
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:
parent
f39d5cec3c
commit
f5619e1a52
19 changed files with 429 additions and 105 deletions
13
rtut/run.py
13
rtut/run.py
|
|
@ -244,8 +244,6 @@ def exercise_update(run: Run, today: date | None = None) -> tuple[str, str]:
|
|||
raise RunError("every step must be done (not skipped) to exercise the pack")
|
||||
if run.data.get("deviations"):
|
||||
raise RunError("a run with deviations (overrides, waivers, push-through) cannot exercise the pack")
|
||||
if any(v.get("pushed_through") for v in run.data["steps"].values()):
|
||||
raise RunError("a run with pushed-through steps cannot exercise the pack")
|
||||
today = today or date.today()
|
||||
old = run.pack.path.read_text()
|
||||
new = STATUS_LINE.sub(f"exercise_status: exercised {today.isoformat()} by {run.data['operator']}",
|
||||
|
|
@ -346,8 +344,9 @@ def finalize(run: Run, outcome: str, note: str = "") -> None:
|
|||
raise RunError("a run succeeds only when every step is done (skipped steps do not count)")
|
||||
if outcome == "succeeded" and any(g["state"] not in ("passed", "waived") for g in run.data.get("gates", {}).values()):
|
||||
raise RunError("a run succeeds only when every gate has passed or been waived")
|
||||
if outcome == "failed" and "failed" not in states:
|
||||
raise RunError("a run can be finished as failed only when a step has failed; otherwise abandon it")
|
||||
gstates = [g["state"] for g in run.data.get("gates", {}).values()]
|
||||
if outcome == "failed" and not ({"failed", "pushed"} & set(states) or {"failed", "pushed"} & set(gstates)):
|
||||
raise RunError("a run can be finished as failed only when a step or gate has failed or was pushed through; otherwise abandon it")
|
||||
run.data["outcome"] = outcome
|
||||
run.data["finished"] = _now()
|
||||
if note.strip():
|
||||
|
|
@ -356,17 +355,17 @@ def finalize(run: Run, outcome: str, note: str = "") -> None:
|
|||
|
||||
|
||||
def effective_outcome(run: Run, now: datetime | None = None, stale_days: int = STALE_DAYS) -> str:
|
||||
"""finished outcome, or 'open', or 'abandoned' for an open run untouched for stale_days."""
|
||||
"""finished outcome, or 'stalled' (unfinished), or 'abandoned' for an unfinished run untouched for stale_days."""
|
||||
if run.data.get("outcome"):
|
||||
return run.data["outcome"]
|
||||
now = now or datetime.now(timezone.utc)
|
||||
last = datetime.fromisoformat(run.data.get("updated") or run.data["created"])
|
||||
return "abandoned" if (now - last).days >= stale_days else "open"
|
||||
return "abandoned" if (now - last).days >= stale_days else "stalled"
|
||||
|
||||
|
||||
def scorecard(pack: Pack, now: datetime | None = None, stale_days: int = STALE_DAYS) -> dict:
|
||||
"""How often a runbook was tried and how it went, derived from its run records."""
|
||||
card = {"tried": 0, "succeeded": 0, "failed": 0, "abandoned": 0, "open": 0,
|
||||
card = {"tried": 0, "succeeded": 0, "failed": 0, "abandoned": 0, "stalled": 0,
|
||||
"feedback": {"good": 0, "neutral": 0, "poor": 0}}
|
||||
for rid in list_runs(pack):
|
||||
r = load(pack, rid)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue