F3 and F4, the two findings the S7 acid test (session 98) reported and left. The order required both descriptions to be treated as PREMISES. One held; the other was felled before anything was built on it. F3 -- premise FELLED, asymmetry real. The order read arm C's two refused calls as "the path names a document that EXISTS". Measured against the base that ran: its root holds 27 directories named del-ii-bilag-N-... and 12 documents named inbox-del-ii-bilag-N-....md, and the requested path matches NEITHER -- it is the directory naming convention applied to a document whose real name carries an inbox- prefix. So the two live rounds were the UNKNOWN-path class, and this delivery does NOT recover them (gated). What IS real: read_file on a directory has named read_dir since session 95, while read_dir on a document named neither the rung nor the path. okf.DocumentPathRefused closes that one direction -- a ValueError, a SIBLING of BundlePathNotFound rather than a subclass, built from context_files (never files) and through the same in_dimension predicate the listing uses, quoting the document's REAL name so what it hands back resolves. F4 -- premise HELD, option (c) felled by measurement. All four live artefacts stamped cost_baseline_anchored: False and each arm invented its cost codes. derive_cost_baseline refuses against the delivered base: K2's price schedule is a pandoc SIMPLE table with ONE column header, so making --derive-cost-baseline reachable there would mean inventing a rule for an unmeasured form -- MAJOR-4's own honesty limit. Chose (b) over (a): --require-cost-baseline / run_project(require_cost_baseline=...), OPT-IN and never default, so every bundle without a cost-baseline.json runs unchanged. The gate sits where both branches have bound baseline and ABOVE the dry-run cut, so it fires on the free trip too and, on the paid one, before the first model call. Three CLI refusals by name, each with an rc-0 control. 12 mutations, all red against the WHOLE suite. Green control 1493/5 -> 1511/5 (+18 node-ids, 0 removed); golden demo-transcript.stdout BYTE-UNCHANGED (shasum -a 1 of the CONTENT = ea8c534773acdbe41ae68f2c55724d69aaf8be4f). No paid run: both findings measured offline. Measurement: docs/2026-09-08-f3-f4-nekten-og-forankringen.md Order: 20260908T020419Z-5837110336-from-portfolio-optimiser Co-Authored-By: Claude <Opus 5>
224 lines
10 KiB
Python
224 lines
10 KiB
Python
"""F3 - ``read_dir`` on a path that names a DOCUMENT answers with the rung that reads it.
|
|
|
|
**The measurement, and the premise it fells.** The live S7 acid test
|
|
(``docs/2026-09-07-syretest-s7-prepass-k2.md`` § 4) recorded arm C calling
|
|
``read_dir('del-ii-bilag-6-teknisk-oppsett')`` and then
|
|
``read_dir('del-ii-bilag-6-teknisk-oppsett.md')``; both were refused, and the two rounds came out
|
|
of a bounded round cap. The order reads that as "the path named a document that EXISTS (with or
|
|
without ``.md``)" and asks whether the refusal can name ``read_file`` and the real path, the way
|
|
``explore.DirectoryPathRefused`` already does in the other direction.
|
|
|
|
**Measured against the base that ran it, the premise is false.** That base holds 27 directories
|
|
named ``del-ii-bilag-N-...`` and 12 root documents named ``inbox-del-ii-bilag-N-....md``. The path
|
|
the model asked for matches NEITHER: it is the directory-naming convention applied to a document
|
|
whose real name carries an ``inbox-`` prefix. So the live two rounds were the UNKNOWN-path class,
|
|
not the wrong-RUNG class, and nothing in this file would have saved them - arm (g) is that fact,
|
|
gated, so no later reader can mistake this delivery for a fix of the measured cost.
|
|
|
|
**What IS real is the asymmetry.** ``read_file`` on a directory raises ``DirectoryPathRefused`` and
|
|
names ``read_dir``; ``read_dir`` on a document raised the generic "no such directory" and named
|
|
neither the rung that reads it nor the path it would take. The symmetry the order asked about is
|
|
absent in exactly one direction, and that is what this file closes.
|
|
|
|
The lookup is built from ``context_files``, NEVER ``files`` (arm (e)): a suggestion built from the
|
|
walk would name a ``type: verdict`` document by path - advertising, in a refusal, the one layer no
|
|
listing mentions and ``read_file`` refuses outright. It honours the ``dimension`` scope for the
|
|
same reason one rung up (arm (f)): naming a document the run would then refuse to open is the
|
|
"filter in name only" S2c measured, inverted.
|
|
|
|
Arms: (a) exact name * (b) the same name without ``.md`` * (c) an unknown path keeps its own
|
|
wording and never names ``read_file`` * (d) anti-vacuity: the named path is usable VERBATIM *
|
|
(e) the verdict layer is never named * (f) a foreign-dimension document is never named * (g) the
|
|
LIVE case is not this class * (h) the refusal is a ``ValueError``, not the crash channel.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from portfolio_optimiser import okf
|
|
from portfolio_optimiser.explore import navigator_tools
|
|
|
|
#: ASCII-clean throughout: a tool result is serialised with ``\uXXXX`` escapes, and a probe with
|
|
#: Norwegian characters was red against a working implementation in session 99.
|
|
_ALPHA = "notat-alpha"
|
|
_GAMMA = "inbox-notat-gamma"
|
|
|
|
|
|
def _tools(bundle_dir: Path, *, dimension: str | None = None) -> dict[str, Any]:
|
|
return {t.name: t for t in navigator_tools((str(bundle_dir),), dimension=dimension)}
|
|
|
|
|
|
def _read_dir(bundle_dir: Path, path: str, *, dimension: str | None = None) -> dict[str, Any]:
|
|
tools = _tools(bundle_dir, dimension=dimension)
|
|
return tools["read_dir"].func(bundle_id=bundle_dir.name, path=path)
|
|
|
|
|
|
def _read_file(bundle_dir: Path, path: str) -> str:
|
|
return _tools(bundle_dir)["read_file"].func(bundle_id=bundle_dir.name, path=path)
|
|
|
|
|
|
def _base(root: Path) -> Path:
|
|
"""A base carrying every class the refusal must tell apart: a plain concept document at the
|
|
top, a real directory, a ``type: verdict`` document, a foreign-dimension document, and a
|
|
document whose real name carries the ``inbox-`` prefix the live model dropped."""
|
|
base = root / "korpus"
|
|
(base / "arkiv").mkdir(parents=True)
|
|
(base / f"{_ALPHA}.md").write_text(
|
|
"---\ntype: concept\ntitle: Notat alpha\n---\n\nalpha body.\n", encoding="utf-8"
|
|
)
|
|
(base / f"{_GAMMA}.md").write_text(
|
|
"---\ntype: concept\ntitle: Notat gamma\n---\n\ngamma body.\n", encoding="utf-8"
|
|
)
|
|
(base / "dom-beta.md").write_text(
|
|
"---\ntype: verdict\ntitle: Dom beta\n---\n\nverdict body.\n", encoding="utf-8"
|
|
)
|
|
(base / "energi-notat.md").write_text(
|
|
"---\ntype: concept\ntitle: Energi\ndimension: energi\n---\n\nenergi body.\n",
|
|
encoding="utf-8",
|
|
)
|
|
(base / "arkiv" / "dok-a.md").write_text(
|
|
"---\ntype: concept\ntitle: Dokument A\n---\n\narkiv body.\n", encoding="utf-8"
|
|
)
|
|
(base / "arkiv" / "index.md").write_text(
|
|
"---\ntype: index\n---\n\n- [Dokument A](dok-a.md)\n", encoding="utf-8"
|
|
)
|
|
(base / "index.md").write_text(
|
|
"---\ntype: index\n---\n\n"
|
|
f"- [Notat alpha]({_ALPHA}.md)\n"
|
|
f"- [Notat gamma]({_GAMMA}.md)\n"
|
|
"- [Dom beta](dom-beta.md)\n"
|
|
"- [Energi](energi-notat.md)\n"
|
|
"- [arkiv](arkiv/index.md)\n",
|
|
encoding="utf-8",
|
|
)
|
|
return base
|
|
|
|
|
|
# --- (a)/(b) the wrong rung, answered as such -----------------------------------------------------
|
|
|
|
|
|
def test_read_dir_on_a_documents_exact_name_names_read_file_and_the_path(tmp_path: Path) -> None:
|
|
"""(a) The symmetry ``explore.DirectoryPathRefused`` already has in the other direction. A
|
|
refusal that only says "no" leaves the caller with the same next move it just made."""
|
|
base = _base(tmp_path)
|
|
|
|
with pytest.raises(okf.DocumentPathRefused) as excinfo:
|
|
_read_dir(base, f"{_ALPHA}.md")
|
|
|
|
message = str(excinfo.value)
|
|
assert "read_file" in message, "the refusal does not name the rung that reads a document"
|
|
assert f"{_ALPHA}.md" in message, "the refusal does not name the path read_file would take"
|
|
|
|
|
|
def test_the_suffix_is_not_what_makes_it_a_document(tmp_path: Path) -> None:
|
|
"""(b) The live model asked BOTH ways in two consecutive rounds. A refusal that only knew the
|
|
``.md`` form would answer one of them and not the other."""
|
|
base = _base(tmp_path)
|
|
|
|
with pytest.raises(okf.DocumentPathRefused) as excinfo:
|
|
_read_dir(base, _ALPHA)
|
|
|
|
message = str(excinfo.value)
|
|
assert "read_file" in message
|
|
assert f"{_ALPHA}.md" in message, (
|
|
"the refusal echoed the caller's path instead of the document's real name; a path that "
|
|
"never existed is worse than no path"
|
|
)
|
|
|
|
|
|
# --- (c) the two branches must not share their wording --------------------------------------------
|
|
|
|
|
|
def test_an_unknown_path_keeps_its_own_wording(tmp_path: Path) -> None:
|
|
"""(c) Two refusals that share a substring cannot be told apart by a test OR by a model. The
|
|
unknown-path branch is unchanged and must stay unable to claim a document exists."""
|
|
base = _base(tmp_path)
|
|
|
|
with pytest.raises(okf.BundlePathNotFound) as excinfo:
|
|
_read_dir(base, "kategori-99")
|
|
|
|
message = str(excinfo.value)
|
|
assert "read_file" not in message, (
|
|
"the unknown-path refusal names read_file, so the two branches say the same thing about "
|
|
"two different facts"
|
|
)
|
|
assert not isinstance(excinfo.value, okf.DocumentPathRefused)
|
|
|
|
|
|
# --- (d) anti-vacuity: the named path must WORK ---------------------------------------------------
|
|
|
|
|
|
def test_the_named_path_is_usable_verbatim(tmp_path: Path) -> None:
|
|
"""(d) The ``_index_excerpt`` rule, one rung down: the caller is a model, so a suggested path
|
|
that does not resolve is worse than none. Proven by feeding it back."""
|
|
base = _base(tmp_path)
|
|
|
|
with pytest.raises(okf.DocumentPathRefused) as excinfo:
|
|
_read_dir(base, _ALPHA)
|
|
named = str(excinfo.value).split("'")[-2]
|
|
|
|
assert "alpha body." in _read_file(base, named)
|
|
|
|
|
|
# --- (e)/(f) the two gates the suggestion must not walk around ------------------------------------
|
|
|
|
|
|
def test_the_verdict_layer_is_never_named(tmp_path: Path) -> None:
|
|
"""(e) Built from ``context_files``, never ``files``. A suggestion built from the walk would
|
|
hand a navigator the path of a prior verdict - the one layer no listing mentions and
|
|
``read_file`` refuses outright (order 20260904T172353Z)."""
|
|
base = _base(tmp_path)
|
|
|
|
with pytest.raises(okf.BundlePathNotFound) as excinfo:
|
|
_read_dir(base, "dom-beta.md")
|
|
|
|
assert "read_file" not in str(excinfo.value)
|
|
assert not isinstance(excinfo.value, okf.DocumentPathRefused)
|
|
|
|
|
|
def test_a_foreign_dimension_document_is_never_named(tmp_path: Path) -> None:
|
|
"""(f) §4.1a, inverted: naming a document the run would then refuse to open is the "filter in
|
|
name only" S2c measured. ONE predicate (``in_dimension``) serves the listing and this."""
|
|
base = _base(tmp_path)
|
|
|
|
with pytest.raises(okf.BundlePathNotFound) as excinfo:
|
|
_read_dir(base, "energi-notat.md", dimension="tunnel")
|
|
|
|
assert "read_file" not in str(excinfo.value)
|
|
# The control: without a scope the SAME path is the wrong-rung class, so the arm above is the
|
|
# dimension deciding rather than the document being invisible.
|
|
with pytest.raises(okf.DocumentPathRefused):
|
|
_read_dir(base, "energi-notat.md")
|
|
|
|
|
|
# --- (g) the LIVE case is not this class ----------------------------------------------------------
|
|
|
|
|
|
def test_the_measured_live_path_is_still_the_unknown_class(tmp_path: Path) -> None:
|
|
"""(g) The felled premise, gated. The live base holds ``inbox-<name>.md`` documents beside
|
|
``<name>``-shaped directories, and the model asked for the un-prefixed form. That names no
|
|
document, so it is refused as unknown - and this delivery does NOT recover the two rounds the
|
|
order measured. Resolving it would mean guessing which document a caller meant, which is
|
|
invention rather than validation (``write_concept_file``'s rule)."""
|
|
base = _base(tmp_path)
|
|
assert (base / f"{_GAMMA}.md").exists(), "the fixture must hold the prefixed document"
|
|
|
|
with pytest.raises(okf.BundlePathNotFound) as excinfo:
|
|
_read_dir(base, _GAMMA.removeprefix("inbox-"))
|
|
|
|
assert "read_file" not in str(excinfo.value)
|
|
assert not isinstance(excinfo.value, okf.DocumentPathRefused)
|
|
|
|
|
|
# --- (h) the channel ------------------------------------------------------------------------------
|
|
|
|
|
|
def test_the_refusal_lands_on_the_refusal_tuple(tmp_path: Path) -> None:
|
|
"""(h) The ``BundlePathNotFound``/``DimensionScopeRefused`` precedent: the caller is a model
|
|
choosing a path, so this belongs on the CLI's refusal tuple and hosting's 400 arm rather than
|
|
the crash channel."""
|
|
assert issubclass(okf.DocumentPathRefused, ValueError)
|