631 lines
28 KiB
Python
631 lines
28 KiB
Python
"""One frontmatter SCANNER behind both readers — the seam the provenance decoder consumes.
|
|
|
|
``okf`` had two independent ``---``-delimiter loops: ``parse_frontmatter`` and ``_read_body``.
|
|
Two copies of one scan is the kø-(p) shape, and here the copies had already drifted — measured,
|
|
not assumed. On a file with an opening ``---`` and no closing one, ``parse_frontmatter`` consumes
|
|
every remaining line as frontmatter while ``_read_body`` falls through and hands back the WHOLE
|
|
file, delimiter line included.
|
|
|
|
That divergence is **pinned here, not fixed.** Fixing it would move the body-rendering path both
|
|
nav-goldens read, which no criterion asks for. The point of this step is that the repo gains a
|
|
second *reader* of the frontmatter block and never a second *parser* of it: ``_split_frontmatter``
|
|
scans once, and each caller keeps applying its own existing rule to the result.
|
|
|
|
Every expectation below was captured from the code as it stood BEFORE the split, so "unchanged"
|
|
means unchanged against a measurement rather than against a recollection.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
import itertools
|
|
import shutil
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from portfolio_optimiser import okf, verdicts
|
|
from portfolio_optimiser.okf import _read_body, parse_frontmatter
|
|
|
|
_OKF_SOURCE = Path(okf.__file__)
|
|
|
|
# The five shapes, and what today's two readers return for each. Captured 2026-09-02 by running
|
|
# both functions over these exact bytes; the block case's junk ``"- { by"`` key and its
|
|
# second-entry-wins value are real output, not an illustration.
|
|
_SHAPES: dict[str, str] = {
|
|
"flow": "---\ntype: concept\nverified: { by: human:a, at: 2026-01-01T00:00:00Z }\n---\nbody line\n",
|
|
"block": (
|
|
"---\ntype: concept\nverified:\n"
|
|
" - { by: human:a, at: 2026-01-01T00:00:00Z }\n"
|
|
" - { by: process:b, at: 2026-01-02T00:00:00Z }\n---\nbody line\n"
|
|
),
|
|
"continuation": "---\ntype: concept\ndescription: first part\n continued part\n---\nbody line\n",
|
|
"none": "no frontmatter here\nsecond line\n",
|
|
"unterminated": (
|
|
"---\ntype: concept\nverified: { by: human:a, at: 2026-01-01T00:00:00Z }\nbody line\n"
|
|
),
|
|
}
|
|
|
|
_EXPECTED_FRONTMATTER: dict[str, dict[str, str]] = {
|
|
"flow": {"type": "concept", "verified": "{ by: human:a, at: 2026-01-01T00:00:00Z }"},
|
|
"block": {
|
|
"type": "concept",
|
|
"verified": "",
|
|
"- { by": "process:b, at: 2026-01-02T00:00:00Z }",
|
|
},
|
|
"continuation": {"type": "concept", "description": "first part"},
|
|
"none": {},
|
|
"unterminated": {"type": "concept", "verified": "{ by: human:a, at: 2026-01-01T00:00:00Z }"},
|
|
}
|
|
|
|
_EXPECTED_BODY: dict[str, str] = {
|
|
"flow": "body line\n",
|
|
"block": "body line\n",
|
|
"continuation": "body line\n",
|
|
"none": "no frontmatter here\nsecond line\n",
|
|
"unterminated": (
|
|
"---\ntype: concept\nverified: { by: human:a, at: 2026-01-01T00:00:00Z }\nbody line\n"
|
|
),
|
|
}
|
|
|
|
|
|
_FIXTURE_COUNTER = itertools.count()
|
|
|
|
|
|
def _fixture(text: str) -> Path:
|
|
"""A throwaway document on disk. The accessor reads FILES, so an in-memory string would test a
|
|
different function than the one shipped."""
|
|
path = Path(tempfile.mkdtemp(prefix="okf-provenance-")) / f"c{next(_FIXTURE_COUNTER)}.md"
|
|
path.write_text(text, encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def _write(tmp_path: Path, shape: str) -> Path:
|
|
path = tmp_path / f"{shape}.md"
|
|
path.write_text(_SHAPES[shape], encoding="utf-8")
|
|
return path
|
|
|
|
|
|
@pytest.mark.parametrize("shape", sorted(_SHAPES))
|
|
def test_parse_frontmatter_is_unchanged_by_the_split(tmp_path: Path, shape: str) -> None:
|
|
"""``parse_frontmatter``'s dict is byte-identical to what it produced before the scanner split."""
|
|
assert parse_frontmatter(_write(tmp_path, shape)) == _EXPECTED_FRONTMATTER[shape]
|
|
|
|
|
|
@pytest.mark.parametrize("shape", sorted(_SHAPES))
|
|
def test_read_body_is_unchanged_by_the_split(tmp_path: Path, shape: str) -> None:
|
|
"""``_read_body``'s string is byte-identical to what it produced before the scanner split."""
|
|
assert _read_body(_write(tmp_path, shape)) == _EXPECTED_BODY[shape]
|
|
|
|
|
|
def test_the_two_readers_diverge_on_an_unterminated_block_and_that_is_pinned(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""The measured disagreement, asserted as a POSITIVE fact rather than left implicit.
|
|
|
|
Without an assertion of its own, a later "tidy-up" that made the two readers agree would look
|
|
like a simplification and would silently move the body-rendering path both nav-goldens read.
|
|
The divergence is the reason ``_split_frontmatter`` returns ``terminated`` instead of deciding
|
|
on its callers' behalf.
|
|
"""
|
|
path = _write(tmp_path, "unterminated")
|
|
|
|
# parse_frontmatter consumed the unterminated block as if it were closed...
|
|
assert parse_frontmatter(path)["type"] == "concept"
|
|
# ...while _read_body treated the same file as having no frontmatter at all.
|
|
assert _read_body(path) == _SHAPES["unterminated"]
|
|
assert _read_body(path).startswith("---\n")
|
|
|
|
|
|
def test_split_frontmatter_is_the_only_delimiter_scanner_in_okf() -> None:
|
|
"""The ``---`` delimiter is COMPARED against in exactly one function: ``_split_frontmatter``.
|
|
|
|
``write_concept_file`` is excluded BY NAME because it *emits* the delimiter into a formatted
|
|
string — emitting is not scanning, and a whole-file substring gate could not tell the two
|
|
apart. Docstrings are excluded for the same reason: prose that mentions the delimiter is not
|
|
a second parser. The check walks the AST and looks only for comparisons whose right-hand side
|
|
is the literal ``"---"``, which is what a scanner does and a writer never does.
|
|
"""
|
|
tree = ast.parse(_OKF_SOURCE.read_text(encoding="utf-8"))
|
|
scanners: set[str] = set()
|
|
for node in ast.walk(tree):
|
|
if not isinstance(node, ast.FunctionDef):
|
|
continue
|
|
for inner in ast.walk(node):
|
|
if isinstance(inner, ast.Compare) and any(
|
|
isinstance(c, ast.Constant) and c.value == "---" for c in inner.comparators
|
|
):
|
|
scanners.add(node.name)
|
|
assert scanners == {"_split_frontmatter"}, (
|
|
f"the delimiter is scanned in {sorted(scanners)}; it must be scanned in exactly one place "
|
|
"(write_concept_file emits it and is excluded by name)"
|
|
)
|
|
|
|
|
|
def test_load_file_reads_each_file_once(tmp_path: Path) -> None:
|
|
"""``_load_file`` opens the document ONCE, not once per reader.
|
|
|
|
Before the split it called ``parse_frontmatter`` and ``_read_body``, each of which read the
|
|
file from disk — two reads of the same bytes, with the second free to see a different file
|
|
than the first.
|
|
"""
|
|
bundle = tmp_path / "bundle"
|
|
bundle.mkdir()
|
|
(bundle / "a.md").write_text(_SHAPES["flow"], encoding="utf-8")
|
|
|
|
reads: list[str] = []
|
|
real_read_text = Path.read_text
|
|
|
|
def counting_read_text(self: Path, *args: object, **kwargs: object) -> str:
|
|
reads.append(str(self))
|
|
return real_read_text(self, *args, **kwargs) # type: ignore[arg-type]
|
|
|
|
with pytest.MonkeyPatch.context() as mp:
|
|
mp.setattr(Path, "read_text", counting_read_text)
|
|
loaded = okf._load_file(str(bundle), "a.md")
|
|
|
|
assert loaded is not None
|
|
assert loaded.type == "concept"
|
|
assert reads.count(str(bundle / "a.md")) == 1, (
|
|
f"file was read {reads.count(str(bundle / 'a.md'))} times"
|
|
)
|
|
|
|
|
|
def test_the_parsed_dict_loses_what_the_accessor_recovers(tmp_path: Path) -> None:
|
|
"""AMENDMENT C — the leak, shown by putting both readers on the SAME file in ONE arm.
|
|
|
|
``parse_frontmatter`` hands back ``""`` for a block-form ``verified``; the accessor hands back
|
|
the SHAPE and the COUNT. Asserting only the accessor's answer would leave the *reason the
|
|
accessor exists* undocumented, and the reason is the whole of condition 2.
|
|
|
|
Authored in Step 2 against an accessor that did not exist yet, and it ENABLED ITSELF when the
|
|
symbol arrived in Step 4 — a self-enabling skip rather than a TODO. What the arm asserts was
|
|
corrected at that point: an unreadable value yields ``UnreadableProvenance``, not entries, and
|
|
a test that had guessed otherwise would have been a test written against an imagined API.
|
|
"""
|
|
read_provenance = getattr(okf, "read_provenance", None)
|
|
if read_provenance is None:
|
|
pytest.skip("okf.read_provenance arrives in Step 4; this arm enables itself when it does")
|
|
|
|
path = _write(tmp_path, "block")
|
|
assert parse_frontmatter(path)["verified"] == ""
|
|
|
|
provenance = read_provenance(path, "verified")
|
|
assert isinstance(provenance, okf.UnreadableProvenance)
|
|
assert (provenance.reason, provenance.items_seen) == ("block-sequence", 2)
|
|
|
|
|
|
# --- Step 3: the flow-form decoder ------------------------------------------------------------
|
|
|
|
# The producer's own bytes, read from `llm-ingestion-okf` at HEAD `62b6192` (read-only).
|
|
# ONE mapping: `examples/ingest-golden-okf-v0-2/expected-bundle/ingest-sales.md:9` — measured, that
|
|
# is the ONLY one of 26 markdown files under `examples/` carrying a `sources:` key, so a two-mapping
|
|
# concept does not exist there to read.
|
|
# TWO mappings: `_render_sources`' byte-pinned output, asserted verbatim by their own
|
|
# `tests/test_multi_source_provenance.py:62`. It is the authoritative referent for the N>1 form at
|
|
# that HEAD, and citing it rather than hand-writing one is what keeps this arm external.
|
|
_PRODUCER_ONE_SOURCE = "[{ id: golden-v0-2-sales, resource: fixture }]"
|
|
_PRODUCER_TWO_SOURCES = (
|
|
"[{ id: golden-catalogue, resource: fixture }, { id: golden-db, resource: OKF_GOLDEN_SQL_DB }]"
|
|
)
|
|
|
|
|
|
def test_the_producers_single_entry_bytes_decode_by_value() -> None:
|
|
"""S2 — the transcription arm, against the producer's real emitted line."""
|
|
assert okf.decode_flow_value(_PRODUCER_ONE_SOURCE) == (
|
|
{"id": "golden-v0-2-sales", "resource": "fixture"},
|
|
)
|
|
|
|
|
|
def test_two_mappings_decode_to_two_INTACT_entries() -> None:
|
|
"""AMENDMENT C — multiple sources must be READABLE, not merely refused without silence.
|
|
|
|
Asserted on BOTH dicts by value. ``len() == 2`` alone stays green against a decoder that
|
|
returns the first entry twice or the last one twice, which is the very last-write-wins shape
|
|
this work exists to remove.
|
|
"""
|
|
assert okf.decode_flow_value(_PRODUCER_TWO_SOURCES) == (
|
|
{"id": "golden-catalogue", "resource": "fixture"},
|
|
{"id": "golden-db", "resource": "OKF_GOLDEN_SQL_DB"},
|
|
)
|
|
|
|
|
|
def test_entries_decode_key_agnostically() -> None:
|
|
"""Condition 2a — the segmented shape adds keys, and the decoder must not filter them.
|
|
|
|
``set(entry)`` is asserted against all four names: a ``len(result) == 1`` assert alone stays
|
|
green against a decoder that silently drops the keys it does not recognise, which would refuse
|
|
the very bundles this seam is built for.
|
|
"""
|
|
raw = "[{ id: s-1, resource: doc.pdf, segment_id: 4, source_offset: 128 }]"
|
|
(entry,) = okf.decode_flow_value(raw)
|
|
assert set(entry) == {"id", "resource", "segment_id", "source_offset"}
|
|
assert entry["segment_id"] == "4"
|
|
|
|
|
|
def test_a_bare_mapping_normalises_to_a_one_element_tuple() -> None:
|
|
"""SPEC §5.2: "Consumers MUST treat a bare mapping as a one-element list"."""
|
|
assert okf.decode_flow_value("{ by: human:a, at: 2026-01-01T00:00:00Z }") == (
|
|
{"by": "human:a", "at": "2026-01-01T00:00:00Z"},
|
|
)
|
|
|
|
|
|
def test_no_yaml_1_1_coercion() -> None:
|
|
"""S5 — a deliberate divergence from PyYAML's resolver, asserted rather than inherited.
|
|
|
|
``yes`` resolves to the boolean ``True`` under YAML 1.1 and ``1`` to an int. Here both come
|
|
back as the strings they were written as, because a value silently changing type between the
|
|
file and the consumer is the coercion class this decoder refuses to import.
|
|
"""
|
|
(entry,) = okf.decode_flow_value("{ by: process:x, flag: yes, n: 1 }")
|
|
assert entry["flag"] == "yes"
|
|
assert entry["n"] == "1"
|
|
assert isinstance(entry["n"], str)
|
|
|
|
|
|
def test_quoted_separators_survive_the_tokeniser() -> None:
|
|
"""The comma and the colon-space are separators OUTSIDE quotes and ordinary text inside them.
|
|
|
|
"Split on a separator" is where this class of decoder fails silently, so both separators get
|
|
an arm.
|
|
"""
|
|
(entry,) = okf.decode_flow_value('{ resource: "a, b", note: "x: y" }')
|
|
assert entry == {"resource": "a, b", "note": "x: y"}
|
|
|
|
|
|
def test_an_unquoted_colon_inside_a_value_is_not_a_separator() -> None:
|
|
"""``by: human:jsmith@acme`` and an ISO timestamp both carry colons with no following space."""
|
|
(entry,) = okf.decode_flow_value("{ by: human:jsmith@acme, at: 2024-01-15T10:00:00Z }")
|
|
assert entry == {"by": "human:jsmith@acme", "at": "2024-01-15T10:00:00Z"}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("raw", "marker"),
|
|
[
|
|
("[ a.pdf, b.pdf ]", "bare scalar"),
|
|
("[{ id: a, resource: b }", "unterminated"),
|
|
("{ id: a, resource: b", "unterminated"),
|
|
("[{ id: a, resource: [x, y] }]", "nested"),
|
|
("{ id: a, resource: { deep: 1 } }", "nested"),
|
|
("[]", "names no source"),
|
|
('{ resource: "unclosed }', "unterminated"),
|
|
],
|
|
)
|
|
def test_each_refusal_shape_raises_by_name(raw: str, marker: str) -> None:
|
|
"""S3 — every refusal is raised by name with its own message, never guessed past."""
|
|
with pytest.raises(okf.FlowDecodeError) as excinfo:
|
|
okf.decode_flow_value(raw)
|
|
assert marker in str(excinfo.value), (
|
|
f"{raw!r} refused, but not by the expected name: {excinfo.value}"
|
|
)
|
|
|
|
|
|
def test_a_flow_value_continued_on_the_next_line_is_refused() -> None:
|
|
"""A flow form that does not fit on one line is outside the accepted subset."""
|
|
with pytest.raises(okf.FlowDecodeError) as excinfo:
|
|
okf.decode_flow_value("[{ id: a,\n resource: b }]")
|
|
assert "one line" in str(excinfo.value)
|
|
|
|
|
|
def test_a_duplicate_key_within_one_entry_is_refused_never_last_wins() -> None:
|
|
"""Last-write-wins INSIDE the decoder would be the defect one level down."""
|
|
with pytest.raises(okf.FlowDecodeError) as excinfo:
|
|
okf.decode_flow_value("{ by: human:a, by: process:b }")
|
|
assert "duplicate" in str(excinfo.value)
|
|
|
|
|
|
def test_a_verified_entry_with_no_actor_is_refused() -> None:
|
|
"""SPEC §5.2 makes ``by`` required within a verification event, symmetric with ``resource``.
|
|
|
|
Refusing here is what lets ``trust_tier`` ASSERT that every entry names an actor instead of
|
|
assuming it — the alternative, tiering an entry that names nobody, is fabricated provenance.
|
|
"""
|
|
with pytest.raises(okf.FlowDecodeError) as excinfo:
|
|
okf.decode_flow_value("{ at: 2026-01-01T00:00:00Z }", key="verified")
|
|
assert "by" in str(excinfo.value)
|
|
|
|
# CONTROL: the same value under a different key is fine — the rule is `verified`-specific,
|
|
# not a blanket requirement that would refuse every `sources` entry.
|
|
assert okf.decode_flow_value("{ at: 2026-01-01T00:00:00Z }", key="sources") == (
|
|
{"at": "2026-01-01T00:00:00Z"},
|
|
)
|
|
|
|
|
|
# --- Step 4: the provenance accessor and its reason vocabulary --------------------------------
|
|
|
|
_FIXTURE_BUNDLE = (
|
|
Path(__file__).resolve().parents[1] / "tests" / "golden" / "block-form-provenance" / "bundle"
|
|
)
|
|
|
|
|
|
def test_a_single_entry_block_sequence_reports_shape_and_count() -> None:
|
|
"""The committed fixture's one-entry block ``verified``: the PAIR is asserted, not the token.
|
|
|
|
``reason`` alone cannot tell a one-entry list from a five-entry one, and the count alone cannot
|
|
say which shape produced it. Together they are strictly more information than a single
|
|
``block-form``/``multi-entry`` token, and neither half mislabels the other.
|
|
"""
|
|
result = okf.read_provenance(_FIXTURE_BUNDLE / "attested.md", "verified")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert (result.reason, result.items_seen) == ("block-sequence", 1)
|
|
assert result.key == "verified"
|
|
|
|
|
|
def test_a_two_entry_block_sequence_counts_both() -> None:
|
|
"""The same shape, a different count — which is what makes ``items_seen`` a real discriminator."""
|
|
result = okf.read_provenance(_FIXTURE_BUNDLE / "multi-verified.md", "verified")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert (result.reason, result.items_seen) == ("block-sequence", 2)
|
|
|
|
|
|
def test_a_block_mapping_is_named_a_mapping_not_a_sequence() -> None:
|
|
"""Both shapes are CONFORMANT OKF and merely outside the accepted subset.
|
|
|
|
The tokens name the shape the value is written in and nothing else — the decoder is not
|
|
entitled to an opinion about whether the author erred, and a token that implied one would file
|
|
this repo's own SPEC-canonical fixture as a defect.
|
|
"""
|
|
path = _fixture(
|
|
"---\ntype: concept\nverified:\n by: human:a\n at: 2026-01-01T00:00:00Z\n---\nbody\n"
|
|
)
|
|
result = okf.read_provenance(path, "verified")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert (result.reason, result.items_seen) == ("block-mapping", 1)
|
|
|
|
|
|
def test_a_key_with_an_empty_value_and_no_continuation_counts_nothing() -> None:
|
|
"""``verified:`` with nothing under it saw zero entries, and says so."""
|
|
path = _fixture("---\ntype: concept\nverified:\ntitle: x\n---\nbody\n")
|
|
result = okf.read_provenance(path, "verified")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert (result.reason, result.items_seen) == ("block-mapping", 0)
|
|
|
|
|
|
def test_a_flow_value_the_decoder_refuses_is_named_unsupported_flow() -> None:
|
|
"""``unsupported-flow`` is the ONE token that denotes a malformation, and it names the FORM."""
|
|
path = _fixture("---\ntype: concept\nsources: [ a.pdf, b.pdf ]\n---\nbody\n")
|
|
result = okf.read_provenance(path, "sources")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.reason == "unsupported-flow"
|
|
assert result.value == "[ a.pdf, b.pdf ]"
|
|
|
|
|
|
def test_a_readable_flow_value_comes_back_decoded() -> None:
|
|
"""CONTROL — an accessor that only ever answered ``UnreadableProvenance`` would pass every
|
|
negative arm above and be worthless."""
|
|
path = _fixture(
|
|
"---\ntype: concept\nsources: [{ id: golden-v0-2-sales, resource: fixture }]\n---\nbody\n"
|
|
)
|
|
assert okf.read_provenance(path, "sources") == (
|
|
{"id": "golden-v0-2-sales", "resource": "fixture"},
|
|
)
|
|
|
|
|
|
def test_an_absent_key_is_None_never_a_default() -> None:
|
|
"""Absence is ``None`` — the F2 principle one layer down.
|
|
|
|
An unread signal must not become an asserted absent one: ``None`` says "this document does not
|
|
carry the key", which is a different fact from "the key is there and unreadable" and from
|
|
"the key is there and empty".
|
|
"""
|
|
path = _fixture("---\ntype: concept\ntitle: x\n---\nbody\n")
|
|
assert okf.read_provenance(path, "sources") is None
|
|
|
|
|
|
def test_a_literal_dash_inside_a_value_does_not_inflate_the_count() -> None:
|
|
"""An item is a continuation line whose STRIPPED form starts with ``- ``, not a substring."""
|
|
path = _fixture("---\ntype: concept\nverified:\n by: human:a - reviewed - twice\n---\nbody\n")
|
|
result = okf.read_provenance(path, "verified")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert (result.reason, result.items_seen) == ("block-mapping", 1)
|
|
|
|
|
|
def test_the_offending_text_is_carried_verbatim() -> None:
|
|
"""``value`` is what the operator will edit, indentation included — never a normalised form
|
|
that would send them looking for a string their file does not contain (the ``SkippedLink``
|
|
rule, applied one layer down)."""
|
|
path = _fixture(
|
|
"---\ntype: concept\nverified:\n - { by: human:a, at: 2026-01-01T00:00:00Z }\n---\nbody\n"
|
|
)
|
|
result = okf.read_provenance(path, "verified")
|
|
assert isinstance(result, okf.UnreadableProvenance)
|
|
assert result.value == " - { by: human:a, at: 2026-01-01T00:00:00Z }"
|
|
assert result.file == str(path)
|
|
|
|
|
|
# --- Step 5: trust tier from the actor prefix -------------------------------------------------
|
|
|
|
|
|
def test_no_entries_at_all_is_unverified() -> None:
|
|
"""SPEC §5.3: "No ``verified`` key ⇒ unverified". ``None`` and the empty tuple say the same
|
|
thing here — nothing was verified — and both must reach the same floor."""
|
|
assert okf.trust_tier(None) == "unverified"
|
|
assert okf.trust_tier(()) == "unverified"
|
|
|
|
|
|
def test_a_machine_actor_gives_machine_confirmed() -> None:
|
|
assert okf.trust_tier(({"by": "process:finance-nightly", "at": "2026-01-01T00:00:00Z"},)) == (
|
|
"machine-confirmed"
|
|
)
|
|
|
|
|
|
def test_a_human_actor_gives_human_reviewed() -> None:
|
|
assert okf.trust_tier(({"by": "human:ktg", "at": "2026-01-01T00:00:00Z"},)) == "human-reviewed"
|
|
|
|
|
|
def test_the_tier_keys_off_the_PREFIX_never_a_substring() -> None:
|
|
"""``bot/human:2`` is a MACHINE actor whose identifier happens to contain ``human:``.
|
|
|
|
A substring test would promote it to the human tier — inventing a human sign-off that never
|
|
happened, which is the fabricated-provenance defect this work removes, one level down.
|
|
"""
|
|
assert okf.trust_tier(({"by": "bot/human:2", "at": "2026-01-01T00:00:00Z"},)) == (
|
|
"machine-confirmed"
|
|
)
|
|
|
|
|
|
def test_an_entry_naming_no_actor_is_refused_never_tiered() -> None:
|
|
""" "Otherwise ⇒ machine-confirmed" would mint a tier out of an entry that names nobody.
|
|
|
|
Step 3 already refuses such an entry when it decodes a ``verified`` value; this door ASSERTS
|
|
that rather than assuming it, because ``trust_tier`` is public and its caller may not have come
|
|
through the decoder.
|
|
"""
|
|
with pytest.raises(ValueError) as excinfo:
|
|
okf.trust_tier(({"at": "2026-01-01T00:00:00Z"},))
|
|
assert "by" in str(excinfo.value)
|
|
|
|
|
|
def test_a_human_LAST_entry_still_resolves_to_human_reviewed() -> None:
|
|
"""AMENDMENT C — the ordering IS the arm.
|
|
|
|
``any(...)`` and "the last entry wins" agree on every single-entry case AND on a human-FIRST
|
|
list. They disagree on exactly one shape: process first, human last. That is the one fixture
|
|
that can tell the specified rule from the measured second-entry-wins defect, so it is the one
|
|
written here.
|
|
"""
|
|
process_first_human_last = (
|
|
{"by": "process:finance-nightly", "at": "2026-01-01T02:00:00Z"},
|
|
{"by": "human:ktg", "at": "2026-01-02T09:00:00Z"},
|
|
)
|
|
assert okf.trust_tier(process_first_human_last) == "human-reviewed"
|
|
|
|
# CONTROL — human FIRST, process LAST. Without it, a function that always answered
|
|
# `human-reviewed` would satisfy the arm above.
|
|
human_first_process_last = (
|
|
{"by": "human:ktg", "at": "2026-01-02T09:00:00Z"},
|
|
{"by": "process:finance-nightly", "at": "2026-01-01T02:00:00Z"},
|
|
)
|
|
assert okf.trust_tier(human_first_process_last) == "human-reviewed"
|
|
|
|
# CONTROL — two machine actors stay machine-confirmed, so the tier is not simply a function
|
|
# of the entry COUNT (SPEC §5.3 derives it from the actor prefix alone).
|
|
assert (
|
|
okf.trust_tier(
|
|
(
|
|
{"by": "process:a", "at": "2026-01-01T02:00:00Z"},
|
|
{"by": "process:b", "at": "2026-01-02T02:00:00Z"},
|
|
)
|
|
)
|
|
== "machine-confirmed"
|
|
)
|
|
|
|
|
|
# --- Step 6: the writers emit `verified`, with the actor verbatim ------------------------------
|
|
|
|
_ENERGI_BUNDLE = Path(__file__).resolve().parents[1] / "shared" / "examples" / "bygg-energi-mikro"
|
|
|
|
|
|
def _bundle_copy(tmp_path: Path) -> str:
|
|
"""Promotion WRITES into the bundle, so every arm works on a throwaway copy — the shared,
|
|
pull-only framework-neutral fixture is never mutated (the Step-8 discipline)."""
|
|
dst = tmp_path / "bundle"
|
|
shutil.copytree(_ENERGI_BUNDLE, dst)
|
|
return str(dst)
|
|
|
|
|
|
def _approved(bundle_dir: str) -> verdicts.Verdict:
|
|
return verdicts.Verdict(
|
|
id="PROVENANCE-ROUNDTRIP",
|
|
proposal_features=verdicts.bundle_candidate_features(bundle_dir),
|
|
decision="approved",
|
|
rationale="godkjent",
|
|
)
|
|
|
|
|
|
def test_verified_field_emits_the_spec_shorthand_exactly() -> None:
|
|
"""(a) The §5.2 single-verifier flow shorthand, byte for byte."""
|
|
assert (
|
|
okf.verified_field("persona", "2026-06-30T09:00:00Z")
|
|
== "{ by: persona, at: 2026-06-30T09:00:00Z }"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("actor", ["a,b", "a{b", "a}b", "a[b", "a]b", "a\nb", "a: b"])
|
|
def test_verified_field_refuses_an_actor_that_would_restructure_the_value(actor: str) -> None:
|
|
"""(d) The unsafe set, and ``": "`` is in it for a reason that is NOT decoration.
|
|
|
|
``decode_flow_value`` splits pairs on colon-SPACE. An actor containing it would be written out
|
|
cleanly and then MIS-DECODED on the way back — the writer and the reader must agree on the
|
|
unsafe set, or the round trip lies while every byte looks fine.
|
|
"""
|
|
with pytest.raises(ValueError):
|
|
okf.verified_field(actor, "2026-06-30T09:00:00Z")
|
|
|
|
|
|
def test_a_promoted_verdict_round_trips_to_machine_confirmed(tmp_path: Path) -> None:
|
|
"""(b) Write with ``promote_verdict``, read with ``read_provenance``, tier with ``trust_tier``.
|
|
|
|
A plain approver lands in ``machine-confirmed``, and that is TRUE of it: the demo passes
|
|
``"ekspert-persona (sim)"``, a scripted stand-in, and prefixing it with ``human:`` on the
|
|
caller's behalf would mint a human sign-off nobody gave — the fabricated-provenance defect one
|
|
layer up.
|
|
"""
|
|
bundle_dir = _bundle_copy(tmp_path)
|
|
path = verdicts.promote_verdict(
|
|
bundle_dir,
|
|
_approved(bundle_dir),
|
|
approver="ekspert-persona (sim)",
|
|
experiment="exp-B",
|
|
timestamp="2026-06-30T09:00:00Z",
|
|
)
|
|
raw = path.read_text(encoding="utf-8")
|
|
assert "verified: { by: ekspert-persona (sim), at: 2026-06-30T09:00:00Z }" in raw
|
|
|
|
entries = okf.read_provenance(path, "verified")
|
|
assert entries == ({"by": "ekspert-persona (sim)", "at": "2026-06-30T09:00:00Z"},)
|
|
assert okf.trust_tier(entries) == "machine-confirmed"
|
|
|
|
|
|
def test_a_human_prefixed_approver_reaches_the_human_tier(tmp_path: Path) -> None:
|
|
"""(b, second half) The ``human-reviewed`` tier is reached by a CALLER saying so."""
|
|
bundle_dir = _bundle_copy(tmp_path)
|
|
path = verdicts.promote_verdict(
|
|
bundle_dir,
|
|
_approved(bundle_dir),
|
|
approver="human:ktg",
|
|
experiment="exp-B",
|
|
timestamp="2026-06-30T09:00:00Z",
|
|
)
|
|
assert okf.trust_tier(okf.read_provenance(path, "verified")) == "human-reviewed"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("bad", "marker"),
|
|
[
|
|
("{ by: a, at: b }\n{ by: c, at: d }", "one line"),
|
|
("- { by: a, at: b }", "neither a flow sequence nor a flow mapping"),
|
|
("[ a.pdf ]", "bare scalar"),
|
|
],
|
|
)
|
|
def test_write_concept_file_refuses_a_verified_value_outside_the_subset(
|
|
tmp_path: Path, bad: str, marker: str
|
|
) -> None:
|
|
"""(c) Validation, never repair — and NOTHING is written.
|
|
|
|
``render_frontmatter`` collapses newlines for every key, which for ``verified`` would turn a
|
|
block value into a single line that decodes to something nobody wrote. The refusal is scoped
|
|
to ``verified`` BY NAME; every other key keeps the old collapsing behaviour, and that is stated
|
|
rather than hidden by the control arm below.
|
|
"""
|
|
with pytest.raises(okf.FlowDecodeError) as excinfo:
|
|
okf.write_concept_file(str(tmp_path), "c.md", {"type": "concept", "verified": bad}, "b\n")
|
|
# The marker pins that the writer's refusal IS the reader's refusal, not a second rule beside
|
|
# it — a hand-rolled copy here would refuse with words of its own and be free to drift.
|
|
assert marker in str(excinfo.value)
|
|
assert not (tmp_path / "c.md").exists(), "the refusal wrote a file anyway"
|
|
|
|
|
|
def test_the_refusal_is_scoped_to_verified_and_other_keys_are_untouched(tmp_path: Path) -> None:
|
|
"""CONTROL — a refusal that fired on every key would break `render_frontmatter`'s contract for
|
|
the whole repo, and a refusal that fired on nothing would be worthless. Both are excluded."""
|
|
path = okf.write_concept_file(
|
|
str(tmp_path),
|
|
"c.md",
|
|
{"type": "concept", "provenance": "line one\nline two", "verified": "{ by: p, at: t }"},
|
|
"body\n",
|
|
)
|
|
written = path.read_text(encoding="utf-8")
|
|
assert "provenance: line one line two" in written, "an unrelated key stopped being collapsed"
|
|
assert "verified: { by: p, at: t }" in written
|