Skip to content

fix: harden subagent completion announce cleanup races#76332

Closed
neilofneils404 wants to merge 1 commit into
openclaw:mainfrom
neilofneils404:fix/subagent-completion-announce-cleanup-races
Closed

fix: harden subagent completion announce cleanup races#76332
neilofneils404 wants to merge 1 commit into
openclaw:mainfrom
neilofneils404:fix/subagent-completion-announce-cleanup-races

Conversation

@neilofneils404

Copy link
Copy Markdown
Contributor

Summary

Fixes a set of subagent completion lifecycle races that can leave completed child runs stuck behind cleanup/announce sequencing or overwrite a terminal error with a later success signal.

The patch makes three related changes:

  • Do not block completion announce delivery on browser-session cleanup. The cleanup is now started after the announce/cleanup flow is leased and runs best-effort in the background, with warnings captured instead of blocking the lifecycle path.
  • Skip waiting for an active embedded child session when a frozen final reply is already available. If we already captured the reply text, the requester can be notified immediately instead of waiting on a stale/active child run check.
  • Preserve a terminal error outcome if a later completion signal reports success for the same run, unless the run was actually restarted after the error. This prevents cleanup/finalization races from turning a failed run into a successful task after the fact.

Why this matters

The lifecycle path has a few asynchronous pieces that can finish in different orders:

  1. child session/browser cleanup
  2. completion announce delivery back to the requester
  3. task finalization bookkeeping
  4. embedded child-run settle checks
  5. terminal lifecycle hooks

Before this patch, a slow or stuck browser cleanup could delay the announce/cleanup flow. Separately, if a child run had already produced frozen reply text but still appeared active, the announce flow could wait unnecessarily. And in a failure path, a later complete event could overwrite the already-recorded terminal error.

That combination makes subagent completion feel flaky from the user's side: the work is done, but the parent session may not receive the completion promptly, or the persisted final task state can become misleading.

Implementation notes

Lifecycle cleanup lease

completeSubagentRun now preclaims cleanup when triggerCleanup is true and cleanup has not already completed/been handled. That lease is passed into startSubagentAnnounceCleanupFlow as skipBeginCleanup, avoiding a second beginSubagentCleanup race while still protecting duplicate cleanup attempts.

If cleanup is not actually triggered, or announce is suppressed for steer-restart, the preclaim is released so the record does not stay permanently handled.

Browser cleanup no longer blocks announce cleanup

Previously browser cleanup was awaited before starting the subagent announce cleanup flow. This patch starts the announce cleanup flow first, retires run-mode bundle MCP runtime as before, and then launches browser cleanup as a best-effort async task:

  • uses injected cleanupBrowserSessionsForLifecycleEnd when provided
  • lazy-loads the browser cleanup helper otherwise
  • logs warning context on failure
  • does not reject or delay completion delivery

Frozen reply short-circuit

runSubagentAnnounceFlow now checks whether roundOneReply already contains non-empty frozen reply text. If it does, the flow does not wait for waitForEmbeddedPiRunEnd even if isEmbeddedPiRunActive reports true. The frozen reply is already the durable completion payload; waiting only increases the chance of a stale active-state race.

Terminal error preservation

When a run already has a terminal error outcome/reason, a later successful completion signal no longer overwrites it unless the entry's startedAt proves the run restarted after the error's endedAt. The ended hook and persisted outcome use the preserved effective reason/outcome.

Tests

Added/updated coverage for:

  • announcing without waiting for browser cleanup
  • skipping announce delivery when completion messages are disabled
  • delete-mode cleanup when completion messages are disabled
  • preserving terminal errors against later success completion
  • allowing success to replace error only after a restart
  • using frozen reply text without waiting on an active embedded child session

Verification run locally:

pnpm test src/agents/subagent-registry-lifecycle.test.ts src/agents/subagent-announce.test.ts

Result: 2 files passed, 29 tests passed.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: M labels May 3, 2026
@clawsweeper

clawsweeper Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Keep open for now: the fix direction is useful, but the PR is currently conflicting, lacks real after-fix behavior proof, and needs a current-main refresh that preserves merged subagent cleanup guards before it can be a safe landing candidate.

Root-cause cluster
Relationship: partial_overlap
Canonical: #67777
Summary: This PR overlaps the canonical durable subagent completion delivery thread, while also carrying narrower cleanup-order and terminal-outcome fixes that are not fully implemented on current main.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

So I’m closing this here because the remaining work is already tracked in the canonical issue.

Review details

Best possible solution:

Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

Do we have a high-confidence way to reproduce the issue?

Yes, source-level. Current main still waits on an active embedded child before using a frozen reply, still awaits browser cleanup before starting the announce tail, and still writes later completion outcome/reason without the PR branch's prior-error preservation logic.

Is this the best way to solve the issue?

No, not as submitted. The patch shape is plausible, but the branch needs a current-main refresh that preserves the merged browser cleanup guard, removes the changelog edit, and adds real behavior proof.

Security review:

Security review cleared: No concrete security or supply-chain concern was found; the diff changes in-repo agent lifecycle code, tests, docs, and changelog text only.

AGENTS.md: found and applied where relevant.

