llm-ingestion-okf/docs/2026-09-12-r7-release-v085.md
Kjell Tore Guttormsen 313a1497b7 docs(release): R7, v0.8.5 tagged locally and measured from the tag
The release report in R6's form: premises re-measured, the one-line lockfile
diff, the README lines the tag would have made false, the gates, and the
acceptance gate run from a local `git archive v0.8.5` export.

Two bookkeeping figures in K3-25's report corrected after re-measuring: the
tests/ call denominator was 129 occurrences on 128 lines (not 112 across 13),
and the outside-tests figure was 19 on 17 lines in 5 files (not 129, which was
the tests/ number written into the wrong paragraph). The "exactly 1 changed
result" finding is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 19:47:30 +02:00

15 KiB

R7: release v0.8.5

A patch release with no new functionality and one change that reaches outside this repository: okf.parse_frontmatter is public API, and after K3-24 it returns a flow STRING for a block sources: where it returned an EMPTY string. The tag carries what rounds 23, 24 and 25 landed and no tag had -- K3-23's link_in_signal instrument, K3-24's block-sources fix in all three flat frontmatter readers, and K3-25 making the reading without the door's link line consume's default. No behaviour change in src/ beyond the version string, the guard pin untouched at v1.4.0, no push.

Tag: v0.8.5, local and annotated, on 64661c7 (chore(release): 0.8.5). v0.8.4 stays on 69dc51f, v0.8.3 on 0963dfa, v0.8.2 on b6c54e9, v0.8.1 on 3daf983 and v0.8.0 on 4d1f9d3 -- verified with git tag --points-at, all five still resolve there.

1 What this round did, and what it did not

