Short description
A completed foreground Bash task can remain unanswered until the host transport times out because the standalone NDJSON loop runs runtime maintenance before flushing ready deferred responses.
What happened?
On macOS arm64 with AFT 0.49.0 under a Pi harness, two Bash requests each timed out at the host transport boundary after 130 seconds. The corresponding persisted AFT Bash task metadata showed that one task had already reached a terminal state after about 4 seconds. After the two Bash timeouts, bash_drain_completions also received no response and eventually caused a bridge restart.
This is not a slow shell command: the Bash child had already exited, but its deferred response was not written to NDJSON stdout.
Expected behavior: once a foreground Bash task is terminal, its response should be flushed promptly even if watcher/index/configure maintenance is busy.
Diagnostics
- Host: Pi-based integration
- AFT bridge package: 0.49.0
- AFT binary: 0.49.0
- Platform: macOS arm64
- Source revision inspected:
566bcde25cbb2c24f09fdc5e4840a58a65a97677
Related: #117 fixed passive status polls incorrectly killing a busy bridge. That fix is already an ancestor of the revision above. This report is a separate failure mode: a terminal Bash task has a ready deferred response, but the binary does not emit it.
Source analysis
In crates/aft/src/main.rs, the periodic/request path currently does:
drain_runtime_events(registry);
write_ready_pending(registry.current(), pending)
A synchronous stall in drain_runtime_events can therefore starve every ready deferred response, including a completed foreground Bash task and bash_drain_completions. The observed sequence is consistent with this ordering.
I do not have a stack trace from the original stalled process, so I cannot identify the exact maintenance sub-phase that blocked. The ordering itself is independently reproducible as a starvation condition.
Proposed fix
Flush ready pending responses before runtime maintenance, then flush again after maintenance so responses that become ready during the drain are also emitted:
let ctx = registry.current();
let mut written = write_ready_pending(ctx, pending)?;
drain_runtime_events(registry);
written += write_ready_pending(ctx, pending)?;
Ok(written)
The same ordering should be used by the test writer helper.
Regression coverage performed
I added a focused regression that registers a ready deferred response and a configure-warning maintenance event. It asserts the pending response is polled/written before the maintenance callback runs; the prior ordering fails that assertion.
Validation of the local patch:
- pending response suite: 8 passed
- Bash terminal/promote state tests: 4 passed
- release binary built successfully
- direct
@cortexkit/aft-bridge smoke against the built binary completed a real Bash task and retrieved its output
I can provide the minimal patch and test if that would help.
Short description
A completed foreground Bash task can remain unanswered until the host transport times out because the standalone NDJSON loop runs runtime maintenance before flushing ready deferred responses.
What happened?
On macOS arm64 with AFT 0.49.0 under a Pi harness, two Bash requests each timed out at the host transport boundary after 130 seconds. The corresponding persisted AFT Bash task metadata showed that one task had already reached a terminal state after about 4 seconds. After the two Bash timeouts,
bash_drain_completionsalso received no response and eventually caused a bridge restart.This is not a slow shell command: the Bash child had already exited, but its deferred response was not written to NDJSON stdout.
Expected behavior: once a foreground Bash task is terminal, its response should be flushed promptly even if watcher/index/configure maintenance is busy.
Diagnostics
566bcde25cbb2c24f09fdc5e4840a58a65a97677Related: #117 fixed passive
statuspolls incorrectly killing a busy bridge. That fix is already an ancestor of the revision above. This report is a separate failure mode: a terminal Bash task has a ready deferred response, but the binary does not emit it.Source analysis
In
crates/aft/src/main.rs, the periodic/request path currently does:A synchronous stall in
drain_runtime_eventscan therefore starve every ready deferred response, including a completed foreground Bash task andbash_drain_completions. The observed sequence is consistent with this ordering.I do not have a stack trace from the original stalled process, so I cannot identify the exact maintenance sub-phase that blocked. The ordering itself is independently reproducible as a starvation condition.
Proposed fix
Flush ready pending responses before runtime maintenance, then flush again after maintenance so responses that become ready during the drain are also emitted:
The same ordering should be used by the test writer helper.
Regression coverage performed
I added a focused regression that registers a ready deferred response and a configure-warning maintenance event. It asserts the pending response is polled/written before the maintenance callback runs; the prior ordering fails that assertion.
Validation of the local patch:
@cortexkit/aft-bridgesmoke against the built binary completed a real Bash task and retrieved its outputI can provide the minimal patch and test if that would help.