What I checked:

  • stale F-rated PR: PR was opened 2026-05-03T00:26:21Z, is older than 60 days, and the latest review rated it F.
  • proof blocker: real behavior proof is mock_only and proof tier is F, so this branch is not merge-ready without contributor follow-up.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • vincentkoc: Authored recent subagent lifecycle and fallback completion delivery work in the same affected surfaces, including duplicate ended-hook handling and delivery fallback hardening. (role: recent lifecycle and delivery contributor; confidence: high; commits: 48042c387556, a1b656705992; files: src/agents/subagent-registry-lifecycle.ts, src/agents/subagent-announce-delivery.ts)
  • steipete: Authored the split that established the current subagent registry lifecycle module this PR changes. (role: feature-history contributor; confidence: high; commits: f862685ed8e4; files: src/agents/subagent-registry-lifecycle.ts, src/agents/subagent-registry.ts)
  • Feelw00: Authored the merged browserCleanupDispatchedAt dedupe change that this stale PR must preserve when refreshed. (role: adjacent merged fix author; confidence: high; commits: a9a86f788b08; files: src/agents/subagent-registry-lifecycle.ts, src/agents/subagent-registry.types.ts, src/agents/subagent-registry-run-manager.ts)
  • BrianWang1990: Authored the browser tab/process cleanup behavior for cron tasks and subagents, which is directly affected by the cleanup ordering change. (role: adjacent lifecycle cleanup contributor; confidence: medium; commits: 72b2e413d610; files: src/agents/subagent-registry-lifecycle.ts, src/gateway/server-cron.ts)
  • tyler6204: Related review context and maintainer routing identify Tyler Yust with agents/subagents work; recent history around subagent announce delivery is adjacent to the frozen-reply behavior. (role: announce delivery history contributor; confidence: medium; commits: 81b93b9ce04b; files: src/agents/subagent-announce.ts)

Codex review notes: model internal, reasoning high; reviewed against 68e06b9ea148.

@neilofneils404
neilofneils404 force-pushed the fix/subagent-completion-announce-cleanup-races branch from 329d72b to ea78e92 Compare May 3, 2026 00:37
@neilofneils404

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in ea78e92067.\n\n- Moved the cleanup preclaim to immediately before , after the awaited pre-cleanup work, so hook/setup exceptions cannot leave without cleanup completion.\n- Added the required Unreleased/Fixes changelog entry.\n- Fixed the lint issue from the original patch.\n\nVerified locally:\n\nbash\ngit diff --check\npnpm test src/agents/subagent-registry-lifecycle.test.ts src/agents/subagent-announce.test.ts\npnpm lint src/agents/subagent-registry-lifecycle.ts src/agents/subagent-registry-lifecycle.test.ts src/agents/subagent-announce.ts src/agents/subagent-announce.test.ts\n

@neilofneils404
neilofneils404 force-pushed the fix/subagent-completion-announce-cleanup-races branch from ea78e92 to 4ac3b9d Compare May 3, 2026 00:37
@neilofneils404

Copy link
Copy Markdown
Contributor Author

Follow-up after rebasing onto current origin/main: addressed the review feedback in 4ac3b9d661.

  • Moved the cleanup preclaim to immediately before startSubagentAnnounceCleanupFlow, after the awaited pre-cleanup work, so hook/setup exceptions cannot leave cleanupHandled=true without cleanup completion.
  • Added the required Unreleased/Fixes changelog entry.
  • Fixed the lint issue from the original patch.
  • Rebasing also resolved the changelog conflict against the latest upstream entries.

Verified locally:

git diff --check
pnpm test src/agents/subagent-registry-lifecycle.test.ts src/agents/subagent-announce.test.ts
pnpm lint src/agents/subagent-registry-lifecycle.ts src/agents/subagent-registry-lifecycle.test.ts src/agents/subagent-announce.ts src/agents/subagent-announce.test.ts

@neilofneils404
neilofneils404 force-pushed the fix/subagent-completion-announce-cleanup-races branch from 4ac3b9d to 36043e0 Compare May 3, 2026 01:08
@neilofneils404

Copy link
Copy Markdown
Contributor Author

Addressed the latest review note in 36043e08d4.

  • Updated docs/tools/subagents.md and docs/automation/tasks.md so they describe the new ordering: subagent announce cleanup starts first, while tracked browser tab/process cleanup runs best-effort in the background and does not block completion delivery.

Verified locally:

git diff --check
pnpm check:docs
pnpm test src/agents/subagent-registry-lifecycle.test.ts src/agents/subagent-announce.test.ts
pnpm lint src/agents/subagent-registry-lifecycle.ts src/agents/subagent-registry-lifecycle.test.ts src/agents/subagent-announce.ts src/agents/subagent-announce.test.ts

I also inspected the current failing CI jobs: the visible failure is in plugin runtime dependency contracts for @tloncorp/tlon-skill, which appears unrelated to this subagent lifecycle/docs patch.

@barnacle-openclaw

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@barnacle-openclaw barnacle-openclaw Bot added the stale Marked as stale due to inactivity label May 30, 2026
@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label May 30, 2026
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed stale Marked as stale due to inactivity labels May 31, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 14, 2026
@clawsweeper

clawsweeper Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant