Repository navigation
Restore the Translate to English right-click command - #61
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reply Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
simon-langoustine
left a comment
There was a problem hiding this comment.
Request changes. Verdict: verifier-failed at head 5d81ac22b587d50d8f8230ce3da63946419c159a.
One act-on item: the restored command can overwrite an operator-named customer language. The inline thread describes the path and required regression. In the local Worker, /reply translate:es stored Spanish and delivered the Spanish reply; after a newer Hindi customer message, Translate to English changed the thread language to Hindi. RK-7 explicitly requires the operator's named language to remain authoritative.
Checks passed at this head:
pnpm exec vp check: 329 files formatted, no lint warnings or type errors in 196 files.pnpm --filter @respondkit/api test: 159 tests passed.pnpm --filter @respondkit/discord test: 54 tests passed.node apps/api/scripts/verify-agent-api.mjs: 50 HTTP calls and 17 captured Discord posts. Includes/approve, Approve draft, legacy approval buttons, hand-back steer notes and outgoing translation.pnpm discord:commands:apply --dry-run --config config/workspaces.example.json: exactly/agent,/approve, Approve draft,/reply,/status,/retry, Translate to English and/activity. No live registration.
The extended signed-interaction walkthrough used the real local Worker, D1 and workflows, with loopback Discord/Gemini fixtures. It verified private type-5/flags-64 acknowledgment, English replying to the original, cache reuse without a second provider call, private wrong-target and unauthorized responses, provider rejection without failing the original, retry recovery, already-English behavior, and a disabled inbox. It also reproduced the language-precedence failure above. This is local backend verification, not a live Discord UI verdict.
Consider: the existing publication workflow stops waiting after about 283 seconds, while translation retries can last longer. A very slow success may need another menu invocation to publish. This timing case was identified by static review, not reproduced, and is not an additional blocker for this restoration.
Noted: no schema migration or inbox-enablement change; interaction credentials stay out of the workflow parameters and the walkthrough's D1 data. The project CONTEXT.md, verify skill/feature map and role-specific rubric were absent. Review used the card, existing walkthrough and interrogate rubric. Two independent GPT reviewers agreed on the language finding; interrogate's Claude/Grok defaults were unavailable. Live Discord rendering, resizing and screenshots require deployment/registration and were not attempted. No deploy, product edits, push or merge.
Evidence: translation walkthrough and reproduction, structured D1 results, reproducible harness, checks, API tests, Discord tests, agent walkthrough, registration dry-run. The API test log includes workerd canceled-request diagnostics; the suite exited zero with all assertions passing.
| } | ||
| if (interaction.command !== "translate") return; | ||
| const messageId = await resolveTranslationMessage(env, scope, interaction); | ||
| const job = await requestMessageTranslation(env, scope, messageId, interaction.targetLanguage); |
There was a problem hiding this comment.
[P1] Preserve an operator-named language when translating a newer customer message
This new command calls requestMessageTranslation, which reaches TranslationWorkflow.save. The existing update at apps/api/src/workflows/translation.ts:126 only compares timestamps. An operator can select Spanish with /reply translate:es, then receive a newer Hindi customer message and invoke this command. The translation changes thread.customer_language from es to hi, so subsequent /reply translate:customer and default draft approvals use Hindi. This violates RK-7's explicit requirement that translation never overwrite a language an operator named.
Keep explicit language choices authoritative and add a regression covering an explicit choice followed by a newer customer message and this menu action. The save SQL predates the PR, but restoring this entry point exposes it through the new user flow.
Reproduced at this head against wrangler dev --local: before {customer_language:"es", customer_language_updated_at:1792328152338}, after {customer_language:"hi", customer_language_updated_at:1792328155344}. Runtime evidence, walkthrough script.
There was a problem hiding this comment.
Fixed: 486a613 adds the regression (an operator-named es, then Translate to English on a newer Hindi message; it failed with hi), and 8835fe0 makes TranslationWorkflow.save fill thread.customer_language only while it is NULL, so detection never replaces a named language. I re-ran your walkthrough against 1e454ba: "operator language preserved" shows es before and after, and all its other cases pass. I didn't add a column recording where the language came from, because that is a D1 migration. Tradeoff: a detected language also stays fixed until an operator names another one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
simon-langoustine
left a comment
There was a problem hiding this comment.
Approved at head 1e454babdb70431a967dfafa599c9f8878a3670d. Verification: unit-test-verified, plus a signed local Worker integration walkthrough. This re-review covers the changes since 5d81ac22b587d50d8f8230ce3da63946419c159a and the original blocker.
Act-on: none. The language-precedence finding is fixed. I reran the original sequence with a real signed /reply translate:es, a newer Hindi customer message, and Translate to English. D1 retained customer_language: es and customer_language_updated_at: 1792328593870 before and after. The new regression passes, and existing unknown-language detection still passes.
The legacy storeCustomerTranslation timestamp update cannot overwrite an operator language through any current production route at this head. Its only production call is the incoming arm of storeTranslation in workflows/message.ts:404. That helper's sole call at line 956 is inside the outgoing branch, so it selects publishOperatorReply. Incoming messages, including customer retries, project their original text at line 826 instead. Translate to English and the agent translation tool use the fixed TranslationWorkflow.save. Both independent reviewers reached the same conclusion. This is a source-level conclusion for this head, not an inspection of historical deployed workflow versions.
Checks run at this head:
pnpm exec vp check: 329 files formatted, no lint warnings or type errors in 196 files.pnpm --filter @respondkit/api test: 160 tests passed, including the new language regression and adjacent approval/reply coverage.pnpm --filter @respondkit/discord test: 54 tests passed.- Extended signed local Worker walkthrough: language preservation, translation reply/private link, cache reuse, wrong target, unauthorized operator, provider failure with original unchanged, retry recovery, already-English response, disabled inbox, and no interaction token in the 38 D1 application tables all passed.
Consider, unchanged from round 1: very slow provider retries can outlast the publication wait and require another menu invocation. No new blocker. The accepted NULL-only update also preserves an earlier detected language until an operator names another; the docs disclose that tradeoff. No migration or inbox-enablement change.
Live Discord rendering remains unverified because deployment and command registration are forbidden here. The unchanged agent API walkthrough and registration dry-run evidence carry forward from round 1. The API suite still emits workerd canceled-request/RPC-disposal diagnostics, but exits zero with all assertions passing. Product code and both review worktrees remain unchanged.
Evidence: runtime reproduction, walkthrough log, reproduction script, legacy reachability, format/lint/types, API tests, Discord tests.
Why
RK-7: Simon needs the Translate to English right-click command back to read customer messages that were not translated. PR #57 removed it along with
/translateand Reject draft.What changed
packages/discord: Translate to English (a message command) is registered again and parsed into atranslateinteraction withtargetLanguage: "en"and the target message ID./translateand Reject draft are still rejected as unsupported.apps/api:handleTranslationInteractionhandlestranslateagain. It maps the selected Discord message to the original customer message in the same authorized support thread, requests the cached English translation, publishes it with the existingTranslationWorkflowpublish mode as a reply to the original, and privately answers with a link (or says why there is none: failure, already English, still running).TranslationWorkflow.savenow fillsthread.customer_languageonly while it is unknown. Before this, Translate to English on a newer customer message replaced a language the operator had named (/reply translate:es, then a Hindi message, gavehi). Review RK-8.docs/architecture/optional-translation.mdand the web Discord inbox page describe the command again; the web page no longer lists/translate.Scope
Only the right-click command.
/translatestays removed, because the card names only the right-click command. The untranslated Captioner thread and the support agent outage are separate cards.Tradeoffs
translation_selectionfreezing is not restored. It protected target-less/translatefrom drifting to a newer message on redelivery. A message command always carriestarget_id, so the lookup is deterministic.storeCustomerTranslation(the older automatic translation path in MessageWorkflow) still has the timestamp-only rule. I didn't touch it because this PR doesn't change that path./translate.Blast radius
The Discord interaction route gains one branch that runs before the reply and approve paths. No migration and no schema change. The guild command set gains one entry. After merge,
pnpm deploy:stagingand thenpnpm discord:commands:applyare needed before the command shows up in Discord. Registration bulk-overwrites the guild commands with the list inDISCORD_GUILD_COMMANDS.Verification
pnpm check: formatting, lint and types pass (329 files / 196 files).packages/discord: 54/54 tests.apps/api: 160/160 tests, including the operator-language regression (it failed withhibefore the fix), including a new test where Translate to English posts the reply to the selected message and links it privately, and restored target-resolution tests (wrong message, other thread).wrangler dev --local(the real Worker, D1 migrations, MessageWorkflow and TranslationWorkflow), with signed Discord interactions and loopback Discord/Gemini fixtures:**English translation · Hindi → English**as a reply to the original (message_reference= original). The private answer isTranslation: https://discord.com/channels/…/600000000000000001.translation_jobstatussucceededwith targeten, onetranslation_postrow, and the original message is unchanged (processing_statussucceeded).esbefore and after; the failure, retry, disabled-inbox and token-not-persisted cases pass.pnpm discord:commands:apply --dry-run(example topology): the guild command PUT includesTranslate to English.🤖 Generated with Claude Code