-
51 号文档阶段 A 上线后发现:CEO 心跳自主立项建出的草稿(split:false)其实自动带 1 条 draft task,dispatch 技术上立刻能跑,原以为的"零 task 闸门"并不存在——安全性曾经完全靠 ceo/tools.md 里"不许自己调 dispatch"这一句提示词约束,没有代码层面的东西挡着。 POST /goals/:goalId/dispatch 新增判断:created_by==='ceo' 且 updated_at===created_at (自建成后从没被 PATCH 过)直接 409。复用既有时间戳字段代理"有没有人碰过",不加新列—— 已知简化(ponytail 注释标了天花板:分不清"真审过"和"随手碰一下",精确区分需要专门的 reviewed_at 列,留给阶段 B)。 task-queue-routes.test.js 新增两条用例:未审阅的 ceo 草稿 dispatch 409、PATCH 一次后放行; human/未填 created_by 的草稿不受影响。相关四个测试文件 84 条用例 + 全量 unit 套件跑过, 无回归(4 个不相关的预存在失败:工作区不干净的环境闸门 + 两个与本次改动无关的仓库卫生检查)。 51 号文档 §四/七/八、docs/README.md 索引同步更新。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWsH2YBwBWTsQggPaPbsKJ
-
## 批 4:解除「一家公司同时只有一个 active root run」 - DROP idx_graph_runs_one_active_root(建表块删掉 + 迁移块显式 DROP, CREATE ... IF NOT EXISTS 对存量库不生效) - max_parallel_tasks 从 company.json 调用时读(不是模块加载时快照, 否则改配置要重启才生效且不报错);缺省 1、非法回落 1 并留声、硬上限 20 - 准入判据「从活跃集外进入活跃集」按额度计数 **生死线用 8 个真进程并发抢坑实测**(不是纸面论证):max=1/2/3/5 四组, 每组恰好抢到 max 个、库里活跃数 = max,落选进程各吃 40 次 RunSlotBusyError, 零 other 错误。唯一索引没了之后,准入事务是真守卫。 ## 批 3 对抗复核的四条遗留 - N1 🔴 cancelQueuedRun 的前提被批 3 自己的 requeueBlockedRootRun 改没了: queued 的 run 现在可以是跑过一半的。探针实测取消后子 run 残留 paused/stop_requested=0 —— 那条 running 的子 run 继续调 agent 烧 token。 改为无条件走 engine.stopRun - L1 ⚠️ 恢复路径对 manual 只兑现一半:注释写着「T1 永远回不来这个后果因此 消失」,但调度器故意不碰 manual → 退回队列后没人接。定案:「manual 不自动 出队」约束的是首次起跑的授权,已被人亲手起过的不算新授权。 判据用 attempt>0 而不是 started_at —— 后者会被 requeue 清成 NULL, 恰好在需要它的那一刻是空的(实测踩过) - N2 🟠「退回队列」对用户完全不可见:200 + ok:true + 按钮全灰 + 图消失 + 一句「节点状态一律 pending」的假提示。加 notice 明说 - N3 🟠 isSlotRaceError 把 InvalidTransitionError 当竞态吞掉,而探针证伪了 那条注释:被推走时条件更新返回 0 不抛,真正抛它的只有「调用方声明了非法 转换」。删掉该分支,让它吵起来 四条里三条是同一个模式:注释承诺的前提被本轮自己的改动改没了,注释还留着。 文档 §十三.3 / §十四.4 已就这个模式立过两次案,这是第三、四、五次。 ## 文档 §十五 实施记录,含 §15.4 一次流程事故:87b776c 由第三方会话在对抗复核完成 前误提,提交说明的「1201 用例全绿」压在一份尚未通过复核的代码上。 全量:128 文件 / 1240 测试全绿,web:build 通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
队列与调度(§五.1): - 下达任务不再因「公司已有 active root run」吃 409,改为建 queued run + queued task 排队;出队走 updateRunStatus 的 CAS + BEGIN IMMEDIATE 准入事务,触发挂在 transitionRun 这个唯一漏斗上,60s sweep 兜底。 - 依赖已不可能满足的排队任务就地推导成 blocked,且每次 tick 重算不落死,上游重跑 成功后能原路解开。 恢复路径(批 3 复核 F1/F2):三条恢复转换的 from 全在活跃集之外,撞坑满时退回队列而不是 永远躺在 blocked;退不回去的裸 run 干净拒绝,不留半截状态。 控制台三处可见性修复 —— 症状全是纯视觉、不报错、git status 干净: 1. 顶栏墙钟恒 `now - started_at`,没有终点。一条昨晚就 blocked 的 run 第二天还在涨到 「16h 58min」,而那是页面上唯一的时间信号 —— 「已经停了等人处置」被显示成了 「还在跑再等等」,这两件事需要的动作相反。终点改取 run.updated_at,并标「已停」。 2. 越界日志只报「留下 N 处改动」,文件清单躺在 timeline.ndjson 里翻不到。判罚就是按 这份清单下的,而人要判断「这真是 agent 写的吗」唯一的依据就是路径(并行的开发会话 改宿主仓库会被算到当时在跑的节点头上,那是误判,看一眼就知道)。改为列出 entries。 3. run 收敛成 blocked 时只把 task 写成 blocked、不写原因,任务列表那一行只剩一个橙色 的「挂起」,必须逐层点进 org 图 → work 图 → 节点详情才知道发生了什么。改为把 「卡在哪个节点 + 什么 fail_reason + 判词摘要」写进 tasks.blocked_reason。 原因绝不带 UPSTREAM_BLOCK_PREFIX —— 带了会被调度器当成依赖推导出的 blocked 自动 推回 queued,而这类 blocked 本来就等着人处置(守卫在 task-blocked-reason.test.js)。 测试:126 文件 / 1201 用例全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>