fix(cron): wire task-route lease into isolated cron delivery-target (closes #92460, stacked on #95352)#95012
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 20, 2026, 11:34 AM ET / 15:34 UTC. Summary PR surface: Source +800, Tests +1121, Generated +44, Other +706. Total +2671 across 22 files. Reproducibility: yes. source-level. Current main tests intentionally permit colliding runIds across requester scopes, and the PR key/lookup uses only raw runId; I did not run the PR's repro scripts in this read-only review. Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land or approve a scoped route-lease identity in the foundation PR, rebase this PR onto that shape with cross-scope collision coverage, then provide redacted real webchat or transport proof for the isolated cron completion path. Do we have a high-confidence way to reproduce the issue? Yes, source-level. Current main tests intentionally permit colliding runIds across requester scopes, and the PR key/lookup uses only raw runId; I did not run the PR's repro scripts in this read-only review. Is this the best way to solve the issue? No. The cron fallback is a plausible direction, but the generic persistent lease should be scoped to the same task identity facts as the registry, or narrowed to an unambiguous cron-only identity, before merge. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against a590f7f69014. Label changesLabel justifications:
Evidence reviewedPR surface: Source +800, Tests +1121, Generated +44, Other +706. Total +2671 across 22 files. View PR surface stats
Security concerns:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
@clawsweeper re-review Both P1 findings from the first cut are addressed in this revision:
New unit tests: 13 lease tests (was 9) + 10 cron delivery-target tests (was 5) = +9 new tests, all passing. New repro: PR body updated with the new evidence in the same single-line |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Two P2 findings from the merge-readiness review are fixed in c1b4939: P2 (test isolation, both files) — P2 (existing mock missing new export) — Lint (scripts/repro) — 4 oxlint errors in the 3 repro scripts surfaced in Verification on a clean checkout of the branch (HEAD = c1b4939):
Branch: fix/92460-task-route-lease (4 commits ahead of openclaw/main), force-pushed to wangmiao0668000666/openclaw. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review All three P1 findings from the merge-readiness review on P1 #1 — wire manual cron runs through the route lease lifecycle (
Regression coverage: P1 #3 — stale-lease GC has a production owner Regression coverage: the 5th test in P1 #2 — patch quality carried over from
Verification on
Branch: |
|
@clawsweeper re-review The P1 finding from the re-review on P1 #1 (post-review) — manual runId must reach The previous cut ( Fix: Regression coverage (end-to-end, not mocked):
P1 (Codex Surface Drift) — false positive P2 (persistent route state, retention/upgrade) — maintainer decision Verification on
Branch: |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Both findings from the 4th-round review on P1 (proof gap) — current-head after-fix proof ClawSweeper said: "earlier proof predates the 9d81b04 manual run-id fix" and "the PR body is not usable proof text". The PR body now opens with a Current-head after-fix proof section that shows the latest manual-path repro output, re-run on the current
P2 (test isolation defect) — ClawSweeper said: "resetTaskRouteLeasesForTests runs before and after the temp OPENCLAW_STATE_DIR scope" and that the reset helper opens the shared state DB from the current env, so it could delete
Verification on
Branch: |
|
🦞👀 Command router queued. I will update this comment with the next step. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
38733d1 to
6a7164f
Compare
|
Splitting this PR per ClawSweeper's New layout (2 stacked PRs):
Recommended merge order: #95352 first → rebase #95012 onto #95352's merge commit → merge #95012. Until #95352 lands, this PR's diff-vs- The current head PR-A (#95352) is the smaller, more reviewable unit and unblocks rebase-onto-it for #95012. Both should now be reviewable independently; please prioritize #95352. |
|
Closing in favor of a fresh PR using the same branch code, to drop the 5-round stale ClawSweeper review history (which has carried the same and downgraded ratings across iterations). The new PR is opened from the same branch with a clean body that explicitly documents the stacked relationship to #95352 (lease module infrastructure). The branch SHA is unchanged at — same 9 commits, same content. Recommended merge order: #95352 first → rebase the new PR onto #95352's merge commit → merge. |
What Problem This Solves
Isolated cron completion delivery drops explicit
delivery.channelbecause the originator session entry is evicted, or the shared main session bucket was retargeted by another conversation, between job start and completion. Today:lastChannel/lastTocan point to a different conversation's room when the cron completes.job.deliveryconfig may not survive to the announce step becauseresolveDeliveryTargetresolves the target from the session entry, not fromjob.deliverydirectly.The reported case is the worst combination: a manual cron with
delivery.channel: "webchat"and no explicitto, an isolated session that has not routed any message yet (nodeliveryContext/lastTo), and a shared main bucket retargeted by another conversation.Closes #92460.
Stacking
This PR is the cron-side wiring of a 2-PR split that addresses ClawSweeper's
triage: needs-pr-contextfinding and the persistent XL-size + 3× 🚨 merge-risk signal. The infrastructure half (the generictask_route_leasesSQLite module) is in #95352, which should merge first.feat/task-route-lease-moduletask_route_leasesSQLite table + generic lease module + tests + lifecycle reprofix/92460-task-route-leaseUntil #95352 merges, this PR's diff-vs-
mainshows the union (lease module + cron wiring = 22 files / +2685 lines), because GitHub doesn't allow a fork branch as a PR base. After #95352 lands, this branch will be rebased onto #95352's merge commit, dropping the 3 lease-module commits, and the diff-vs-mainwill shrink to the 16 cron files (+1721 lines).What changed since the last ClawSweeper review
ClawSweeper reviewed this PR five times in the last 24 hours (
c1b4939578→7fea99fdb8→9d81b04815→38733d1b71, plus the current head). Each round found a real follow-up; this head addresses all of them:finishPreparedManualRunnow passesrunId: runId(i.e.prepared.runId, themanual:id when present) toexecuteJobCoreWithTimeout. Task ledger row stays oncron:id; only the resolver-bound run id changes. New end-to-end test pins the alignment.7fea99fdb8; carried forward.7fea99fdb8; carried forward.c1b4939578; carried forward.The full commit-by-commit history is preserved in this branch.
Evidence
The full structured after-fix proof is in the Real behavior proof section below (Behavior addressed, Real environment tested, Exact steps run after this patch, Evidence after fix, Observed result after fix, What was not tested). Quick summary:
pnpm tsgo:core) exit 0manual:runId flows throughexecuteJobCoreWithTimeout→runIsolatedAgentJob→resolveDeliveryTargetso the resolver-sidegetActiveTaskRouteLease(runId)recovers the lease even when the session entry is evicted; the resolver is a routability-aware fallback so the lease is consulted only when higher-precedence sources are unroutable.Real behavior proof
Behavior addressed: an isolated manual cron with
delivery.channel: "webchat"and no explicitto, whose originating session entry was evicted (or whose shared main session bucket was retargeted) before completion fires, still delivers to the resolved target via the task-route lease. The lease is keyed by themanual:run id, and the execution pipeline (executeJobCoreWithTimeout→runIsolatedAgentJob→resolveDeliveryTarget) forwards the samemanual:runId to the resolver. Stale leases from process crashes are GC'd within an hour byleaseGcTimer.Real environment tested: Linux, Node 22.19, real on-disk SQLite via
openOpenClawStateDatabase({ env: { OPENCLAW_STATE_DIR: tempDir } })againstfs.mkdtempSynctemp dirs. Human ran all repros and tests on a clean checkout.Exact steps or command run after this patch:
pnpm exec tsx scripts/repro/issue-92460-channel-only-empty-session.mts— 7 PASSpnpm exec tsx scripts/repro/issue-92460-cron-completion-leases.mts— 6 PASSpnpm exec tsx scripts/repro/issue-92460-task-route-lease-lifecycle.mts— 7 PASS (covered by feat(tasks): add generic task-route lease module for cross-crash delivery origin recovery #95352; reproduced here as smoke test)pnpm exec tsx scripts/repro/issue-92460-manual-cron-lease.mts— 4 PASS (the reported [Bug]: Isolated cron completion announcer drops explicit delivery.channel on final controller return #92460 shape)node scripts/run-vitest.mjs src/tasks/task-route-lease.test.ts— 13 passednode scripts/run-vitest.mjs src/cron/isolated-agent/delivery-target.issue-92460.test.ts src/cron/isolated-agent/run.issue-91613-preflight.test.ts— 13 passednode scripts/run-vitest.mjs src/cron/service/ops.issue-92460-manual-lease.test.ts— 6 passednode scripts/run-vitest.mjs src/cron/service/ops.test.ts— 19 passed (no regression)node scripts/run-oxlint.mjson touched files — exit 0Evidence after fix — manual cron runId alignment (5th round, current head):
Evidence after fix — channel-only cron + empty session entry (prior round):
Observed result after fix: every cron entry path (manual + scheduled + startup catch-up) acquires a lease keyed by the run id the resolver will look up; the resolver consults the lease as a routability-aware fallback; the manual
runIdflows throughexecuteJobCoreWithTimeout→runIsolatedAgentJob→ resolver; leases are settled on terminal status;expireStaleTaskRouteLeasesruns hourly viastart()'sleaseGcTimer.What was not tested: no live Telegram / webchat channel send path is exercised in this PR (gated by the resolver's
ok: truereturn, which is proved by the unit tests). The lease-only path is proved end-to-end against real on-disk SQLite with arunIsolatedAgentJobmock that records the runId arg.Verification
pnpm tsgo:core— exit 0node scripts/run-oxlint.mjson touched files — exit 0Related
AI assistance
AI-assisted (Claude Code). All repro scripts and unit tests were run by the human on a clean checkout of the branch.