llm-ingestion-okf/tests/test_index_policy.py
Kjell Tore Guttormsen 2c01e58259 fix(profiles): P1-F1 — a permitted root key is not a required one
`IndexPolicy.root_frontmatter` carried two claims at once: which keys the
root index may carry and in what order, and which it must carry. The judge
read it as the second, so `OKF_V0_2` — which names `okf_version` only to fix
its position, upstream granting it as a MAY (§8:509-510, §12:773-775) —
reported every bundle exercising that MAY as violating. Measured in P1 over
17 bundles: 14 failed with exactly this one violation, upstream's own four
reference bundles among them, while D5's emitter treated the same key as
optional. We emitted a MAY correctly and graded it a MUST.

`root_frontmatter` now permits and orders; `root_frontmatter_required`
demands. A required key outside the ordered set fails at construction, the
same contradiction `FrontmatterSchema` already refuses when `required`
strays outside `allowed`.

- `OKF_V0_2` requires none — upstream's MAY, stated on the judge side too.
- `STRICT_V1` requires all three: the proving consumer's root index carries
  exactly those keys in that order on every bundle measured (`c5141f8`), so
  separating the meanings costs them nothing.
- `DEFAULT` names no root keys and is untouched.

Re-measured over the eight bundle-root indexes reachable locally (our four
goldens, upstream's four): 7 of 8 failing under the old semantics, 0 of 8
after. Emit path byte-identical — the golden suite fails otherwise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqCmfJ2ukpFXeFjfab8wvy
2026-07-31 18:16:13 +02:00

333 lines
15 KiB
Python

"""The index policy as configuration, and the reader that judges it (Phase 3).
Three index shapes have to be expressible without either becoming the default:
- **DEFAULT** — the ingest-spec shape. One index at the bundle root, entries
`- [label](target)`, curated prose alongside them, and no relationship
between the index and the directory it sits in.
- **STRICT_V1** — `claude-code-llm-wiki`, read out of their code rather than
recalled: an index at every level (`bundle.py:527-567`), exactly one `# `
heading, entries `* [Title](link) - description` and nothing else, the root
index carrying `okf_version`/`bundle_profile`/`okf_spec_commit` while nested
indexes carry no frontmatter at all, and gate `BUNDLE_INDEX_COMPLETE`
(`validate.py:1081-1120`, severity ERROR) demanding the index and the
directory match exactly in both directions.
- **The second-brain shape** — not built here, but it must not be excluded.
The catalog reported on 2026-07-25 that their index is an *authored* count
of a directory's children and never a filesystem lookup, that a reader or
validator enumerating the directory has implemented the wrong contract, and
that they require prose in the index but no per-entry description.
The two consumers are directly opposed on exactly one point: the wiki mandates
the directory-derived match the catalog calls the wrong contract. That is what
settles the design. Every one of these differences is a policy field, none is
a library invariant, and `violations` never touches the filesystem — a caller
supplies the listing, and a profile whose index is authored refuses to be
handed one at all.
"""
from __future__ import annotations
import re
import pytest
from llm_ingestion_okf.profiles import DEFAULT, OKF_V0_2, STRICT_V1, IndexPolicy
# --- DEFAULT keeps the Phase 1/2 shape (assumption C1) --------------------
def test_default_index_policy_judges_nothing() -> None:
"""Every new field is off under DEFAULT, so the reader has nothing to say.
Door A emits an index with a curated summary above managed links, Door B
and Door C append to one that may be empty. A judging default would
declare invalid the bundles this library itself produces — the same
reasoning that kept a required key set off DEFAULT's frontmatter schema.
"""
text = "# Sales bundle\n\nCurated prose the operator wrote.\n\n- [Sales](ingest-sales.md)\n"
assert DEFAULT.index.violations(text) == ()
assert DEFAULT.index.violations(text, is_root=True) == ()
def test_default_index_is_root_only_because_upstream_binds_it_there() -> None:
"""Upstream OKF binds `index.md` to the bundle root alone.
A nested directory without its own index is not an error there, which is
why per-directory indexing is policy rather than an OKF rule: a DEFAULT
demanding one per level would declare upstream-conforming bundles invalid.
"""
assert DEFAULT.index.per_directory is False
assert DEFAULT.index.required_indexes(["", "concepts", "concepts/hooks"]) == ("index.md",)
def test_default_index_link_is_unchanged_and_needs_no_description() -> None:
assert DEFAULT.index.requires_description is False
assert DEFAULT.index.render_link("Sales", "ingest-sales.md") == "- [Sales](ingest-sales.md)"
# --- the description on an entry ------------------------------------------
def test_render_link_carries_a_description_when_the_template_names_one() -> None:
assert STRICT_V1.index.requires_description is True
line = STRICT_V1.index.render_link("Hooks", "hooks.md", "How hooks fire.")
assert line == "* [Hooks](hooks.md) - How hooks fire."
def test_render_link_refuses_to_emit_a_half_written_entry() -> None:
"""A template naming `{description}` and a caller omitting it must not
silently render `* [T](t) - `, which parses as nothing and reads as an
entry."""
with pytest.raises(ValueError, match="description"):
STRICT_V1.index.render_link("Hooks", "hooks.md")
def test_a_description_offered_to_a_template_without_one_is_refused() -> None:
"""The mirror case: silently dropping it would lose operator bytes."""
with pytest.raises(ValueError, match="description"):
DEFAULT.index.render_link("Sales", "ingest-sales.md", "dropped on the floor")
def test_strict_v1_link_template_and_pattern_describe_the_same_shape() -> None:
"""Rendering and recognising are separate operations; this is the pair's
round-trip, the same test DEFAULT's shape already carries."""
for label, target, description in (
("Hooks", "hooks.md", "How hooks fire."),
("Concepts", "concepts/index.md", "Machine-generated index of concepts."),
("", "a.md", "-"),
):
line = STRICT_V1.index.render_link(label, target, description)
match = STRICT_V1.index.link_pattern.match(line)
assert match is not None
assert match.group("label") == label
assert match.group("target") == target
assert match.group("description") == description
def test_strict_v1_link_pattern_is_anchored_to_the_whole_line() -> None:
assert STRICT_V1.index.link_pattern.match("see * [Hooks](hooks.md) - x below") is None
# --- per-directory indexes ------------------------------------------------
def test_per_directory_policy_names_one_index_per_level() -> None:
"""The caller supplies the directories; the policy never enumerates them.
This is the catalog's construction rule honoured at the type level: the
library cannot list a directory because it is never given one to list.
"""
assert STRICT_V1.index.per_directory is True
assert STRICT_V1.index.required_indexes(["", "concepts", "concepts/hooks"]) == (
"concepts/hooks/index.md",
"concepts/index.md",
"index.md",
)
# --- the reader: headings -------------------------------------------------
ROOT_HEAD = "---\nokf_version: 0.1\nbundle_profile: strict-v1\nokf_spec_commit: d44368c\n---\n"
def test_strict_index_wants_exactly_one_h1() -> None:
body = "\n* [Hooks](hooks.md) - How hooks fire.\n"
missing = STRICT_V1.index.violations(
ROOT_HEAD + body, is_root=True, expected_targets={"hooks.md"}
)
assert [violation.code for violation in missing] == ["index_heading_missing"]
two = ROOT_HEAD + "\n# Wiki\n\n# Again\n" + body
extra = STRICT_V1.index.violations(two, is_root=True, expected_targets={"hooks.md"})
assert [violation.code for violation in extra] == ["index_heading_extra"]
assert extra[0].subject == "# Again"
def test_a_second_level_heading_is_prose_not_a_heading() -> None:
"""Only `# ` is the heading; `## ` is body text the strict shape forbids."""
text = ROOT_HEAD + "\n# Wiki\n\n## Section\n"
codes = [v.code for v in STRICT_V1.index.violations(text, is_root=True, expected_targets=set())]
assert codes == ["index_prose_not_allowed"]
# --- the reader: prose ----------------------------------------------------
def test_strict_index_admits_nothing_but_the_heading_and_the_entries() -> None:
text = ROOT_HEAD + "\n# Wiki\n\nA sentence the generator would never write.\n"
found = STRICT_V1.index.violations(text, is_root=True, expected_targets=set())
assert [v.code for v in found] == ["index_prose_not_allowed"]
assert found[0].subject == "A sentence the generator would never write."
def test_prose_is_the_catalog_shape_and_stays_expressible() -> None:
"""Their index requires progressive-disclosure prose and no per-entry
description — the opposite of the wiki on both counts, and both have to be
constructible."""
second_brain = IndexPolicy(
name="index.md",
link_template="- [{label}]({target})",
link_pattern=DEFAULT.index.link_pattern,
per_directory=True,
heading_required=True,
allows_prose=True,
root_frontmatter=("okf_version", "okf_layout"),
)
text = "# Notes\n\nWhat lives here and why.\n\n- [A note](a.md)\n"
assert second_brain.violations(text) == ()
assert second_brain.entries_match_directory is False
# --- the reader: directory match, both directions -------------------------
def test_the_match_is_bidirectional_and_names_each_side() -> None:
text = ROOT_HEAD + "\n# Wiki\n\n* [Hooks](hooks.md) - How hooks fire.\n"
found = STRICT_V1.index.violations(
text, is_root=True, expected_targets={"skills.md", "concepts/index.md"}
)
assert [(v.subject, v.code) for v in found] == [
("concepts/index.md", "index_entry_missing"),
("hooks.md", "index_entry_unexpected"),
("skills.md", "index_entry_missing"),
]
def test_a_derived_index_cannot_be_judged_without_the_listing() -> None:
"""Skipping the check silently is the failure mode that matters: a gate
that quietly passes when its input is absent is worse than one that
raises."""
text = ROOT_HEAD + "\n# Wiki\n"
with pytest.raises(ValueError, match="listing"):
STRICT_V1.index.violations(text, is_root=True)
def test_an_authored_index_refuses_a_listing() -> None:
"""The catalog's rule, enforced rather than documented: a profile whose
index is authored cannot be asked to check it against the directory, so
code written to the wrong contract fails at the call instead of passing
every test anyone would think to write."""
with pytest.raises(ValueError, match="authored"):
DEFAULT.index.violations("- [Sales](ingest-sales.md)\n", expected_targets={"a.md"})
# --- the reader: root vs nested frontmatter -------------------------------
def test_the_root_index_pins_its_keys_in_order() -> None:
text = ROOT_HEAD + "\n# Wiki\n"
assert STRICT_V1.index.violations(text, is_root=True, expected_targets=set()) == ()
def test_a_root_index_missing_a_pinned_key_is_reported_per_key() -> None:
text = "---\nokf_version: 0.1\n---\n\n# Wiki\n"
found = STRICT_V1.index.violations(text, is_root=True, expected_targets=set())
assert [(v.subject, v.code) for v in found] == [
("bundle_profile", "index_root_key_missing"),
("okf_spec_commit", "index_root_key_missing"),
]
def test_the_pinned_keys_are_ordered_not_merely_present() -> None:
"""Their `ROOT_INDEX_KEY_ORDER` is what makes a regeneration byte-identical,
so an out-of-order root index is a finding even with every key present."""
text = "---\nbundle_profile: strict-v1\nokf_version: 0.1\nokf_spec_commit: d44368c\n---\n\n# Wiki\n"
found = STRICT_V1.index.violations(text, is_root=True, expected_targets=set())
assert [v.code for v in found] == ["index_root_key_order"]
# --- P1-F1: permitted-and-ordered is not the same claim as required -------
def test_a_permitted_root_key_may_be_omitted_without_a_finding() -> None:
"""P1-F1, measured 2026-07-31: `OKF_V0_2` names `okf_version` because
upstream §8/§12 say a bundle-root index MAY carry it — a permission, not a
demand. Judging the key as required made 14 of the 17 bundles swept fail
with exactly this one violation, upstream's own four reference bundles
among them, while D5's emitter treats the same key as optional (omitting
`root_frontmatter_values` writes no block). We emitted a MAY correctly and
graded it a MUST."""
text = "# Bundle\n\n- [Sales](ingest-sales.md)\n"
assert OKF_V0_2.index.violations(text, is_root=True) == ()
def test_the_permitted_key_is_still_ordered_and_still_root_only() -> None:
"""Separating the two meanings must not cost the checks that were right:
a nested index carrying the key is still a finding, and a root index that
carries it is still judged for order."""
nested = OKF_V0_2.index.violations("---\nokf_version: 0.2\n---\n\n# Concepts\n")
assert [(v.subject, v.code) for v in nested] == [
("okf_version", "index_root_frontmatter_unexpected")
]
root = OKF_V0_2.index.violations("---\nokf_version: 0.2\n---\n\n# Bundle\n", is_root=True)
assert root == ()
def test_a_required_root_key_is_still_reported_when_absent() -> None:
"""`STRICT_V1` mirrors the proving consumer's ratified contract, which
demands all three (they emit all three — measured across their bundle at
`c5141f8`). The fix separates the meanings; it does not relax them."""
found = STRICT_V1.index.violations(
"---\nokf_version: 0.1\n---\n\n# Wiki\n", is_root=True, expected_targets=set()
)
assert [(v.subject, v.code) for v in found] == [
("bundle_profile", "index_root_key_missing"),
("okf_spec_commit", "index_root_key_missing"),
]
def test_a_required_key_outside_the_ordered_set_fails_at_construction() -> None:
"""A key demanded but never named in the ordered tuple could never be
judged for position — the same contradiction `FrontmatterSchema` refuses
when `required` strays outside `allowed`."""
with pytest.raises(ValueError, match="ordered"):
IndexPolicy(
name="index.md",
link_template="- [{label}]({target})",
link_pattern=DEFAULT.index.link_pattern,
root_frontmatter=("okf_version",),
root_frontmatter_required=frozenset({"bundle_profile"}),
)
def test_a_nested_index_carries_no_frontmatter_at_all() -> None:
"""Confirmed independently in two consumers: the wiki writes frontmatter
only at the bundle root (`bundle.py:544-557`), and the catalog reports
their `okf_version`/`okf_layout` markers as root-exclusive. The asymmetry
is shape, not one repo's preference."""
found = STRICT_V1.index.violations(ROOT_HEAD + "\n# Concepts\n", expected_targets=set())
assert [(v.subject, v.code) for v in found] == [
("okf_version", "index_root_frontmatter_unexpected")
]
def test_a_nested_index_without_frontmatter_is_clean() -> None:
assert STRICT_V1.index.violations("# Concepts\n", expected_targets=set()) == ()
# --- determinism ----------------------------------------------------------
def test_violations_are_sorted_so_two_runs_agree() -> None:
text = "---\nokf_spec_commit: d\nokf_version: 0.1\n---\n\nz prose\na prose\n"
found = STRICT_V1.index.violations(text, is_root=True, expected_targets={"m.md"})
assert list(found) == sorted(found, key=lambda v: (v.subject, v.code))
assert found == STRICT_V1.index.violations(text, is_root=True, expected_targets={"m.md"})
def test_index_policy_stays_frozen() -> None:
with pytest.raises(Exception):
STRICT_V1.index.per_directory = False # type: ignore[misc]
def test_the_pattern_group_names_the_template_needs_are_present() -> None:
"""A template naming `{description}` whose pattern cannot recover one is a
pair that round-trips in only one direction."""
with pytest.raises(ValueError, match="description"):
IndexPolicy(
name="index.md",
link_template="* [{label}]({target}) - {description}",
link_pattern=re.compile(r"^\* \[(?P<label>[^\]]*)\]\((?P<target>[^)]+)\)$"),
)