feat(okf): one bundle-id rule, reconciled against the mount and origin-marked
[skip-docs] — the invariant row for this plan lands in Step 13, where the mutations that force it have been measured. Documenting a seam before its measurement is the claim-without-evidence class this repo writes rows against. Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
60e51ab76c
commit
842c51401d
5 changed files with 388 additions and 4 deletions
|
|
@ -846,6 +846,101 @@ def navigate_bundle(bundle_dir: str) -> Bundle:
|
|||
return Bundle(dir=bundle_dir, files=tuple(files), skipped=tuple(skipped))
|
||||
|
||||
|
||||
class BundleIdMismatch(ValueError):
|
||||
"""A base declares a ``bundle_id`` its mount does not carry.
|
||||
|
||||
A ``ValueError`` DELIBERATELY: it must land on ``run.main``'s refusal tuple and on hosting's
|
||||
400 arm rather than on the crash channel. ``ExplorationError`` is a ``RuntimeError`` and would
|
||||
give an operator a traceback and an automated caller a 500 — a configuration mistake dressed
|
||||
as a server fault.
|
||||
"""
|
||||
|
||||
|
||||
#: Which source answered. THREE values, not a boolean (operator decision B1): a caller that cannot
|
||||
#: tell "the concept said so" from "we fell back twice" has been handed a stamp it cannot audit.
|
||||
BundleIdOrigin = Literal["declared-concept", "declared-index", "mount-derived"]
|
||||
|
||||
_BUNDLE_ID_KEY = "bundle_id"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ResolvedBundleId:
|
||||
"""A base's id together with WHERE it came from.
|
||||
|
||||
``origin`` is REQUIRED WITHOUT DEFAULT, for the reason ``ProvenanceStamp.cost_baseline_anchored``
|
||||
is: both defaults would lie about an event. Defaulting to ``"mount-derived"`` would let a
|
||||
resolver that never read the base claim it had; defaulting to a declared value would claim a
|
||||
declaration that never happened.
|
||||
"""
|
||||
|
||||
id: str
|
||||
origin: BundleIdOrigin
|
||||
|
||||
|
||||
def _declared_bundle_id(frontmatter: dict[str, str]) -> str:
|
||||
"""The declared id, de-quoted through ``unquote_scalar`` — this repo's ONE de-quoting rule."""
|
||||
return unquote_scalar(frontmatter.get(_BUNDLE_ID_KEY, "")).strip()
|
||||
|
||||
|
||||
def _reconciled(
|
||||
declared: str, mount: str, origin: BundleIdOrigin, bundle_dir: str
|
||||
) -> ResolvedBundleId:
|
||||
if declared != mount:
|
||||
raise BundleIdMismatch(
|
||||
f"knowledge base {bundle_dir!r} declares bundle_id {declared!r} but is mounted as "
|
||||
f"{mount!r}; an approach names a base by that id, so a run would evaluate against one "
|
||||
f"and report the other (source: {origin})"
|
||||
)
|
||||
return ResolvedBundleId(id=declared, origin=origin)
|
||||
|
||||
|
||||
def reconcile_bundle_id(
|
||||
bundle_dir: str | Path, *, concept_name: str | None = None
|
||||
) -> ResolvedBundleId:
|
||||
"""Resolve a base's ``bundle_id`` against the mount it was opened from — the ONE rule.
|
||||
|
||||
``bundle_id`` denotes the corpus **as a mounted artefact**, never the production event that made
|
||||
it (operator decision D6). That denotation cannot be changed once artefacts bear the stamp,
|
||||
which is why it is written down here rather than left to each call site.
|
||||
|
||||
Resolution order is operator decision B1 — identity is the pair ``(bundle_id, concept_id)``, and
|
||||
the CONCEPT's own frontmatter is consulted first:
|
||||
|
||||
1. ``concept_name``'s frontmatter, when a concept is named and declares the key →
|
||||
``"declared-concept"``;
|
||||
2. the root ``index.md``'s frontmatter → ``"declared-index"``;
|
||||
3. the mount's basename → ``"mount-derived"``.
|
||||
|
||||
A declared id that DISAGREES with the mount raises ``BundleIdMismatch`` at whichever source
|
||||
declared it. A base whose root ``index.md`` cannot be read is **unknown, not undeclared**:
|
||||
``navigate_bundle``'s fail-fast propagates unchanged, because reading an unreadable base as
|
||||
"it declares nothing" would widen the answer on missing evidence.
|
||||
|
||||
MEASURED 2026-09-02: no file under ``shared/`` declares the key (zero ``^bundle_id`` hits
|
||||
against a known-positive control of 31 files carrying ``^type:``), so every base in this repo
|
||||
resolves ``mount-derived`` today and both declared branches are DEFENSIVE.
|
||||
|
||||
:raises BundleIdMismatch: a declared id the mount does not carry.
|
||||
:raises ValueError: the root ``index.md`` is missing or unreadable.
|
||||
"""
|
||||
root = str(bundle_dir)
|
||||
mount = Path(root).name
|
||||
if concept_name is not None:
|
||||
concept = _load_file(root, concept_name)
|
||||
if concept is None:
|
||||
raise ValueError(f"OKF bundle has no readable concept {concept_name!r}: {root!r}")
|
||||
declared = _declared_bundle_id(concept.frontmatter)
|
||||
if declared:
|
||||
return _reconciled(declared, mount, "declared-concept", root)
|
||||
index = _load_file(root, _INDEX_NAME)
|
||||
if index is None:
|
||||
raise ValueError(f"OKF bundle has no readable {_INDEX_NAME}: {root!r}")
|
||||
declared = _declared_bundle_id(index.frontmatter)
|
||||
if declared:
|
||||
return _reconciled(declared, mount, "declared-index", root)
|
||||
return ResolvedBundleId(id=mount, origin="mount-derived")
|
||||
|
||||
|
||||
def bundle_context(bundle: Bundle, *, dimension: str | None = None) -> str:
|
||||
"""Render a navigated bundle as agent read-context via progressive disclosure: the ``index.md``
|
||||
summary, then each concept file as ``## {type}: {title}\\n{body}``. ``type: verdict`` files are
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue