Isolate raw malformed native argument JSON per call (#325) #369

Closed
nsaspy wants to merge 5 commits from rage/325-native-call-isolation into main
Owner

Fixes #325

Runtime invariant implemented

A single attributable normalize/malformed_arguments fault in one provider
native tool call no longer aborts the whole batch. native_tool_calls_classify/2
now returns an ordered inert fault entry — normalized call identity, bounded
normalize/malformed_arguments cause, and the original raw argument text for
assistant-message integrity comparison — while valid read-only siblings
continue and the bounded loop can repair on the next provider turn.

Everything else stays fail-closed, evaluated against the ORIGINAL requested
batch before anything executes:

  • malformed lists, envelopes, fields, call IDs, types, function objects, tool
    names, and duplicate IDs (including duplicates involving faulted calls)
    remain batch-fatal;
  • a malformed_arguments payload that is not actual wire text (host-supplied
    closed Prolog term) has no assistant-message wire representation and remains
    batch-fatal (fail-closed hardening over the first draft);
  • strict native_tool_call_normalize/2 / native_tool_calls_normalize/2
    keep their all-or-nothing contract;
  • effect isolation is unchanged: every call that resolves to a trusted binding
    retains that binding even when its argument validation fails recoverably, so
    a malformed effectful call still counts as a requested effectful operation
    and multi-call effectful requests remain entirely batch-fatal;
  • faulted calls are never executable, never invent placeholder arguments, and
    are never charged as tool/context operations; observations and turns stay
    inside the existing budgets.

Important non-goals

  • No change to the #313 recoverable-fault policy set (recoverable_fault/2
    whitelist unchanged: normalize/schema malformed_arguments, catalog
    unavailable_tool_schema); no new fault class became repairable.
  • No change to authority, durable-effect admission, idempotency, or retry
    semantics; a repair is an explicit new model turn inside the existing loop.
  • No #329 singleton-warning fixes; those warnings remain visible and
    independently owned.
  • No AgentProlog roadmap change: no milestone, dependency, tool-availability,
    or approval-behavior status moves in this slice.

Tests and evidence

  • rage/325-native-call-isolation.org carries the RED reproduction, design,
    focused GREEN, pre-final gate, and exact-head gate records.
  • RED commit 20657de reproduces the issue: exactly 2/38 focused tests fail
    because one malformed argument string aborts every sibling call.
  • Focused: rlm_direct_partial_batch 40/40 (both raw-malformed orderings and
    both effectful isolation orderings), rlm_native_tool 14/14 (strict-API
    compatibility, duplicate IDs across faulted entries, payload identity,
    malformed-envelope and non-wire-term rejection), rlm_direct 28/28.
  • Exact-head local CI-equivalent gate at the published head (517f4e8):
    deterministic PlUnit 93 suites 1138/1138 passed (0 failed/timeout/blocked/
    fixme); deterministic benchmark 16/16; deep experiment 15/15;
    check_runtime/load_all/research-approval validator/SPEC-PLAN design gate;
    main-ownership probe; runner-integrity 19/19; CLI demo + graph trace-view
    smoke; fresh-process graph and durable-artifact restart fixtures;
    git diff --check. Log artifacts under /tmp/opencode/325-*.

Remaining follow-up

  • GitHub Actions (including the credentialed REAL OpenRouter lane) is the
    canonical gate for this exact head; fake providers never count as live
    evidence.
  • #329 singleton warnings remain open and independently owned.
Fixes #325 ## Runtime invariant implemented A single attributable `normalize/malformed_arguments` fault in one provider native tool call no longer aborts the whole batch. `native_tool_calls_classify/2` now returns an ordered inert fault entry — normalized call identity, bounded `normalize/malformed_arguments` cause, and the original raw argument text for assistant-message integrity comparison — while valid read-only siblings continue and the bounded loop can repair on the next provider turn. Everything else stays fail-closed, evaluated against the ORIGINAL requested batch before anything executes: - malformed lists, envelopes, fields, call IDs, types, function objects, tool names, and duplicate IDs (including duplicates involving faulted calls) remain batch-fatal; - a `malformed_arguments` payload that is not actual wire text (host-supplied closed Prolog term) has no assistant-message wire representation and remains batch-fatal (fail-closed hardening over the first draft); - strict `native_tool_call_normalize/2` / `native_tool_calls_normalize/2` keep their all-or-nothing contract; - effect isolation is unchanged: every call that resolves to a trusted binding retains that binding even when its argument validation fails recoverably, so a malformed effectful call still counts as a requested effectful operation and multi-call effectful requests remain entirely batch-fatal; - faulted calls are never executable, never invent placeholder arguments, and are never charged as tool/context operations; observations and turns stay inside the existing budgets. ## Important non-goals - No change to the #313 recoverable-fault policy set (`recoverable_fault/2` whitelist unchanged: normalize/schema `malformed_arguments`, catalog `unavailable_tool_schema`); no new fault class became repairable. - No change to authority, durable-effect admission, idempotency, or retry semantics; a repair is an explicit new model turn inside the existing loop. - No #329 singleton-warning fixes; those warnings remain visible and independently owned. - No AgentProlog roadmap change: no milestone, dependency, tool-availability, or approval-behavior status moves in this slice. ## Tests and evidence - `rage/325-native-call-isolation.org` carries the RED reproduction, design, focused GREEN, pre-final gate, and exact-head gate records. - RED commit `20657de` reproduces the issue: exactly 2/38 focused tests fail because one malformed argument string aborts every sibling call. - Focused: `rlm_direct_partial_batch` 40/40 (both raw-malformed orderings and both effectful isolation orderings), `rlm_native_tool` 14/14 (strict-API compatibility, duplicate IDs across faulted entries, payload identity, malformed-envelope and non-wire-term rejection), `rlm_direct` 28/28. - Exact-head local CI-equivalent gate at the published head (`517f4e8`): deterministic PlUnit 93 suites 1138/1138 passed (0 failed/timeout/blocked/ fixme); deterministic benchmark 16/16; deep experiment 15/15; check_runtime/load_all/research-approval validator/SPEC-PLAN design gate; main-ownership probe; runner-integrity 19/19; CLI demo + graph trace-view smoke; fresh-process graph and durable-artifact restart fixtures; `git diff --check`. Log artifacts under `/tmp/opencode/325-*`. ## Remaining follow-up - GitHub Actions (including the credentialed REAL OpenRouter lane) is the canonical gate for this exact head; fake providers never count as live evidence. - #329 singleton warnings remain open and independently owned.
docs: record exact-head gate evidence for #325
Some checks failed
Tree-sitter FFI / Direct SWI-Prolog Tree-sitter FFI (pull_request) Successful in 8m55s
CI / REAL OpenRouter integration (pull_request) Has been skipped
Nix flake / Flake package and clean runtime load (pull_request) Failing after 4s
Clean SWI pack install / Install and load copied pack (pull_request) Successful in 1m27s
Paid OpenRouter / Pinned paid OpenRouter integration (pull_request) Failing after 1m3s
CI / Deterministic unit and load checks (pull_request) Successful in 2m45s
517f4e8faa
nsaspy closed this pull request 2026-09-04 22:56:25 +00:00
Some checks failed
Tree-sitter FFI / Direct SWI-Prolog Tree-sitter FFI (pull_request) Successful in 8m55s
CI / REAL OpenRouter integration (pull_request) Has been skipped
Nix flake / Flake package and clean runtime load (pull_request) Failing after 4s
Clean SWI pack install / Install and load copied pack (pull_request) Successful in 1m27s
Paid OpenRouter / Pinned paid OpenRouter integration (pull_request) Failing after 1m3s
CI / Deterministic unit and load checks (pull_request) Successful in 2m45s

Pull request closed

Sign in to join this conversation.
No description provided.