test: close the per-file coverage gaps for the scoping surface

Every subject-extractor row of the invariants carrier table is exercised
with a matching and a foreign-keyed carrier; the HMR re-apply seed path
(sessions of agents that predate the plugin are marked started) is pinned;
the scoped tool-provider disposal, plural restrict() validation, singular
scopeHost absentee, tool-subagent passthrough, stale-stage drop, and
disposing-parent spawn (INACTIVE_EFFECT, no orphan) each gain their test.
Two genuinely defensive branches carry justified v8-ignore markers.
This commit is contained in:
Tianyi Cui
2026-07-09 03:41:37 +08:00
parent cc24e79cd2
commit e7b712453a
10 changed files with 169 additions and 3 deletions
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import Loader from '@cordisjs/plugin-loader'
import LlmService from '@deepseek-ai/dsh-llm'
@@ -377,4 +377,23 @@ describe('dsh-subagent-spawn', () => {
expect(ctx.agents.list().length).toBe(before)
})
})
it('spawning from a DISPOSING parent fails loud with no orphaned child (INACTIVE_EFFECT teaching error)', async () => {
const { ctx } = await setup([])
// A handle-owned parent we can dispose (config agents dispose with the loop fiber).
const parentHandle = ctx.agents.create({
agentId: AgentId('doomed-parent'),
sessionId: SessionId('doomed-s'),
agentOptions: { model: 'mock' },
})
await parentHandle.dispose()
const before = ctx.agents.list().length
expect(() => ctx.subagents.start('spawn', {
prompt: [{ type: 'text', text: 'do X' }],
parent: parentHandle.agent,
})).toThrow(/inactive context/)
// The freshly created child's disposal was initiated before the rethrow
// (fire-and-forget — start() throws synchronously); quiescence follows.
await vi.waitFor(() => { expect(ctx.agents.list().length).toBe(before) })
})
})