Skip to content

cron: keep failure destination for threaded delivery#43808

Closed
Alex-Alaniz wants to merge 4 commits into
openclaw:mainfrom
Alex-Alaniz:codex/cron-telegram-threadid
Closed

cron: keep failure destination for threaded delivery#43808
Alex-Alaniz wants to merge 4 commits into
openclaw:mainfrom
Alex-Alaniz:codex/cron-telegram-threadid

Conversation

@Alex-Alaniz

@Alex-Alaniz Alex-Alaniz commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes cron failure-destination dedupe for threaded announce delivery.
  • When the primary cron delivery targets an explicit Telegram thread/topic, a same-chat failure destination without a thread is no longer treated as the same target and suppressed.
  • Keeps the patch narrow to delivery-plan behavior on current main; no cron schema expansion and no local gateway/runtime startup.

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope

  • Gateway / orchestration
  • Integrations
  • Auth / tokens
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Topic-routed Telegram cron jobs can still send failure notifications to the configured same chat instead of silently suppressing the fallback notice as a duplicate destination.

Security Impact

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Real behavior proof

  • Behavior or issue addressed: same-chat cron failure destinations were incorrectly deduped away when the primary Telegram announce delivery had an explicit threadId.
  • Real environment tested: local macOS OpenClaw checkout on current main-based PR branch, using the actual src/cron/delivery-plan.ts implementation through node --import tsx. No local gateway or network channel service was started.
  • Exact steps or command run after this patch: ran a live Node command that imports resolveFailureDestination() and resolves a cron job whose primary delivery is telegram chat 111, threadId 99, account bot-a, with a same-chat failure destination.
  • Evidence after fix: copied live terminal output from the command:
{
  "afterFixFailureDestination": {
    "mode": "announce",
    "channel": "telegram",
    "to": "111",
    "accountId": "bot-a"
  }
}
  • Observed result after fix: the failure destination is preserved as an announce target instead of returning null, so it will not be suppressed as a duplicate of the threaded primary delivery.
  • What was not tested: I did not send a live Telegram message; this proof covers the cron delivery-plan decision point, and the local regression/gateway tests cover the surrounding cron paths.

Verification

  • PNPM_HOME= COREPACK_ENABLE_PROJECT_SPEC=0 /opt/homebrew/bin/pnpm exec oxfmt --check --threads=1 src/cron/delivery-plan.ts src/cron/delivery.test.ts CHANGELOG.md
  • PNPM_HOME= COREPACK_ENABLE_PROJECT_SPEC=0 /opt/homebrew/bin/pnpm test src/cron/delivery.test.ts
  • PNPM_HOME= COREPACK_ENABLE_PROJECT_SPEC=0 /opt/homebrew/bin/pnpm test src/cron/delivery.test.ts src/cron/normalize.test.ts src/cron/service/jobs.apply-patch.test.ts src/cron/isolated-agent/delivery-target.test.ts
  • PNPM_HOME= COREPACK_ENABLE_PROJECT_SPEC=0 /opt/homebrew/bin/pnpm test src/gateway/server.cron.test.ts
  • PNPM_HOME= COREPACK_ENABLE_PROJECT_SPEC=0 /opt/homebrew/bin/pnpm check:changed

Notes

  • I did not resolve or reply to review threads from automation; this push only updates the branch and PR description.

@greptile-apps

greptile-apps Bot commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a gap where delivery.threadId was accepted in cron job configs but never propagated into the outbound announce delivery, causing Telegram forum-topic cron jobs to always land in the General topic instead of the configured topic.

The fix is well-scoped and consistent:

  • Type contract (types.ts): threadId?: string | number added to CronDelivery; CronDeliveryPatch inherits it via Partial<CronDelivery>.
  • Delivery plan (delivery.ts): New normalizeThreadId helper (mirrors existing normalizeAccountId/normalizeTo pattern) extracts and trims threadId into CronDeliveryPlan.
  • Target resolution (isolated-agent/delivery-target.ts, run.ts): deliveryPlan.threadId is forwarded as explicitThreadId to resolveSessionDeliveryTarget, which already sets threadIdExplicit: true for explicitly-provided values — the downstream carry-through guard then passes it through to the resolved target.
  • Normalization / patching (normalize.ts, service/jobs.ts): coerceDelivery and mergeCronDelivery are extended with the same presence-check + trim pattern used for other delivery fields; blank/invalid values are stripped, and existing threadId is preserved when unrelated fields are patched.
  • Tests: Unit tests cover trimming, blank-stripping, patch-preserve, and end-to-end forum-topic delivery with messageThreadId: 15 confirmed at the Telegram send layer.

