fix(orchestration): stop projecting absence and a self-loop for a proven exit

This commit is contained in:
Jinwoo-H
2026-09-04 04:11:17 -04:00
parent dc28aa04fb
commit 64b3a58fff
3 changed files with 80 additions and 1 deletions
+6 -1
View File
@@ -61,7 +61,12 @@ export function projectOrchestrationFleetAttention(
if (facts.liveness.verdict === 'unverifiable') {
categories.push(facts.liveness.reason === 'stale_status' ? 'stale' : 'unverifiable')
}
if (facts.outcome === 'outcome_unknown' || facts.outcome === 'finished_unverified') {
// A proven exit is evidence, not absence: `unverifiable` beside an `exited` verdict told a
// reader to keep waiting on a worker the execution host had already reported gone.
if (
facts.liveness.verdict !== 'exited' &&
(facts.outcome === 'outcome_unknown' || facts.outcome === 'finished_unverified')
) {
if (!categories.includes('unverifiable')) {
categories.push('unverifiable')
}
@@ -439,4 +439,68 @@ describe('fleet liveness and attention after a host verdict', () => {
})
expect(projected.workers[0]!.nextAction.kind).toBe('inspect')
})
// The live worker-list row from a stopped worker: the same receipt proved the exit,
// called it absence, and pointed back at the command that reported the settlement.
it('never contradicts a proven exit on a stopped worker still owning its terminal', () => {
const projected = projectOrchestrationFleet({
workers: [
worker('1', {
workerState: 'stopped',
dispatchStatus: 'completed',
workerStage: 'process_stopped',
outcome: 'outcome_unknown',
terminalState: 'retained',
resource: {
id: 'resource-1',
ownerDispatchId: '1',
worktreeId: 'workspace-1',
paneKey: 'tab-1:leaf-1',
hostScope: null,
ownershipState: 'owned',
releaseState: 'active',
updatedAt: '2026-09-04T00:00:00.000Z'
}
})
],
statuses: [],
now: 10_000
})
const row = projected.workers[0]!
expect(row.liveness.verdict).toBe('exited')
expect(row.attention.categories).not.toContain('unverifiable')
expect(row.attention.requiresAction).toBe(false)
expect(row.nextAction).toEqual({
kind: 'release',
argv: ['orchestration', 'worker-release', '--dispatch', '1']
})
})
it('asks nothing more of a settled worker whose terminal is already released', () => {
const projected = projectOrchestrationFleet({
workers: [
worker('1', {
workerState: 'stopped',
dispatchStatus: 'completed',
outcome: 'outcome_unknown',
terminalState: 'retained',
resource: {
id: 'resource-1',
ownerDispatchId: '1',
worktreeId: 'workspace-1',
paneKey: 'tab-1:leaf-1',
hostScope: null,
ownershipState: 'user_owned',
releaseState: 'active',
updatedAt: '2026-09-04T00:00:00.000Z'
}
})
],
statuses: [],
now: 10_000
})
expect(projected.workers[0]!.nextAction).toEqual({ kind: 'none', argv: [] })
})
})
@@ -121,6 +121,16 @@ function nextAction(worker: FleetDurableWorker, liveness: FleetLiveness): FleetN
) {
return { kind: 'none', argv: [] }
}
// A settled worker still owning its terminal owes the release decision. Pointing it at
// worker-show was a self-loop: the command that reported the settlement.
if (SETTLED_WORKER_STATES.has(worker.workerState) && worker.resource) {
return worker.resource.ownershipState === 'owned' && worker.resource.releaseState !== 'released'
? {
kind: 'release',
argv: ['orchestration', 'worker-release', '--dispatch', worker.dispatchId]
}
: { kind: 'none', argv: [] }
}
// A proven exit under a worker that never settled is a stall; worker-show would
// only restate it. Read the transcript, then stop or abandon. `unverifiable` is
// absence and must never land here.