Skip to content

fix(subagent): publish terminal status on LLM timeout and capture debug dumps#6406

Merged
bug-ops merged 1 commit into
mainfrom
fix/6381-6391-subagent-llm-visibility
Jul 17, 2026
Merged

fix(subagent): publish terminal status on LLM timeout and capture debug dumps#6406
bug-ops merged 1 commit into
mainfrom
fix/6381-6391-subagent-llm-visibility

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • call_provider_with_status (crates/zeph-subagent/src/agent_loop.rs) now publishes a terminal Failed status via status_tx when the sub-agent's LLM call times out, mirroring the sibling provider-error arm. Previously the timeout branch early-returned via ? without publishing, leaving the TUI agents sidebar permanently showing a WORKING zombie entry with an ever-climbing elapsed counter that is never reaped by collect_finished_subagents().
  • Adds a DebugDumpSink trait (crates/zeph-llm/src/debug_dump.rs) threaded through SpawnContext/AgentLoopArgs and wired at zeph-core's build_spawn_context, so sub-agent and orchestration-dispatched LLM calls are captured by the debug_dump interceptor the same way top-level agent loop calls already are. This includes the /agent resume path, which previously passed None for spawn context and silently dropped debug-dump capture (and other spawn-context fields) for resumed sub-agents.

Scope notes

  • Sub-agent debug dumps get the same baseline scrub_content/redact_binary_blobs scrubbing as top-level dumps (secrets, binary blobs), but skip the optional extra PiiFilter layer top-level dumps get when enabled. This is a deliberate, documented scope decision, not an oversight — a follow-up issue will track closing this parity gap.
  • A more robust join_handle-based zombie-reaping fallback (covering a hypothetical panic in run_agent_loop, not just the timeout path fixed here) is out of scope for this PR — tracked as a separate follow-up.

