Skip to content

feat(verifier): tool-output postcondition validation + correctionPrompt retry (#130) - #157

Merged
Weegy merged 1 commit into
mainfrom
feat/130-tool-postcondition
May 28, 2026
Merged

feat(verifier): tool-output postcondition validation + correctionPrompt retry (#130)#157
Weegy merged 1 commit into
mainfrom
feat/130-tool-postcondition

Conversation

@Weegy

@Weegy Weegy commented May 28, 2026

Copy link
Copy Markdown
Contributor

Closes #130.

Summary

  • Plugins declare an optional Zod output schema on each tool. The bridge validates the tool's return against the schema; a mismatch is stamped on the RunTrace as RunToolCall.postcondition.
  • The verifier picks up that marker and produces a synthetic tool_postcondition ClaimVerdict (status='contradicted'), flipping the verdict to blocked.
  • The existing VerifierService retry loop fires automatically; buildCorrectionPrompt adds a new German ## Tool-Output nicht spec-konform section that instructs the model to re-call the tool with corrected args (or pick a different tool).
  • Backwards-compatible: tools without an output schema behave exactly as today. LocalSubAgentTool.handle returns a string | LocalSubAgentToolResult union — existing plugins return strings unchanged.

What this enables

Once #127 (deterministic calculator tool) and #128 (logic-inference tools) land, they will be the first real consumers of output schemas. Until then this PR is a no-op at runtime for existing tools but unblocks the deterministic-tools slice.

Files

  • plugin-api: LocalSubAgentToolResult union + RunToolCall.postcondition
  • channel-sdk: mirror RunToolCall.postcondition (structural copy from S+10-2 lift)
  • harness-orchestrator: bridgeTool result unwrap in localSubAgent.dispatch, AskObserver.onSubToolResult event extension, RunTraceCollector copies marker, verifierService.extractPostconditionViolations
  • harness-verifier: ClaimType adds 'tool_postcondition', VerifierInput.toolPostconditionViolations, pipeline builds synthetic verdicts before extraction, buildCorrectionPrompt emits the new section
  • dynamicAgentRuntime: bridgeTool runs the postcondition schema after td.run(parsed) and returns the structured shape on mismatch
  • Tests: verifierPipeline.test.ts +2 (blocks-on-violation, backward-compat); new correctionPromptPostcondition.test.ts +3

Test plan

Addresses Recommendation #4 from the May 2026 LLM-harness audit.

🤖 Generated with Claude Code

Plugins can now declare an optional Zod `output` schema on each tool.
The bridge validates the tool's return value against the schema; a
mismatch is recorded as a structured postcondition marker on the
RunTrace and surfaces as a synthetic `tool_postcondition` claim
(status='contradicted') in the verifier verdict.

That contradicted verdict flips the aggregate to `blocked` and feeds
into the existing `correctionPrompt` retry loop in `VerifierService`,
so no new retry pathway is introduced — the orchestrator simply re-runs
the turn with a German "Tool-Output nicht spec-konform" section in the
correction hint, instructing the model to re-call the tool with fixed
arguments or pick a different tool.

Backwards-compatible: tools without an `output` schema behave exactly
as before; `LocalSubAgentTool.handle` returns a `string | LocalSubAgentToolResult`
union so existing plugins keep returning strings unchanged.

Touches the cross-package run-trace shape: `RunToolCall.postcondition`
is mirrored in `@omadia/plugin-api` (KG-side) and `@omadia/channel-sdk`
(structural copy lifted in S+10-2).

Addresses #130 (Recommendation #4, May 2026 LLM-harness audit).

Tests: 14 new (verifierPipeline +2 — blocks-on-violation, backward-compat;
correctionPromptPostcondition +3 — section emitted, isolated from other
sections, undefined for non-blocked verdicts). Full middleware suite:
2629/2634 pass (3 preexisting privacyV4Bypass fails unrelated to #130).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Weegy
Weegy merged commit 01151f4 into main May 28, 2026
7 checks passed
Weegy added a commit that referenced this pull request May 28, 2026
Extends the #130 postcondition mechanism to native (top-level orchestrator)
tools — until now only sub-agent tools (UploadedToolkit / LocalSubAgentTool)
could declare a postcondition. Native tools like the upcoming
deterministic-tools plugin (#127, #128) need the same protection.

What changed:
- `NativeToolHandler` may now return `string | NativeToolResult` where
  `NativeToolResult = { output, postcondition? }`. Backwards-compatible:
  every existing plugin returns plain strings unchanged.
- `Orchestrator.dispatchToolInner` / `.dispatchTool` normalise the
  handler result into the structured shape; the slot-loop and
  `finishSlotInvocation` stamp the postcondition onto
  `RunToolCall.postcondition` for the trace.
- A postcondition violation also flips `isError` on the slot so the
  Anthropic tool_result is marked as such — the orchestrator already
  short-circuits the answer when it sees a tool error, and the verifier
  pipeline (#130 PR #157) then raises a `tool_postcondition` claim that
  drives the existing correctionPrompt retry loop.

Tests: new `runTraceCollectorPostcondition.test.ts` covers the one
runtime hand-off (collector accepts + emits postcondition on
orchestratorToolCalls) that the TypeScript compiler can't catch. Full
middleware suite stays at 2629/2634 pass (3 preexisting privacyV4Bypass
fails unrelated to this change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(runtime): tool-result postcondition validation in dynamicAgentRuntime

1 participant