mirror of
https://github.com/stablyai/orca.git
synced 2026-10-21 00:03:46 +00:00
fix(agent-status): resolve identity-matched child drains to done without a lead record
Review finding round 3: rosterChanged let a known-child drain bypass the no-evidence guard into the unconditional 'working' default — sticky until the stale sweeper when no lead hook ever arrives. With no lead record the pane state now follows tracked children: live child -> working, identity- matched drain/park -> done. Duplicate stale stops stay suppressed.
This commit is contained in:
@@ -3132,6 +3132,30 @@ describe('shared agent-hook-listener', () => {
|
||||
expect(state.claudeSubagentRosterByPaneKey.has(PANE_KEY)).toBe(false)
|
||||
})
|
||||
|
||||
it('resolves a known-child drain to done instead of sticky working when no lead ever spoke', () => {
|
||||
claudeEvent({
|
||||
hook_event_name: 'SubagentStart',
|
||||
agent_id: 'a1',
|
||||
agent_type: 'general-purpose'
|
||||
})
|
||||
// Why: the child's own identity-matched stop drains the roster; the old no-lead
|
||||
// 'working' default left this pane spinning until the stale sweeper.
|
||||
const drained = claudeEvent({ hook_event_name: 'SubagentStop', agent_id: 'a1' })
|
||||
expect(drained?.payload.state).toBe('done')
|
||||
expect(drained?.payload.subagents).toBeUndefined()
|
||||
})
|
||||
|
||||
it('suppresses a duplicate stale stop after a no-lead drain', () => {
|
||||
claudeEvent({
|
||||
hook_event_name: 'SubagentStart',
|
||||
agent_id: 'a1',
|
||||
agent_type: 'general-purpose'
|
||||
})
|
||||
claudeEvent({ hook_event_name: 'SubagentStop', agent_id: 'a1' })
|
||||
const duplicate = claudeEvent({ hook_event_name: 'SubagentStop', agent_id: 'a1' })
|
||||
expect(duplicate).toBeNull()
|
||||
})
|
||||
|
||||
it('re-emits the lead done state on a start-less SubagentStop after a completed turn', () => {
|
||||
claudeEvent({ hook_event_name: 'UserPromptSubmit', prompt: 'go' })
|
||||
claudeEvent({ hook_event_name: 'Stop', background_tasks: [] })
|
||||
|
||||
@@ -2573,14 +2573,16 @@ function buildClaudeChildDrivenStatusPayload(
|
||||
// Why: no lead record, no live child, and an unchanged roster mean the event proves nothing is
|
||||
// running. Compact's start-less SubagentStop is exactly that; minting the default below left a
|
||||
// sticky 'working' no Stop ever clears on freshly resumed panes (STA-2915).
|
||||
if (!lead && !rosterChanged && !claudeRosterHasWorkingSubagent(roster)) {
|
||||
const rosterHasWorking = claudeRosterHasWorkingSubagent(roster)
|
||||
if (!lead && !rosterChanged && !rosterHasWorking) {
|
||||
return null
|
||||
}
|
||||
// Why: default 'working' — a spawn proves activity even before the lead's first state-bearing event (e.g. Orca restarted mid-session).
|
||||
const leadState = lead?.state ?? 'working'
|
||||
// Why: with no lead record the pane state is inferred from tracked children only — a live child
|
||||
// shows working, while an identity-matched drain/park resolves to done. The old unconditional
|
||||
// 'working' default stuck until the stale sweeper once the last child left (STA-2915 review).
|
||||
const leadState = lead?.state ?? (rosterHasWorking ? 'working' : 'done')
|
||||
return buildClaudeStatusPayload(state, eventName, '', paneKey, hookPayload, {
|
||||
stateName:
|
||||
leadState === 'done' && claudeRosterHasWorkingSubagent(roster) ? 'working' : leadState,
|
||||
stateName: leadState === 'done' && rosterHasWorking ? 'working' : leadState,
|
||||
updateToolSnapshot: false,
|
||||
interrupted: lead?.interrupted
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user