Skip to content

fix(outbound): check delivery identity for media results before recording success#93401

Closed
xzh-icenter wants to merge 2 commits into
openclaw:mainfrom
xzh-icenter:fix/cron-tts-delivery-identity-check
Closed

fix(outbound): check delivery identity for media results before recording success#93401
xzh-icenter wants to merge 2 commits into
openclaw:mainfrom
xzh-icenter:fix/cron-tts-delivery-identity-check

Conversation

@xzh-icenter

Copy link
Copy Markdown
Contributor

Summary

The sendPayload branch checks hasDeliveryResultIdentity before counting a delivery result as sent, but the media branch was missing this check. When a channel adapter returns a media result with no messageId or other identity fields (e.g. QQBot rejecting a TTS temp file outside its media roots), the delivery was incorrectly recorded as successful.

This caused cron auto-TTS deliveries to QQBot to be reported as delivered when the message was actually lost, since the generated audio file was outside QQBot's allowed media roots and the adapter returned an error result with no delivery identity.

Real behavior proof

From source inspection of current main:

  1. Cron applies auto-TTS before delivery: src/cron/isolated-agent/delivery-dispatch.ts:894 transforms payloads through maybeApplyTtsToCronPayloads before sendDurableMessageBatch, replacing text with generated audio when messages.tts.auto="always".

  2. TTS creates local temp audio: packages/speech-core/src/tts.ts:2090 writes synthesized audio under the preferred temp root (e.g. /tmp/openclaw/tts-*/voice-*.mp3).

  3. QQBot rejects temp files outside media roots: extensions/qqbot/src/engine/messaging/outbound-media-send.ts:132 validates local media paths against allowed media roots. Generated TTS temp files outside those roots produce an error result with no delivery identity.

  4. QQBot returns no-identity result: extensions/qqbot/src/channel.ts:124 converts engine results with messageId: result.messageId ?? "", so an error still returns an object with no delivery identity.

  5. Media branch lacks identity check: src/infra/outbound/deliver.ts:1894 pushes every media result and counts it as sent based on a non-empty slice. Unlike the sendPayload branch at line 1730, it does not check hasDeliveryResultIdentity before classifying success.

Fix

Add hasDeliveryResultIdentity check to the media delivery loop in deliver.ts, consistent with the sendPayload branch behavior. No-identity media results are now properly skipped instead of being counted as sent.

Testing

The existing test in src/infra/outbound/deliver.test.ts covers the media delivery path with valid results. This fix adds the missing identity check that prevents no-identity results from being counted as sent.

Related

…n show' so rebind detects changed roots

When a managed QMD collection's root path changes (e.g. workspace repoint),
ensureCollections() should detect the change and rebind (remove + re-add)
the collection. The rebind decision in shouldRebindCollection() compares
the listed path against the desired collection path, but
listCollectionsBestEffort() only runs 'qmd collection list --json' which
omits the filesystem path. With listed.path always undefined,
shouldRebindCollection() takes its defensive incomplete-metadata branch
and returns false — collections never rebind.

Add enrichCollectionPaths() which runs 'qmd collection show <name>' for
each listed managed collection whose path is still missing, extracting
the Path line to populate the path field so shouldRebindCollection() can
detect a changed root and trigger a rebind.

Closes openclaw#91251
…ding success

The sendPayload branch checks hasDeliveryResultIdentity before counting
a delivery result as sent, but the media branch was missing this check.
When a channel adapter returns a media result with no messageId or other
identity fields (e.g. QQBot rejecting a TTS temp file outside its media
roots), the delivery was incorrectly recorded as successful.

This caused cron auto-TTS deliveries to QQBot to be reported as
delivered when the message was actually lost, since the generated audio
file was outside QQBot's allowed media roots and the adapter returned
an error result with no delivery identity.

Add the same hasDeliveryResultIdentity check to the media delivery loop
so that no-identity media results are properly skipped, consistent with
the sendPayload branch behavior.

Fixes openclaw#92816
@openclaw-barnacle openclaw-barnacle Bot added extensions: memory-core Extension: memory-core size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 15, 2026
@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

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

