feat(okf): conform the context seam to the pulled navigation + stamp-integrity contracts

The commons pull (7aa53fc -> a2b57d2) rewrote method-spec §3 Step 1 and added two §11
seams. Measuring okf.py against the new normative text found six contradictions; this
closes all six, gated by the commons-owned nav-goldens that came with the pull.

method-spec §3 Step 1 (navigate_bundle / bundle_context):
- follow cross-links RECURSIVELY, depth-first in first-seen order (there was no
  recursion at all — only the root index's links were read, so no hierarchy was
  navigable even with the other fixes in place);
- resolve a leading `/` against the BUNDLE ROOT, anything else against the LINKING
  file's directory, and drop the retired "a path separator means out-of-bundle"
  heuristic, which conflated depth with escape and forbade valid nesting;
- de-duplicate on the RESOLVED path (`./a.md` == `a.md` == `/a.md`), which is also
  what terminates cycles;
- exclude index files by BASENAME at every level, so a nested index is navigation and
  never renders as content (flat rendering regardless of depth);
- bind index_summary to the ROOT index alone.

safe_resolve stays the sole in-/out-of-bundle test, fail-closed: a target that fails to
resolve for ANY reason is skipped, never raised.

ingest-spec §3 (write_concept_file): it is the repo's one authoring primitive that
materialises a concept file from caller-supplied frontmatter, so it now refuses the
COMPLETE ownership stamp (`generated: true` + `ingest_manifest`) with IngestStampError,
while permitting either field alone. A validation, never a repair — nothing is written.

Gates (tests/test_okf.py, 529 -> 537):
- nav-golden-hierarchy and nav-golden-escape compared against the shipped
  expected-read-context.md fasit (trailing-whitespace normalisation only, which the
  fixture README explicitly permits; internal blank-line structure stays gated);
- traversal order pinned separately from the rendered output, so a right-looking render
  from a wrong walk still fails;
- unit seams for the recursion in isolation, resolved-path dedup, and the leading-`/`
  rule's breach case (a real out-of-bundle file addressed by its absolute path).

Load-bearing MEASURED, not asserted: seven mutations each go red — detach the recursion,
restore the separator prefilter, dedup on the raw target, read `/` as filesystem-absolute,
render nested index bodies as content, drop the stamp guard, and the fully naive navigator
with no boundary check (which is what makes the `/`-trap test bite). okf.py restored from a
checksum-verified copy after each.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WetWTHpdRbqinN5XHFTaTb
This commit is contained in:
Kjell Tore Guttormsen 2026-07-31 21:07:29 +02:00
commit 0d50ab89d3
3 changed files with 288 additions and 29 deletions

View file

@ -36,6 +36,23 @@ Python ≥3.10. MAF (`agent-framework-core` 1.9.0). Pakkehåndtering: `uv`. To b
fra denne konteksten: tidligere dommer når hypotese-prompten KUN via den gatede ExpeL-folden.
Load-bearing: `test_bundle_context_excludes_verdict_layer` + empty-store-kontrollen i
`test_step1_expel_loadbearing.py` (realiseringssignalet lekker aldri inn via kontekst).
- **Navigasjons-kontrakten er hierarkisk, og escape — ikke dybde — er forbudt** (`method-spec`
§3 Steg 1): `navigate_bundle` følger cross-links REKURSIVT, dybde-først i først-sett-rekkefølge;
ledende `/` betyr **bundle-rot** (aldri filsystem-absolutt), alt annet er relativt til den
LENKENDE filas katalog; dedup skjer på **resolvert** sti (så `./a.md` == `a.md`, og sykler
termineres). `safe_resolve` er den ENESTE inn-/ut-av-bundle-testen (fail-closed) — den erstattet
den pensjonerte «separator = utenfor bundelen»-heuristikken, som forvekslet dybde med escape.
Manglende `index.md` er feil KUN i bundle-rota (navigasjon følger lenker, aldri katalog-enumerering).
Rendering er FLAT uansett dybde; nestede `index.md` er navigasjon, ikke innhold. Gaten er
commons-eide nav-goldens (`shared/examples/nav-golden-*/expected-read-context.md`, byte-nivå
fasit): `test_nav_golden_hierarchy_*` (positiv) + `test_nav_golden_escape_*` (negativ — en gate
som bare kan bli grønn beviser ingenting).
- **Kuraterte skrivere kan ikke forfalske ingest-stempelet** (`ingest-spec` §3): `write_concept_file`
er repoets ene authoring-primitiv som materialiserer en konseptfil fra CALLER-oppgitt frontmatter,
og avviser derfor det KOMPLETTE eierskaps-stempelet (`generated: true` + `ingest_manifest`) med
`IngestStampError` — mens hver halvdel alene er lovlig (kuratert innhold kan bære ett
provenance-felt). Validering, ALDRI reparasjon: ingenting skrives. Uten dette kunne en kuratert fil
bli stille slettet av en senere re-materialisering, som fjerner nøyaktig det som bærer stempelet.
- **Stoppkriterier + budsjett-tak påkrevd ved oppstart** (fail-fast, aldri ubegrenset loop).
- **Group Chat maker-checker** som debatt-default (IKKE Magentic, som er eksperimentell).
- **To falsifiserere, samme kandidat (Steg 3/4, målbilde §2/§6):** den deterministiske validatoren