Test plan

  • New regression test for TUI agents sidebar shows sub-agent as WORKING forever after LLM-call timeout (zombie entry, elapsed counter climbs indefinitely) #6381: triggers the LLM-call-timeout branch specifically and asserts the terminal Failed status lands on status_rx/SubAgentManager::statuses(), not just that the function returns an error.
  • New regression tests for debug_dump interceptor doesn't capture sub-agent / orchestration LLM calls #6391: drive a real sub-agent LLM call end-to-end with a RecordingDumpSink, assert a correlated request+response dump pair is produced; verified via grep that both standalone /agent spawn paths and the orchestration-dispatched spawn path route through the same build_spawn_context wiring point.
  • New regression test for the resume-path fix: drives a real resume() call with a RecordingDumpSink, polls to terminal status, asserts the sink was invoked.
  • cargo +nightly fmt --check
  • cargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings
  • cargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins — 14120/14120 passed
  • rustdoc gate (RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler")
  • gitleaks protect --staged
  • .local/testing/playbooks/subagent-llm-visibility.md and .local/testing/coverage-status.md updated
  • CHANGELOG.md [Unreleased] updated

Closes #6381
Closes #6391

@github-actions github-actions Bot added documentation Improvements or additions to documentation llm zeph-llm crate (Ollama, Claude) rust Rust code changes core zeph-core crate bug Something isn't working size/XL Extra large PR (500+ lines) labels Jul 17, 2026
@bug-ops
bug-ops enabled auto-merge (squash) July 17, 2026 18:14
@bug-ops
bug-ops force-pushed the fix/6381-6391-subagent-llm-visibility branch from 4e19341 to 8df124d Compare July 17, 2026 18:16
…ug dumps

call_provider_with_status now publishes a terminal Failed status via
status_tx when the sub-agent's LLM call times out, mirroring the sibling
provider-error arm. Previously the timeout branch early-returned without
publishing, leaving the TUI agents sidebar showing a permanent WORKING
zombie entry with an ever-climbing elapsed counter.

Adds a DebugDumpSink trait (zeph-llm) threaded through SpawnContext and
AgentLoopArgs so sub-agent and orchestration-dispatched LLM calls are
captured by the debug_dump interceptor the same way top-level agent loop
calls are, including on the /agent resume path which previously dropped
the spawn context entirely.

Closes #6381
Closes #6391
@bug-ops
bug-ops force-pushed the fix/6381-6391-subagent-llm-visibility branch from 8df124d to 2999a6c Compare July 17, 2026 18:24
@bug-ops
bug-ops merged commit 27f3237 into main Jul 17, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/6381-6391-subagent-llm-visibility branch July 17, 2026 18:35
bug-ops added a commit that referenced this pull request Jul 17, 2026
…just status_rx

collect_finished_subagents() previously determined reapability purely from
status_rx terminal state, so a panicking sub-agent task left its handle
frozen in the last observed Working state and never got reaped, reproducing
the zombie-sidebar-entry symptom via a path #6406 did not close. Add
join_handle.is_finished() as an independent terminal signal.

collect() also early-returned via `?` on a panicked join_handle, skipping
fleet mark_terminal and the final TranscriptMeta write; flattened the
double-Result so both still run for panicked tasks.

Closes #6408
bug-ops added a commit that referenced this pull request Jul 17, 2026
Sub-agent LLM-call debug dumps wired in #6406 received the baseline
scrub_content/redact_binary_blobs pass but skipped the optional extra
PiiFilter.scrub() layer that top-level dumps get when [classifiers]/PII
filtering is enabled, giving sub-agent dumps a weaker PII-redaction
posture than top-level dumps.

Add PiiScrubbingDumpSink, wrapping DebugDumper with the same PiiFilter
handle used by top-level dump construction, and wire it into
build_spawn_context's debug_dump_sink for both fresh spawn and
/agent resume. PiiFilter now derives Clone (cheap, Regex is Arc-backed).

Closes #6407
bug-ops added a commit that referenced this pull request Jul 17, 2026
…II in sub-agent debug dumps (#6414)

* fix(subagent): reap sub-agent handles on join_handle completion, not just status_rx

collect_finished_subagents() previously determined reapability purely from
status_rx terminal state, so a panicking sub-agent task left its handle
frozen in the last observed Working state and never got reaped, reproducing
the zombie-sidebar-entry symptom via a path #6406 did not close. Add
join_handle.is_finished() as an independent terminal signal.

collect() also early-returned via `?` on a panicked join_handle, skipping
fleet mark_terminal and the final TranscriptMeta write; flattened the
double-Result so both still run for panicked tasks.

Closes #6408

* fix(core): apply optional PiiFilter scrub to sub-agent debug dumps

Sub-agent LLM-call debug dumps wired in #6406 received the baseline
scrub_content/redact_binary_blobs pass but skipped the optional extra
PiiFilter.scrub() layer that top-level dumps get when [classifiers]/PII
filtering is enabled, giving sub-agent dumps a weaker PII-redaction
posture than top-level dumps.

Add PiiScrubbingDumpSink, wrapping DebugDumper with the same PiiFilter
handle used by top-level dump construction, and wire it into
build_spawn_context's debug_dump_sink for both fresh spawn and
/agent resume. PiiFilter now derives Clone (cheap, Regex is Arc-backed).

Closes #6407

* test(core): add negative-path regression test for #6408 reap fix

collect_finished_subagents_reaps_handle_whose_task_finished_without_terminal_status
covers the reap side of the join_handle.is_finished() backstop but nothing
guarded against over-eager reaping. Add a test asserting a handle whose
task is genuinely still running (status_rx == Working, is_finished() ==
false) survives collect_finished_subagents().

* fix(core): resolve review findings for #6408

Replace redundant closure with a bare function reference in the
negative-path regression test (clippy::redundant_closure under -D
warnings), and add the missing CHANGELOG.md entry for the #6408 fix.

* fix(core): remove broken rustdoc link in PiiScrubbingDumpSink docs

DebugState::write_chat_debug_dump is private (agent::state is
pub(crate)), so linking to it from a pub item trips
rustdoc::private_intra_doc_links under -D warnings. Drop the link
markup, keep the reference as plain inline code text.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working core zeph-core crate documentation Improvements or additions to documentation llm zeph-llm crate (Ollama, Claude) rust Rust code changes size/XL Extra large PR (500+ lines)

Projects

None yet

1 participant