Evidence export of closed runs and rtut seal (RBT-WP-0009-T04)
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
e021fd979f
commit
6d70f4d97d
12 changed files with 149 additions and 19 deletions
23
rtut/ui.py
23
rtut/ui.py
|
|
@ -301,7 +301,9 @@ class App:
|
|||
outcome = r.data.get("outcome")
|
||||
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)}"
|
||||
box = (f"<p><b>Run finished: {e(outcome)}</b>{note}</p>{self.locked_note(r)}"
|
||||
f"<p><a href='/run/{i}/{e(rid)}/record.json'>Download the run record (JSON)</a> "
|
||||
"<span class=note>Check it anywhere with <code>rtut seal <file></code>.</span></p>")
|
||||
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 = []
|
||||
|
|
@ -480,6 +482,21 @@ def make_handler(app: App, port_holder: list):
|
|||
self.send_header("Content-Disposition", f"attachment; filename=\"{safe}\"")
|
||||
self.end_headers(); self.wfile.write(body)
|
||||
|
||||
def _send_record(self, path, filename: str):
|
||||
"""A closed record, exactly as stored, as a download. Its seal can be checked with `rtut seal <file>`."""
|
||||
import json
|
||||
data = json.loads(path.read_text())
|
||||
if not data.get("outcome"):
|
||||
return self._send_file(409, b"the record can be downloaded once the run is closed", "text/plain; charset=utf-8", None)
|
||||
state = R.record_seal_state(data)
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("X-Content-Type-Options", "nosniff")
|
||||
self.send_header("X-Rtut-Seal", state)
|
||||
self.send_header("Content-Security-Policy", "default-src 'none'; sandbox")
|
||||
self.send_header("Content-Disposition", f"attachment; filename=\"{filename}\"")
|
||||
self.end_headers(); self.wfile.write(path.read_bytes())
|
||||
|
||||
def _host_ok(self) -> bool:
|
||||
return (self.headers.get("Host", "").split(":")[0] in ("127.0.0.1", "localhost"))
|
||||
|
||||
|
|
@ -506,6 +523,10 @@ def make_handler(app: App, port_holder: list):
|
|||
body = UA.set_page(app, parts[1])
|
||||
if body:
|
||||
return self._send(body)
|
||||
if parts[0] == "run" and len(parts) == 4 and parts[3] == "record.json":
|
||||
return self._send_record(R.load(app.packs[int(parts[1])], parts[2]).path, f"run-{parts[2]}.json")
|
||||
if parts[0] == "project-run" and len(parts) == 3 and parts[2] == "record.json":
|
||||
return self._send_record(PRUN.load(app.registry(), parts[1]).path, f"project-run-{parts[1]}.json")
|
||||
if parts[0] == "run" and len(parts) == 5 and parts[3] == "artefact":
|
||||
status, body, ctype, dl = UI2.serve_artefact(app, int(parts[1]), parts[2], parts[4])
|
||||
want_dl = bool(parse_qs(urlparse(self.path).query).get("download"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue