test(frontmatter): what okf writes must be YAML a YAML reader reads back the same
K3-22, red first. SPEC SS 11 point 1 requires "a parseable YAML frontmatter
block" in every file. Measured with PyYAML 6.0.3 before any code moved: the
K2 default bundle this repository pins fails safe_load on 41 of 455
frontmatter blocks, all on `title` (a leading `- `, `**` or `*`, or ": "),
and the R761 build on 1 of 2 763 (a title ending in `:`). No `sources` value
okf itself wrote failed; the 4 605 consumer failures come from that
consumer's own writer.
Each case goes through a public path (render_inbox_concept, the profile
emitter, Door A's sources renderer, skill.render) and is held to three
readers: safe_load must not raise, BaseLoader must return the same strings
as parse_frontmatter / read_sources, and the pinned guard must admit it. The
guard is why quoting inside a flow mapping is not the fix: 1.3.0 refuses any
quote in a flow mapping (measured), so a `sources` leaf PyYAML needs quoted
has no form both readers accept, and is refused instead.
57 of 85 red on 0308169; the 28 green are the known-negatives and controls.
The rest of the suite is unchanged: 1695 passed (1667 + 28), 1 skipped.
pyyaml joins [dependency-groups] dev and nothing else; uv.lock moves by
exactly the two lines that dev dependency adds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0308169c79
commit
06e61a5acf
3 changed files with 387 additions and 1 deletions
|
|
@ -124,7 +124,14 @@ ocr = ["rapidocr>=3.9,<4", "onnxruntime>=1.20,<2", "pypdfium2>=4,<6"]
|
||||||
# tree read green while 0.16.6 found 148 things in it. The floor is now the
|
# tree read green while 0.16.6 found 148 things in it. The floor is now the
|
||||||
# version the acceptance was measured under, and the ceiling is the next minor,
|
# version the acceptance was measured under, and the ceiling is the next minor,
|
||||||
# because 0.16 is itself the release that widened the default rule set.
|
# because 0.16 is itself the release that widened the default rule set.
|
||||||
dev = ["pytest>=8", "mypy>=1.14", "ruff>=0.16.6,<0.17"]
|
#
|
||||||
|
# pyyaml is a TEST reader and nothing else (K3-22): SPEC SS 11 requires "a
|
||||||
|
# parseable YAML frontmatter block" in every file, and the only way to measure
|
||||||
|
# that is to ask a YAML reader. How a value is WRITTEN stays decided by a rule
|
||||||
|
# in `profiles`, never by a parser, so `src/` imports no yaml; the tests
|
||||||
|
# validate the rule against this reader. The floor is the version it was
|
||||||
|
# measured under (6.0.3, 2026-09-11).
|
||||||
|
dev = ["pytest>=8", "mypy>=1.14", "ruff>=0.16.6,<0.17", "pyyaml>=6.0.3,<7"]
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/llm_ingestion_okf"]
|
packages = ["src/llm_ingestion_okf"]
|
||||||
|
|
|
||||||
377
tests/test_yaml_frontmatter.py
Normal file
377
tests/test_yaml_frontmatter.py
Normal file
|
|
@ -0,0 +1,377 @@
|
||||||
|
"""Frontmatter this library WRITES is frontmatter a YAML reader reads (K3-22).
|
||||||
|
|
||||||
|
SPEC SS 11, point 1: "Every non-reserved `.md` file in the tree contains a
|
||||||
|
parseable YAML frontmatter block." SS 4 calls the block YAML and names no
|
||||||
|
version and no subset, so "parseable" is whatever reader the consumer has;
|
||||||
|
PyYAML is the common one and the one measured here. It is a DEV dependency and
|
||||||
|
never a runtime one: how a value is written is decided by a rule in
|
||||||
|
`profiles`, and these tests validate that rule against the reader.
|
||||||
|
|
||||||
|
Three readers must agree on every value written, each for its own reason:
|
||||||
|
|
||||||
|
- PyYAML, which a consumer reads a bundle with. `safe_load` must not raise,
|
||||||
|
AND `BaseLoader` -- the same grammar with no implicit typing -- must return
|
||||||
|
the same string this library's own readers return. Without the second half
|
||||||
|
parsing is measured and meaning is not: `title: Kap #3` loads without an
|
||||||
|
error, as `Kap`.
|
||||||
|
- `materialize.parse_frontmatter` for top-level keys and `consume.read_sources`
|
||||||
|
for the `sources` entries -- what this library reads its own bundles with.
|
||||||
|
- the pinned guard's `okf.parse_frontmatter`, which Door C imports a bundle
|
||||||
|
through. Measured on 1.3.0 it refuses ANY quote inside a flow mapping, so a
|
||||||
|
`sources` value that needs quoting in flow has no form both it and PyYAML
|
||||||
|
read, and is refused rather than written.
|
||||||
|
|
||||||
|
MEASURED before any code moved (PyYAML 6.0.3): the K2 default bundle this
|
||||||
|
repository pins fails `safe_load` on 41 of its 455 frontmatter blocks, every
|
||||||
|
one a `title` (a leading `- `, `**` or `*`, a `": "`), and the R761 build on 1
|
||||||
|
of 2 763 (a title ending in `:`). No `sources` value okf itself wrote failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import yaml
|
||||||
|
from llm_ingestion_guard import okf as guard_okf
|
||||||
|
|
||||||
|
from llm_ingestion_okf import skill as okf_skill
|
||||||
|
from llm_ingestion_okf.consume import read_sources
|
||||||
|
from llm_ingestion_okf.errors import MaterializationError
|
||||||
|
from llm_ingestion_okf.inbox import render_inbox_concept
|
||||||
|
from llm_ingestion_okf.manifest import FileSource
|
||||||
|
from llm_ingestion_okf.materialize import _render_sources, parse_frontmatter
|
||||||
|
from llm_ingestion_okf.profiles import DEFAULT, SEGMENTED_OKF_V0_2
|
||||||
|
from llm_ingestion_okf.profiles import _split_frontmatter as _profiles_split
|
||||||
|
from llm_ingestion_okf.structure import _split_frontmatter as _structure_split
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
STAMP = "2026-09-11T00:00:00Z"
|
||||||
|
GOLDEN = PROJECT_ROOT / "examples" / "ingest-golden-segmented-okf-v0-2" / "expected-bundle"
|
||||||
|
|
||||||
|
|
||||||
|
def _block(text: str) -> str:
|
||||||
|
lines = text.splitlines()
|
||||||
|
assert lines[0] == "---"
|
||||||
|
return "\n".join(lines[1 : lines.index("---", 1)])
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_every_reader_agrees(text: str, tmp_path: Path) -> None:
|
||||||
|
"""Parseable by PyYAML, the same strings as ours, and admitted by the guard."""
|
||||||
|
block = _block(text)
|
||||||
|
yaml.safe_load(block)
|
||||||
|
loaded = yaml.load(block, Loader=yaml.BaseLoader)
|
||||||
|
path = tmp_path / "concept.md"
|
||||||
|
path.write_text(text, encoding="utf-8")
|
||||||
|
ours = parse_frontmatter(path)
|
||||||
|
assert set(ours) == set(loaded)
|
||||||
|
for key, value in loaded.items():
|
||||||
|
if key == "sources":
|
||||||
|
entries, present = read_sources(block.splitlines())
|
||||||
|
assert present
|
||||||
|
assert [dict(entry) for entry in entries] == value
|
||||||
|
elif isinstance(value, str):
|
||||||
|
# A flow COLLECTION (`source_offset: [0, 4]`) is an opaque string
|
||||||
|
# to the flat reader by design; every scalar must match exactly.
|
||||||
|
assert ours[key] == value, key
|
||||||
|
guard_okf.parse_frontmatter(text)
|
||||||
|
|
||||||
|
|
||||||
|
def _concept(title: str, **overrides: object) -> str:
|
||||||
|
kwargs: dict[str, object] = {
|
||||||
|
"okf_type": "note",
|
||||||
|
"title": title,
|
||||||
|
"source_file": "f.md",
|
||||||
|
"source_bytes": b"x",
|
||||||
|
"ingested_at": STAMP,
|
||||||
|
"profile": DEFAULT,
|
||||||
|
}
|
||||||
|
kwargs.update(overrides)
|
||||||
|
return render_inbox_concept("body\n", **kwargs) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
|
||||||
|
# --- block scalars: the emitter every key goes through ----------------------
|
||||||
|
|
||||||
|
#: Plain today and plain after: a colon with no space, a `#` with no space
|
||||||
|
#: before it, `=`, `&`. Written byte-for-byte as before -- the known-negative
|
||||||
|
#: that keeps the fix from quoting what never needed it.
|
||||||
|
BLOCK_PLAIN = ["N100:2023", "Kap#3", "R761 Prosesskoden:2025", "a=b&c", "1:2 utskifting"]
|
||||||
|
|
||||||
|
#: Each is refused or MISREAD by PyYAML as a plain block scalar.
|
||||||
|
BLOCK_NOT_PLAIN = [
|
||||||
|
"N100: 2023",
|
||||||
|
"Kap #3",
|
||||||
|
"Eksempel kontur:",
|
||||||
|
"- punkt",
|
||||||
|
"{x} y",
|
||||||
|
'"sitat" og mer',
|
||||||
|
"'sitat' og mer",
|
||||||
|
"* stjerne",
|
||||||
|
"**Avvik**",
|
||||||
|
"&anker",
|
||||||
|
"%prosent",
|
||||||
|
"@at",
|
||||||
|
"`kode`",
|
||||||
|
"!tag",
|
||||||
|
"|pipe",
|
||||||
|
">gt",
|
||||||
|
"#hash",
|
||||||
|
"C:\\mappe og \\ en",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("title", BLOCK_PLAIN + BLOCK_NOT_PLAIN)
|
||||||
|
def test_a_title_reads_back_the_same_in_every_reader(title: str, tmp_path: Path) -> None:
|
||||||
|
_assert_every_reader_agrees(_concept(title), tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("title", BLOCK_PLAIN)
|
||||||
|
def test_a_title_that_was_already_plain_is_written_byte_for_byte(title: str) -> None:
|
||||||
|
assert f"\ntitle: {title}\n" in _concept(title)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"value",
|
||||||
|
[*BLOCK_PLAIN, *BLOCK_NOT_PLAIN, "[Utkast] plan", "slutt :", "a: b og c: d"],
|
||||||
|
)
|
||||||
|
def test_a_run_stated_value_reads_back_the_same_in_every_reader(value: str, tmp_path: Path) -> None:
|
||||||
|
"""`--frontmatter KEY=VALUE` goes through the same emitter, and `[` -- which
|
||||||
|
a title may not carry -- reaches it here."""
|
||||||
|
text = _concept("T", concept_frontmatter_values={"utgave": value})
|
||||||
|
_assert_every_reader_agrees(text, tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_run_stated_description_with_a_colon_space_reads_back(tmp_path: Path) -> None:
|
||||||
|
"""K3-19's 217: a DERIVED spec point carrying `": "` is still omitted
|
||||||
|
(`test_sts_description.py`); a STATED one is the caller's words and is
|
||||||
|
written in a form a YAML reader returns verbatim."""
|
||||||
|
value = "Omfatter maling: rekkverk og gjerder."
|
||||||
|
text = _concept(
|
||||||
|
"T", profile=SEGMENTED_OKF_V0_2, concept_frontmatter_values={"description": value}
|
||||||
|
)
|
||||||
|
_assert_every_reader_agrees(text, tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
# --- the `sources` flow mapping ---------------------------------------------
|
||||||
|
|
||||||
|
#: Written verbatim inside the flow mapping, and every reader agrees.
|
||||||
|
FLOW_KEPT = ["N100:2023", "R761 Prosesskoden 2025", "Kap#3", "a=b&c"]
|
||||||
|
|
||||||
|
#: No form both PyYAML and the guard read: plain, PyYAML refuses or misreads
|
||||||
|
#: it; quoted, the guard refuses it. Refused rather than written.
|
||||||
|
FLOW_REFUSED = ["N100: 2023", "Kap #3", "slutt:", "a?b", "Vegvesen's", 'Sa "hei"', "*x", "a, b"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("title", FLOW_KEPT)
|
||||||
|
def test_a_sources_title_every_reader_reads_is_written_verbatim(title: str, tmp_path: Path) -> None:
|
||||||
|
text = _concept("T", profile=SEGMENTED_OKF_V0_2, source_title=title)
|
||||||
|
assert f"sources: [{{ resource: f.md, title: {title} }}]\n" in text
|
||||||
|
_assert_every_reader_agrees(text, tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("title", FLOW_REFUSED)
|
||||||
|
def test_a_sources_title_no_reader_pair_can_share_is_refused(title: str) -> None:
|
||||||
|
with pytest.raises(MaterializationError) as excinfo:
|
||||||
|
_concept("T", profile=SEGMENTED_OKF_V0_2, source_title=title)
|
||||||
|
assert excinfo.value.code == "inbox_source_title_unaddressable"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("source_file", ["Hva er nytt?.pdf", "Kap #3.pdf", "del/*utkast.pdf"])
|
||||||
|
def test_a_source_file_no_reader_pair_can_share_is_refused(source_file: str) -> None:
|
||||||
|
"""`del/*utkast.pdf` is plain as a path and NOT as the file name the entry
|
||||||
|
carries for its `title` -- `*` opens an alias -- so both are checked."""
|
||||||
|
with pytest.raises(MaterializationError) as excinfo:
|
||||||
|
_concept("T", profile=SEGMENTED_OKF_V0_2, source_file=source_file)
|
||||||
|
assert excinfo.value.code == "inbox_source_file_unaddressable"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("source_file", ["a=b&c.pdf", "del/-utkast.pdf"])
|
||||||
|
def test_a_source_file_every_reader_reads_is_written(source_file: str, tmp_path: Path) -> None:
|
||||||
|
"""Known-negatives: `=` and `&` are ordinary characters, and so is a leading
|
||||||
|
`-` followed by a non-space inside a flow mapping -- both readers return
|
||||||
|
`-utkast.pdf` verbatim, and refusing it would refuse a document they read."""
|
||||||
|
text = _concept("T", profile=SEGMENTED_OKF_V0_2, source_file=source_file)
|
||||||
|
_assert_every_reader_agrees(text, tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_run_stated_sources_with_a_query_string_is_refused() -> None:
|
||||||
|
"""The consumer case the order was measured on: `?` ends a plain scalar in
|
||||||
|
a PyYAML flow mapping, and the guard refuses the quoted form."""
|
||||||
|
stated = "[{ resource: https://h.no/api/1?languageCode=nb&x=2, title: T }]"
|
||||||
|
with pytest.raises(MaterializationError) as excinfo:
|
||||||
|
_concept("T", profile=SEGMENTED_OKF_V0_2, concept_frontmatter_values={"sources": stated})
|
||||||
|
assert excinfo.value.code == "run_frontmatter_invalid"
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_run_stated_sources_without_one_is_written_verbatim(tmp_path: Path) -> None:
|
||||||
|
stated = "[{ resource: https://h.no/api/1, title: N100:2023 }]"
|
||||||
|
text = _concept("T", profile=SEGMENTED_OKF_V0_2, concept_frontmatter_values={"sources": stated})
|
||||||
|
assert f"\nsources: {stated}\n" in text
|
||||||
|
_assert_every_reader_agrees(text, tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("root", ["data?x=1", "Kap #3", "*utkast"])
|
||||||
|
def test_door_a_refuses_a_locator_no_reader_pair_can_share(root: str) -> None:
|
||||||
|
with pytest.raises(MaterializationError) as excinfo:
|
||||||
|
_render_sources([FileSource(id="a", root=root)])
|
||||||
|
assert excinfo.value.code == "source_reference_unquotable"
|
||||||
|
|
||||||
|
|
||||||
|
# --- the readers read both forms ---------------------------------------------
|
||||||
|
|
||||||
|
QUOTED = '---\ntitle: "N100: 2023 \\"sitat\\" C:\\\\mappe"\nprosessnr: \'1\'\n---\n\nBody.\n'
|
||||||
|
UNQUOTED_TITLE = 'N100: 2023 "sitat" C:\\mappe'
|
||||||
|
|
||||||
|
|
||||||
|
def test_every_flat_reader_unquotes_a_double_quoted_value(tmp_path: Path) -> None:
|
||||||
|
path = tmp_path / "q.md"
|
||||||
|
path.write_text(QUOTED, encoding="utf-8")
|
||||||
|
assert parse_frontmatter(path)["title"] == UNQUOTED_TITLE
|
||||||
|
assert _profiles_split(QUOTED)[0]["title"] == UNQUOTED_TITLE
|
||||||
|
assert _structure_split(QUOTED)[0]["title"] == UNQUOTED_TITLE
|
||||||
|
assert yaml.safe_load(_block(QUOTED))["title"] == UNQUOTED_TITLE
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_single_quoted_value_keeps_its_meaning_in_every_flat_reader(tmp_path: Path) -> None:
|
||||||
|
"""11 193 single-quoted values exist in consumer bundles today, and 0
|
||||||
|
double-quoted ones: only the `"` form is newly unquoted, so none of them
|
||||||
|
moves. `structure` already unquoted BOTH forms before K3-22, on its own
|
||||||
|
documented rule (`version: '2021'` is a string), and keeps doing so -- the
|
||||||
|
measured state, pinned rather than changed."""
|
||||||
|
path = tmp_path / "q.md"
|
||||||
|
path.write_text(QUOTED, encoding="utf-8")
|
||||||
|
assert parse_frontmatter(path)["prosessnr"] == "'1'"
|
||||||
|
assert _profiles_split(QUOTED)[0]["prosessnr"] == "'1'"
|
||||||
|
assert _structure_split(QUOTED)[0]["prosessnr"] == "1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_sources_unquotes_in_both_yaml_forms() -> None:
|
||||||
|
block = [
|
||||||
|
"sources:",
|
||||||
|
' - resource: "https://h.no/api/1?languageCode=nb"',
|
||||||
|
' title: "N100: 2023"',
|
||||||
|
]
|
||||||
|
flow = ['sources: [{ resource: "https://h.no/a?x=1, y", title: "N100: 2023" }]']
|
||||||
|
assert read_sources(block)[0] == (
|
||||||
|
{"resource": "https://h.no/api/1?languageCode=nb", "title": "N100: 2023"},
|
||||||
|
)
|
||||||
|
# A quoted comma is part of the value, not an entry separator.
|
||||||
|
assert read_sources(flow)[0] == ({"resource": "https://h.no/a?x=1, y", "title": "N100: 2023"},)
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_only(path: Path) -> dict[str, str]:
|
||||||
|
"""The reader as it stood before K3-22, for the known-positive below."""
|
||||||
|
lines = path.read_text(encoding="utf-8").splitlines()
|
||||||
|
found: dict[str, str] = {}
|
||||||
|
for line in lines[1 : lines.index("---", 1)]:
|
||||||
|
if line[:1] not in (" ", "\t"):
|
||||||
|
key, sep, value = line.partition(":")
|
||||||
|
if sep:
|
||||||
|
found[key.strip()] = value.strip()
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
def test_every_shipped_frontmatter_reads_exactly_as_before() -> None:
|
||||||
|
"""Known-positive: the unquoting must be a no-op on every file this
|
||||||
|
repository ships, because none of them carries a `"`-wrapped value."""
|
||||||
|
paths = [
|
||||||
|
path
|
||||||
|
for root in (PROJECT_ROOT / "tests" / "fixtures", PROJECT_ROOT / "examples")
|
||||||
|
for path in sorted(root.rglob("*.md"))
|
||||||
|
if path.read_text(encoding="utf-8").startswith("---\n")
|
||||||
|
]
|
||||||
|
assert len(paths) >= 26
|
||||||
|
for path in paths:
|
||||||
|
assert parse_frontmatter(path) == _strip_only(path), path
|
||||||
|
|
||||||
|
|
||||||
|
# --- the rules, validated against the reader --------------------------------
|
||||||
|
|
||||||
|
#: Constructed, so both directions can be counted on values the corpora may not
|
||||||
|
#: carry. MEASURED over the real bundles the rule and PyYAML agree on every
|
||||||
|
#: value; here the safe direction is required and the over-refusals are pinned.
|
||||||
|
CONSTRUCTED = [
|
||||||
|
*BLOCK_PLAIN,
|
||||||
|
*BLOCK_NOT_PLAIN,
|
||||||
|
*FLOW_REFUSED,
|
||||||
|
"https://h.no/a/1",
|
||||||
|
"https://h.no/a/1?x=nb&y=2",
|
||||||
|
"-punkt",
|
||||||
|
"?spm",
|
||||||
|
":kolon",
|
||||||
|
",komma",
|
||||||
|
"[Utkast] plan",
|
||||||
|
"a{b}c",
|
||||||
|
"tab\there",
|
||||||
|
"slutt :",
|
||||||
|
"Statens vegvesen",
|
||||||
|
"100 %",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _block_verbatim(value: str) -> bool:
|
||||||
|
try:
|
||||||
|
return yaml.load(f"k: {value}", Loader=yaml.BaseLoader) == {"k": value}
|
||||||
|
except yaml.YAMLError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_verbatim(value: str) -> bool:
|
||||||
|
line = f"sources: [{{ resource: r.pdf, title: {value} }}]"
|
||||||
|
try:
|
||||||
|
yaml_ok = yaml.load(line, Loader=yaml.BaseLoader) == {
|
||||||
|
"sources": [{"resource": "r.pdf", "title": value}]
|
||||||
|
}
|
||||||
|
except yaml.YAMLError:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
guard_okf.parse_frontmatter(f"---\n{line}\n---\n")
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return yaml_ok
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_block_rule_never_keeps_a_value_pyyaml_would_not_return_verbatim() -> None:
|
||||||
|
from llm_ingestion_okf.profiles import yaml_block_plain
|
||||||
|
|
||||||
|
kept_but_misread = [v for v in CONSTRUCTED if yaml_block_plain(v) and not _block_verbatim(v)]
|
||||||
|
refused_but_read = [v for v in CONSTRUCTED if not yaml_block_plain(v) and _block_verbatim(v)]
|
||||||
|
assert kept_but_misread == []
|
||||||
|
assert refused_but_read == ["-punkt", "?spm", ":kolon"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_flow_rule_never_keeps_a_value_pyyaml_or_the_guard_would_refuse() -> None:
|
||||||
|
from llm_ingestion_okf.profiles import yaml_flow_plain
|
||||||
|
|
||||||
|
kept_but_refused = [v for v in CONSTRUCTED if yaml_flow_plain(v) and not _flow_verbatim(v)]
|
||||||
|
refused_but_read = [v for v in CONSTRUCTED if not yaml_flow_plain(v) and _flow_verbatim(v)]
|
||||||
|
assert kept_but_refused == []
|
||||||
|
assert refused_but_read == []
|
||||||
|
|
||||||
|
|
||||||
|
# --- the generated SKILL.md header -------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_generated_skill_header_is_yaml_whatever_the_bundle_calls_itself(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
"""`description` carries the root index's `bundle_id` raw, and a bundle
|
||||||
|
this library did not build may call itself anything."""
|
||||||
|
bundle = tmp_path / "bundle"
|
||||||
|
shutil.copytree(GOLDEN, bundle)
|
||||||
|
index = bundle / "index.md"
|
||||||
|
text = index.read_text(encoding="utf-8")
|
||||||
|
old = next(line for line in text.splitlines() if line.startswith("bundle_id:"))
|
||||||
|
index.write_text(text.replace(old, "bundle_id: golden: two #1"), encoding="utf-8")
|
||||||
|
|
||||||
|
skill_text, _payload = okf_skill.render(bundle, out=tmp_path / "skill")
|
||||||
|
|
||||||
|
header = yaml.safe_load(_block(skill_text))
|
||||||
|
assert "`golden: two #1`" in header["description"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_source_sha256_still_hashes_the_original_bytes() -> None:
|
||||||
|
"""A guard on the fixture itself: `_concept` must not have drifted."""
|
||||||
|
assert f"source_sha256: {hashlib.sha256(b'x').hexdigest()}\n" in _concept("T")
|
||||||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -569,6 +569,7 @@ ocr = [
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "mypy" },
|
{ name = "mypy" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -587,6 +588,7 @@ provides-extras = ["extract", "ocr"]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "mypy", specifier = ">=1.14" },
|
{ name = "mypy", specifier = ">=1.14" },
|
||||||
{ name = "pytest", specifier = ">=8" },
|
{ name = "pytest", specifier = ">=8" },
|
||||||
|
{ name = "pyyaml", specifier = ">=6.0.3,<7" },
|
||||||
{ name = "ruff", specifier = ">=0.16.6,<0.17" },
|
{ name = "ruff", specifier = ">=0.16.6,<0.17" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue