test(propose): pin today's grid-table split with a golden that can fire

The K3 Arm E round (order 20260907T075834Z-18584396-from-.claude) measures a
rule that has not been written yet. This commit pins what the code does TODAY,
so the later claim "the default did not move" rests on a committed artifact
rather than on reading a diff.

Three characterization tests and one new golden fixture. Every count they
assert was measured against unmodified code before the test was written:
GRID_TABLE yields 3 candidates, TWO_TABLES_BLANK_SEPARATED yields 3,
TWO_TABLES_RULE_SEPARATED 2, PIPE_TABLE 1, PROSE_WITH_A_STRAY_RULE 1.

The second golden is not duplication. `propose-golden-default.json` is taken
over `OUTLINE_DOCUMENT`, which contains no `|` row and no `+` rule line, so no
table rule -- present or future -- can move its bytes. It is a guard that
cannot fire. `propose-golden-grid-default.json` is taken over a document that
has a pandoc grid table, and it is generated here from PRE-Arm-E code: a
fixture recorded after the change would pin post-change bytes and prove
nothing about the default.

Two fixture shapes are load bearing and say so in the band's comment. The
second table in TWO_TABLES_BLANK_SEPARATED has exactly ONE row group, so it
cannot be joined -- that is the only shape that catches a join whose pending
state is never cleared. And `propose()` is excluded from this band: it pops
`source` and discards every other kwarg, so an Arm E test written through it
would silently measure the default and pass.

Tests first: 1 red (the golden did not exist), then green. 1230 -> 1233.
ruff check: exit 0. ruff format --check: exit 0. pytest -q: exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-07 10:45:05 +02:00
commit 3fa772e917
3 changed files with 267 additions and 0 deletions

View file

@ -162,3 +162,25 @@ two independent parsers return the same wrong shape, because the breakage is in
the documents' ruling geometry rather than in either library. PDFs enter this the documents' ruling geometry rather than in either library. PDFs enter this
library as **prose**, and structured tables are out of scope until that is library as **prose**, and structured tables are out of scope until that is
decided separately. decided separately.
## `propose-golden-grid-default.json`
Pins the DEFAULT proposer artifact over a document containing a pandoc **grid
table**. Generated on unmodified code, before Arm E's rule existed, with
exactly the command the test runs:
```
write(tmp_path, GRID_GOLDEN_DOCUMENT, "grid.md")
okf_propose_segments.main([source, "--out", out, "--proposed-at", "2026-09-03T00:00:00Z"])
```
It exists because `propose-golden-default.json` **cannot** pin this. That
golden is taken over `OUTLINE_DOCUMENT`, which contains no `|` row and no `+`
rule line, so no table rule -- present or future -- can move its bytes. A guard
that is structurally incapable of firing is a trap written down but never
armed. This one is taken over a document that has a grid table, so an
accidentally default-on table rule turns it red.
Both goldens transitively pin `observed_extractor_version` and `PROPOSER_VERSION`.
A red here after a dependency change is a legitimate red: read the diff and
decide, do not re-record the expectation.

View file