View file

@ -1,19 +1,29 @@
"""OKF (Open Knowledge Format) bundle navigation — framework-neutral, D7-portable context seam.
Reads a bundle the way OKF intends (progressive disclosure): start at ``index.md``, follow
intra-bundle cross-links, parse each file's YAML frontmatter, classify by the one required
``type`` field. **NO** ``agent_framework``, **NO** ``mcp`` pure stdlib, so the SAME navigation
serves both the MAF and the Claude-SDK implementations unchanged (målbilde §4 vendor-neutrality).
intra-bundle cross-links **recursively, depth-first in first-seen link order**, parse each file's
YAML frontmatter, classify by the one required ``type`` field. **NO** ``agent_framework``, **NO**
``mcp`` pure stdlib, so the SAME navigation serves both the MAF and the Claude-SDK
implementations unchanged (målbilde §4 vendor-neutrality).
Link resolution follows ``shared/method-spec.md`` §3 Step 1: a leading ``/`` denotes the **bundle
root** (NEVER a filesystem-absolute path), any other form is relative to the LINKING FILE's own
directory so a bundle may be a hierarchy. It is **escape, not depth**, that is forbidden; this
resolve-and-boundary-check replaced the old "a path separator means out-of-bundle" heuristic, which
conflated the two and forbade valid nesting. Repeated links de-duplicate on the RESOLVED path, so
``./a.md`` and ``a.md`` are one entry and cycles terminate.
Robustness is part of the spec (OKF SPEC §4): consumers MUST tolerate broken links and unknown
fields. A link to a missing file or one escaping the bundle is silently skipped, never raised.
Path-safety reuses ``retrieval.safe_resolve`` (also pure stdlib): each cross-link is canonicalised
and boundary-checked against the bundle dir, fail-closed.
fields. A target that fails to resolve for ANY reason (missing file, invalid path component,
escape) is silently skipped, never raised. Path-safety reuses ``retrieval.safe_resolve`` (also pure
stdlib): each cross-link is canonicalised and boundary-checked against the bundle dir, fail-closed
the SOLE in-/out-of-bundle test.
"""
from __future__ import annotations
import json
import posixpath
import re
from dataclasses import dataclass
from pathlib import Path
@ -23,8 +33,8 @@ from portfolio_optimiser.retrieval import PathSecurityError, safe_resolve
_INDEX_NAME = "index.md"
_IR_PROJECTION = "validator-input.json"
# Intra-bundle markdown cross-links: ``](target.md)``. Targets with a path separator (``/``) are
# treated as out-of-bundle and skipped (only same-dir bundle files are navigated).
# Intra-bundle markdown cross-links: ``](target.md)``. A path separator is NOT a rejection reason —
# ``_resolve_target`` decides in-/out-of-bundle, and only escape is refused (method-spec §3 Step 1).
_LINK_RE = re.compile(r"\]\(([^)]+\.md)\)")
@ -76,7 +86,8 @@ class Bundle:
@property
def index_summary(self) -> str:
"""The index body — the progressive-disclosure entry point (not whole-bundle stuffing)."""
"""The ROOT index body — the progressive-disclosure entry point (not whole-bundle stuffing).
Bound to the root alone: a nested ``a/index.md`` is navigation, never summary prose."""
return next((f.body for f in self.files if f.name == _INDEX_NAME), "")
@property
@ -88,8 +99,16 @@ class Bundle:
def context_files(self) -> list[BundleFile]:
"""The non-index, non-``verdict`` concept files — the bodies that form the agent context.
The verdict layer is deliberately EXCLUDED: prior verdicts reach the hypothesis prompt only
through the gated ExpeL fold, never by stuffing them into the read-context (målbilde §4)."""
return [f for f in self.files if f.name != _INDEX_NAME and f.type != "verdict"]
through the gated ExpeL fold, never by stuffing them into the read-context (målbilde §4).
The exclusion is a TYPE CHECK on each reached file, applied at EVERY level never a
property of the link graph, so a mislabelled or injected navigation edge cannot smuggle a
nested verdict in. Index files are dropped by BASENAME at every level too: a nested
``a/index.md`` is navigation, not content."""
return [
f
for f in self.files
if posixpath.basename(f.name) != _INDEX_NAME and f.type != "verdict"
]
@property
def hypothesis(self) -> BundleFile | None:
@ -98,8 +117,8 @@ class Bundle:
def _load_file(bundle_dir: str, name: str) -> BundleFile | None:
"""Resolve ``name`` within ``bundle_dir`` and read it, or ``None`` if missing / escaping the
bundle (OKF §4 broken-link tolerance + fail-closed path-safety)."""
"""Resolve the bundle-relative ``name`` within ``bundle_dir`` and read it, or ``None`` if
missing / escaping the bundle (OKF §4 broken-link tolerance + fail-closed path-safety)."""
try:
resolved = Path(safe_resolve(bundle_dir, name))
except PathSecurityError:
@ -110,23 +129,60 @@ def _load_file(bundle_dir: str, name: str) -> BundleFile | None:
return BundleFile(name=name, type=fm.get("type", ""), frontmatter=fm, body=_read_body(resolved))
def _resolve_target(bundle_dir: str, from_name: str, target: str) -> tuple[str, str] | None:
"""Resolve one cross-link into ``(bundle-relative posix name, canonical path)``, or ``None``
when it fails to resolve for ANY reason (escape, invalid path component) the caller skips,
never raises (method-spec §3 Step 1).
A leading ``/`` denotes the BUNDLE ROOT, not the filesystem root: an implementation that let
``os.path.join`` see an absolute target would either escape to the real filesystem path or
refuse a legitimate root-relative link. Any other form resolves against the LINKING FILE's own
directory, so an index links its immediate children one segment at a time."""
if target.startswith("/"):
rel = posixpath.normpath(target.lstrip("/"))
else:
rel = posixpath.normpath(posixpath.join(posixpath.dirname(from_name), target))
try:
return rel, safe_resolve(bundle_dir, rel)
except PathSecurityError:
return None
def _walk(bundle_dir: str, current: BundleFile, files: list[BundleFile], seen: set[str]) -> None:
"""Follow ``current``'s cross-links depth-first in first-seen order, appending each newly
reached file and recursing into it. De-duplication is on the CANONICAL RESOLVED path (so
``./a.md``, ``a.md`` and ``/a.md`` are one entry), which is also what terminates cycles."""
for target in _LINK_RE.findall(current.body):
resolved = _resolve_target(bundle_dir, current.name, target)
if resolved is None:
continue
rel, canonical = resolved
if canonical in seen:
continue
seen.add(canonical)
linked = _load_file(bundle_dir, rel)
if linked is None:
continue # broken link: tolerated, never raised (OKF §4)
files.append(linked)
_walk(bundle_dir, linked, files, seen)
def navigate_bundle(bundle_dir: str) -> Bundle:
"""Navigate the OKF bundle from ``index.md``: parse the index, follow its intra-bundle ``.md``
cross-links, and read each linked file's frontmatter + body. Deterministic: index first, then
links in first-seen order, de-duplicated. Broken / escaping links are skipped (§4). Raises
``ValueError`` only when ``index.md`` itself is unreadable (a bundle has no entry point)."""
"""Navigate the OKF bundle from ``index.md``: parse the root index, then follow intra-bundle
``.md`` cross-links RECURSIVELY, depth-first in first-seen link order, reading each reached
file's frontmatter + body. Fully deterministic. Broken / escaping links are skipped (§4).
Navigation follows LINKS ONLY a directory is never enumerated. Hence the missing-``index.md``
error binds the bundle ROOT alone (a bundle has no entry point without it); an intermediate
directory reached by a link needs no ``index.md`` of its own, and content nothing links to is
simply unreachable, not an error. Raises ``ValueError`` only for the unreadable root index."""
index = _load_file(bundle_dir, _INDEX_NAME)
if index is None:
raise ValueError(f"OKF bundle has no readable {_INDEX_NAME}: {bundle_dir!r}")
files: list[BundleFile] = [index]
seen = {_INDEX_NAME}
for target in _LINK_RE.findall(index.body):
if "/" in target or target in seen:
continue # only same-dir bundle files; de-dup repeated links
seen.add(target)
linked = _load_file(bundle_dir, target)
if linked is not None:
files.append(linked)
root = _resolve_target(bundle_dir, _INDEX_NAME, _INDEX_NAME)
seen = {root[1]} if root is not None else set()
_walk(bundle_dir, index, files, seen)
return Bundle(dir=bundle_dir, files=tuple(files))
@ -140,15 +196,23 @@ def bundle_context(bundle: Bundle, *, dimension: str | None = None) -> str:
When ``dimension`` is given, only concept files whose frontmatter ``dimension`` matches or that
carry no ``dimension`` at all (un-scoped knowledge is never dropped) are rendered; the default
``dimension=None`` renders every concept file, byte-identical to the prior behavior. ``dimension``
is a plain ``str`` (not the ``Dimension`` type) so ``okf`` stays MAF-free and import-cycle-free."""
sections = [bundle.index_summary]
is a plain ``str`` (not the ``Dimension`` type) so ``okf`` stays MAF-free and import-cycle-free.
Rendering is FLAT regardless of nesting depth (method-spec §3 Step 1): directory structure is
navigation, not presentation, so a nested concept file renders as the same
``## {type}: {title}`` section a root file would — there is no level heading, and nested index
bodies do not appear at all. The serialisation (heading, blank line, body; sections separated by
one blank line) is what the commons nav-golden ``expected-read-context.md`` fasit compares
against see ``tests/test_okf.py`` nav-golden gates."""
sections = [bundle.index_summary.strip("\n")]
for f in bundle.context_files:
if dimension is not None:
file_dim = f.frontmatter.get("dimension")
if file_dim is not None and file_dim != dimension:
continue
title = f.frontmatter.get("title", f.name).strip('"')
sections.append(f"## {f.type or 'document'}: {title}\n{f.body}")
body = f.body.strip("\n")
sections.append(f"## {f.type or 'document'}: {title}\n\n{body}")
return "\n\n".join(s for s in sections if s.strip())
@ -163,11 +227,42 @@ def render_frontmatter(frontmatter: dict[str, str]) -> str:
return "\n".join(f"{key}: {' '.join(str(value).split())}" for key, value in frontmatter.items())
class IngestStampError(ValueError):
"""A curated writer was handed frontmatter carrying the COMPLETE ingest ownership stamp
(``shared/ingest-spec.md`` §3). The stamp is the sole mark separating ingest-owned files from
curated ones, and re-materialization replaces exactly what carries it so a curated file that
forged it could be silently deleted by a later ingest run."""
def _carries_complete_ingest_stamp(frontmatter: dict[str, str]) -> bool:
"""Whether ``frontmatter`` carries BOTH halves of the ingest ownership stamp: ``generated:
true`` together with a non-empty ``ingest_manifest`` reference (ingest-spec §7).
The test is on the COMPLETE stamp, never on the individual field names curated content may
legitimately carry a single provenance field, and a verbatim round-trip of one half must keep
working. Values are compared the way ``parse_frontmatter`` yields them (line-oriented strings,
quotes retained), so surrounding quotes and case are normalised away here."""
generated = str(frontmatter.get("generated", "")).strip().strip('"').lower()
manifest = str(frontmatter.get("ingest_manifest", "")).strip().strip('"')
return generated == "true" and bool(manifest)
def write_concept_file(bundle_dir: str, name: str, frontmatter: dict[str, str], body: str) -> Path:
"""Write a typed OKF concept file (``---`` frontmatter + markdown body) into ``bundle_dir``,
path-safe via ``safe_resolve`` (fail-closed: a ``name`` escaping the bundle raises
``PathSecurityError``). Pure stdlib the D7-portable counterpart of ``navigate_bundle``'s read.
This is the repo's one authoring primitive that materialises a concept file from CALLER-SUPPLIED
frontmatter, so it is the surface ingest-spec §3's "no other writer may forge the stamp" binds:
a frontmatter carrying the complete ingest stamp raises ``IngestStampError`` and NOTHING is
written. A validation, never a repair the caller is told, not silently corrected.
Returns the written path."""
if _carries_complete_ingest_stamp(frontmatter):
raise IngestStampError(
"refusing to write a curated concept file carrying the COMPLETE ingest ownership stamp "
"(generated: true + ingest_manifest); only the ingest materializer may claim it "
"(ingest-spec §3) — either field alone is permitted"
)
resolved = Path(safe_resolve(bundle_dir, name))
resolved.parent.mkdir(parents=True, exist_ok=True)
resolved.write_text(f"---\n{render_frontmatter(frontmatter)}\n---\n\n{body}", encoding="utf-8")

View file

@ -29,7 +29,26 @@ _MAF_FREE_MODULES = [
"semretrieval.py",
]
BUNDLE_DIR = Path(__file__).resolve().parents[1] / "shared" / "examples" / "bygg-energi-mikro"
_EXAMPLES_DIR = Path(__file__).resolve().parents[1] / "shared" / "examples"
BUNDLE_DIR = _EXAMPLES_DIR / "bygg-energi-mikro"
def _normalise_trailing_ws(text: str) -> str:
"""The ONLY normalisation the nav-golden gate applies, and the README explicitly permits it
("byte-exact or after trailing-whitespace normalization"): per-line trailing whitespace and the
file's terminating newline. Internal blank-line structure is NOT normalised — the serialisation
shape (``## {type}: {title}`` + blank line + body, sections separated by one blank line) stays
gated, so a renderer that drops or doubles a separator still goes RED."""
return "\n".join(line.rstrip() for line in text.rstrip("\n").split("\n"))
def _nav_golden(case: str) -> tuple[str, str]:
"""A commons-owned nav-golden case: ``(bundle_dir, expected_read_context)``. The fixture class
is bundle-in / read-context-out the only shape that can express method-spec §3 Step 1's
cross-implementation property "two conformant implementations MUST produce an identical
read-context from the same bundle"."""
root = _EXAMPLES_DIR / case
return str(root / "bundle"), (root / "expected-read-context.md").read_text(encoding="utf-8")
def test_navigate_classifies_every_typed_file() -> None:
@ -84,6 +103,106 @@ def test_bundle_context_excludes_verdict_layer() -> None:
assert "progressiv disclosure" in context.lower() # the index summary is the entry point
def test_nav_golden_hierarchy_read_context_matches_commons_fasit() -> None:
"""CONFORMANCE GATE (method-spec §3 Step 1, commons-owned nav-golden). The positive case: the
rendered read-context of a HIERARCHICAL bundle must equal the shipped ``expected-read-context.md``
the cross-implementation fasit. One assertion covers the whole Q3 navigation contract at once:
recursive depth-first traversal, both link forms, resolved-path dedup, cycle termination, the
root-only binding of the missing-index rule, recursive verdict exclusion, and FLAT rendering
(nested index bodies are navigation, never content)."""
bundle_dir, expected = _nav_golden("nav-golden-hierarchy")
context = okf.bundle_context(okf.navigate_bundle(bundle_dir))
assert _normalise_trailing_ws(context) == _normalise_trailing_ws(expected)
def test_nav_golden_hierarchy_traversal_is_depth_first_first_seen() -> None:
"""The fasit above pins the RENDERED output; this pins the TRAVERSAL that produced it, so a
renderer that accidentally reproduced the right text from a wrong walk still goes RED. Order is
the commons README's trace verbatim: root index -> overview -> /a/index -> a/doc-a ->
a/verdict-nested (REACHED, then excluded by type) -> a/b/index -> a/b/doc-b, with ``/a/index.md``
and ``/overview.md`` deduped on their RESOLVED paths (cycle termination). ``c/orphan.md`` is
unreachable a link-following navigator never sees it, a directory-walking one wrongly would,
and ``c/`` having no ``index.md`` is NOT an error (that rule binds the bundle root alone)."""
bundle_dir, _ = _nav_golden("nav-golden-hierarchy")
bundle = okf.navigate_bundle(bundle_dir)
assert [f.name for f in bundle.files] == [
"index.md",
"overview.md",
"a/index.md",
"a/doc-a.md",
"a/verdict-nested.md",
"a/b/index.md",
"a/b/doc-b.md",
]
# Verdict exclusion is a TYPE check on each reached file, applied recursively — never a property
# of the link graph: the nested verdict IS navigated, and IS kept out of the context.
assert [f.name for f in bundle.verdicts] == ["a/verdict-nested.md"]
assert [f.name for f in bundle.context_files] == ["overview.md", "a/doc-a.md", "a/b/doc-b.md"]
def test_nav_golden_escape_read_context_matches_commons_fasit() -> None:
"""CONFORMANCE GATE, negative case (a gate that can only pass proves nothing). Every link but
the first escapes the bundle; a conformant navigator skips them all, reads none of them, RAISES
NOTHING, and still returns the one valid sibling. The decoy really exists one level up."""
bundle_dir, expected = _nav_golden("nav-golden-escape")
bundle = okf.navigate_bundle(bundle_dir)
assert [f.name for f in bundle.files] == ["index.md", "valid.md"]
context = okf.bundle_context(bundle)
assert _normalise_trailing_ws(context) == _normalise_trailing_ws(expected)
# The decoy's CONTENT, not its filename: the name legitimately appears in the index body as the
# (skipped) link target, so asserting on the name would pass for a navigator that read the file.
decoy = (_EXAMPLES_DIR / "nav-golden-escape" / "SHOULD-NOT-BE-READ.md").read_text(
encoding="utf-8"
)
assert decoy.split("---")[-1].strip() not in context
def test_navigate_follows_links_recursively(tmp_path) -> None:
"""§11 "Navigation boundary", the RECURSION on its own: a pure same-directory chain
``index -> a.md -> b.md``. Deliberately flat, so it is isolated from link-form and boundary
concerns a navigator that only reads the ROOT index's links returns ``[index, a]`` and goes
RED here even though every target is a legal same-dir sibling."""
(tmp_path / "index.md").write_text("---\ntype: index\n---\n\n- [A](a.md)\n", encoding="utf-8")
(tmp_path / "a.md").write_text(
"---\ntype: project\n---\n\nA body\n\n- [B](b.md)\n", encoding="utf-8"
)
(tmp_path / "b.md").write_text("---\ntype: reference\n---\n\nB body\n", encoding="utf-8")
bundle = okf.navigate_bundle(str(tmp_path))
assert [f.name for f in bundle.files] == ["index.md", "a.md", "b.md"]
def test_navigate_dedups_on_resolved_path(tmp_path) -> None:
"""Dedup is on the RESOLVED path, not the raw target string: ``./a.md``, ``a.md`` and ``/a.md``
are one entry. A navigator de-duplicating raw targets reads (and renders) the same file three
times and goes RED."""
(tmp_path / "index.md").write_text(
"---\ntype: index\n---\n\n- [1](a.md)\n- [2](./a.md)\n- [3](/a.md)\n", encoding="utf-8"
)
(tmp_path / "a.md").write_text("---\ntype: project\n---\n\nA body\n", encoding="utf-8")
bundle = okf.navigate_bundle(str(tmp_path))
assert [f.name for f in bundle.files] == ["index.md", "a.md"]
def test_navigate_root_relative_link_is_bundle_root_not_filesystem(tmp_path) -> None:
"""§11 "Navigation boundary", the ratified leading-``/`` rule and its breach. A leading ``/``
denotes the BUNDLE ROOT, never a filesystem-absolute path so an index carrying the real
absolute path of a file OUTSIDE the bundle resolves to ``{bundle}/{that path}`` (missing ->
skipped), and the outside file is never opened. An implementation reading ``/`` as
filesystem-absolute exfiltrates it: that is the path-traversal breach the escape golden's
``/etc/passwd`` trap describes, made assertable here with a file this test owns."""
outside = tmp_path / "outside.md"
outside.write_text("---\ntype: secret\n---\n\nEXFIL-SENTINEL\n", encoding="utf-8")
bundle_dir = tmp_path / "bundle"
bundle_dir.mkdir()
(bundle_dir / "index.md").write_text(
f"---\ntype: index\n---\n\n- [trap]({outside.resolve().as_posix()})\n", encoding="utf-8"
)
bundle = okf.navigate_bundle(str(bundle_dir))
assert [f.name for f in bundle.files] == ["index.md"] # resolved under the bundle -> missing
assert all("EXFIL-SENTINEL" not in f.body for f in bundle.files)
assert "EXFIL-SENTINEL" not in okf.bundle_context(bundle)
def _dimension_bundle(tmp_path) -> str:
(tmp_path / "index.md").write_text(
"---\ntype: index\n---\n\n# Bundle\n\n"
@ -227,6 +346,34 @@ def test_render_frontmatter_single_lines_scalars(tmp_path) -> None:
assert parsed["decision"] == "approved" # the trailing key was NOT lost to a spurious ---
def test_write_concept_file_refuses_complete_ingest_stamp(tmp_path) -> None:
"""§11 "Stamp integrity (curated writers)" (ingest-spec §3, third bullet). The ingest stamp is
the SOLE mark separating ingest-owned files from curated ones and re-materialization deletes
what carries it. ``write_concept_file`` materialises a concept file from CALLER-SUPPLIED
frontmatter, so it is exactly the authoring primitive that could forge the stamp; it MUST refuse
the COMPLETE stamp and write NOTHING. A validation, never a repair: the file must not appear
stamp-stripped either."""
fm = {"type": "reference", "generated": "true", "ingest_manifest": "bygg@0123456789abcdef"}
with pytest.raises(okf.IngestStampError):
okf.write_concept_file(str(tmp_path), "forged.md", fm, "body\n")
assert not (tmp_path / "forged.md").exists() # refused, not silently repaired
def test_write_concept_file_permits_either_stamp_field_alone(tmp_path) -> None:
"""The check is on the COMPLETE stamp, never on the individual field names: curated content may
legitimately carry a single provenance field, and a genuine verbatim round-trip is preserved.
Both halves alone are written unchanged."""
okf.write_concept_file(str(tmp_path), "a.md", {"type": "reference", "generated": "true"}, "b\n")
okf.write_concept_file(
str(tmp_path),
"b.md",
{"type": "reference", "ingest_manifest": "bygg@0123456789abcdef"},
"b\n",
)
assert okf.parse_frontmatter(tmp_path / "a.md")["generated"] == "true"
assert okf.parse_frontmatter(tmp_path / "b.md")["ingest_manifest"] == "bygg@0123456789abcdef"
def _minimal_bundle(tmp_path) -> str:
(tmp_path / "index.md").write_text(
"---\ntype: index\n---\n\n# Bundle\n\n- [proj](bygg.md)\n", encoding="utf-8"