Skip to content

fix(sdk/go): claude prompt via stdin; note endpoint /api/v1 prefix#746

Closed
AbirAbbas wants to merge 3 commits into
mainfrom
fix/go-harness-claude-args
Closed

fix(sdk/go): claude prompt via stdin; note endpoint /api/v1 prefix#746
AbirAbbas wants to merge 3 commits into
mainfrom
fix/go-harness-claude-args

Conversation

@AbirAbbas

Copy link
Copy Markdown
Contributor

Summary

Two bugs found by live E2E testing of the SWE-AF Go port against a real control plane:

  1. Critical — claude provider prompt swallowed. The harness appended the prompt as a trailing positional after repeated --allowedTools flags; claude CLI 2.1.x treats --allowedTools as variadic and consumes the positional, so every tool-passing call failed with Input must be provided either through stdin or as a prompt argument when using --print. The prompt now flows via stdin (RunCLIWithStdin), which --print reads natively. codex/opencode/gemini were audited: their arg constructions have no variadic-before-positional hazard. Verified against the real CLI via a build-tagged integration test (AGENTFIELD_CLAUDE_BIN-gated) — exit 0 with real structured output on the exact tools+stdin path.
  2. Minor — Note() 404. Posted to {base}/executions/note without the /api/v1 prefix (route is registered under the /api/v1 group in routes_core.go:150), silently dropping every agent progress note. Existing tests masked it by baking /api/v1 into the base URL contrary to the SDK-wide bare-base contract; tests corrected to the real contract.

Test plan

  • go mod tidy no diff; go build ./...; go vet ./... (incl. -tags integration)
  • go test ./... — all packages green
  • New unit tests: prompt arrives on stdin and absent from the arg vector; note URL asserts /api/v1/executions/note from a bare base
  • Gated integration test PASSES against the real claude CLI

🤖 Generated with Claude Code

The claude provider appended the prompt as a trailing positional after
repeated --allowedTools flags; claude CLI 2.1.x treats --allowedTools
as variadic and swallows the positional, so every tool-passing call
exited 1 with 'Input must be provided'. The prompt now flows through
stdin (RunCLIWithStdin), which --print reads natively; codex/opencode/
gemini were inspected and are unaffected. Verified against the real
CLI via a gated integration test.

Note() posted to {base}/executions/note without the /api/v1 prefix,
404ing on the control plane (route registered under the /api/v1 group)
and silently dropping every progress note; existing tests masked it by
baking /api/v1 into the base URL, contrary to the SDK-wide bare-base
contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Performance

SDK Memory Δ Latency Δ Tests Status
Go 222 B -21% 0.62 µs -38%

✓ No regressions detected

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage gate

Thresholds from .coverage-gate.toml: per-surface ≥ 84%, aggregate ≥ 85%, max per-surface regression ≤ 1.0 pp, max aggregate regression ≤ 0.50 pp.

Surface Current Baseline Δ
control-plane 86.90% 87.40% ↓ -0.50 pp 🟡
sdk-go 92.20% 92.00% ↑ +0.20 pp 🟢
sdk-python 93.76% 93.73% ↑ +0.03 pp 🟢
sdk-typescript 90.42% 90.42% → +0.00 pp 🟢
web-ui 84.75% 84.79% ↓ -0.04 pp 🟡
aggregate 85.54% 85.75% ↓ -0.21 pp 🟡

✅ Gate passed

No surface regressed past the allowed threshold and the aggregate stayed above the floor.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

📐 Patch coverage gate

Threshold: 80% on lines this PR touches vs origin/main (from .coverage-gate.toml:thresholds.min_patch).

Surface Touched lines Patch coverage Status
control-plane 0 ➖ no changes
sdk-go 211 99.00%
sdk-python 0 ➖ no changes
sdk-typescript 0 ➖ no changes
web-ui 0 ➖ no changes

✅ Patch gate passed

Every surface whose lines were touched by this PR has patch coverage at or above the threshold.

AbirAbbas and others added 2 commits July 9, 2026 18:32
Agent.Call was a synchronous POST /execute/{target} bounded by the
15s CallTimeout http.Client default, killing any caller whose child
reasoner worked longer. It now mirrors the Python SDK: submit via
/execute/async/{target}, then poll the execution record with
Python-parity pacing (0.25s doubling to 4s, jittered), CallTimeout
bounding each request rather than the overall wait, ctx-cancellable,
lineage headers on submit and polls so DAG parentage is unchanged.

The claude provider now uses --output-format stream-json --verbose:
per-event output keeps the CLI idle watchdog fed during long silent
turns (plain json emitted nothing until completion, so >120s turns
were SIGKILLed), and the terminal result event carries the same
fields including total_cost_usd. Real 2.1.191 capture committed as a
fixture; ctx cancellation now kills the whole CLI process group.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lt-runner fallback

Adds tests closing the patch-coverage gap on PR #746's async-submit
Agent.Call rewrite and the claudecode stdin/stream-json changes.

agent_call_coverage_test.go (package agent) exercises the error/edge
branches of Call's submit + poll loop that the happy-path tests miss:
  - nextCallPollInterval clamps (below floor / above ceiling)
  - submitAsyncExecution: build-request, transport, read-body,
    structured 4xx, and JSON-decode errors
  - awaitExecutionResult: ctx-already-cancelled, GET build error,
    ctx-cancel-during-poll, transport error, read-body error,
    poll HTTP-error status, poll decode error, succeeded-result
    decode error, and succeeded-null-result

claudecode_defaultrunner_test.go covers the runCLI == nil fallback in
ClaudeCodeProvider.Execute via a struct-literal provider (bypassing
NewClaudeCodeProvider) driving the real default runner.

Lifts sdk-go patch coverage from 61% to 98% (agent.go 98.29%,
claudecode.go 100%), clearing the 80% min_patch gate. Tests only; no
production code changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AbirAbbas added a commit that referenced this pull request Jul 10, 2026
…lt-runner fallback

Adds tests closing the patch-coverage gap on PR #746's async-submit
Agent.Call rewrite and the claudecode stdin/stream-json changes.

agent_call_coverage_test.go (package agent) exercises the error/edge
branches of Call's submit + poll loop that the happy-path tests miss:
  - nextCallPollInterval clamps (below floor / above ceiling)
  - submitAsyncExecution: build-request, transport, read-body,
    structured 4xx, and JSON-decode errors
  - awaitExecutionResult: ctx-already-cancelled, GET build error,
    ctx-cancel-during-poll, transport error, read-body error,
    poll HTTP-error status, poll decode error, succeeded-result
    decode error, and succeeded-null-result

claudecode_defaultrunner_test.go covers the runCLI == nil fallback in
ClaudeCodeProvider.Execute via a struct-literal provider (bypassing
NewClaudeCodeProvider) driving the real default runner.

Lifts sdk-go patch coverage from 61% to 98% (agent.go 98.29%,
claudecode.go 100%), clearing the 80% min_patch gate. Tests only; no
production code changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 5b763d8)
@AbirAbbas

Copy link
Copy Markdown
Contributor Author

Consolidated into #750 — the same commits (7c6f11e, 18c3f3e, 5b763d8) are cherry-picked there.

@AbirAbbas AbirAbbas closed this Jul 10, 2026
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.

1 participant