feat(profiles): materialize_bundle takes a keyword-only profile (req 6)
`OKF_V0_2` landed in D2 but was unreachable from outside: no door took a profile. This threads one through, keyword-only behind the `*` the signature already carried, so every three-positional call site stays source-compatible — which is what po-claude asked for, and what makes additivity a property of the signature rather than something a consumer measures. Nine sites, not the ~6 STATE claimed. The load-bearing one is the call at materialize.py:378: the CONTENT phase has accepted `profile` since D2, but the call site never passed one, so A-E3/A-E4/A-E5 were all unreachable. The other eight are the disk phase (ownership glob, index name, index maintenance, concept filenames) plus `generated_filename` in manifest.py. `link_in_index` is public and called from all three doors, so it gets `*, profile=DEFAULT` rather than having the lookup moved to the call site: doors B and C keep exactly the behaviour they had, and which profile THEY own stays an open question instead of being decided silently by a signature change. Byte-neutrality is proven, not asserted: `OKF_V0_2.paths is DEFAULT.paths` and `.index is DEFAULT.index`, and the golden suite is green. That identity is also why six of the nine sites cannot be proven reachable by any shipped-profile test — no assertion distinguishes two names for one object. A synthetic test-only profile renaming the index and the concept files closes that gap, so a site left on `DEFAULT` fails by name rather than passing quietly. Scope stated rather than glossed: the profile does NOT reach manifest type validation (`manifest.py:198` still reads `DEFAULT.types`; measured equal to `OKF_V0_2.types`, so nothing is hidden today), and `STRICT_V1` is not supported here — its index policy sets three judging fields the materializer does not honour. Both are named in the docstring. No `okf_version` anywhere: that lands once, at D5, when the §12 placement question closes. 550 tests pass (was 542). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tf2BbC8uSRVU4ApQ9NL7QR
This commit is contained in:
parent
ddc8f76446
commit
ed08ac15e9
3 changed files with 270 additions and 18 deletions
|
|
@ -15,7 +15,7 @@ from pathlib import Path
|
|||
from typing import Any, Union
|
||||
|
||||
from .errors import ManifestError
|
||||
from .profiles import DEFAULT
|
||||
from .profiles import DEFAULT, BundleProfile
|
||||
|
||||
_ID_PATTERN = re.compile(r"[a-z0-9][a-z0-9-]*\Z")
|
||||
|
||||
|
|
@ -59,13 +59,17 @@ class Manifest:
|
|||
extractions: tuple[Extraction, ...]
|
||||
|
||||
|
||||
def generated_filename(extraction_id: str) -> str:
|
||||
def generated_filename(extraction_id: str, *, profile: BundleProfile = DEFAULT) -> str:
|
||||
"""The concept filename for an extraction (spec §5).
|
||||
|
||||
The `ingest-` prefix keeps the namespace disjoint from `index.md` and
|
||||
`promoted-verdict-*` (spec §3) for every id the §4 grammar admits.
|
||||
|
||||
The prefix and suffix are the profile's, because the ownership scan globs
|
||||
on the same two values: a name built from one profile and scanned for under
|
||||
another is a file the library cannot recognise as its own.
|
||||
"""
|
||||
return f"{DEFAULT.paths.ingest_prefix}{extraction_id}{DEFAULT.paths.concept_suffix}"
|
||||
return f"{profile.paths.ingest_prefix}{extraction_id}{profile.paths.concept_suffix}"
|
||||
|
||||
|
||||
def load_manifest(path: Path) -> Manifest:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue