feat: cloud 门禁/超时/成本/沙箱收敛为单一判定源

- 新增标准库叶子层 timeouts/gates/quota/sandbox_policy:
  审批 3600 与单轮 600 各有名常量与环境覆盖层,已建项
  expires_at 永不重算;目录/控制/合并状态门禁统一判定,
  目录违规无任何覆盖参数;配额取数与比较唯一来源
  (USD 先于无价、命中均为 >=、无价只计 actual 行);
  /tmp 隔离矩阵与失联清理判定纯函数化。
- 调用点委托:server/worker/automations/auto_memory 的
  配额 SQL 与比较、server/tree/marker 的目录解析、
  marker 的控制门、sandbox 的任务容器名正则、两处 300 秒
  陈旧阈值默认值均收敛,别名与错误文案不变。
- 统一两个 P0 口径分歧:服务端无价计数由全部 usd IS NULL
  行改为 actual-only;显式不限额用户在服务端准入亦不受
  无价上限约束(与 worker 运行时一致)。
- 不新增门禁种类与 record 模式(主线无此实现);不改
  skill_seed/skills/SKILL.md;不改 v1.1 契约与分支保护。
- 新增 test_arch_converge.py 18 项。
This commit is contained in:
host committed 2026-11-03 02:49:25 +00:00
1 parent 2720acef46
commit 41e8c98512
16 files changed
+1068 -301

No files matched your search

+125
View File
@@ -0,0 +1,125 @@
"""门禁层:调用方只传事实,本层只出决定。
收敛对象是主线上已经存在的硬门禁,不是新发明的门禁
种类。主线(origin/main)上不存在 merge_gate 的
record/enforce 模式、分支保护门禁与全自动门禁——那些只
在未合并的分支里;本轨不从分支搬功能、不新增门禁种类、
不把硬门禁降级为“只记录不阻断”(那会削弱安全语义,
不属于收敛)。本层收敛的是三类已有判定:
1. 目录门(resolve_inside):worktree 内相对路径的安全
解析。服务端 _safe_worktree_path、cloud/tree.py 的
resolve_tree_path、cloud/marker_tools.py 的
safe_worktree_path(worker 经 marker_tools 调用)此前
各写了一份 realpath + commonpath,仅在 commonpath
的 ValueError 处理上不一致(服务端不捕,另两处捕)。
现统一为本函数:绝对路径、.. 逃逸、符号链接逃逸、
跨盘符 ValueError 一律拒绝;空串返回原 worktree。
2. 控制门(decide_control):marker_tools.check_control_gate
的纯函数判定(cancel / retry 的目标状态与派生关系),
现为本层 canonical 实现,marker_tools 只保留同名薄壳
(调用点与测试 import 的名字不变)。
3. 合并状态门(merge_state_decision):服务端 _merge_task
的状态判定——只有 completed 可标记 merged,其它状态
一律拒绝(含已 merged 的重复调用)。权限判定
(approve_merge)仍在服务端,不进本层:本层只看事实
(任务状态),不查权限。
不可覆盖规则(冻结):
- 目录门的拒绝没有任何 confirm / override / bypass 参数。
本模块的函数签名里 deliberately 没有这类参数:显式
确认即使存在,也只能覆盖“保护类风险”,绝不能覆盖
目录违规——而主线上目录违规本来就没有确认通道,
任何调用方都不得以“用户已确认”为由绕过本层。
测试钉住:resolve_inside 与 decide_control 的签名中
不含 confirm / override / bypass 字样。
- 合并状态门同样没有覆盖参数:非 completed 任务不存在
“确认后合并”的路径,重复 merged 调用仍是拒绝。
返回形态沿用 codebase 的元组风格:(allowed, reason),
reason 为空表示放行;目录门另有 resolve_inside 直接
返回路径或 None(旧调用点的形态不变)。
"""
import os
TERMINAL_STATUSES = ("completed", "failed", "interrupted", "merged",
"merge-failed")
RETRYABLE_STATUSES = ("failed", "interrupted", "merge-failed")
def resolve_inside(base, rel):
"""worktree 内相对路径的安全解析:越界回 None。
空串返回 base 本身(旧三处行为一致);绝对路径、
.. 逃逸、符号链接逃逸、commonpath 跨盘符 ValueError
一律 None。目录内指回内部的符号链接放行到真实位置
(调用方仍只拿到 worktree 内的内容)。
"""
if not rel:
return base
if os.path.isabs(rel):
return None
real_base = os.path.realpath(base)
target = os.path.realpath(os.path.join(real_base, rel))
try:
if os.path.commonpath([real_base, target]) != real_base:
return None
except ValueError:
return None
return target
def is_goal_task(task):
"""goal 任务判定:verify_command 非空或 goal_mode 为真。
与 worker run_one 的 goal 分支同款;goal 字段本身是
goal_mode=1 时 prompt 的副本,不单独当判定条件。
"""
if not task:
return False
return bool(task.get("verify_command") or task.get("goal_mode"))
def decide_control(target, sender, relation, kind):
"""task-cancel / task-retry 的纯函数门禁,返回 (ok, reason)。
kind 只能是 "cancel" 或 "retry",其它值一律拒绝。
权限层不在这里:worker 侧另查 create_task /
approve_merge;本函数只管目标状态与关系,单测不需
AuthStore。goal 任务不可取消或重试(goal 循环自有轮次)。
"""
if kind not in ("cancel", "retry"):
return False, "未知的控制操作"
if target is None:
return False, "目标任务不存在"
if (target.get("project_id") is not None
and sender.get("project_id") is not None
and target.get("project_id") != sender.get("project_id")):
return False, "跨项目任务不可操作"
if target.get("id") == sender.get("id"):
return False, "不能操作自己"
if is_goal_task(target):
return False, "goal 任务不可取消或重试(goal 循环自有轮次)"
status = target.get("status")
if kind == "cancel":
if status in TERMINAL_STATUSES:
return False, "目标已终态(%s),不可取消" % status
else:
if status not in RETRYABLE_STATUSES:
return False, ("仅 failed / interrupted / merge-failed"
" 可重试,当前状态为 %s" % status)
if relation not in ("descendant", "same_creator"):
return False, "目标不在发送方派生链上且创建人不同"
return True, ""
def merge_state_decision(status):
"""合并标记的状态判定:仅 completed 放行,返回 (ok, reason)。
含已 merged 的重复调用在内,其它状态一律拒绝;文案与
服务端旧 409 错误一致。权限判定不在本层。
"""
if status == "completed":
return True, ""
return False, "only completed tasks can be marked merged"