Cover unbound tool payloads behind a shared scenario driver

Merged
VA
3d ago
+182-8

Problem

Unregistered model calls must fail before dispatch while preserving the complete name and arguments. Subset matching of arguments allowed inserted keys and array elements to escape the scenario oracle.

Design

Eight data cases exercise ordinary, pinned, facade, multiple, recovered and array-shaped unbound calls. Assert each full tool name and argument value, the failure, absence of tool dispatch/completion, request counts and final files. Argument values compare by exact JSON equality; observation metadata keeps subset matching. Negative controls corrupt names, replace arguments, append an array element and insert nested or top-level keys. A shrinking property states that argument matching succeeds iff the complete JSON values are equal.

All discovered scenarios invoke one Driver trait. TaskDriver currently selects JournalDriver; switching the production task-file backend changes one alias. Composer and notification component cases use the same dispatch point.

Deletions

Reuse the existing provider, case loader, matchers and runner. No registry or new model fake. Net +174 lines (+182/-8), including the driver refactor and verification.

Tests run

The exact signed #611 delta at a11da432859b17a1b69ddfa826f352e766ed64c0 is included unchanged in tested #614 head 975eb5ebbe7369fd34af252773ea4c362b553dd6. On bb1, the built e2e binary passed all 39 tests in 1.22 s and all 370 scenarios in 0.644 s, including both unbound-call controls and the shrinking exact-argument property. The gate used bs.ps1, one native feature set (filesystem-local,mcp-http), and the built test binary directly. No whole-suite elapsed assertion or repeated cargo build. rustfmt and git diff --check pass.

Decisions

These are explicitly executor cases while task-file admission is unavailable. No test-owned admission, synthetic wire publication, tool, API, store, protocol message or synchronization is added.

Follow-up

The wire is the task volume, not the legacy socket/HarnessWireApi. #555 provides snapshot-plus-cursor observe, but #495 still needs to consume ordinary request-file writes and publish actual execution chunks, results and terminal status there. Exact missing bindings: https://github.com/acyclic-labs/sdk/pull/495#issuecomment-6205062451. Select the file-backed driver when that production binding lands, then assert followed frames/status and resulting files. The user explicitly authorized landing this payload fix with the journal driver in the meantime.