fix(acp): scope connection-owned continuation drain
This commit is contained in:
@@ -107,9 +107,9 @@ Child release occurs only after the child Agent is quiescent, every child of tha
|
||||
|
||||
Ownership is retained until the child Activation is disposed. A later refinement may release a request-scoped lease earlier, but it would require an exact turn-completion correlation that this Task-free proposal deliberately does not add.
|
||||
|
||||
Top-level teardown is host-owned rather than represented as another Activation. The host first asks the manager to enter draining synchronously, which rejects new creation, resume, and delivery admission. The manager then awaits every materialization that already passed admission until it either installs a resident Activation or completes rollback, snapshots the stable live forest, disposes it child-first, and awaits all `AgentHandle.dispose()` calls. Every materialized start and live delivery rechecks caller cancellation, draining, Activation disposal, and exact parent authority in the same synchronous span as inbox submission, so teardown or parent replacement that wins before acceptance prevents delivery to the closing handle. Only after that drain settles may the host dispose top-level Agents and the manager scope. Manager unload uses the same drain.
|
||||
Top-level teardown is host-owned rather than represented as another Activation. Manager unload uses `drainContinuable()` to close manager-wide admission synchronously, await every admitted materialization through publication or rollback, stop the stable live forest, and release it child-first. A host that owns selected top-level Agents instead uses `drainContinuableDescendants(parents)`: exact Agent identities close admission only below those roots until each leaves the registry, while unrelated forests and manager-wide admission remain live; the manager stops their visible descendants before its first await, waits only materializations admitted below those roots, and releases only the selected branches. Every materialized start and live delivery rechecks caller cancellation, the applicable draining scope, Activation disposal, and exact parent authority in the same synchronous span as inbox submission, so teardown or parent replacement that wins before acceptance prevents delivery to the closing handle. Only after the applicable drain settles may the host dispose its top-level Agents; only the global drain precedes manager-scope disposal.
|
||||
|
||||
The activation-owner scope exists because ordinary Cordis owner effects unwind in reverse registration order, which cannot express the dynamic child graph. Manager initialization registers the private scope's structural disposer first and its drain disposer afterward, so reverse unwind invokes the drain before releasing that scope; merely registering a cleanup effect on the same scope as later Agent handles would allow structural handle disposal to bypass child-first ordering. The manager snapshots the live roots after closing admission, stops its outward lifecycle notifications before cancellation, and retains its internal ownership bookkeeping until every handle settles. Each Activation has one memoized disposal promise so host shutdown, manager unload, child release, and normal settlement can converge without double release. Sibling branches drain independently; one disposal failure is recorded but does not prevent the manager from attempting the remaining handles, and the aggregate drain reports failure after all branches settle. Durable child Sessions survive this process-local teardown.
|
||||
The activation-owner scope exists because ordinary Cordis owner effects unwind in reverse registration order, which cannot express the dynamic child graph. Manager initialization registers the private scope's structural disposer first and its drain disposer afterward, so reverse unwind invokes the drain before releasing that scope; merely registering a cleanup effect on the same scope as later Agent handles would allow structural handle disposal to bypass child-first ordering. Each materialization registers its barrier participant and snapshots its exact live ancestry before starting the inner transaction, then remains tracked until it installs an Activation or fully rolls back. The Activation retains weak membership of that ancestry, so an intermediate Agent may leave the registry without hiding a still-live descendant from its host root. Each Activation installs one memoized disposal promise before cancellation or recursive callbacks, allowing scoped host shutdown, global manager unload, child release, and normal settlement to converge without double release. Cancellation propagates top-down before slow descendant cleanup; handle release remains child-first. Sibling branches drain independently; one disposal failure is recorded but does not prevent the manager from attempting the remaining selected handles, and the aggregate drain reports failure after all selected branches settle. Durable child Sessions survive this process-local teardown.
|
||||
|
||||
### Deferred report delivery
|
||||
|
||||
@@ -135,7 +135,7 @@ Parent-originated delivery requires the parent to be live when admitted and keep
|
||||
|
||||
Without Tasks there is no `task_output`, `task_kill`, Task status, per-message result promise, or public subagent cancellation operation. The caller signal can abort start or follow-up only before inbox acceptance. After acceptance, the parent cannot cancel the message, turn, or Activation through `ctx.subagents`; `Agent.cancel()` remains a lower-level Agent capability that this version does not expose through the subagent service.
|
||||
|
||||
Host and manager teardown remains the lifecycle-wide stop path. It closes admission, disposes every live Activation forest child-first, and preserves the durable Sessions.
|
||||
Host and manager teardown remains the lifecycle stop path. Manager unload applies it globally; a host applies it only below the exact top-level Agents it owns. Each form closes the applicable admission scope, stops the selected visible Activations, awaits admitted materializations in that scope, releases child-first, and preserves the durable Sessions.
|
||||
|
||||
Each turn requests the Session durability checkpoint, and final Activation settlement requires the manager to inspect `ctx.sessions.flush()` rather than ignore its boolean result. `true` confirms that at least one durability listener participated and every listener settled successfully. `false` or rejection reports `DURABILITY_FAILED`; normal background settlement logs the lifecycle failure, while an explicit host or manager drain includes it in the aggregate rejection after all branches settle. Either way, the manager still disposes the handle and releases ownership, and the persisted child state may be missing or stale on a later resume.
|
||||
|
||||
@@ -188,17 +188,17 @@ The implementation pins these behaviors:
|
||||
- `followup()` accepts only the exact live direct parent and rechecks that identity at the final no-await inbox-admission boundary after any materialization; durable message provenance cannot authorize delivery.
|
||||
- Continuation messages always use `Agent.followup()` and share its inbox FIFO, including when the child already has an open turn.
|
||||
- `ctx.subagents.followup()` and its `send_message` adapter return only the accepted `MessageId`; the continuation layer accepts no delivery target and defines no subagent-specific route result.
|
||||
- This version exposes no public subagent cancellation operation; caller signals stop start and follow-up only before inbox acceptance, while host and manager teardown retains child-first global cleanup.
|
||||
- This version exposes no public subagent cancellation operation; caller signals stop start and follow-up only before inbox acceptance, while host-scoped and manager-global teardown retain child-first cleanup.
|
||||
- This version exposes no subagent steering operation or current-turn controller state.
|
||||
- An idle Agent with live owned children yields a `waiting` Activation whose `AgentHandle` remains retained.
|
||||
- A `next-turn` delivered to `waiting` wakes the same Activation; delivery after completed disposal cold-resumes a new Activation.
|
||||
- Every continuation-managed parent Activation disposes only after all directly owned child Activations complete `AgentHandle` disposal; top-level Agents do not join the waiting graph.
|
||||
- Final Activation settlement treats only `ctx.sessions.flush(child.session) === true` as durability confirmation; `false` and rejection report `DURABILITY_FAILED`, still dispose the child handle, and still release parent ownership so durability failure cannot leak a `waiting` Activation.
|
||||
- Host and manager teardown synchronously enter draining, reject new materialization and delivery, await every admitted materialization through publication or rollback, stop manager-owned outward notifications, dispose the stable live Activation forest child-first, await every branch despite individual failures, and only then dispose top-level Agents and the manager scope; a private activation-owner scope preserves this order against Cordis effect unwinding, and one memoized disposal promise per Activation makes concurrent normal settlement idempotent.
|
||||
- Manager teardown closes admission globally; a host owning selected top-level Agents instead closes admission only below their exact identities until those roots leave the registry. Both track admitted materializations by exact ancestry, install one memoized disposal cutoff per selected visible Activation, propagate cancellation top-down, release handles child-first, await every selected branch despite individual failures, and only then dispose the corresponding top-level Agents or manager scope.
|
||||
- This version exposes no `report` tool, child-to-parent content delivery, or automatic parent wakeup.
|
||||
- Session logs reconstruct only messages that were actually written, with their admitted provenance; inbox-accepted but unlogged messages have no restart guarantee.
|
||||
- No continuable-subagent path creates or depends on a Task, `TaskId`, Task completion notice, Task cancellation, or intermediate result-bearing execution wrapper.
|
||||
- Unit coverage pins the `startContinuable()` inbox-acceptance return boundary, complete rollback for each pre-acceptance and lifecycle-publication failure, drain quiescence for a materialization caught between Agent publication and Activation registration, provider-independent cold resume, final exact-parent reauthorization after cold-resume materialization, caller-signal and teardown ownership on both sides of acceptance, and the absence of automatic replay for accepted-but-unlogged messages.
|
||||
- Unit coverage pins the `startContinuable()` inbox-acceptance return boundary, complete rollback for each pre-acceptance and lifecycle-publication failure, global and parent-scoped drain quiescence for materialization caught between Agent publication and Activation registration, sibling-forest isolation, exact ancestry after an intermediate Agent leaves the registry, provider-independent cold resume, final exact-parent reauthorization after cold-resume materialization, caller-signal and teardown ownership on both sides of acceptance, and the absence of automatic replay for accepted-but-unlogged messages.
|
||||
- Unit coverage pins the residency-only routing table, single-inbox ordering, `MessageId` correlation through inbox events, follow-up during an open turn, waiting wakeup, cold resume, ownership registration and release, child-first disposal, send-versus-dispose races, both `false` and rejection from the final durability checkpoint without ownership leaks, and the absence of public subagent cancellation, steering, and report tools.
|
||||
- A keyless assembled-app snapshot covers parent delegation and follow-up queueing, the absence of subagent steering, report delivery, and automatic parent wakeup, retained waiting `AgentHandle`, and child-first disposal.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user