fix(frontmatter): write a value a YAML reader reads back, and read both forms
K3-22. SPEC SS 11 point 1: "Every non-reserved `.md` file in the tree
contains a parseable YAML frontmatter block." Measured with PyYAML 6.0.3,
okf's own default K2 bundle failed safe_load on 41 of 455 blocks and the
R761 build on 1 of 2 763, every one a block scalar written verbatim.
Block (the profile emitter, every key): a value the K3-19 rule refuses as
plain is written double-quoted, `\` and `"` escaped; every other value keeps
its bytes, and a flow collection or an empty value is written as it stands.
The rule, now `profiles.yaml_block_plain`, agrees with PyYAML on every
top-level value in eleven measured trees (0 refused that it reads verbatim,
0 kept that it does not). Double, never single: 0 values in those trees are
`"`-wrapped and 11 193 are `'`-wrapped.
Flow (`sources`, Door A and Door B, and a run-stated flow value): the pinned
guard refuses ANY quote in a flow mapping (1.3.0, measured), so a leaf PyYAML
needs quoted has no form both read. `yaml_flow_plain` refuses it instead:
`,[]{}`, `?`, a quote, ": ", " #", a trailing `:`, a leading indicator -- a
leading `-` before a non-space excepted, which both readers take. The file
name is checked too, because it is the entry's `title` when the document
declares none. Existing codes: inbox_source_file_unaddressable,
inbox_source_title_unaddressable, source_reference_unquotable,
run_frontmatter_invalid.
Readers: parse_frontmatter, profiles' and structure's copies, and both
read_sources branches unquote a `"`-wrapped value (`\"` and `\\` decoded,
nothing else); `'`-wrapped values are untouched, and structure keeps the
single-quote rule it already had. The flow-mapping split is quote-aware, so
`{ title: "a, b" }` is one pair. The generated SKILL.md header goes through
the same block rule.
TWO K3-19 TESTS MOVED, deliberately: test_run_frontmatter built with
`sources=[{ resource: ...?languageCode=nb, ... }]`, the exact form PyYAML
refused on 2 761 of 2 761 frontmatters of K3-19's flagged build. The two
build tests now write an address without `?`; the flag-grammar test keeps
the `?` address (it only splits), and a new test holds that the build
refuses it with exit 2 and writes nothing.
1753 passed, 1 skipped (OKF_HTML_CORPUS, known). No golden moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
06e61a5acf
commit
ed0418f228
7 changed files with 288 additions and 79 deletions
|
|
@ -24,6 +24,12 @@ THE FORM IS NOT A STYLE CHOICE. The value is written verbatim on ONE line,
|
|||
because this package's own readers are line-oriented and measured blind to a
|
||||
block-form `sources`. The flag splits on the FIRST `=` and only there: the
|
||||
value a publisher's address needs carries `=` itself.
|
||||
|
||||
K3-22 moved one thing here, and it is a refusal: a flow value is written as
|
||||
given, so every leaf in it must be one a YAML reader and the guard both read
|
||||
back. The publisher's address with `?languageCode=nb` is not -- PyYAML refused
|
||||
all 2 761 frontmatters K3-19's flagged build wrote with it -- and the build
|
||||
tests below now write an address without `?`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -43,6 +49,12 @@ OPAQUE = "0f1e2d3c-4b5a-6978-8796-a5b4c3d2e1f0.xml"
|
|||
|
||||
ADDRESS = "https://normer.example.test/api/sts/900?languageCode=nb"
|
||||
STATED_SOURCES = f"[{{ resource: {ADDRESS}, title: R900:2024 }}]"
|
||||
# K3-22: the address above carries `?`, which ends a plain scalar inside a
|
||||
# PyYAML flow mapping, and the guard refuses the quoted form -- so no build may
|
||||
# write it. The flag's GRAMMAR still takes it (it only splits); the build
|
||||
# refuses it. A build that writes an address uses this one.
|
||||
WRITABLE_ADDRESS = "https://normer.example.test/api/sts/900/nb"
|
||||
WRITABLE_SOURCES = f"[{{ resource: {WRITABLE_ADDRESS}, title: R900:2024 }}]"
|
||||
MARKDOWN = b"# Innledning\n\nTekst her.\n\n# Omfang\n\nMer tekst her.\n"
|
||||
|
||||
|
||||
|
|
@ -115,7 +127,7 @@ def test_every_concept_carries_the_stated_keys_once(tmp_path: Path) -> None:
|
|||
"--frontmatter",
|
||||
"utgave=R900:2024",
|
||||
"--frontmatter",
|
||||
f"sources={STATED_SOURCES}",
|
||||
f"sources={WRITABLE_SOURCES}",
|
||||
)
|
||||
== 0
|
||||
)
|
||||
|
|
@ -130,7 +142,7 @@ def test_every_concept_carries_the_stated_keys_once(tmp_path: Path) -> None:
|
|||
# the door would derive -- the document's own title on the STS file,
|
||||
# the file name on the markdown one -- and never adds a second.
|
||||
assert [line for line in lines if line.startswith("sources:")] == [
|
||||
f"sources: {STATED_SOURCES}"
|
||||
f"sources: {WRITABLE_SOURCES}"
|
||||
], path
|
||||
|
||||
|
||||
|
|
@ -138,11 +150,11 @@ def test_the_stated_address_reads_back_through_this_packages_own_readers(
|
|||
tmp_path: Path,
|
||||
) -> None:
|
||||
bundle = tmp_path / "bundle"
|
||||
assert _main(_inbox(tmp_path), bundle, "--frontmatter", f"sources={STATED_SOURCES}") == 0
|
||||
assert _main(_inbox(tmp_path), bundle, "--frontmatter", f"sources={WRITABLE_SOURCES}") == 0
|
||||
for path in _concept_files(bundle):
|
||||
assert parse_frontmatter(path)["sources"] == STATED_SOURCES
|
||||
assert parse_frontmatter(path)["sources"] == WRITABLE_SOURCES
|
||||
assert read_sources(_frontmatter_lines(path)) == (
|
||||
({"resource": ADDRESS, "title": "R900:2024"},),
|
||||
({"resource": WRITABLE_ADDRESS, "title": "R900:2024"},),
|
||||
True,
|
||||
)
|
||||
|
||||
|
|
@ -227,6 +239,18 @@ def test_a_pair_that_would_not_read_back_verbatim_is_refused(tmp_path: Path, pai
|
|||
assert not bundle.exists()
|
||||
|
||||
|
||||
def test_a_stated_address_no_yaml_reader_reads_back_is_refused(
|
||||
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
"""K3-19's own flagged build wrote this form, and 2 761 of 2 761 of its
|
||||
frontmatters then failed `yaml.safe_load`. It is refused, never written
|
||||
(K3-22): `?` ends a PyYAML flow scalar, and the guard refuses the quote."""
|
||||
bundle = tmp_path / "bundle"
|
||||
assert _main(_inbox(tmp_path), bundle, "--frontmatter", f"sources={STATED_SOURCES}") == 2
|
||||
assert "sources" in capsys.readouterr().err
|
||||
assert not bundle.exists()
|
||||
|
||||
|
||||
def test_a_value_spanning_lines_is_refused_through_the_api(tmp_path: Path) -> None:
|
||||
with pytest.raises(IngestError) as caught:
|
||||
cli.build(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue