后续设计与实现必须遵守:ARCHITECTURE_GUARDRAILS.md。
能力进入系统的顺序必须是:
可表达 -> 可校验 -> 可审计 -> 可隔离 -> 可执行 -> 可并发
不能跳级。边界优先于自动化程度,确定性优先于智能程度。
第一版产品停在 M3 Manual 闭环 MVP。它不依赖模型,也已经能用于真实任务治理。M4 以后必须根据 dogfood 结果决定是否继续。
默认假设:
agent 输出不可信
仓库文本、网页和 MCP 返回值可能包含 prompt injection
adapter 和 MCP server 默认不可信
control CLI 是 trusted computing base
模型不能自行决定授权
telemetry 是 evidence,不是真相
一句话分工:
prompt 负责提出动作
policy 负责判断动作
sandbox 负责限制动作
event log 负责解释动作
worktree 用于隔离变更和降低冲突,不是安全边界。MCP roots 用于提示范围,也不是访问控制。真正的强制边界必须由控制层 policy 与 sandbox 执行。
OMP dogfood 证明,仅限制工具调用不足以约束模型行为。控制层还必须约束授权、审计与完成声明:
结构化 proposal
-> pending_approval
-> scoped lease
-> implement
-> audit_hold
-> deterministic audit
-> human_resume | completed | stopped
协议原则:
- proposal 声明 milestone、objective、allowed paths、schema 变化、依赖变化、required gates 和禁止变化。
- 人类批准结构化 proposal 后,控制层才生成绑定 task、run、资源、动作、TTL 和最大使用次数的 lease。
- scope 扩大必须生成新的 proposal,不能由模型自行推断。
- 审计触发器命中后立即进入
audit_hold。该状态只能运行只读检查和允许模板内的离线 gate。 ASK、STOP与UNVERIFIED是合法终态,不会因为模型被要求“持续完成任务”而自动绕过。- completion interlock 独立检查证据、baseline 回退与未决审批。模型和 reviewer 都不能自行宣布完成。
自动 reviewer 只在后续 adapter 协议稳定后接入。它是额外 sensor,不是事实源,也不是机器 gate 的替代品。
events.jsonl = append-only canonical truth
telemetry.jsonl = append-only evidence index
task.json = 由 replay 重建的任务投影视图
control.json = 由 reconcile 重建的决策投影视图
artifacts/ = 被 hash 引用的不可变证据
Markdown = 人类可读解释,不参与状态判断
外部执行器不能直接追加 canonical event。agent、adapter 和人工工具只能提交 evidence,由控制层验证后生成事件。
MVP 只做一个 aggregate:Task。AgentRun、worktree 和 telemetry 先保留引用字段,出现并发执行需求后再拆分。
stateDiagram-v2
[*] --> Planning
Planning --> Ready: task_marked_ready
Ready --> InProgress: task_started
InProgress --> Review: task_submitted_for_review
Review --> InProgress: task_reopened
Review --> Completed: task_completed
Planning --> Cancelled: task_cancelled
Ready --> Cancelled: task_cancelled
InProgress --> Cancelled: task_cancelled
Review --> Cancelled: task_cancelled
hold 与 archived 不进入 phase:
hold是正交状态:越界、gate 失败、等待审批或人工暂停都可以触发。archived是存储属性:终态任务归档后仍然保留completed或cancelled语义。boundary_violation_recorded必须同时记录越界并进入 hold,避免崩溃后继续执行。
关键不变量:
| 操作 | 前置条件 |
|---|---|
ready |
objective、scope、至少一个 gate 合法 |
start |
phase 为 ready,没有 hold |
submit |
phase 为 in_progress,没有 hold |
finish |
phase 为 review,所有 required gate 最新结果通过,没有 hold |
archive |
phase 为 completed 或 cancelled |
revise |
MVP 只允许 phase 为 planning |
schema
event_id
command_id
task_id
seq
occurred_at
actor
type
payload
schema
id
kind # implementation(默认)| research(M-d 引入,创建后不可变)
objective
baseline_commit
read_scope
write_allow
write_deny
risk_triggers
gates
depends_on # 必须先完成的任务 ID(M-d 依赖边)
(注:早期草案的 mode(small/medium/large 风险级别)未作为 TaskDefinition 字段落地;
风险通过 risk_triggers + --quick 快速通道表达。)
allowed
rule_ids
diagnostics
approval_required
id
task_id
adapter
contract
scopes
context_hashes
required_capabilities
acceptance
run_id
source
command
exit_code
touched_files
input_hashes
output_hashes
所有边界对象使用 JSON Schema Draft 2020-12,默认关闭未知字段:
{"unevaluatedProperties": false}用户价值
团队知道系统承诺什么、不承诺什么。先冻结协议,再写执行器。
交付内容
schemas/
control.task-definition.v1.schema.json
control.event-envelope.v1.schema.json
control.task-view.v1.schema.json
ctl schema validate
ctl boundary check
ctl boundary explain
ctl architecture check
定义:
TaskDefinition、Scope、Gate、EventEnvelope。- 事件表、状态转换表和 reducer fixtures。
- 路径规范化规则:Windows 大小写、分隔符、绝对路径、
..、symlink、junction、UNC。 - 默认拒绝和 hard forbid 规则。
- proposal、scoped lease、
audit_hold、completion interlock 与 baseline manifest 的协议。 - 固定审计矩阵及
PASS / ASK / STOP / UNVERIFIED判定规则。 - proposal、approval、scoped lease、assignment、evidence、audit report、completion interlock 与 drift report 的最小 schema 方案;本阶段只冻结文档设计,不修改
schemas/**。
退出条件
- 合法 fixture 通过 schema 校验。
- 未知字段、缺少 gate、非法状态转换被拒绝。
..、绝对路径、symlink、junction、UNC 和 root 外路径被拒绝。- 相同事件流始终生成相同
task.json。 - hard forbid 不能被 permit 或 approval 覆盖。
- 测试、fixture、schema 或 required gate 数量下降时触发 baseline 回退并停止。
- 审计触发后进入只读
audit_hold,未通过 completion interlock 时不能生成完成事件。 - 固定审计矩阵至少覆盖 schema 反例、非法状态转换、replay、Windows 路径逃逸、受保护文件、依赖和 required gate 变化。
暂不做
有副作用的执行器、真实工作区 diff 集成、自动 reviewer、agent、telemetry 评分、drift 自动化、网络访问。
用户价值
可以稳定记录任务生命周期,并在中断后恢复。
命令
ctl init
ctl task create
ctl task revise
ctl task ready
ctl task start
ctl task status
ctl task cancel
ctl replay
ctl validate
ctl doctor
实现约束
events.jsonl是唯一可追加领域事实。task.json只能由 reducer 生成,禁止人工或 agent 写入。- 每条事件拥有严格递增的
seq和幂等command_id。 - reducer 不访问时间、Git、文件系统或网络。
- 写视图使用临时文件和原子替换。
退出条件
- 临时 Git 仓库内可以跑通创建、ready、start、cancel 生命周期。
- 删除
task.json后可以仅依赖events.jsonl恢复。 - 重复 replay 输出字节级一致。
- 缺失、重复和损坏事件被拒绝,不静默修复。
- 归档终态之外的任务不能归档。
暂不做
上下文 manifest、gate 执行、adapter、并发 append。
用户价值
任务可以明确声明“允许修改什么”,并且在完成前接受机器验收。
命令
ctl context build
ctl boundary check
ctl boundary explain
ctl gate run
ctl gate record
ctl task submit
ctl task reopen
ctl task finish
ctl task archive
ctl reconcile
实现约束
- M0 的协议级
boundary check / explain在此接入真实工作区 diff、上下文 manifest 和任务生命周期。 finish自动执行 scope check、required gates、事件追加和视图折叠。baseline_commit与任务启动前脏工作区快照分开记录,避免误判。.git/**、策略文件、gate 定义、canonical events 默认禁止写入。- gate runner 明确命令模板、工作目录、环境变量、超时与日志脱敏。
boundary_violation_recorded自动进入 hold。
退出条件
- 越界修改能够被拦截,并输出命中规则、证据和解除方式。
- gate 未通过时无法 finish。
- agent 自述或伪造 telemetry 不能直接完成任务。
- 归档冲突可检测,重复归档具备幂等语义。
small任务从创建到归档可以在真实仓库使用。
暂不做
自动 agent 执行、策略 runtime、任意 shell、依赖自动安装。
用户价值
不接任何模型,也能把 AI 开发任务变成可声明、可审计、可验收的受控过程。
命令
ctl assignment export
ctl run ingest --adapter manual
ctl audit --id <task>
ctl report
实现约束
manual是第一个正式 adapter,不是临时兜底。- 人或任意 AI 工具读取
assignment.json,工作后提交agent-output.json。 - 控制层独立检查实际 diff、gate 和证据 hash。
assignment.json和agent-output.json是后续 OMP adapter 的 contract test 基线;自动执行器不得另建隐式协议。small / medium / large是风险控制级别,不只是文档数量选项。- 允许显式升级模式,禁止静默降级。
退出条件
- 从任务创建、assignment export、人工执行、output ingest、gate 到归档端到端通过。
- 中断后
ctl board可以恢复上下文。 - evidence 包含输入、输出、命令和文件 hash。
- 重放后 audit 结论一致。
- 使用 M3 完成至少 10 个真实 small 任务。
暂不做
OMP 调用、多智能体、连续 drift 分数、daemon、数据库。
用户价值
OMP 可以执行单个 assignment,同时守住主工作区边界。
命令
ctl adapter capabilities --adapter omp
ctl workspace create
ctl workspace diff
ctl workspace apply
ctl run start --adapter omp
ctl approval request
ctl approval grant
ctl approval deny
实现约束
- OMP 只在 disposable worktree 中写入。
- worktree diff 必须经过 apply gate,不能直接进入主工作区。
- 越界 diff 永远不能 apply。
- 删除、依赖变化、Git 操作、网络访问、公共 API 和安全策略变化需要 step-up approval。
- approval 只能批准结构化 request,不能批准一句自然语言。
- lease 绑定 task、run、资源、动作、TTL 与最大使用次数。
- OMP adapter 必须映射通用的
proposal -> lease -> implement -> audit_hold协议,不能另建一套隐式授权状态。 - 自动 reviewer 可以提交 review evidence,但 completion interlock 仍由控制层执行。
退出条件
- OMP 中断后可以恢复或明确失败。
- 越界 diff、过期 lease、跨任务 lease 和重复 lease 全部拒绝。
- 高风险变更未经 approval 不能 apply。
- 至少用 M3/M4 完成 20 个真实任务,再决定是否进入 M5/M6。
暂不做
并发写入、自动合并、其他执行器 adapter、长期密钥注入。
M0–M4 的退出条件「至少用 M3/M4 完成 20 个真实任务,再决定是否进入 M5/M6」已达成:
- 任务账本累计 66 个 completed(走完整生命周期)+ 17 个 cancelled(弃置实验)。
- 仅算真功能/里程碑(非冒烟探针)也有 30+ 个:本轮硬层 8 个(M-a…M-g / M-f / M6-reviewer
/ flaky 修复 / 分发对齐)、更早的 quick-mode·stepup·architecture-convergence·unify-governance
等、以及
df-m4-01…21的 M4 dogfood 批。 - 治理层(边界·账本·门·审核·提交联锁·身份绑定)不仅实现且在线强制,并被这 66 个任务反复压测
(本轮 9 个提交全程 dogfood,连
cargo install自身都走 deps 审批 + 完成审计流程)。
结论:M5/M6 的前置条件满足。下一个任务开启 M5(可解释控制闭环);M6 主体(完整 capability lease 并发)按 ROADMAP 仍排在 M5 之后。
用户价值
系统可以回答“为什么继续、暂停或重规划”。
命令
ctl telemetry add # 向证据索引追加一条信号
ctl drift compute # 透明规则算出 drift level/score
ctl drift explain # 列出信号、规则 ID、evidence 与建议动作
ctl next-action # pass / ask / stop / replan / rescope(只读、建议性)
实现约束
- drift 首先使用透明规则,不使用模型自由打分。
- 相同 evidence 和规则必须生成相同决策。
- 未知信号默认不能放宽权限。
- drift 升高只能触发暂停或重规划,不能自动扩权。
replan和rescope只生成结构化 proposal,并停止当前执行;不得自动修改 scope、批准 lease 或启动新任务。
退出条件
- Golden fixtures 对应固定动作。
- 每次决策列出信号、规则 ID 和 evidence。
control.json可以由 reconcile 重建。
暂不做
模型评分、dashboard、OpenTelemetry Collector、自动重规划执行。
已落地:
- 证据索引而非 canonical event——按事实模型,telemetry 是独立的 append-only 证据索引
(
.ctl/telemetry.jsonl),不是 canonical event:ctl telemetry add是 M5 唯一写操作。 因此 冻结的 event-envelope schema 与 reducer 完全不动(telemetry 是 evidence 不是真相, 也不该污染 canonical 事件流)。条目带schema/task_id/kind/value/recorded_at/source,recorded_at由应用层打戳,domain 保持无时钟。 - 纯函数 drift 引擎——
src/domain/drift.rs(无 IO、无时钟,过 MODULE-001/002 纯度门):evaluate(signals)→DriftReport+next_action(report,phase)→NextActionProposal。 固定规则目录 DRIFT-001…009(边界越界 30 / 门失败 20 / 审核 needs_work 25 / 测试失败 15 / lint 5 / retries≥3 15 / 越界写 30 / held 10 / 未知信号 10),整数计分、规则按 ID 升序输出, 故相同 signals 字节一致。level 阈值 none=0 / low=1–19 / medium=20–49 / high≥50。 - next-action 决策表(首个命中胜出,确定性):未知信号→
ASK(失败关闭,绝不放宽)· high+越界信号→STOP· high→REPLAN· medium+in_progress→RESCOPE· medium→ASK· held→STOP· low/none→PASS。REPLAN/RESCOPE只产出结构化 proposal(CLI 打印), 不发事件、不改 scope、不批 lease、不启动任务——全程建议性。 - 决策投影进 control.json——
generate_board()每任务行追加drift_level/drift_score/ drift_rules/recommended_action;reconcile重建,且带 telemetry 时重复 reconcile 仍字节一致 (信号取整数、引擎无时钟)。signals 由drift_signals_from(events,state,telemetry)统一派生,compute_drift/next_action与 board 投影同口径。 - 测试:drift 纯函数单测(逐规则、阈值边界、确定性、不放宽不变量)+ golden fixture
fixtures/m5_drift_golden.json→固定动作 + store telemetry round-trip + 应用层 (telemetry_add 写/dry-run 不写、compute/next-action 不发事件、reconcile 字节一致)。共 215 测试绿。
用户价值
在写入范围不重叠的前提下安全并行,提高吞吐量。
命令
ctl schedule plan
ctl schedule validate
ctl schedule run
ctl agent-report
ctl run merge-candidate
实现约束
- 这时再拆出独立
AgentRunaggregate。 - 每个写入 agent 使用独立 worktree 和 capability lease。
- 每个写入 agent 必须有独立 scoped lease;lease 的 write scope 不能与其他写入 agent 重叠。
- 重叠写 scope 必须拒绝。
- 只读任务可以并发。
- 合并候选仍需人工确认。
退出条件
- 重叠写入被拒绝。
- 崩溃恢复不会重复执行副作用。
- 共享
.git风险有明确防护。 - 脏工作区和合并冲突可以恢复。
暂不做
全自动 merge、commit、push、deploy、动态扩权和多厂商并发。
软层已先行落地:control-guard 指挥主代理在「申请编辑」和「任务完成」时派只读 ctl-review
子代理审核/审计,靠约定与 explore 只读放行生效。下列硬层把它从「约定」升级为「网关强制」,
顺序是先修治理基座、再上强制审核门——每条都依赖前一条。除已标注 ✅ 的条目外,本节为设计、未启用。
问题:compute_gov_state(src/cli/mod.rs)遇到第一个 in_progress 任务即 early-return;
多个任务同时活动时,其余在网关层静默失管。并发子代理审核会制造多活动任务,不修则强制门名存实亡。
方向:网关按「派发该工具调用的那个任务」绑定治理(见 M-e),或在存在多活动写入任务时显式拒绝。 与 M5(reconcile)的活动任务集合定义对齐。
已落地:先取「显式拒绝」分支,后由 M-e 补上派发绑定(见下)。compute_gov_state 改为收集全部
非归档 in_progress 任务而非首个;活动写入任务定义为 phase=in_progress、非归档、write_allow
非空。≥2 个活动写入任务时返回新状态 MultipleActive { task_ids },网关对 write/edit 与可写子代理
失败关闭(只读、cargo check/test/build/fmt 等非变更操作仍放行,便于消歧期间验证);恰好 1 个写入
任务时按该任务绑定治理(held 优先),与旧单任务行为一致。只读 in_progress 任务(空 write_allow)
不产生写治理歧义。消解方式:ctl task submit/hold 其余任务只留一个 in_progress,或用派发令牌把
单次调用绑定到某个活动任务(M-e)。
用户价值:跨任务集中视图。reconcile 投影 control.json(AGENTS.md 预留,M5+),
ctl board 按 phase / hold / active / gate / 审核裁决聚合全部任务。审核裁决数据来自软层的
verdict→evidence 事件。
已落地:
- 投影——
ControlApp::generate_board()对事件账本作纯函数投影(无墙钟字段,重复 reconcile 字节一致,与 task.json 同语义),每任务一行 + 聚合 totals;project_control()原子写.ctl/control.json(temp+rename)。reconcile()末尾顺带投影 control.json。 - 列定义——phase /
held/active/gates_passing/gates_total/review/write_scope。active与 M-a 的网关关注集合对齐:非归档且 phase ∈ {in_progress, review}。 - 审核裁决——
review_status_from_events从软层evidence_rejected/evidence_accepted事件派生needs_work | passed | none,与 finish 联锁同口径(只认 file-keyed 未解决驳回)。 - CLI——
ctl board(人类表格)/ctl board --json(脚本/adapter);已登记进 CLI surface 守卫。 control.json暂不版本化 JSON Schema(派生投影、.ctl/被 gitignore),待 M5 reconcile 正式化时再定。
方向:「申请编辑」时调用现有 src/application/schedule.rs::detect_write_scope_overlap,
本次写入若撞上其他活动任务的 write_allow 则硬拒;并补全此前为桩的 ctl schedule validate /
ctl schedule run(现均已落地)。
已落地:
- 网关跨任务硬拒——
first_overlapping_active_task(src/cli/mod.rs):write/edit命中治理任务 自身write_allow后,再查是否落入其他活动任务的write_allow,命中即硬拒并回报conflicting_task。"活动"沿用ctl board口径(in_progress | review,非归档),全程一个 "活动"定义贯穿 M-a/M-b/M-c。这是detect_write_scope_overlap的单路径特化(路径 vs 集合); 与 M-a 互补——M-a 拦"多个 in_progress 写任务",M-c 拦"in_progress 写入踩进 review 任务(M-g 提交窗口)的领地"。spec 路径(.ctl/spec)仍前置放行,不受此限。 ctl schedule validate——读取.ctl/plans/<id>.json,对计划内每个任务取当前状态,跑schedule::validate_plan(同时修正其 phase 字符串期望Ready/InProgress→规范的ready/in_progress),输出有效/逐条 INVALID 并以非零退出。ctl schedule run——补全到安全边界:先按活态重校验,无效计划拒绝执行;有效则解析并打印 分组并行安全的执行顺序。真正的并发监督(worktree-per-agent、capability lease、文件锁、崩溃恢复) 按设计留给 M6,本命令永不 spawn 执行器。
方向:在调度现有的互斥分组之上增加「A 先于 B」的依赖关系(恢复 Trellis parent/child 的能力), 依赖写入任务声明而非靠树位置隐含。
已落地:
- 任务声明、持久化——
task_created/task_revisedpayload 新增可选depends_on(task ID 列表), 随 canonical 事件持久化;空依赖时不写该键,保持无依赖事件字节不变。此 schema 改动属受保护 边界变更(schemas/被边界 normalizer 拒入write_allow),由人类显式授权后施加,而非 agent 经 任务自助修改——符合"禁止 agent 改 canonical event"原则。reducer(src/domain/task.rs)用optional_string_set解析(缺省即空)。 - 调度拓扑——
plan_schedule(src/application/schedule.rs)在 M-c 写隔离分组之上叠加依赖:B依赖A(均在计划内)则B必落入比A更晚的组;按(依赖层级, id)贪心 earliest-fit 分配;无 依赖时退化为原互斥分组行为(既有测试不变)。计划外的依赖边视为已满足(忽略)。 - 环检测——Kahn 算法,
plan_schedule对存在依赖环的计划返回Err并列出涉及任务;ctl schedule plan据此拒绝并非零退出。 - 校验——
validate_plan增加依赖序检查:每个计划内前置必须在更早的组,否则 INVALID。 - CLI/可观测——
ctl task create/quick/revise --depends-on声明;ctl task status(人类+--json)、ctl board --json与control.json均展示depends_on(持久化的task.json投影受 task-view schema 冻结,暂不改,故不含该字段)。
方向:可写子代理按「派发它的那个任务」的 write_allow 受治,而非网关扫描第一个活动任务;
与 M6 AgentRun aggregate 和 capability lease 对齐。
已落地:
- 派发令牌——
ctl hook gate新增--task <id>,缺省回退到CTL_TASK_ID环境变量(空值视为无绑定)。.claude/hooks/ctl-gate.py从CTL_TASK_ID透传--task,作为显式且可审计的接缝;ctl 自身亦读同名 env,二者互为兜底。 - 绑定治理——把 M-a 的「收集全部活动任务 → 归约为单一治理态」抽成纯函数
resolve_active_governance(无 IO,直接单测)。令牌命中某个活动任务时,即使存在多个活动写入任务也按该任务绑定 (派发已显式声明,无歧义可言);令牌指向非活动/未知任务则视为过期、忽略,回退到 M-a 扫描 (≥2 写入任务仍MultipleActive失败关闭,绝不因坏令牌扩权)。绑定到 held 任务仍 held;绑定到只读 任务则按其空write_allow拒写——每个子代理按它自己的任务受治。 - M-c 跨任务写重叠对绑定任务依然生效(绑定不豁免重叠硬拒)。
- 8 条纯函数单测 + 真实 ledger e2e:无绑定写→
multiple_active拒;--task/CTL_TASK_ID绑定→in_progress放行本作用域、拒他作用域;过期令牌→回退multiple_active。 - 这关闭了 M-a 遗留的「M-e 子代理绑定仍留后续」,并解除 M-f 的前置依赖(M-a + M-e 均就绪)。
命令
ctl apply --path <p> # 越界编辑申请 → 路径级 approval;grant 后网关放行该路径 ✅
ctl review accept|reject # 记录完成审计裁决(pass/fail)✅
ctl task finish # 联锁:要求存在通过的完成审计裁决事件 ✅
依赖:M-a(多活动任务治理)+ M-e(子代理绑定)——两者均已就绪,前置依赖已解除。
已落地(完成审计联锁):把审核从「软层约定」(control-guard 派只读 ctl-review 子代理)升级为
网关联锁——finish(Review→Completed)现要求存在一条新鲜的通过完成审计裁决。
- 裁决事件——
ctl review accept|reject --id <task> [--note],应用层record_completion_audit。 复用现有evidence_accepted/evidence_rejected,以区分性source="completion_audit"标记 (schemas/control.event-envelope.v1.schema.json的source为自由串,无需改冻结 schema); 审计人身份由事件顶层actor携带。只能在 Review 录入(提交后的审计窗口)。 - 新鲜度——裁决只在「晚于最后一次
task_submitted_for_review的 seq」时才计入;返工 (reopen→resubmit)会让上一轮的 pass 失效。最新一条completion_audit裁决必须是 pass; fail 或全无审计 → 拒绝 finish。与 M-g 干净树联锁并列检查。 - 流程:implement → submit(→Review) →
ctl review accept(完成审计)→ commit/push(M-g 窗口)→ finish(M-f+M-g 双联锁)→ archive。 - 5 条应用层单测 + 真实 ledger e2e:无审计→拒;reject→拒(FAIL);accept→放行;返工后旧 pass 作废。
已落地(分发对齐):软层技能改为调用硬层命令,关闭 review-contract 分发缺口——
control-guard/SKILL.md:关闭流程 Step 5 与 Gate 2 插入ctl review accept|reject, 「verdict → event」节给出具体命令并标明 mode=B 完成审计为finish硬前置;命令参考新增 「Record audit verdict」行。ctl-review/SKILL.md:声明只读 reviewer 不能写,裁决由派发者经ctl review录入,要求输出 明确 pass/fail + 一行摘要供--note。.ctl/spec/guides/review-contract.md:verdict 节标明录入命令与 mode=audit 的硬门语义。
已落地(ctl apply 越界编辑放行):把 mode=A 编辑审核从软层升级为网关有条件放行——
ctl apply --id <task> --path <p> --reason <r>:把越界编辑建模为路径级 approval (scope={action:"apply", path}),复用既有 approval 事件/grant 流(无需改冻结 schema)。 grant(应在 ctl-review mode-A pass 之后发)即记录的 reviewer 裁决。- 网关
write/edit分支:目标在write_allow之外时,若存在覆盖该路径的已授予 apply → 放行 (reason=reviewed out-of-scope exception);否则拒绝并提示ctl apply。放行是按路径的 (只开被授予的那条),且 M-c 跨任务重叠仍硬拒(apply 不豁免重叠)。 GovState::InProgress/ActiveTask增approved_apply_paths,由compute_gov_state从 granted approvals(action=apply) 提取,经resolve_active_governance绑定透传。- 1 条解析器单测 + 真实 ledger e2e:apply 前越界写→拒;grant 后同路径→放行;另一越界路径→仍拒。
- 与 M-c 互补:M-c 拒跨任务重叠,
ctl apply经审核有条件放行本任务的越界编辑。
已落地(M6 审计人绑定 / reviewer-lease):把「审计人 ≠ 实现者」从约定升级为硬规则——
- 事件 actor 真身份化:
ControlApp增actor字段,由CTL_ACTOR环境变量设定(缺省human),build_event改用self.actor(此前硬编码human,无从区分实现者与审计人)。with_actor供显式覆盖。这是 reviewer 绑定的地基。 - reviewer ≠ implementer:
record_completion_audit(pass)拒绝由实现者 actor 录入——实现者集 =task_started的 actor ∪ 非审计evidence_accepted的 actor。自审批(self-approve)被拒; 自我reject(实现者自曝问题)仍允许——只有自我认证完成是威胁。 - 分发:control-guard / ctl-review 技能与 review-contract 改为以
CTL_ACTOR=ctl-review录入裁决。 - 3 条应用层单测(self-approve 拒 / self-reject 放 / actor 取自 with_actor)+ 真实 ledger e2e。
留后续:
- 完整 capability lease(M6 主体):worktree-per-agent、scoped write lease、崩溃恢复——见 M6 正文。 本条只交付了 reviewer 身份绑定这一子集(M-f 的收尾),未引入完整 lease aggregate。
目标:任务在代码提交之前不得进入 Completed,让"完成"真正等于"已提交且通过审计"。
当前冲突(必须先解):现在 git_commit/git_push 只在 Completed 放行(finish 之后开窗)。
若直接加"未提交不得 finish",则 commit 要 Completed、Completed 又要 commit,死锁。
设计:
- 把提交窗口前移到
Review:git_commit/git_push在Review与Completed均放行 (src/cli/mod.rs网关 bash 分支)。 finish(Review → Completed)增加联锁:工作树必须干净(git status --porcelain为空, 或本任务write_allow内无未跟踪/未提交改动)。脏树则拒绝 finish,提示先提交。- 流程变为:implement → submit(→Review) → 完成审计(ctl-review B) → commit/push → finish(→Completed) → archive。
实现约束:联锁需要在 Review→Completed 的 reducer/应用层校验调用一次 git status;与
M-f 的"完成审计裁决事件"联锁并列检查。注意 .ctl/ 被 gitignore,干净判定应针对任务
write_allow 覆盖的、git 跟踪的路径,避免被运行时态误判为脏。
已落地:
- 提交窗口前移——
src/cli/mod.rs网关git_commit/git_push分支改为在Review与Completed均放行(GovState::Review升级为携带task_id+write_allow)。in_progress仍拒提交。 - 干净判定——新增
src/infrastructure/workspace::dirty_paths_in_scope,对任务write_allow作git status --porcelain -- <scope>,返回作用域内的脏路径;.ctl/被 gitignore 故天然排除。 非 git 仓库 / 无git时返回None(不可验证,不臆造裁决)。 - finish 联锁——
finish_task(应用层,非 reducer,守纯函数约束)在门禁与证据检查之后调用上述 helper:作用域非空且存在脏路径则拒绝 finish 并提示先在 Review 内提交;只读任务(空write_allow) 与非 git 仓库跳过。 - 流程:implement → submit(→Review) → 完成审计 → commit/push → finish(→Completed) → archive。
决策:M6「受限多智能体」正文设想的 capability-lease 并行(A/B/C 在不重叠写范围内同时跑、
worktree-per-agent、scoped lease、崩溃恢复)暂不实现,按「禁止过早进入自动路径」原则
(no premature automation)改以依赖驱动的串行执行交付 M6 的可用价值。理由:真正的并发监督
(共享 .git 防护、文件锁、崩溃幂等、合并恢复)是重资产,而当前 dogfood 负载是单写者串行;
先用依赖边把「A 先于 B」这一最常见的编排需求做硬,再在有真实并发压力时引入 lease 主体。
决策更新(增量交付并发主体):在串行启动之上,并发主体改为分片增量交付而非一次性大改。
切片 1 已落地(见下「M6:并发执行(切片 1)」):激活 AgentRun aggregate + 每 run 独立
worktree + 跨活动 run 的写范围重叠拒绝,由 ctl schedule run + OMP 驱动,永不 spawn 执行器。
仍留后续(未实现):崩溃恢复(孤儿 run/worktree 检测与幂等续跑)、脏工作区/合并冲突恢复、
共享 .git 进一步加固、自动 merge/commit/push。
问题:M-d 已让任务声明 depends_on 并在调度/board 投影里用它排序,但任务 start 不查依赖——
B 可以在 A 完成前就 ctl task start。M-a 只拦「≥2 个活动写任务」,并不理解「B 必须等 A 完成」。
已落地:
unmet_dependencies(task_id)(src/application/mod.rs,应用层跨任务读,reducer 仍纯)——回放 本任务取depends_on,逐个回放依赖:仅当依赖存在且 phase=Completed 才算满足(archive 保留 Completed,故已归档完成的前置仍满足);缺失任务或任何非完成态(planning/ready/in_progress/review/ cancelled)一律视为未满足,失败关闭。返回排序后的阻塞 ID 列表。start_task门控——start 前先查unmet_dependencies,非空则拒绝并列出阻塞依赖 ID + 解法 (先完成/归档前置,或ctl task revise --depends-on改边)。依赖链 A→B→C 因此严格串行:B 在 A 完成 前拒启,C 在 B 完成前拒启。与 M-a 互补——M-a 拦并发写歧义,本条拦「次序未到就启动」。- 可观测——
ctl task status(人类「Blocked by (unfinished dependencies): …」行)与--json(新增派生blocked_by数组)暴露阻塞依赖。冻结的 task-view schema / 持久化task.json投影不动:blocked_by是 CLI 显示层的派生字段(该 JSON 本就是冻结投影的超集,含depends_on/gate_results等非 schema 字段),既不写盘也不入 schema。 - 测试——6 条应用层单测:依赖未完成→拒启(错误含阻塞 ID);依赖完成/归档→放行;链 A→B→C 严格串行; 未知依赖→拒启(失败关闭);无依赖→不受影响照常启动。
方向:把已建好但从未接线的 M6 脚手架激活为活态并发单元。此前 AgentRunState/apply_run
(run.rs)、RunEventStore(.ctl/runs/)、control.run-state.v1 schema、ctl agent-report
全部存在却无人调用——ControlApp 从不实例化 RunEventStore,活态 run_start 仍是 M4 单执行器
路径(任务内嵌单 active_run),schedule run 只打印顺序从不建 run。
已落地:
- aggregate 接线——
ControlApp经惰性run_store()句柄读写 run aggregate;replay_run回放单个 run;active_runs()枚举所有处于Running的 run(活态并发集)。run 事件由apply_runreducer + 信封结构校验(Event::is_valid,读时强制)治理,不走信封 JSON schema 的按类型 payload 条件分支 ——那里的run_started描述的是 M4 任务态 run 指针(run_id+adapter+lease_id),与 M6 run aggregate 形状(worktree_path+lease_id)有意不同,拿任务条件校验 run 事件是范畴错误。 create_run(task_id)——任务须InProgress,run 继承其write_allow/deny/gates/adapter,落run_created到.ctl/runs/<run_id>/。start_run(run_id)——M6 核心不变量:起 run 的写范围必须与其他每个活动 run 不相交,check_run_scope_overlap(复用detect_write_scope_overlap)命中即拒并指名冲突 run;重叠判定先于 任何副作用,被拒的起动不留 worktree/事件。通过后建每 run 独立 worktree (.ctl/runs/<run_id>/worktree,分支omp-run-<run_id>),生成 scoped lease 与 OMP manifest (prepare_run,纯函数,不 spawn),落run_started,投影run.json。- 终止——
finish_run/fail_run/abort_run走terminate_run:best-effort 清理 worktree 后落终态 事件;run 一旦离开Running即释放其写范围,原被拒的重叠 run 方可起动。M4run_start单执行器 路径原样保留(重度被测)。 ctl schedule run——校验计划后,把首个并行安全分组激活为并发 run:逐任务create_run+start_run(重叠强制),打印建好的 run/worktree/manifest 与 OMP 后续步骤;仍永不 spawn,无效计划 仍拒,--dry-run只报意图不写盘。后续分组待当前组完成后重跑。- 测试——7 条应用层单测:起 run 须 InProgress;run aggregate 持久化于
.ctl/runs/<id>/;重叠起动被拒 (指名冲突 run、不留 worktree);完成 run 释放范围;两不相交 run 并发活动;replay 确定性;以及一条 真 git 仓 e2e:两不相交 run 经start_run各建真实 worktree 并发到Running,第三个重叠 run 被拒, 完成其一后被拒 run 方可起。
分发:切片 1 + 依赖门控串行启动已 cargo install --path . --force 装上 on-PATH 二进制
(install-m6-concurrency 任务,经 deps step-up 批准),活态验证通过。
方向:编排器在 run 处于 Running 时死掉,重启后 run aggregate 卡在 Running、可能残留孤儿
worktree。按用户选定的只读检测 + 显式 abort 策略收尾(不自动续跑、不 spawn)。
已落地:
recover_report()(应用层,只读:回放 aggregate + stat 文件系统,绝不落事件)——对每个Runningrun 给出快照(run_id、task_id、write_allow、worktree_path、worktree_exists、manifest_exists);worktree_exists=false即标记为不一致 run(隔离工作区已不在,疑似崩溃),是恢复-abort 候选。orphaned_run_worktrees()——.ctl/runs/下 run 已终态/缺失却仍留有 worktree 目录的,列为可安全清理的 孤儿。- CLI——
ctl run recover(人类表格,MISSING worktree 高亮 +--json)报告Runningrun 与孤儿 worktree;ctl run recover --abort <run_id>经既有abort_run拆除单个 run(落run_aborted、清理 worktree、释放其写范围,原被它挡住的重叠 run 即可起)。 - 幂等/无重复副作用——对已终态 run 再 abort 被 run reducer 拒;recover 报告只读;worktree 清理对不存在 路径跳过。
- 测试——4 条应用层单测:
recover_report只列Running(排除 Queued/终态)且对缺失 worktree 置 flag;abort 释放范围 + run 转Aborted+ 从报告消失 + 二次 abort 被拒;孤儿 worktree 列出终态 run 的 残留目录;真 git 仓 e2e:真实 worktree 报告worktree_exists=true且 abort 后被删除。
仍留后续(未实现):自动续跑/收尾、脏工作区与合并冲突恢复(见切片 3)、共享 .git 进一步加固、
自动 merge/commit/push、动态扩权、多厂商并发。
方向:补完 M6 退出条件「脏工作区和合并冲突可以恢复」。任务态 merge_candidate 早已存在,但切片
1–2 是 run aggregate 制,完成的 run 没有「能否落地 / 如何恢复」的裁决。按既定的只读检测 + 显式恢复
哲学补 run 级裁决,绝不自动 merge。
已落地:
run_merge_candidate(run_id)(应用层,只读,不落事件、不合并)——对一个 run aggregate 的隔离 worktree 相对 HEAD 取 diff,给出裁决:touched_files;out_of_scope(越出 run 写范围);cross_run_conflicts(触及其他活动 run 的写范围——切片 1 已保证活动 run 互不相交,此为纵深防御, 抓「越界写进并发同伴领地」);workspace_conflicts(主工作区在触及路径上是脏的);requires_approval(高风险变更,仅提示不阻塞);mergeable= 无阻塞。复用file_in_write_scope/diff_worktree/dirty_paths_in_scope/detect_high_risk。- 恢复分类——每个阻塞项映射成一条
recovery建议:out_of_scope→ctl run recover --abort重新 划范围;cross_run_conflict→让对方 run 先落地或 abort,再复查;dirty_main_workspace→先 commit/stash 主工作区这些文件再复查。worktree 缺失/未起 → 明确报错并指向ctl run recover --abort。 - CLI——
ctl run merge-candidate --run <run_id>(人类摘要 +--json)。恢复动作本身沿用切片 2 的ctl run recover --abort,不新增破坏性命令。 - 测试——4 条应用层单测(多为真 git e2e):干净 run → mergeable;主工作区脏 → 阻塞且给
dirty_main_workspace恢复建议;越界写 →out_of_scope+cross_run_conflict同时命中各自恢复建议; worktree 缺失 → 报错指向恢复。
M6 功能性收口:四个退出条件均已交付——重叠写拒绝(切片 1)、崩溃恢复无重复副作用(切片 2)、
共享 .git 防护(worktree-per-agent)、脏工作区/合并冲突可恢复(切片 3)。仍属增量后续(非阻塞):
自动续跑/收尾、git rebase 自动消解、共享 .git 进一步加固、自动 merge/commit/push、动态扩权、多厂商
并发——均按「禁止过早进入自动路径」继续留待真实压力出现再做。
方向:闭合 M-f 留后续里点名的「完整 capability lease(M6 主体)」子集。此前 LeaseState 与 lease
事件(lease_created/lease_used/lease_revoked/lease_expired)只活在 task aggregate(M4 任务内嵌
run 路径),M6 start_run 仅 generate_uuid() 盖一个不透明 lease_id,跨 run 重叠安全完全靠 run aggregate
而非 lease。这次把 run aggregate 自己的 lease 生命周期接上。
已落地:
- typed lease 迁移——
domain/lease.rs暴露LeaseState::grant(LeaseGrant)/consume/revoke/expire(返回 typedLeaseError,不解析Event);task 与 run 两个 reducer 各自把 payload 解析成 typed 入参后委托同一套迁移,task 错误文案保持字节一致。LeaseState增 serde-default 绑定字段task_id/adapter/scopes(旧 task lease 投影不破)。 - run aggregate lease——
AgentRunState.lease: Option<LeaseState>;apply_run新增lease_created/lease_used/lease_revoked。run 绑定不变量:max_uses >= 2(保证起跑消费一次后仍 Active;task lease 旧语义max_uses>=1不变)、task_id/adapter与 run 一致、scopes与 runwrite_allow完全相等 (V1)。run_started在存在原生 lease 时要求lease_id匹配 + Active +remaining_uses>0;legacy run (无lease_created)仍按不透明lease_id放行。 - start_run 授予并消费——在既有 registry+per-run 临界区内依次落
lease_created→lease_used(起跑消费一次,remaining_uses=99)→run_started,并把同一lease_id盖进 run manifest。不要求外部 预授予 lease;不声称 worktree/manifest 文件系统副作用与账本追加构成原子——崩溃窗口由只读ctl run recover(孤儿 worktree / Queued 持 lease 的 partial-start)披露,绝不自动消解。 - 终止撤销——
finish/fail/abort在终态事件前对仍 Active 的原生 lease 落lease_revoked。 - recover 只读披露——
run.json投影与ctl run recover(人类 LEASE/USES 列 +--json)暴露结构化lease_status(ACTIVE/REVOKED/EXPIRED,legacy →UNKNOWN+lease_compat=pre_lease_run)、remaining_uses、lease_stale(墙钟 TTL 超时仅报告不追加lease_expired)、lease_nonactive、partial_starts。无散文式状态串。 - 复用 M4 默认值经共享常量——
RUN_LEASE_TTL_SECONDS=3600/RUN_LEASE_MAX_USES=100,M4 与 M6 同源。 - 测试——18 条新增(负例为主):lease 迁移(grant/consume/expire-at-0/revoke 后拒用);run reducer
(lease_id 不匹配、
max_uses<2、scopes≠write_allow、revoke 后拒用、legacy 可回放、fixture 确定性 99 uses);应用层(授予+消费、重叠拒绝不落 lease 事件、二次 start 不重复消费、finish 撤销并解锁重叠 run、legacy→UNKNOWN、partial-start 只读检测、Running 持 revoked lease 标nonactive)。
仍留后续(未实现):每次实际文件写入消费一次 lease use(V1 只在 start 消费一次)、跨账本原子性、
加密能力、自动续跑——均按「禁止过早进入自动路径」留待真实需求。未改冻结 schema:run aggregate 事件
沿用 Event::is_valid() + reducer 校验(既有 run 事件即如此),不走信封 per-type schema。
硬层(M-a…M-g)之上的一组 record-only / 只读能力。它们从不门控 create/finish、不打分、 不替代审核独立性——只记录与披露,把判断权留给人与硬层。设计细节见 EPISTEMIC_CONTROL.md。
ctl brainstorm(来源溯源)✅ —record / attach-critic / skip-critic / show:记录任务派生自哪些 发散/收敛认知工件。record-only,不声称思考质量或评审独立性。ctl uncertainty(未知项账本)✅ —record / evidence / dispose / status:登记任务携带的未知项及 其处置(resolved/accepted_as_assumption/invalidated)。modeloracle 仅顾问性——命令层拒绝 以 model 证据 resolve 一个未知项。ctl research(研究 / spike)✅ —record / status:研究型任务以「证据 + 未知项处置」而非代码完成。ctl handoff export(只读交接)✅ — 导出可移交的任务快照,供另一会话或人接手;不发事件。ctl prd init(PRD 脚手架)✅ — 生成带结构化## Tasks段的 PRD 模板。ctl prd plan / validate / status(认知闭环)✅ —plan把已确认 PRD 的## Tasks段机械变成受治理任务(逐条create_task+ brainstorm provenance,仍走 PreToolUse gate);validate预校验格式/边界/重叠/门模板;status披露 alignment→PRD→任务→进度血缘。无新事件、无模型判断在 plan 时介入——分解在 PRD 编写阶段已完成、人已确认。ctl ralph(无人值守安全监督)✅ —ralph-safe-run-v1:环绕外部 run 的只读 dead-man switch。 从不 spawn 执行器、从不写代码——一旦该交还人类注意力即停。ctl next-task(确定性调度建议)✅ — 在所有 Ready 任务里挑出依赖满足 + drift 最低 + 写范围与活动 in_progress 任务无冲突的那个,建议ctl task start --id <X>;无 Ready 则回退到 Planning 任务建议ready。完全确定性(drift 分数升序,平手按 id 升序),只读,不发事件。- 富上下文注入(
ctl hook context扩展)✅ — session 开始时模型不仅看到 active task 的边界, 还看到 drift level + next-action 建议、open uncertainties(带着哪些未决问题)、blockers(被哪些 未完成依赖卡住)、provenance(来自哪个 PRD/alignment)。三平台 hook(Claude / OMP / opencode) 同步渲染。纯上下文丰富——零决策变化、零边界放松,把已有确定性信号喂给模型。 - 知识/记忆层(已外挂)✅ — 原子事实捕获 + 检索 + 全局记忆卫生从 ctl Rust 移到 workflow
伴侣
scripts/knowledge.py(fact add/list/promote/summary+memory verify)。ctl 不再 own 记忆/知识内容(它是 evidence,agent-owned);编排下沉到ctl-cognitiveskill(决定何时记录 brainstorm/uncertainty/research 等 canonical 状态、何时经 script 管理知识库)。canonical 追加仍由 ctl(不变量:外部 actor 不能追加 canonical 事件)。ctl spec doctor(确定性 lint,非记忆)保留在 ctl。决策动机:保持 ctl Rust 精简(governance-only),把"没用"的内容管理下沉到 workflow 层。
为不破坏「单一事实源」而刻意未接线、但 reducer 已实现并测试的前瞻状态机:
- 任务级多 run 镜像:
run_scheduled/run_launched/run_merged事件 +TaskState.active_runs/RunRef/schedule_plan_id的 reducer 分支已就绪并测试,但ctl schedule run走的是 AgentRun 聚合(run-store)这条唯一事实源路径,不再镜像到任务级——接线会造成两份冲突状态。 保留为前瞻脚手架,待真实需求再决定接线或移除。 - run 级 gate/evidence 重复事件:
run_failed/gate_checked/evidence_accepted/evidence_rejected在 run reducer 中存在,但生产以任务级同名事件为准,run 级副本仅供回放/测试。 prd plan✅ 已实现 — 见上「V1 认知层」段的ctl prd plan / validate / status。
ctl-review 依赖 review-contract.md,ctl-diagnose 依赖 failure-diagnosis.md。
已采用方案二:这些固定规则文件随仓库维护于 .omp/spec/guides/,并经
src/infrastructure/skills.rs 的 include_str! 嵌进 ctl 二进制,由 ctl init 注入
.omp/spec/guides/(fresh-clone 安全;skills 引用 ../../spec/guides)。下游 ctl init
不再引用到不存在的契约文件。
来源:SECURITY_AUDIT_2026-07.md(2026-07 系统审计)+ 后续讨论。条目按类别分组,优先级 ★★★/★★/★ 标注,工作量 S/M/L。
当前 ctl 共 36,148 LOC,两个最大文件已涨到非可持续规模。0.0.13 release notes 已规划"per-domain split of the production bodies",V1 认识层进来后非但没拆反而又涨了 ~400 行。现在该动手。
- ★★★ P1/S — 拆
src/cli/mod.rs(8265 LOC)成命令组模块。机械重构,只调pub(super)可见性。 目标布局:cli/{task,run,handoff,spec,brainstorm,research,board,gate,hook,apply,init}.rscli/governance.rs(GovState + resolve_active_governance + compute_gov_state)cli/write_gate.rs(classify_write_target + is_spec_path + path_in_scope)cli/render.rs(format_brainstorm_provenance / format_uncertainty_ledger / format_research_output / format_decisions / format_decision_line — 已是纯函数,零风险)cli/mod.rs仅留顶层Commandsenum + dispatch(~500 LOC)。
- ★★ P2/M — 拆
src/application/mod.rs(5948 LOC)成 service 模块。 目标布局:application/{task,run,workspace,brainstorm,uncertainty,research,handoff,drift,event_build,view}_service.rsapplication/mod.rs仅留ControlAppstruct + open/init + 共享 helper(~800 LOC)。
- ★★ P3/M — 拆
src/domain/task.rs(2024 LOC)的 reducer arms。 目标布局:domain/task.rs留 TaskState + structs + apply dispatch(~600 LOC)domain/task_reducer/{lifecycle,cognitive,run}.rs。
- ★ P4/S —
src/domain/audit_matrix.rs(2536 LOC)按 0.0.13 pattern 拆: 基线声明(~300 LOC,留 production) + 测试(进audit_matrix_tests.rs)。 - ★ P5/S — 顺手清理:
has_opaque_wrapper未使用 import;cmd_hook_check_write整个废弃(已被cmd_hook_gate取代,且有文档化缺陷,删比修省事)。
ctl 的"记忆"不是一个系统,是 6 个独立载体 + 1 个全局目录,语义/完整性/生命周期完全不同。详见 SECURITY_AUDIT_2026-07.md 配套讨论。载体清单:
| 载体 | 位置 | 完整性 | 写入者 |
|---|---|---|---|
| canonical 事件流 | .ctl/tasks/<id>/events.jsonl |
sha256 + schema + 单写者锁 | ctl 自己 |
| curated spec | .ctl/spec/**/*.md |
无 | AI agent(经 ctl-spec skill) |
| atomic facts | .ctl/facts.jsonl |
结构化,无 hash | scripts/knowledge.py fact add(外挂) |
| 全局偏好 | ~/.ctl/memory/<slug>.md + MEMORY.md |
无,纯 prose 规则 | AI agent |
| handoff 快照 | .ctl/handoffs/<id>.json |
schema 校验 + task_id 绑定 | ctl handoff capture |
| 决策日志 | .ctl/decisions.jsonl |
canonical:false + ts |
host hook |
ctl 自己只在 src/cli/mod.rs:7980 读 ~/.ctl/memory/ 的 mtime 做 wrap-up 提醒——完全不写入、不校验、不知道内容。
- ★★★ #2/S ✅ 已实现 —
ctl spec doctor扫.ctl/spec/**/*.md里引用的代码路径是否还存在,报 stale。保留在 ctl(确定性 lint,非记忆)。 - ★★ #1/S ✅ 已外挂 — 移到
scripts/knowledge.py memory verify(ctl 不再 own 记忆内容)。 - ★★ #4/M —
scripts/knowledge.py聚合查询(只读):canonical provenance(经 ctl)+.ctl/spec/目录树 + facts 摘要 + 最近 N 个 handoffs + 最近 decisions。统一入口(外挂脚本,非 ctl 命令)。 - ★ #3/M — 注入加预算与相关性排序:hook 当前注入 facts 摘要无预算。改成 hook 调
scripts/knowledge.py fact summary --budget <tokens> --task <id>,按 task objective 检索相关 facts 返回预算内摘要。涉及 3 个平台 hook(.claude/.omp/npm-omp)。 - ★ #5/S —
scripts/knowledge.py fact retire --id F-XXX:软删除(打retired_epoch标记,保留 append-only 性质);list默认隐藏,加--all才看。 - ★ #6/S —
scripts/knowledge.py memory list --stale/memory retire <slug>:全局记忆生命周期。 - ★ #7/S — facts → spec 双向回链:
promote时在 spec 末尾加<!-- fact: F-XXX -->注释,或维护.ctl/spec/.fact-index.json。事后能查"哪些 spec 段落基于哪个 fact"。
完整明细见 SECURITY_AUDIT_2026-07.md 残留风险与跟进。每条都已识别 file:line + 触发输入 + 修复方向,可直接拆 issue。下面是分类索引:
- ★ A2/L —
apply_changes非事务化。多文件 changeset 中途崩溃留下半应用状态,workspace_applied 事件也未写。真修需暂存目录 + 原子 rename + marker 文件 + 启动时回滚/前滚。位置:src/infrastructure/workspace/mod.rs:203-228。 - ★ A3/S — scope 区分大小写与 FS/
is_protected不一致。path_in_scope用字节比较,Windows/macOS FS 是大小写不敏感。write_allow=["Src"]在 Windows 上不匹配src/x.rs。修需cfg(target_os)下做大小写不敏感组件比较。位置:src/cli/mod.rs:7095-7103。 - ★ A4/S —
create_worktree不验证祖先链非 symlink。.ctl/tasks/<id>若被预置为 symlink,worktree 会落在 project_root 外。低概率,防线纵深。位置:src/infrastructure/workspace/mod.rs:46-66。
- ★ B5/L — Windows kill-窗口残留风险。完全闭合需 wrapper 脚本架构或 winapi 绑定加
MOVEFILE_DELAY_UNTIL_REBOOTfallback。 - ★ B6/S — 静默降级。
ctl self-update --version <older>不警告(只对 latest 跑is_newer守卫)。修:--version时仍比对 current,降级要--allow-downgrade。位置:src/infrastructure/self_update.rs:239-247。 - ★ B7/S — staging 目录名可预测(PID)+ 默认 umask。多用户主机上本地攻击者可预创建
.ctl-update-<pid>赢得 TOCTOU。修用tempfile::TempDir(随机名 0700)。位置:src/infrastructure/self_update.rs:268-270。 - ★ B8/S — TOCTOU 在 tag 解析与 latest download 之间。修从解析出的 tag URL 直接下载。位置:
src/infrastructure/self_update.rs:113-130。 - ★ B9/S —
extract走 PATH 上的tar。修 pin 绝对路径或进程内解压。位置:src/infrastructure/self_update.rs:158-170。
- ★★ C10/S —
run_startedarm 无 phase guard。不像兄弟workspace_created要求Phase::InProgress,run_started只查active_run.is_some()。位置:src/domain/task.rs:1266-1296。 - ★★ C11/S —
hold_entered/boundary_violation_recorded无 phase guard。终态任务上追加这类事件会翻is_held,而task_archived不在豁免列表 → 终态任务可被永久不可归档。位置:src/domain/task.rs:1081-1089。 - ★ C12/S —
command_id唯一性不在 append 强制。重复 command_id 事件被 reducer 幂等 no-op 但仍写进账本。修validate_event在state.processed_commands命中时 reject。位置:src/application/mod.rs:3296-3323。 - ★ C13/M —
cmd_hook_check_write读task.json投影做决策。违反"投影永不做决策输入"。要么改走 replay,要么直接废弃(见 P5)。
- ★★ D14/L — 手写 validator 不是完整 Draft 2020-12。静默忽略
$ref/$defs/pattern/oneOf/anyOf/not/dependentSchemas/prefixItems/maximum/maxItems/maxLength/exclusiveMinimum/multipleOf/else/contains/minProperties/maxProperties/无 sibling properties 的unevaluatedProperties。当前 6 个 schema 只用到已实现子集(已扫,健全),但任何引入未支持关键词的 schema 编辑会静默绕过。位置:src/infrastructure/schema_validator.rs:43-290。长期换jsonschemacrate(需评估 DEP-001..004)。 - ★ D15/S —
date-time格式校验拒绝所有 fractional-second 时间戳。&rest[digits_end + 1..]多跳一字节。位置:src/infrastructure/schema_validator.rs的is_valid_iso8601_datetime。改成&rest[digits_end..]+ 加回归。 - ★ D16/S — UUID 格式只查结构,不查 v4 version/variant 位。位置:
src/infrastructure/schema_validator.rs:80-92。 - ★ D17/S —
$idlookup 用子串匹配。id.starts_with(schema_id) || id.contains(schema_id)的第三分支无锚定。位置:src/infrastructure/schema_validator.rs:30-34。
- ★ E18/S —
validate_output接受非字符串touched_files元素。下游用as_str().unwrap_or("")静默丢掉,记录空 changeset 的 run_completed。位置:src/adapters/omp/mod.rs:71-77、src/adapters/opencode/mod.rs:71-78。 - ★ E19/S —
validate_output不读 exit_code。agent 报告exit_code:1仍被run_ingest记录为 run_completed。位置:src/application/mod.rs:3882-3896。 - ★★ E20/S —
run_start在校验 adapter 名之前就提交 worktree + lease_created。未知 adapter 名留下孤儿 worktree 和 dangling lease 事件,无回滚。位置:src/application/mod.rs:3722-3756。 - ★ E21/S —
manual文档化为 adapter 但不在SUPPORTED_ADAPTERS。两套 adapter 概念静默分歧。位置:src/cli/mod.rs:1199-1200、src/adapters/manual/mod.rs、src/adapters/mod.rs:14。
- ★ F22/S — drain 缓冲无界。
read_to_end无上限,OUTPUT_CAP(64KB)只在读完后才截。话痨 gate 可在 60s 窗口内写到 OOM。修在 read 循环内到 OUTPUT_CAP 即 break。位置:src/infrastructure/gates/mod.rs:198-204, 387-393。 - ★★ F23/M — PATH/CARGO_HOME/NODE_PATH 不剥离。任务能影响 ctl 父 env 时可注入木马
cargo/npx总是退出 0,所有 gate 假绿。修 pin 到绝对已知好路径,或限制 PATH 到系统/工具链目录。位置:src/infrastructure/gates/mod.rs:358-381。 - ★★ F24/S — Windows
taskkill继承全部 env 且 PATH-resolved。把 F23 放大成每次 timeout 都执行任意代码。位置:src/infrastructure/gates/mod.rs:294-298。修用绝对系统路径 + 同 denylist。 - ★ F25/M — gate 总在 project_root 跑,忽略任务隔离 worktree。pre-merge gate 查的是 main 分支代码,不是任务在 worktree 里的修改。位置:
src/application/mod.rs:1182、4917-4927、4237。 - ★ F26/M — npx gate 在文档说"fail-closed 拒绝 registry 拉取"下仍可联网。
filter_allowed_env只剥代理/token,不挡直接 egress。位置:src/infrastructure/gates/mod.rs:67-95, 358-381。
- ★ G27/S — install.ps1 PATH 去重逻辑只看精确字符串。同路径不同大小写/尾斜杠会重复。位置:
scripts/install.ps1:80-90。
若要按"实现成本 vs 用户感知收益"排序,前 5 个建议立刻动手:
- P1/S — Rust 文件拆分(
cli/mod.rs→ 命令组)。代码已涨到非可持续,越拖越大。 - #2/S —
ctl spec doctor只读命令。记忆系统里投入产出比最高的小修。 - P2/M —
application/mod.rs拆分。配合 P1 一起做。 - #1/S ✅ — 已外挂到
scripts/knowledge.py memory verify。 - C10+C11/S — reducer 两个 phase guard 防御补强。应用层已挡,reducer 是最后防线,小修大安心。
互不依赖,各自独立可交付。
最早验证路径:
ctl init
# 三步法(create/ready/start),或用 `ctl task quick` 一步融合:
ctl task create --id fix-config-parsing --objective "fix config parsing" `
--read-scope src/config.rs --write-allow src/config.rs --gates cargo_test
ctl task ready --id fix-config-parsing
ctl task start --id fix-config-parsing
ctl context build --id fix-config-parsing
# 人或任意 AI 工具完成修改
ctl gate run --id fix-config-parsing --gate cargo_test
ctl task submit --id fix-config-parsing
ctl task finish --id fix-config-parsing
ctl task archive --id fix-config-parsing
ctl board体验标准:
- 30 秒内创建并启动一个 small 任务。
- 同一仓库默认只有一个 active task,减少反复输入 ID。
- 每个成功命令输出建议的下一步命令。
ctl board给人看,ctl board --json给脚本和 adapter 看。- 所有写操作支持
--dry-run。 - 拒绝操作时输出命中的边界、证据和解除方式。
write_code_any
任意 shell
自动扩权
自动安装依赖
无限制网络
长期密钥注入
自动 commit / push / merge / deploy
数据库迁移
公共 API 与安全策略自动变更
多个 agent 并发写同一文件
未经 wrapper 的 MCP tool 透传
让 agent 修改 gate、policy、canonical event
- 路径规范化:Windows 大小写、反斜杠、glob、symlink、junction、UNC 和仓库外路径。
- 事件持久化:文件锁、
seq、command_id、checksum、fsync、原子替换和崩溃恢复。 - 脏工作区归因:任务启动前已有修改如何记录。
- Gate 安全模型:是否允许命令模板之外的 shell,如何限制环境变量、子进程和超时。
- 状态迁移:是否允许归档后恢复,审批 hold 如何解除。
- Adapter 协议:版本化 JSON stdin/stdout、timeout、cancel、retry 和幂等键。
- 任务 ID:时区、同名冲突、归档目录冲突。
- 模式升级:用户可显式选择;系统只能建议升级,不能静默降级。
- Capability lease:资源表达、TTL、
max_uses、撤销与过期进程终止。 - 许可证隔离:哪些内容只吸收思想,哪些模板可能构成复制。
- 审计协议:触发器、
audit_hold、允许的只读 gate、恢复事件和 completion interlock。 - Baseline manifest:稳定检查项、回退判定、显式升级与人工解除方式。
Temporal server
数据库或 daemon
Cedar / OPA runtime
OpenTelemetry Collector
Vault
完整 in-toto 签名链
Codex / Claude / OpenCode adapter
先把字段、事件、trait 和 enforcement point 留出来。只有真实需求出现后,再接入对应能力。