fix(discord): raise thread title timeout and tokens to fit reasoning models#64734
fix(discord): raise thread title timeout and tokens to fit reasoning models#64734hanamizuki wants to merge 3 commits into
Conversation
Greptile SummaryRaises Confidence Score: 5/5Safe to merge — two constant bumps in a fire-and-forget background path with no user-latency impact and well-supported by production data in the PR description. All findings are P2 or lower. The changes are minimal (two constants + one test assertion), the fire-and-forget dispatch means no user-visible regression risk, and the values chosen (4096 tokens, 60 s) are within every mainstream provider's output ceiling while covering the observed p95 latency with significant margin. No files require special attention. Reviews (1): Last reviewed commit: "fix(discord): raise thread title timeout..." | Re-trigger Greptile |
|
Codex review: needs real behavior proof before merge. Reviewed June 6, 2026, 12:57 AM ET / 04:57 UTC. Summary PR surface: Source +29, Tests +58, Docs +1. Total +88 across 3 files. Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Review detailsBest possible solution: Retry the Codex review after fixing the execution failure. Do we have a high-confidence way to reproduce the issue? Unclear. The review failed before ClawSweeper could establish a reproduction path. Is this the best way to solve the issue? Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction. AGENTS.md: unclear because the file could not be read completely. Codex review notes: model gpt-5.5, reasoning high; reviewed against 9313471fa579. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +29, Tests +58, Docs +1. Total +88 across 3 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
|
|
Resolved — added the missing changelog entry under Unreleased Fixes with |
86258a9 to
78034dd
Compare
26a4128 to
8b6082a
Compare
|
Rebased onto current Earlier commit base was on a The Re-running CI; previous failures were stale. |
|
Resolved the P2 finding (Clamp title tokens to selected model limits).
Branch rebased onto current |
|
ClawSweeper PR egg ✨ Hatched: 💎 rare Brave Patch Peep Hatch commandComment Hatchability rules:
Rarity: 💎 rare. What is this egg doing here?
|
|
This pull request has been automatically marked as stale due to inactivity. |
…models Follow-up to openclaw#64172. That PR raised DISCORD_THREAD_TITLE_MAX_TOKENS from 24 to 512, which unblocked very short messages but left two failure modes for moderately complex inputs on reasoning models: 1. 512 is still too tight when the thinking block alone exceeds the budget - extractAssistantText returns empty, generateThreadTitle returns null, rename is silently skipped. 2. DEFAULT_THREAD_TITLE_TIMEOUT_MS = 10_000 kills ~15% of real production samples: observed MiniMax M2.7 thinking latency ranges 1.7s to 17s for the same task, median 7s, p95 16.9s (n=20). Raise MAX_TOKENS 512 -> 4096 and TIMEOUT 10_000 -> 60_000. Both headrooms are safe because maybeRenameDiscordAutoThread is dispatched without await - a longer rename cannot block message delivery. Worst case is the thread keeps its original title for up to a minute longer before being renamed, which is strictly better than the 15% silent failure rate today. Update the matching maxTokens assertion in the unit test. Full data, percentile distribution, and why-these-numbers analysis in the PR description. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Forwarding the fixed 4096 title budget unchanged as options.maxTokens can exceed a selected model's configured output limit. Anthropic/OpenAI transports prefer a runtime maxTokens over the model cap, so the provider can reject the over-limit request and the fire-and-forget rename is silently skipped. Clamp to the positive minimum of the requested budget and the model cap, mirroring the established resolveImageToolMaxTokens idiom, with default (uncapped/high) and clamped coverage.
|
Clownfish 🐠 reef update Thanks for the useful work here. Clownfish could not update this branch directly, so the replacement PR is the writable swim lane for the same fix path. Replacement PR: #92836 fish notes: model gpt-5.5, reasoning xhigh; reviewed against b7f3f5a. |
|
Thanks for the fix. I landed this via the ProjectClownfish replacement path because the contributor fork branch could not be safely rebased/pushed by the GitHub App workflow, while the replacement branch preserved your changelog credit and co-author attribution. |
Follow-up to merged PR #64172 (
fix(discord): raise thread title max tokens for reasoning models).tl;dr
PR #64172 raised
DISCORD_THREAD_TITLE_MAX_TOKENSfrom 24 to 512. That unblocked very short messages but left two bugs in place:Both failure paths are silent (
logVerboseonly), so the feature "works most of the time on short messages" but silently degrades on anything harder, with no signal in production logs.This PR:
DISCORD_THREAD_TITLE_MAX_TOKENS512 → 4096.DEFAULT_THREAD_TITLE_TIMEOUT_MS10_000 → 60_000.Both constants are safe to raise significantly because the rename request is fire-and-forget:
maybeRenameDiscordAutoThreadis called withoutawait, so increasing headroom only costs worst-case "thread renamed a little later," not user-visible latency.Real behavior proof
Behavior or issue addressed: Discord
autoThreadName: "generated"rename silently no-ops on reasoning models — the 512-token budget is fully consumed by the thinking block before any title text is emitted, and/or the 10 s timeout aborts mid-thinking. Both paths return null with no production signal (logVerboseonly, suppressed on non-verbose gateways).Real environment tested: Production OpenClaw gateway running as a non-verbose LaunchAgent, Discord channel set to
autoThreadName: "generated", modelminimax-portal/MiniMax-M2.7overanthropic-messages.Exact steps or command run after this patch: Rebuilt the patched gateway, restarted the running OpenClaw gateway, sent a real ~17-character user message into the auto-thread channel, then read the gateway runtime log for the fire-and-forget
completeThreadTitleoutcome (diagnostic lines emitted into the compiled gateway output for capture).Evidence after fix: Gateway runtime log, same rename flow, before vs after raising the budget to 4096:
Captured abort on the timeout path under the old 10 s limit (one of 3/20 real production latency samples that exceeded it):
Observed result after fix: With the 4096 budget and 60 s timeout the gateway response carries both a
thinkingand atextblock,extractAssistantTextreturns a non-empty title, and the Discord thread is renamed. All 20 real production latency samples (max 16,935 ms) complete inside the 60 s window; the pre-fix silent skip no longer reproduces on this setup.What was not tested: The model-cap clamp (P2 follow-up) was not reproduced against a live under-cap provider. It is a defensive guard against an over-limit request on the same fire-and-forget path, whose failure is by design unobservable in production logs; it mirrors the shipped
resolveImageToolMaxTokensidiom and is covered structurally rather than by a live capture.Background
OpenClaw's Discord auto-thread rename flow:
maybeCreateDiscordAutoThreaddetectsautoThreadName: "generated"and kicksmaybeRenameDiscordAutoThread(fire-and-forget).generateThreadTitle→prepareSimpleCompletionModelForAgent→completeThreadTitle→ Anthropic-messages API.extractAssistantTextpulls thetextblock from the response;normalizeGeneratedThreadTitletrims it./channels/{id}to rename.Any failure in steps 3-5 is caught internally and logged through
logVerbose(), which is suppressed unless the gateway is started with--verbose. Production gateways run as LaunchAgent / systemd services without verbose, so every failure is invisible.Problem 1: 512 token budget is still too tight for real messages
The pattern
DISCORD_THREAD_TITLE_MAX_TOKENSis a hard ceiling on the total response from the model. For reasoning models served throughanthropic-messages(MiniMax M2.7, Claude thinking models, OpenAI o-series), the response contains athinkingblock followed by atextblock, and both count againstmax_tokens.If the thinking block consumes the full budget before any text is emitted:
contenthas only a{type: "thinking", ...}entryextractAssistantTextreturns""(it only extractstextblocks)normalizeGeneratedThreadTitle("")returns""generateThreadTitlereturnsnullmaybeRenameDiscordAutoThreadhitsif (!generated) return;and silently gives upPre-fix evidence (24-token budget, from #64172)
{ "content": [ {"type": "thinking", "thinking": "The user is asking me to generate a concise Discord thread title...", "thinkingSignature": ""} ] }Response has only
thinking, notext.rawTextLen=0. This is the baseline failure PR #64172 was trying to fix.Why 512 was a half-fix
512 tokens works for trivial prompts like "Auto Thread Name Test" (12-char English, low thinking overhead) — and that was the scenario I tested against when validating #64172. But real agent inputs look nothing like that.
An actual production message — a short (~17 character) informal user instruction in Chinese, routed to
minimax-portal/MiniMax-M2.7. Response under 512:Thinking alone exceeded 512 tokens. Result: silent
nullreturn, thread never renamed, and zero log output in a production gateway.Post-fix behavior at 4096
Same flow, budget raised to 4096:
Response now contains both a
thinkingblock and atextblock.rawLen > 0,extractAssistantTextproduces a non-empty title, rename proceeds.Why 4096 (not higher, not lower)
max_tokensis a cap, not an allocation. Billing is based on actual tokens generated. A 5-word title costs the same whether the cap is 512 or 32k.max_tokensoften lets the thinking phase expand into the extra space, which leads us into the second bug.4096 sits comfortably inside every mainstream provider's output ceiling, leaves a ~3k-token safety margin above the largest observed thinking pass, and doesn't push the model into unbounded reasoning.
Problem 2: 10-second timeout is too tight for reasoning models
Observed symptom
After deploying the 4096-token fix, a second failure surfaced on the next real message. Debug log (
console.errorpatches inserted into the compiled output for diagnosis):Diagnosis:
dtMs=10016— the AbortController fired at exactly the 10-secondDEFAULT_THREAD_TITLE_TIMEOUT_MSboundary.contentBlocks=thinking— the model was still inside the thinking phase when the abort hit; the text block never materialized.rawLen=0— identical end-state to the 24-token pre-fix failure, but through a completely different code path.This is not a token budget problem. The budget is 4096 and the model was not CPU-bound on output limits. The model simply took longer than 10 seconds to finish thinking, and our timeout killed it.
Distribution data (20 real samples from production observation period)
Collected from a MiniMax M2.7 reasoning model running across normal production traffic, sorted ascending (dtMs):
Summary statistics:
Under the previous
DEFAULT_THREAD_TITLE_TIMEOUT_MS = 10_000:Under
DEFAULT_THREAD_TITLE_TIMEOUT_MS = 60_000:Why 10s was a reasonable default that aged badly
When thread rename was originally written the calling model was presumably a non-reasoning completion model, for which 10 seconds is ample. With reasoning models the latency distribution has a much heavier tail — not because reasoning models are slower on average (median is well under 10s), but because they have a genuinely bimodal response time where occasional thinking passes take 2-3× the median. A 10-second cap puts the timeout right on top of the body of the distribution.
Why 60s is safe
maybeRenameDiscordAutoThreadis dispatched withoutawait:The parent handler (
maybeCreateDiscordAutoThread) has already returned thecreatedIdand user-facing message processing has moved on. The rename is a background best-effort upgrade. A longer timeout cannot block message delivery, cannot delay the agent's reply, and cannot leak into user-perceived latency.The worst-case consequence of the 60s ceiling is: an auto-thread exists with its original message as its title for up to a minute longer than expected, before being renamed. This is strictly better than the current behavior, where on 15% of requests the thread is never renamed at all.
60s also leaves a meaningful margin above the observed p95 (16.9s), giving headroom for:
Changes
extensions/discord/src/monitor/thread-title.tsComments updated to explain the reasoning-model thinking-budget interaction and the fire-and-forget safety argument.
extensions/discord/src/monitor/thread-title.generate.test.tsexpect(completeWithPreparedSimpleCompletionModelMock.mock.calls[0]?.[0]?.options).toEqual( expect.objectContaining({ - maxTokens: 512, + maxTokens: 4096, }), );Observability
This investigation took significantly longer than it needed to because every failure path in
thread-title.tsandmaybeRenameDiscordAutoThreadis logged throughlogVerbose(), which a default production gateway suppresses. The feature appeared broken with no logs, no error events, and no metrics — we only confirmed the failure modes by injectingconsole.errorpatches into the compiled output and triggering real messages.Suggestion for a future PR (not in this one to keep the diff focused): move the four failure points in
maybeRenameDiscordAutoThreadandgenerateThreadTitleofflogVerboseand onto a structuredwarn-level logger, so operators can diagnose autoThread rename drift from production logs alone. Candidate log points:Each one is a rare event under a healthy config and wouldn't add meaningful log volume.
Test plan
thread-title.generate.test.ts)Not tested (low-risk paths, noted for reviewer awareness):
Related
DISCORD_THREAD_TITLE_MAX_TOKENS24 → 512. Merged 2026-04-10. This PR is the follow-up that addresses the two failure modes that survived that first fix.