feat(toolbox): the first four doors out of the toolbox, without a chat client on the way

B-gate row 1's premise, made callable. Every path through the framework CLI constructs a chat
client, so an outside caller -- a human at a terminal, or an agent that is NOT po -- could not
reach a single run-path step without paying for a model. These four steps need no model at all.

One CLI, four subcommands, one core call each:

  navigate-bundle  --bundle-dir                         -> okf.navigate_bundle
  cost-baseline    --bundle-dir --project-id            -> okf.derive_cost_baseline
  retrieve-chunks  --query --docs-dir [--top-k]         -> datasource.retrieve_chunks
  prepass-admit    --payload --bundle-dir [--dimension] -> prepass.admit_payload

Each handler is a thin adapter: strings in, the SAME function the run path calls, JSON on stdout,
and an exit code that says what happened (0 ran, 2 malformed call, 3 the step refused, named).
A handler that computed anything of its own would be a second implementation of a run-path step,
and the outside caller would stop getting what the debate gets.

Dispatch is an explicit branch per command, not argparse's `set_defaults(handler=...)`: the table
hides the one thing a reader wants to see, and B-gate row 1 asks the same question of the source
(it walks the call graph from `main` down to the step's symbol), where a callable in a Namespace
is a hop neither can follow.

Probes (`tests/test_toolbox_doors.py`, 10 arms): each starts the door as a SUBPROCESS with the
subcommand in argv and asserts on what it wrote -- never by importing the core function, which is
the whole difference the gate exists to measure. The yardstick is outside the door in every arm:
the filesystem (navigate-bundle, including the one deliberate outside-bundle link), a table
transcribed from the priced fixture (cost-baseline), the in-process seam it must equal byte for
byte (retrieve-chunks), and the producer's own checked-in payload (prepass-admit). Every refusal
arm has an rc-0 control beside it.

`portfolio-optimiser-toolbox` is the THIRD console script, and the pin test now says why: it is
the door the other two cannot be used for. README and CLAUDE.md updated with the command and the
reason it exists; every documented invocation was run.

Row 1: 1 -> 5 of 17 (four subcommands + `gate`, which the class fix in e47be68 stopped rejecting
on a name technicality). No other row moved; exit 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-20 08:11:58 +02:00
commit 38df79126f
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
8 changed files with 532 additions and 37 deletions

View file

@ -130,11 +130,14 @@
"scope": "run_project"
},
"entry": {
"kind": "console-script",
"module": "run.py",
"scope": "main"
"kind": "subcommand",
"module": "toolbox.py",
"scope": "main",
"command": "navigate-bundle"
},
"probe": []
"probe": [
"tests/test_toolbox_doors.py::test_navigate_bundle_from_outside_reports_every_file_the_base_holds"
]
},
{
"id": "kostnadsgrunnlag",
@ -146,11 +149,14 @@
"scope": "run_project"
},
"entry": {
"kind": "console-script",
"module": "run.py",
"scope": "main"
"kind": "subcommand",
"module": "toolbox.py",
"scope": "main",
"command": "cost-baseline"
},
"probe": []
"probe": [
"tests/test_toolbox_doors.py::test_cost_baseline_from_outside_derives_exactly_what_the_priced_table_says"
]
},
{
"id": "kontekst",
@ -162,11 +168,14 @@
"scope": "run_project"
},
"entry": {
"kind": "console-script",
"module": "run.py",
"scope": "main"
"kind": "subcommand",
"module": "toolbox.py",
"scope": "main",
"command": "retrieve-chunks"
},
"probe": []
"probe": [
"tests/test_toolbox_doors.py::test_retrieve_chunks_from_outside_matches_the_in_process_seam_exactly"
]
},
{
"id": "prepass",
@ -178,11 +187,14 @@
"scope": "run_project"
},
"entry": {
"kind": "console-script",
"module": "run.py",
"scope": "main"
"kind": "subcommand",
"module": "toolbox.py",
"scope": "main",
"command": "prepass-admit"
},
"probe": []
"probe": [
"tests/test_toolbox_doors.py::test_prepass_admit_from_outside_admits_the_producers_own_payload"
]
},
{
"id": "validering",

View file

@ -0,0 +1,190 @@
"""The toolbox door: po's run-path steps as ONE command line, without a chat client on the way.
This is B's premise made callable. The framework's own CLI (``portfolio_optimiser.run``) drives a
MAF debate and therefore constructs a chat client on every path through it; an outside caller
a human at a terminal, or an agent that is NOT po cannot reach ``navigate_bundle`` or
``retrieve_chunks`` through that door without paying for a model. The steps themselves need no
model at all. This module exposes exactly those steps, and nothing else.
**One CLI, four subcommands, one core call each.** Each subcommand parses arguments, calls the
SAME function the run path calls, writes the result as JSON on stdout, and returns an exit code
that says what happened: ``0`` the step ran, ``2`` the call was malformed (argparse), ``3`` the
step refused and the refusal is named in the JSON. There is no fourth code and no silent zero
the exit code is the only signal a calling agent has before it reads a byte.
**No re-implementation, and that is the load-bearing part.** Every handler below is a thin
adapter: it converts strings to the types the core function already takes and converts what came
back to JSON. A handler that computed anything of its own would be a second implementation of a
run-path step, and the two would drift which is the one thing a toolbox door must not do,
because the whole claim is that the outside caller gets what the debate gets.
**Deliberately NOT here:** anything that needs a chat client, anything that writes into the
run's outbox, and any path back into ``portfolio_optimiser.run``. The import list is part of the
contract: B-gate row 1 refuses a door whose entry reaches a chat-client name, and row 3 refuses
a repository with a written path to Claude. Both read this file.
"""
from __future__ import annotations
import argparse
import json
import sys
from collections.abc import Mapping, Sequence
from typing import Any
from portfolio_optimiser import okf, prepass
from portfolio_optimiser.datasource import retrieve_chunks
__all__ = ["main"]
#: Exit code for a step that REFUSED. Separate from 2 (malformed call) because they are two
#: different facts about the caller's request: 2 means "you asked wrong", 3 means "you asked
#: right and the answer is no". An agent retries the first and reports the second.
REFUSED = 3
#: What a refusal is allowed to be. Everything here is a REFUSAL the core functions document —
#: a bad bundle, an unpriced schedule, a payload that is not this base's cut. A bug in po is not
#: in this tuple and must keep crashing with its traceback.
_REFUSALS = (ValueError, FileNotFoundError, OSError)
def navigate_bundle_command(args: argparse.Namespace) -> Mapping[str, Any]:
"""``navigate-bundle`` — open a knowledge base and report what navigation reached.
``skipped`` is carried out of the door rather than summarised away: "this document was never
written" and "the link to it was wrong" are two different repairs, and a caller that only
saw a file count could act on neither."""
bundle = okf.navigate_bundle(args.bundle_dir)
return {
"bundle_dir": bundle.dir,
"files": [
{"name": f.name, "type": f.type, "characters": len(f.body)} for f in bundle.files
],
"skipped": [
{"from_file": s.from_file, "target": s.target, "reason": s.reason}
for s in bundle.skipped
],
"counts": {"files": len(bundle.files), "skipped": len(bundle.skipped)},
}
def cost_baseline_command(args: argparse.Namespace) -> Mapping[str, Any]:
"""``cost-baseline`` — derive the anchor from a priced schedule already in the base.
``project_id`` is required for the reason ``okf.derive_cost_baseline`` requires it: the base
carries one too, and reading it here would make this a second reader of a fact that has an
owner."""
bundle = okf.navigate_bundle(args.bundle_dir)
baseline = okf.derive_cost_baseline(bundle, project_id=args.project_id)
return baseline.model_dump()
def retrieve_chunks_command(args: argparse.Namespace) -> list[dict[str, Any]]:
"""``retrieve-chunks`` — the shared data-source call, byte for byte.
The SAME function the in-process ``FunctionTool`` and the MCP path call. An outside caller
gets the citation-ready shape the agents get, not a variant of it."""
return retrieve_chunks(args.query, args.docs_dir, args.top_k)
def prepass_admit_command(args: argparse.Namespace) -> Mapping[str, Any]:
"""``prepass-admit`` — everything that must hold before a declared cut may shape a run.
The identity check needs the base's RESOLVED id, so the door resolves it the one way the
repository resolves it (``okf.reconcile_bundle_id``) rather than reading the mount name."""
payload = prepass.load_prepass_payload(args.payload)
resolved = okf.reconcile_bundle_id(args.bundle_dir)
prepass.admit_payload(
payload,
bundle_dir=args.bundle_dir,
resolved_id=resolved,
dimension=args.dimension,
)
return {
"admitted": True,
"bundle_id": resolved.id,
"bundle_id_origin": resolved.origin,
"mount": resolved.mount,
"considered": payload.denominators.considered,
"withheld": payload.denominators.withheld,
"delivered": payload.denominators.delivered,
}
def build_parser() -> argparse.ArgumentParser:
"""The four doors, each registered by name.
``required=True`` on the subparsers: a toolbox invoked with no command must be a usage error,
never a zero. Measured as a class in this repository an exit 0 for a call that did nothing
is indistinguishable, to an automated caller, from a call that did everything."""
parser = argparse.ArgumentParser(
prog="portfolio-optimiser-toolbox",
description="po's run-path steps as plain commands — no model call, no network. "
"Exit 0 when the step ran, 2 on a malformed call, 3 when the step refused.",
)
sub = parser.add_subparsers(dest="command", required=True)
naviger = sub.add_parser("navigate-bundle", help="open a knowledge base and report its files")
naviger.add_argument("--bundle-dir", required=True, help="the base's root directory")
kostnad = sub.add_parser("cost-baseline", help="derive the cost baseline from a priced table")
kostnad.add_argument("--bundle-dir", required=True, help="the base's root directory")
kostnad.add_argument("--project-id", required=True, help="the project the baseline anchors")
hent = sub.add_parser("retrieve-chunks", help="retrieve citation-ready chunks")
hent.add_argument("--query", required=True, help="what to retrieve for")
hent.add_argument("--docs-dir", required=True, help="the folder to retrieve from")
hent.add_argument("--top-k", type=int, default=3, help="how many chunks (default: 3)")
slipp = sub.add_parser("prepass-admit", help="admit a declared pre-pass cut, or refuse it")
slipp.add_argument("--payload", required=True, help="the producer's payload JSON")
slipp.add_argument("--bundle-dir", required=True, help="the base the cut claims to be of")
slipp.add_argument("--dimension", default=None, help="restrict admission to one dimension")
return parser
def dispatch(args: argparse.Namespace) -> Any:
"""Name the four handlers, one branch each — deliberately not a ``set_defaults(handler=…)``.
The dispatch table argparse offers is one line shorter and hides the only thing a reader of
this module wants to see: which command reaches which run-path step. B-gate row 1 asks the
same question of the source (it walks the call graph from ``main`` down to the step's symbol)
and a callable stored in a Namespace is a hop neither a reader nor the gate can follow."""
if args.command == "navigate-bundle":
return navigate_bundle_command(args)
if args.command == "cost-baseline":
return cost_baseline_command(args)
if args.command == "retrieve-chunks":
return retrieve_chunks_command(args)
if args.command == "prepass-admit":
return prepass_admit_command(args)
raise RuntimeError(f"unregistered command {args.command!r}") # pragma: no cover - argparse
def main(argv: Sequence[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
try:
result = dispatch(args)
except _REFUSALS as refusal:
json.dump(
{
"error": {
"kind": type(refusal).__name__,
"message": str(refusal),
"command": args.command,
}
},
sys.stdout,
ensure_ascii=False,
indent=2,
)
print()
return REFUSED
json.dump(result, sys.stdout, ensure_ascii=False, indent=2)
print()
return 0
if __name__ == "__main__": # pragma: no cover - dekket av subprosess-probene
raise SystemExit(main())