Maturity from run history, pack revision digests, attribute sets, projects and templates (RBT-WP-0008)
Also fixes run lists and scores mixing runs of different packs in one repository. 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
2f60d47746
commit
0203bf553c
19 changed files with 1311 additions and 33 deletions
33
rtut/run.py
33
rtut/run.py
|
|
@ -11,7 +11,7 @@ from datetime import date, datetime, timezone
|
|||
from pathlib import Path
|
||||
|
||||
from .legacy import repo_root
|
||||
from .pack import PLACEHOLDER, SECRET_RE, Pack, render
|
||||
from .pack import PLACEHOLDER, SECRET_RE, Pack, digest, render
|
||||
|
||||
STATES = ("todo", "running", "done", "failed", "skipped")
|
||||
VERIFY_TIMEOUT = 60
|
||||
|
|
@ -22,7 +22,7 @@ class RunError(Exception):
|
|||
|
||||
|
||||
def _now() -> str:
|
||||
return datetime.now(timezone.utc).isoformat(timespec="seconds")
|
||||
return datetime.now(timezone.utc).isoformat(timespec="microseconds") # sortable even within one second
|
||||
|
||||
|
||||
def _clean(value: str, what: str) -> str:
|
||||
|
|
@ -69,12 +69,23 @@ def _writable(run: Run) -> None:
|
|||
raise RunError(f"run is finished (outcome: {run.data['outcome']}); start a new run")
|
||||
|
||||
|
||||
def create(pack: Pack, operator: str, params: dict[str, str] | None = None) -> Run:
|
||||
ENVIRONMENTS = ("rehearsal", "staging", "production")
|
||||
|
||||
|
||||
def _context(ctx: dict | None) -> dict:
|
||||
"""Where a run applies: project id, target and environment class. Non-secret; all optional."""
|
||||
ctx = dict(ctx or {})
|
||||
if ctx.get("environment") and ctx["environment"] not in ENVIRONMENTS:
|
||||
raise RunError(f"environment must be one of {', '.join(ENVIRONMENTS)}")
|
||||
return {k: _clean(str(v), f"context {k}") for k, v in ctx.items() if v and k in ("project", "target", "environment")}
|
||||
|
||||
|
||||
def create(pack: Pack, operator: str, params: dict[str, str] | None = None, context: dict | None = None) -> Run:
|
||||
if not pack.ok or pack.data.get("engine") != "native":
|
||||
raise RunError("only valid native packs can be run")
|
||||
if pack.spec == "runbook-pack/v0.2":
|
||||
from . import run2
|
||||
return run2.create(pack, operator, params)
|
||||
return run2.create(pack, operator, params, context=context)
|
||||
if pack.spec != "runbook-pack/v0.1":
|
||||
raise RunError(f"{pack.spec} packs cannot be run")
|
||||
operator = _clean(operator.strip(), "operator")
|
||||
|
|
@ -102,6 +113,8 @@ def create(pack: Pack, operator: str, params: dict[str, str] | None = None) -> R
|
|||
"created": _now(),
|
||||
"params": resolved,
|
||||
"steps": {s["id"]: {"state": "todo"} for s in pack.data["steps"]},
|
||||
"pack_digest": digest(pack),
|
||||
"context": _context(context),
|
||||
}
|
||||
run = Run(pack, data)
|
||||
run.save()
|
||||
|
|
@ -116,8 +129,18 @@ def load(pack: Pack, run_id: str) -> Run:
|
|||
|
||||
|
||||
def list_runs(pack: Pack) -> list[str]:
|
||||
"""Run ids of this pack. All packs of a repository share one run folder, so filter by pack id."""
|
||||
d = runs_dir(pack)
|
||||
return sorted(p.stem for p in d.glob("*.json")) if d.exists() else []
|
||||
if not d.exists():
|
||||
return []
|
||||
out = []
|
||||
for f in sorted(d.glob("*.json")):
|
||||
try:
|
||||
if json.loads(f.read_text()).get("pack_id") == pack.id:
|
||||
out.append(f.stem)
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
return out
|
||||
|
||||
|
||||
def _order(run: Run, step_id: str) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue