From 879a623095345df5e117bf9512cb33bf32c4a0aa Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sun, 2 Aug 2026 12:28:48 +0800 Subject: [PATCH] fix(subagent): cover the scope-disposal effect registration with setup rollback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `childCtx.effect()` that routes scope disposal into `releaseChild` was registered after the install loop's try/catch, so a hypothetical throw from the registration itself (effect() rejects only on an inactive fiber, which a live unpublished scope cannot be) would leak the just-installed batch — neither the setup-rollback catch nor `releaseChild` would release it. Move the registration inside the try so the existing rollback path covers it; no observable behavior change. --- packages/subagent/subagent/src/activation-setup-registry.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/subagent/subagent/src/activation-setup-registry.ts b/packages/subagent/subagent/src/activation-setup-registry.ts index c0fc84552c..dca194f113 100644 --- a/packages/subagent/subagent/src/activation-setup-registry.ts +++ b/packages/subagent/subagent/src/activation-setup-registry.ts @@ -123,6 +123,10 @@ export class SubagentActivationSetupRegistry { // Dispose that escaped record and invalidate the provisioning batch. if (isRemoved(registration)) this.release(installation) } + // Register the scope-disposal release inside the same try so the + // setup-rollback catch also covers a hypothetical effect-registration + // throw; today effect() cannot reject on a live unpublished scope. + childCtx.effect(() => () => { this.releaseChild(childCtx) }, 'subagents.activationSetup()') } catch (error: unknown) { // Keep the installer failure authoritative, but attempt every rollback. try { @@ -133,7 +137,6 @@ export class SubagentActivationSetupRegistry { } throw error } - childCtx.effect(() => () => { this.releaseChild(childCtx) }, 'subagents.activationSetup()') return { assertIntact: () => { if (!state.invalidated) return