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
Oekt 17 found the class on four named files. This sweep ENUMERATES it: 42 negative
substring assertions across 21 test files (STATE's "~34 across 23" was a premise --
measured, it is 42/21). Sixteen of them measured an absence without ever having
shown presence; all sixteen now carry a positive control asserting the searched-for
string PRESENT in the source artifact, in EXACTLY the form the negative looks for.
Files touched: test_costsim, test_loop, test_okf (3 sites), test_preflight,
test_run_entrance, test_s10_run_layer, test_sdk_version_guard, test_simulation
(2 sites), test_step1_expel, test_step5_refine, test_step7_async_loop,
test_step8_promotion, test_valuereport.
VALUE-PROOF (green-without / red-with, per the oekt-17 rule that a detach proof is
not a value proof). Seven source/fixture mutations, each making the negative vacuous:
M1 verdict fixture loses the realization signal VALUE-PROVEN
M2 decoy fixture loses its text VALUE-PROVEN
M3 renderer stops emitting typed section headings VALUE-PROVEN
M4 promotion stops writing the marker VALUE-PROVEN (pass 2)
M5 fold stops rendering the realization surface VALUE-PROVEN
M6 report stops labelling the cost section VALUE-PROVEN
M7 preflight stops importing the SDK VALUE-PROVEN
M4 needed pass 2: a PRECEDING assertion caught the same mutation, hiding the new
control behind it -- the oekt-17 lesson reproduced. The remaining nine controls are
vacuity guards (non-emptiness / form-presence) whose mutation would have to break
the source artificially; they are stated as guards, not claimed as value-proven.
MEASURED FINDING (test_loop): the FIRST-RUN-MARKER negative cannot be given a
positive control at all. Within a run only the CHECKER's critique is fed back --
the proposer's own prior reasoning crosses no prompt boundary, not even within a
run. So that negative holds trivially. Left in place with the limitation stated in
the test rather than dressed up as a controlled seam; the CRITIQUE negative beside
it IS controlled and is the real seam.
Mutations were in-place on src/ and shared/ with original bytes restored and
sha-verified; git status clean before and after. Suite 688 -> 688 (assertions added
inside existing tests, no new test cases). ruff + mypy --strict green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Vc5PmZGjwuJypdhzKnJa5
The pulled method-spec (commons 9801d35) retires the "a target containing a
path separator is out-of-bundle" heuristic, which conflated depth with escape
and forbade valid hierarchy. Triage of the pull found FIVE contradictions in
okf.py, not the two STATE had measured on line 127 alone:
1. the separator ban skipped every legal nested target;
2. de-duplication keyed on the RAW target (`resolved` was computed a line
later), not on the resolved path;
3. navigation never recursed — only the root index's links were read;
4. a leading `/` became filesystem-absolute via pathlib rather than denoting
the bundle root (safe, because the boundary check caught it, but the right
outcome for the wrong reason — and wrong the moment `/a/index.md` must be
FOLLOWED);
5. rendering excluded only `verdict`, so a nested index body would render as
content.
navigate_bundle is now depth-first in first-seen link order, de-duplicating on
the resolved path (so `./a.md` and `a.md` are one entry and cycles terminate);
resolution and the fail-closed boundary check move to _resolve_target, the sole
in-/out-of-bundle test. The missing-index rule binds the bundle root alone.
bundle_context renders flat regardless of depth and drops nested index bodies:
only the root index is the summary.
The gate is the commons-owned nav-golden pair that arrived with the same pull —
bundle in, expected-read-context out. Its negative case exists so the gate can
go red at all, and carries a real decoy one level up plus a `/etc/passwd` trap.
Detach-proved (mutate, run, restore from copy) — each new seam goes RED:
D1 reinstate the separator heuristic -> RED
D2 re-key dedup on the raw target -> RED
D3 read a leading `/` as absolute -> RED
D4 render nested index bodies -> RED
Control after restore: 24 passed. Suite 631 -> 637, ruff + mypy --strict clean.
Comments asserting the retired doctrine were corrected rather than left to
document a rule the code no longer follows.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M1zp3BxCuzRnUtJPzvEFTQ
navigate_bundle died on an index.md that carried a frontmatter block without
a 'type' field: _parse_index_entry keyed its tolerance on the ABSENCE of the
block, so a frontmatter-ful index fell through to parse_concept_file and
raised. Not merely the index read failed — the whole navigation did.
OKF v0.2 (A-E6) triggers this: it stamps okf_version into index.md. Our own
assumption ("a generated index has no frontmatter") is frozen in the golden,
so nothing caught it. The defect is ours, not theirs.
method-spec §3 Step 1 renders the index as "the index body (the summary)"
and every OTHER file as a "non-index concept file" — the index is not a
concept file, so the 'type' requirement never reached it in the first place.
The tolerance is now keyed on being the index, which is what it always meant.
Load-bearing, both directions detach-proved:
- test_index_with_frontmatter_lacking_type_navigates goes RED when the
tolerance is re-keyed on absence-of-frontmatter.
- test_missing_type_is_an_error goes RED when the index default leaks onto
concept files. Its vehicle moved from index.md to a non-index file: it
proves the concept-file rule, and an index.md vehicle now proves the
opposite of what the test is named for. This also closes a real gap —
nothing tested a non-index file WITH frontmatter but WITHOUT 'type'.
Goldens untouched (shared bundle has type: index; ingest golden index has no
frontmatter). Suite 627 -> 628 passed.
navigate_bundle's out-of-bundle filter drops every '/'- and '\'-bearing
target before resolution, but a NUL-byte target carries no path separator
and slipped through to (bundle_dir / target).resolve(), which raises
ValueError: embedded null character — propagating instead of being skipped.
method-spec §72 requires a broken cross-link to be tolerated (skipped,
never raised). Wrap resolve/is_file in `except ValueError: continue`;
parse_concept_file stays outside the guard so malformed concept files
still raise.
Load-bearing test drives the first dangerous target THROUGH the filter
into resolution; detach-proved RED (ValueError propagates) when the guard
is removed. Closes the nullbyte item reported OPEN in OKF trinn E.
442→443 green, golden byte-exact, full gate clean (ruff+format+mypy strict).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RiTwaKLesgcwXx2mDviqpt
Speiler MAF I2 fra shared/ingest-spec.md alene: manifest → CSV-konnektor →
materialisert OKF-bundle, byte-identisk med den delte golden-fasiten.
- ingest.py: ManifestContract (pydantic, fail-fast, file-kilde, verdict-reservasjon
§3, id-grammatikk, max_rows), CSV-konnektor (boundary-checked fail-closed),
materialisering (§5-frontmatter eksakt rekkefølge, markdown-tabell m/ escaping,
LF-only, SHA-256 manifest-stamp), index-generering (§6), replacement §3/§5.
- okf.py: _parse_index_entry — tolererer frontmatterløs index (method-spec §3:
index rendres via body = summary, ikke som typet concept-fil). Golden var
spec-konform; D7-okf var strengere enn standarden. Scoped: non-index concept-
filer krever fortsatt type (honesty-test).
- examples/ingest-golden-file/: repo-lokal golden (byte-frossen kopi av I2s fasit).
- Speiltester (I2s load-bearing-sett, alle detach-bevist røde): golden byte-fasit
+ mutasjonskontroller · provenance/navigability/verdict-reservasjon/re-ingest-safety
· kontrakt fail-fast/max_rows/boundary/kollisjon · spec-integritet §11.
- docs/2026-07-04-I3-brief.md: brief + de to operatør-avgjorte beslutningene.
Suite 239 passed uten nøkkel/nettverk (189 + 50 nye) · ruff + mypy --strict rene.
[skip-docs] README + docs/extending.md er bevisst utsatt til I7 per sesjonsplan
(programmet batcher ingest-doc der, avgrenset til det D7 faktisk har — CSV nå,
SQL/HTTP senere). Endringen er dokumentert i docs/2026-07-04-I3-brief.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MM6BWb1hWmJZuXFZ7rjxT
Step 1 of the loop, built from method-spec §3 alone:
- okf.py (pure stdlib): frontmatter parse, deterministic/tolerant/boundary-checked
index navigation, bundle_context rendering with type:verdict exclusion
- experience.py: CandidateFeatures from the IR projection, §4.2 id minting,
structural ranking (0.60·Jaccard + 0.25·type + 0.15·magnitude bucket),
first-write-wins store, bundle seeding with the realization marker,
fold-before-generation (empty retrieval → base unchanged)
Load-bearing (§11), each proved RED on detach: verdict-layer exclusion, fold,
seed learning fields, agent-toolkit import guard. 76/76 without an API key;
ruff + mypy --strict clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QdSfQdND84oeq2mbjueLTS