portfolio-optimiser/tests/test_prepass_excerpt_fields_loadbearing.py
Kjell Tore Guttormsen 2453246d4b feat(prepass): the excerpt's new fields reach the prompt, po stops dropping them twice
P2 measured that the producer's payload now carries title/req_number/sources/source_* on
every excerpt (14 members, was 9), but PrepassExcerpt ignored them (extra="ignore") and
_data_blocks rendered only concept_id/adjudication/trust_tier -- so (b') was a po verdict,
never a model verdict. title/req_number/sources are now named fields; source_* locators are
read via model_extra and a prefix scan (measured: the producer treats source_* as an
open-ended family, not a fixed allowlist), so a future producer's new source_foo key reaches
the prompt without a code change here. A P1-form payload renders byte-identical to before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 16:42:17 +02:00

187 lines
9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Load-bearing gate for order 20260908T141941Z (P3): the excerpt's new fields reach the prompt.
P2 (`docs/2026-09-08-n-bundlene-hypoteseform.md` SS 4) measured that llm-ingestion-okf's producer
now emits ``title``, ``req_number``, ``sources`` and per-excerpt ``source_*`` locators on every
excerpt (14 members, was 9) -- and that ``po`` drops all five TWICE: ``PrepassExcerpt`` is
``extra="ignore"`` so parsing throws them away, and ``_data_blocks`` renders only ``concept_id``,
``adjudication`` and ``trust_tier`` so even a field that survived parsing would not reach the
DATA block. (b') -- "did the model name the concept" -- was therefore a ``po`` verdict, never a
model verdict: no implementation of the model could have passed it while the field never arrived.
**Field-form decision, measured, not guessed.** The producer's OWN message (cited in P2 SS 4,
"differansen er at K2 baerer fem source_*-lokatorer der N-bundlene baerer to -- prefiks-regelen,
ikke en allowlist") states that ``source_*`` is an open-ended FAMILY, not a fixed two-member
allowlist. Two forms were compared against "does a future producer's new ``source_foo`` key reach
the prompt without a code change here":
- **Named fields** (``source_element_id: str | None``, ``source_sha256: str | None``, ...): a
producer adding a THIRD ``source_*`` member requires a new field declared on ``PrepassExcerpt``
and a new line in the renderer -- a code change, exactly what the prefix rule says not to expect.
- **``model_extra``** (``extra="allow"`` on ``PrepassExcerpt`` alone, read back via a
``source_``-prefix scan): a new ``source_*`` key lands in ``model_extra`` unnamed and is picked
up by the SAME scan with zero code change.
The second form is what P3 implements, for ``source_*`` locators only. ``title``, ``req_number``
and ``sources`` are SS-8-declared, singular, fixed members -- not a growing family -- so they are
named fields.
**Known-negative, gated separately below:** a payload without the five new fields (the P1 form,
9 members) must render BYTE-IDENTICAL to before this change. The header line's format is the
thing that could drift, so it is pinned literally, not diffed against a recorded golden string.
"""
from __future__ import annotations
import json
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"
def _raw() -> dict[str, Any]:
return json.loads(FIXTURE.read_text(encoding="utf-8"))
def _raw_with_new_fields() -> dict[str, Any]:
"""The P1-form fixture's first excerpt, upgraded to the shape P2 measured on a real payload."""
raw = _raw()
excerpt = raw["excerpts"][0]
assert excerpt["concept_id"] == "bygg-kontor-nord"
excerpt["title"] = "Kontorbygg Nord (BYGG-KONTOR-NORD)"
excerpt["req_number"] = "Krav 3.3.113"
excerpt["sources"] = [
{"resource": "https://viewers.vegnorm.vegvesen.no/api/nisosts/859984", "title": "N100:2023"}
]
excerpt["source_element_id"] = "id-4b61eee9-a149-42b3-863d-293b8320c15a"
excerpt["source_sha256"] = "c58e8bbc5fa9a5400c111e51b04c05f2cfd9edabd884ef5352a486fdab2cb5ab"
return raw
# --- the fields survive parsing ---------------------------------------------------------------
def test_the_excerpt_carries_title_req_number_and_sources() -> None:
payload = prepass.PrepassPayload.model_validate(_raw_with_new_fields())
excerpt = payload.excerpts[0]
assert excerpt.title == "Kontorbygg Nord (BYGG-KONTOR-NORD)"
assert excerpt.req_number == "Krav 3.3.113"
assert excerpt.sources is not None
assert excerpt.sources[0].resource == "https://viewers.vegnorm.vegvesen.no/api/nisosts/859984"
def test_a_future_source_star_key_survives_without_a_code_change() -> None:
"""The measured discriminator: a THIRD locator a future producer adds, never declared as a
named field anywhere in this module, still reaches the excerpt through the prefix scan."""
raw = _raw_with_new_fields()
raw["excerpts"][0]["source_page_number"] = "12"
payload = prepass.PrepassPayload.model_validate(raw)
excerpt = payload.excerpts[0]
locators = dict(excerpt.source_locators())
assert locators["source_element_id"] == "id-4b61eee9-a149-42b3-863d-293b8320c15a"
assert locators["source_sha256"] == (
"c58e8bbc5fa9a5400c111e51b04c05f2cfd9edabd884ef5352a486fdab2cb5ab"
)
assert locators["source_page_number"] == "12"
def test_source_locators_excludes_the_named_sources_field() -> None:
"""``sources`` (the list) is a named field and must not double up as its own locator entry --
it is not itself a ``source_*`` SCALAR."""
payload = prepass.PrepassPayload.model_validate(_raw_with_new_fields())
keys = {key for key, _ in payload.excerpts[0].source_locators()}
assert "sources" not in keys
def test_a_p1_form_excerpt_has_no_source_locators() -> None:
"""The absence control: an excerpt carrying none of the new fields reports an empty tuple,
never a fabricated entry."""
payload = prepass.load_prepass_payload(str(FIXTURE))
assert payload.excerpts[0].source_locators() == ()
assert payload.excerpts[0].title is None
assert payload.excerpts[0].req_number is None
assert payload.excerpts[0].sources is None
# --- the fields reach the DATA block ------------------------------------------------------------
def _header_for(rendering: str, concept_id: str) -> str:
for line in rendering.split("\n"):
if line.startswith(f"--- BEGIN DATA {concept_id} "):
return line
raise AssertionError(f"no BEGIN DATA line for {concept_id!r} in rendering")
def test_the_data_block_renders_req_number_title_and_the_address() -> None:
payload = prepass.PrepassPayload.model_validate(_raw_with_new_fields())
header = _header_for(prepass.render_context(payload), "bygg-kontor-nord")
assert "Krav 3.3.113" in header
assert "Kontorbygg Nord (BYGG-KONTOR-NORD)" in header
assert "https://viewers.vegnorm.vegvesen.no/api/nisosts/859984" in header
assert "id-4b61eee9-a149-42b3-863d-293b8320c15a" in header
def test_the_data_block_renders_a_future_locator_too() -> None:
"""The renderer reads the SAME ``source_locators()`` scan the parsing test proved is
code-change-free, so a new locator reaches the prompt without touching ``_data_blocks``."""
raw = _raw_with_new_fields()
raw["excerpts"][0]["source_page_number"] = "12"
payload = prepass.PrepassPayload.model_validate(raw)
header = _header_for(prepass.render_context(payload), "bygg-kontor-nord")
assert "source_page_number: 12" in header
def test_render_seed_also_carries_the_new_fields() -> None:
"""``render_seed`` shares ``_data_blocks`` with ``render_context`` (ko-(p)); a fix that patched
only one arm would leave the exploration door silently behind the debate door."""
payload = prepass.PrepassPayload.model_validate(_raw_with_new_fields())
header = _header_for(prepass.render_seed(payload), "bygg-kontor-nord")
assert "Krav 3.3.113" in header
assert "Kontorbygg Nord (BYGG-KONTOR-NORD)" in header
# --- known-negative: a P1-form payload renders byte-identical to before ------------------------
def test_a_p1_form_payload_renders_the_header_exactly_as_before() -> None:
"""The known-negative. Every excerpt in the shipped fixture carries none of the five new
fields, so the header must be EXACTLY the old two-field form -- pinned literally, because a
change to the conditional append logic that leaked an empty clause (e.g. a trailing ``, ``)
would not show up in a substring assert."""
payload = prepass.load_prepass_payload(str(FIXTURE))
rendering = prepass.render_context(payload)
for excerpt in payload.excerpts:
expected = (
f"--- BEGIN DATA {excerpt.concept_id} "
f"(adjudication: {excerpt.adjudication}, trust_tier: {excerpt.trust_tier}) ---"
)
assert expected in rendering, rendering
def test_a_p1_form_payload_render_seed_also_unchanged() -> None:
payload = prepass.load_prepass_payload(str(FIXTURE))
rendering = prepass.render_seed(payload)
for excerpt in payload.excerpts:
expected = (
f"--- BEGIN DATA {excerpt.concept_id} "
f"(adjudication: {excerpt.adjudication}, trust_tier: {excerpt.trust_tier}) ---"
)
assert expected in rendering, rendering
def test_a_partially_upgraded_payload_only_appends_the_fields_it_has() -> None:
"""One excerpt carries the new fields, a sibling does not (a real corpus mixes eras of
ingestion) -- the sibling's header must stay in the old two-field form."""
raw = _raw_with_new_fields()
payload = prepass.PrepassPayload.model_validate(raw)
rendering = prepass.render_context(payload)
sibling = payload.excerpts[1]
assert sibling.title is None
expected_sibling = (
f"--- BEGIN DATA {sibling.concept_id} "
f"(adjudication: {sibling.adjudication}, trust_tier: {sibling.trust_tier}) ---"
)
assert expected_sibling in rendering, rendering