The record for K3-21 A-D: what an excerpt, a heading-only body, the checker and the index now carry, and every gate with its denominator -- the pinned K2 bundle (7 passed in each export), 32 regression payloads, R761 with and without `--shell-parent` (hit@1/8/50 6/6 at both k, the known-positive at rank 1, 675 of 710 shells with exactly one link and 0 broken, 710 of 710 pointing at the source's own ancestor after D), consumption 6 of 6 in both forms, the byte cost against the consumer's 51.0 %, the ranking cost the link's path carries, and the 35 shells with no ancestor holding text, listed. Deviations from the order stated first; honesty limits last. CHANGELOG under Unreleased; README and CLAUDE.md say 710 of 710, 17 rules, and that the outline mark keeps its depth. The README's project measurement was re-run (26 concepts, 0 checkout paths, `okf check` 17 rules, 0 findings). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
20 KiB
K3 round 21: parent all the way to the reader
Date: 2026-09-11 · Base: e717b1c · Commits: a5cd7c5 (red),
4f7bd61 (A), ca83dad (red), 839bd61 (B), 46e555d (B, defaults),
c188a0c (red), 5970369 (C), 9822e55 (red), bd13c5f (D) · Measured
from: frozen git archive exports of the base and of 4f7bd61, 839bd61,
5970369 and bd13c5f (Python 3.11.14, each __file__ under a scratch
directory), never the working tree and never the okf on PATH.
Round 20 gave a section whose body is its heading alone a parent: key naming
the nearest ancestor that holds text (okf build --shell-parent) -- 675 of
710 such sections in one 2 761-concept standard -- and no reader could see it:
okf consume did not read the key, the payload did not carry it, the body held
no link, and the index rendered it unresolved. Four deliveries, in order, each
with its own red test and its own commit: A the pointer reaches the excerpt
and the body; B the ancestor's text reaches the reader, in two forms, and
both defaults are chosen on numbers; C the index resolves the pointer; D
the depth-7 trap.
0. Where the measurement differs from the order, first
- HEAD was
e717b1c, not4595dde, and the suite baseline 1 753 passed, 1 skipped, rc 0, not 1 667 / 1: K3-22 landed after dispatch. The shell rule (--shell-parent,propose._link_shells) is in the tree. - The order's R761 build command exits 2 on
e717b1c. K3-22 refuses a--frontmatter sources=flow value whose URL carries?. Every R761 bundle here is built with the same command minus that flag (utgavekept), sosourcesfalls back to the document's own layer. The base row still reproduces round 20's numbers exactly: hit@1/8/50 6/6 at bothk, the known-positive (KP) at rank 1 at both, delivered per question 7 8 7 8 8 8 7 7 at the defaultk. - The known-positive moved in every payload. SS 7.4's known-positive IS
docs/consumption-contract.md, and A and B edit its SS 8, sobudget.known_positivemoves in every payload with no bundle changing (13 238 / 12 893 / 345 -> 14 455 / 14 083 / 372 -> 14 721 / 14 346 / 375). Every "byte-identical" row below after A and after B is measured with that block removed, and the whole-payload count is given beside it. - The hit@k instrument moved. The consumer repository stands on
f783e52, not4f70e9d;experiments/hitk_sk2.pywas last changed in6fad6a2, as stated. The scratch copy differs from it in two lines (the payload path, because a concurrent session writes the hard-coded one), and the form (b) copy in one more (--follow-parenton theokf consumecall). The consumer repository'sgit status --porcelainis empty before and after. - The generated skill tells a reader to write
/tmp/payload.json, a path a concurrent session shares. Each consumption run used a scratch copy of the skill whose only change is that path (and, for form (b), the flag line). - The link's first design failed on the fixture before it shipped. Read as
body text,
Enclosing section: [...](/...)is a bundle-local link target, and structure derivation turned it into a SECOND relation --references: [/...md?]withderived: [references], rendered unresolved because nothing resolves the absolute form. The link is therefore appended after derivation and screened on its own, the ruledescriptionalready follows. - One red test met a mechanism instead of the rule. B's "no room" case
asked through
build_payloadatlimit == spent, where the knapsack's 500-byte buckets admit nothing (budget_admits_nothing). The green commit holds the rule atattach_parent_textand says so.
1. The spec, verbatim, and which paragraph each half leans on
Canonical SPEC.md at ad30107:
- SS 4.1, Extensions: "Producers MAY include any additional keys.
Consumers SHOULD preserve unknown keys when round-tripping and MUST NOT
reject documents with unrecognized fields." -- the ground for the
parent:KEY and for the payload member. - SS 5.1: "Lineage is expressed through links, not a dedicated field." -- the ground for the LINK: the key alone is not the spec's form of lineage.
- SS 6.1: "Absolute (bundle-relative): begins with
/, interpreted relative to the bundle root. This is the recommended form because it is stable when documents are moved within their subdirectory." And: "The specific kind (parent/child, references, joins-with, depends-on) is conveyed by the surrounding prose, not by the link itself." And: "Consumers MUST tolerate broken links: a link whose target does not exist in the bundle is not malformed; it may simply represent not-yet-written knowledge." -- the link's form, its two words of prose, and why an unresolved pointer is named in the payload rather than refused.
Contract SS 8, first paragraph, changed: "Additional members are permitted
and are not read by the checker." now reads "Additional members are permitted;
the checker reads only the members this section names." -- because A's rule
reads parent, which point 6 now names.
2. A: the pointer reaches the excerpt and the body
The payload. consume.link_parents resolves parent among the concepts
sharing the pointing concept's source_file: a segment id is unique only
inside one document's plan, so p1 exists in every document of a
multi-document bundle. One pass over concepts already read, no file opened
again. An excerpt carries parent: { concept_id, title } -- never the raw
segment_id, which a reader can open nothing with -- and
parent_unresolved: true where the pointer lands nowhere. The member is
conditional, like req_number.
What the excerpt carries was chosen on this: the raw id costs least and
can be followed only by enumerating the bundle, which SS 9.2 forbids; the
concept id is one Read; the title is what a citation is made of. Measured
cost of the chosen member: 5 members, about 528 B, over the 60 excerpts of the
eight default-k payloads; 34 members, about 3 927 B, over 353 at k 50.
The body. A heading-only body whose plan entry has a parent gains ONE line:
Enclosing section: [<title>](/<bundle-relative path>). Only such a body --
the segmented goldens' declared parents are bodies holding text, and stay
byte-identical. The two words are English and generic, because the code
cannot know the source's language; the relation they name is true of an
adjudicator's parent and of a shell's nearest ancestor holding text alike.
The checker. Seventeenth rule, parent_unfollowable: a parent that is
not a concept_id and a title, names its own excerpt, or names a concept in
neither excerpts nor withheld. Those two lists together name every concept
the pre-pass considered (SS 5.2), so the rule sees a dangling pointer without
opening the bundle. parent_unresolved is not a finding (SS 6.1).
| row | result | denominator |
|---|---|---|
| K2 pin in the export, gold set copied in | 7 passed | 7 |
| K2 default + K2 Arm B + N100/N200/N500 + five-document bundle | 32 of 32 identical without known_positive; 0 of 32 whole |
12 + 15 + 5 |
| K2 ranks, both bundles | (1,1,1,1,1,5) unchanged; parent members 0 |
6 · 6 |
| five-document bundle, known-negative row | rank 1 unchanged ([1,1,1,1,2]) |
5 |
| R761 without the flag | 16 of 16 payloads identical without known_positive; bundle diff -r only log.md (the converter's path) |
16 · 2 761 |
| R761 with the flag, hit@1 · 8 · 50 | 6/6 · 6/6 · 6/6 at both k, KP rank 1 / 1 |
6 |
| shells carrying exactly one link | 675 of 710, 0 with more, 0 broken, 675 of 675 targets = the parent |
710 |
| files differing from the flagged base bundle | 676 = 675 concepts + log.md; 0 index files |
5 518 |
okf check on the flagged payloads |
16 of 16 rc 0, 17 rules, 0 findings | 16 |
| known-negatives | a dangling concept_id and the raw "p1977": 1 finding each, rc 1 |
2 |
| link bytes | 72 265 B = 4.49 % of all body bytes (D2's inheritance section: 51.0 %); 69.2 % of the 675 linked bodies; median line 103 B, max 247 B | 1 610 590 B |
| shells, PM script / concepts with no source text of their own | 35 / 710 | 2 761 |
The honest shell count is 710. The PM script counts non-empty lines not
starting with #, and a link line is one; 675 of the 710 now carry a line and
still no text of their own.
What the link costs in ranking, measured. hit@k does not move. The
delivered SET does: at the default k S2-S6 and KP are identical, while S1
exchanges 3 of 7 places and KN 2 of 7, every newcomer a linked shell; at k 50
three of eight questions move (KP 23 in / 19 out). Of the newly delivered
shells, those matching the question ONLY through the link line are 1 of 3
(S1), 4 of 7 (S1 at k 50), 6 of 23 (KP at k 50) and 0 of 2 / 0 of 8 (KN),
and in every such case through the PATH, never the title: the bundle-absolute
form repeats the document's directory in 675 bodies -- the saturation round 20
took out of the id signal, back in through the body. The rest of the movement
is not decomposed per signal.
3. B: the ancestor's text reaches the reader
Form (a), the pointer, is A. Form (b), okf consume --follow-parent,
puts the enclosing concept's text inside parent, with that concept's own
sha256, AFTER the cut and only from the room the cut left, in rank order. A
parent the payload already holds, or one a higher-ranked excerpt carried,
travels once; a text that does not fit is cut to the longest prefix that does
and marked truncated. Inherited text therefore cannot displace an excerpt --
the mechanism a consumer measured when copied-in ancestor text pushed the
right section to withheld place 504 and 1 069.
| row | (a) pointer | (b) --follow-parent |
denominator |
|---|---|---|---|
hit@1 · 8 · 50, default k |
6/6 · 6/6 · 6/6 | 6/6 · 6/6 · 6/6 | 6 |
hit@1 · 8 · 50, k 50 |
6/6 · 6/6 · 6/6 | 6/6 · 6/6 · 6/6 | 6 |
KP rank, both k |
1 / 1 | 1 / 1 | 1 |
| delivered list and withheld list equal to (a) | -- | 16 of 16 | 16 |
| consumption correct | 6 of 6 | 6 of 6 | 6 |
| the fasit section cited first | 4 of 6 | 5 of 6 | 6 |
spent, default k, where it differs |
S1 28 020, KN 10 514 | S1 30 508, KN 13 260 (the other six equal) | 8 |
spent, k 50, largest difference |
KP 96 965 | KP 117 782 (12 texts, 0 truncated) | 8 |
okf check |
16 of 16 rc 0 | 16 of 16 rc 0, 17 rules | 16 |
The row that is the point: one heading-only section as a reader gets it. Question: a shell's own title (33.212). Top-1 is its sibling 33.211, whose title differs only in a token too short to score; both are shells under 33.2 "Sikringsbolter". Eight excerpts delivered in each form.
| before A | (a) | (b) | |
|---|---|---|---|
the excerpt's text |
its heading line | its heading + Enclosing section: [Sikringsbolter](/.../33-2/sikringsbolter.md) |
same |
parent |
absent | concept_id, title |
+ sha256, text (3 847 characters, not truncated) |
| excerpt bytes | 858 | 1 034 | 5 067 |
payload spent |
6 864 | 8 272 | 12 305 |
| what the reader must do to learn what the section inherits | nothing it can do: no pointer | one Read of the named concept (SS 2.2: the payload names it reachable) |
nothing: it is in the payload |
The consumption difference is not the form. S1-S6's answers are never
heading-only sections. The first-cited difference (S4) sits on a payload that
carries no parent at all and is identical in both forms apart from the
known-positive block, so it is the draw, not the form. S1's form (a) answer
used the pointer on its own: it named a delivered shell's enclosing section by
its parent.
Defaults, one sentence each:
--follow-parent: off. It delivers the same set as the pointer by construction and measured, and on six questions whose answers are never shells it bought nothing a reader scored, at +2 488 and +2 746 B on the 2 of 8 default-kpayloads holding a linked shell and up to +20 817 B atk50.--shell-parent: off, with the reason rewritten. "okf consumereads noparentkey" is false after A; the measured reason is the path saturation above -- hit@k intact, the delivered set moved on 2 of 8 questions at the defaultkand 3 of 8 atk50. It moves the bytes of a bundle holding a heading-only section and nothing else: 0 of 453 K2 bodies are heading-only and the five-document folder builds byte-identical with it.
4. C: the index resolves the pointer
structure._segment_lookup keys (source_file, segment_id) off each
concept's own frontmatter (DocumentStructure.declared, no file read again)
and resolve_structure asks it first for a parent edge, inside the pointing
concept's document. A value no segment answers to is a document number and is
looked up as before; a pointer naming nothing keeps ?. The rendering rule is
untouched -- a resolved relation renders as its subject -- so parent: p1977?
becomes parent: p1977. consume's document prior reads only an index
entry's label, never its facets, so C moves no rank.
| row | before | after | denominator |
|---|---|---|---|
parent facets resolved, R761 with the flag |
0 | 675 | 675 |
parent facets with ? |
675 | 0 (? anywhere: 792 -> 117, the unflagged bundle's own count) |
675 |
concepts whose derived: names parent |
0 | 0 (the regex finds a fabricated positive) | 2 761 |
| files differing from A's flagged build | -- | 676: index files and log.md, 0 concepts |
5 518 |
| R761 without the flag, five-document folder | -- | only log.md differs |
2 |
| segmented goldens | parent: s0? |
parent: s0 -- 4 lines; the four goldens test_the_four_existing_goldens_are_untouched guards are not among them |
4 |
hit@k, both k; KP |
6/6; 1 | 6/6; 1, delivered and withheld lists identical to A's, 16 of 16 | 16 |
| regression payloads | -- | 32 of 32 wholly identical to B's | 32 |
okf check |
-- | 16 of 16 rc 0, 17 rules | 16 |
5. D: the depth-7 trap
_StsReader._walk wrote one clipped level into the ATX heading and the
OutlineMark alike. The heading keeps the clip; the mark carries the declared
depth. The extracted text does not move one character.
The gate, every row measured:
| row | result | denominator |
|---|---|---|
| mark levels 1-7 | 28 / 118 / 500 / 1 141 / 868 / 97 / 9 (before: 106 at 6, 0 at 7) | 2 761 |
| extracted text | identical (text_sha256 equal) |
1 |
| plan without the flag | 0 entries changed | 2 761 |
| plan with the flag | 2 entries changed: p646 and p647 (36.73211, 36.73212), parent_id p644 (36.732) -> p645 (36.7321) |
2 761 |
shells pointing at the ancestor the <sec> nesting names |
710 of 710 (708 before); direct parent 533 (531) | 710 |
| concept count | 2 761 | -- |
| bundle with the flag vs C | the 2 concepts, their 2 index files, log.md |
5 518 |
| bundle without the flag, five-document folder | only log.md |
2 |
| the four goldens | untouched (the suite's own test, repository root) | 4 |
| regression payloads | 32 of 32 wholly identical to C's | 32 |
hit@k, both k; KP |
6/6; 1, lists identical to C's, 16 of 16 | 16 |
_unit_fold's unit level, the risk the order named, is not reached: the
declared route closes its candidates with unit_fold=False, and the plan
confirms it with 0 changes outside the two entries.
6. The 35 shells with no ancestor holding text
No pointer was invented for them. 13 sit at depth 1 with no ancestor at all; 22 have ancestors, every one heading-only too. 28 are containers with titled descendants; 7 are leaves. None has a direct lettered point.
| segment | label | depth | titled descendants |
|---|---|---|---|
| p3 | 2 | 1 | 4 |
| p9 | 4 | 1 | 8 |
| p11 | 4.2 | 2 | 3 |
| p21 | 8 | 1 | 7 |
| p28 | 8.7 | 2 | 0 |
| p32 | 12 | 1 | 0 |
| p33 | Hovedprosess 1 | 1 | 97 |
| p34 | 11 | 2 | 11 |
| p42 | 11.5 | 3 | 3 |
| p46 | 12 | 2 | 28 |
| p129 | 17 | 2 | 0 |
| p130 | 18 | 2 | 0 |
| p131 | Hovedprosess 2 | 1 | 191 |
| p132 | 21 | 2 | 25 |
| p170 | 23 | 2 | 46 |
| p217 | 24 | 2 | 56 |
| p225 | 24.4 | 3 | 6 |
| p228 | 24.43 | 4 | 3 |
| p249 | 24.6 | 3 | 9 |
| p306 | 27 | 2 | 16 |
| p323 | Hovedprosess 3 | 1 | 331 |
| p653 | Prosess 37 | 2 | 0 |
| p654 | Prosess 38 | 2 | 0 |
| p655 | Hovedprosess 4 | 1 | 218 |
| p874 | Hovedprosess 5 | 1 | 97 |
| p972 | Hovedprosess 6 | 1 | 94 |
| p975 | 62 | 2 | 0 |
| p1033 | 66 | 2 | 14 |
| p1067 | Hovedprosess 7 | 1 | 264 |
| p1108 | 72 | 2 | 19 |
| p1186 | 75 | 2 | 60 |
| p1238 | 75.6 | 3 | 7 |
| p1275 | 77 | 2 | 56 |
| p2108 | Hovedprosess 86 | 1 | 44 |
| p2153 | Hovedprosess 87 | 1 | 229 |
What they are in the source: the front-matter chapters 2, 4, 8 and 12 and the main processes 1-7, 86 and 87 are containers whose text lives below them, not above; 17, 18, 37, 38 and 62 are placeholder processes reserved for operation and maintenance; 8.7 is a worked-example section; the first 12 is the chapter that introduces the processes. A rule pointing at an ancestor WITHOUT text would reach at most 22 of the 35, and only with a pointer to another heading. It is not built.
7. Suites and gates
| when | working tree | frozen export |
|---|---|---|
| base | 1 753 passed, 1 skipped | -- |
| A red, after add and after commit | 11 failed (all new), 1 754 passed, 1 skipped | -- |
| A | 1 765 passed, 1 skipped | 1 764 passed, 1 skipped, 1 failed |
| B red | 7 failed (all new), 1 766 passed | -- |
| B / defaults | 1 773 / 1 774 passed, 1 skipped | 1 772 passed, 1 failed |
| C red / C | 3 failed (new), 1 776 / 1 779 passed | 1 778 passed, 1 failed |
| D red / D | 2 failed (new), 1 780 / 1 782 passed | 1 781 passed, 1 failed |
The one export failure is the known artefact
test_segmented_golden.py::test_the_four_existing_goldens_are_untouched (an
export is no git repository). ruff check src tests tools, ruff format --check . and mypy --strict src were clean at every green commit, rc
captured directly. The first red commit (a5cd7c5) carried a test file ruff format rewrote in the next one; no other file moved that way. uv.lock was
never staged.
Honesty limits
- Everything about shells is measured on ONE document (N = 1). "Every outline document" is a claim about the code, which reads the plan's level and order; the fixtures and the five-document folder (0 shells of 26) are the only other rows.
- Consumption is one non-deterministic draw per question, twelve runs on a
subscription (
ANTHROPIC_API_KEYunset), judged by reading the answer against the fasit section. Answer texts are not reproduced. The six questions never have a shell as their answer, so form (b)'s value shows only in the probe row, and that probe is one question chosen here, not a fasit. - "Byte-identical" after A and B excludes
budget.known_positive. The contract document is the known-positive, so whole-payload identity is 0 of 32 across A and B and 32 of 32 across B, C and D. - The K2 statement for
--shell-parentis read off the pinned bundle (0 of 453 bodies heading-only), not off a rebuild with the flag. - The link's ranking cost is attributed only where a newcomer matches through the link alone; the rest of the delivered-set movement, and any effect of the heavier excerpts on the knapsack, is not decomposed.
- The instrument is someone else's, with two lines changed (three for form (b)), and it scores a title or a section-number pair, not an answer.
- C's "resolved" says a concept of the same document answers the id, not that it is the right ancestor; D's 710 of 710 is what checks that, against the source's own nesting.
- Conformance is the floor and never the proof.
okf checkis green on all 48 flagged payloads here, including the known-negative question's, which answers nothing.