Files
dsh_ai1net_server/交付物/唤醒-需求统一启停-20260930.md
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

113 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 需求统一启停控制台(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`(① 四层清单图:标出钩子层"不可启停";② 三层守备图:各层各拦什么)。