One minor observation worth awareness: isSameDeliveryTarget (failure-destination deduplication in delivery.ts) does not account for threadId. If a job delivers to a specific forum topic via explicit threadId and a failure destination happens to share the same channel/to/accountId, the deduplication would consider them equivalent and suppress the failure notification — even though one goes to a specific topic and the other would default to General. This is explicitly out of scope for this PR (failure-destination routing is listed as unchanged), but reviewers may want to track it as a follow-up.

Confidence Score: 5/5

  • This PR is safe to merge — the change is additive, backward-compatible, and covers the new path with solid tests.
  • The fix is minimal and well-contained: a new optional field is threaded through existing normalization and target-resolution pipelines using the same patterns already present for accountId and to. No existing behavior is altered for jobs that don't set threadId. Normalization strips blank/invalid values consistently. Tests cover trimming on create, blank-stripping on patch, preservation when unrelated fields change, and an end-to-end forum-topic delivery assertion confirming the correct numeric messageThreadId reaches the Telegram send layer. The only out-of-scope gap (failure-destination deduplication ignoring threadId) is an existing limitation, not a regression introduced by this PR.
  • No files require special attention. The isSameDeliveryTarget function in src/cron/delivery.ts is a known follow-up candidate but is explicitly out of scope for this PR.

Last reviewed commit: 28de381

Re-review progress:

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 28de381c84

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cron/delivery.ts Outdated
@Alex-Alaniz Alex-Alaniz force-pushed the codex/cron-telegram-threadid branch from ee06f72 to 68d52bd Compare March 12, 2026 15:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 68d52bd32f

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cron/delivery.ts Outdated
@Alex-Alaniz

Copy link
Copy Markdown
Contributor Author

One remaining blocker appears to be infra-only.

  • checks-windows (node, test, 6, 6, pnpm test) failed in Checkout before Node/pnpm/install/test steps started.
  • Windows shards 1/6 through 5/6 passed.
  • check, Bun unit tests, full Node tests, and compat-node22 also passed on the rebased head.
  • Rebasing onto latest main fixed the earlier unrelated src/secrets/target-registry.test.ts failure.

I also re-ran the targeted tests locally on the rebased head:

  • src/secrets/target-registry.test.ts
  • src/cron/normalize.test.ts
  • src/cron/service.jobs.test.ts
  • src/cron/isolated-agent.delivery-target-thread-session.test.ts
  • src/cron/isolated-agent.direct-delivery-forum-topics.test.ts

Could a maintainer rerun the failed Windows shard or the workflow?

@Alex-Alaniz

Copy link
Copy Markdown
Contributor Author

@tyler6204 this is ready on the rebased head 68d52bd32f1bd06d35b5bb5eb66e5ff826b65114.

The only remaining red check is checks-windows (node, test, 6, 6, pnpm test), and that job failed in Checkout before install or tests started. All other test lanes are green on this head, including Linux, Bun, and Windows shards 1/6 through 5/6.

Could you rerun that failed Windows shard or the workflow when you have a moment?

@Alex-Alaniz Alex-Alaniz force-pushed the codex/cron-telegram-threadid branch from 68d52bd to 656eb1d Compare March 12, 2026 18:13
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime size: M and removed size: S labels Mar 12, 2026
@Alex-Alaniz

Copy link
Copy Markdown
Contributor Author

Follow-up pushed in Alex-Alaniz@656eb1d.

This addresses the remaining threadId gap in two places:

  • keep failure notifications when the primary announce target uses explicit delivery.threadId
  • accept and return delivery.threadId through the gateway cron schema so add/update/list round-trip cleanly

Local verification:

  • bunx vitest run src/cron/delivery.test.ts
  • bunx vitest run src/gateway/server.cron.test.ts --testNamePattern='handles cron CRUD, normalization, and patch semantics'
  • pnpm exec oxfmt --check src/cron/delivery.ts src/cron/delivery.test.ts src/gateway/protocol/schema/cron.ts src/gateway/server.cron.test.ts
  • pnpm exec oxlint src/cron/delivery.ts src/cron/delivery.test.ts src/gateway/protocol/schema/cron.ts src/gateway/server.cron.test.ts

