Repository navigation
Lift the connector's cap on workers, and let a start set one - #1040
Conversation
The runner ran at most four workers at once: a number nobody chose, changed only by a hand edit to runner.json and a restart. Now every request gets a worker as it arrives, and a cap is something the operator sets when they want that control. `basecamp connect --max-workers N` caps how many workers run at once, so a request past the cap waits its turn. Like the rest of what a start names it is kept for the profile, as max_workers in runner.json, so --run starts with it. `--max-workers 0` takes a kept cap off, and a start that does not name it leaves the kept one alone. A cap is read once, at a start. `basecamp connect workers list` says what the running connector's is. A connector with no max_workers of its own goes from four at once to no cap. So that a stop does not start a turn only to end it, which the cap's wait used to prevent half the time, a run that is ending starts no worker turn.
There was a problem hiding this comment.
🟢 Approved
No active review issues block approval of the worker-concurrency configuration changes.
No open issues 🎉
What changed in this PR
Removes the connector runner’s default four-worker limit and adds an optional, profile-persisted --max-workers cap.
Changes:
- Defaults to uncapped worker concurrency; explicit caps are saved in
runner.json, and0removes them. - Reports the active cap in worker status and adds an early shutdown check before worker turns.
- Adds tests and documents persistence, startup-only configuration, and restart behavior.
[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or rungh pr ready --undo.
Click "Ready for review" or rungh pr readyto reengage.
| File | Description |
|---|---|
skills/basecamp-connect/SKILL.md |
Explains setting, removing, and changing worker caps. |
internal/connector/runner/workers.go |
Makes slot waiting optional and checks for shutdown early. |
internal/connector/runner/spec.go |
Validates and persists optional caps without an upper ceiling. |
internal/connector/runner/spec_test.go |
Tests cap validation, preservation, and removal. |
internal/connector/runner/socket.go |
Exposes the active cap in status responses. |
internal/connector/runner/runner.go |
Removes the default cap and applies startup overrides. |
internal/connector/runner/runner_test.go |
Tests uncapped execution, cap precedence, and startup-only behavior. |
internal/commands/connect.go |
Documents cap controls and restart semantics in help. |
internal/commands/connect_runner.go |
Registers the flag, saves its value, and displays the cap. |
internal/commands/connect_runner_test.go |
Tests flag parsing and persistence across starts. |
internal/commands/connect_run.go |
Passes startup settings through and expands the shadow-mode error. |
AGENTS.md |
Documents optional caps and when they take effect. |
Review effort: Balanced
There was a problem hiding this comment.
1 issue found across 12 files
You’re at about 91% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="internal/commands/connect_run.go">
<violation number="1" location="internal/commands/connect_run.go:231">
P3: The refusal omits `--orchestrator-model`, `--orchestrator-effort`, `--worker-model`, and `--worker-effort`, even though each triggers this branch. List them so the error tells users which runner flags to drop.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| if f.runner.requested() { | ||
| if f.shadow { | ||
| return output.ErrUsage("A shadow run hands nothing off, so it has no orchestrator: drop --shadow, or the orchestrator and worker flags") | ||
| return output.ErrUsage("A shadow run hands nothing off, so it has no orchestrator: drop --shadow, or the flags that have the connector do the work (--orchestrator, --worker, --permissions, --max-workers, --run)") |
There was a problem hiding this comment.
P3: The refusal omits --orchestrator-model, --orchestrator-effort, --worker-model, and --worker-effort, even though each triggers this branch. List them so the error tells users which runner flags to drop.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/commands/connect_run.go, line 231:
<comment>The refusal omits `--orchestrator-model`, `--orchestrator-effort`, `--worker-model`, and `--worker-effort`, even though each triggers this branch. List them so the error tells users which runner flags to drop.</comment>
<file context>
@@ -228,7 +228,7 @@ func runConnect(cmd *cobra.Command, f *connectRunFlags) error {
if f.runner.requested() {
if f.shadow {
- return output.ErrUsage("A shadow run hands nothing off, so it has no orchestrator: drop --shadow, or the orchestrator and worker flags")
+ return output.ErrUsage("A shadow run hands nothing off, so it has no orchestrator: drop --shadow, or the flags that have the connector do the work (--orchestrator, --worker, --permissions, --max-workers, --run)")
}
if start, err = connectRunnerSettle(f.runner, connectRunnerConfigPath(path)); err != nil {
</file context>
| return output.ErrUsage("A shadow run hands nothing off, so it has no orchestrator: drop --shadow, or the flags that have the connector do the work (--orchestrator, --worker, --permissions, --max-workers, --run)") | |
| return output.ErrUsage("A shadow run hands nothing off, so it has no orchestrator: drop --shadow, or the flags that have the connector do the work (--orchestrator, --orchestrator-model, --orchestrator-effort, --worker, --worker-model, --worker-effort, --permissions, --max-workers, --run)") |
- A run that is ending is checked for where a turn is about to be started, on every turn and not only a worker's first. - The --shadow refusal names the model and effort flags too. - `workers list` says in its help that it reports the cap. - Comments on runner.json and on the cap's check say what is so. - The capped test's subtests fail as themselves.
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
There was a problem hiding this comment.
1 existing issue remains and no new issues found across 5 files (changes from recent commits).
You’re at about 92% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.
View guided diff | Turn on auto-fix | Re-trigger cubic
A worker reaching its turn as the connector stopped asked after the hold with a context already ended, and logged that the hold stands.
* commit 'ef2b350fa4662d0060c2b5e90bcbe69e32e76dd0': Stop telling an agent to send a Markdown fold as HTML (#1030) Update nix flake and plugin version for v0.30.0 (#1042) Watch a connector in a terminal, and talk to its orchestrator (#1039) Lift the connector's cap on workers, and let a start set one (#1040) Never give the connector's monitor a timeout (#1035) Update nix flake and plugin version for v0.29.0 (#1037) Let assignments notify people with --notify (#1034) Update nix flake and plugin version for v0.28.1 Bind local config trust to approved settings Update nix flake and plugin version for v0.28.0 (#1029) Let the connector do the work itself, on the coding agent you choose (#1026) Build with Go 1.26.11 (#1027) Open a release PR for the version bump instead of pushing it to main (#1025) Update nix flake and plugin version for v0.27.0
What this does
A connector doing the work itself no longer caps how many workers run at once. Every request gets a worker as it arrives.
basecamp connect --max-workers Nsets a cap for the operator who wants one: at most N workers run at once, and a request past the cap waits its turn.runner.json(asmax_workers), so--runstarts with it.--max-workers 0removes a kept cap. A start that does not name the flag leaves the kept one alone, including one written intorunner.jsonby hand.basecamp connect workers listsays "at most N at once" for a running connector that has a cap, and carries it asmax_workersin its JSON.Why
The runner ran at most four workers at once. Nobody chose that number, and the only way to change it was a hand edit to
runner.jsonand a restart. The cap is now a control the operator reaches for, not a default everyone gets.Behavior changes to know about
runner.jsonalready hasmax_workers. The first start afterwards continues every worker a stop interrupted at once, not four at a time.max_workersabove 64 is accepted. It was refused before. With no cap as the default, a ceiling on the cap has nothing to protect.Worth a reviewer's attention
AGENTS.mdsay it is read at a start.basecamp connect restart, because a restart re-runs the original command line. A connector started with--max-workers 3keeps 3 through a restart even ifrunner.jsonwas edited in between.--permissionsalready behaves this way; the help text now says so for the cap.--shadowrefusal named only the orchestrator and worker flags. It now names every flag that asks for the runner, this one included.Verified
bin/cion this branch.runner.json, a cap from the start over the file's, a start that takes the cap off, an edit during a run not changing the running cap, and the flag being kept, left alone and removed across starts.--shadow.Not verified
Summary by cubic
Lifts the connector's built-in four-worker cap: every request now gets a worker as it arrives unless a start sets one with
--max-workers N. The cap is kept inrunner.jsonasmax_workersand shown bybasecamp connect workers list.Behavior changes
runner.jsonalready hasmax_workers.runner.jsonwhile the connector runs applies at the next start.--max-workers 0removes a kept cap; a start without the flag leaves the kept cap alone.basecamp connect restartre-runs the original flags, so a cap set at start survives a restart.Written for commit 25940cc. Summary will update on new commits.


Confidence Score: 5/5 
This PR appears safe to merge; no actionable issues were found.Summary
Removes the default four-worker cap and adds
--max-workers. The chosen cap is saved inrunner.json, applied at startup, and shown byworkers list.Reviewed the source and related SDK references. No runtime tests were run.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A["Start connector"] --> B{"--max-workers given?"} B -->|Yes| C["Save the supplied cap"] B -->|No| D["Keep the saved cap"] C --> E["Choose the cap once"] D --> E E --> F{"Cap is zero?"} F -->|Yes| G["Workers start without waiting for a slot"] F -->|No| H["Workers wait for one of N slots"]Reviews (1) · Last reviewed commit: "Lift the connector's cap on workers, and..." · Reviewed by Greptile