Did: the version on the three lines it lives on (pyproject.toml:7, src/llm_ingestion_okf/__init__.py:79, and the package's entry in uv.lock:550), CHANGELOG.md's ## [Unreleased] to ## [0.8.5] — 2026-09-12 with the em dash copied from the [0.8.4] head, the five @v0.8.4 install lines to @v0.8.5, the two pairing paragraphs, the tag history list, README's test counts and its history parenthesis, and one local annotated tag.

Did NOT: move the guard pin, which stays at v1.4.0 on all six tracked places; move the dependency floor >=1.2,<2.0, which R6 measured and deliberately left standing as an operator decision; add a rule, an extractor, an arm or a flag; move --shell-parent, --pdf-outline or --bold-title, all three still OFF and all three operator questions with numbers already delivered; touch skills/; rewrite docs/; or push anything.

link_in_signal's default is K3-25's delivery, not this round's. This round releases it.

2 Premises, re-measured

Every premise the order carried was reproduced before anything was edited.

premise measured here
inbox empty find ~/.claude/coord/llm-ingestion-okf/inbox/ -type f | wc -l = 0, rc 0
order queue 0 pending, 1 claimed (this order); K3-25's order already archived
git status --short empty
HEAD 06c057f; git log --oneline 7faa380..HEAD = exactly K3-25's three commits (3816ed5, 38320cd, 06c057f), nothing else
five tags stand v0.8.0 4d1f9d3, v0.8.1 3daf983, v0.8.2 b6c54e9, v0.8.3 0963dfa, v0.8.4 69dc51f, all git cat-file -t = tag
remote refs/heads/main = 150c726; all five v0.8* tags present, v0.8.4^{} = 69dc51f. R6's tag backlog is gone, so the push list is ONE line
okf check rules len(contract_check.RULES) = 17 in the work tree and 17 from the tag. K3-25 added none
suite 1827 collected, 1826 passed, 1 skipped, rc 0 (pytest -q and pytest --collect-only -q, repo root, [extract] installed)
ruff 0.16.6, the version R4, R5 and R6 measured

The order's line numbers had moved, as it warned: CHANGELOG.md's [0.8.4] head is at 103, not 82, and README's fourth install line at 589, not 580. Every number used here was read off this base.

The CHANGELOG's [Unreleased] block held 5 posts under 4 headings when this round started -- ### Added (K3-23), ### Changed (K3-25), ### Fixed (K3-24), ### Changed (K3-24, two posts) -- counted with sed -n '9,102p' CHANGELOG.md | grep -c '^- ' and grep -n '^### ', not read.

Gate 1 was already closed by K3-25. The order expected K3-23's Added post to still end in "in a later round", a sentence the tag would falsify. It does not: the post now says the recommendation was "carried out in the same unreleased block below". The query that could find it is the joined one -- tr '\n' ' ' < CHANGELOG.md | grep -c 'in a later round' = 0 -- run beside the known-positive tr '\n' ' ' < CHANGELOG.md | grep -c 'recommended as' = 1, so the zero is a measurement. Nothing was edited.

Gate 2 was open and one post was written. K3-24's ### Fixed post says the rendering "is a READING projection, not a claim that the value is writable", but names no cost and no denominator. One post was added under ### Changed carrying the measurement in section 7 below. It is the only post in the [0.8.5] block this round wrote; the other five moved byte-identically, verified by diffing the old block's non-heading lines against the new block's (the only addition is the 13 lines of the new post; the two K3-24 ### Changed posts appear on both sides unchanged).

The two ### Changed headings were merged into one, in the order Added, Changed, Fixed: K3-25's post, then K3-24's two, then the new one. Each post body moved byte-identically and no other line changed. Leaving both would also have been legal; one was chosen because a single version block with two identically named sections is a reader's problem, not a record.

3 The lockfile

pyproject.toml and __init__.py were corrected first, then uv lock was run and the diff measured. Exactly one line, the okf version at uv.lock:550: 0.8.4 -> 0.8.5. No other package moved. The guard lines :546 and :578 are untouched and still carry v1.4.0. tests/test_guard_adapter.py passed 21 of 21 afterwards -- the check that uv lock did nothing to the guard it was not asked to do.

R6's diff was four lines, three of them the pin's. This round's is one, because the pin does not move.

4 README lines the tag would otherwise make false

  • The five install lines (README.md:12, :62, :77, :589, llms.txt:9), measured on this base, not quoted from the order.
  • The two pairing paragraphs. README.md:66-67 and :80-81 each had ONE number to correct this round, not two: the okf tag. The guard tag v1.4.0 stays in both, and README.md:76 -- the pip fallback that installs the guard -- was not touched at all.
  • The tag history list: a v0.8.5 row as "the current tag", written from this round's own CHANGELOG wording and carrying seventeen rules as measured here, not copied from the row above; v0.8.4 demoted from "the current tag" to past by striking those three words and nothing else; v0.8.3, v0.8.2, v0.8.1, v0.8.0 and v0.7.0 untouched. The row makes no claim about the guard pin, so README's v1.4.0 count stays at 4.
  • The test counts (:950-952): 1783/1782/1 of 2026-09-11 to 1827/1826/1 of 2026-09-12, both measured here.
  • The sentence that follows them -- "the figure published before the v0.8.2 release was the PASSED count, and pytest --collect-only -q reported one more" -- was left standing, because it is still true on this round's numbers: 1827 is exactly one more than 1826, and the one is the skip.
  • The history parenthesis: 1783 becomes a past leg ("through the v0.8.4 release, after K3-22 and K3-21") and the current figure becomes the v0.8.5 release's, after K3-23, K3-24 and K3-25. Changing only the date would have made the previously published figure disappear rather than become history.

tests/test_docs_promises.py (5 of 5) and tests/test_packaging.py (6 of 6) were run after the README and version edits, both green.

5 Two bookkeeping figures in K3-25's report

Both were re-measured rather than taken from the order, and both were wrong in the published file.

  • Paragraph 2 said tests/ holds 112 call occurrences of the four functions across 10 files, of which 13 pass the parameter. Measured: 129 occurrences on 128 lines across 10 files, with 20 lines passing link_in_signal=. The substantive finding -- exactly 1 changed result -- stands, and 1 of 112 became 1 of 129.
  • Paragraph 5 said 129 hits outside tests/. Measured: 19 occurrences on 17 lines across 5 files. The 129 is the tests/ figure written into the wrong paragraph. Of the 19, 8 fall outside consume.py: one is prose in CLAUDE.md and four are that report counting itself.

Both corrections name what the wrong number actually was, so a reader meeting the old figure elsewhere can place it.

6 The gates

Run after git add and after the commit, never before:

gate result
ruff check src tests tools All checks passed (ruff 0.16.6)
ruff format --check . 132 files already formatted
mypy src Success: no issues found in 21 source files
pytest -q 1826 passed, 1 skipped, rc 0

Grep criteria, each with a control:

query result
grep -n 'okf\.git@v0\.8\.4' README.md llms.txt 0 hits, rc 1
grep -c 'okf\.git@v0\.8\.5' … README 4, llms.txt 1 = 5 -- the known-positive that makes the zero above a measurement
grep -n 'v0\.8\.4' README.md llms.txt 2, both named: README.md:102 the tag-history row, README.md:959 the history parenthesis. Not 0, and not to be made 0
grep -n 'v1\.4\.0' pyproject.toml uv.lock README.md pyproject 1, uv.lock 2, README 4 -- unmoved, which is what says the pin was not touched
grep -n 'v1\.3\.0' pyproject.toml README.md llms.txt 0 hits, rc 1
grep -rn 'v0\.7\.0|0\.7\.0' README.md docs llms.txt CLAUDE.md | wc -l 35 lines over 8 docs/ files -- unchanged. README's one hit is the v0.7.0 tag-history row and stays

7 The acceptance gate: installation FROM THE TAG

Local export only. git archive v0.8.5 | tar -x into /private/tmp/okf-r7-scratch/r7-install/export, then uv sync --frozen --extra extract (plain --frozen drops [extract] and mypy src falls on pdfplumber). No uv tool install, no --force, no uninstall: the operator's own okf at /Users/ktg/.local/bin/okf was read and left alone. Everything below ran from <export>/.venv/bin/.

row denominator result
version 1 importlib.metadata.version('llm-ingestion-okf') = 0.8.5
guard version 1 llm-ingestion-guard = 1.4.0, unmoved
uv lock --check 1 rc 0, captured directly
contract_check.RULES 1 17, equal to the work tree's
okf check on the shipped skill 1 rc 0, conformant: 17 rules over 3 excerpts and 0 withheld entries, 0 findings
okf project on the five-document folder 5 documents 26 concepts, 52 md files, 0 of 5 left out -- R6's numbers to the unit
HTML corpus 828 documents proposed 828 plan(s); 0 document(s) with no boundary; 0 unreadable, 6 015 md
R761 XML 110 source files, 1 at top level 2 761 concepts over 2 737 directories, of which 11 and 12 hold two each; document directory is r761-prosesskoden/, not the UUID
hit@1 / @8 / @50 on R761 N = 6 scored of 8 asked 6/6 · 6/6 · 6/6
known-positive rank 1 rank 1 at the default k and rank 1 at k 50
known-negative (K3-17) 1 question rank 1 = the RITB section (6 793 B), rank 2 = «Prøvedrift» (10 590 B); both concepts present and delivered
block sources: read to 0 empty 4 605 files see below
the flow string through PyYAML 4 605 files see below

The hit@1 row matters more this round than last, because K3-25 makes link_in_signal=False the default and that is a ranking change on a published payload form. K3-23's claim was that none of the five shipped bundles carries the door's link line, so the row should not move. It did not: every S1-S6 row is rank 1, the known-positive is rank 1 at both k, and S1's spent is 31 031 -- the value STATE records for the default reading.

The four bundles, read from the tag's bytes. Source directory, named because it is not named in K3-24's report: /Users/ktg/repos/vegnormal-okf/build/ferdig/. Chosen after verifying its denominators against K3-24's table first: files carrying a frontmatter block are 2 757 / 447 / 1 134 / 271 for r761-2025, n100-2023, n200-2024, n500-2024, exactly K3-24's. Of those, the ones carrying a BLOCK sources: are 2 756 / 446 / 1 133 / 270 = 4 605. All three flat readers (materialize.parse_frontmatter, structure._split_frontmatter, profiles._split_frontmatter) return a non-empty value on 0 empty of each, reading nothing but the bundles.

The known-positive control for that zero. The same read with v0.8.4's flat reader (git archive v0.8.4 src/llm_ingestion_okf into scratch, sys.path.insert) returns EMPTY on 2 756 / 446 / 1 133 / 270 -- 4 605 of 4 605, K3-24's before-numbers to the file. The instrument can find, so the zero is a measurement.

The reservation, measured and not asserted. The string parse_frontmatter now returns for a block sources: was passed to yaml.safe_load on each of the same 4 605 files. It is read back on 0 of 4 605. The reason is visible in one value:

[{ resource: https://…/api/nisosts/859990?languageCode=nb, title: N500:2024 }]

The ? opening the query string ends the flow scalar, and PyYAML raises ParserError while parsing a flow mapping. So the fix is an improvement for a consumer who read the empty value and concluded the address was absent, and a REGRESSION for a consumer who passed the return value straight to a YAML reader: they now get a parse error where they got something empty that parsed. The emitter materialize._render_sources still writes flow, so no bundle bytes move. PyYAML 6.0.3 is a dev dependency and the export carries it; this row was measured with the export's interpreter, and reproduced identically with the work tree's.

8 Honesty limits

  • One machine, one interpreter, a LOCAL export. The installation is measured on one Intel Mac from git archive v0.8.5, not from the Forgejo tag, which does not exist until the operator pushes. It is not a portability claim and not a claim that the channel works.
  • okf project's 26 is ONE corpus of five documents -- a regression control, not a sample.
  • 828 of 828 and 2 761 of 2 761 are ONE publisher's files from ONE product.
  • hit@1 and the known-positive rank are 6 scored questions and ONE known-positive on ONE bundle, against a fasit someone else set.
  • The known-negative is ONE constructed question on ONE small bundle.
  • The four bundle rows are ONE producer's four bundles from ONE generator. "0 empty" says nothing about a bundle someone else built, and the directory they were read from is named above because K3-24's report does not name it: the denominators match K3-24's table, which is the evidence it is the same material, not a certificate that it is the same directory.
  • The grep criterion is deliberately NOT "0 hits on v0.8.4". Two occurrences remain and must. The zero on okf.git@v0.8.4 ran beside a known-positive that returned 5, so it is a measurement and not a query that could never match.
  • okf.parse_frontmatter CHANGES PUBLIC READ BEHAVIOUR. A consumer who passed its return value for sources to a YAML reader and got something empty that parsed now gets a parse error: PyYAML reads the returned string back on 0 of 4 605 block files. That is a regression for them even though it is a fix for everyone who read the empty value as an absent address. It is stated here, in the CHANGELOG and in the tag-history row, because a release that sells a read fix without saying who it costs lies by omission.
  • "The tag installs" is not "the tag is correct". No functionality is verified here beyond what K3-23, K3-24 and K3-25 already measured; the rows above prove the exported bytes run and reproduce the counts the repository already published.