@Alex-Alaniz

Copy link
Copy Markdown
Contributor Author

The remaining red check job is hitting the same repo-wide formatter drift that is now failing on main, not anything in the cron diff.

Evidence:

  • PR check failure: ui/src/styles/chat/grouped.css, ui/src/styles/chat/layout.css, ui/src/styles/layout.css
  • current main CI is failing on the same three files in check
  • this branch diff does not touch any ui/src/styles/* files

I’m leaving those CSS files out of this PR to keep the scope on the cron fix.

@openclaw-barnacle

Copy link
Copy Markdown

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

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 27, 2026
@clawsweeper

clawsweeper Bot commented Apr 27, 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: current main still has the source-level cron failure-destination dedupe gap, and this PR is a focused repair with sufficient terminal proof, but the head is now merge-conflicting and needs adaptation to the newer delivery-target routing code before merge.

Canonical path: Close this PR as superseded by #31059.

So I’m closing this here and keeping the remaining discussion on #31059.

Review details

Best possible solution:

Close this PR as superseded by #31059.

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

Yes, at source level. Current main still compares failure destinations without delivery.threadId, matching the reported suppression path; I did not run tests because this review is read-only.

Is this the best way to solve the issue?

Yes, after rebase. The proposed direction is the narrow owner-boundary fix, but it must be adapted to current main's newer delivery-target/session-route implementation before merge.

Security review:

Security review cleared: The diff changes cron routing logic, tests, and changelog text without new dependencies, workflow permissions, secret handling, downloads, or execution surfaces.

What I checked:

  • linked superseding PR: feat(cron): add failure destination support to failed cron jobs #31059 (feat(cron): add failure destination support to failed cron jobs) is merged at 2026-03-02T15:27:41Z.
  • cluster evidence: the durable review links that PR in the work cluster or recommended risk path.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • kesor: Authored the merged failure-destination feature PR that added the failure alert routing and duplicate-prevention surface this PR amends. (role: feature introducer; confidence: high; commits: 4b4ea5df8bc1; files: src/cron/delivery.ts, src/cron/types.ts, src/gateway/protocol/schema/cron.ts)
  • cwmine: Credited on the merged Telegram topic-routing fix that added cron/session threadId support adjacent to this failure-destination behavior. (role: topic-routing contributor; confidence: high; commits: 6776306387cc; files: src/cron/delivery.ts, src/cron/isolated-agent/delivery-target.ts, src/cron/types.ts)
  • Shakker: Split cron delivery planning from sending and moved the failure-destination planner into the current central delivery-plan file. (role: refactor author; confidence: medium; commits: de952c036abc; files: src/cron/delivery-plan.ts, src/cron/delivery.ts)
  • steipete: Recent current-main commits and blame touch the same cron delivery-plan and delivery-target route resolution surfaces now conflicting with this PR. (role: recent adjacent contributor; confidence: medium; commits: c4f0da00a9fe, 45fbf2d81a0e; files: src/cron/delivery-plan.ts, src/cron/isolated-agent/delivery-target.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 45fbf2d81a0e.

@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Apr 28, 2026
@Alex-Alaniz Alex-Alaniz force-pushed the codex/cron-telegram-threadid branch from 656eb1d to 80e0167 Compare May 5, 2026 07:17
@openclaw-barnacle openclaw-barnacle Bot removed app: web-ui App: web-ui gateway Gateway runtime labels May 5, 2026
@Alex-Alaniz Alex-Alaniz changed the title cron: honor delivery.threadId for Telegram announce delivery cron: keep failure destination for threaded delivery May 5, 2026
@openclaw-barnacle openclaw-barnacle Bot added size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed size: M labels May 5, 2026
@Alex-Alaniz Alex-Alaniz force-pushed the codex/cron-telegram-threadid branch from 80e0167 to 21c07ca Compare May 5, 2026 07:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21c07ca88b

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cron/delivery-plan.ts Outdated
@@ -223,6 +224,7 @@ function isSameDeliveryTarget(
return (
failureChannelNormalized === primaryChannelNormalized &&
failurePlan.to === primaryTo &&
failurePlan.accountId === primaryAccountId
failurePlan.accountId === primaryAccountId &&
primaryThreadId === undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve dedupe when failure target still resolves same thread

The new primaryThreadId === undefined guard disables deduplication for every threaded primary delivery, but failure destinations without an explicit threadId can still resolve back to the same thread via session-based thread carry (resolveSessionDeliveryTarget/resolveDeliveryTarget keep lastThreadId when to matches). In that case this function now returns false and we send an unnecessary duplicate failure notification to the same effective destination; this regression is triggered whenever a threaded job has a same-chat failure destination and a matching session thread context.

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S and removed size: XS labels May 5, 2026
@Alex-Alaniz Alex-Alaniz force-pushed the codex/cron-telegram-threadid branch from e43f67c to e80edd9 Compare May 5, 2026 14:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e80edd9261

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cron/delivery-plan.ts Outdated

if (
failureChannelNormalized !== primaryChannelNormalized ||
(failurePlan.to !== primaryTo && !failureUsesSessionRecipient) ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Don't dedupe channel-only failure destinations

This new comparison path treats a failure destination with channel set but no to as equivalent to an explicit primary recipient, so resolveFailureDestination() can return null even when the fallback would actually send somewhere else. Specifically, when failurePlan.to is undefined, the (failurePlan.to !== primaryTo && !failureUsesSessionRecipient) check is bypassed and same-channel/account targets are deduped; but resolveDeliveryTarget() can still resolve that channel-only fallback to lastTo from session state (which may differ from delivery.to). In jobs with explicit primary targets plus channel-only failure destinations, this can silently suppress legitimate failure alerts.

Useful? React with 👍 / 👎.

@Alex-Alaniz

Alex-Alaniz commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Added exact-head real behavior proof for the suppressSessionThreadId failure-send path.

Command run locally on the PR branch head 8f07f8cdcc335683d96efc054c3ad97d582ac20d:

pnpm exec vitest run --config test/vitest/vitest.cron.config.ts src/cron/delivery.failure-notify.test.ts src/cron/isolated-agent/delivery-target.test.ts --reporter verbose

Relevant live output:

✓ |cron| src/cron/isolated-agent/delivery-target.test.ts > resolveDeliveryTarget > suppresses session Telegram topic threadId carry for threadless failure destinations
✓ |cron| src/cron/isolated-agent/delivery-target.test.ts > resolveDeliveryTarget > suppresses session Telegram topic threadId carry when the failure destination inherits the session target
✓ |cron| src/cron/delivery.failure-notify.test.ts > sendFailureNotificationAnnounce > can suppress session-derived threadIds while preserving the failure session context

Test Files  2 passed (2)
Tests  41 passed (41)

What this proves:

  • resolveDeliveryTarget(..., { suppressSessionThreadId: true }) drops the session-derived Telegram topic thread for a threadless failure destination.
  • The inherited-session target case is covered too, so a failure destination using the cron delivery session key does not accidentally reuse the original topic.
  • sendFailureNotificationAnnounce() passes { suppressSessionThreadId: true } into delivery-target resolution while still preserving the failure session context.

No code changes in this comment; this is the missing proof requested by ClawSweeper.

Re-review progress:

@Alex-Alaniz Alex-Alaniz force-pushed the codex/cron-telegram-threadid branch from 8f07f8c to 7f425f9 Compare May 7, 2026 03:12
@openclaw-barnacle openclaw-barnacle Bot added the proof: supplied External PR includes structured after-fix real behavior proof. label May 7, 2026
@clawsweeper clawsweeper Bot added mantis: telegram-visible-proof Mantis should capture Telegram visible proof. proof: sufficient ClawSweeper judged the real behavior proof convincing. P2 Normal backlog priority with limited blast radius. impact:message-loss Channel message delivery can be lost, duplicated, or misrouted. impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. and removed mantis: telegram-visible-proof Mantis should capture Telegram visible proof. labels May 10, 2026
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. 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. and removed impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. impact:message-loss Channel message delivery can be lost, duplicated, or misrouted. labels May 18, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

✨ Hatched: ✨ glimmer Neon Branchling

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

Rarity: ✨ glimmer.
Trait: stacks clean commits.
Image traits: location workflow harbor; accessory release bell; palette coral, mint, and warm cream; mood curious; pose standing beside its cracked shell; shell soft velvet shell; lighting moonlit rim light; background subtle branch markers.
Share on X: post this hatch
Copy: My PR egg hatched a ✨ glimmer Neon Branchling in ClawSweeper.

What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

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

Labels

gateway Gateway runtime mantis: telegram-visible-proof Mantis should capture Telegram visible proof. 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. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cron delivery ignores threadId for Telegram forum topics

1 participant