Daily

Daily Lab · recorded replay · run 008af826 · clean

keyed-v2

Accepted for review

all 6 criteria satisfied over 60 applicable casesUnder the current criteria, generation 2. This run faced generation 1 when it executed — both verdicts are below.

Proposed contract: every verdict names its article, the id set must match exactly, and a non-stop finish_reason is a failure.

01

Verdict

2 criteria generations

Criteria generation

spec f027762ab4d08b35

Accepted for review

all 6 criteria satisfied over 60 applicable cases

Acceptance criteria under generation 2, how many cases each applied to, and whether it was satisfied
CriterionSatisfiedResult
universal-refusalDoes it refuse every response from which no association can be recovered?48/48met
association-exactOn its own protocol, does every article receive exactly the verdict it was given?3/3met
protocol-violation-refusalDoes it refuse duplicate, unknown, missing ids and unusable scores?9/9met
no-crashDoes it terminate on every case without crashing or hanging?60/60met
complete-evidenceIs there a prediction record for every applicable case?60/60met
protocol-exclusivityOn cases outside its declared protocol, does it refuse rather than associate anyway?4/4met

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.

03

The patch

against positional_v0.py
Unified diff · 163 lines · applies with git apply
diff --git a/backend/lab/contract/versions/positional_v0.py b/backend/lab/contract/versions/keyed_v2.py--- a/backend/lab/contract/versions/positional_v0.py+++ b/backend/lab/contract/versions/keyed_v2.py@@ -1,18 +1,26 @@-"""Historical behaviour, transcribed from `origin/main`.+"""The proposed contract: every verdict identifies its own article. -Source: backend/app/services/openai_service.py, score_articles_batch, the-`normalized` loop. Verbatim semantics:+Modelled on the id-keyed validation Daily already ships in+`backend/app/services/ranking_contract.py:131` (`validate_judgments`), which is+the pattern this repository already trusts for the S7 ranking path: -    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"}+    packs = {e.article_id: e for e in evidence}+    ids = [v.article_id for v in values]+    if len(ids) != len(set(ids)) or set(ids) != set(packs):+        raise ValueError('ranker must return exact article ID set') -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.+One set-equality check covers duplicate, unknown and missing ids at once, and+refuses to salvage a partial answer. The same shape is reproduced here, plus+the truncation check `ranking_provider.py:296` performs and+`score_articles_batch` does not:++    if choice.get('finish_reason') != 'stop':+        raise ProviderFailure('incomplete_response')++What this version does NOT claim: that it ranks better. Association is a+correctness property, and correctness is all that is under test. Relevance+quality under this protocol is unmeasured — it needs recordings that do not+exist, because no budgeted keyed run has been made. """  from __future__ import annotations@@ -54,45 +62,88 @@ import json from typing import Any  -VERSION_ID = "positional-v0"-PROTOCOL = "positional-v0"+VERSION_ID = "keyed-v2"+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])+    error = response.get("error")+    if error:+        if error == "no_recording":+            return refuse("no_recording", "no recorded response for this request")+        if error in {"timeout", "cancelled"}:+            return refuse(error, error)+        if error == "budget_exceeded":+            return refuse("retries_exhausted", error)+        return refuse("retries_exhausted", str(error))++    # A truncated completion is a failure, not a shorter answer. Production+    # never reads finish_reason, which is why 33 recorded responses that stopped+    # at the output ceiling were indistinguishable from malformed ones.+    finish_reason = response.get("finish_reason")+    if finish_reason is not None and finish_reason != "stop":+        return refuse("truncated_response", f"finish_reason={finish_reason}")      content = response.get("content")     if content is None:-        return ok([verdict(a["id"], False, 0.0, "scoring unavailable") for a in articles])+        return refuse("retries_exhausted", "no content returned")      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"]-        ]+    except Exception as exc:+        return refuse("malformed_json", str(exc))++    if not isinstance(result, dict) or not isinstance(result.get("results"), list):+        return refuse("unexpected_shape", "expected an object with a results array")++    entries = result["results"]+    expected = {a["id"]: a for a in articles}++    seen: list[str] = []+    for entry in entries:+        if not isinstance(entry, dict):+            return refuse("invalid_type", f"entry is {type(entry).__name__}")+        article_id = entry.get("article_id", entry.get("id"))+        if not isinstance(article_id, str):+            return refuse("invalid_type", "article_id missing or not a string")+        seen.append(article_id)++    # Duplicate, unknown and missing are distinguished for the operator even+    # though any one of them is fatal. `validate_judgments` collapses all three+    # into one message; the Lab separates them so a counterexample can name the+    # exact failure, then refuses just as hard.+    if len(seen) != len(set(seen)):+        dupes = sorted({i for i in seen if seen.count(i) > 1})+        return refuse("duplicate_id", f"repeated ids: {', '.join(dupes[:5])}")+    unknown = [i for i in seen if i not in expected]+    if unknown:+        return refuse("unknown_id", f"ids not in the request: {', '.join(sorted(unknown)[:5])}")+    missing = [i for i in expected if i not in set(seen)]+    if missing:+        return refuse("missing_id", f"no verdict for: {', '.join(sorted(missing)[:5])}")      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"))-    return ok(out)+    for entry in entries:+        article_id = entry.get("article_id", entry.get("id"))+        score = finite_unit_score(entry.get("score"))+        if score is None:+            raw = entry.get("score")+            if isinstance(raw, (int, float)) and not isinstance(raw, bool):+                # Distinguishes 9e9 (out of range) from NaN (not finite); both+                # are clamped silently by production.+                return refuse(+                    "score_not_finite" if raw != raw or raw in (float("inf"), float("-inf"))+                    else "score_out_of_range",+                    f"score={raw!r} for {article_id}",+                )+            return refuse("invalid_type", f"score is {type(raw).__name__} for {article_id}")+        relevant = entry.get("relevant", score >= 0.5)+        if not isinstance(relevant, bool):+            return refuse("invalid_type", f"relevant is {type(relevant).__name__}")+        out.append(verdict(article_id, relevant, score, str(entry.get("reason", ""))))++    # Emit in request order so downstream consumers see a stable sequence. The+    # association itself is by id and does not depend on this ordering — the+    # permutation property test asserts exactly that.+    by_id = {v["article_id"]: v for v in out}+    return ok([by_id[a["id"]] for a in articles]) 

