Skip to content

fix(harness): limit opencode concurrency with global semaphore (#438)#505

Merged
AbirAbbas merged 6 commits intoAgent-Field:mainfrom
dinzz005:fix/opencode-concurrency-limit
May 1, 2026
Merged

fix(harness): limit opencode concurrency with global semaphore (#438)#505
AbirAbbas merged 6 commits intoAgent-Field:mainfrom
dinzz005:fix/opencode-concurrency-limit

Conversation

@dinzz005
Copy link
Copy Markdown
Contributor

Summary

Adds a concurrency limit to OpenCodeProvider.Execute to prevent unbounded subprocess spawning under parallel workloads.

Changes

  • Introduced a package-level semaphore (buffered channel) to cap concurrent executions
  • Lazy initialization via sync.Once
  • Configurable using OPENCODE_MAX_CONCURRENT env var (default: 4)
  • Semaphore acquisition happens before RunCLI and release in defer
  • Waiting callers respect context cancellation

Motivation

Previously, each Execute() call spawned a subprocess without any limit, causing:

  • rate-limit issues (429)
  • resource exhaustion (FDs, processes)
  • unstable behavior under fan-out workloads

This change introduces backpressure and stabilizes execution.

Notes

  • Matches Python SDK behavior (process-level concurrency control)
  • No changes to CLI invocation or retry logic

Testing

  • ./scripts/test-all.sh

  • Additional verification (please describe):

  • Added concurrency test ensuring at most N executions run simultaneously

  • Added context cancellation test to ensure blocked callers exit immediately

Checklist

  • [] I updated documentation where applicable.
  • [x ] I added or updated tests (or none were needed).
  • I updated CHANGELOG.md (or this change does not warrant a changelog entry).

Screenshots (if UI-related)

Related issues

@dinzz005 dinzz005 requested review from a team and AbirAbbas as code owners April 28, 2026 10:51
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 28, 2026

CLA assistant check
All committers have signed the CLA.

@dinzz005
Copy link
Copy Markdown
Contributor Author

If there are any issues or improvements needed, please let me know—I’ll fix them.

Comment thread sdk/go/harness/opencode.go Outdated
Comment thread sdk/go/harness/opencode.go Outdated
Comment thread sdk/go/harness/opencode_test.go
@dinzz005
Copy link
Copy Markdown
Contributor Author

dinzz005 commented May 1, 2026

if you have a time review and tell me if there is any changes to be made.

@dinzz005
Copy link
Copy Markdown
Contributor Author

dinzz005 commented May 1, 2026

hey guys if you check this pr and able to merge it then i can work on this issue (#404).

@AbirAbbas
Copy link
Copy Markdown
Contributor

hey guys if you check this pr and able to merge it then i can work on this issue (#404).

taking a look!

AbirAbbas and others added 3 commits May 1, 2026 14:10
The release tooling auto-generates CHANGELOG.md entries on each
chore(release) commit; manual edits collide with that and produced a
duplicate [0.1.72-rc.6] heading here. Reverting CHANGELOG.md to its
pre-edit state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing semaphore tests mock `p.runCLI`, which proves the channel
arithmetic but not that the semaphore actually serializes real subprocess
spawns alongside the new opencode 1.14+ flag construction (Agent-Field#519). Adds
two functional tests that drive the full Execute path through RunCLI:

- TestOpenCodeConcurrencyLimit_RealSubprocess: a fake `opencode` shell
  script writes per-invocation start/end timestamps; a sweep-line over
  the recorded spans asserts the maximum overlap never exceeds
  OPENCODE_MAX_CONCURRENT (and, conversely, that real overlap was
  observed — guarding against a future change that accidentally
  serializes everything to 1).
- TestOpenCodeSemaphore_ReleasedOnSubprocessFailure: with limit=1, three
  sequential calls into a script that exits non-zero must all complete.
  If a slot leaked on the failure path the second call would block
  forever.

Manually smoke-tested against a real opencode 1.14.29 binary as well:
4 concurrent Execute calls with limit=2 produced the expected ~2x
single-call duration (paired stair pattern), and the binary accepted
the new `run --dir <project> -m <model> --dangerously-skip-permissions
<prompt>` invocation without help-screen fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

Performance

SDK Memory Δ Latency Δ Tests Status
Go 233 B -17% 0.65 µs -35%

✓ No regressions detected

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

📊 Coverage gate

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

Surface Current Baseline Δ
control-plane 87.40% 87.30% ↑ +0.10 pp 🟡
sdk-go 91.70% 90.70% ↑ +1.00 pp 🟢
sdk-python 93.66% 93.63% ↑ +0.03 pp 🟢
sdk-typescript 92.63% 92.56% ↑ +0.07 pp 🟢
web-ui 89.69% 90.01% ↓ -0.32 pp 🟡
aggregate 88.85% 89.01% ↓ -0.16 pp 🟡

✅ Gate passed

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

📐 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 17 100.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 AbirAbbas enabled auto-merge May 1, 2026 19:14
@AbirAbbas AbirAbbas added this pull request to the merge queue May 1, 2026
Merged via the queue into Agent-Field:main with commit 994e3bc May 1, 2026
19 checks passed
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.

3 participants