llm-ingestion-okf/tests/test_okf_v0_2_profile.py
Kjell Tore Guttormsen 805a2d50d9 fix(okf-v0.2): generated.by is process:okf-ingest — the id commons decided
v0.5.0a1 shipped the one actor value the spec owner had already excluded.
Caught before any pilot was notified, so it costs a tag, not a migration.

Commons decided <fast id> = process:okf-ingest on 2026-07-31, on this repo's
own proposal, superseding option (d) (process:llm-ingestion-okf) chosen here on
07-27. The exclusion is ingest-spec.md:7-8, frozen on the spec being
framework-neutral: normalising OUR repo name into the normative id would force
every other conformant implementation to write it into its own output. Verified
against three independent sources before touching anything — commons' coord
message 20260731T154140Z, their plan :215-216/:244, and their STATE :37.

Why this had to land before the pilot notifications rather than after:

  actor is both the stamp written and the value owned back (OwnershipPolicy),
  and recognition is one-way. A pilot that had run Test A against the excluded
  id would hold bundles this library stops recognising the moment the id is
  corrected — collision_unstamped on their OWN files. That is the A-E5 failure
  mode, and we would have inflicted it.

Worse, it would not have shown up as a failure: the plan's A-E3 expectation
(:854) named the same excluded value as the code, so Test A would have PASSED
and confirmed the error. Expectation and implementation agreeing is not
evidence when both predate the decision.

Nothing in the wild carried the old value: OKF_V0_2 did not exist at v0.4.0, so
the profile has never been released. v0.5.0a1 is abandoned, not moved — a tag
already on a public remote does not get force-pushed, and the history should
say plainly that a1 was wrong.

A-E3 now records both corrections with dates. The V1 paragraph at :1169 is
superseded in place rather than rewritten: its reasoning still holds, only its
outcome moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVTup4v7tW9QiVyBENk2LV
2026-07-31 20:50:21 +02:00

341 lines
13 KiB
Python

