da20722e76
gates / gates (push) Successful in 27s
INTERIM CHECKPOINT — evidence off the machine at the end of the phase that produced it (R-320), not at the end of the session. Phases 2-5 follow in a later commit. Phase 0, all three mechanisms proven with their controls: - the fleet floor to 0.261.0 with its declared MinAgent — both demo boxes in 13 s, the hub logging `managed floor SERVED ... from declared (golden 0.258.0)`. - a PRIVATE DRILL CATALOG (admin/app-catalog-drill), so that broken, dummy, cross-repo and engine-major edges can be measured without the live catalog ever carrying one. Positive control quoted, and two negative controls: the live catalog's main and both real boxes' caches unchanged. - a throwaway image store on the scratch guest, which is what makes an UNATTENDED HOLD measurable at all: an edge that PASSES the within-a-major test and still fails. CompareImageRefs was proven to order host:port/ references by RUNNING it (4 positive cases + 1 negative control), not by reading it. Phase 1: real within-a-major upstream edges walked on guest 9202 through the product's own guarded Update, each app seeded and read back through its OWN front door (R-156), with a per-edge verdict record in 09's shape. `inconclusive` is never collapsed into `failed`. TWO INSTRUMENT FIXES, both in this repo's own evidence code: - 00-api-recipe.md said the app page is /app/<n>; it is /apps/<n>, and every call it described 404s. Corrected, with the session-expiry note that cost the same time. - unattended-caller.py's follow() read update_phase/updating off the API ENVELOPE, so both were always None and EVERY followed update ran to its 900 s timeout and was then recorded `timeout` and never-press-again. Fixed before B1 relied on it. R-623. No controller, agent or hub code was written. The live catalog carries no broken reference. Gates: repo_gates.py --fast — all 15 OK, exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
89 lines
3.7 KiB
Python
89 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Build the verdict table, the three lines and the promotion list from the verdict records.
|
|
|
|
Nothing here judges anything — it reads what each edge wrote and lays it out. `inconclusive` is
|
|
carried through as itself and is never folded into `failed`.
|
|
"""
|
|
import glob, json, os, sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
def short(refs):
|
|
if not refs:
|
|
return "—"
|
|
out = []
|
|
for k, v in refs.items():
|
|
v = str(v)
|
|
out.append(v.rsplit("/", 1)[-1])
|
|
return ", ".join(sorted(set(out)))
|
|
|
|
|
|
def load():
|
|
recs = []
|
|
for p in sorted(glob.glob(os.path.join(HERE, "apps", "*", "verdict.json"))):
|
|
try:
|
|
d = json.load(open(p))
|
|
d["_dir"] = os.path.basename(os.path.dirname(p))
|
|
recs.append(d)
|
|
except Exception as e:
|
|
print(f"skipped {p}: {e}", file=sys.stderr)
|
|
return recs
|
|
|
|
|
|
def main():
|
|
recs = load()
|
|
proven = [r for r in recs if r["verdict"] == "proven"]
|
|
failed = [r for r in recs if r["verdict"] == "failed"]
|
|
inconc = [r for r in recs if r["verdict"] == "inconclusive"]
|
|
|
|
print("## The verdict table\n")
|
|
print("One row per edge attempted tonight. `inconclusive` means *we could not measure it*, which")
|
|
print("is a different fact from *it does not work* — and only one of them is about the app.\n")
|
|
print("| app | from → to | class | box verdict | seed before → after | secs | migration line seen | evidence |")
|
|
print("|---|---|---|---|---|---|---|---|")
|
|
for r in sorted(recs, key=lambda x: (x["verdict"] != "proven", x["app"])):
|
|
mig = r.get("migration_observed")
|
|
mig = "yes" if mig else ("—" if r["verdict"] != "proven" else "none printed")
|
|
arrow = f"{short(r.get('from'))} → {short(r.get('to'))}"
|
|
print(f"| `{r['app']}` | {arrow} | {r.get('class') or r.get('leg') or '—'} "
|
|
f"| **{r['verdict']}** | {r.get('seed_read_before')} → {r.get('seed_read_after')} "
|
|
f"| {r.get('duration_s')} | {mig} | `apps/{r['_dir']}/` |")
|
|
|
|
print(f"\n**{len(proven)} proven · {len(failed)} failed · {len(inconc)} inconclusive "
|
|
f"— out of {len(recs)} attempted.**\n")
|
|
|
|
if inconc:
|
|
print("### Why each inconclusive edge could not be judged\n")
|
|
for r in inconc:
|
|
why = "; ".join(r.get("notes") or []) or "—"
|
|
print(f"- **`{r['app']}`** — {why}")
|
|
print()
|
|
|
|
if failed:
|
|
print("### The edges that failed — the most valuable results of the night\n")
|
|
for r in failed:
|
|
why = "; ".join(r.get("notes") or []) or "—"
|
|
print(f"- **`{r['app']}`** — final phase `{r.get('final_phase')}`, "
|
|
f"hold `{r.get('hold_reason')}`, error `{r.get('update_error')}`. {why}")
|
|
print()
|
|
|
|
print("## The promotion list for the operator\n")
|
|
print("**CC promotes nothing.** These are the real, within-a-major edges that ended `proven` on")
|
|
print("the box tonight, with the data read back through the app's own front door both before and")
|
|
print("after. Moving each of them on the LIVE catalog is the operator's call.\n")
|
|
print("| app | the move | what it would mean for a box in the field |")
|
|
print("|---|---|---|")
|
|
for r in sorted(proven, key=lambda x: x["app"]):
|
|
frm, to = short(r.get("from")), short(r.get("to"))
|
|
mig = r.get("migration_observed")
|
|
note = ("the app runs its own schema migration on the way — proven here, and the update "
|
|
"takes a backup first" if mig else
|
|
"no migration line printed; the app came up on the new version with its data intact")
|
|
print(f"| `{r['app']}` | {frm} → {to} | {note} |")
|
|
print()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|