log DirectProject 审批拒绝原因 #566

Merged
k88936 merged 9 commits from fix/log-cmd-refuse-reason into master 2026-10-01 17:01:22 +08:00
Member

宿主拒绝 app-server 的审批 / 交互请求时,原因落日志。

宿主拒绝 app-server 的审批 / 交互请求时,原因落日志。
k88936 added 1 commit 2026-10-01 15:29:13 +08:00
DirectProject 审批拒绝原因留痕
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m43s
Project CI / Backend tests (pull_request) Failing after 17s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m13s
Project CI / Frontend tests (pull_request) Successful in 2m12s
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m57s
Project CI / Native shell tests (pull_request) Successful in 5m54s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m41s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 10m49s
ea33e8c166
宿主拒绝 app-server 的审批 / 交互请求时,原因必须落 AppData `RUST` 日志。稳定键 `agent.direct_codex.approval.denied`,字段为 `thread_id` / `method` / `reason` / `distinct_reasons`;未绑定宿主执行器时 `reason=no-bound-host-execution-adapter`,回包写入失败另记 `agent.direct_codex.approval.response_write_failed`。
Author
Member
  • 1. 未绑定执行器路径与回包失败的留痕没有去重与限流(performance · medium)

    • 现状:denied_response(execution.rs,未绑定宿主执行器时的交互兜底)与 response_write_failed 每次触发都无条件 app_log! 写一行。
    • 问题:属实。denied_response 恰好就是「模型被拒后反复重试同一动作」的路径,而 app_log! 走的是同一份 AppData 日志,逐次写会把其它诊断刷掉,也和适配器路径已有的有界去重设计不一致。
    • 修法:未绑定路径改为进程级 (method, outcome) 有界去重(NO_ADAPTER_INTERACTION_LOGGED);适配器拒绝与回包失败抽出共用的 log_once。上限统一 MAX_DENIED_REASON_LOGS = 64,并新增 distinct_outcomes 保留「一共出现过几种」。
    • 提交:e3041d84e
  • 2. 透传的宿主错误文本可能把绝对路径写进日志(security · low)

    • 现状:reason 直接取自 format!("lease-admit-failed: {error}"),而 error 可能来自 validate_patch_paths → 私有路径校验,形如 ...:{绝对路径}。
    • 问题:基本属实。落盘那一路有 sanitize_diagnostic_message 兜底,但 app_log! 还会把未清洗的文本原样 eprintln!,去重键里也存原文,而模块文档写的是「绝不带路径」——契约没有在源头兑现。
    • 修法:在留痕唯一出口 log_denied_reason 调 crate::sanitize_diagnostic_message,脱敏后再做去重键与输出。
    • 提交:493457c3e(含 denied_reason_logging_scrubs_embedded_paths 用例)
  • 3. 持 state 互斥锁期间做同步文件 I/O(performance · low)

    • 现状:respond 解析 target 的临界区里,多处 return self.denied(...) 是在 guard 仍存活时调用的;log_denied_reason → app_log! → append_application_log_line 会同步 open/write/flush,必要时还做日志轮转,且走全局诊断锁。
    • 问题:属实。影响确实小(只发生在拒绝分支、函数随即返回、guard 立刻释放),但「在锁里做 I/O」这个形态本身不对。
    • 修法(已做):target 解析抽成 ExecutionAdapter::resolve_approval_gate,锁内只做状态判定与 ticket 登记;新增 ApprovalGateOutcome::{Proceed, Cached, Deny},拒绝原因带出临界区,state 释放后才走 denied 留痕。state.tickets.insert 与 MAX_REQUEST_CACHE 清理仍在锁内。15 条拒绝原因分类逐条等价迁移(已用集合比对确认零增零漏),返回形状不变。
    • 提交:fbfc0b62b。验证:native_approval_is_bound_to_item_and_reuses_one_pass_without_duplicate_leases、same_argument_mcp_cohort_keeps_all_permits_until_every_member_finishes、patch_targets_and_moves_are_checked_and_owned_mcp_does_not_double_charge、unknown_development_fingerprint_preserves_execution_but_invalidates_verification 等 17 条用例全过。
    • 同源附带项(已做):TurnBindMismatch 原先共用「执行许可回包丢失,不能确认原操作是否已执行。」,现在按 cause 分开——只有写失败说回包丢失,绑定失败说明「回合绑定与当前 app-server 不一致,无法确认本轮操作的作用域」。提交:2220682e5,含 response_delivery_failure_texts_match_their_cause 用例。
  • 4. response_write_failed 被回合绑定失败复用,日志名不达意(maintainability · low)

    • 现状:mod.rs:4887(stdin 写入 / flush 失败)与 mod.rs:4919(bind_turn 返回 false)调用同一个函数、共用一个日志键。
    • 问题:属实。绑定失败时回包其实已经产出,日志却说「写入失败」。
    • 修法:更名 response_delivery_failed(cause),新增 ResponseDeliveryFailure::{Write, TurnBindMismatch},两个调用点各传真实原因;日志键改为 agent.direct_codex.approval.response_delivery_failed 并带 cause 字段。
    • 提交:ce4ada8bb
  • 5. denied_response 把非审批方法也记成 approval.denied(maintainability · low)

    • 现状:denied_response 是所有交互方法的兜底,但日志无条件写 approval.denied。
    • 问题:属实。item/permissions/requestApproval 回的是空授权、未知方法回 -32601 error,两者都不是 decline,日志会误导排查方向。
    • 修法:日志键改为 agent.direct_codex.interaction.no_adapter,新增 outcome 字段区分 decline / empty-permissions / unsupported-method。
    • 提交:c34e45e0c
- [x] 1. 未绑定执行器路径与回包失败的留痕没有去重与限流(performance · medium) - 现状:`denied_response`(execution.rs,未绑定宿主执行器时的交互兜底)与 `response_write_failed` 每次触发都无条件 `app_log!` 写一行。 - 问题:属实。`denied_response` 恰好就是「模型被拒后反复重试同一动作」的路径,而 `app_log!` 走的是同一份 AppData 日志,逐次写会把其它诊断刷掉,也和适配器路径已有的有界去重设计不一致。 - 修法:未绑定路径改为进程级 `(method, outcome)` 有界去重(`NO_ADAPTER_INTERACTION_LOGGED`);适配器拒绝与回包失败抽出共用的 `log_once`。上限统一 `MAX_DENIED_REASON_LOGS = 64`,并新增 `distinct_outcomes` 保留「一共出现过几种」。 - 提交:e3041d84e - [x] 2. 透传的宿主错误文本可能把绝对路径写进日志(security · low) - 现状:`reason` 直接取自 `format!("lease-admit-failed: {error}")`,而 `error` 可能来自 `validate_patch_paths` → 私有路径校验,形如 `...:{绝对路径}`。 - 问题:基本属实。落盘那一路有 `sanitize_diagnostic_message` 兜底,但 `app_log!` 还会把未清洗的文本原样 `eprintln!`,去重键里也存原文,而模块文档写的是「绝不带路径」——契约没有在源头兑现。 - 修法:在留痕唯一出口 `log_denied_reason` 调 `crate::sanitize_diagnostic_message`,脱敏后再做去重键与输出。 - 提交:493457c3e(含 `denied_reason_logging_scrubs_embedded_paths` 用例) - [x] 3. 持 `state` 互斥锁期间做同步文件 I/O(performance · low) - 现状:`respond` 解析 target 的临界区里,多处 `return self.denied(...)` 是在 guard 仍存活时调用的;`log_denied_reason` → `app_log!` → `append_application_log_line` 会同步 open/write/flush,必要时还做日志轮转,且走全局诊断锁。 - 问题:属实。影响确实小(只发生在拒绝分支、函数随即返回、guard 立刻释放),但「在锁里做 I/O」这个形态本身不对。 - 修法(已做):target 解析抽成 `ExecutionAdapter::resolve_approval_gate`,锁内只做状态判定与 ticket 登记;新增 `ApprovalGateOutcome::{Proceed, Cached, Deny}`,拒绝原因带出临界区,`state` 释放后才走 `denied` 留痕。`state.tickets.insert` 与 `MAX_REQUEST_CACHE` 清理仍在锁内。15 条拒绝原因分类逐条等价迁移(已用集合比对确认零增零漏),返回形状不变。 - 提交:fbfc0b62b。验证:`native_approval_is_bound_to_item_and_reuses_one_pass_without_duplicate_leases`、`same_argument_mcp_cohort_keeps_all_permits_until_every_member_finishes`、`patch_targets_and_moves_are_checked_and_owned_mcp_does_not_double_charge`、`unknown_development_fingerprint_preserves_execution_but_invalidates_verification` 等 17 条用例全过。 - 同源附带项(已做):`TurnBindMismatch` 原先共用「执行许可回包丢失,不能确认原操作是否已执行。」,现在按 `cause` 分开——只有写失败说回包丢失,绑定失败说明「回合绑定与当前 app-server 不一致,无法确认本轮操作的作用域」。提交:2220682e5,含 `response_delivery_failure_texts_match_their_cause` 用例。 - [x] 4. `response_write_failed` 被回合绑定失败复用,日志名不达意(maintainability · low) - 现状:`mod.rs:4887`(stdin 写入 / flush 失败)与 `mod.rs:4919`(`bind_turn` 返回 false)调用同一个函数、共用一个日志键。 - 问题:属实。绑定失败时回包其实已经产出,日志却说「写入失败」。 - 修法:更名 `response_delivery_failed(cause)`,新增 `ResponseDeliveryFailure::{Write, TurnBindMismatch}`,两个调用点各传真实原因;日志键改为 `agent.direct_codex.approval.response_delivery_failed` 并带 `cause` 字段。 - 提交:ce4ada8bb - [x] 5. `denied_response` 把非审批方法也记成 `approval.denied`(maintainability · low) - 现状:`denied_response` 是所有交互方法的兜底,但日志无条件写 `approval.denied`。 - 问题:属实。`item/permissions/requestApproval` 回的是空授权、未知方法回 `-32601` error,两者都不是 `decline`,日志会误导排查方向。 - 修法:日志键改为 `agent.direct_codex.interaction.no_adapter`,新增 `outcome` 字段区分 `decline` / `empty-permissions` / `unsupported-method`。 - 提交:c34e45e0c
k88936 added 7 commits 2026-10-01 16:11:11 +08:00
- 未绑定宿主执行器的交互兜底不再一律记成审批拒绝
- 新增稳定键 agent.direct_codex.interaction.no_adapter,字段为 method / outcome / request_id
- outcome 区分 decline / empty-permissions / unsupported-method,权限申请与未知方法不再被误标为 decline
- 同步更新 shared-memory 决策记录里的日志键口径
- 未绑定宿主执行器的交互兜底按进程级 (method, outcome) 只写第一行,新增 distinct_outcomes
- 适配器拒绝留痕抽出共用的 log_once,回包未送达同样按回合内原因只记一次
- 上限沿用 MAX_DENIED_REASON_LOGS = 64,模型重试不再逐次刷日志
- 同步更新 shared-memory 决策记录里的去重口径
- response_write_failed 改名为 response_delivery_failed,新增 ResponseDeliveryFailure 枚举
- 两个调用点分别传 Write(stdin 写入 / flush 失败)与 TurnBindMismatch(bind_turn 返回 false)
- 日志键改为 agent.direct_codex.approval.response_delivery_failed 并带 cause 字段,回合绑定失败不再被误报成写入失败
- 同步更新 shared-memory 决策记录里的日志键口径
- log_denied_reason 在唯一出口调用 sanitize_diagnostic_message,透传的宿主错误文本不再把绝对路径写进日志
- 去重键改用脱敏后的文本,内存与 stderr 拷贝同样不带原文
- 新增 denied_reason_logging_scrubs_embedded_paths 用例固化该约定
- 同步更新 shared-memory 决策记录里的脱敏口径
- respond 的 target 解析抽成 resolve_approval_gate,锁内只做状态判定与 ticket 登记
- 新增 ApprovalGateOutcome(Proceed / Cached / Deny),拒绝原因带出临界区,state 释放后才调用 denied 写日志
- 拒绝留痕不再持有 state 做同步文件 I/O(app_log 写入与日志轮转),不再拉长审批临界区
- 15 条拒绝原因分类逐条等价迁移,行为与返回形状不变
- ResponseDeliveryFailure 新增 interrupt_reason,按 cause 给回合终态说明
- 只有 response-write-failed 保留「执行许可回包丢失」;turn-bind-mismatch 改为说明回合绑定不一致、作用域无法确认
- 新增 response_delivery_failure_texts_match_their_cause 用例钉住两句文案
- 同步更新 shared-memory 决策记录里的收束说明口径
Merge remote-tracking branch 'origin/master' into fix/log-cmd-refuse-reason
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m14s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m47s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
ba79cbcece
k88936 added 1 commit 2026-10-01 16:22:14 +08:00
Merge remote-tracking branch 'origin/master' into fix/log-cmd-refuse-reason
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m5s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m20s
Project CI / Backend tests (pull_request) Successful in 4m42s
Project CI / Native shell tests (pull_request) Successful in 5m49s
Project CI / Frontend tests (pull_request) Successful in 2m1s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m47s
Project CI / Repository checks (pull_request) Successful in 2m26s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 12m7s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 11m44s
387dba37f3
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
k88936 merged commit b51bf03854 into master 2026-10-01 17:01:22 +08:00
k88936 deleted branch fix/log-cmd-refuse-reason 2026-10-01 17:01:22 +08:00
Sign in to join this conversation.