Repository navigation
Keep an Antigravity automation running while its subagents outlive the parent's turn - #34084
brennanb2025 wants to merge 7 commits into
Conversation
…s outlive the parent's turn agy reports its own turn over OSC 7501 but not the invoke_subagent children it runs in-process, so when a parent ended its turn to wait, its program status said done while its Stop hook said fullyIdle=false. Program status yields to a disagreeing hook row only for two minutes of hook silence, and subagent hooks never refresh the parent's row, so a subagent that ran past that window let the program's done settle the pane: the automation completed and closed the tab mid-work. Antigravity rows now publish the lead's own verdict through the shared lead/child fold (fullyIdle=false is agy's word that work outlives the turn), and a same-agent program done no longer settles a hook row that is held open by child work. Session boundaries, other apps and rows not held open keep the silence fallback. Tracked in #33973.
|
No actionable comments were generated in the recent review.
|
There was a problem hiding this comment.
Actionable comments posted: 1

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
775ce985-da8f-446e-a77e-783c92e3611d
Files selected for processing: 13-
src/main/agent-hooks/server-closed-tab-suppression.test.tssrc/main/agent-hooks/server-main-agent-turn-verdicts.test.tssrc/main/agent-hooks/server-program-status-hook-yield.test.tssrc/main/agent-hooks/server-turn-ended-at.test.tssrc/main/agent-hooks/server.test-fixtures.tssrc/main/agent-hooks/server/program-status/hook-precedence.test.tssrc/main/agent-hooks/server/program-status/hook-precedence.tssrc/main/agent-hooks/server/server-tab-cleanup.tssrc/shared/agent-hook-cancel-verdict-latch.tssrc/shared/agent-hook-listener-antigravity.test.tssrc/shared/agent-hook-listener-extraction-characterization.test.tssrc/shared/agent-hook-listener/listener-state.tssrc/shared/agent-hook-listener/providers/antigravity-events.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
-
| eventName !== 'Stop' && | ||
| state.antigravityCompletedTranscriptByPaneKey.get(paneKey) === transcriptPath | ||
| state.antigravityCompletedTranscriptByPaneKey.get(paneKey) === transcriptPath && | ||
| (eventName !== 'Stop' || stopStillBusy) |
There was a problem hiding this comment.
🔎 Supported by static analysis
-
Script executed:
sed -n 1,130p src/shared/agent-hook-listener/providers/antigravity-events.ts sed -n 1,60p src/main/agent-hooks/server/program-status/hook-precedence.ts
Repository: stablyai/orca
Length of output: 7368
Script executed:
set -u echo "=== antigravity-event-rules.ts ===" sed -n 1,200p src/shared/agent-hook-listener/providers/antigravity-event-rules.ts echo "=== agent-lead-status-fold.ts ===" sed -n 1,200p src/shared/agent-lead-status-fold.ts echo "=== server ingest guards (stale/turn/invocation) ===" rg -n -i 'stale|turnId|turn_id|invocation' src/main/agent-hooks/server.ts src/main/agent-hooks/server || test "$?" -eq 1 echo "=== transcript path references near antigravity ===" rg -n -i 'antigravity' src/main/agent-hooks src/shared/agent-hook-listener src/cli -l || test "$?" -eq 1 rg -n -i 'transcript_?path' src/main/agent-hooks src/shared/agent-hook-listener src/cli || test "$?" -eq 1
Repository: stablyai/orca
Length of output: 29012
Script executed:
set -u echo "=== server-foreign-turn-hook.ts ===" sed -n 1,80p src/main/agent-hooks/server/server-foreign-turn-hook.ts echo "=== callers of foreign turn guard ===" rg -n -F -- 'server-foreign-turn-hook' src/main/agent-hooks || test "$?" -eq 1 echo "=== antigravity installer/registry transcript or Stop fields ===" rg -n -i 'antigravity' src/main/agent-hooks/managed-agent-hook-registry.ts src/main/agent-hooks/remote-managed-hook-installers.ts || test "$?" -eq 1 rg -n -i 'transcript' src/main/agent-hooks/managed-agent-hook-registry.ts || test "$?" -eq 1
Repository: stablyai/orca
Length of output: 2665
Script executed:
set -u echo "=== server-status-update.ts ===" sed -n 1,140p src/main/agent-hooks/server/server-status-update.ts echo "=== server-status-inference.ts 105-135 ===" sed -n 105,135p src/main/agent-hooks/server/server-status-inference.ts
Repository: stablyai/orca
Length of output: 7640
Key the completed-turn cache by conversation when no transcript path exists.
An idle Stop with conversationId and no transcriptPath records no completion. A delayed busy Stop for the same conversation then skips the completion guard. It folds to a working row, and mainAgent.state becomes done. programStatusHookYieldEndsAt then returns Infinity for a same-agent program done, so the row stays working until a later hook changes it.
Use transcriptPath ?? conversationId as the cache key. The existing PreInvocation reset already clears this per-pane entry.
🐛 Suggested fix
-
const stopStillBusy = eventName === 'Stop' && isAntigravityStopStillBusy(hookPayload) + const completionKey = transcriptPath ?? conversationId if (eventName === 'PreInvocation') { state.antigravityCompletedTranscriptByPaneKey.delete(paneKey) } else if ( - transcriptPath && - state.antigravityCompletedTranscriptByPaneKey.get(paneKey) === transcriptPath && + completionKey && + state.antigravityCompletedTranscriptByPaneKey.get(paneKey) === completionKey && (eventName !== 'Stop' || stopStillBusy) ) {
if (eventName === 'Stop' && !stopStillBusy) { state.antigravitySubagentParentByPaneKey.delete(paneKey) - if (transcriptPath) { - state.antigravityCompletedTranscriptByPaneKey.set(paneKey, transcriptPath) + if (completionKey) { + state.antigravityCompletedTranscriptByPaneKey.set(paneKey, completionKey) } }
There was a problem hiding this comment.
🔇 Additional comments: 1
src/shared/agent-hook-listener-antigravity.test.ts-531-532: 1
531-532:Minor · Functional Correctness · Quick win
⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.Pass the fixed timing values to
setAgentStatus.
normalizeAgentStatusEventdrops top-levelreceivedAtandstateStartedAt. Because no timing argument is passed,setAgentStatusstill usesDate.now()for each store. (raw.githubusercontent.com)Pass these values through the fourth
setAgentStatusargument, or use fake timers. Otherwise, this fixture remains wall-clock dependent.

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
ce620f0b-6a13-4554-a8dd-c1c9ecbd5eb9
Files selected for processing: 1-
src/shared/agent-hook-listener-antigravity.test.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.
-
|
Closing as superseded by #34128, which landed the same fix for #33973 on main (Antigravity rows mark the lead's turn done but held open while Checked against main 7713098 before closing: the original two-minute repro passes on main, and the regressions this PR's review found in its broader variant (child question suppressed by the new clause, late final text lost, an inferred interruption erased) do not reproduce there. One pre-existing behaviour remains on main, unchanged by either PR: a delayed |
An Antigravity automation can finish and close its terminal while a subagent is still working. The parent has ended its own turn to wait, so its OSC status says done, while its hooks correctly say background work remains. After two minutes without another parent hook, Orca trusts that narrower status and completes the run with the waiting message instead of the final report. Tracked in #33973.
The fix publishes the parent’s state separately from the combined state. A root-only done report cannot complete a row still held open by child work. The parent’s final idle Stop, a later wake, and real session boundaries still settle it. The existing fallback remains for other agents, explicit child questions, and rows whose parent is still working.
Review also covered delayed observations: a completed transcript rejects stale busy events while allowing idle Stop retries to recover final text. A finished status without a recognized outcome remains unknown and cannot immediately erase an accepted interruption. Explicit outcomes and new turns retain their authority; the existing bounded cancellation policy stays unchanged.
This is the full review path: the change affects event ordering, asynchronous completion and independently updated relay hosts. It reuses the existing optional lead-state field and does not add a saved format or wire operation.
Differences from the common pattern are intended: external terminal hooks can report child work that the program omits, so those hooks remain authoritative for that work. If every final-wake hook and its replay is lost, a child-held row waits for later evidence or session end. Older relays without the optional lead field retain their existing behavior until updated; mixed versions remain supported. Terminal cancellation remains inferred where no owned provider turn is available, using the existing bounded policy.
Validation is being completed on the final head. Baseline agy1.4.7 evidence reproduced premature completion at120,002ms with a real model and120,011ms with a scripted145s child. In the real run, translation files existed but the parent report was absent and a child was still active. The scripted run confirmed all21 hooks reached Orca. No baseline failure screenshot was captured; raw hooks, OSC, accepted rows and automation timelines establish the failure.