feat(consume): the payload says what of the question it reached, and row 4 reads it

`coverage` carries three lists: the terms the pre-pass read the question as,
the terms no concept in the bundle answers, and the terms no delivered
excerpt answers. Without it a reader holding eight excerpts cannot tell a
bundle that ANSWERED its question from one that merely ranked something --
the two payloads have the same shape.

FACTS, AND NO VERDICT, which is a measurement and not caution. Two readings
were built and both falsified over 81 questions (16 synthetic, 65 across the
three real sets, 2026-09-20): the share of question terms a delivered
excerpt answers separates the synthetic controls at 0.33 against 0.50 and
REVERSES on real data (covered questions down to 0.27, one genuinely
uncovered question at 0.71); the share of a bundle tying the best lexical
match is ~0.00 for every real question either way. Question style dominates
the first, corpus size the second.

The one bar this repository declares is the gate's: `UNANSWERED_BAR = 2/3`
over `unanswered_in_bundle`, swept and collapsing at both ends -- at 0.50
eleven real covered questions are marked, at 0.70 the row falls to 5 of 6,
at 2/3 the row is 6 of 6 and 0 of 65 real questions are marked. The margin
is thin (0.6087 against 0.6667) and is published that way, together with
what it does not catch: r761-sk2's own known-negative sits at 0.2857.

Row 4: 3 of 6 RED -> 6 of 6 GREEN, with the 10 answered synthetic questions
held unmarked as the known-negative. The contract's SS 8 gains point 7, the
consumption skill is told to read the block, and the SS 7.4 known-positive
moves with the document (14 721/375 -> 16 389/417). Suite 2292 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-20 08:44:06 +02:00
commit 05cb19087a
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
8 changed files with 216 additions and 42 deletions

View file