@ -0,0 +1,92 @@
{
"version": "1",
"source_sha256": "4a50a27785e57b8d12319e3aad1118529251287b6ae8ecd3060bbac9fe04aee4",
"text_sha256": "4a50a27785e57b8d12319e3aad1118529251287b6ae8ecd3060bbac9fe04aee4",
"extractor_id": "md",
"extractor_version": "stdlib-1",
"adjudicated_at": "2026-09-03T00:00:00Z",
"adjudicated": false,
"proposed_by": "okf-propose-segments/1",
"entries": [
{
"segment_id": "p1",
"path": "romskjema.md",
"title": "Romskjema",
"okf_type": "reference",
"span": [
0,
67
],
"ingested_at": "2026-09-03T00:00:00Z",
"anchor": {
"quote": "# Romskjema\n\nInnledende avsnitt om romskjemaet.\n\n+-------+-------+\n",
"prefix": "",
"suffix": "| Navn | Verdi |\n+=======+=======+\n| Areal | 12"
},
"derived": [
"PROPOSED",
"rule:heading"
]
},
{
"segment_id": "p2",
"path": "tabell-linje-6.md",
"title": "Tabell linje 6",
"okf_type": "reference",
"span": [
67,
103
],
"ingested_at": "2026-09-03T00:00:00Z",
"anchor": {
"quote": "| Navn | Verdi |\n+=======+=======+\n",
"prefix": "ende avsnitt om romskjemaet.\n\n+-------+-------+\n",
"suffix": "| Areal | 120 |\n+-------+-------+\n| Hoyde | 3 "
},
"derived": [
"PROPOSED",
"rule:table-block"
]
},
{
"segment_id": "p3",
"path": "tabell-linje-8.md",
"title": "Tabell linje 8",
"okf_type": "reference",
"span": [
103,
139
],
"ingested_at": "2026-09-03T00:00:00Z",
"anchor": {
"quote": "| Areal | 120 |\n+-------+-------+\n",
"prefix": "--+-------+\n| Navn | Verdi |\n+=======+=======+\n",
"suffix": "| Hoyde | 3 |\n+-------+-------+\n"
},
"derived": [
"PROPOSED",
"rule:table-block"
]
},
{
"segment_id": "p4",
"path": "tabell-linje-10.md",
"title": "Tabell linje 10",
"okf_type": "reference",
"span": [
139,
175
],
"ingested_at": "2026-09-03T00:00:00Z",
"anchor": {
"quote": "| Hoyde | 3 |\n+-------+-------+\n",
"prefix": "==+=======+\n| Areal | 120 |\n+-------+-------+\n",
"suffix": ""
},
"derived": [
"PROPOSED",
"rule:table-block"
]
}
]
}

View file

