fix agent status identity recovery gaps

This commit is contained in:
Merge Sim
2026-09-11 00:20:29 -07:00
parent 6b352d5ac9
commit a3337003c2
4 changed files with 41 additions and 7 deletions
@@ -88,6 +88,9 @@ export abstract class AgentHookServerIngestTerminal extends AgentHookServerInges
) {
// Why: OSC has no child identity or lead boundary, so it cannot replace a persisted child-only proof before the lifecycle hook arrives.
this.commitStatusRowMutation(mutationBefore, previous)
if (mutationBefore !== undefined) {
this.emitEnrichedStatus(previous)
}
return
}
// Why: preserve the hook-completed turn stamp while OSC repaints the current state.
@@ -197,6 +197,31 @@ describe('the terminal handle a status row is stamped with', () => {
)
})
it('publishes only the remint observation for a Claude child-only row', () => {
const server = new AgentHookServer()
const enriched = vi.fn()
server.subscribeEnrichedStatus(enriched)
const payload = { state: 'working' as const, prompt: 'ship it', agentType: 'claude' as const }
ingest(server, { payload })
const row = server._getStateForTests().lastStatusByPaneKey.get(PANE_KEY) as
| { claudeLeadBoundaryChildOnly?: true }
| undefined
if (!row) {
throw new Error('expected seeded status row')
}
row.claudeLeadBoundaryChildOnly = true
enriched.mockClear()
ingest(server, { payload })
expect(enriched).not.toHaveBeenCalled()
ingest(server, { paneKey: NEW_PANE_KEY, tabId: 'tab-reminted', payload })
expect(enriched).toHaveBeenCalledOnce()
expect(enriched).toHaveBeenCalledWith(
expect.objectContaining({ paneKey: NEW_PANE_KEY, terminalHandle: HANDLE })
)
})
it('does not renew freshness from a provider-session-only dismissal remnant', () => {
const server = new AgentHookServer()
const freshness = vi.fn()
+6 -6
View File
@@ -190,10 +190,10 @@ async function startOrcadRuntime(
// which is safe but silently discards accept records on every launch.
initSshHostKeyStoreFile(profile.dataFile)
uninstallObservedStatusIdentity = agentHookServer.subscribeEnrichedStatus((enriched) =>
observedStatusCapture.observe(enriched)
)
if (isAgentStatusHooksEnabled(store.getSettings())) {
uninstallObservedStatusIdentity = agentHookServer.subscribeEnrichedStatus((enriched) =>
observedStatusCapture.observe(enriched)
)
await agentHookServer.start({ env: 'production', userDataPath: runtimeUserDataPath })
}
@@ -258,9 +258,6 @@ async function startOrcadRuntime(
// spawning an unauthenticated agent.
await registerHeadlessPtyRuntime(runtime, undefined, () => store.getSettings(), undefined, store)
// PTY recovery binds terminal identities; only now can startup spool observations be fenced.
observedStatusCapture.attach(runtime)
// Why: same post-registration reconciliation `--serve` performs. Skipping it leaves
// restored orchestration rows claiming an authority this host never took over.
// Why before the RPC server binds: a client host attaching first would find no pages to recover.
@@ -269,6 +266,9 @@ async function startOrcadRuntime(
await runtime.refreshRestoredOrchestrationAuthority()
await runtime.reconcileLegacyWorkerTerminals()
// Recovery binds terminal and dispatch identities; only now can startup observations be fenced.
observedStatusCapture.attach(runtime)
const bindHost = resolveOrcadBindHost(options.bind)
rpc = new OrcaRuntimeRpcServer({
runtime,
@@ -78,12 +78,14 @@ describe('headless PTY registry hydration ordering', () => {
const runtime = source.indexOf('const runtime = new OrcaRuntimeService(')
const identityReader = source.indexOf('readObservedAgentStatusPaneIdentity:', runtime)
const identitySubscription = source.indexOf('agentHookServer.subscribeEnrichedStatus(')
const hooksEnabled = source.indexOf('if (isAgentStatusHooksEnabled(', identitySubscription)
const identityFlush = source.indexOf('observedStatusCapture.attach(runtime)', runtime)
expect(runtime).toBeGreaterThanOrEqual(0)
expect(identityReader).toBeGreaterThan(runtime)
expect(identitySubscription).toBeGreaterThanOrEqual(0)
expect(identitySubscription).toBeLessThan(runtime)
expect(hooksEnabled).toBeGreaterThan(identitySubscription)
expect(identityFlush).toBeGreaterThan(runtime)
expect(source.slice(identitySubscription, runtime)).toContain(
'observedStatusCapture.observe(enriched)'
@@ -96,13 +98,17 @@ describe('headless PTY registry hydration ordering', () => {
const hookStart = source.indexOf('await agentHookServer.start(', subscription)
const runtime = source.indexOf('const runtime = new OrcaRuntimeService(')
const handlers = source.indexOf('await registerHeadlessPtyRuntime(', runtime)
const identityRecovery = source.indexOf('await runtime.refreshRestoredOrchestrationAuthority()')
const workerRecovery = source.indexOf('await runtime.reconcileLegacyWorkerTerminals()')
const replay = source.indexOf('observedStatusCapture.attach(runtime)', runtime)
expect(subscription).toBeGreaterThanOrEqual(0)
expect(hookStart).toBeGreaterThan(subscription)
expect(runtime).toBeGreaterThan(hookStart)
expect(handlers).toBeGreaterThan(runtime)
expect(replay).toBeGreaterThan(handlers)
expect(identityRecovery).toBeGreaterThan(handlers)
expect(workerRecovery).toBeGreaterThan(identityRecovery)
expect(replay).toBeGreaterThan(workerRecovery)
expect(source.slice(subscription, runtime)).toContain('observedStatusCapture.observe(enriched)')
expect(source.slice(replay)).toContain('observedStatusCapture.attach(runtime)')
})