Close as superseded: the only useful remaining outbound accounting change is already covered by the broader, clean, proof-positive PR at #79811, while this branch is dirty and also carries unrelated already-main QMD work.

Canonical path: Close this PR and keep review focused on #79811; if maintainers reject that broader path, a fresh narrow branch should be opened from current main.

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

Review details

Best possible solution:

Close this PR and keep review focused on #79811; if maintainers reject that broader path, a fresh narrow branch should be opened from current main.

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

Yes, from source inspection: current main can append a no-identity media result and classify a non-empty delivered slice as sent. I did not run a live QQBot cron reproduction for this PR.

Is this the best way to solve the issue?

No, this is not the best landing path. The media-only patch is plausible but incomplete, and #79811 is the cleaner canonical fix because it applies the identity-required contract across all shared empty-receipt paths with proof and tests.

Security review:

Security review cleared: No concrete security or supply-chain issue was found; the diff changes TypeScript delivery/memory logic only and does not alter dependencies, workflows, secrets, or package execution surfaces.

AGENTS.md: found and applied where relevant.

What I checked:

  • Current media accounting surface: Current main still pushes each returned media result before outcome classification, so a no-identity media result can make the delivered slice non-empty; the nearby sendPayload path already checks hasDeliveryResultIdentity. (src/infra/outbound/deliver.ts:1894, dc573a38dc9c)
  • This PR is media-only and incomplete: The PR inserts a guard before the media results.push, but it does not update the later media emitMessageSent({ success: true }) path or the text/formatted result collection paths. (src/infra/outbound/deliver.ts:1891, 3a7d0ee21bb2)
  • Canonical broader fix: The open canonical PR adds identified-result helpers, applies them to text, formatted-text, fallback, and media result collection, and changes media hook success to depend on actual identified results. (src/infra/outbound/deliver.ts:1906, e046c2868b6a)
  • Canonical PR viability: Live GitHub REST shows the canonical PR is open, non-draft, mergeable, and clean; it is also labeled proof: sufficient and status: ready for maintainer look. (e046c2868b6a)
  • Prior same media-only path was already superseded: The earlier media-only accounting PR was closed as duplicate/superseded by the same broader open PR, preserving the same canonical landing path. (9b1590c09488)
  • Unrelated QMD work is already fixed on main: The unrelated QMD collection-root issue was closed as fixed on main by commit 22bda60, and current main already contains a bounded managed-collection enrichment implementation. (extensions/memory-core/src/memory/qmd-manager.ts:692, 22bda60cbe15)

Likely related people:

  • indulgeback: Authored the broader open PR that applies the identity-required delivery result contract across text, formatted-text, and media paths. (role: canonical open fix author; confidence: high; commits: e046c2868b6a; files: src/infra/outbound/deliver.ts, src/infra/outbound/deliver.test.ts, src/cron/isolated-agent.direct-delivery-core-channels.test.ts)
  • Peter Steinberger: git shortlog shows the heaviest history on the shared outbound delivery file, and nearby history includes foundational channel delivery seam work. (role: feature-history owner; confidence: high; commits: d163278e9cdf, 0feb939cb3bb; files: src/infra/outbound/deliver.ts)
  • sliverp: Authored the merged QQBot channel PR that introduced the plugin surface involved in the linked cron/TTS delivery report. (role: original QQBot feature contributor; confidence: high; commits: bf6f506dfae6; files: extensions/qqbot/src/channel.ts, extensions/qqbot/src/engine/utils/platform.ts)
  • vincentkoc: Fixed the unrelated QMD collection-root issue already present on this branch, which explains one stale branch conflict source. (role: recent adjacent memory-core contributor; confidence: medium; commits: 22bda60cbe15; files: extensions/memory-core/src/memory/qmd-manager.ts, extensions/memory-core/src/memory/qmd-manager.test.ts)

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

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. 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: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 15, 2026
@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: memory-core Extension: memory-core merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS 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.

[Bug]: 当tts设置为always时,cron会投递失败,但是结果会显示已投递

1 participant