@ -1084,3 +1084,156 @@ def test_the_help_attributes_arm_d_inside_its_own_option_chunk(
assert "not defined upstream" not in arm_c_chunk[0] assert "not defined upstream" not in arm_c_chunk[0]
# The whole-output count must also be 1: the literal belongs to Arm D alone. # The whole-output count must also be 1: the literal belongs to Arm D alone.
assert squeezed.count("not defined upstream") == 1 assert squeezed.count("not defined upstream") == 1
# --- Arm E: the grid-rule join --------------------------------------------
#
# Arm E is not defined in `docs/2026-09-02-k3-k4-k5-metode.md` -- that file
# contains no occurrence of the word "arm" at all. The definition below was
# written for order 20260907T075834Z-18584396-from-.claude and is the author's.
#
# Two things about the fixtures are load bearing rather than stylistic.
#
# FIRST: every count asserted below was MEASURED against today's code before
# the test was written, so a fixture cannot be green by accident. `GRID_TABLE`
# yields 3 candidates today and `TWO_TABLES_BLANK_SEPARATED` yields 3, not
# because those numbers look right but because that is what `find_candidates`
# returns on this text right now.
#
# SECOND: the second table in `TWO_TABLES_BLANK_SEPARATED` has exactly ONE row
# group, so it CANNOT be joined. That is the only shape that can catch a join
# whose "a rule line is pending" state is never cleared -- a table's trailing
# bottom rule sets it, and if the blank line after the table does not clear it,
# the next table's first row is marked joined with no join having occurred. A
# fixture whose second table also joins would stay green through that defect.
#
# The `propose()` helper above is deliberately NOT used anywhere in this band:
# it pops `source` and silently discards every other kwarg, so a test written
# as `propose(tmp_path, table_grid=True)` would measure the DEFAULT and pass.
GRID_TABLE = """+-------+-------+
| Navn | Verdi |
+=======+=======+
| Areal | 120 |
+-------+-------+
| Hoyde | 3 |
+-------+-------+
"""
TWO_TABLES_BLANK_SEPARATED = """+-------+-------+
| Navn | Verdi |
+=======+=======+
| Areal | 120 |
+-------+-------+
Mellomtekst som skiller de to tabellene.
+-------+-------+
| Rom | Antall|
+-------+-------+
Avsluttende avsnitt.
"""
TWO_TABLES_RULE_SEPARATED = """+-------+-------+
| Navn | Verdi |
+-------+-------+
| Rom | Antall|
+-------+-------+
"""
PIPE_TABLE = """| Navn | Verdi |
|-------|-------|
| Areal | 120 |
| Hoyde | 3 |
Etterfoelgende avsnitt.
"""
PROSE_WITH_A_STRAY_RULE = """# Notat
Et avsnitt med tekst.
+-------+-------+
Enda et avsnitt med tekst.
"""
GRID_GOLDEN_DOCUMENT = """# Romskjema
Innledende avsnitt om romskjemaet.
+-------+-------+
| Navn | Verdi |
+=======+=======+
| Areal | 120 |
+-------+-------+
| Hoyde | 3 |
+-------+-------+
"""
def test_todays_grid_table_cuts_one_table_into_one_concept_per_row_group() -> None:
"""Characterization. ONE table, three concepts -- the defect Arm E measures.
Committed BEFORE the rule exists, so the later claim that the default did
not move rests on a pinned artifact rather than on reading a diff.
"""
candidates = okf_propose_segments.find_candidates(GRID_TABLE)
assert len(candidates) == 3
assert [c.rule for c in candidates] == [okf_propose_segments.RULE_TABLE_BLOCK] * 3
assert [c.title for c in candidates] == [
"Tabell linje 2",
"Tabell linje 4",
"Tabell linje 6",
]
# The spans tile: the document is cut into three, not sampled at three points.
assert candidates[0].start == 18
assert [(c.start, c.end) for c in candidates] == [(18, 54), (54, 90), (90, 126)]
assert candidates[-1].end == len(GRID_TABLE)
def test_a_grid_rule_line_matches_no_shipping_grammar() -> None:
"""The four rules that ship today all decline a grid rule line.
Fed with the trailing newline every one of them actually sees, because
`find_candidates` iterates `splitlines(keepends=True)`. A stripped literal
would pass here while the shipped loop never met the same string.
"""
rules = ("+---+---+\n", "+===+===+\n", "+:--+--:+\n", " +---+---+\n")
for line in rules:
assert okf_propose_segments._TABLE_ROW.match(line) is None, line
assert okf_propose_segments._ATX.match(line) is None, line
assert okf_propose_segments._NUMBERED.match(line) is None, line
assert okf_propose_segments._OUTLINE.match(line) is None, line
# Known-positive controls: each grammar CAN match something, so the four
# `is None` assertions above are evidence rather than four dead regexes.
assert okf_propose_segments._TABLE_ROW.match("| a | b |\n") is not None
assert okf_propose_segments._ATX.match("# Tittel\n") is not None
assert okf_propose_segments._NUMBERED.match("3.1 Brannkonsept\n") is not None
assert okf_propose_segments._OUTLINE.match("3 Brannkonsept\n") is not None
def test_the_default_artifact_over_a_grid_table_matches_its_committed_golden(
tmp_path: Path,
) -> None:
"""The second golden, and the reason there has to be a second one.
`propose-golden-default.json` is taken over `OUTLINE_DOCUMENT`, which
contains no `|` row and no `+` rule line. It is therefore STRUCTURALLY
incapable of going red if a table rule were ever defaulted on -- a trap
written down but unable to fire. This one is taken over a document that
has a grid table, so it can.
"""
source = write(tmp_path, GRID_GOLDEN_DOCUMENT, "grid.md")
out = tmp_path / "grid-plan.json"
assert (
okf_propose_segments.main(
[str(source), "--out", str(out), "--proposed-at", GOLDEN_PROPOSED_AT]
)
== 0
)
golden = Path(__file__).parent / "fixtures" / "propose-golden-grid-default.json"
assert out.read_bytes() == golden.read_bytes(), (
"the default artifact over a grid table diverges from its committed golden bytes"
)