@ -160,6 +160,11 @@ are permitted; the checker reads only the members this section names.
"known_positive": { "case": "<name>", "expected": 10406, "measured": 10406 }
},
"denominators": { "considered": 439, "withheld": 401, "delivered": 38 },
"coverage": {
"question_terms": ["<the terms the pre-pass read the question as>"],
"unanswered_in_bundle": ["<those no concept in the bundle answers>"],
"unanswered_in_payload": ["<those no delivered excerpt answers>"]
},
"excerpts": [
{
"bundle_id": "<id>",
@ -224,6 +229,27 @@ are permitted; the checker reads only the members this section names.
the enclosing concept's text inside `parent``text`, with that concept's
own `sha256`, and `truncated` where it was cut to fit — and then only from
the room its cut left, so that inherited text never displaces an excerpt.
7. A pre-pass SHOULD carry `coverage`: the terms it read the question as, the
ones no concept in the bundle answers, and the ones no delivered excerpt
answers. Without it a reader holding eight excerpts cannot tell a bundle
that answered its question from one that merely ranked something — the two
payloads have the same shape — and § 6's states are then unreachable from
the payload alone.
**Facts, and no verdict.** The member carries lists and never a score, a
confidence or a "weak" flag, because the two obvious verdicts were built
and falsified against 81 questions on 2026-09-20: the share of a question's
terms a delivered excerpt answers separates a synthetic corpus at 0.33
against 0.50 and then reverses on real data, where covered questions run
down to 0.27 while one genuinely uncovered question sits at 0.71; and the
share of a bundle tying the best lexical match is ~0.00 for every question
in a large bundle, covered or not. Question style dominates the first and
corpus size the second, so a pre-pass emitting a verdict would assert
across corpora what was measured on one. Where a bar belongs is in the
consumer's own judgement or in a gate that publishes its sweep.
The checker does not read this member, for the same reason: it holds a
payload's FORM, and these lists carry no claim a form can be held to.
## 9. Prohibitions

View file

@ -199,6 +199,16 @@ at which this strategy stops fitting the budget.>`
The payload reports three counts — `considered`, `withheld`, `delivered` — and
`considered == withheld + delivered`. Carry them into your output.
It also reports **what of the question it reached**. `coverage` carries the
terms the pre-pass read the question as, the terms no concept in the bundle
answers, and the terms no delivered excerpt answers. Read it before you answer.
It carries no score and no verdict — deliberately: two were built and both
reversed on real corpora, so the judgement is yours. Where the bundle answers
none of the terms that make the question specific, say so and stop; do not
compose an answer out of excerpts that were ranked anyway. A cut always returns
its best candidates, so an ungrounded answer looks exactly like a grounded one
until somebody checks which of the asked-about words actually arrived.
Any claim of the form "there is no X", "nothing further was found" or "all N are
Y" reports the denominator it was measured over and the command that produced
it. A negative result whose scope is unstated is **unmeasured**, and is reported

View file

@ -210,7 +210,7 @@ carries its denominator.
| Limit | `120000` |
| Unit | `utf-8 bytes of emitted JSON` |
| Instrument | `okf_consume.measure (len of the ensure_ascii=False JSON encoding, utf-8)` |
| Known-positive | `docs/consumption-contract.md, encoded as a JSON string` at `14721` |
| Known-positive | `docs/consumption-contract.md, encoded as a JSON string` at `16389` |
The instrument reproduces the known-positive figure before any of its own
numbers are believed. Report what the run actually spent.
@ -263,6 +263,16 @@ full one is not evidence of presence.** When the delivered excerpts do not
actually answer the question, say `[sourced-not-sufficient]` and report that
the cut found nothing responsive.
It also reports **what of the question it reached**. `coverage` carries the
terms the pre-pass read the question as, the terms no concept in the bundle
answers, and the terms no delivered excerpt answers. Read it before you answer.
It carries no score and no verdict — deliberately: two were built and both
reversed on real corpora, so the judgement is yours. Where the bundle answers
none of the terms that make the question specific, say so and stop; do not
compose an answer out of excerpts that were ranked anyway. A cut always returns
its best candidates, so an ungrounded answer looks exactly like a grounded one
until somebody checks which of the asked-about words actually arrived.
Any claim of the form "there is no X", "nothing further was found" or "all N are
Y" reports the denominator it was measured over and the command that produced
it. A negative result whose scope is unstated is **unmeasured**, and is reported

View file

@ -11,10 +11,10 @@
"spent": 2289,
"known_positive": {
"case": "docs/consumption-contract.md, encoded as a JSON string",
"expected": 14721,
"measured": 14721,
"raw_bytes": 14346,
"encoding_delta": 375
"expected": 16389,
"measured": 16389,
"raw_bytes": 15972,
"encoding_delta": 417
}
},
"denominators": {
@ -23,6 +23,22 @@
"delivered": 3
},
"question": "Hva sier veiledningen om krav?",
"coverage": {
"question_terms": [
"hva",
"sier",
"veiledningen",
"krav"
],
"unanswered_in_bundle": [
"hva",
"sier"
],
"unanswered_in_payload": [
"hva",
"sier"
]
},
"excerpts": [
{
"bundle_id": "b-golden-segmented-okf-v0-2",

View file

@ -661,14 +661,14 @@ KNOWN_POSITIVE_CASE = "docs/consumption-contract.md, encoded as a JSON string"
#: `measure()`'s own answer for that file. Vacuous ALONE -- which is why the
#: delta below exists.
KNOWN_POSITIVE_EXPECTED = 14_721
KNOWN_POSITIVE_EXPECTED = 16_389
#: The second, independent route. `wc -c` reports 14 346 raw bytes for the same
#: The second, independent route. `wc -c` reports 15 972 raw bytes for the same
#: file; the difference is this file's JSON quoting and escaping overhead. A
#: reader can derive it without running `measure()` at all, and it moves the
#: moment `measure()` changes what it counts -- which is what stops
#: `expected == measured` from proving nothing.
KNOWN_POSITIVE_ENCODING_DELTA = 375
KNOWN_POSITIVE_ENCODING_DELTA = 417
#: The two places that file can be, resolved in this order.
#:
@ -2091,6 +2091,58 @@ def root_bundle_id_of(bundle_root: Path, *, profile: BundleProfile = DEFAULT_PRO
return declared
def excerpt_text(excerpt: Mapping[str, object]) -> str:
"""The strings a delivered excerpt actually shows its reader.
Title, id and body, which is the same union the two lexical signals score
-- a coverage report counting a term the consumer cannot see would be a
report about the ranker rather than about the payload.
"""
return " ".join(str(excerpt.get(key, "")) for key in ("title", "concept_id", "text"))
def unanswered_terms(
terms: Sequence[str],
tokenised: Sequence[Sequence[str]],
*,
stems: frozenset[str] | None,
) -> list[str]:
"""The question terms none of `tokenised` answers, in the question's order.
FACTS, NEVER A VERDICT, and that is a measurement rather than caution. Two
readings were built and both were falsified against 81 questions (16
synthetic, 65 over the three real sets, 2026-09-20):
- the SHARE of question terms a delivered excerpt answers separates the
synthetic controls at 0.33 against 0.50 and then reverses on real data,
where covered questions run down to 0.27 and one real uncovered question
sits at 0.71. Question STYLE dominates it: a question carrying its own
instructions has a long tail of words no corpus answers.
- the share of the bundle that ties the best lexical match separates the
synthetic corpus and is ~0.00 for every real question, covered or not,
because a 4 369-concept bundle has no ties to speak of.
So this module states what it measured and stops there. A bar over these
lists is the READER's -- a model weighing "the bundle answers none of
`koster`, `doegn`, `gjester`" is doing something no constant here can do
across corpora, and `tools/okf_retrieval_gate.py`'s row 4 carries the one
bar this repository does declare, with its own measurement beside it.
The matcher is the ranker's own (`tokens_match`, same `stems`). The cost
vocabulary's bridge is NOT applied: it is off by default and it is a rule
about documents naming money in other words, which would make a term read
as answered by a text that never carries it.
"""
remaining = set(terms)
for tokens in tokenised:
if not remaining:
break
for term in tuple(remaining):
if any(tokens_match(term, other, stems=stems) for other in tokens):
remaining.discard(term)
return [term for term in terms if term in remaining]
def build_payload(
bundle_root: Path,
*,
@ -2165,28 +2217,17 @@ def build_payload(
for concept_id in concept_ids
]
)
# The text the two lexical signals read, tokenised ONCE: the stem
# vocabulary, the rarity `df` and the coverage report below all count over
# the same strings, so none of them can weigh a token by how rare it is
# somewhere it is not read.
texts = searchable_text(concepts, link_in_signal=link_in_signal)
tokenised = [normalise(text) for text in texts]
# The bundle's OWN vocabulary, and the reason the rule is a set rather than
# a threshold: `pris` is a word here and `bila` is not, which is what
# separates a Norwegian compound from four coincidental characters. One
# pass, over the same text the ranking reads.
stems = (
frozenset(
token
for text in searchable_text(concepts, link_in_signal=link_in_signal)
for token in normalise(text)
)
if stem_prefix
else None
)
weights = (
rarity_weights(
normalise(question),
searchable_text(concepts, link_in_signal=link_in_signal),
stems=stems,
)
if rarity_weight
else None
)
# separates a Norwegian compound from four coincidental characters.
stems = frozenset(token for tokens in tokenised for token in tokens) if stem_prefix else None
weights = rarity_weights(normalise(question), texts, stems=stems) if rarity_weight else None
ranked = concept_scores(
concepts,
question,
@ -2238,6 +2279,7 @@ def build_payload(
# one-sided, and kept because SS 7.3 is a MUST about the emitted
# payload rather than about the algorithm that produced it.
raise ConsumeError(f"spent ({spent}) exceeds limit ({limit})", code="budget_exceeded")
question_terms = list(dict.fromkeys(normalise(question)))
return {
"contract": CONTRACT_REVISION,
"bundle": {
@ -2275,6 +2317,18 @@ def build_payload(
"delivered": len(delivered),
},
"question": question,
# SS 8: what of the QUESTION this payload reaches. Facts, and no
# verdict -- see `unanswered_terms` for the two readings that were
# measured and felled.
"coverage": {
"question_terms": question_terms,
"unanswered_in_bundle": unanswered_terms(question_terms, tokenised, stems=stems),
"unanswered_in_payload": unanswered_terms(
question_terms,
[normalise(excerpt_text(excerpt)) for excerpt in delivered],
stems=stems,
),
},
"excerpts": list(delivered),
# Emitted only under the flag, and then only where the concept carries
# a title, so a bundle whose concepts have none produces the same bytes

View file

@ -2661,14 +2661,14 @@ def test_the_payload_names_the_question_terms_the_bundle_answers_to_none_of() ->
the ones no delivered excerpt answers.
"""
payload = okf_consume.build_payload(
FIXTURE, question="Hva koster et doegn paa hytta for gjester?"
FIXTURE, question="Hva koster et doegn for gjester i kravet?"
)
assert "coverage" in payload, "the payload says nothing about what it covered"
coverage = payload["coverage"]
assert isinstance(coverage, dict)
# The terms are the ranker's own, counted here rather than read back.
expected = list(
dict.fromkeys(okf_consume.normalise("Hva koster et doegn paa hytta for gjester?"))
dict.fromkeys(okf_consume.normalise("Hva koster et doegn for gjester i kravet?"))
)
assert coverage["question_terms"] == expected
assert expected, "a question with no terms would make every assertion below vacuous"

View file

@ -297,9 +297,18 @@ def test_row_three_is_green_when_the_printed_reason_is_the_true_one(tmp_path: Pa
# --- row 4 --------------------------------------------------------------------
def test_row_four_is_red_because_an_uncovered_question_still_gets_excerpts(
tmp_path: Path,
def test_row_four_goes_red_again_when_the_payload_stops_saying_what_it_missed(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""The RED direction, driven by an input: the payload's own words.
This row was 3 of 6 until 2026-09-20 because the payload said nothing
about what of the question it reached, so N3, N4 and N5 -- 8, 8 and 1
excerpts -- were indistinguishable from an answered question. A payload
that reports every term as answered puts the row back there, with the same
detail line.
"""
monkeypatch.setattr(consume, "unanswered_terms", lambda *args, **kwargs: [])
row = gate.row_four([_case(tmp_path, "set-controls.json")])
assert row.status == gate.RED
assert row.m == 6
@ -639,7 +648,7 @@ 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]
assert [row.number for row in rows if row.fails] == [4, 5, 7, 8, 9]
assert [row.number for row in rows if row.fails] == [5, 7, 8, 9]
# 10, not 9: `set-quota.json` adds row 3's known-positive, one question the
# source quota genuinely decides, and it is a hit.
assert (by_number[1].k, by_number[1].m) == (10, 10)
@ -661,7 +670,7 @@ def test_the_command_exits_one_and_prints_every_row(
printed = capsys.readouterr().out
for number in range(1, 10):
assert f"\n{number} " in f"\n{printed}"
assert "GATE RED: rows 4, 5, 7, 8, 9" in printed
assert "GATE RED: rows 5, 7, 8, 9" in printed
def test_the_json_form_carries_the_same_rows(capsys: pytest.CaptureFixture[str]) -> None:

View file

@ -1157,21 +1157,68 @@ def row_three(cases: Sequence[Case]) -> Row:
)
#: Row 4's bar, and the ONE threshold this gate applies to a payload.
#:
#: SWEPT over 81 questions on 2026-09-20 -- the 16 of the synthetic sets and
#: the 65 of the three real sets -- against `coverage.unanswered_in_bundle` as
#: a share of the question's own terms:
#:
#: - at **0.50** row 4 is 6 of 6 and ELEVEN real questions whose fasit is in
#: their bundle come back marked;
#: - at **2/3** row 4 is 6 of 6 and **0 of 65** real questions are marked; the
#: highest a real covered question reaches is 0.6087 (two of the vegnormal
#: set's, 14 of 23 terms), and the lowest an uncovered control reaches is
#: 0.6667 (N3, 4 of 6);
#: - at **0.70** N3 falls under the bar and row 4 is 5 of 6.
#:
#: It collapses at both ends, which is what makes 2/3 a reading of the
#: distribution rather than a number picked to pass. The margin is THIN --
#: 0.6087 against 0.6667 -- and it is published that way.
#:
#: WHAT IT DOES NOT CATCH, measured on the same data: `r761-sk2`'s own
#: known-negative, a question naming a section number the document does not
#: have, sits at 0.2857 -- every other word of it is in a 2 756-concept road
#: standard. One term absent among seven is not a share, and no bar over this
#: list reaches it. Row 4's denominator is the synthetic controls, so this
#: costs the row nothing and is stated rather than implied.
UNANSWERED_BAR = 2 / 3
def marked(payload: Mapping[str, object]) -> bool:
"""Can a consumer READ, from the payload alone, that the bundle does not
cover the question?
Today exactly one reading does that: nothing was delivered, and the
withheld rules say why. Eight excerpts with no score, no confidence and no
statement is not a marked answer -- it is the same payload an answered
question gets. When the payload grows a field that says so, this function
is where it is read, and the row moves with it.
Two readings, and both are facts the payload carries:
- nothing was delivered, and the withheld rules say why; or
- the bundle answers none of at least `UNANSWERED_BAR` of the question's
own terms (`coverage.unanswered_in_bundle`, added to the payload
2026-09-20 for exactly this reading).
Eight excerpts with no score, no confidence and no statement is not a
marked answer -- it is the same payload an answered question gets, which
is what took this row to 3 of 6 on N3, N4 and N5.
THE BAR IS THIS GATE'S AND NOT THE LIBRARY'S. `consume.unanswered_terms`
records the two readings that were built and falsified against real
corpora; a library asserting "this bundle does not cover your question"
would be asserting across corpora what was measured on one. A gate is
where a declared bar belongs, and this one carries its sweep above.
"""
counts = payload.get("denominators")
assert isinstance(counts, dict)
delivered = counts["delivered"]
assert isinstance(delivered, int)
return delivered == 0
if delivered == 0:
return True
coverage = payload.get("coverage")
assert isinstance(coverage, dict), "the payload carries no coverage block to read"
terms = coverage["question_terms"]
unanswered = coverage["unanswered_in_bundle"]
assert isinstance(terms, list) and isinstance(unanswered, list)
if not terms:
return True
return len(unanswered) / len(terms) >= UNANSWERED_BAR
def row_four(cases: Sequence[Case]) -> Row:
@ -1195,7 +1242,8 @@ def row_four(cases: Sequence[Case]) -> Row:
"covered by the bundle and MARKED anyway"
if control.covered
else f"not covered and NOT marked -- {counts['delivered']} "
"excerpts, no score, no statement"
"excerpts, and the bundle answers enough of the question's "
"terms that nothing in the payload says so"
)
)
return _row(
@ -1203,7 +1251,8 @@ def row_four(cases: Sequence[Case]) -> Row:
"an uncovered question comes back marked, a covered one does not",
correct,
total,
"marked = a reading the consumer can act on (today: nothing delivered)",
"marked = a reading the consumer can act on: nothing delivered, or the "
f"bundle answers none of >= {UNANSWERED_BAR:.0%} of the question's own terms",
details,
)