feat(accounting): okf build accounts for every source element
okf build --accounting PATH inventories every source document before extraction, in the gate's per-format vocabulary, and after the run gives each element one fate (carried / pointer / coded rejection), written as JSON and summarised in log.md. "carried" is checked against the written concept bodies, so a gate that drops a line is found (test). Exit 1 on anything unaccounted or double-booked. Opt-in: +744 s (+19 %) on the 43-document reference corpus, and that corpus fails the check on 24 real losses (22 images on text-less PDF pages, 2 docx Title paragraphs). Changed without the flag: - okf build exits 1 when it extracted documents and persisted none. Door B and corpus.measure are unchanged. One test relied on exit 0. - An image file carried through a persisted document is its own K1b column, no longer also extractor_unknown. The set is what the resolver actually carried (ExtractedDocument.files), never a byte match. tools/okf_accounting_gate.py (checks untouched) is green on all six rows, R761 110 of 110 under both gates. Report: docs/2026-09-17-innholdsregnskapet-bygget.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0b00de4408
commit
864570b320
13 changed files with 1751 additions and 59 deletions
|
|
@ -1,7 +1,7 @@
|
|||
"""The content-accounting gate's own tests (`tools/okf_accounting_gate.py`).
|
||||
|
||||
The gate is RED today by design: `okf build` has no source inventory and no
|
||||
per-element accounting. These tests are GREEN and prove three things.
|
||||
The gate was written RED at `0b00de4`, before `okf build` had a source
|
||||
inventory or a per-element account. These tests are GREEN and prove three things.
|
||||
|
||||
1. The fasit is independent. `tools/okf_witness.py` imports no
|
||||
`llm_ingestion_okf` module -- checked on the live import graph of a process
|
||||
|
|
@ -10,9 +10,11 @@ per-element accounting. These tests are GREEN and prove three things.
|
|||
again to hand counts on four documents.
|
||||
2. Every row CAN turn green and CAN turn red, each boundary driven from both
|
||||
sides with synthetic build output (the door the capability must open).
|
||||
3. Run against the real `okf build` at this commit, the gate sees the three
|
||||
defects it was ordered for: no inventory, a file carried AND rejected, and
|
||||
a rejected document logged as "0 carried of 0 found".
|
||||
3. Run against the real `okf build`, the fixture rows are green. They were
|
||||
red at `0b00de4` on the three defects the gate was ordered for (no
|
||||
inventory, a file carried AND rejected, a rejected document logged as "0
|
||||
carried of 0 found"); `test_a_file_carried_through_a_document_and_rejected_
|
||||
is_double_booked` and the row 4 tests keep those defects detectable.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -395,31 +397,23 @@ def test_bad_usage_exits_two() -> None:
|
|||
def real_rows() -> list[gate.Row]:
|
||||
pytest.importorskip("pdfplumber")
|
||||
pytest.importorskip("pypandoc")
|
||||
if gate.door_available():
|
||||
pytest.skip("the accounting door exists; these rows describe the build before it")
|
||||
return gate.evaluate(r761=None, ci=True, consume=False)
|
||||
|
||||
|
||||
def test_the_real_gate_is_red_on_rows_two_three_and_four(real_rows: list[gate.Row]) -> None:
|
||||
assert [r.number for r in real_rows if r.fails] == [2, 3, 4]
|
||||
assert [(r.number, r.status) for r in real_rows if not r.fails] == [
|
||||
def test_the_door_exists() -> None:
|
||||
assert gate.door_available()
|
||||
|
||||
|
||||
def test_the_real_gate_is_green_on_every_fixture_row(real_rows: list[gate.Row]) -> None:
|
||||
"""Rows 1-5 against the real `okf build`; row 6 needs R761 and is skipped
|
||||
here. Written red at 0b00de4 (rows 2, 3, 4 red), green once the build
|
||||
accounts for every element."""
|
||||
assert [(r.number, r.status) for r in real_rows] == [
|
||||
(1, gate.GREEN),
|
||||
(2, gate.GREEN),
|
||||
(3, gate.GREEN),
|
||||
(4, gate.GREEN),
|
||||
(5, gate.GREEN),
|
||||
(6, gate.SKIPPED),
|
||||
]
|
||||
|
||||
|
||||
def test_the_real_gate_sees_the_graphics_carried_and_rejected(real_rows: list[gate.Row]) -> None:
|
||||
row3 = real_rows[2]
|
||||
assert "d = 2 double-booked" in row3.reason
|
||||
doubled = [d for d in row3.details if "AND rejected" in d]
|
||||
assert [d.split(":")[0] for d in doubled] == [
|
||||
"file graphics/figur-84-1.png",
|
||||
"file graphics/tabell-84-2.png",
|
||||
]
|
||||
|
||||
|
||||
def test_the_real_gate_sees_the_rejected_document_logged_as_empty(
|
||||
real_rows: list[gate.Row],
|
||||
) -> None:
|
||||
assert "log says 0 carried of 0 found; the source declares 1" in "\n".join(real_rows[3].details)
|
||||
assert real_rows[2].reason.startswith("u = 0 unaccounted, d = 0 double-booked")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue