112 lines
7.6 KiB
Markdown
112 lines
7.6 KiB
Markdown
# 需求统一启停控制台(2026-09-30)
|
||||
|
|
|
|||
|
|
> 起因(用户原话):**「使用协作skill后 某个需求 统一管理 各种对应任务和程序的启停,包括协作程序等」**
|
|||
|
|
> 落地物:`.workbuddy/collab/goalctl.py`(新)+(复用)`.workbuddy/collab/stop-collab.py`。
|
|||
|
|
> 全文读数均为本机实测,⛔ 无推断。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. 结论(三行)
|
|||
|
|
|
|||
|
|
1. **一个需求牵扯的"可启停物"只有四层**(实测盘清,别再数):**排期层/程序层/总开关层/服务层**;钩子层**不在可启停范围内**(下条)。
|
|||
|
|
2. 🔴 **一个硬事实决定了整个设计形状**:**钩子是全局注册、跨项目共用的** —— `E:/ProgramData/.workbuddy/settings.json` 里 12 个 hook,其中还混着**别的工作区**的 `ai1net-decision-laya/bridge/decision_bridge.py`(×4 事件)。⇒ ⛔ **不能靠"装卸钩子"来启停某个需求**(会连累所有项目)⇒ 只能 **开关文件 + 入口早退**。
|
|||
|
|
3. **入口已落地成三条命令**:`goalctl.py status | start | stop`(**默认干跑**,加 `--yes` 才真做;写前自动备份)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 清单:一个需求到底牵扯哪些东西(本机实测)
|
|||
|
|
|
|||
|
|
| 层 | 具体物 | 本棒实测状态 | 谁能改 | 拦在哪(生效点) |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| ① **排期层** | `automations` 表里的**周期排期** | **3 条,全 PAUSED** | ⛔ **只有会话**(`automation_update` 工具) | 不再起新会话 |
|
|||
|
|
| ② **程序层** | `collabd.config.json` 的 `wake_enable`(协作程序**投递总闸**) | 本棒已置 `false` | ✅ 脚本 | 程序不再投递(=不再唤醒主会话) |
|
|||
|
|
| ③ **总开关层** | `goal.json` 的 `run` 段(`active`/`paused`) | 本棒已置 `paused` | ✅ 脚本 | 唤醒轮/程序读到即**立刻退** |
|
|||
|
|
| ④ **服务层** | 看板 `board.py --serve`/守护 `guard`/网关心跳 | **全停**(0 个 / 没在跑 / 登记册 0 条) | ✅ 复用 `stop-collab.py` | 写标志 ⇒ 对端**优雅退出** |
|
|||
|
|
| — **钩子层** | 全局 12 个 hook(`wb-result-hook.py`、`lock-guard-hook.py`…) | 常开、不可装卸 | ⛔ 只有全局配置能改 | **不拦** —— 零 token,且"要不要干活"全在它调的程序内部 |
|
|||
|
|
|
|||
|
|
**剔除项(⛔ 不是启停对象)**:**一次性棒 15 条**(`schedule_type='once'`、`next_run_at` 空=已耗),它们 `status` 仍是 ACTIVE 但永远不会再触发。
|
|||
|
|
⚠️ **判"某条排期还在不在跑"不能只看 `status`** —— 删除是**软删**(`deleted_at` 有值、`status` 照旧 ACTIVE)⇒ 脚本里已按 `deleted_at` 过滤。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 三条硬约束(决定了为什么长这样)
|
|||
|
|
|
|||
|
|
| # | 约束 | 后果 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| ① | **钩子全局注册、跨项目共用** | ⛔ 不装卸钩子;启停只能靠"开关文件 + 入口早退" |
|
|||
|
|
| ② | **排期只能由会话改**(脚本直改 `workbuddy.db` 的 `automations` = 改活库,且宿主是否重读未验) | 脚本**只打印待办**;⚠️ 并给**降级保障**:漏改时唤醒轮读到 `run=paused` 会立刻退(一次空跑) |
|
|||
|
|
| ③ | ⛔ 不 kill 会话/worker | ⛔ 不重启宿主 | ⛔ 不改全局配置 | 停服务一律"**写标志让对方自己退**"(`guard.stop`)、或停**自建**看板实例 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 启停语义:三层同时翻,缺一层就留缝
|
|||
|
|
|
|||
|
|
| 想要 | ① 排期层 | ② 程序层 | ③ 总开关 | ④ 服务层 |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| **真停** | `PAUSED` | `wake_enable=false` | `run=paused` | `stop-collab.py --yes` |
|
|||
|
|
| **真启** | `ACTIVE`(+两条相位差 ≈30 分钟) | `wake_enable=true` | `run=active` | ⛔ **不自启任何常驻**(常驻在本机已判走不通) |
|
|||
|
|
|
|||
|
|
**为什么三层缺一不可**(各自守备范围不同):
|
|||
|
|
|
|||
|
|
- **排期层** 管:**会不会起新会话**(唯一能产生新交付的通道)
|
|||
|
|
- **程序闸** 管:**会不会投递**(投递=唤醒活会话,会占主会话一轮 ⇒ 用户体感"卡")
|
|||
|
|
- **总开关** 管:**起来了之后该不该干活**(兜底 + 人/AI 都直接可读)
|
|||
|
|
- 🔴 三者**互补不重叠**:只关程序闸 ⇒ 排期照旧起会话;只停排期 ⇒ 程序照旧投递;只有三个一起翻才是"真停"。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 用法
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
PY="E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe"
|
|||
|
|
"$PY" .workbuddy/collab/goalctl.py # 状态台(只读,四层一次列清)
|
|||
|
|
"$PY" .workbuddy/collab/goalctl.py stop # 干跑:列出会做什么
|
|||
|
|
"$PY" .workbuddy/collab/goalctl.py stop --yes # 真停
|
|||
|
|
"$PY" .workbuddy/collab/goalctl.py start --yes # 真启
|
|||
|
|
"$PY" .workbuddy/collab/goalctl.py start "新目标名" --yes # 启 + 改名
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**驱动口径(=用户已定的那条)**:启停由 **「执行 XXX 目标」这道口令**驱动 ——
|
|||
|
|
下口令 ⇒ `start`;**目标完成或有阻碍 ⇒ `stop`**。⛔ **唤醒轮自己不许把自己启回来**。
|
|||
|
|
|
|||
|
|
### 状态台实际输出(本棒读数)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
【总开关】run = paused
|
|||
|
|
目标 = 本机(桌面客户端)内的多会话协作
|
|||
|
|
目标状态 = 三路全过
|
|||
|
|
队列 = {'done': 4}
|
|||
|
|
【② 程序闸】wake_enable = False
|
|||
|
|
钩子链最后唤起 = tick 2 分钟前 / once 4 分钟前
|
|||
|
|
【① 排期层】周期排期 3 条(**这才是启停对象**)/一次性棒 15 条(已耗,⛔ 不是启停对象)
|
|||
|
|
⚪ 1eaf45c3 PAUSED [协同监管 · 心跳……]
|
|||
|
|
⚪ 16bec5ce PAUSED [本机协作]-唤醒轮B
|
|||
|
|
⚪ caca9a89 PAUSED [本机协作]-唤醒轮A
|
|||
|
|
⇒ 一句话:这个需求现在是 **停着**
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 本棒实际做了什么
|
|||
|
|
|
|||
|
|
1. **新增** `.workbuddy/collab/goalctl.py`(状态台 + start/stop;**默认干跑**、写前备份到 `.workbuddy/collab/bak-goalctl-<日期>/`、⛔ 从不删文件)。
|
|||
|
|
2. **接进唤醒轮**:两条提示词都加了**第 0 步"先读总开关"** —— `run=paused` ⇒ ⛔ 不派活、⛔ 不改文件、⛔ 不自启,一句话结束(="停"的兜底)。
|
|||
|
|
3. **按已定口径落成一致状态**:A/B 从未 ACTIVE(没下过"执行 XXX 目标"口令)⇒ 正解就是停 ⇒ 执行 `stop --yes`:
|
|||
|
|
`run=paused`/`wake_enable=false`/服务层全停/排期 2 条本已 PAUSED。
|
|||
|
|
4. 🔴 **顺手修掉一个真 bug**(会致"误判完成"):原实现把任务图按"默认点"读 `tmp/supervise-inbox/taskgraph.json` —— **该文件根本不存在** ⇒ 三路并集少一路 ⇒ 可能把"未完成"看成"已完成"。现改为**按配置 `taskgraph` 解析**(`交付物/任务图.json`,18 节点全 done);并给**读不到**单独打标(⛔ 不许读不到就判完成)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 已知边界(诚实说,⛔ 别当它能做到)
|
|||
|
|
|
|||
|
|
- ⛔ **做不到"一秒内把所有在跑的会话停下"** —— 会话不可 kill(用户明令)。能做的只有「**不再叫新的** + **起来了不干活**」。
|
|||
|
|
- ⚠️ **排期层脚本改不了** ⇒ 必须由会话执行;有 `run=paused` 兜底,但**那一轮仍会被起一次**(成本=一次空会话)。
|
|||
|
|
- ⚠️ **一个工作区当前只有一份 `goal.json`** ⇒ **"需求"与"工作区"目前是 1:1**。若将来一个工作区要并行多个需求,得把开关做成多份(本棒不做)。
|
|||
|
|
- ℹ️ `wake_enable=false` **只关投递**:程序的投影、看板、台账照常(零成本),所以"关"没有副作用。
|
|||
|
|
- ℹ️ ④ 服务层里**网关心跳已被判死能力**(桌面端硬编码 `CODEBUDDY_DISABLE_CRON=1`)⇒ 那一项是"名存实亡",`stop` 只是顺手清登记册。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 图
|
|||
|
|
|
|||
|
|
见同目录 `唤醒-需求统一启停-20260930-图.html`(① 四层清单图:标出钩子层"不可启停";② 三层守备图:各层各拦什么)。
|