Commons ratified V1 2026-08-02 and executed it at `54e0ec7`; verified
against their tree rather than taken on report. ingest-spec.md:217 now
defines `generated` as `{ by: process:okf-ingest, at: <ingested_at> }`,
unquoted, `at` repeating `ingested_at` verbatim. `generated: true` no
longer appears in the spec.
`DEFAULT` states commons' §5 layer, so its stamp is theirs to decide.
`DEFAULT.ownership` gains the actor; the four goldens this repo's plan
named in advance were regenerated by RUNNING the materializer, each on
its own case's `ingested-at.txt`. The v0.2 golden was untouched, as
predicted -- it has carried the O2 form since D5.
Not a migration onto OKF v0.2: `DEFAULT` stays v0.1 on every axis
upstream owns and still emits no `sources`. Commons' spec and the Google
version are independent axes, and comments that narrated them as one
were rewritten rather than left to mislead. README and CLAUDE.md said
the additive rule without that boundary, which would have told a
consumer their DEFAULT bytes can never move; both now state it.
V-A3 is amended, not dropped. `DEFAULT` must OWN the mapping it now
writes -- a profile refusing its own output fires the collision gate on
files its own previous run wrote -- while a mapping naming a foreign
actor, or §7's `human:` actor on curated content, stays unowned. That
half is what carried the safety and it is asserted directly.
§11's stamp-integrity condition moved with the value: the forgeable
stamp was `true` and is now the mapping naming the ingest actor. The
defence was never the value -- the §3 scan globs `ingest-*.md`, so a
Door C import is unreachable however well it forges. Second spoof test
added; both were hand-mutated (glob widened to `*.md`) to confirm they
can fail.
The characterization test derived its foreign-stamp fixture from the
literal `generated: true`, which V1 leaves without a referent -- a
silent no-op waiting to happen. It now derives the needle from the
profile and asserts the substitution occurred.
Door B is deliberately untouched: not the ingest-spec's, marker is
`generated` + `source_file`, disjoint from Door A's `ingest_manifest`,
and the divergence predates V1.
Nothing released or notified. The pilot set pins `v0.5.0a2`, not `main`,
so this is invisible to portfolio-optimiser's freeze and demo; the
consumer exposure report is owed at the release that carries this.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwcjUXbKySLbEG5WqTNkta
230 lines
10 KiB
Python
230 lines
10 KiB
Python
"""Requirement 6 — `profile` threaded through `materialize_bundle`'s disk phase.
|
|
|
|
`OKF_V0_2` landed in D2, but nothing outside the library could ask for it: the
|
|
content phase already accepted `profile`, while `materialize_bundle` neither
|
|
took one nor passed one down. The parameter is keyword-only behind the `*` the
|
|
signature already carried, so every three-positional call site stays
|
|
source-compatible — which is what the consumer who asked for it needs.
|
|
|
|
Two of these tests would be untestable by construction if written only against
|
|
the shipped profiles: `OKF_V0_2.paths is DEFAULT.paths` and
|
|
`.index is DEFAULT.index`, so no assertion can tell `profile.index.name` apart
|
|
from `DEFAULT.index.name` when they are the same object. That identity is what
|
|
makes the change byte-neutral, and it is also what would let six of the nine
|
|
threading sites stay hard-coded with every shipped-profile test still green.
|
|
`_SYNTHETIC` exists to close exactly that gap: it renames the index and the
|
|
concept files, so a site still reading `DEFAULT` writes to the wrong path and
|
|
fails. It is a test instrument, never a supported profile.
|
|
|
|
What this does NOT claim: the materializer honours `profile.types` (validation
|
|
still runs against `DEFAULT` in `manifest.py`; both policies compare equal
|
|
today, so nothing is hidden — but the parameter does not reach it), nor
|
|
`IndexPolicy`'s judging fields (`per_directory`, `entries_match_directory`,
|
|
`root_frontmatter`). `STRICT_V1` sets all three, so passing it here produces a
|
|
bundle that does not meet its own profile. Supported here: `DEFAULT` and
|
|
`OKF_V0_2`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import replace
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from llm_ingestion_okf.materialize import materialize_bundle
|
|
from llm_ingestion_okf.profiles import DEFAULT, OKF_V0_2, BundleProfile
|
|
|
|
INGESTED_AT = "2026-07-16T12:00:00Z"
|
|
|
|
# Renames both halves the disk phase reads: a threading site left on `DEFAULT`
|
|
# writes `ingest-orders.md` / `index.md` instead, and the assertions below say
|
|
# so by name rather than by a byte diff that cannot point at the cause.
|
|
_SYNTHETIC = replace(
|
|
OKF_V0_2,
|
|
paths=replace(DEFAULT.paths, ingest_prefix="x-", concept_suffix=".mdx"),
|
|
index=replace(DEFAULT.index, name="contents.md"),
|
|
)
|
|
|
|
|
|
def _manifest_data() -> dict[str, Any]:
|
|
return {
|
|
"manifest_version": 1,
|
|
"source": {"type": "file", "id": "catalogue-1", "root": "data"},
|
|
"bundle_summary": "A test bundle.",
|
|
"extractions": [
|
|
{
|
|
"id": "orders",
|
|
"title": "Orders",
|
|
"query": "orders.csv",
|
|
"okf_type": "dataset",
|
|
"max_rows": 100,
|
|
}
|
|
],
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def file_setup(tmp_path: Path) -> tuple[Path, Path]:
|
|
"""A manifest + CSV catalogue in tmp/src, and an empty bundle target path."""
|
|
src = tmp_path / "src"
|
|
src.mkdir(parents=True)
|
|
manifest_path = src / "manifest.json"
|
|
manifest_path.write_text(json.dumps(_manifest_data()), encoding="utf-8")
|
|
(src / "data").mkdir()
|
|
(src / "data" / "orders.csv").write_text("a,b\n1,x\n2,y\n", encoding="utf-8", newline="")
|
|
return manifest_path, tmp_path / "bundle"
|
|
|
|
|
|
# --- the profile reaches the content phase ---
|
|
|
|
|
|
def test_v0_2_profile_reaches_the_emitted_frontmatter(file_setup: tuple[Path, Path]) -> None:
|
|
"""`_render_concept_file` took `profile` since D2; the call site did not pass
|
|
one, so `OKF_V0_2` was unreachable through the only public door.
|
|
"""
|
|
manifest_path, bundle = file_setup
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=OKF_V0_2)
|
|
text = (bundle / "ingest-orders.md").read_text(encoding="utf-8")
|
|
assert f"generated: {{ by: process:okf-ingest, at: {INGESTED_AT} }}" in text
|
|
assert "sources: [{ id: catalogue-1, resource: data }]" in text
|
|
assert "generated: true" not in text
|
|
|
|
|
|
def test_default_run_is_unchanged_by_the_new_parameter(file_setup: tuple[Path, Path]) -> None:
|
|
"""Byte-neutrality at the signature: passing `DEFAULT` explicitly and passing
|
|
nothing must produce the same bundle, or the default is not the default.
|
|
"""
|
|
manifest_path, bundle = file_setup
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT)
|
|
implicit = (bundle / "ingest-orders.md").read_bytes()
|
|
index_implicit = (bundle / "index.md").read_bytes()
|
|
|
|
explicit_dir = bundle.parent / "bundle-explicit"
|
|
materialize_bundle(manifest_path, explicit_dir, INGESTED_AT, profile=DEFAULT)
|
|
assert (explicit_dir / "ingest-orders.md").read_bytes() == implicit
|
|
assert (explicit_dir / "index.md").read_bytes() == index_implicit
|
|
# Not redundant with the equality above: it pins WHICH profile the implicit
|
|
# run used. Without it the two calls could agree while both being wrong.
|
|
assert f"generated: {{ by: process:okf-ingest, at: {INGESTED_AT} }}".encode() in implicit
|
|
|
|
|
|
def test_profile_is_keyword_only(file_setup: tuple[Path, Path]) -> None:
|
|
"""The parameter sits behind the `*` the signature already had. A positional
|
|
fourth argument must not be accepted, or a later reordering would silently
|
|
rebind existing call sites.
|
|
"""
|
|
manifest_path, bundle = file_setup
|
|
with pytest.raises(TypeError):
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, DEFAULT) # type: ignore[misc]
|
|
|
|
|
|
# --- A-E5: two runs into the SAME directory ---
|
|
|
|
|
|
def test_second_v0_2_run_into_the_same_directory_succeeds(
|
|
file_setup: tuple[Path, Path],
|
|
) -> None:
|
|
"""A-E5, end to end. The emitter and the ownership predicate are coupled
|
|
through the stamp value; `OKF_V0_2` changes that value. If the predicate is
|
|
not given the same profile the emitter used, the library stops recognising
|
|
its own output and the §3 collision gate fires `collision_unstamped` on the
|
|
files the previous run wrote. One run compared byte for byte cannot see it.
|
|
"""
|
|
manifest_path, bundle = file_setup
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=OKF_V0_2)
|
|
first = (bundle / "ingest-orders.md").read_bytes()
|
|
first_index = (bundle / "index.md").read_bytes()
|
|
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=OKF_V0_2)
|
|
|
|
assert (bundle / "ingest-orders.md").read_bytes() == first
|
|
assert (bundle / "index.md").read_bytes() == first_index
|
|
|
|
|
|
# --- the paths/index half of the profile reaches disk ---
|
|
|
|
|
|
def test_profile_paths_and_index_name_reach_disk(file_setup: tuple[Path, Path]) -> None:
|
|
"""Six of the nine threading sites read `paths`/`index`, which are the same
|
|
objects on every shipped profile. `_SYNTHETIC` renames both so the sites are
|
|
provable rather than merely regression-covered.
|
|
"""
|
|
manifest_path, bundle = file_setup
|
|
result = materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=_SYNTHETIC)
|
|
|
|
assert (bundle / "x-orders.mdx").is_file()
|
|
assert not (bundle / "ingest-orders.md").exists()
|
|
assert (bundle / "contents.md").is_file()
|
|
assert not (bundle / "index.md").exists()
|
|
assert [path.name for path in result.written] == ["x-orders.mdx"]
|
|
assert "- [Orders](x-orders.mdx)" in (bundle / "contents.md").read_text(encoding="utf-8")
|
|
|
|
|
|
def test_second_synthetic_run_replaces_rather_than_colliding(
|
|
file_setup: tuple[Path, Path],
|
|
) -> None:
|
|
"""The §3 ownership scan globs `{ingest_prefix}*{concept_suffix}` and
|
|
excludes `index.name`. Left on `DEFAULT`, the glob finds nothing under a
|
|
renamed prefix and suffix, `owned` is empty, and the gate refuses to
|
|
overwrite the file this same code just wrote. The index must also not
|
|
gain a second link for the same target.
|
|
"""
|
|
manifest_path, bundle = file_setup
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=_SYNTHETIC)
|
|
first = (bundle / "x-orders.mdx").read_bytes()
|
|
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=_SYNTHETIC)
|
|
|
|
assert (bundle / "x-orders.mdx").read_bytes() == first
|
|
index_text = (bundle / "contents.md").read_text(encoding="utf-8")
|
|
assert index_text.count("- [Orders](x-orders.mdx)") == 1
|
|
|
|
|
|
def test_removed_extraction_is_unlinked_under_a_renamed_index(
|
|
file_setup: tuple[Path, Path], tmp_path: Path
|
|
) -> None:
|
|
"""`_update_index_lines` runs only on an EXISTING index, so it is reached
|
|
solely by a second run — and only its `link_pattern`/`render_link` are
|
|
profile-owned. Dropping an extraction is what forces the removal branch.
|
|
"""
|
|
manifest_path, bundle = file_setup
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=_SYNTHETIC)
|
|
|
|
data = _manifest_data()
|
|
data["extractions"][0]["id"] = "invoices"
|
|
data["extractions"][0]["title"] = "Invoices"
|
|
manifest_path.write_text(json.dumps(data), encoding="utf-8")
|
|
(tmp_path / "src" / "data" / "invoices.csv").write_text(
|
|
"a,b\n1,x\n", encoding="utf-8", newline=""
|
|
)
|
|
|
|
materialize_bundle(manifest_path, bundle, INGESTED_AT, profile=_SYNTHETIC)
|
|
|
|
index_text = (bundle / "contents.md").read_text(encoding="utf-8")
|
|
assert "- [Invoices](x-invoices.mdx)" in index_text
|
|
assert "x-orders.mdx" not in index_text
|
|
assert not (bundle / "x-orders.mdx").exists()
|
|
|
|
|
|
def test_synthetic_profile_is_not_a_shipped_profile() -> None:
|
|
"""Guards the instrument: if a future profile ever shares these overrides,
|
|
the two tests above stop proving anything and would go quietly green.
|
|
"""
|
|
assert isinstance(_SYNTHETIC, BundleProfile)
|
|
assert _SYNTHETIC.paths is not DEFAULT.paths
|
|
assert _SYNTHETIC.index is not DEFAULT.index
|
|
assert OKF_V0_2.paths is DEFAULT.paths
|
|
# `OKF_V0_2.index` stopped being DEFAULT's object at D5, which named
|
|
# `okf_version` in its `root_frontmatter`. That is NOT the distinctness this
|
|
# guard is about: every field that carries a NAME — the index filename and
|
|
# the concept filenames — is still shared, so a threading bug that passed
|
|
# the wrong profile would still emit identical bytes under either. Object
|
|
# identity was only ever a proxy for that; assert the thing itself, or this
|
|
# guard would go green on a change that leaves the instrument blind.
|
|
assert OKF_V0_2.index.name == DEFAULT.index.name
|
|
assert OKF_V0_2.index.link_template == DEFAULT.index.link_template
|
|
assert _SYNTHETIC.index.name != DEFAULT.index.name
|
|
assert _SYNTHETIC.paths.concept_suffix != DEFAULT.paths.concept_suffix
|