mirror of
https://github.com/stablyai/orca.git
synced 2026-10-27 16:02:42 +00:00
fix(agent-status): count empty-inventory reaps as removal evidence
Review finding round 12: reaping the last inventory-derived rows emitted nothing, stranding the previously published working until the sweeper. The reap is same-source-correlated removal evidence — the authority that minted those rows now reports empty — so it rides the existing done veto chain.
This commit is contained in:
@@ -3372,6 +3372,26 @@ describe('shared agent-hook-listener', () => {
|
||||
expect(staleStop?.payload.subagents).toEqual([expect.objectContaining({ id: 'a-new' })])
|
||||
})
|
||||
|
||||
it('resolves to done when an empty inventory reaps the last inventory-derived rows', () => {
|
||||
// Why: the authority that minted a1's row now reports empty; a silent
|
||||
// drain would strand the previously published working until the sweeper.
|
||||
const first = claudeEvent({
|
||||
hook_event_name: 'SubagentStop',
|
||||
agent_id: 'ghost-1',
|
||||
background_tasks: [
|
||||
{ id: 'a1', type: 'subagent', status: 'running', agent_type: 'general-purpose' }
|
||||
]
|
||||
})
|
||||
expect(first?.payload.state).toBe('working')
|
||||
const second = claudeEvent({
|
||||
hook_event_name: 'SubagentStop',
|
||||
agent_id: 'ghost-2',
|
||||
background_tasks: []
|
||||
})
|
||||
expect(second?.payload.state).toBe('done')
|
||||
expect(second?.payload.subagents).toBeUndefined()
|
||||
})
|
||||
|
||||
it('clears restored phantom rows when a no-lead stop carries a complete empty inventory', () => {
|
||||
seedClaudeSubagentRosterFromSnapshots(state, PANE_KEY, [
|
||||
{ id: 'a1', state: 'working', startedAt: 1, agentType: 'general-purpose' },
|
||||
|
||||
@@ -2474,10 +2474,13 @@ function normalizeClaudeSubagentLifecycleEvent(
|
||||
if (inventory.present && inventory.tasks.length === 0 && roster) {
|
||||
// Why: an empty complete inventory reaps only claims not backed by live lifecycle events
|
||||
// (restored/inventory-derived phantoms). A delayed stale stop's empty list must not clear
|
||||
// a newer live start's row — that is the uncorrelated-retirement race again.
|
||||
// a newer live start's row — that is the uncorrelated-retirement race again. The reap is
|
||||
// same-source-correlated removal evidence: the authority that minted those rows now
|
||||
// reports empty, so a silent drain must not strand the published working.
|
||||
for (const [id, tracked] of roster) {
|
||||
if (tracked.restoredFromSnapshot || tracked.backgroundTasksAuthoritative) {
|
||||
roster.delete(id)
|
||||
removedRow = true
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
|
||||
Reference in New Issue
Block a user