The context sets, the packaged knowledge bases and the example bundles are replaced by one fictitious example set about IT operations in an invented organisation: three context sets (serverrom-2027, driftsavtale-2027 and the two-base drift-og-avtale-2027), two synthetic knowledge bases under src/portfolio_optimiser/data/kunnskapsbaser and two example bundles under src/portfolio_optimiser/data/bundles. Numbers, codes and structural values in tests and fixtures are kept; names, ids and wording change. Dated measurement documents that only recorded runs on the replaced material are deleted. Gate figures measured on the new set are not comparable with earlier ones. The exclusion gate from the previous commit is green: 0 tracked files hit outside the shared/ subtree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
268 lines
14 KiB
Python
268 lines
14 KiB
Python
"""P13b: a BLOCK sequence of mappings is provenance po can read, not provenance po cannot.
|
|
|
|
RED-FIRST, measured 2026-09-12 with the full denominator: every concept file in all four
|
|
knowledge bases delivered at the time wrote ``sources`` as a BLOCK sequence — **4605 of 4605, and 0
|
|
in flow form**. ``read_provenance`` answered ``UnreadableProvenance(reason="block-sequence")`` for
|
|
every one of them, so ``evidence_for`` reported ``state="unreadable"`` on 4605 of 4605 documents:
|
|
the falsification layer had no address for any document in any base po was about to be
|
|
stress-tested against. The package's two example bases write the same form (``sources`` read as
|
|
entries in 306 of 306 and 301 of 301 concept files, re-measured below rather than quoted).
|
|
|
|
**This widens the READER and nothing else.** The producer still EMITS flow (measured: okf's
|
|
``materialize._render_sources`` is unchanged in 0.8.5), so no bundle bytes move, and
|
|
``write_concept_file``/``verified_field`` still refuse what the flow decoder refuses — the writer
|
|
keeps refusing exactly what the reader could not read, which is the property the round-trip gate
|
|
exists for. What changes is that a form the producer's own SPEC §5.1 documents, and that the
|
|
delivered bases actually use, stops being reported as unreadable.
|
|
|
|
**Two refusals are KEPT, and they are this file's discriminators.** ``block-mapping`` (an indented
|
|
continuation with no ``- `` item) and ``unsupported-flow`` (a single-line value the flow decoder
|
|
refuses) must still come back as ``UnreadableProvenance``. Without those arms a reader widened to
|
|
"anything indented is entries" would pass every positive arm here while inventing entries the
|
|
document does not have — the repo's vacuous-gate class.
|
|
|
|
The grammar is po's OWN, never okf's imported: the pair separator is colon-SPACE
|
|
(``_find_pair_separator``, the rule ``decode_flow_value`` already uses), because two separator rules
|
|
in one module are two answers to one question. okf's ``consume.read_sources`` was read for the FORM
|
|
and not called — po calls no okf reader, which is measured and deliberate (P13 § 1a).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from portfolio_optimiser import frozen_bundles, okf
|
|
|
|
_FIXTURES = Path(__file__).resolve().parent / "fixtures" / "p13b-block-sources"
|
|
|
|
#: A byte-identical copy of a REAL concept file of the example base (driftskrav-2027,
|
|
#: ``krav/D100/id-7aed3659-…md``, sha1 0242b9f9…) — pinned byte-for-byte by the arm below, so it
|
|
#: cannot drift from what the base ships. A hand-written approximation would prove the reader
|
|
#: handles what I imagined the form to be.
|
|
_REAL = _FIXTURES / "driftskrav-krav-4-2-5-1-3.md"
|
|
_REAL_IN_BASE = "krav/D100/id-7aed3659-dbbe-5eb5-9b96-e6a693ff0c3c.md"
|
|
|
|
|
|
def _write(tmp_path: Path, frontmatter: str) -> Path:
|
|
path = tmp_path / "c.md"
|
|
path.write_text(f"---\n{frontmatter}\n---\n\nbody\n", encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def test_the_real_delivered_concept_file_yields_its_address() -> None:
|
|
"""The measurement this whole row exists for, against the actual bytes on disk."""
|
|
entries = okf.read_provenance(_REAL, "sources")
|
|
assert isinstance(entries, tuple), f"still unreadable: {entries!r}"
|
|
assert entries == (
|
|
{
|
|
"resource": "https://example.invalid/eksempelvirksomheten/driftskrav/d100",
|
|
"title": "D100:2027",
|
|
},
|
|
)
|
|
|
|
|
|
def test_the_fixture_is_the_base_file_byte_for_byte_and_every_base_file_reads() -> None:
|
|
"""The fixture is only evidence while it IS the delivered file: pinned against the base the
|
|
package ships. And the one file stands for the whole population — every concept file of both
|
|
example bases yields its ``sources`` as entries, with the denominator named."""
|
|
base = frozen_bundles.bundle_dir("driftskrav-2027")
|
|
assert _REAL.read_bytes() == (base / _REAL_IN_BASE).read_bytes()
|
|
counts = {}
|
|
for name in ("driftskrav-2027", "prosesskatalog-2027"):
|
|
root = frozen_bundles.bundle_dir(name)
|
|
files = okf.navigate_bundle(str(root)).context_files
|
|
read = sum(isinstance(okf.read_provenance(root / f.name, "sources"), tuple) for f in files)
|
|
counts[name] = (read, len(files))
|
|
assert counts == {"driftskrav-2027": (306, 306), "prosesskatalog-2027": (301, 301)}, counts
|
|
|
|
|
|
def test_evidence_for_reports_present_on_the_real_file() -> None:
|
|
"""The three-state answer a falsification verdict acts on, end to end. ``tier`` stays ``None``
|
|
because ``sources`` is not the key SPEC §5.3 tiers — the B4 rule, unchanged by this widening."""
|
|
evidence = okf.evidence_for(_REAL, "sources")
|
|
assert evidence.state == "present"
|
|
assert evidence.items_seen == 1
|
|
assert evidence.tier is None
|
|
assert okf.evidence_notice(evidence) is None
|
|
|
|
|
|
def test_a_two_entry_block_sequence_keeps_both_entries(tmp_path: Path) -> None:
|
|
"""More than one address, so the positive arm cannot pass on a reader that returns the first."""
|
|
path = _write(
|
|
tmp_path,
|
|
"type: concept\n"
|
|
"sources:\n"
|
|
" - resource: https://a.example/doc?q=1\n"
|
|
" title: A\n"
|
|
" - resource: https://b.example/doc\n"
|
|
" id: b\n",
|
|
)
|
|
assert okf.read_provenance(path, "sources") == (
|
|
{"resource": "https://a.example/doc?q=1", "title": "A"},
|
|
{"resource": "https://b.example/doc", "id": "b"},
|
|
)
|
|
|
|
|
|
def test_the_key_after_a_block_sequence_is_still_read(tmp_path: Path) -> None:
|
|
"""The continuation must stop at the first unindented line, or the reader swallows the rest of
|
|
the frontmatter into the last entry."""
|
|
path = _write(
|
|
tmp_path,
|
|
"type: concept\nsources:\n - resource: https://a.example/d\nstatus: stable\n",
|
|
)
|
|
assert okf.read_provenance(path, "sources") == ({"resource": "https://a.example/d"},)
|
|
assert okf.parse_frontmatter(path)["status"] == "stable"
|
|
|
|
|
|
def test_a_value_carrying_a_colon_survives_whole(tmp_path: Path) -> None:
|
|
"""``title: D100:2027`` is one pair and the value keeps its own colon, as does a URL's ``://``.
|
|
|
|
**This arm is a REGRESSION GUARD, not a discriminator, and the distinction was measured rather
|
|
than assumed.** It was written claiming to prove the colon-SPACE rule, and mutation M5 (separator
|
|
changed to the FIRST colon) left it GREEN: the first colon in ``resource: https://…`` and in
|
|
``title: D100:2027`` is the same one colon-SPACE finds, so the two rules agree on every value
|
|
shaped like a delivered one. The arm that actually tells them apart is
|
|
``test_an_item_with_no_pair_separator_is_refused`` — under first-colon, ``- https://a.example/d``
|
|
decodes to ``{'https': '//a.example/d'}``, a key invented out of a URL scheme. Kept because it
|
|
pins the values the delivered bases actually carry; labelled honestly because a test that
|
|
cannot separate two implementations proves nothing about them."""
|
|
path = _write(
|
|
tmp_path,
|
|
"type: concept\nsources:\n - resource: https://x.example/a\n title: D100:2027\n",
|
|
)
|
|
assert okf.read_provenance(path, "sources") == (
|
|
{"resource": "https://x.example/a", "title": "D100:2027"},
|
|
)
|
|
|
|
|
|
# --- the KEPT refusals: the discriminators ----------------------------------------------------
|
|
|
|
|
|
def test_a_block_mapping_is_still_unreadable(tmp_path: Path) -> None:
|
|
"""No ``- `` item, so no entry was opened. Folding it into one would invent an entry the
|
|
document does not carry."""
|
|
path = _write(tmp_path, "type: concept\nverified:\n by: someone\n at: 2026-01-01\n")
|
|
result = okf.read_provenance(path, "verified")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.reason == "block-mapping"
|
|
|
|
|
|
def test_an_indented_line_before_any_item_is_refused(tmp_path: Path) -> None:
|
|
"""The sharp case: a continuation that LOOKS like a sequence but opens with a bare pair. Refused
|
|
rather than folded, which is where a naive block reader invents an entry.
|
|
|
|
**A predicted reason was FALSIFIED by the measurement and is written down as measured.** This
|
|
arm expected ``block-mapping``; the answer is ``block-sequence``, because the caller's reason is
|
|
chosen by whether any ``- `` item is PRESENT, and one is — it simply does not decode. That is
|
|
the honest label: ``block-mapping`` is reserved for a continuation carrying no item at all
|
|
(the arm above), and relabelling this one would make the two indistinguishable."""
|
|
path = _write(
|
|
tmp_path,
|
|
"type: concept\nsources:\n title: stray\n - resource: https://a.example/d\n",
|
|
)
|
|
result = okf.read_provenance(path, "sources")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.reason == "block-sequence"
|
|
assert result.items_seen == 1
|
|
|
|
|
|
def test_an_item_with_no_pair_separator_is_refused(tmp_path: Path) -> None:
|
|
"""``- just-a-scalar`` names a resource without saying so — the same refusal the flow decoder
|
|
makes for a bare scalar entry, so the two carriers cannot disagree about what an entry is.
|
|
|
|
**This is the ONE arm that witnesses the colon-SPACE rule** (measured by mutation M5): a reader
|
|
splitting on the FIRST colon reads this item as ``{'https': '//a.example/d'}``, inventing a key
|
|
out of a URL scheme instead of refusing. Every delivered ``resource`` is a URL, so that reader
|
|
would mint 4605 such keys rather than fail once."""
|
|
path = _write(tmp_path, "type: concept\nsources:\n - https://a.example/d\n")
|
|
result = okf.read_provenance(path, "sources")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.reason == "block-sequence"
|
|
|
|
|
|
def test_an_unsupported_flow_value_is_still_unreadable(tmp_path: Path) -> None:
|
|
"""The other kept refusal: a single-line value the flow decoder rejects never reaches the block
|
|
path at all."""
|
|
path = _write(tmp_path, "type: concept\nsources: [not-a-mapping]\n")
|
|
result = okf.read_provenance(path, "sources")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.reason == "unsupported-flow"
|
|
|
|
|
|
def test_an_absent_key_is_still_none(tmp_path: Path) -> None:
|
|
"""Absence is ``None``, never an empty tuple — the F2 principle one layer down, and the control
|
|
that keeps "unreadable" and "absent" from collapsing into each other."""
|
|
path = _write(tmp_path, "type: concept\ntitle: t\n")
|
|
assert okf.read_provenance(path, "sources") is None
|
|
|
|
|
|
def test_the_flow_form_still_decodes(tmp_path: Path) -> None:
|
|
"""Control: widening the reader to the block form must not cost the flow form, which is what
|
|
every commons-owned fixture and the producer's own emitter still write."""
|
|
path = _write(tmp_path, "type: concept\nsources: [{ id: a, resource: https://a.example/d }]\n")
|
|
assert okf.read_provenance(path, "sources") == ({"id": "a", "resource": "https://a.example/d"},)
|
|
|
|
|
|
@pytest.mark.parametrize("key", ["verified", "sources", "attester"])
|
|
def test_the_block_reader_is_key_agnostic(tmp_path: Path, key: str) -> None:
|
|
"""Whatever key carries the sequence, the same grammar reads it — never a hard-coded
|
|
``{id, resource}``, which would refuse the very shapes this seam exists for (the B4 rule)."""
|
|
path = _write(tmp_path, f"type: concept\n{key}:\n - by: a\n at: 2026-01-01\n")
|
|
assert okf.read_provenance(path, key) == ({"by": "a", "at": "2026-01-01"},)
|
|
|
|
|
|
# --- the FLOW-mapping item: found by measuring my own reader, not by a failing arm -------------
|
|
#
|
|
# A block sequence whose ITEMS are flow mappings (``- { id: a, resource: x }``) is SPEC-canonical
|
|
# and is exactly what ``tests/golden/block-form-provenance`` writes for ``verified``. The first
|
|
# block reader decoded it as ``{'{ id': 'a, resource: https://x/y }'}`` — a key that is not a key —
|
|
# which is the "invent an entry the document does not carry" failure its own docstring forbids. No
|
|
# arm caught it: on ``verified`` the §5.2 "must name an actor" rule refused it for an unrelated
|
|
# reason, so the fixture that carries the shape was shielded by accident. Measured, then fixed.
|
|
|
|
|
|
def test_a_block_item_that_is_a_flow_mapping_is_decoded_by_the_flow_decoder(tmp_path: Path) -> None:
|
|
"""One decoder, two carriers: the item goes through ``decode_flow_value``, so the block and flow
|
|
spellings of the same entry cannot come back different."""
|
|
path = _write(
|
|
tmp_path, "type: concept\nsources:\n - { id: a, resource: https://x.example/y }\n"
|
|
)
|
|
assert okf.read_provenance(path, "sources") == ({"id": "a", "resource": "https://x.example/y"},)
|
|
|
|
|
|
def test_two_flow_mapping_items_keep_their_order(tmp_path: Path) -> None:
|
|
"""The order-sensitive case the fixture's own prose calls the whole point: a reader that kept
|
|
the last entry it saw would report the wrong actor first."""
|
|
path = _write(
|
|
tmp_path,
|
|
"type: concept\nverified:\n"
|
|
" - { by: human:a, at: 2026-09-02T09:00:00Z }\n"
|
|
" - { by: process:b, at: 2026-09-02T10:00:00Z }\n",
|
|
)
|
|
assert okf.read_provenance(path, "verified") == (
|
|
{"by": "human:a", "at": "2026-09-02T09:00:00Z"},
|
|
{"by": "process:b", "at": "2026-09-02T10:00:00Z"},
|
|
)
|
|
|
|
|
|
def test_a_flow_item_with_a_bare_continuation_is_refused(tmp_path: Path) -> None:
|
|
"""Mixing the two spellings within one entry is refused rather than merged — merging would
|
|
decide, silently, which carrier wins for a document that used both."""
|
|
path = _write(
|
|
tmp_path,
|
|
"type: concept\nsources:\n - { id: a, resource: https://x.example/y }\n title: T\n",
|
|
)
|
|
result = okf.read_provenance(path, "sources")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.reason == "block-sequence"
|
|
|
|
|
|
def test_a_malformed_flow_item_is_refused_not_guessed(tmp_path: Path) -> None:
|
|
"""The discriminator against the defect this block exists for: an item the flow decoder refuses
|
|
must come back unreadable, never as a key that is not a key."""
|
|
path = _write(tmp_path, "type: concept\nsources:\n - { id a, resource x }\n")
|
|
result = okf.read_provenance(path, "sources")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.reason == "block-sequence"
|