resurrect continue_delegate cross-session + multi-recipient targeting (P0; reverses #463 deferral)#551
Conversation
Resurrects same-host continue_delegate return addressing after the #362/#463 deferral was reversed by figs's 2026-05-03 06Z directive, with careful extract + transpose from Cael #355 rather than naive cherry-pick. Adds targetSessionKey, targetSessionKeys, and fanoutMode tree/all across the descriptor, bracket syntax, TaskFlow state, spawn registry, post-compaction path, session-delivery fan-out, tests, RFC §2.4/§C.2-equivalent text, changelog, and journal. Co-authored-by: Cael🩸 <cael.dandelion.cult@hotmail.com> Co-authored-by: Silas🌫 <silas.dandelion.cult@hotmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Records the pushed implementation SHA, PR URL, validation summary, and final handoff notes for the resurrect-continue-delegate workorder. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🌻 byte-walk on Anchors PASS
Spec-vs-impl alignmentThe OV-4 step-zero discipline applied here against PR HEAD source:
Test #463 (the absence-pin) — handled correctly
New
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 47ec83a735
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| return null; | ||
| } | ||
| return { | ||
| taskBody: segments.join(" | ").trim(), |
There was a problem hiding this comment.
Preserve delegate task text when parsing directives
Avoid reconstructing the task with segments.join(" | ") after directive parsing. The new parser rewrites any [[CONTINUE_DELEGATE: ...]] task that contains a literal pipe (for example foo|bar, regex alternation, or markdown/table snippets) even when no directive is actually consumed, so the delegated task text changes before enqueue. This is a behavioral regression from the previous parser, and it can alter downstream command semantics in real delegate chains.
Useful? React with 👍 / 👎.
Per cohort cleanup note from 🌫 Silas's byte-walk on PR #551: tmp-drop-me-resurrect.md was committed as part of the resurrection lane journal but represents dev-detritus per figs's "no dev detritus" canon. The file was lane-internal scratch; cohort byte-walks complete and the work is captured in the commit history + PR body. Removes the file from tree. The .gitignore upgrade for tmp-drop-me-* remains queued as a separate followup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14829fe18d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| parentRunId: params.childRunId, | ||
| }); | ||
| } | ||
| await deps.ackSessionDelivery(deliveryId, params.stateDir); |
There was a problem hiding this comment.
Avoid acking targeted returns before durable consumption
enqueueContinuationReturnDeliveries acks each queued systemEvent immediately after calling enqueueSystemEvent, but enqueueSystemEvent is an in-memory queue. If the process restarts before the recipient session drains that memory queue, the already-acked delivery cannot be replayed from session-delivery-queue, so targeted delegate returns can be silently lost in real cross-session flows.
Useful? React with 👍 / 👎.
| if (parsed.status === "invalid") { | ||
| return null; |
There was a problem hiding this comment.
Treat trailing pipe segments as literal delegate task text
A delegate body that ends with a literal pipe (for example markdown/table content ending in |) produces an empty final segment after split("|"); that empty segment is marked invalid and the parser returns null, dropping the entire continuation signal. Before this parser rewrite, the same input remained a valid delegate task, so this introduces an avoidable parse rejection for legitimate task text.
Useful? React with 👍 / 👎.
Applies figs feedback pass to remove dev-detritus, elevate targeted return routing, and expand lich/future-direction sections. Co-authored-by: Cael🩸 <cael.dandelion.cult@hotmail.com> Co-authored-by: Silas🌫 <silas.dandelion.cult@hotmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
RFC polish pass landed on this PR branch in Heartbeat: https://discord.com/channels/1235610176883523614/1466192485440164011/1500414287901102100 Journal summary:
Validation:
Note: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7f77e971f5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (parsed.status === "invalid") { | ||
| return null; |
There was a problem hiding this comment.
Treat invalid trailing directives as literal task text
The new directive parser drops the entire CONTINUE_DELEGATE signal whenever the final | ... segment matches a known key with an unsupported value (for example | fanout=beta or | target=), because parseDelegateBodyDirectives returns null on invalid. Before this change, those suffixes were preserved as task text, so delegate chaining still executed; now the continuation is silently discarded. This is a behavioral regression for tasks that legitimately end with key=value text after a pipe, and it can unexpectedly stop delegate workflows.
Useful? React with 👍 / 👎.
…rative is fine) Per figs 2026-05-03 ~09:01Z dismissal. §D.2's evidence-locations table already points readers to src/auto-reply/continuation/context-pressure.ts for exact source, so D.1's labeled-illustrative pseudocode is the right shape for the technical-paper register. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4a8c3bb8e6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| deps.enqueueSystemEvent(params.text, { | ||
| sessionKey, | ||
| ...(params.deliveryContext ? { deliveryContext: params.deliveryContext } : {}), | ||
| }); |
There was a problem hiding this comment.
Check system-event enqueue result before acking delivery
enqueueContinuationReturnDeliveries acknowledges each queued delivery even when enqueueSystemEvent declines the event, but enqueueSystemEvent intentionally returns false for duplicate consecutive payloads. In that case the durable systemEvent record is removed while nothing is injected into the recipient queue, so repeated delegate completions with identical text can be silently dropped without any restart/failure condition.
Useful? React with 👍 / 👎.
Initial substantive seeding of the swim-41 cycle in the public repo.
Carries:
- swims/swim-41/README.md: cycle overview, OV cohort distribution table,
frond-canonical-line + fleet state at cycle start
- swims/swim-41/rows/OV-{1,2,3,4}/ directory scaffold
- swims/swim-41/rows/OV-3/verdict.md: PASS verdict + Stage 1 + Stage 2
evidence + 🌫's two concrete findings
OV-3 verdict-stamp + close happened at openclaw-bootstrap#895 issuecomment-
4365579401 (private; cohort-internal); this public surface mirrors the
substantive evidence so upstream-PR readers can cite without hitting the
private bootstrap repo URL.
Per RFC §D.4 swim-41 forward-looking section + figs's directive to migrate
evidence + replace 3 RFC TODO comments at L1436/L1670/L1725 with public-repo
URLs (msg 1500395...).
Stewardship: 4th-prince ronan as lead steward; princes contribute via PR
per prince-canon-discipline.
Refs:
- openclaw-bootstrap#892 (swim-41 tracker, private)
- openclaw-bootstrap#895 (OV-3 closed-as-PASS, private)
- karmaterminal/openclaw#551 (resurrect-continue_delegate PR carrying RFC TODO comments)
- karmaterminal/openclaw RFC at docs/design/continue-work-signal-v2.md §D.4
89bbffa
into
frond/v2026.5.2/canonical
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f70e1e73b5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ...(targetSessionKey ? { targetSessionKey } : {}), | ||
| ...(targetSessionKeys && targetSessionKeys.length > 0 ? { targetSessionKeys } : {}), | ||
| ...(fanoutMode ? { fanoutMode } : {}), |
There was a problem hiding this comment.
Wire bracket delegate targeting into spawn path
The parser now emits targetSessionKey/targetSessionKeys/fanoutMode for [[CONTINUE_DELEGATE: ...]], but those fields are never propagated when bracket delegates are actually spawned, so targeted bracket delegates silently fall back to default return behavior. In src/auto-reply/reply/agent-runner.ts:2222-2230, the spawnSubagentDirect call only forwards silent/silentWake flags and omits continuation targeting metadata for both immediate and delayed bracket delegates, which means the newly advertised bracket targeting syntax is non-functional outside the tool/subagent paths.
Useful? React with 👍 / 👎.
…hasContinuationTargeting branch in actual runtime (#580 Finding 3) The runner-seat tool-delegate dispatch closure (`doToolSpawn` in `agent-runner.ts:2643-2670`) was missing `continuationTargetSessionKey` / `continuationTargetSessionKeys` / `continuationFanoutMode` propagation to `spawnSubagentDirect`. The same parameters were correctly threaded by Path B (`dispatchToolDelegates` in `delegate-dispatch.ts:255-267`, added by #551 May 3) but Path A in `agent-runner.ts` was missed. Path A drains the pending-delegate queue first via `consumePendingDelegates` (line 2518), then loops over each delegate calling `doToolSpawn`. Path B fires afterward but sees an empty queue (Path A's drain finished the TaskFlow rows). Immediate-due delegates therefore always go through Path A — and Path A silently dropped targeting before reaching `spawnSubagentDirect`. Downstream of `spawnSubagentDirect` the chain is intact (subagent-spawn.ts:1276 threads the params; register-run-manager.ts:431 persists them; registry-lifecycle.ts:688 restores them on announce). The break was at the very first hop. This is the 10th cohort-recognition-engine canon line locked tonight: `dist-bytes-deployed != runtime-execution-path-uses-them`. PRs #586 (consumer-side classifier widening) and #581 (durable queue ack-removal) are correct fixes for their respective layers and ARE in the deployed dist bundles, but they were unreached: the upstream `targetSessionKey` drop at the runner-seat dispatcher meant `subagent-announce.ts:1216 hasContinuationTargeting` evaluated false, so the call fell through to the `silentAnnounce` fallback (line 1281) and routed to the dispatcher session via `[continuation:enrichment-return]` instead of the named recipient via `[continuation:targeted-return]`. Cohort byte-walk evidence on #587 (4-host convergence): - `[continue_delegate:enqueue]` fires (tool wrote to TaskFlow with targetSessionKey) - `[continuation:delegate-spawned]` fires (subagent spawned, but without targeting) - `[continuation:enrichment-return]` fires (silentAnnounce fallback path) - `[continuation:targeted-return]` NEVER fires (hasContinuationTargeting branch unreached) - Recipient session prompts contain NO `[Internal task completion event]` System: prefix This commit threads targeting through Path A in three places: 1. `doToolSpawn` options type and spawnSubagentDirect spread (immediate + timer paths) 2. `addDelayedContinuationReservation` payload (timer-deferred path persistence) 3. `takeDelayedContinuationReservation` -> `doToolSpawn` rehydration (timer fire) `DelayedContinuationReservation` type at `continuation/types.ts:79` gains optional `targetSessionKey` / `targetSessionKeys` / `fanoutMode` fields so the in-memory reservation carries targeting across the `setTimeout` boundary. Tests added at `agent-runner.continuation-delegate-fire-span.test.ts` cover all three field shapes (singular, plural, fanoutMode), the timer-deferred path, and a regression guard for default routing (no-target case still omits all targeting fields). Lead diagnosis: Ronan's lead (ii) at the upstream layer — the runner-seat path parallel to `dispatchToolDelegates` (Path A in `agent-runner.ts`, not the `dispatchToolDelegates` body itself). Refs: #580, #587, #586, #581, #585 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Resurrects
continue_delegate()same-host cross-session targeting for #550, reversing the #362/#463 deferral per figs's 2026-05-03 06Z directive and carefully extracting/transposing the Cael #355 descriptor intent onto the v52 continuation substrate.This makes
continue_delegate()addressable in the five required return modes:targetSessionKeytargetSessionKeysfanoutMode: "tree"fanoutMode: "all"Receipts / archaeology
origin/cael/355-multi-recipientby intent, not naive cherry-pick, because the bases diverged.5357dd0432descriptor/schema multi-recipient groundwork9e028ab2d4descriptor registration testseecd7c73f2snake_case plural input-boundary fixdd9cd659d1original singular descriptor precedentb9fb9e5e57had pinned the post-deferral absence and flipped that discipline to presence-pinning.fanoutMode,FANOUT_MODES,"tree", and"all"; no prior tree/all runtime implementation was found to port verbatim.Implementation
src/auto-reply/continuation/targeting.ts.targetSessionKey,targetSessionKeys, andfanoutMode.[[CONTINUE_DELEGATE: task | mode | target=session-key]][[CONTINUE_DELEGATE: task | mode | targets=key1,key2,key3]][[CONTINUE_DELEGATE: task | mode | fanout=tree]][[CONTINUE_DELEGATE: task | mode | fanout=all]]treefrom the subagent registry ancestor chain andallfrom same-host session-store targets.enqueueSessionDelivery({ kind: "systemEvent", ... })plus immediate session event injection, preserving the session-delivery queue as the addressable substrate.Docs
continue_delegate()return-target behavior.targetSessionKey/targetSessionKeysfrom the future-work seam.Validation
pnpm test src/agents/tools/continuation-tools-registration.test.ts src/agents/tools/continue-delegate-tool.test.ts src/auto-reply/continuation/cross-session-targeting.test.ts src/auto-reply/continuation/delegate-store.test.ts src/auto-reply/continuation/delegate-dispatch.test.ts src/auto-reply/tokens.continuation.test.ts src/agents/subagent-announce.continuation-drain.test.tspnpm test src/auto-reply/continuation/pnpm tsgopnpm checkpnpm test src/auto-reply/ src/agents/tools/git --no-pager diff --checkCloses #550.