Reproduce this run

cd backend
EVAL_OFFLINE=1 venv/bin/python -m lab.orchestrate \
  --candidate keyed-v2 --tag clean
cd ../web && npm run export:lab -- --check

Source under test

backend/lab/contract/versions/keyed_v2.pysha256 bcde0d5383b54e71… · 6390 bytestranscribed from modelled on backend/app/services/ranking_contract.py:131

04

Timeline

1 attempt
  1. 01succeededcompleted 64 cases in 25.7mslocal-known · started 2026-09-22T19:47:57.596Z · ended 2026-09-22T19:47:57.621Z

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.

05

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.

06

Unscored

Measured, and deliberately not graded

A 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.

0/4

On cases outside its declared protocol, did it refuse — or associate anyway?

It produced no association on any case outside its declared protocol.

07

Provenance

What can and cannot be established
Executed at revision
4e8bee7625820107e84184f8e24c6bd7125f2e86+dirtyrecorded when the harness ran, not re-derived at export
Inputs sha256
7f0557be5e3bdcbbcases, 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.12.13
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.
Recording cost
unknown
Sandbox limits
python3.13, network disabled120s wall clock, none secrets. This candidate matched a committed implementation, so it ran locally and the boundary was not exercised here.

Case suites

backend/lab/cases/observed.json42 cases · sha256 3d7f4b4143d85ab3backend/evals/.cache/llm via offline replay

backend/lab/cases/synthetic.json22 cases · sha256 21c92da332a9837elab/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/
  • Caution. Association correctness is measured; relevance quality under keyed-v2 is not. Sending article ids changes the request, which invalidates every recorded response for this runner — new budgeted recordings would be required and none exist.backend/evals/llm_cache.py:133
The full artifact, as published

Validated against the schema in web/lib/lab/artifact.ts before it was written. Download the JSON.