"""D2 — the `OKF_V0_2` profile, profile-aware ownership, `sources` derivation.
Three things, in the order the risk runs:
1. **The profile constant.** Names the §5 families, requires only `type` (§4:
"the only always-required key"), and closes nothing — §14 forbids a consumer
to reject on unknown types or unknown keys, so an allowlist here would put
the profile in violation of the spec it is named after.
2. **Ownership.** `_is_ingest_owned` is the pre-mutation collision gate, so it
is the one piece of this work whose defects are expensive and quiet. The v0.2
stamp is a flow mapping carrying `ingested_at`, so equality is impossible by
construction and the predicate is a prefix test. The operator decided the
direction: `OKF_V0_2` accepts BOTH stamps, so a bundle written under `DEFAULT`
re-runs IN PLACE; `DEFAULT` does not accept the v0.2 form, so the recognition
is one-way and the fail-safe from V-A3 is preserved.
3. **`sources`.** Derived from the manifest's source as an inline flow sequence
(requirement 1's measured form), with the locator taken verbatim per source
type: `root`, `connection_ref`, `base_url`. `credential_ref` is never a
locator and never reaches frontmatter.
`generated.by` is `process:okf-ingest` — decided by commons 2026-07-31 on this
repo's own proposal, superseding option (d) (`process:llm-ingestion-okf`), which
was chosen here 2026-07-27 and is now explicitly excluded. The exclusion is
`ingest-spec.md:7-8`, frozen on the spec being framework-neutral: normalising
*our* repo name would force every other conformant implementation to write it
into its own output. The value carries no version either way, which is what
keeps a byte-compared fixture stable across releases.
"""
from __future__ import annotations
import re
from pathlib import Path
import pytest
from llm_ingestion_okf.errors import MaterializationError
from llm_ingestion_okf.manifest import (
Extraction,
FileSource,
HttpSource,
Manifest,
Source,
SqlSource,
)
from llm_ingestion_okf.materialize import _is_ingest_owned, _render_concept_file
from llm_ingestion_okf.profiles import DEFAULT, OKF_LATEST, OKF_V0_2, STRICT_V1, BundleProfile
INGESTED_AT = "2026-07-25T12:00:00Z"
STAMP = "manifest@0123456789abcdef"
V0_2_STAMP = f"{{ by: process:okf-ingest, at: {INGESTED_AT} }}"
def _manifest(source: Source) -> Manifest:
return Manifest(
manifest_version=1,
source=source,
bundle_summary="A test bundle.",
extractions=(
Extraction(
id="orders", title="Orders", query="orders.csv", okf_type="dataset", max_rows=10
),
),
)
def _concept(source: Source, *, profile: BundleProfile = OKF_V0_2) -> str:
manifest = _manifest(source)
return _render_concept_file(
manifest,
manifest.extractions[0],
"Body.\n",
ingested_at=INGESTED_AT,
stamp=STAMP,
profile=profile,
)
def _stamped_concept(generated: str) -> bytes:
return (
"---\ntype: dataset\ntitle: Orders\n"
f"ingest_manifest: {STAMP}\n"
f"generated: {generated}\n---\n\nBody.\n"
).encode()
# --- the profile constant -------------------------------------------------
def test_the_v0_2_profile_names_the_five_families_it_must_express() -> None:
"""C-E3's list, as emission order rather than as prose. Naming a key is what
lets a caller emit it in canonical order; judging a structured value is D1's
reader and deliberately absent here.
"""
order = OKF_V0_2.frontmatter.order
for key in ("generated", "sources", "verified", "status", "stale_after"):
assert key in order
assert "timestamp" not in order # §13.1: the fallback is for v0.1 documents
assert order.index("generated") < order.index("sources")
def test_the_v0_2_profile_requires_only_type() -> None:
"""§4/§11: `type` is the only always-required key, and a concept carrying
just `type` is fully conformant."""
assert OKF_V0_2.frontmatter.required == frozenset({"type"})
def test_the_v0_2_profile_closes_neither_the_type_set_nor_the_key_namespace() -> None:
"""§14's MUST NOTs, expressed as configuration: a consumer must not reject
on an unknown `type` value or on unknown additional keys. A profile that
closed either would be non-conformant against the version it is named for.
"""
assert OKF_V0_2.types.allowed is None
assert OKF_V0_2.frontmatter.allowed is None
assert OKF_V0_2.frontmatter.key_pattern is None
def test_the_v0_2_profile_keeps_the_source_query_collapse_and_the_default_paths() -> None:
"""The parts that are commons' §5 rather than upstream's: the doors write
the same filenames whatever the OKF version, and a multi-line SELECT still
renders on one line."""
assert OKF_V0_2.frontmatter.collapsed_keys == frozenset({"source_query"})
assert OKF_V0_2.paths == DEFAULT.paths
def test_okf_latest_still_means_v0_1_until_ga() -> None:
"""`OKF_LATEST` is "the latest version supported as STABLE", not the latest
present in the tree. Flipping it is the GA event — a single auditable
action rather than a side effect of this merge — so during the pilot a
consumer bound to the alias keeps v0.1 semantics.
"""
assert OKF_LATEST is DEFAULT
# --- ownership ------------------------------------------------------------
def test_the_default_profile_still_stamps_the_v0_1_literal() -> None:
assert DEFAULT.ownership.stamp(INGESTED_AT) == "true"
def test_the_v0_2_stamp_is_a_flow_mapping_whose_at_is_the_argument_exactly() -> None:
"""A-E3, corrected: the actor is the §7 `process:<id>` form and carries no
version. `at` binds to the already-validated `ingested_at` argument, so no
wall-clock is introduced and two runs with the same argument agree.
"""
assert OKF_V0_2.ownership.stamp(INGESTED_AT) == V0_2_STAMP
assert "llm-ingestion-okf/" not in OKF_V0_2.ownership.stamp(INGESTED_AT)
def test_the_v0_2_predicate_accepts_the_stamp_its_own_profile_writes(tmp_path: Path) -> None:
"""A-E5's second run: a v0.2 bundle re-runs into the directory it wrote."""
path = tmp_path / "ingest-orders.md"
path.write_bytes(_stamped_concept(V0_2_STAMP))
assert _is_ingest_owned(path, "manifest", profile=OKF_V0_2) is True
def test_the_v0_2_predicate_also_accepts_the_v0_1_stamp(tmp_path: Path) -> None:
"""The operator's decision (2026-07-27): a `DEFAULT`-written bundle is
re-runnable IN PLACE under `OKF_V0_2`. Without this the profile switch would
fire `collision_unstamped` on the library's own previous output, and the
black-box promise — an upstream release costs a consumer a re-run, nothing
more — would not hold across the profile change.
"""
path = tmp_path / "ingest-orders.md"
path.write_bytes(_stamped_concept("true"))
assert _is_ingest_owned(path, "manifest", profile=OKF_V0_2) is True
def test_the_default_predicate_still_refuses_the_v0_2_stamp(tmp_path: Path) -> None:
"""Recognition is one-way, which is what preserves V-A3. `DEFAULT` meeting a
v0.2 file refuses the run rather than replacing the file."""
path = tmp_path / "ingest-orders.md"
path.write_bytes(_stamped_concept(V0_2_STAMP))
assert _is_ingest_owned(path, "manifest") is False
assert _is_ingest_owned(path, "manifest", profile=DEFAULT) is False
def test_a_foreign_v0_2_actor_is_never_ingest_owned(tmp_path: Path) -> None:
"""U3 as a test: `generated` is attribution, and upstream writes it for
HAND-AUTHORED files. Presence proves nothing — the actor has to be ours, or
the collision gate would claim a human's file that merely carries the key.
"""
path = tmp_path / "ingest-orders.md"
path.write_bytes(_stamped_concept(f"{{ by: human:jsmith@acme, at: {INGESTED_AT} }}"))
assert _is_ingest_owned(path, "manifest", profile=OKF_V0_2) is False
def test_the_v0_2_stamp_still_has_to_name_this_manifest(tmp_path: Path) -> None:
"""§10.2 per-manifest ownership is unchanged by the profile: a sibling
manifest sharing the bundle keeps its own files."""
path = tmp_path / "ingest-orders.md"
path.write_bytes(_stamped_concept(V0_2_STAMP))
assert _is_ingest_owned(path, "other-manifest", profile=OKF_V0_2) is False
# --- `sources` derivation -------------------------------------------------
SOURCE_CASES = [
pytest.param(FileSource(id="golden-catalogue", root="fixture"), "fixture", id="file"),
pytest.param(
SqlSource(id="golden-db", connection_ref="OKF_GOLDEN_SQL_DB"),
"OKF_GOLDEN_SQL_DB",
id="sql",
),
pytest.param(
HttpSource(id="golden-api", base_url="https://golden.example.test"),
"https://golden.example.test",
id="http",
),
]
@pytest.mark.parametrize("source,resource", SOURCE_CASES)
def test_sources_is_an_inline_flow_sequence_carrying_the_locator(
source: Source, resource: str
) -> None:
"""A-E4. One entry, `id` from the manifest source and `resource` its locator
verbatim — the filesystem root, the env-var NAME holding the DSN, or the base
URL. Upstream's `resource` is a bundle-internal path; ours is not, and
inventing the `author`/`last_modified` upstream also carries would be writing
fields with no reader.
"""
line = f"sources: [{{ id: {source.id}, resource: {resource} }}]"
assert line in _concept(source).splitlines()
def test_sources_follows_generated_in_the_emitted_block() -> None:
"""The order the schema declares is the order that reaches disk."""
lines = _concept(FileSource(id="golden-catalogue", root="fixture")).splitlines()
assert [line.split(":")[0] for line in lines[1:9]] == [
"type",
"title",
"source_system",
"source_query",
"ingested_at",
"ingest_manifest",
"generated",
"sources",
]
def test_the_default_profile_emits_neither_sources_nor_a_v0_2_generated() -> None:
"""V-A6 at the unit level: the seven-key v0.1 block, byte for byte. The
golden suite proves the same thing end to end; this one localizes a failure
to the renderer rather than to a fixture diff.
"""
rendered = _concept(FileSource(id="golden-catalogue", root="fixture"), profile=DEFAULT)
assert rendered == (
"---\n"
"type: dataset\n"
"title: Orders\n"
"source_system: golden-catalogue\n"
"source_query: orders.csv\n"
f"ingested_at: {INGESTED_AT}\n"
f"ingest_manifest: {STAMP}\n"
"generated: true\n"
"---\n"
"\n"
"Body.\n"
)
def test_the_http_credential_reference_never_reaches_the_frontmatter() -> None:
"""A credential reference is not a locator. The convention forbids the
credential itself in frontmatter; the env-var NAME holding it has no reader
in a bundle either, so it is not written.
"""
source = HttpSource(
id="golden-api",
base_url="https://golden.example.test",
credential_ref="OKF_GOLDEN_API_TOKEN",
)
assert "OKF_GOLDEN_API_TOKEN" not in _concept(source)
@pytest.mark.parametrize(
"root",
["data, backup", "data{1}", "data[1]", "key: value"],
ids=["comma", "braces", "brackets", "colon-space"],
)
def test_a_locator_that_would_restructure_the_flow_mapping_is_refused(root: str) -> None:
"""Fail-fast rather than emit a silently WRONG provenance record.
These characters terminate or restructure a YAML flow mapping, and the
failure is quiet rather than loud. Measured with PyYAML 2026-07-27 rather
than argued: `[{ id: a, resource: data, backup }]` raises nothing and yields
`[{'id': 'a', 'resource': 'data', 'backup': None}]` — a clean parse into a
record no one wrote. Refusing is the same posture as the filename-length
gate: validation, not repair.
The same run confirms what IS emitted parses as intended — `generated` to a
two-key mapping, `sources` to a one-element list of one — including a base
URL, whose colons stay inside a plain scalar in flow context.
"""
with pytest.raises(MaterializationError) as excinfo:
_concept(FileSource(id="golden-catalogue", root=root))
assert excinfo.value.code == "source_reference_unquotable"
def test_the_default_profile_is_unaffected_by_an_unquotable_locator() -> None:
"""The refusal is a property of the v0.2 emission, not a new gate on v0.1:
`DEFAULT` never writes the locator, so it has nothing to refuse. A shared
refusal would have broken additivity — a manifest that ran yesterday must
still run."""
rendered = _concept(FileSource(id="golden-catalogue", root="data, backup"), profile=DEFAULT)
assert "data, backup" not in rendered
# --- V-A5 extended to the new profile -------------------------------------
_VERSION_LITERAL_RE = re.compile(r"^\d+(\.\d+)+$")
@pytest.mark.parametrize(
"profile", [DEFAULT, STRICT_V1, OKF_V0_2], ids=["DEFAULT", "STRICT_V1", "OKF_V0_2"]
)
def test_no_profile_hard_codes_an_upstream_version(profile: BundleProfile) -> None:
"""V-A5, restated over the profile the name of which is the one place a
version literal would look natural. `OKF_V0_2` is a NAME; the value belongs
to catalog (E1) and is declared at D5, in the fixture, once.
Kept alongside the characterization suite's copy rather than replacing it:
that one pins the two shipped profiles at the point they were measured.
"""
from test_okf_v0_2_characterization import _strings_in
assert sorted({s for s in _strings_in(profile) if _VERSION_LITERAL_RE.match(s)}) == []