• v1.2.0 cec9b8fe50

    v1.2.0 Stable

    ktg released this 2026-09-20 12:46:28 +00:00 | 14 commits to main since this release

    Signed by ktg
    SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q

    The release that opens the run path to callers who are not this framework. Every path through the
    CLI builds a debate, and therefore a chat client; the steps the debate is built on need no model
    at all, and this release gives each of them a command of its own.

    Added

    • portfolio-optimiser-toolbox (portfolio_optimiser.toolbox), a third console command beside the
      CLI and the offline demo. Fourteen subcommands, each a thin adapter over the function the run
      path itself calls, never a second implementation: navigate-bundle, cost-baseline,
      retrieve-chunks, prepass-admit, validate-proposal, verdict-key, capture-verdict,
      write-run-config, write-coverage, write-outbox, write-prepass, write-parse-failures,
      write-proposal-reviews and write-debate-tools. No model call and no network on any of them;
      JSON on stdout; exit 0 the step ran, 2 the call was malformed, 3 the step refused and the
      refusal is named. A proposal the deterministic validator blocks is also 3, and carries the
      verbatim reason and the stage rather than an exception envelope.
    • The outcome an outbox records is derived, never supplied by the caller: write-outbox runs
      the proposal through the same blocking gate the run path uses, because a door that accepted the
      outcome as an argument would let anyone author an outbox of assertions. The outbox directory is
      always the caller's to name, and --stop-reason is required - "finished" and "never found out"
      are different runs.
    • A measurement gate for the whole surface (python -m portfolio_optimiser.evals.b_gate): which
      run-path steps are callable from outside, proved by a probe that executes the door and asserts on
      data that came back from it. Fifteen of seventeen steps at this release; the round binder and its
      report share one module entry point without a subcommand that tells them apart.

    Security

    • A caller-supplied name that carries a path is refused by all seven outbox doors before
      anything is written. Each writer composes <out_dir>/<name>-<artefact>.json; the directory was
      guarded and the names were not, so --outbox-dir <d>/inni --run-id ../../X wrote two levels
      above the directory the caller named and answered 0. Separators, a bare . or .., an
      absolute prefix, an embedded NUL and the empty string are now refused with exit 3, the reason
      named, and no directory left behind. The class is every caller-supplied value that reaches a
      file name, not one flag: write-outbox composes <run_id>-<approach_id>, and
      --approach-id a/../../../X escaped the same way through the other half of that stem. Both go
      through one rule.
    • Containment as the last link: a composed artefact path that does not resolve under the
      output directory is refused, in outbox itself, where all eleven compositions are built and
      before the directory is created. The string rule above lives in the door, so this one covers the
      callers that never pass a door - the run path hands its own --run-id straight to these writers
      • and the next value someone interpolates into a file name.

    Fixed

    • --stop-reason being required is now guarded by a test rather than only by prose. The rule is
      load-bearing - "the run finished" and "we never found out" must not be the same value - but
      swapping required=True for default="" passed the entire suite before this release.

    Changed

    • Console entry points are pinned against the installed distribution's metadata rather than
      against pyproject.toml: a [project.scripts] line that has never been installed is a claim,
      not a command.
    Downloads