fix(okf): nested frontmatter keys must not forge the type that gates verdict exclusion
The frontmatter parser is a line-oriented approximation of YAML with no nesting model, and it flattened INDENTED keys into the same mapping. Two measured defects followed, the second load-bearing: 1. Sibling blocks sharing an inner key COLLIDED. Given OKF §10's canonical Attested Computation shape, `executor.resource` vanished silently and `attester.resource` was promoted to a top-level `resource`. No error raised. 2. An indented `type:` OVERWROTE the column-0 one, making the verdict-exclusion gate in `bundle_context` forgeable. A file declaring `type: verdict` at column 0, carrying any nested block with a `type:` in it, rendered its body straight into the read-context — defeating the §11 seam whose own docstring claimed "a mislabelled or injected edge cannot smuggle a verdict into the context". The type CHECK was there; the VALUE it checked was writable. This is spec-legal input, not malformed input: method-spec §2 calls it YAML frontmatter, and ingest-spec §7 (`:153`, `:216`) says unknown keys MAY follow the stamp and ride through navigation. Fix: only column-0 keys participate; indented lines are skipped, never flattened. Nested blocks become OPAQUE — stated as a limitation in the docstring, not dressed up as a nesting model we do not have (§1 honesty rule). Single parse site, so the fix covers the class: `hitl.py:157` (verdict_id, gated on .type) and `experience.py:137` (realization_rate, expected_actual) all read through `ConceptFile.frontmatter`; `promotion.py` only emits, from a fixed template. Golden-neutral by measurement: 0 indented frontmatter lines across all 17 frontmatter-bearing files in `shared/examples/`. Both tests written RED first and confirmed to die on the SEAM assertion, not on a collateral one — the forgery test rendered `## project: Seed` before the fix. Each negative carries a standalone positive control (sessions 17-18: a control can itself hide behind a preceding assert). Provenance: hypothesis received from llm-ingestion-okf, who measured the flattening in their own parser and flagged that ours shares the form. They had NOT measured our side. The type-clobber variant is ours, found by measuring the neighbourhood rather than only the reported case. Suite 688 -> 690. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJmse16bEkaSBtvXhncEUc
This commit is contained in:
parent
30ba68a703
commit
d691510163
2 changed files with 77 additions and 1 deletions
|
|
@ -56,6 +56,23 @@ def _parse_frontmatter_and_body(path: Path) -> tuple[dict[str, str], str]:
|
|||
|
||||
Unknown fields are preserved as strings. The ``type`` requirement is NOT applied
|
||||
here — it belongs to concept files, not to the index entry point.
|
||||
|
||||
ONLY COLUMN-0 KEYS participate. This parser is a line-oriented approximation of
|
||||
YAML, not YAML: it has no nesting model, so an INDENTED line is skipped rather
|
||||
than flattened into the same mapping. Nested block mappings are therefore OPAQUE
|
||||
— their inner keys are not readable here, and this is a stated limitation, not a
|
||||
representation (§1 honesty rule). Frontmatter is spec-legal YAML (method-spec §2)
|
||||
and MAY carry unknown structured keys that ride through navigation (ingest-spec
|
||||
§7), so nested blocks are expected input, not malformed input.
|
||||
|
||||
Flattening was a defect on two counts, both measured. Sibling blocks sharing an
|
||||
inner key COLLIDED — ``executor.resource`` vanished silently while
|
||||
``attester.resource`` was promoted to a top-level ``resource`` (OKF §10 Attested
|
||||
Computation is exactly this shape). Worse, an indented ``type:`` overwrote the
|
||||
column-0 one, making the verdict-exclusion gate in ``bundle_context`` FORGEABLE:
|
||||
a file declaring ``type: verdict`` rendered into the read-context. The gate is
|
||||
load-bearing (§11), so the value it reads must not be attacker- or
|
||||
accident-writable from a nested block.
|
||||
"""
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
if not lines or lines[0].strip() != "---":
|
||||
|
|
@ -66,6 +83,8 @@ def _parse_frontmatter_and_body(path: Path) -> tuple[dict[str, str], str]:
|
|||
if line.strip() == "---":
|
||||
body_start = i + 1
|
||||
break
|
||||
if line[:1].isspace():
|
||||
continue
|
||||
key, sep, value = line.partition(":")
|
||||
if sep:
|
||||
frontmatter[key.strip()] = _strip_matching_quotes(value.strip())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue