Conversation
The foreground gate paused every provider whenever any non-owned session was busy, and took the idle delay from the most recent activity across both. Working in Claude therefore held an expiring Codex allowance idle, even for a Codex-only skill. Which behaviour is correct depends on what the pause protects. If the concern is quota, pausing both wastes an allowance. If it is the machine and the working tree, pausing both is right, because an agent editing files underneath you is equally disruptive whichever tool you are sitting in. That is a property of someone's workflow, not something the scheduler should decide, so add foregroundScope rather than swap one hardcoded policy for another. foregroundScope defaults to 'any', preserving current behaviour on upgrade; 'provider' scopes the pause to the provider in use. Both call sites — the eligibility filter and the re-check inside the admission transaction — go through one foregroundReason helper so they cannot drift apart. Under 'any' that helper now runs per candidate, so identical reasons are deduped before they reach doctor. The existing foreground tests all used Claude-only fixtures, so the cross-provider case had no coverage and passed unchanged under either policy. Added tests for the per-provider pause (including that admit launches on the free provider), the per-provider idle delay, and the default pausing both while reporting it once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a
foregroundScopeconfig setting controlling how wide the foreground pause reaches:any(default) — pauses every provider whenever either native agent is active. This is today's behaviour, so nothing changes on upgrade.provider— pauses only the provider in use.Why
The gate paused every provider whenever any non-owned session was busy, and took the idle delay from
Math.maxover activity across both providers. Working in Claude therefore held an expiring Codex allowance idle — even for a skill registeredproviders: ["codex"]only.Which behaviour is correct depends on what the pause is protecting, and the two readings disagree:
Both are legitimate, and the right answer is a property of someone's workflow. So this makes it a setting rather than swapping one hardcoded policy for another. The reserve and runway logic is already per-provider, which is part of why the global gate was surprising.
How
Both call sites — the
eligibilityfilter and the re-check inside theadmittransaction — now go through a singleforegroundReason(store, config, provider, now)helper, so the two cannot drift apart.eligibilityfilters paused providers out of the candidate list instead of returning an empty one.Under
anythe helper runs once per candidate and would report an identical reason per provider, so reasons are deduped before reachingdoctor. Underprovider, reasons are prefixed (claude: Foreground agent is active) sodoctornames which tool is holding things up.Sessions Last Call owns stay excluded under both scopes, so its own agents never pause it.
Testing
npm run check— typecheck clean, 52 tests pass.Worth flagging: every existing foreground test used a Claude-only fixture, so the cross-provider case had no coverage and passed unchanged under either policy. I confirmed the new tests fail against the previous implementation (
2 failed | 49 passed) rather than passing vacuously. Added:admitactually launches on the free provideranydefault pausing both and reporting it exactly onceAlso verified through the CLI:
config set foregroundScope providerpersists, and an invalid value is rejected withexpected one of "any"|"provider". This needed raw-string handling inconfig set, which otherwiseJSON.parses every value and would have required'"provider"'.Not addressed
slotsremains a global pool. Underprovider, one provider can still starve the other of capacity even though neither pauses the other — arguably the same coupling one layer down. Left out as a separate concern.🤖 Generated with Claude Code