背景
真实运行日志表明,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 假设漂移”的问题。
范围
本轮直接覆盖以下内容:
-
修正 Claude translator 对 request_user_input 的响应翻译语义
- 不能再把 answer-only 响应默认为 deny
- 允许条件必须与该 request semantic 的真实成功条件对齐
-
修正相关 resolved metadata / follow-up 行为所依赖的 request decision 记录方式
- 避免把无
decision 的成功回答记录成“像是拒绝”或空含义混淆
- 保持
plan_confirmation、can_use_tool 这类 approval-like request 的既有语义不被破坏
-
回归测试对齐真实线上契约
AskUserQuestion 的 translator test 不再使用与线上不一致的伪造 decision: accept
- 增加 answer-only 成功路径回归覆盖
- 如有必要,补充同类 semantic 边界测试,证明
request_user_input 与 approval 类请求已按不同语义处理
-
做一轮定向审计
- 明确哪些 Claude request semantic 已确认一致
- 哪些只是高风险未证实
- 对本轮顺手能补上的明显契约漂移一并补齐测试或实现
非目标
本轮不处理以下内容:
- 更大范围的 Claude backend 产品能力扩展
- 与本 bug 无关的 Feishu UI 表现调整
mcp_server_elicitation 或 tool_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 --check 与 go-file-length gate 通过
- durable sync:issue body 已同步根因、审计结论、验证结果与 close-out 状态;本轮无需额外同步
docs/general 或 AGENTS.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.sh、issuectl.sh close-plan --issue 680、issuectl.sh finish --issue 680 --close
- 当前 blocker:无
- 最近变更假设:当前修复仍限定在已证实的 request semantic 边界内,没有扩大到无证据的广泛重构
- 最后一致状态:本地修改仅涉及 Claude translator 与其测试,独立 verifier 已 pass
- 未完成尾项:commit/push、finish/close
恢复步骤
- 重新读取本 issue body,确认当前已进入 close-out tail only 状态
- 重新检查
git status --short,确认本地只包含本 issue 修改
- 先提交本地改动,再执行
./safe-push.sh
- 推送完成后重跑
issuectl.sh close-plan --issue 680
- close-plan 通过后执行
issuectl.sh finish --issue 680 --close
背景
真实运行日志表明,Claude backend 当前在内部 helper 交互里,对
AskUserQuestion/request_user_input的响应语义存在稳定错译。具体表现为:
AskUserQuestion后,飞书端已经完成选择并提交。request.respond是 answer-only 成功响应,只包含answers,不包含decision。decision == accept|acceptForSession。behavior=deny,默认错误消息是Request declined by user。这不是单次偶发,已经在多次真实日志中复现:
request_id=50403643-a0b2-4da2-ad6a-233e472f9405request_id=c2ac6a29-57dd-43ad-a141-d04a78016e5crequest_id=e2b6a898-46c5-4f5e-8e11-1e3805ba0c072026-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 假设漂移”的问题。范围
本轮直接覆盖以下内容:
修正 Claude translator 对
request_user_input的响应翻译语义修正相关 resolved metadata / follow-up 行为所依赖的 request decision 记录方式
decision的成功回答记录成“像是拒绝”或空含义混淆plan_confirmation、can_use_tool这类 approval-like request 的既有语义不被破坏回归测试对齐真实线上契约
AskUserQuestion的 translator test 不再使用与线上不一致的伪造decision: acceptrequest_user_input与 approval 类请求已按不同语义处理做一轮定向审计
非目标
本轮不处理以下内容:
mcp_server_elicitation或tool_callback的新功能设计完成标准
AskUserQuestion/request_user_input在 answer-only 成功响应下,会被 Claude translator 正确翻成 allow,而不是 denyplan_confirmation/can_use_tool的 approval 语义保持正确request_user_inputanswer-only successplan_confirmationrevise / accept 不回归can_use_toolapproval 不回归相关文档
docs/general/issue-orchestration-workflow.mdAGENTS.md.codex/skills/relay-stack-playbook/SKILL.md.codex/plugins/cache/openai-curated/superpowers/265aae08/skills/systematic-debugging/SKILL.md涉及文件
internal/adapter/claude/commands.gointernal/adapter/claude/translator.gointernal/adapter/claude/request_resolution.gointernal/adapter/claude/observe.gointernal/adapter/claude/translator_test.gointernal/adapter/claude/translator_request_response_test.go建议范围
未拆分直做。
原因:当前已确认的根因、影响路径、主要逻辑载体和验证面都集中在 Claude request translation 这一条链路,且核心修复目标明确,不需要先拆成母/子 issue 才能形成稳定执行闭包。
如果实现过程中发现需要对更多 request semantic 做独立产品或协议决策,再回到本 issue 记录并决定是否正式拆分。
实现参考
internal/adapter/claude/commands.gobuildRequestResponsePayload(...)当前把 allow/deny 统一建立在decision上,是本轮最主要的设计边界点。internal/adapter/claude/translator.goresolveRequestDecision(...)当前只认显式decision,是 request result 语义落盘的关键辅助点。internal/adapter/claude/observe.goobserveAskUserQuestionRequest(...)、observePlanConfirmationRequest(...)、observeToolApprovalRequest(...)定义了各类 request semantic 的进入点。internal/adapter/claude/request_resolution.gorequestResultMetadata(...)会把request.Decision透出到 resolved metadata,需要确认修复后语义仍然自洽。internal/core/orchestrator/service_request.gobuildRequestUserInputResponse(...)已证明真实request_user_input成功载荷是 answer-only,是本轮契约 source of truth。检查参考
internal/adapter/claude/translator_test.gointernal/adapter/claude/translator_request_response_test.gointernal/adapter/claude/translator_plan_revise_test.gointernal/adapter/claude/translator_plan_permissions_test.gointernal/adapter/claude/translator_tool_permissions_test.go收尾参考
issuectl.sh finish --issue 680执行决策
#680本身审计结论
已证实
AskUserQuestion/request_user_input的真实成功响应是 answer-only,不带decisionbehavior=deny本轮已排除 / 已验证一致
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 完成响应,因此天然属于同一语义面;本轮修复已覆盖该类完成态的统一翻译边界独立 verifier 结果
独立 verifier 结果:pass
request_user_inputanswer-only success、approval 无效响应显式报错、plan_confirmation/can_use_tool回归路径均已覆盖或复核git diff --check与go-file-lengthgate 通过docs/general或AGENTS.md当前执行点
request_user_input现在按 answer-driven success 判定 allowdecision,现在显式报 translator error,不再伪装成“用户拒绝”request_user_input成功回答会把 stored decision 记录成accept,保持 resolved metadata 语义自洽go test ./internal/adapter/claudego test ./internal/core/orchestrator -run 'Request|MCP|ToolCallback'go test ./internal/adapter/claude ./internal/core/orchestratorbash scripts/check/go-file-length.shgit diff --check./safe-push.sh、issuectl.sh close-plan --issue 680、issuectl.sh finish --issue 680 --close恢复步骤
git status --short,确认本地只包含本 issue 修改./safe-push.shissuectl.sh close-plan --issue 680issuectl.sh finish --issue 680 --close