portfolio-optimiser/tests/test_bundle_id_reconciliation_loadbearing.py
Kjell Tore Guttormsen 842c51401d 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>
2026-09-02 21:04:04 +02:00

275 lines
13 KiB
Python

"""Step 10-11 (Session 4) - ONE bundle-id rule, and a cross-base collision that stops being silent.
**The defect, measured 2026-09-02.** Two modules derived a base's id by hand and identically:
``explore._bundle_index`` (``Path(raw).name``) and ``run_mandate_across_bundles`` (``run.py:1514``,
a second copy of the same line with its own refusal beside it). Neither consulted what the base
itself says. A corpus that declares its own id in ``index.md`` -- the form OKF SPEC leaves open --
would have been mounted under a directory name that disagreed with it, and every artefact the run
stamped would have named the mount while the base named itself something else. Two copies of one
derivation rule is the ko-(p) drift class; a derivation that ignores the artefact is worse, because
nothing ever disagrees out loud.
**Operator decision B1 (D6 ratified), and why ``origin`` has THREE values.** Identity is the pair
``(bundle_id, concept_id)``, and ``bundle_id`` is read from the CONCEPT's own frontmatter first,
with the root ``index.md`` as fallback, and the mount's basename as the last resort. ``origin`` is
REQUIRED WITHOUT DEFAULT for ``cost_baseline_anchored``'s reason -- both defaults would lie about an
event -- and it is not a boolean, because a caller that cannot tell "the concept said so" from "we
fell back twice" has been handed a stamp it cannot audit.
**MEASURED before building, and it decides the shape of this file (nevner stated).** No file
anywhere under ``shared/``, ``src/`` or ``tests/`` declares ``bundle_id`` in frontmatter: zero hits
for ``^bundle_id`` against a known-positive control of 31 files carrying ``^type:`` under
``shared/examples/``. Every base in the repo therefore resolves ``mount-derived`` today, and the 27
``bundle_dirs=`` call sites stay green. That makes ``declared-concept`` and ``declared-index``
DEFENSIVE branches (the ``budget_stop`` precedent): they are driven from CRAFTED bases here,
because nothing else in the suite would keep them alive.
**``explore._bundle_index`` stays PURE, and that is a correction from review, measured.**
``tests/test_explore_loadbearing.py:750`` passes ``/tmp/base-a`` and ``:764`` passes
``/tmp/one/shared-name`` -- directories that do not exist -- and both expect an ``ExplorationError``
about ids. Reading ``index.md`` there would raise on I/O before any id logic ran. Reconciliation
therefore happens where a base is actually OPENED: ``explore.read_bundle``, ``run_project``'s
bundle arm, and the dispatcher.
**A base with no readable ``index.md`` is NOT "undeclared".** ``navigate_bundle``'s fail-fast
propagates unchanged; reading an unreadable base as "it declares nothing" is the tolerant-read-
widens-the-answer defect research topic 2 measured in SPARQL's ``SILENT``.
Arms: (a) the CONTROL, a shipped base resolving ``mount-derived`` * (b) a declared, agreeing root
index * (c) a declaring CONCEPT beating a declaring index -- the ordering discriminator * (d) the
three origins are pairwise distinct, read off the three arms above rather than off a literal *
(e) disagreement refused BY NAME * (f) disagreement refused through the CLI with ZERO model calls *
(g) an unreadable index refuses instead of falling back * (h)-(j) the same helper is reached from
all three doors that open a base.
"""
from __future__ import annotations
import shutil
from pathlib import Path
from typing import Any
import pytest
from portfolio_optimiser import explore, okf, run
from portfolio_optimiser.mandate import Approach, Mandate
from portfolio_optimiser.simulation import ScriptedChatClient
_EXAMPLES = Path(__file__).resolve().parents[1] / "shared" / "examples"
_BYGG = _EXAMPLES / "bygg-energi-mikro"
_PID = "BYGG-KONTOR-NORD"
def _base_copy(tmp_path: Path, *, name: str = "bygg-energi-mikro") -> Path:
"""A throwaway copy of a shipped base. Mutations of fixture content NEVER touch the git-tracked
tree (the repo's ``shutil.copytree`` discipline)."""
dst = tmp_path / name
shutil.copytree(_BYGG, dst)
return dst
def _declare(path: Path, value: str) -> None:
"""Insert a ``bundle_id`` line into an existing frontmatter block, after the opening ``---``."""
lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
assert lines[0].startswith("---"), f"{path} has no frontmatter block to declare into"
lines.insert(1, f"bundle_id: {value}\n")
path.write_text("".join(lines), encoding="utf-8")
# --- (a) the CONTROL: nothing declares, so the mount answers ------------------------------------
def test_a_base_that_declares_nothing_resolves_from_the_mount() -> None:
"""(a) CONTROL. Every base shipped today takes this branch -- measured: zero ``^bundle_id``
declarations against a known-positive control of 31 files carrying ``^type:``. Without this arm
the two declared arms below could not be read as the exceptions they are."""
resolved = okf.reconcile_bundle_id(str(_BYGG))
assert resolved.id == "bygg-energi-mikro"
assert resolved.origin == "mount-derived"
# --- (b)/(c) the two declared origins ------------------------------------------------------------
def test_a_declaring_root_index_is_reconciled_and_marked_declared_index(tmp_path: Path) -> None:
"""(b) DEFENSIVE (``budget_stop`` precedent): no shipped index declares the key, so this branch
is crafted or it is untested."""
base = _base_copy(tmp_path)
_declare(base / "index.md", base.name)
resolved = okf.reconcile_bundle_id(str(base))
assert resolved.id == base.name
assert resolved.origin == "declared-index"
def test_a_declaring_concept_beats_a_declaring_index(tmp_path: Path) -> None:
"""(c) The ORDERING discriminator, and the reason both files declare the SAME (agreeing) value:
with only the concept declaring, an index-first implementation would fall through to
``mount-derived`` and could be mistaken for a bug elsewhere. With BOTH declaring, index-first
answers ``declared-index`` and concept-first answers ``declared-concept`` -- two implementations
that differ in exactly the field under test, which is what makes the arm falsifiable."""
base = _base_copy(tmp_path)
concept = next(p for p in sorted(base.rglob("*.md")) if p.name != "index.md")
_declare(base / "index.md", base.name)
_declare(concept, base.name)
resolved = okf.reconcile_bundle_id(str(base), concept_name=concept.relative_to(base).as_posix())
assert resolved.id == base.name
assert resolved.origin == "declared-concept"
def test_the_three_origins_are_pairwise_distinct(tmp_path: Path) -> None:
"""(d) B1's own reason for three values rather than a boolean: a caller must be able to tell
"the concept said so" from "the index said so" from "we fell back twice". Read off the three
RESOLUTIONS, never off a literal list -- an assert on a ``Literal``'s members is a static
property of the type that no runtime mutation can redden (the M8 correction)."""
declared_index = _base_copy(tmp_path, name="only-index")
_declare(declared_index / "index.md", "only-index")
declared_concept = _base_copy(tmp_path, name="with-concept")
target = next(p for p in sorted(declared_concept.rglob("*.md")) if p.name != "index.md")
_declare(target, "with-concept")
origins = {
okf.reconcile_bundle_id(str(_BYGG)).origin,
okf.reconcile_bundle_id(str(declared_index)).origin,
okf.reconcile_bundle_id(
str(declared_concept),
concept_name=target.relative_to(declared_concept).as_posix(),
).origin,
}
assert len(origins) == 3, f"two sources collapsed onto one origin: {sorted(origins)}"
# --- (e)/(f) disagreement is refused, and refused EARLY -------------------------------------------
def test_a_base_that_disagrees_with_its_mount_is_refused_by_name(tmp_path: Path) -> None:
"""(e) The refusal itself. ``BundleIdMismatch`` subclasses ``ValueError`` deliberately, so it
lands on the CLI's refusal tuple and hosting's 400 arm rather than the crash channel --
``ExplorationError`` is a ``RuntimeError`` and would give a traceback and a 500."""
base = _base_copy(tmp_path)
_declare(base / "index.md", "a-name-the-mount-does-not-carry")
with pytest.raises(okf.BundleIdMismatch) as excinfo:
okf.reconcile_bundle_id(str(base))
message = str(excinfo.value)
assert "a-name-the-mount-does-not-carry" in message and base.name in message
assert issubclass(okf.BundleIdMismatch, ValueError)
def test_the_cli_refuses_a_disagreeing_base_before_it_spends_a_single_model_call(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""(f) The assert is on ZERO model calls, never on the exit code alone: a refusal that arrives
AFTER the spend looks identical at rc 1 (the M12 signature, and the hoist idiom
``test_explore_callsites_loadbearing.py`` already uses twice)."""
monkeypatch.delenv("PORTFOLIO_MODEL_MAP", raising=False)
monkeypatch.delenv("PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT", raising=False)
base = _base_copy(tmp_path)
_declare(base / "index.md", "not-the-mount")
sink: list[str] = []
def counting_factory(profile: Any) -> Any:
return lambda role: ScriptedChatClient(sink=sink, role=role, default_reply="ok")
monkeypatch.setattr("portfolio_optimiser.run._default_factory", counting_factory)
rc = run.main([_PID, "--docs-dir", str(base), "--bundle-dir", str(base)])
assert rc == 1
assert sink == [], (
"the run reached the model before the mount was reconciled -- the base was paid for "
f"before it was refused ({len(sink)} calls)"
)
# --- (g) absence is not a declaration ------------------------------------------------------------
def test_an_unreadable_index_refuses_instead_of_falling_back_to_the_mount(tmp_path: Path) -> None:
"""(g) The tolerant-read trap, refused. A base whose index cannot be read is UNKNOWN, not
undeclared; answering ``mount-derived`` there would widen the answer on missing evidence."""
empty = tmp_path / "no-index-here"
empty.mkdir()
with pytest.raises(ValueError, match="index.md"):
okf.reconcile_bundle_id(str(empty))
# --- (h)-(j) one helper, reached from all three doors ---------------------------------------------
@pytest.fixture
def _spy(monkeypatch: pytest.MonkeyPatch) -> list[str]:
"""Record every reconciliation while delegating to the real one. A private basename copy at a
call site leaves this list empty, which is the only witness that distinguishes "reconciled" from
"still derived by hand" (mutation M11)."""
seen: list[str] = []
real = okf.reconcile_bundle_id
def spy(bundle_dir: Any, **kwargs: Any) -> Any:
seen.append(str(bundle_dir))
return real(bundle_dir, **kwargs)
monkeypatch.setattr(okf, "reconcile_bundle_id", spy)
return seen
def test_explore_read_bundle_reconciles_the_base_it_opens(_spy: list[str]) -> None:
"""(h) The tool is driven DIRECTLY: measured in okt 56, a scripted client returns TEXT and never
emits a tool call, so a gate that only drove ``explore()`` would never enter this body."""
tools = {t.name: t for t in explore.navigator_tools((str(_BYGG),))}
assert tools["read_bundle"].func(bundle_id=_BYGG.name) != ""
assert str(_BYGG) in _spy
@pytest.mark.asyncio
async def test_run_project_reconciles_the_base_its_bundle_arm_opens(_spy: list[str]) -> None:
"""(i) The pipeline door. ``run_project``'s bundle arm is where the project, the stage-0
baseline and the read context are all derived from one base -- the id must come from the same
reconciliation, not from a fourth private copy."""
await run.run_project(
_PID,
docs_dir=str(_BYGG),
bundle_dir=str(_BYGG),
client_factory=lambda role: ScriptedChatClient(role=role, default_reply="ok"),
live_dry_run=True,
)
assert str(_BYGG) in _spy
@pytest.mark.asyncio
async def test_the_dispatcher_refuses_a_disagreeing_base_before_it_starts_any_run(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""(j) The dispatcher door, and it needs its OWN discriminator.
A declared id that AGREES with the mount is by construction the basename, so
``reconcile_bundle_id(raw).id`` and ``Path(raw).name`` return the same string on every base
that resolves at all -- an assert on the routed id could not tell the two implementations
apart. What CAN: a disagreeing base must be refused while assembling the routing table, i.e.
BEFORE the first ``run_project`` is dispatched. Left as a private basename copy, routing
succeeds and the first base is started; ``run_project``'s own reconciliation would refuse it,
but only after the run had begun -- the M11/M12 pairing, one level up.
"""
base = _base_copy(tmp_path)
_declare(base / "index.md", "a-name-the-mount-does-not-carry")
started: list[str] = []
async def counting_run_project(*args: Any, **kwargs: Any) -> Any:
started.append(str(kwargs.get("bundle_dir")))
raise AssertionError("a run was dispatched against an unreconciled base")
monkeypatch.setattr(run, "run_project", counting_run_project)
with pytest.raises(okf.BundleIdMismatch):
await run.run_mandate_across_bundles(
Mandate(
objective="o",
approaches=(Approach(id="a", label="A", bundle_id=base.name),),
allow_own_proposals=False,
),
(str(base),),
)
assert started == [], "the dispatcher started a run before reconciling the base it routed to"