外观
07 · 人机竞速与超时仲裁
Who decides what
枪声一响,三方赛跑:人(点官方面板按钮)、宿主倒计时(唯一权威作者)、LLM 结论(scope 内可抢占)。raceHumanDecision(decision.ts:L100-141)是仲裁器:
- 启动
raceHumanDecision(() => next(), {seconds, action}, handle)把「求人」委托给官方 ApprovalPanel(next()),同时宿主开一个setTimeout(seconds*1000)。 - 超时先到宿主 tap 时钟:记录规范文案
[dsh-auto-approval-llm] no response: auto-approved / auto-rejected (Ns)(文案永远宿主生成),按 timeoutAction 结案allowed-once | rejected,timedOut=true。关闭的标签页/headless 永远挂不死审批。 - 人先答面板 outcome 结案,
timedOut=false。 - LLM 抢先(仅 MEDIUM+scope 内)
handle.claim('allowed-once'|'rejected'):清定时器、claimed=true、立即结案 —— 这就是「LLM 接管」的唯一可信信号。
7.1 诚实来源标注 approvalSource decision.ts:L158-175 —— 谁决定,就标谁
| 条件 | source 标签 |
|---|---|
| 宿主倒计时到期 | timeout-allow / timeout-deny |
| LLM 接管(claim) | llm-allow / llm-deny |
| 客户端自动应答(autoAnswered 标记) | auto-allow / auto-deny |
| 真实人工点击 | human-allow / human-deny |
| 请求被取消/会话销毁 | abort → 永远 action=reject(不假装有人决定过) |
| 声明规则(G1)/静态名单(G2) | rule-deny / rule-allow、denyList-deny / allowlist-allow |
| 策略层无条件硬拒(运行态文件等) | policy-deny → 立即 rejected,无倒计时、不计熔断、不发布 review-status |
防张冠李戴
仅仅存在一份 advisory 评审结论(表明 LLM 看过)绝不允许把这次决议标成 llm-*。只有 claimed(宿主侧竞速被抢占)才算 LLM 接管。同理,advisory 拒绝 不会计入熔断 —— 熔断只认「LLM 真正拍板的拒绝」。
7.2 反重标的四重防线(防止客户端/迟到 ACK 篡改决议性质)
| 机制 | 作用 |
|---|---|
resolvedCallIds(TTL 30s) | 宿主已结案的 callId;迟到的 FEEDBACK ACK 无法再把它 relabel 成「no response: auto-*」 |
FEEDBACK 双重门 | 只有当 !decisionFeedback.has && !resolvedCallIds.has 才写 timeoutFeedback;客户端只能传 outcome∈{allowed-once,rejected},其余强制 rejected |
outcome 白名单 | 超时标记唯一作者是宿主计时器,客户端只上报 outcome |
answer-once Set | 客户端 answeredApprovals 保证同一审批只答一次、不双写 |
7.3 follow 阶段与 TTL 家族
ReviewStatus 状态机:countdown(倒计时中)→ follow(已结案,等客户端收敛面板)。follow 保留 FOLLOW_STATE_TTL_MS = 120_000(覆盖 Chrome 后台标签节流 ≥1min),客户端 followSweep 每秒清扫。
| 定时器 | 值 |
|---|---|
FOLLOW_STATE_TTL_MS | 120 s |
RESOLVED_TTL_MS | 30 s |
AUTO_ANSWERED_TTL_MS | 60 s |
feedback 表 TTL / 上限 | 60 s / 256 条 |
followSweep 周期 | 1 s |
客户端 poll 频率 | 500 ms |