test(review): pin the tool-subagent plugin fiber's lifecycle ownership; doc nits
Codex round-2 findings: - A committed test now proves the REAL plugin fiber (not a direct apply) owns the provider-lifecycle listeners: disposing a mounted tool's fiber unmounts the tool and leaves the provider intact, and a fiber disposed while WAITING never zombie-mounts when its provider arrives later. - TODO(subagent-dup-toolname) records the invalid-config blast radius of two waiting fibers sharing a toolName (the duplicate throw propagates through subagent/provider-added and rolls back the provider). - CONTEXT.md drops its creation-history sentence; the RFC's acceptance checklist becomes present-tense shipped invariants (docs/AGENTS.md writing rules).
This commit is contained in:
@@ -231,6 +231,29 @@ describe('dsh-tool-subagent', () => {
|
||||
expect(ctx.tools.schemas().find(s => s.name === 'subagent')!.description).toContain('INHERITS this conversation')
|
||||
})
|
||||
|
||||
it('the tool PLUGIN fiber owns its lifecycle listeners: disposal unmounts, and a disposed fiber never zombie-mounts', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRegistry)
|
||||
await ctx.plugin(SubagentService)
|
||||
|
||||
// Arm 1: a mounted tool dies with its plugin fiber; the provider survives.
|
||||
await ctx.plugin(mock, { name: 'mock' })
|
||||
const mounted = await ctx.plugin(tool, { provider: 'mock' })
|
||||
expect(ctx.tools.schemas().some(s => s.name === 'subagent')).toBe(true)
|
||||
await mounted.dispose()
|
||||
expect(ctx.tools.schemas().some(s => s.name === 'subagent')).toBe(false)
|
||||
expect(ctx.subagents.getProvider('mock')).toBeDefined()
|
||||
|
||||
// Arm 2: a fiber disposed while WAITING must not react to the provider
|
||||
// arriving later — a surviving listener would re-register a tool that no
|
||||
// live plugin owns (the zombie mount).
|
||||
const waiting = await ctx.plugin(tool, { provider: 'later', toolName: 'subagent_later' })
|
||||
await waiting.dispose()
|
||||
await ctx.plugin(mock, { name: 'later' })
|
||||
expect(ctx.tools.schemas().some(s => s.name === 'subagent_later')).toBe(false)
|
||||
})
|
||||
|
||||
it('ignores lifecycle events for OTHER providers', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt)
|
||||
|
||||
Reference in New Issue
Block a user