test(gates): retire the K2 track; re-measure the retrieval gate's premises for BM25

Operator decision 2026-09-21: the test track built on material tied to the
operator's employer (K2) is retired -- not re-measured, not frozen. Public
tests and gates run on invented material.

Retrieval gate:
- The four FUSION_PREMISE xfails are gone and pass through their INPUTS: the
  synthetic MISS, LOOKUP and QUOTA bundles were re-measured for BM25 (the
  miss fasit no longer shares the rare word `maa`; lookup and quota decoys
  carry the question's words so each partition and the quota decide their
  own fixture). SPECS_SHA256 moved with them. Rows 2 and 3 green again.
- Row 7's mutants M04, M06, M07, M08, M10 now patch `bm25`, the code the
  default runs. Three survive with 0 ranks moved (passage body, title
  weight, bm25.RRF_K), each with its mechanism printed. M07 was not forced:
  every synthetic body carries its title as a heading.
- Row 9 (K2) removed; row 8 requires `wiki-20` alone, the `r761` and
  `vegnormal` adapters are gone. Chose the broad reading of "K2" because the
  operator decision defines it as employer-tied material and the order's
  grep includes `vegnormal`.

Also removed: tests/test_default_bundle_pin.py, the K2 arms of
test_okf_consume, the four real-arm tests of test_quality, the R761 soft
hyphen test, the N101/N200 delivery tests and okf_accounting_gate's default
real corpus (and H5's guard, which only existed for those defaults). Two
fixtures carrying road-standard identifiers are rewritten with invented ones.

Gate after: 1 10/10, 2 7/7, 3 5/5, 4 6/6, 5 0/1, 6 10/10, 7 11/14,
8 NOT RUN -> GATE RED: rows 5, 7, 8. Suite 2423 passed, 1 skipped,
0 xfailed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-21 10:05:43 +02:00
commit 3d149f955a
12 changed files with 288 additions and 1096 deletions

View file

@ -10,10 +10,12 @@ Three rules this suite is written under, all of them the house pattern:
- **Every zero carries a control.** The socket guard is fired before its
silence during a run counts as evidence; the sha pin is shown to refuse a
tampered set before its silence on the committed ones means anything.
- **Nothing here touches a private corpus or the network.** The real sets in
`claude-code-llm-wiki` and `vegnormal-okf` are never read by a test: the
adapters are exercised against files written in their shapes, with invented
content, inside `tmp_path`.
- **Nothing here touches a private corpus or the network.** The real set in
`claude-code-llm-wiki` is never read by a test: its adapter is exercised
against files written in its shape, with invented content, inside
`tmp_path`. The test track built on material tied to the operator's
employer was retired 2026-09-21, and with it the gate's row 9 and two
adapters; nothing below reads, needs or names that material.
"""
from __future__ import annotations
@ -130,19 +132,6 @@ def test_row_one_is_green_when_the_ranker_delivers_every_fasit(tmp_path: Path) -
assert (row.k, row.m, row.status) == (9, 9, gate.GREEN)
#: v1.1 C1 moved the default ranking to BM25, and four of this gate's premises
#: were built against the fusion it replaced: `set-miss` and B1/B2 force a
#: concept BELOW k that BM25 now delivers, `set-quota` needs the source quota to
#: decide a cut it no longer decides, and row 7's mutants patch fusion code the
#: default no longer runs. The gate says so itself (`premise broken`, `NOT
#: RUN`, `8 of 14`). STRICT, so the day the fixtures are re-measured for BM25
#: these flip loudly instead of staying skipped.
FUSION_PREMISE = pytest.mark.xfail(
strict=True,
reason="fixture premise built against the fusion ranking; re-measure for BM25",
)
def test_row_one_is_red_when_a_fasit_is_not_delivered(tmp_path: Path) -> None:
path, sha = _set_file(
tmp_path / "set.json",
@ -180,7 +169,6 @@ def test_row_one_never_counts_a_question_that_declares_the_class_it_forces(
# --- row 2 --------------------------------------------------------------------
@FUSION_PREMISE
def test_row_two_is_green_when_every_class_is_the_one_its_fixture_forces(
tmp_path: Path,
) -> None:
@ -276,7 +264,6 @@ class _AlwaysTheQuota(dict[str, str]):
return "source_quota_exceeded"
@FUSION_PREMISE
def test_row_three_is_green_on_the_shipped_code(tmp_path: Path) -> None:
"""And the same three fixtures, unmutated, are the green direction.
@ -494,7 +481,6 @@ def _noop_mutant() -> gate.Mutant:
return gate.Mutant("N01 nothing is changed", 1, lambda: gate._patched())
@FUSION_PREMISE
def test_row_seven_is_green_when_every_mutant_is_felled(tmp_path: Path) -> None:
cases, _ = gate.synthetic_cases(tmp_path / "bundles", FIXTURES)
baseline = gate.deterministic_rows(cases)
@ -538,7 +524,7 @@ def test_a_mutant_is_felled_by_the_row_that_got_worse_and_never_by_one_that_did_
assert after[3] >= before[3]
# --- rows 8 and 9 -------------------------------------------------------------
# --- row 8 --------------------------------------------------------------------
def test_row_eight_is_never_green_when_it_did_not_run() -> None:
@ -574,72 +560,21 @@ def test_the_wiki_adapter_reads_its_own_shape_and_hits_by_source_file(tmp_path:
assert question_set.questions[0].fasit[0].by == "source_file"
assert question_set.questions[0].fasit[0].value == "haandbok.md"
row = gate.row_eight([(question_set, {"wiki": _bundles(tmp_path)["positive"]})])
# J2: one set of three is NOT a measurement of the three -- this asserted
# GREEN until 2026-09-19, which is the breakthrough PM measured.
assert row.status == gate.NOT_RUN
# The one required set, answered: row 8's own green direction. What
# refuses a self-written file of this shape is the pin on the command-line
# path (`test_a_wiki_set_of_one_question_is_refused_on_the_command_line`).
assert (row.k, row.m, row.status) == (1, 1, gate.GREEN)
assert any("citation granularity" in detail for detail in row.details)
def test_the_r761_adapter_splits_the_known_negative_out_of_the_questions(
tmp_path: Path,
) -> None:
path = tmp_path / "r761-shaped.json"
path.write_text(
json.dumps(
{
"fasit_form": "the normalised STS title",
"sporsmal": [
{"id": "S1", "sporsmal": "Hva sier punktet?", "fasit": "4.2 Vakthold"},
{"id": "KN", "sporsmal": "Hvilken safran passer til fiskesuppe?", "fasit": ""},
],
}
),
encoding="utf-8",
)
question_set = gate.read_real_set("r761", path, gate.sha256_of(path))
assert [q.id for q in question_set.questions] == ["S1"]
assert [c.id for c in question_set.controls] == ["KN"]
assert question_set.questions[0].fasit[0].by == "title"
# No quote in this set's fasit, so it is concept granularity and says so.
assert not question_set.quoted
def test_the_vegnormal_adapter_makes_one_question_per_standard(tmp_path: Path) -> None:
path = tmp_path / "vegnormal-shaped.json"
path.write_text(
json.dumps(
{
"sporsmal": [
{
"id": "T1-1",
"sporsmal": "Hva viser kravet til?",
"must_cite": [
{"normal": "N100:2023", "req_number": "2.3.2-3", "fil": "a"},
{"normal": "N200:2024", "req_number": "1.6.3-3", "fil": "b"},
],
}
]
}
),
encoding="utf-8",
)
question_set = gate.read_real_set("vegnormal", path, gate.sha256_of(path))
assert sorted(q.id for q in question_set.questions) == ["T1-1/N100:2023", "T1-1/N200:2024"]
assert {q.bundle for q in question_set.questions} == {"N100:2023", "N200:2024"}
assert question_set.units == 2
def test_an_unknown_real_set_name_is_refused(tmp_path: Path) -> None:
path = tmp_path / "x.json"
path.write_text("{}", encoding="utf-8")
with pytest.raises(gate.GateUsage):
gate.read_real_set("something-else", path, gate.sha256_of(path))
def test_row_nine_states_k2_s_denominator_and_never_passes() -> None:
row = gate.row_nine()
assert (row.k, row.m, row.status) == (0, 6, gate.RED)
assert any("the answer key does not" in detail for detail in row.details)
# The two retired adapters are unknown names now, like any other.
for name in ("something-else", "r761", "vegnormal"):
with pytest.raises(gate.GateUsage) as refusal:
gate.read_real_set(name, path, gate.sha256_of(path))
assert "unknown real set" in str(refusal.value)
# --- the verdict --------------------------------------------------------------
@ -667,14 +602,16 @@ def test_the_verdict_names_every_failing_row() -> None:
def test_the_gate_is_red_today_and_says_which_rows(tmp_path: Path) -> None:
rows = gate.evaluate(tmp_path / "bundles")
by_number = {row.number: row for row in rows}
assert sorted(by_number) == [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Rows 2, 3 and 7 went red with v1.1 C1's BM25 default: see
# `FUSION_PREMISE`. Row 1 holds every fasit it held.
assert [row.number for row in rows if row.fails] == [2, 3, 5, 7, 8, 9]
assert sorted(by_number) == [1, 2, 3, 4, 5, 6, 7, 8]
# Rows 2 and 3 are green again since the synthetic corpus was re-measured
# for BM25 (2026-09-21). Row 7 is red on three survivors, each printed with
# what it moved.
assert [row.number for row in rows if row.fails] == [5, 7, 8]
assert (by_number[1].k, by_number[1].m) == (10, 10)
assert (by_number[2].k, by_number[2].m) == (5, 7)
assert (by_number[3].k, by_number[3].m) == (3, 5)
assert (by_number[6].k, by_number[6].m) == (12, 12)
assert (by_number[2].k, by_number[2].m) == (7, 7)
assert (by_number[3].k, by_number[3].m) == (5, 5)
assert (by_number[6].k, by_number[6].m) == (10, 10)
assert (by_number[7].k, by_number[7].m) == (11, 14)
def test_the_same_tree_measures_the_same_twice(tmp_path: Path) -> None:
@ -688,15 +625,15 @@ def test_the_command_exits_one_and_prints_every_row(
) -> None:
assert gate.main([]) == 1
printed = capsys.readouterr().out
for number in range(1, 10):
for number in range(1, 9):
assert f"\n{number} " in f"\n{printed}"
assert "GATE RED: rows 2, 3, 5, 7, 8, 9" in printed
assert "GATE RED: rows 5, 7, 8" in printed
def test_the_json_form_carries_the_same_rows(capsys: pytest.CaptureFixture[str]) -> None:
assert gate.main(["--json"]) == 1
payload = json.loads(capsys.readouterr().out)
assert [row["row"] for row in payload["rows"]] == list(range(1, 10))
assert [row["row"] for row in payload["rows"]] == list(range(1, 9))
assert payload["gate"] == gate.RED
assert set(payload["classes"]) == {"a", "b", "c", "d", "e"}
assert payload["limits"]
@ -879,26 +816,25 @@ def _hitting_set(set_id: str, *, quote: str, entries: int = 1) -> gate.QuestionS
def test_j2_row_eight_is_not_run_when_a_required_set_is_left_out(tmp_path: Path) -> None:
"""PM's J2: one set of three came back `6 of 6 GREEN`."""
"""PM's J2: one set of three came back `6 of 6 GREEN`. The rule outlived
the two retired sets: a set that is not the required one measures
something, and the row is still not a measurement of the required set."""
bundles = {"positive": _bundles(tmp_path)["positive"]}
row = gate.row_eight([(_hitting_set("wiki-20", quote="innen 1. november"), bundles)])
row = gate.row_eight([(_hitting_set("some-other-set", quote="innen 1. november"), bundles)])
assert row.status == gate.NOT_RUN
assert row.fails
assert "r761-sk2" in row.reason and "vegnormal-32" in row.reason
assert "wiki-20" in row.reason
# The numbers it DID measure are still carried: a missing set must not cost
# the reader the set that ran.
assert any("wiki-20: " in detail for detail in row.details)
assert any("some-other-set: " in detail for detail in row.details)
def test_row_eight_is_green_only_with_all_three_named_sets(tmp_path: Path) -> None:
def test_row_eight_is_green_only_with_every_named_set(tmp_path: Path) -> None:
bundles = {"positive": _bundles(tmp_path)["positive"]}
real = [
(_hitting_set(name, quote="innen 1. november"), bundles)
for name in ("wiki-20", "r761-sk2", "vegnormal-32")
]
real = [(_hitting_set(name, quote="innen 1. november"), bundles) for name in ("wiki-20",)]
row = gate.row_eight(real)
assert row.status == gate.GREEN
assert (row.k, row.m) == (3, 3)
assert (row.k, row.m) == (1, 1)
def test_j2b_row_eight_never_sums_the_two_granularities_into_its_headline(
@ -909,17 +845,16 @@ def test_j2b_row_eight_never_sums_the_two_granularities_into_its_headline(
bundles = {"positive": _bundles(tmp_path)["positive"]}
real = [
(_hitting_set("wiki-20", quote="innen 1. november", entries=2), bundles),
(_hitting_set("r761-sk2", quote=""), bundles),
(_hitting_set("vegnormal-32", quote=""), bundles),
(_hitting_set("an-invented-concept-set", quote=""), bundles),
]
row = gate.row_eight(real)
# Three questions, one per set: the headline is at QUESTION granularity.
# The sum it must NOT be is 4 of 4 -- two citation units plus two concept
# units -- which is why the first set carries two fasit entries.
assert (row.k, row.m) == (3, 3)
# Two questions, one per set: the headline is at QUESTION granularity.
# The sum it must NOT be is 3 of 3 -- two citation units plus one concept
# unit -- which is why the first set carries two fasit entries.
assert (row.k, row.m) == (2, 2)
assert "question" in row.reason
assert any(
"2 of 2 at citation granularity, 2 of 2 at concept granularity" in detail
"2 of 2 at citation granularity, 1 of 1 at concept granularity" in detail
for detail in row.details
)
@ -1081,40 +1016,6 @@ def test_a_corpus_that_is_not_the_pinned_corpus_is_refused(
assert "not the corpus that was pinned" in str(refusal.value)
def _k2_shaped(tmp_path: Path, questions: int) -> tuple[Path, str]:
"""A K2 gold set in this gate's own shape. `set-positive.json` carries
exactly six questions the bundle answers, which is K2's denominator."""
spec = json.loads((FIXTURES / "set-positive.json").read_text(encoding="utf-8"))
spec["set_id"] = "k2-gold"
spec["questions"] = spec["questions"][:questions]
path = tmp_path / "k2.json"
path.write_text(json.dumps(spec, ensure_ascii=False), encoding="utf-8")
return path, gate.sha256_of(path)
def test_row_nine_is_green_when_a_k2_gold_set_arrives(tmp_path: Path) -> None:
path, sha = _k2_shaped(tmp_path, gate.K2_QUESTIONS)
question_set = gate.load_set(path, sha)
row = gate.row_nine((question_set, _bundles(tmp_path)))
assert (row.k, row.m, row.status) == (6, 6, gate.GREEN)
def test_row_nine_is_red_when_the_gold_set_is_not_answered(tmp_path: Path) -> None:
path, sha = _k2_shaped(tmp_path, gate.K2_QUESTIONS)
question_set = gate.load_set(path, sha)
bundles = dict(_bundles(tmp_path))
bundles["positive"] = bundles["miss"] # the same six questions, the wrong bundle
row = gate.row_nine((question_set, bundles))
assert (row.k, row.m, row.status) == (0, 6, gate.RED)
def test_a_k2_set_of_another_size_is_another_set_and_is_refused(tmp_path: Path) -> None:
path, sha = _k2_shaped(tmp_path, gate.K2_QUESTIONS - 1)
with pytest.raises(gate.GateUsage) as refusal:
gate._k2_set([str(path), sha, str(tmp_path)])
assert "K2's denominator" in str(refusal.value)
def test_j7_a_cut_that_delivers_nothing_is_exit_two_and_never_a_quiet_green_row(
tmp_path: Path,
) -> None:
@ -1135,7 +1036,7 @@ def test_row_eight_names_the_bundle_identity_of_every_set_it_measured(
) -> None:
"""The row printed the SET's sha256 and never the bundle's identity.
Measured 2026-09-19 by PM with one pinned r761 set: three bundles gave
Measured 2026-09-19 by PM with one pinned real set: three bundles gave
4 of 7 and a fourth gave 0 of 7, while the session recorded 7 of 7 -- from
a bundle no line of the output named. A number nobody can attach to a
bundle cannot be reproduced and cannot be felled.
@ -1158,15 +1059,15 @@ def test_row_eight_names_the_bundle_identity_of_every_set_it_measured(
assert ref[:24] in block, f"{set_id}: the bundle ref is nowhere in the output"
def test_all_three_real_sets_are_pinned_and_not_only_the_first(tmp_path: Path) -> None:
"""Every one of the three, driven through the command-line path.
def test_every_required_real_set_is_pinned(tmp_path: Path) -> None:
"""Every required set, driven through the command-line path.
The names and the count are written out here rather than read from
`REQUIRED_REAL_SETS`: a test taking its denominator from the tuple it is
checking would stay green if a name were dropped from both.
"""
names = [("wiki", "wiki-20"), ("r761", "r761-sk2"), ("vegnormal", "vegnormal-32")]
assert len(names) == 3
names = [("wiki", "wiki-20")]
assert len(names) == len(gate.REQUIRED_REAL_SETS) == 1
bundle = _bundles(tmp_path)["positive"]
for adapter, set_id in names:
path = tmp_path / f"{adapter}-invented.json"
@ -1178,29 +1079,14 @@ def test_all_three_real_sets_are_pinned_and_not_only_the_first(tmp_path: Path) -
def _invented_real_set(adapter: str) -> str:
"""One question, in the adapter's own shape, with invented content."""
if adapter == "wiki":
return json.dumps(
{
"questions": [
{
"id": "W1",
"question": "Naar kontrolleres vinterberedskapen?",
"fasit": [{"doc": "haandbok", "quote": "innen 1. november"}],
}
]
}
)
if adapter == "r761":
return json.dumps(
{"sporsmal": [{"id": "S1", "sporsmal": "Hva sier punktet?", "fasit": "4.2 Vakthold"}]}
)
assert adapter == "wiki", adapter
return json.dumps(
{
"sporsmal": [
"questions": [
{
"id": "T1-1",
"sporsmal": "Hva viser kravet til?",
"must_cite": [{"normal": "N100:2023", "req_number": "2.3.2-3"}],
"id": "W1",
"question": "Naar kontrolleres vinterberedskapen?",
"fasit": [{"doc": "haandbok", "quote": "innen 1. november"}],
}
]
}
@ -1282,7 +1168,6 @@ def test_a_threshold_that_is_not_a_number_is_refused(tmp_path: Path) -> None:
assert row.fails
@FUSION_PREMISE
def test_the_threshold_is_compared_with_the_measured_hold_out(tmp_path: Path) -> None:
"""Both directions, from the same code path: a set the bundle answers
clears a threshold under it, and a set it does not answer falls under one