test(runtime): execute the structured-host call site in worktree ps

Both structured-row suites called attachRuntimeWorktreeAgentRows directly
with summaries they built themselves, so nothing ever ran getWorktreePs's
own `getStructuredAgentSessionHost()?.liveSessionStatusSummaries()`. That
file carries `@ts-nocheck`, so renaming the accessor was green in typecheck
and in the suite, while `orca worktree ps` and mobile's 3s poll would throw
a TypeError for every user — the `?.` optional-chains the host, not the
method. Swapping the call back to a whole-cache read was equally invisible:
the liveness suite injects feed.liveSessionSummaries() itself, and the
string-match guard only needs the identifier to appear somewhere in the file.

Drives the real runtime with a stub host over a real status feed that has
published two sessions and forgotten one, asserting the live session's row
reaches ps output, that the live accessor is the one called, and that the
`?? []` fallback still returns a page with no host installed. The stub is
typed against the real host, so a class-side rename reddens tc here.
This commit is contained in:
Merge Sim
2026-09-07 13:48:04 -07:00
parent 3785a410c3
commit ce1024096b
2 changed files with 147 additions and 0 deletions
@@ -0,0 +1,146 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from '../orca-runtime-test-mocks.spec'
import { TEST_WORKTREE_ID, store } from '../orca-runtime-test-fixtures.spec'
import type { AgentJournalRenderItem } from '../../../shared/agent-session-journal-types'
import type {
AgentSessionStatusEvent,
AgentSessionStatusSummary
} from '../../../shared/agent-session-wire'
import type { AgentSessionJournal } from '../../native-chat/agent-session-journal/journal-store'
import type { StructuredAgentSessionHost } from '../../native-chat/agent-session-wire/structured-agent-session-host'
import {
getStructuredAgentSessionHost,
setStructuredAgentSessionHost
} from '../../native-chat/agent-session-wire/structured-agent-session-registry'
import { StructuredAgentSessionStatusFeed } from '../../native-chat/agent-session-wire/structured-agent-session-status-feed'
/**
* The production wiring, not the projection. Both structured-row suites call
* `attachRuntimeWorktreeAgentRows` directly with summaries they built themselves, so nothing
* executed `getWorktreePs`'s own `getStructuredAgentSessionHost()?.liveSessionStatusSummaries()`
* — and that file carries `@ts-nocheck`, so renaming the accessor stayed green in typecheck AND
* in the suite while `orca worktree ps` and mobile's poll would throw for every user.
*/
const HELD_SESSION = 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
const FORGOTTEN_SESSION = 'b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e'
const OBSERVED_AT = 1_757_030_400_000
function runningTurn(prompt: string): AgentJournalRenderItem[] {
return [
{
itemId: 'user-1',
sequence: 1,
revision: 1,
observedAt: OBSERVED_AT,
body: { kind: 'message', role: 'user', blocks: [{ type: 'text', text: prompt }] }
},
{
itemId: 'turn-1',
sequence: 2,
revision: 1,
observedAt: OBSERVED_AT,
body: {
kind: 'status',
text: 'Working',
turnLifecycle: { turnId: 'turn-1', state: 'running' }
}
}
]
}
function journalWith(prompt: string): AgentSessionJournal {
return {
isReadOnly: false,
lastActivityAt: () => OBSERVED_AT,
snapshot: () => ({ items: runningTurn(prompt) })
} as unknown as AgentSessionJournal
}
/** A real feed the host still holds one session on, having forgotten the other. Its `published`
* cache never retracts, so the two views genuinely differ. */
function statusFeed(): StructuredAgentSessionStatusFeed {
const session = (prompt: string) => ({
journal: journalWith(prompt),
params: { location: { workspaceId: TEST_WORKTREE_ID }, provider: 'claude' as const }
})
const sessions = new Map([
[HELD_SESSION, session('ship the thing')],
[FORGOTTEN_SESSION, session('rm the branch')]
])
const feed = new StructuredAgentSessionStatusFeed({
sessions,
getRecord: () => null,
now: () => OBSERVED_AT
})
feed.publish(HELD_SESSION)
feed.publish(FORGOTTEN_SESSION)
// `forget-session`, the last eviction step, drops the session and leaves the feed alone.
sessions.delete(FORGOTTEN_SESSION)
return feed
}
/** Everything the feed retained, read through the snapshot a subscriber opens on. No production
* code reads this; it is here so swapping the call site back to a whole-cache read is a one-line
* edit that this suite must catch. */
function retainedSummaries(feed: StructuredAgentSessionStatusFeed): AgentSessionStatusSummary[] {
let retained: AgentSessionStatusSummary[] = []
feed.subscribe({
id: 'retained-probe',
emit: (event: AgentSessionStatusEvent) => {
if (event.type === 'snapshot') {
retained = event.sessions
}
}
})()
return retained
}
function installHost(feed: StructuredAgentSessionStatusFeed) {
const liveSessionStatusSummaries = vi.fn(() => feed.liveSessionSummaries())
const retainedSessionStatusSummaries = vi.fn(() => retainedSummaries(feed))
// Typed against the real host, so renaming the accessor on the class reddens `tc` here — the
// caller cannot, because `orca-runtime-get-worktree-ps.ts` is `@ts-nocheck`.
const host: Pick<StructuredAgentSessionHost, 'liveSessionStatusSummaries'> & {
retainedSessionStatusSummaries: () => AgentSessionStatusSummary[]
} = { liveSessionStatusSummaries, retainedSessionStatusSummaries }
setStructuredAgentSessionHost(host as unknown as StructuredAgentSessionHost)
return { liveSessionStatusSummaries, retainedSessionStatusSummaries }
}
describe('worktree ps reads the installed structured host', () => {
afterEach(() => {
setStructuredAgentSessionHost(null)
})
it('reports the held session and asks the host for its live summaries', async () => {
const feed = statusFeed()
const { liveSessionStatusSummaries } = installHost(feed)
const { worktrees } = await new OrcaRuntimeService(store).getWorktreePs()
const worktree = worktrees.find((entry) => entry.worktreeId === TEST_WORKTREE_ID)
expect(worktree).toBeDefined()
// Exactly one: the forgotten session is still in the feed's retained cache, so a call site
// that enumerated that cache instead would report two.
expect(worktree?.agents).toHaveLength(1)
expect(worktree?.agents[0]).toMatchObject({
state: 'working',
agentType: 'claude',
prompt: 'ship the thing'
})
// Pins the call site to the live-intersecting accessor, not merely to some accessor.
expect(liveSessionStatusSummaries).toHaveBeenCalledTimes(1)
})
it('succeeds with no structured rows when no host is installed', async () => {
// Guard the guard: these specs share one module registry, so state the premise.
expect(getStructuredAgentSessionHost()).toBeNull()
const { worktrees } = await new OrcaRuntimeService(store).getWorktreePs()
const worktree = worktrees.find((entry) => entry.worktreeId === TEST_WORKTREE_ID)
expect(worktree).toBeDefined()
expect(worktree?.agents).toEqual([])
})
})
+1
View File
@@ -85,6 +85,7 @@ await import('./orca-runtime-tests/mobile-summaries.spec')
await import('./orca-runtime-tests/mobile-summaries-part-02.spec')
await import('./orca-runtime-tests/mobile-summaries-part-03.spec')
await import('./orca-runtime-tests/mobile-summaries-part-04.spec')
await import('./orca-runtime-tests/worktree-ps-structured-host.spec')
await import('./orca-runtime-tests/terminal-sleep-and-teardown.spec')
await import('./orca-runtime-tests/terminal-sleep-and-teardown-part-02.spec')
await import('./orca-runtime-tests/terminal-sleep-and-teardown-part-03.spec')