Replace the combined concept total, the distinct-token total and the per-level document count of earlier example bases with general wording in prose, comments and docstrings. No constant, assertion or test data changes. 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 — **every one of them, and 0
|
|
in flow form**. ``read_provenance`` answered ``UnreadableProvenance(reason="block-sequence")`` for
|
|
every one of them, so ``evidence_for`` reported ``state="unreadable"`` on every document:
|
|
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 one such key per concept 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"
|