Skip to content

Make the foreground pause scope configurable - #3

Open
hawkeyexl wants to merge 1 commit into
mainfrom
per-provider-foreground-scope
Open

hawkeyexl wants to merge 1 commit into
mainfrom
per-provider-foreground-scope

Conversation

@hawkeyexl

Copy link
Copy Markdown

What

Adds a foregroundScope config 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.max over activity across both providers. Working in Claude therefore held an expiring Codex allowance idle — even for a skill registered providers: ["codex"] only.

Which behaviour is correct depends on what the pause is protecting, and the two readings disagree:

  • If the concern is quota, pausing both is over-broad and wastes an allowance that is about to expire — the exact thing this tool exists to spend.
  • If the concern is the machine and the working tree, pausing both is right: an agent editing files, hitting APIs, or opening PRs underneath you is equally disruptive whichever tool you happen to be sitting in.

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 eligibility filter and the re-check inside the admit transaction — now go through a single foregroundReason(store, config, provider, now) helper, so the two cannot drift apart. eligibility filters paused providers out of the candidate list instead of returning an empty one.

Under any the helper runs once per candidate and would report an identical reason per provider, so reasons are deduped before reaching doctor. Under provider, reasons are prefixed (claude: Foreground agent is active) so doctor names 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:

  • per-provider pause, asserting admit actually launches on the free provider
  • per-provider idle delay
  • the any default pausing both and reporting it exactly once

Also verified through the CLI: config set foregroundScope provider persists, and an invalid value is rejected with expected one of "any"|"provider". This needed raw-string handling in config set, which otherwise JSON.parses every value and would have required '"provider"'.

Not addressed

slots remains a global pool. Under provider, 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

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>
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