feat(prepass,explore): padding dies at the prompt; a refusal the model can act on is a return value [skip-docs]
Order 20260908T195801Z. Findings 4 and 5 from the S7 acid test, then the two things finding 99 measured and deliberately did not fix (D3, D2). No user-facing surface changes: no new flag, no new command, no changed output contract. Both seams are internal (the pre-pass rendering, and the shape a tool answers a model with), so [skip-docs] rather than a README edit that would describe nothing an operator can do differently. FINDING 4 -- MEASURED, NOTHING BUILT. K2's price schedule IS readable without guessing (8 column spans, 71 of 91 non-blank rows give >= 2 cells, the split stable for K = 2..64). But 0 of 92 rows name all three of code/quantity/unit_cost -- also under a looser substring match -- and 0 of 91 data rows carry code + quantity + amount. The triple is not formatted away; it is not in the document. It is a price SUMMARY plus nine rate cards whose unit-price columns are empty (pre-award). The order's binding decision rule therefore falls against building: --derive-cost-baseline keeps refusing, and MAJOR-4's own honesty limit holds. FINDING 5 -- BUILT. Measured on the actual rendering path (concept_text, not the raw file): the delivered excerpt is 104 lines / 67 245 chars, carrying 208 interior whitespace runs, 117 of them >= 100 and the longest 887 -- 56 806 of 67 245 characters = 84.5 %, over 72 of 104 lines. collapse_padding, called from _data_blocks (the one renderer both arms share, and therefore AFTER verify_against_bundle -- collapsing in concept_text would break every payload's own digest), gives -72.4 %: line count invariant, non-whitespace byte-identical, leading indentation untouched, no number changed. F99-D3 -- read_file / read_dir / read_bundle now RETURN their refusal. MAF turns a tool raise into "Error: Function failed." (_tools.py:1410-1432, :1427) and counts it against DEFAULT_MAX_CONSECUTIVE_ERRORS_PER_REQUEST = 3, so everything the refusing arm knows is destroyed on the way out. The gates are unchanged; the property they exist for -- the reason travels, the bytes never do -- is now asserted explicitly on the returned value. The arm is keyed on named classes, never bare Exception, because ExplorationError is itself a RuntimeError subclass. F99-D2 -- the invariant row, plus one for finding 5 (a stated deviation from "one row only": finding 5 is a separately built seam and the ledger's standing rule requires its own row). 19 existing arms rewritten, never deleted and never weakened: where the class carried a distinction, the refusal KIND carries it now. 13 mutations, all red against the whole suite (W1-W5, M1-M8), each restored from scratchpad with shasum -c. Control 1543 passed / 5 skipped (from 1529/5, a strict superset, 0 removed). Golden demo-transcript.stdout unchanged (shasum -a 1 of the CONTENT = ea8c534773acdbe41ae68f2c55724d69aaf8be4f). Measurement: docs/2026-09-08-funn-4-5-og-read-nekt.md Co-Authored-By: Claude <Opus 5>
This commit is contained in:
parent
078a099898
commit
eb4137415c
13 changed files with 975 additions and 69 deletions
|
|
@ -72,9 +72,10 @@ def _a_directory(bundle_dir: Path) -> str:
|
|||
def test_a_directory_path_is_refused_and_the_other_rung_is_named() -> None:
|
||||
"""(1) the refusal says WHAT the path is and WHICH tool reads it."""
|
||||
path = _a_directory(_NESTED)
|
||||
with pytest.raises(DirectoryPathRefused) as excinfo:
|
||||
_tools(_NESTED)["read_file"].func(bundle_id=_NESTED.name, path=path)
|
||||
message = str(excinfo.value)
|
||||
message = _tools(_NESTED)["read_file"].func(bundle_id=_NESTED.name, path=path)
|
||||
assert message.startswith(f"REFUSED ({DirectoryPathRefused.__name__})"), (
|
||||
"the refusal is not distinguishable from a document's body"
|
||||
)
|
||||
assert path in message
|
||||
assert "directory" in message
|
||||
assert "read_dir" in message, "a refusal that does not name the other rung strands the caller"
|
||||
|
|
@ -88,9 +89,14 @@ def test_the_refusal_is_a_value_error_not_an_os_error() -> None:
|
|||
wrapper: re-raising ``IsADirectoryError`` with a better message would pass arm (1).
|
||||
"""
|
||||
path = _a_directory(_NESTED)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
_tools(_NESTED)["read_file"].func(bundle_id=_NESTED.name, path=path)
|
||||
assert not isinstance(excinfo.value, OSError)
|
||||
answer = _tools(_NESTED)["read_file"].func(bundle_id=_NESTED.name, path=path)
|
||||
# F99-D3: the refusal is RETURNED now, so the branch is identified by the kind it names. The
|
||||
# channel assertion moves onto the class itself and keeps both halves: it is on the CLI's
|
||||
# refusal tuple, and it is NOT a re-raised ``IsADirectoryError`` wearing a better message --
|
||||
# an ``OSError`` is outside ``_RETURNABLE_REFUSALS`` and would propagate rather than answer.
|
||||
assert answer.startswith(f"REFUSED ({DirectoryPathRefused.__name__})")
|
||||
assert issubclass(DirectoryPathRefused, ValueError)
|
||||
assert not issubclass(DirectoryPathRefused, OSError)
|
||||
|
||||
|
||||
async def test_the_refusal_holds_through_the_real_tool_surface() -> None:
|
||||
|
|
@ -100,8 +106,9 @@ async def test_the_refusal_holds_through_the_real_tool_surface() -> None:
|
|||
would be absent from the one caller it was written for.
|
||||
"""
|
||||
path = _a_directory(_NESTED)
|
||||
with pytest.raises(DirectoryPathRefused):
|
||||
await _invoke(_tools(_NESTED)["read_file"], bundle_id=_NESTED.name, path=path)
|
||||
answer = await _invoke(_tools(_NESTED)["read_file"], bundle_id=_NESTED.name, path=path)
|
||||
assert answer.startswith(f"REFUSED ({DirectoryPathRefused.__name__})")
|
||||
assert "read_dir" in answer
|
||||
|
||||
|
||||
def test_a_listing_already_separates_directories_from_documents() -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue