fix(feishu): fallback when accepted turns send no visible reply#87896
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed May 31, 2026, 2:36 PM ET / 18:36 UTC. Summary PR surface: Source +108, Tests +336. Total +444 across 9 files. Reproducibility: no. live current-main reproduction was run in this read-only review. Source inspection plus the linked Feishu logs show the relevant current behavior: accepted turns can complete with queuedFinal=false/replies=0 and current main has no fallback path. Review metrics: 1 noteworthy metric.
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
Security Review detailsBest possible solution: Land this after current-head Feishu proof shows the fallback appears for true no-visible accepted turns and stays suppressed for intentional silence, source-denied delivery, message-tool-only delivery, and already-visible replies. Do we have a high-confidence way to reproduce the issue? No live current-main reproduction was run in this read-only review. Source inspection plus the linked Feishu logs show the relevant current behavior: accepted turns can complete with queuedFinal=false/replies=0 and current main has no fallback path. Is this the best way to solve the issue? Yes, this is the best bounded fix shape I found: Feishu owns the user-visible fallback while core only exposes dispatch metadata needed to avoid false fallbacks. The remaining blocker is proof of the exact current PR head, not a different code direction. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 7e0d275f7add. Label changesLabel justifications:
Evidence reviewedPR surface: Source +108, Tests +336. Total +444 across 9 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
|
19632ad to
80a578d
Compare
80a578d to
f783875
Compare
|
Additional live context from our Feishu incident triage: this PR is still highly relevant to accepted Feishu turns producing no useful visible response. Observed adjacent symptoms in production around May 28:
I agree with the current review direction: the no-visible-reply fallback needs to cover empty streaming/card closes and active broadcast zero-final dispatches before merge. From the user-impact side, this is not cosmetic logging; it is a channel delivery integrity issue where an accepted turn can appear ignored. |
|
Status refresh after checking the PR without pulling the repo locally:
So this should stay in author-action / needs-code-change state rather than being treated as ready for merge based only on green checks. |
|
@clawsweeper automerge Special instructions: |
f783875 to
8727789
Compare
95c9055 to
7834e3e
Compare
|
Maintainer repair/review complete for exact head What changed:
Local proof on the rebased branch:
CI:
Known gap: no new live Feishu account run was performed; this was covered with focused dispatcher/bot/lifecycle/streaming-card regression tests plus CI Real behavior proof. |
…claw#87896) * fix(feishu): fallback when accepted turns send no visible reply * fix(feishu): cover no-visible-reply fallback gaps * fix(feishu): mark media replies visible * fix(feishu): honor suppressed delivery fallback * test(auto-reply): trim fallback test churn * fix(feishu): gate empty fallback eligibility * test(auto-reply): expect fallback metadata after denied dispatch * fix(feishu): fallback after failed visible final sends * test(feishu): keep reply dispatcher mock shape aligned * fix(auto-reply): respect silent policy for no-visible fallback * fix(feishu): wait for streaming close before fallback * fix(feishu): clear silent skip before later finals * fix(feishu): preserve visible state across keepalives * test(feishu): align lifecycle dispatcher mocks * fix(feishu): require accepted streaming content for fallback --------- Co-authored-by: ArthurNie <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
|
CI triage for current head This does not look like missing user-side proof anymore; it is CI/code-quality cleanup on the repaired head. Current failing checks I inspected:
So the actionable next step appears to be a maintainer/ClawSweeper cleanup pass on lint/type/boundary issues for the repaired head, not more production proof from Arthur. @clawsweeper re-review |
|
🦞🧹 Reason: re-review requires an open issue or PR. |
…claw#87896) * fix(feishu): fallback when accepted turns send no visible reply * fix(feishu): cover no-visible-reply fallback gaps * fix(feishu): mark media replies visible * fix(feishu): honor suppressed delivery fallback * test(auto-reply): trim fallback test churn * fix(feishu): gate empty fallback eligibility * test(auto-reply): expect fallback metadata after denied dispatch * fix(feishu): fallback after failed visible final sends * test(feishu): keep reply dispatcher mock shape aligned * fix(auto-reply): respect silent policy for no-visible fallback * fix(feishu): wait for streaming close before fallback * fix(feishu): clear silent skip before later finals * fix(feishu): preserve visible state across keepalives * test(feishu): align lifecycle dispatcher mocks * fix(feishu): require accepted streaming content for fallback --------- Co-authored-by: ArthurNie <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
…claw#87896) * fix(feishu): fallback when accepted turns send no visible reply * fix(feishu): cover no-visible-reply fallback gaps * fix(feishu): mark media replies visible * fix(feishu): honor suppressed delivery fallback * test(auto-reply): trim fallback test churn * fix(feishu): gate empty fallback eligibility * test(auto-reply): expect fallback metadata after denied dispatch * fix(feishu): fallback after failed visible final sends * test(feishu): keep reply dispatcher mock shape aligned * fix(auto-reply): respect silent policy for no-visible fallback * fix(feishu): wait for streaming close before fallback * fix(feishu): clear silent skip before later finals * fix(feishu): preserve visible state across keepalives * test(feishu): align lifecycle dispatcher mocks * fix(feishu): require accepted streaming content for fallback --------- Co-authored-by: ArthurNie <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
Summary
Why
Feishu/Lark accepted inbound turns can currently end with no user-visible output when the runtime aborts, a restart interrupts before visible content, or streaming-card terminalization fails before content is shown. In those cases the user sees silence even though the message was accepted and dispatched.
This patch makes Feishu track whether the dispatcher actually delivered visible output, rather than treating
counts.final === 0alone as proof of silence. The bot still uses the zero-final dispatch completion as the trigger point, but the dispatcher guard prevents false fallbacks after visible streaming/card output or intentional NO_REPLY.Tests
Lightweight local checks on a constrained VPS:
extensions/feishu/src/reply-dispatcher.tsextensions/feishu/src/reply-dispatcher.test.tsextensions/feishu/src/bot.tsgit diff --checkpnpm exec vitest run --config test/vitest/vitest.unit.config.ts extensions/feishu/src/reply-dispatcher.test.ts --runInBandReal behavior proof
Behavior or issue addressed: Feishu accepted inbound turns could complete without user-visible output after interruption / restart / streaming-card empty terminalization. The fix ensures an explicit visible fallback is sent when no visible reply was delivered, while preserving intentional NO_REPLY/silent behavior.
Real environment tested: Live OpenClaw gateway on VPS host
iv-yegrn5k9vky6d72c8f3t, Feishu accountdefault, real Feishu group chat runtime. Local runtime hotfix was applied to the installed OpenClaw/Lark runtime and the gateway was restarted.Exact steps or command run after this patch: Applied the local runtime hotfix equivalent to the PR behavior, restarted the live OpenClaw gateway, then observed real Feishu runtime logs for an accepted turn reaching the empty-reply streaming fallback path instead of staying silent.
Evidence after fix: Redacted runtime log excerpt from the live setup:
Local evidence artifacts retained on the live host:
Observed result after fix: The real Feishu runtime no longer left that empty terminalization path silent; it reached the visible empty-reply fallback path after the hotfix/restart. The upstream PR turns that local behavior into dispatcher-level visible-output tracking so duplicate fallback is suppressed after already-visible card/text output and intentional silent finals are not converted into user-visible warnings.
What was not tested: The exact upstream TypeScript patch was not package-released and installed into the live gateway yet; targeted unit coverage is included in this PR and CI is expected to validate it.