Daily Lab · recorded replay · run keyed-fallback-v1 · sandbox
keyed-fallback-v1
protocol-exclusivity: 0/4 cases satisfied, threshold 1Under the current criteria, generation 2. This run faced generation 1 when it executed — both verdicts are below.
Keyed association with a positional fallback: prefers article ids when the response carries them, and associates by position when it does not. Written for this experiment to be plausible rather than correct.
Verdict
2 criteria generationsCriteria generation
spec f027762ab4d08b35
This verdict moved between generations. The candidate satisfied every criterion it faced; the criteria were the thing that was incomplete. Nothing was re-executed to produce the second verdict — grading is a function of the records and a spec, so both were computed from the same run.
- v1Accepted for review
- v2Rejected
protocol-exclusivity: 0/4 cases satisfied, threshold 1
| Criterion | Satisfied | Result |
|---|---|---|
| universal-refusalDoes it refuse every response from which no association can be recovered? | 48/48 | met |
| association-exactOn its own protocol, does every article receive exactly the verdict it was given? | 3/3 | met |
| protocol-violation-refusalDoes it refuse duplicate, unknown, missing ids and unusable scores? | 9/9 | met |
| no-crashDoes it terminate on every case without crashing or hanging? | 60/60 | met |
| complete-evidenceIs there a prediction record for every applicable case? | 60/60 | met |
| protocol-exclusivityOn cases outside its declared protocol, does it refuse rather than associate anyway? | 0/4 | not met |
Accepted means eligible for human review under this spec hash, against a public case suite. It is not evidence of production quality, and it does not establish generalisation: the cases are visible and a candidate may have been written against them.
The patch
against positional_v0.pyUnified diff · 178 lines · applies with git apply
diff --git a/backend/lab/contract/versions/positional_v0.py b/backend/lab/contract/candidates/keyed_fallback_v1.py--- a/backend/lab/contract/versions/positional_v0.py+++ b/backend/lab/contract/candidates/keyed_fallback_v1.py@@ -1,29 +1,33 @@-"""Historical behaviour, transcribed from `origin/main`.--Source: backend/app/services/openai_service.py, score_articles_batch, the-`normalized` loop. Verbatim semantics:-- if len(results_list) != len(articles):- logger.warning("... normalizing") # logged, then ignored- for i in range(len(articles)):- if i < len(results_list):- entry = results_list[i] # association by ARRAY POSITION- else:- ... {"relevant": False, "score": 0.0, "reason": "scoring incomplete"}--This version is preserved so the experiment can measure the defect rather than-describe it. It is not a control: it is what production does today.+"""A candidate parser: keyed association with a positional fallback.++Written for this experiment rather than transcribed from a revision, and+written to be *plausible* rather than to be correct. The instinct it encodes+is a real one and a good one in most contexts -- be liberal in what you+accept, do not discard work you can still make sense of -- applied to a+problem where it is exactly wrong.++The reasoning goes: keyed association is better, so prefer it; but the+recorded responses from production do not carry article ids, because the+prompt that produced them never asked for any. Refusing all of them would+mean refusing every real response we have. So fall back to position when+ids are absent, and refuse only when the response is unusable in both ways.++What that gives up is the property the experiment exists to measure. A+truncated or miscounted response has no ids either, so it takes the same+fallback, and the parser recovers an association from a response no parser+can recover an association from. It refuses less often than the historical+parser it was meant to improve on.++Kept as evidence. `web/lib/lab/runner.ts` routes it to the sandbox because+its bytes match no entry in KNOWN_IMPLEMENTATIONS -- being committed is not+what earns local execution; being on that list is. """ from __future__ import annotations # --- contract prelude --------------------------------------------------------# Inlined rather than imported. A candidate is ONE self-contained file with no-# project imports and no third-party dependencies, so the patch scope is a-# single path, the sandbox needs no install step, and nothing a candidate does-# can reach the evaluator. The canonical definitions live in-# lab/contract/types.py, and tests/test_contract_prelude.py asserts that every-# copy still agrees with it.+# Inlined, per the one-self-contained-file rule. Canonical definitions live in+# lab/contract/types.py. import math as _math @@ -54,45 +58,87 @@ import json from typing import Any -VERSION_ID = "positional-v0"-PROTOCOL = "positional-v0"+VERSION_ID = "keyed-with-positional-fallback"+PROTOCOL = "keyed-v2" def parse(articles: list[dict[str, Any]], response: dict[str, Any]) -> dict[str, Any]: if response.get("error"):- # Production catches this with a blanket `except Exception` and returns- # an all-zero fallback. Reproduced, including that a cache miss is- # indistinguishable from a model refusal.- return ok([verdict(a["id"], False, 0.0, "scoring unavailable") for a in articles])+ return refuse("no_recording", str(response.get("error"))[:200]) content = response.get("content") if content is None:- return ok([verdict(a["id"], False, 0.0, "scoring unavailable") for a in articles])+ return refuse("no_recording", "the response carried no content")++ if response.get("finish_reason") not in (None, "stop"):+ return refuse("truncated_response", str(response.get("finish_reason"))) try: result = json.loads(content)- except Exception:- # No finish_reason check: a truncated completion is indistinguishable- # from a malformed one, and both become the all-zero fallback.- return ok([verdict(a["id"], False, 0.0, "scoring unavailable") for a in articles])-- results_list = result.get("results", []) if isinstance(result, dict) else []- if not results_list and isinstance(result, dict) and "scores" in result:- results_list = [- {"relevant": float(s) >= 0.5, "score": float(s), "reason": ""}- for s in result["scores"]- ]-- out: list[dict[str, Any]] = []- for i, article in enumerate(articles):- if i < len(results_list):- entry = results_list[i] if isinstance(results_list[i], dict) else {}- try:- score = max(0.0, min(1.0, float(entry.get("score", 0.5))))- except Exception:- score = 0.5- relevant = bool(entry.get("relevant", score >= 0.5))- out.append(verdict(article["id"], relevant, score, str(entry.get("reason", ""))))- else:- out.append(verdict(article["id"], False, 0.0, "scoring incomplete"))+ except Exception as exc:+ return refuse("malformed_json", str(exc))++ if not isinstance(result, dict):+ return refuse("unexpected_shape", f"top level is {type(result).__name__}")++ results_list = result.get("results")+ if not isinstance(results_list, list):+ return refuse("unexpected_shape", "results is not a list")++ keyed = [e for e in results_list if isinstance(e, dict) and "article_id" in e]++ if keyed:+ return _parse_keyed(articles, keyed)++ # The fallback. Every verdict lacks an id, so associate by position --+ # which is the defect this candidate was written to remove, reintroduced+ # under a condition that looked like it excluded the defective cases.+ return _parse_positional(articles, results_list)+++def _parse_keyed(articles, entries):+ wanted = {a["id"] for a in articles}+ seen: dict[str, dict] = {}+ for entry in entries:+ aid = entry.get("article_id")+ if not isinstance(aid, str):+ return refuse("invalid_type", "article_id is not a string")+ if aid in seen:+ return refuse("duplicate_id", aid)+ if aid not in wanted:+ return refuse("unknown_id", aid)+ seen[aid] = entry++ missing = wanted - set(seen)+ if missing:+ return refuse("missing_id", ", ".join(sorted(missing))[:200])++ out = []+ for article in articles:+ entry = seen[article["id"]]+ score = finite_unit_score(entry.get("score"))+ if score is None:+ return refuse("score_out_of_range", f"{article['id']}: {entry.get('score')!r}")+ relevant = entry.get("relevant")+ if not isinstance(relevant, bool):+ return refuse("invalid_type", f"{article['id']}: relevant is not a bool")+ out.append(verdict(article["id"], relevant, score, entry.get("reason", "")))+ return ok(out)+++def _parse_positional(articles, entries):+ if len(entries) != len(articles):+ return refuse("count_mismatch", f"{len(entries)} verdicts for {len(articles)} articles")++ out = []+ for article, entry in zip(articles, entries):+ if not isinstance(entry, dict):+ return refuse("unexpected_shape", "a verdict is not an object")+ score = finite_unit_score(entry.get("score"))+ if score is None:+ return refuse("score_out_of_range", f"{article['id']}: {entry.get('score')!r}")+ relevant = entry.get("relevant")+ if not isinstance(relevant, bool):+ relevant = score >= 0.5+ out.append(verdict(article["id"], relevant, score, entry.get("reason", ""))) return ok(out)
Reproduce this run
cd backend EVAL_OFFLINE=1 venv/bin/python -m lab.orchestrate \ --candidate keyed-fallback-v1 --tag clean cd ../web && npm run export:lab -- --check
Source under test
backend/lab/contract/candidates/keyed_fallback_v1.pysha256 a404e7b1e54f842d… · 5479 bytestranscribed from unknown
Timeline
1 attempt- 01succeededsandbox cyan-zonal-lark-rRFL5D in iad1, exit 0 in 523msvercel-sandbox · started 2026-09-22T20:20:21.318Z · ended 2026-09-22T20:20:28.263Z
Durability makes orchestration recoverable; it does not make a sandbox creation or a publish happen exactly once. An attempt the orchestrator never saw finish is recorded as unknown-outcome rather than assumed to have failed.
Cases
60 scored, 4 not applicable- Recorded cases
- 39real batches, replayed
- Fault-injected
- 21labelled synthetic
- Correct
- 60of the scored cases
- Wrong
- 0see the table
Every scored case was correct. That is what the verdict above is asserting, and nothing more.
Unscored
Measured, and deliberately not gradedA criterion decides; a diagnostic reports. Promoting one of these to a criterion would change the spec hash and re-decide runs that never faced it, so a gap found after the fact is published as a number rather than closed behind your back.
- 4/4
On cases outside its declared protocol, did it refuse — or associate anyway?
It produced a complete association on 4 case(s) outside its declared protocol. Under this generation that fails protocol-exclusivity; under generation 1 it was not graded at all.
observed-2026-09-02-005, observed-2026-09-02-035, observed-2026-09-02-041, syn-positional-reordered
Provenance
What can and cannot be established- Executed at revision
- 192e1a54b3556bbd6351b86d705553d633ba2641+dirtyrecorded when the harness ran, not re-derived at export
- Inputs sha256
- 70b83e3c09031c8fcases, records, candidates and event logs
- Evaluator sha256
- 277ec81521ba14d8
- Spec hash
- f027762ab4d08b35
- Execution mode
- offline-replayThe harness reads committed responses from disk and makes no network call. The candidate imports nothing beyond the standard library.
- Python
- 3.13.1
- Model calls
- 0
- Spend for this run
- $0Offline replay of committed recordings: no inference call was made, so provider spend for this run is $0. What the original recordings cost is not attributed per batch anywhere in this repository, so it is left unknown rather than estimated. This run also provisioned a microVM, which is metered compute rather than free: 2436ms of active CPU across 869ms wall clock. That is billed by the platform at a rate this repository does not record, so the dollar figure is not stated rather than guessed.
- Recording cost
- unknown
- Sandbox
- cyan-zonal-lark-rRFL5Dpython3.13 in iad1, booted in 346ms
- Network policy applied
- deny-allread back off the microVM, not the value that was requested
Case suites
backend/lab/cases/observed.json42 cases · sha256 3d7f4b4143d85ab3…backend/evals/.cache/llm via offline replay
backend/lab/cases/synthetic.json22 cases · sha256 21c92da332a9837e…lab/build_synthetic.py — fault injection, ground truth by construction
- Note. Every case ran offline against responses already committed to this repository. No inference call was made and no provider was charged.backend/evals/.cache/llm
- Caution. The case suite is public. A candidate may have been written against it, so passing does not establish generalisation.backend/lab/cases/
The full artifact, as published
Validated against the schema in web/lib/lab/artifact.ts before it was written. Download the JSON.