feat(channels): batch 2 producers drop media placeholder bodies#111665
Conversation
Media-placeholder program batch 2: Discord, Feishu, and MSTeams stop minting <media:kind> placeholder bodies. Primary bodies carry captions only; native resources produce ordered structured facts with type-only facts for failures/rejections (batch-1 alignment contract). Discord stickers travel as native kind "sticker" (not MIME-derivable); text carriers (forwarded snapshots, room history, thread starters, pending text) render via formatMediaPlaceholderText. Feishu audio preflight and MSTeams unavailable selection move from body-string equality to structured facts; dead placeholder payload builders deleted.
|
Codex review: needs real behavior proof before merge. Reviewed July 20, 2026, 1:34 AM ET / 05:34 UTC. Summary PR surface: Source +139, Tests +438, Other -1. Total +576 across 55 files. Reproducibility: not applicable. This PR changes an established inbound-media representation rather than reporting a standalone current-main failure. Review metrics: none identified. Stored data model 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:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Keep caption text and structured media facts as the single inbound contract, retain formatter output only for text-only history surfaces, and land only after redacted real ingress proof covers media-only behavior in Discord, Feishu, and Microsoft Teams. Do we have a high-confidence way to reproduce the issue? Not applicable: this PR changes an established inbound-media representation rather than reporting a standalone current-main failure. Is this the best way to solve the issue? Yes, conditionally: extending the established structured-media-fact contract is the narrowest maintainable direction, but it needs real ingress proof before the compatibility-visible transition is accepted. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against a22ede72e494. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +139, Tests +438, Other -1. Total +576 across 55 files. View PR surface stats
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
Review history (1 earlier review cycle)
|
|
Maintainer decision on the current ClawSweeper rank-up moves: accept automated-only evidence for this batch and proceed with landing. This is the second producers-first migration using the already-landed structured media-fact contract. I explicitly accept the documented compatibility-visible rendering transition: primary inbound bodies are captions only, while text-only history carriers render structured facts. The changed Discord, Feishu, and Microsoft Teams paths have focused success/failure/alignment coverage; independent autoreview is clean; exact-head CI is green; and delegated Blacksmith check:changed is green in https://github.com/openclaw/openclaw/actions/runs/29716797532. I am therefore not requiring separate live credentials and redacted ingress captures from all three services for this landing. The reported serialized-state items do not require a migration: one is a Feishu test file, and the history media kind change is additive structured metadata rather than a persisted schema-version change. |
|
Merged via squash.
|
…claw#111665) * feat(channels): batch 2 producers drop media placeholder bodies Media-placeholder program batch 2: Discord, Feishu, and MSTeams stop minting <media:kind> placeholder bodies. Primary bodies carry captions only; native resources produce ordered structured facts with type-only facts for failures/rejections (batch-1 alignment contract). Discord stickers travel as native kind "sticker" (not MIME-derivable); text carriers (forwarded snapshots, room history, thread starters, pending text) render via formatMediaPlaceholderText. Feishu audio preflight and MSTeams unavailable selection move from body-string equality to structured facts; dead placeholder payload builders deleted. * test(discord): make allowlist policy explicit
What Problem This Solves
Batch 2 of the media-placeholder elimination program (PR1 #111315, batch 1 #111447). Discord, Feishu, and MSTeams — the aggregate/expected-count/sticker channels — still minted
<media:kind>placeholder bodies for media-only inbound messages and used body-string equality for behavior decisions (Feishu audio preflight, MSTeams unavailable-media selection).Why This Change Was Made
kind: "sticker"(image-compatible MIME makes them non-derivable) — the existing inbound/history kind unions and the formatter vocabulary grow by that one member, no SDK export change. PR1's audio precedence (definitive MIME > voice fields > constrained filename) is preserved and re-tested. Forwarded snapshots, room-event history, and thread starters render facts viaformatMediaPlaceholderText; the deadbuildDiscordMediaPayloadand per-attachment placeholder strings are deleted (guardrails inventory updated for exactly that export). The 1,365-line message-utils test file was split; its stale max-lines baseline entry removed (no cap raised).parseMessageContentreturns captions/speech-to-text only; the(file_name)annotation is deliberately dropped (only non-derivable annotation, not a stable cross-channel fact). Audio preflight gates on native message type + materialized audio facts instead ofcontent === "<media:audio>".sourceId) or a sole compatible id-less slot; unavailable notices derive from pathless facts, not body comparison. The dead placeholder payload builder is hard-deleted.User Impact
Media-only messages from these three channels no longer inject
<media:image> (N images)-style text into transcripts, previews, or prompts; captions are exactly what the user typed. Failed/partial downloads keep their existing notices and now preserve attachment kind/count/order to the model. Accepted rendering drift on text carriers: a single item renders as a bare tag (no "(1 image)" annotation).Evidence
node scripts/run-vitest.mjs(one file per invocation): Discord 12 files (message-media 33+4, message-text 18, preflight 50, monitor 42, threading 11, audio 5, context/history 16, routing 10, inbound-job 5, message-utils 9), Feishu bot 94 + helpers 14, MSTeams 6 files (123 tests), shared/core formatter 8, inbound-media 7, history 4.check:changedexit 0 on Blacksmith Testbox (run 29716797532) — typechecks, lint, formatting, max-lines ratchet, SDK API baseline, plugin boundaries, dependency guards, import cycles. Two earlier remote rounds caught and fixed a stale max-lines entry and a Feishu test-local type gap before this PR was opened.git diff --checkclean, zero<media:*>literals left in the three channels' production code.