P8's grounding-offer measurement repeated on the okf v0.7.0 bundles, free, NOK 0.
N100/N200/N500 reproduce P8 EXACTLY (446/1133/270 concepts, 462041/1500962/408220
chars, 435/982/272 identifiers, 0 cost lines, derive REFUSED) - as predicted, because
vegnormal never went through okf's pandoc converter. K2 on the pinned v0.7.0 build
(453 concepts / 865 md) offers 65 identifiers against P8's 50: no row got worse
(0 lost, 15 new, derive REFUSED on both builds with a byte-identical message).
The cause is MEASURED, not guessed: CONTENT, not the concept rename. Concept NAMES
contribute 0 of 50 identifiers in the old build and 0 of 65 in the new one, because
the identifier forms require an uppercase head and a concept slug is lowercase - so
the rename cannot move the count at all. The 15 new tokens come from three documents
present under the SAME name in both builds with different bodies (snitt-e.md
28 -> 11029 chars, generell-orientering.md 1662 -> 47503); 102 of 414 shared concept
names differ in body length.
okf check (0.7.0, --skill/--payload): 15 rules; payload-n100 rc 0 with 0 findings;
the two K2 payloads rc 1 with 12 and 8 findings, all excerpt_unnamed - payload AGE
(recorded before the title field existed), not a v0.7.0 regression; known-negative
{} rc 1 with 9 findings, reproducing V3's figure, so the check can fail.
Two docstrings corrected to the new concept id, each naming the build its numbers
were measured on. src/ carries 0 occurrences of the old form. The recorder-test
string label and the two *-SYNTETISK fixtures are left untouched with the reason
stated: the label is arbitrary and looked up nowhere, and the fixtures simulate
converter output as it was.
No new seam, no new function, no contract change against okf. _ground_against_input
is untouched. No paid run.
Suite 1570 passed / 5 skipped, golden demo-transcript.stdout content sha1 unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
9.3 KiB
Python
182 lines
9.3 KiB
Python
"""A spreadsheet render's PADDING is dropped on the way into the prompt; nothing else is.
|
|
|
|
**Finding 5 of the S7 acid test** (``docs/2026-09-07-syretest-s7-prepass-k2.md`` § 10 pkt. 5), after
|
|
S7c had already felled the obvious cure. Opening both locks DID deliver K2's price schedule -- it
|
|
came back at rank 10 and its bytes stood in 2 of 11 prompts -- and the model still cited
|
|
``5647500`` / ``prissammenstilling`` / ``prisskjema`` / ``pris`` in **0 of 4** and **0 of 11**
|
|
answers (``docs/2026-09-08-syretest-s7c-begge-laaser-k2.md`` § 5). The blind spot had moved off the
|
|
ranking and onto the READING, and what po owns there is the rendering of the excerpt that reaches
|
|
the prompt.
|
|
|
|
**MEASURED FIRST, on the actual rendering path** (``prepass.concept_text``, the local re-derivation
|
|
that ``verify_against_bundle`` binds a payload to -- not the raw file), against
|
|
``~/repos/portfolio-optimiser/scratchpad/s7-prepass/k2-bundle-s7/del-ii-bilag-7-prisskjema/prissammenstilling-sheet-1.md``
|
|
(the id is quoted as that bundle actually carried it; okf ``6ff18fd`` dropped the converter's
|
|
``{#...}`` anchor, so a bundle built after it names the same concept ``prissammenstilling.md`` --
|
|
the numbers below are the ``k2-bundle-s7`` build and are not restated for any later one):
|
|
|
|
* raw on disk 119 lines / 101 188 B; delivered excerpt **104 lines / 67 245 characters** -- which
|
|
reconciles the two denominators the order carried: the same document, before and after the
|
|
frontmatter split and the per-line ``rstrip`` the producer does.
|
|
* the delivered text still holds **208** interior whitespace runs of >= 2 characters, **117** of
|
|
them >= 100 and the longest **887**;
|
|
* those >= 100 runs are **56 806 of 67 245 characters = 84.5 %** of the excerpt, spread over
|
|
**72 of 104 lines**.
|
|
|
|
So a label and its amount reach the model hundreds of characters apart -- ``82`` then 593 spaces
|
|
then ``Prosjektering (tiltransport av prosjekterende)`` then 250 spaces then ``5647500.0`` -- and
|
|
84.5 % of what the run pays for is a column width pandoc chose.
|
|
|
|
**The collapse is DETERMINISTIC and carries no judgement**, and each half of that is gated below:
|
|
|
|
* interior runs of spaces/tabs collapse to ONE space -- K = 2, i.e. there is no threshold to argue
|
|
about; "two or more" is the whole rule.
|
|
* LEADING whitespace is untouched. Indentation is markdown structure (nested lists, indented code),
|
|
not padding, and a rule that flattened it would rewrite documents rather than unpad them.
|
|
* no newline is touched, so the line count is invariant and no row is merged with its neighbour.
|
|
* no non-whitespace character is touched, so no number can change -- asserted as the per-line
|
|
non-whitespace sequence, not merely as "the digits are still somewhere".
|
|
|
|
**WHERE it lives is the load-bearing choice.** It is in ``_data_blocks`` -- the ONE renderer both
|
|
arms share (kø-(p)) -- and therefore AFTER ``verify_against_bundle``. ``concept_text`` is the
|
|
re-derivation a payload's bytes are checked against, so collapsing there would make every payload
|
|
ever written fail its own digest, and would defeat the gate that stops a payload delivering bytes
|
|
the base does not hold. Arm (f) is that separation.
|
|
|
|
**Scope limit, stated.** That a model then USES the numbers cannot be shown here -- it would take a
|
|
paid run, and none is ordered. What is delivered is the deterministic property; a paid confirmation
|
|
is a later, separate order.
|
|
|
|
**Honesty limit, stated.** Collapsing to a single space loses the CELL BOUNDARY: after the collapse
|
|
``Post SUM`` is indistinguishable from prose that happens to contain those two words. That is a
|
|
real loss. Inventing a delimiter to carry the boundary would be exactly the judgement this rule is
|
|
written to avoid, and no measurement here says which delimiter a model reads better; so the loss is
|
|
recorded rather than papered over.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from portfolio_optimiser import prepass
|
|
|
|
FIXTURE = Path(__file__).parent / "fixtures" / "prepass" / "bygg-energi-mikro-fixture.payload.json"
|
|
|
|
#: A K2-shaped row: code, 593 spaces, description, 250 spaces, amount. The widths are the ones
|
|
#: measured on line 34 of the real price schedule, so the fixture is the measurement rather than a
|
|
#: round number chosen for the test.
|
|
_CODE = "82"
|
|
_DESC = "Prosjektering (tiltransport av prosjekterende)"
|
|
_AMOUNT = "5647500.0"
|
|
_PADDED_ROW = f" {_CODE}{' ' * 593}{_DESC}{' ' * 250}{_AMOUNT}"
|
|
#: An indented fenced block: leading whitespace is structure and must survive untouched.
|
|
_INDENTED = " indented code line"
|
|
_PADDED_TEXT = "\n".join(["## Prissammenstilling", "", _PADDED_ROW, _INDENTED, ""])
|
|
|
|
|
|
def _payload_with(text: str) -> prepass.PrepassPayload:
|
|
raw: dict[str, Any] = json.loads(FIXTURE.read_text(encoding="utf-8"))
|
|
raw["excerpts"] = [raw["excerpts"][0]]
|
|
raw["excerpts"][0]["text"] = text
|
|
return prepass.PrepassPayload.model_validate(raw)
|
|
|
|
|
|
def _longest_interior_run(text: str) -> int:
|
|
"""The longest run of horizontal whitespace that is NOT a line's leading indentation."""
|
|
longest = 0
|
|
for line in text.split("\n"):
|
|
body = line.lstrip(" \t")
|
|
for match in re.finditer(r"[ \t]{2,}", body):
|
|
longest = max(longest, len(match.group(0)))
|
|
return longest
|
|
|
|
|
|
# --- (a) the delivered excerpt reaches the prompt with a BOUNDED separator ---------------------
|
|
|
|
|
|
def test_the_rendered_excerpt_separates_label_and_amount_by_a_bounded_separator() -> None:
|
|
"""(a) The property finding 5 is about. Detach point: remove the collapse from
|
|
``_data_blocks`` -> RED, because the 593- and 250-character runs ride into the prompt."""
|
|
rendered = prepass.render_context(_payload_with(_PADDED_TEXT))
|
|
assert _longest_interior_run(rendered) <= 1, (
|
|
"the rendered excerpt still carries a padding run; a label and its amount reach the "
|
|
"model hundreds of characters apart"
|
|
)
|
|
assert f"{_CODE} {_DESC} {_AMOUNT}" in rendered, (
|
|
"the row's three cells no longer read as one row"
|
|
)
|
|
|
|
|
|
def test_a_text_that_needs_no_collapse_renders_unchanged() -> None:
|
|
"""(a) CONTROL. An excerpt with no padding must render exactly as before, or arm (a) would be
|
|
satisfied by a renderer that mangles every payload equally."""
|
|
plain = "## Heading\n\nOne ordinary sentence with single spaces.\n"
|
|
assert plain in prepass.render_context(_payload_with(plain))
|
|
|
|
|
|
# --- (b) known-negative: no number, and no other character, is ever changed --------------------
|
|
|
|
|
|
def test_no_non_whitespace_character_is_changed() -> None:
|
|
"""(b) KNOWN-NEGATIVE. The per-line sequence of non-whitespace characters is identical before
|
|
and after. Stronger than "the digits are still somewhere": a rule that stripped a trailing
|
|
``.0``, or reordered two cells, would pass a looser assertion and fail this one."""
|
|
collapsed = prepass.collapse_padding(_PADDED_TEXT)
|
|
for before, after in zip(_PADDED_TEXT.split("\n"), collapsed.split("\n"), strict=True):
|
|
assert re.sub(r"\s+", "", before) == re.sub(r"\s+", "", after)
|
|
assert _AMOUNT in collapsed
|
|
|
|
|
|
# --- (c) known-negative: no line is merged with its neighbour ----------------------------------
|
|
|
|
|
|
def test_no_line_is_merged_with_its_neighbour() -> None:
|
|
"""(c) KNOWN-NEGATIVE. Newlines are not whitespace this rule may touch: merging rows is how a
|
|
naive ``re.sub(r"\\s+", " ", text)`` would silently turn a table into a paragraph."""
|
|
collapsed = prepass.collapse_padding(_PADDED_TEXT)
|
|
assert collapsed.split("\n") != []
|
|
assert len(collapsed.split("\n")) == len(_PADDED_TEXT.split("\n"))
|
|
|
|
|
|
# --- (d) leading indentation is structure, not padding -----------------------------------------
|
|
|
|
|
|
def test_leading_indentation_survives() -> None:
|
|
"""(d) An indented line keeps its indentation: markdown reads it as structure, and collapsing
|
|
it would rewrite the document rather than unpad it."""
|
|
collapsed = prepass.collapse_padding(_PADDED_TEXT)
|
|
assert _INDENTED in collapsed.split("\n")
|
|
|
|
|
|
# --- (e) ONE seam, both arms --------------------------------------------------------------------
|
|
|
|
|
|
def test_both_arms_collapse_because_they_share_one_renderer() -> None:
|
|
"""(e) ``render_seed`` is the exploration's arm and ``render_context`` the debate's; they share
|
|
``_data_blocks``. A fix in one only would leave the other reading padding."""
|
|
payload = _payload_with(_PADDED_TEXT)
|
|
for rendered in (prepass.render_context(payload), prepass.render_seed(payload)):
|
|
assert _longest_interior_run(rendered) <= 1
|
|
|
|
|
|
# --- (f) the gate is untouched: verification reads the UNCOLLAPSED text -------------------------
|
|
|
|
|
|
def test_the_local_re_derivation_is_not_collapsed() -> None:
|
|
"""(f) ``concept_text`` is what ``verify_against_bundle`` binds a payload's bytes to. Collapsing
|
|
THERE would make every payload ever written fail its own digest and would break the gate that
|
|
stops a payload delivering bytes the base does not hold. Detach point: move the collapse into
|
|
``concept_text`` -> RED."""
|
|
padded = Path(__file__).parent / "fixtures" / "prepass" / "_padded-concept.md"
|
|
padded.parent.mkdir(parents=True, exist_ok=True)
|
|
padded.write_text(f"---\ntype: reference\n---\n\n{_PADDED_ROW}\n", encoding="utf-8")
|
|
try:
|
|
derived = prepass.concept_text(padded)
|
|
assert " " * 593 in derived, (
|
|
"the local re-derivation collapsed padding; a payload's own digest can no longer match"
|
|
)
|
|
finally:
|
|
padded.unlink()
|