Skip to content

bug: Claude AskUserQuestion answer-only responses are mistranslated as user decline #680

Description

@kxn

背景

真实运行日志表明,Claude backend 当前在内部 helper 交互里,对 AskUserQuestion / request_user_input 的响应语义存在稳定错译。

具体表现为:

  • Claude 发出 AskUserQuestion 后,飞书端已经完成选择并提交。
  • orchestrator 发出的 request.respond 是 answer-only 成功响应,只包含 answers,不包含 decision
  • Claude translator 却把“是否允许继续”统一收口为 decision == accept|acceptForSession
  • 因此 answer-only 的合法成功响应被稳定翻译成 behavior=deny,默认错误消息是 Request declined by user
  • Claude 随后会按“用户拒绝回答”这一错误前提继续推理,造成执行方向偏离用户真实选择。

这不是单次偶发,已经在多次真实日志中复现:

  • 2026-05-03 request_id=50403643-a0b2-4da2-ad6a-233e472f9405
  • 2026-06-01 request_id=c2ac6a29-57dd-43ad-a141-d04a78016e5c
  • 2026-06-04 request_id=e2b6a898-46c5-4f5e-8e11-1e3805ba0c07

2026-06-04 这次具体表现为:用户选的是 Rich + 纯终端 (TUI)单个可执行文件,但 Claude 最终收到的是 Request declined by user,随后按“用户拒绝回答”继续推理并偏向 CustomTkinter

目标

修正 Claude request translation 对 AskUserQuestion / request_user_input 的成功判定,使其与真实 relay payload 契约一致;同时对 Claude request handling 做一轮同类语义审计,避免其他 request semantic 再出现“线上 payload 契约 vs translator/test 假设漂移”的问题。

范围

本轮直接覆盖以下内容:

  1. 修正 Claude translator 对 request_user_input 的响应翻译语义

    • 不能再把 answer-only 响应默认为 deny
    • 允许条件必须与该 request semantic 的真实成功条件对齐
  2. 修正相关 resolved metadata / follow-up 行为所依赖的 request decision 记录方式

    • 避免把无 decision 的成功回答记录成“像是拒绝”或空含义混淆
    • 保持 plan_confirmationcan_use_tool 这类 approval-like request 的既有语义不被破坏
  3. 回归测试对齐真实线上契约

    • AskUserQuestion 的 translator test 不再使用与线上不一致的伪造 decision: accept
    • 增加 answer-only 成功路径回归覆盖
    • 如有必要,补充同类 semantic 边界测试,证明 request_user_input 与 approval 类请求已按不同语义处理
  4. 做一轮定向审计

    • 明确哪些 Claude request semantic 已确认一致
    • 哪些只是高风险未证实
    • 对本轮顺手能补上的明显契约漂移一并补齐测试或实现

非目标

本轮不处理以下内容:

  • 更大范围的 Claude backend 产品能力扩展
  • 与本 bug 无关的 Feishu UI 表现调整
  • mcp_server_elicitationtool_callback 的新功能设计
  • 未经证据支持的广泛重构;如果没有同类契约错配证据,不人为扩大修复面

完成标准

  • AskUserQuestion / request_user_input 在 answer-only 成功响应下,会被 Claude translator 正确翻成 allow,而不是 deny
  • 真实用户回答不再被 Claude 误理解成“用户拒绝回答”
  • plan_confirmation / can_use_tool 的 approval 语义保持正确
  • 回归测试覆盖至少以下路径:
    • request_user_input answer-only success
    • plan_confirmation revise / accept 不回归
    • can_use_tool approval 不回归
  • issue 中记录本轮审计结论:已证实、已排除或仍待后续验证的点分别是什么

相关文档

  • docs/general/issue-orchestration-workflow.md
  • AGENTS.md
  • .codex/skills/relay-stack-playbook/SKILL.md
  • .codex/plugins/cache/openai-curated/superpowers/265aae08/skills/systematic-debugging/SKILL.md

涉及文件

  • internal/adapter/claude/commands.go
  • internal/adapter/claude/translator.go
  • internal/adapter/claude/request_resolution.go
  • internal/adapter/claude/observe.go
  • internal/adapter/claude/translator_test.go
  • internal/adapter/claude/translator_request_response_test.go
  • 视审计结果可能涉及的 Claude translator 相关测试文件

建议范围

未拆分直做。

原因:当前已确认的根因、影响路径、主要逻辑载体和验证面都集中在 Claude request translation 这一条链路,且核心修复目标明确,不需要先拆成母/子 issue 才能形成稳定执行闭包。

如果实现过程中发现需要对更多 request semantic 做独立产品或协议决策,再回到本 issue 记录并决定是否正式拆分。

实现参考

  • internal/adapter/claude/commands.go
    • buildRequestResponsePayload(...) 当前把 allow/deny 统一建立在 decision 上,是本轮最主要的设计边界点。
  • internal/adapter/claude/translator.go
    • resolveRequestDecision(...) 当前只认显式 decision,是 request result 语义落盘的关键辅助点。
  • internal/adapter/claude/observe.go
    • observeAskUserQuestionRequest(...)observePlanConfirmationRequest(...)observeToolApprovalRequest(...) 定义了各类 request semantic 的进入点。
  • internal/adapter/claude/request_resolution.go
    • requestResultMetadata(...) 会把 request.Decision 透出到 resolved metadata,需要确认修复后语义仍然自洽。
  • internal/core/orchestrator/service_request.go
    • buildRequestUserInputResponse(...) 已证明真实 request_user_input 成功载荷是 answer-only,是本轮契约 source of truth。

检查参考

  • internal/adapter/claude/translator_test.go
  • internal/adapter/claude/translator_request_response_test.go
  • internal/adapter/claude/translator_plan_revise_test.go
  • internal/adapter/claude/translator_plan_permissions_test.go
  • internal/adapter/claude/translator_tool_permissions_test.go
  • 必要时补查真实运行日志,确认修改后目标语义与线上 payload 一致

收尾参考

  • 回写 issue 当前执行点和审计结论
  • 如逻辑边界变化足够大,补充说明哪些 Claude request semantic 本轮已核实
  • 完成后运行 issuectl.sh finish --issue 680
  • 默认做一轮独立 verifier 再决定是否关闭

执行决策

  • 是否拆分:不拆
  • 不拆原因:本问题已形成稳定单 worker 执行闭包,根因集中,修复链路单一,验证面也以 Claude translator 为主
  • 当前执行单元:#680 本身
  • verifier:预期需要;本轮实现完成后默认做一轮独立验收再收尾

审计结论

已证实

  • AskUserQuestion / request_user_input 的真实成功响应是 answer-only,不带 decision
  • Claude translator 旧实现把这类合法成功响应稳定误翻成 behavior=deny
  • 该问题不是单次偶发,而是 2026-05-03、2026-06-01、2026-06-04 三次真实日志都能复现的整类语义错误

本轮已排除 / 已验证一致

  • ExitPlanMode / plan_confirmation 仍是 decision-driven approval 语义;accept / revise 路径测试未回归
  • can_use_tool / approval_can_use_tool 当前仍是显式 decision-driven approval 语义;主链与 permissionSelection 相关测试未回归

高风险但本轮未证实扩散

  • request_user_input 的 optional skip 最终仍会形成 answer-only 完成响应,因此天然属于同一语义面;本轮修复已覆盖该类完成态的统一翻译边界
  • 当前未发现 Claude adapter 中还有其他已落地 request semantic 会以同样方式误用 answer-only 成功载荷

独立 verifier 结果

独立 verifier 结果:pass

  • 目标对齐:实现直接命中 issue 目标,没有偏离到更大范围的 Claude backend 改造
  • 非目标纪律:未扩展到无证据的 request semantic 重构,也未触碰 Feishu UI 或其他无关模块
  • 验收覆盖:request_user_input answer-only success、approval 无效响应显式报错、plan_confirmation / can_use_tool 回归路径均已覆盖或复核
  • 回归面:Claude adapter 整包测试与 orchestrator request 相关定向测试通过;git diff --checkgo-file-length gate 通过
  • durable sync:issue body 已同步根因、审计结论、验证结果与 close-out 状态;本轮无需额外同步 docs/generalAGENTS.md

当前执行点

  • 当前阶段:verifier 完成,待提交推送与 finish/close
  • 已完成:真实日志复核、根因定位、issue 整形、translator 语义修正、回归测试补齐、独立 verifier
  • 实现结果:
    • request_user_input 现在按 answer-driven success 判定 allow
    • approval 类 request 若缺少有效 decision,现在显式报 translator error,不再伪装成“用户拒绝”
    • request_user_input 成功回答会把 stored decision 记录成 accept,保持 resolved metadata 语义自洽
  • 已验证:
    • go test ./internal/adapter/claude
    • go test ./internal/core/orchestrator -run 'Request|MCP|ToolCallback'
    • go test ./internal/adapter/claude ./internal/core/orchestrator
    • bash scripts/check/go-file-length.sh
    • git diff --check
  • 正在进行:close-out tail work
  • 下一步:commit、./safe-push.shissuectl.sh close-plan --issue 680issuectl.sh finish --issue 680 --close
  • 当前 blocker:无
  • 最近变更假设:当前修复仍限定在已证实的 request semantic 边界内,没有扩大到无证据的广泛重构
  • 最后一致状态:本地修改仅涉及 Claude translator 与其测试,独立 verifier 已 pass
  • 未完成尾项:commit/push、finish/close

恢复步骤

  1. 重新读取本 issue body,确认当前已进入 close-out tail only 状态
  2. 重新检查 git status --short,确认本地只包含本 issue 修改
  3. 先提交本地改动,再执行 ./safe-push.sh
  4. 推送完成后重跑 issuectl.sh close-plan --issue 680
  5. close-plan 通过后执行 issuectl.sh finish --issue 680 --close

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:codexCodex protocol translation and wrapper integrationarea:wrapperWrapper process, proxy handling, and child launch flowbugSomething isn't workingstatus:implementable-nowReady to implement with current issue body and context

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions