Repository navigation
Bash auto-promotion can waste agent context and tokens on long validation commands #124
Description
Short description
Bash auto-promotion can waste agent context on long validation commands
What happened?
TL;DR
Bash commands are auto-promoted to background after 8 seconds. In some use cases that does not make the session more efficient.
Refer to attached agent example session, where I have a 10-15min build/validation script running in Docker.
In this case I want the AI to sit tight until it gets a list of failing tests to debug and fix them. Instead, the AI was mulling around doing random ceremony.
After promotion of the test/build script, the session accumulated roughly:
- 16 additional tool calls
- 17 assistant responses
- 5
bash_statuscalls - about 120k transcript characters, roughly 30k tokens by a crude chars/4 estimate
The command then hit the hard 600-second timeout and returned partial results. The timeout caused the failure, but backgrounding made the session noisier and gave the agent many chances to burn context before the real result existed.
The core issue: backgrounding is useful for servers, watchers, and genuinely parallel work. It is not automatically useful for deterministic validation commands.
Concrete motivating case
My script runs a sequence of Docker-backed checks, including Cargo, Clippy, and TypeScript validation. Cargo compilation alone takes around 14 minutes in this environment, before the rest of the suite completes.
My AFT bash settings are:
"bash": {
"rewrite": false,
"compress": false,
"background": false,
"subagent_background": false,
"long_running_reminder_enabled": false,
"long_running_reminder_interval_ms": 600000,
"foreground_wait_window_ms": 8000
}Observed behavior:
- I don't want bash calls to be put in background.
- After 8 seconds, AFT auto-promoted it to a background task anyway.
- The agent then performed status checks, narration, recovery from status timeout, and manual reading of output files.
- The validation command eventually timed out at about 601 seconds with exit 124.
This made the user experience look like bash.background: false was ignored, even though technically only explicit backgrounding was disabled.
Problems
1. bash.background: false is ambiguous
A user reading this config reasonably expects:
Do not put bash commands in the background.
Current behavior appears to mean:
Do not allow explicit background starts, but still auto-promote long foreground commands after
foreground_wait_window_ms.
That distinction may make sense internally, but the config name does not communicate it.
2. Auto-promotion is treated as generally helpful
For some commands, it is helpful:
- dev servers
- watchers
- long-running indexers
- benchmarks where the agent has useful parallel work
For deterministic validation commands, it can be harmful:
- the useful result is the final exit code and final output
- intermediate status checks usually do not help
- each assistant/tool turn consumes context
- agents tend to poll even when told not to
- the command may still fail if the original timeout is too low
In this case, the efficient path was probably:
- run the validation command;
- wait with a long enough timeout;
- return the final result once.
Just let the bash script do it's thing. There isn't anything else more productive for AI to do in the mean time. And if there is, it should spawn a subagent to do the side thing.
3. The 8-second foreground window is too short for build/test workflows
Eight seconds is enough to identify an interactive command as “not immediately done,” but not enough to infer that backgrounding is useful.
For Rust, TypeScript, Docker, and monorepo validation scripts, 8 seconds may only cover startup overhead.
4. Timeout guidance is missing
The command timeout was 600 seconds. This Cargo compilation alone can take around 14 minutes in this environment.
AFT should help surface this mismatch. A build/test/check command with a 10-minute timeout may be doomed before it starts, especially in Docker or cold-cache conditions.
5. bash_status is too easy to misuse
The tool output says not to poll, but the session still produced repeated status checks.
For background validation tasks, AFT should bias agents toward a blocking wait-until-exit primitive, not repeated snapshots.
6. Runtime messages appear stale in places
Some background-disabled messages still refer to experimental.bash.background, while the current user-facing config uses top-level bash.background.
That makes an already confusing behavior look more broken.
Requested improvements
1. Split explicit backgrounding from auto-promotion
Add separate config semantics, for example:
"bash": {
"explicit_background": false,
"auto_promote_foreground": false,
"subagent_background": false,
"foreground_wait_window_ms": 8000
}Alternative shape:
"bash": {
"background": {
"explicit": false,
"auto_promote": false,
"subagents": false
}
}Expected behavior:
explicit_background: falseblocksbash({ background: true }).auto_promote_foreground: falseprevents foreground commands from being promoted to background.subagent_background: falsekeeps current subagent safety behavior.
Backward compatibility option:
- Treat existing
bash.background: falseas disabling both explicit backgrounding and auto-promotion. - Or keep the current behavior for one release, but emit a warning that
bash.backgroundonly controls explicit backgrounding and that auto-promotion has a separate setting.
Preferred behavior: bash.background: false should disable all backgrounding because that matches normal user expectation.
2. Add a validation/wait mode for long deterministic commands
AFT should support a mode optimized for build/test/check scripts:
"bash": {
"validation_timeout_ms": 1800000,
"validation_auto_promote": false
}or at tool-call level:
{
"command": "...",
"mode": "wait",
"timeout": 1800000
}Behavior:
- do not auto-promote;
- wait for completion;
- return final output once;
- avoid extra assistant/tool turns;
- preserve output truncation metadata and output file paths.
This is a better fit for Cargo, Clippy, TypeScript, Docker, and project check scripts.
3. Detect likely validation commands and warn about low timeout
If a command contains common validation patterns, AFT should warn when the timeout is likely too low.
Examples:
cargo checkcargo testcargo clippynpm testpnpm testbun testtscvitestdocker build- project scripts named
check,test,ci,validate
Example warning:
This looks like a build/test validation command. Timeout is 600s. Rust/Docker validation can exceed this, especially on cold caches. Consider 1800s or configure bash.validation_timeout_ms.
This should not block execution. It should make the failure mode obvious.
4. Prefer wait/watch over polling
When AFT does auto-promote a foreground command, the returned message should push the agent toward a blocking wait primitive.
Instead of primarily saying:
use bash_status(...) to inspect output
prefer:
Use bash_watch({ taskId, exit: true, timeoutMs: ... }) to wait for completion. Avoid polling with bash_status; it is snapshot-only.
Also consider:
- warning after repeated
bash_statuscalls on the same running task; - adding “recommended next action: bash_watch” to
bash_statuswhile the process is still running; - exposing a simple
bash_waittool ifbash_watchis too general for agents.
5. Make reminder messaging match config
If long_running_reminder_enabled: false, auto-promotion should not tell the user that a completion reminder will be delivered.
The message should distinguish:
Completion reminder is enabled.
from:
No completion reminder is configured. Use bash_watch to wait for completion.
6. Update stale config references
Replace references to deprecated config keys such as:
experimental.bash.background
with the current top-level config shape.
Example:
background bash is disabled by bash.background: false
If separate explicit/auto-promote fields are introduced, the message should name the exact field that blocked or allowed the behavior.
7. Show resolved bash behavior in diagnostics
Add resolved bash behavior to aft doctor, aft configure, or equivalent diagnostics:
Bash:
hoisted: true
rewrite: false
compress: false
explicit_background: false
auto_promote_foreground: true
subagent_background: false
foreground_wait_window_ms: 8000
long_running_reminder_enabled: false
This would make the current behavior obvious before the user has to infer it from a live session.
Acceptance criteria
-
Users can configure AFT so bash commands are never backgrounded, including no foreground auto-promotion.
-
Explicit background execution and foreground auto-promotion are separately configurable.
-
Existing
bash.background: falseno longer creates the impression that the setting is ignored. -
Long build/test/check commands can run as a single wait-until-exit operation with a realistic timeout.
-
AFT warns when likely validation commands have obviously low timeouts.
-
Auto-promoted tasks guide agents toward
bash_watchor wait-until-exit behavior, not repeatedbash_statuspolling. -
Repeated polling is discouraged or warned about.
-
Runtime messages use current config names, not deprecated
experimental.bash.*names. -
Resolved bash behavior is visible in diagnostics.
-
Tests cover:
- explicit background disabled;
- foreground auto-promotion disabled;
bash.background: falsebehavior;- validation command timeout warning;
- reminder disabled messaging;
- stale
experimental.bash.*message removal; - guidance toward wait/watch instead of repeated status polling.
To sum up
AFT’s background bash support is useful, but it should not be treated as a catch-all solution for long commands.
For validation commands, the most efficient agent behavior is often boring: run the command, wait long enough, return the final result once. In the observed session, auto-promotion did not save time. It mostly converted one long validation wait into many extra tool calls and assistant turns before the same timeout failure.
Diagnostics
Plugin version
v0.39.2
AFT binary version
v0.39.2
Platform
windows x64
Activity
alfonso-aft commentedon Jun 17, 2026
Fixed in v0.39.3, in two parts:
bash.background: falsenow fully disables the background surface — thebash_status/bash_kill/bash_watchtools are no longer registered, thebackground/ptyparameters are dropped frombash, and long commands run to completion in the foreground (bounded bytimeout, default 30 min). Previously it only blocked explicitbackground: truewhile long commands still auto-promoted.bash_watchis now interruptible (a message disengages the wait), so agents are steered to block on it when the result is the next thing they need, end the turn when there's parallel work, and not loopbash_status. The syncbash_watchcap was also raised from 5 to 30 minutes so it can wait out a real build.