feat(check): the contract and the checker state the payload's v1.1 forms
C6. `docs/consumption-contract.md` now says what the pre-pass emits:
- SS 2.5 point 2: sub-questions are written in the bundle's terms and given to
the pre-pass in ONE run where it takes several;
- SS 8's example is `okf-consumption/2` as shipped: `withheld` is the
`{total, by_rule, nearest, complete}` mapping, `coverage` carries
`absent_terms` and `weak`, an excerpt carries `text`, `text_sha256`,
`passage` and `own_title`; point 1 closes the identity on `withheld.total`;
- point 7 keeps the falsified verdicts on record and states the one reading
carried since v1.1, `weak`, with its rule: a word held in no form, or
nothing delivered -- whether a word exists at all, which does not move with
question style or corpus size the way the two shares did;
- point 8 the passage (`{start, end, of}`, `text_sha256` over the passage,
`sha256` still the file's), point 9 several sub-questions in one run (one
cut, `questions`, `subquestions`, per-sub-question coverage), point 10 a
title inherited from the heading above, with `own_title`;
- SS 10 names this library's default ranking (BM25) and `--ranking fusion`.
`okf check` holds the two new forms a reader acts on: `passage_malformed`
(not whole numbers with 0 <= start < end <= of) and `subquestions_unindexed`
(not distinct indices into `questions`, or indices in a payload listing none).
17 -> 19 rules; the two tests that pin the published count move with it.
Each rule is held against a real payload (0 findings) and against that
payload broken six ways.
Editing the contract moved the SS 7.4 known-positive, measured once after the
edit: 19 837 -> 23 672 encoded, 19 358 -> 23 092 raw, delta 479 -> 580. The
example payload and `skills/okf-consume` are regenerated by the published
recipe and `okf check` reports 19 rules, 0 findings on them.
Suite on a clean tree after `git add`: 2443 passed, 2 skipped, 4 xfailed.
ruff, ruff format, mypy --strict clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
da6faf8776
commit
977040f575
11 changed files with 308 additions and 28 deletions
|
|
@ -785,14 +785,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 = 19_837
|
||||
KNOWN_POSITIVE_EXPECTED = 23_672
|
||||
|
||||
#: The second, independent route. `wc -c` reports 19 358 raw bytes for the same
|
||||
#: The second, independent route. `wc -c` reports 23 092 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 = 479
|
||||
KNOWN_POSITIVE_ENCODING_DELTA = 580
|
||||
|
||||
#: The two places that file can be, resolved in this order.
|
||||
#:
|
||||
|
|
|
|||
|
|
@ -421,6 +421,88 @@ def rule_excerpt_parent(ctx: Context) -> list[Finding]:
|
|||
return findings
|
||||
|
||||
|
||||
def rule_subquestions_indexed(ctx: Context) -> list[Finding]:
|
||||
"""SS 8.9: a payload asking several sub-questions indexes them.
|
||||
|
||||
`questions` is the list the caller asked, and every excerpt names the
|
||||
sub-questions it answered as `subquestions`: distinct whole-number indices
|
||||
into that list, at least one. An index a reader cannot look up names no
|
||||
question, and an excerpt carrying indices in a payload that lists no
|
||||
questions answers something the payload never states.
|
||||
"""
|
||||
if not ctx.payload_is_mapping:
|
||||
return []
|
||||
raw = ctx.payload.get("questions")
|
||||
questions = _sequence(raw)
|
||||
excerpts = _sequence(ctx.payload.get("excerpts"))
|
||||
carried = [
|
||||
position for position, excerpt in enumerate(excerpts) if "subquestions" in _mapping(excerpt)
|
||||
]
|
||||
if raw is None:
|
||||
return [
|
||||
Finding(
|
||||
"subquestions_unindexed",
|
||||
f"excerpt {position} names `subquestions` but the payload lists no "
|
||||
"`questions` to look them up in (SS 8.9)",
|
||||
)
|
||||
for position in carried
|
||||
]
|
||||
findings: list[Finding] = []
|
||||
count = len(questions)
|
||||
for position, excerpt in enumerate(excerpts):
|
||||
indices = _mapping(excerpt).get("subquestions")
|
||||
values = [_whole(value) for value in _sequence(indices)]
|
||||
if (
|
||||
not isinstance(indices, list)
|
||||
or not values
|
||||
or any(value is None or not 0 <= value < count for value in values)
|
||||
or len(set(values)) != len(values)
|
||||
):
|
||||
findings.append(
|
||||
Finding(
|
||||
"subquestions_unindexed",
|
||||
f"excerpt {position} does not name the sub-questions it answered as "
|
||||
f"distinct indices into the payload's {count} `questions` (SS 8.9)",
|
||||
)
|
||||
)
|
||||
return findings
|
||||
|
||||
|
||||
def rule_passage_placed(ctx: Context) -> list[Finding]:
|
||||
"""SS 8.8: an excerpt delivered as a passage says where it lies.
|
||||
|
||||
`passage` is `{start, end, of}`: whole numbers with `0 <= start < end <=
|
||||
of`, character offsets into the concept's body. A place that is not a
|
||||
place sends a reader who fetches the whole concept to the wrong
|
||||
characters of it.
|
||||
"""
|
||||
if not ctx.payload_is_mapping:
|
||||
return []
|
||||
findings: list[Finding] = []
|
||||
for position, raw in enumerate(_sequence(ctx.payload.get("excerpts"))):
|
||||
excerpt = _mapping(raw)
|
||||
if "passage" not in excerpt:
|
||||
continue
|
||||
passage = excerpt["passage"]
|
||||
place = _mapping(passage)
|
||||
start, end, total = (_whole(place.get(key)) for key in ("start", "end", "of"))
|
||||
if (
|
||||
not isinstance(passage, Mapping)
|
||||
or start is None
|
||||
or end is None
|
||||
or total is None
|
||||
or not 0 <= start < end <= total
|
||||
):
|
||||
findings.append(
|
||||
Finding(
|
||||
"passage_malformed",
|
||||
f"excerpt {position} carries a `passage` that is not whole numbers "
|
||||
"with 0 <= start < end <= of (SS 8.8)",
|
||||
)
|
||||
)
|
||||
return findings
|
||||
|
||||
|
||||
def rule_excerpt_states(ctx: Context) -> list[Finding]:
|
||||
if not ctx.payload_is_mapping:
|
||||
return []
|
||||
|
|
@ -736,6 +818,8 @@ RULES: tuple[Callable[[Context], list[Finding]], ...] = (
|
|||
rule_excerpt_named,
|
||||
rule_excerpt_parent,
|
||||
rule_excerpt_states,
|
||||
rule_subquestions_indexed,
|
||||
rule_passage_placed,
|
||||
rule_denominator_identity,
|
||||
rule_denominator_lists,
|
||||
rule_withheld_rules,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue