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/ 知识文件,按口径入库)
This commit is contained in:
1 parent
30b46dbd0c
commit
c1b5e4d966
735 files changed
+153192
-2415
No files matched your search
@@ -0,0 +1,283 @@
|
||||
# 执行检查长期在线(Windows)
|
||||
|
||||
> 🔴 **定案(2026-10-03 23:17–2026-10-04 07:15 实测)**:**长期在线只有一条路** ——
|
||||
> **Windows 计划任务 + 永不返回的守护循环脚本**(跑 `collabd.py --supervise`)。
|
||||
> 本文档是**唯一权威**(⛔ 别处不要再写另一套)。
|
||||
> 证据源=测试3 的复盘 `目标-会话协作测试3-7cd276/S3_常驻启动成功复盘_20261003.md`
|
||||
> + `S4_常驻自愈闭环复盘_20261003.md` + `S5_A6自指修复与目标收口_20261004.md`。
|
||||
|
||||
## 〇、🔴🔴 先分场景:两种"长期"不是一回事
|
||||
|
||||
**两种都要长期在线,但它们要的东西不同** —— ⛔ 别混用,混用就会出现
|
||||
"看起来在跑、其实随时会死"或"该停时还在跑"。
|
||||
|
||||
| | **场景 A · 用户手动创建主会话** | **场景 B · 定时任务创建主会话** |
|
||||
|---|---|---|
|
||||
| 典型形态 | 用户在 WorkBuddy 里自己开一条 `[主]-…` 会话,盯着它推进 | 排期按点拉起 `[主]-…`(`recurring`,如 `FREQ=HOURLY;INTERVAL=1`) |
|
||||
| 会话寿命 | **随用户何时收工**(可能几分钟,也可能几小时) | 跑完即 `completed`,**与下一跳之间有静默窗** |
|
||||
| 🔴 **对"常驻"的硬要求** | ⛔ **不能靠会话活着** —— 一收工就没人拉 ⇒ **必须脱离会话** | ✅ 会话自己周期性回来 ⇒ 但**每跳之间仍有空窗** ⇒ **同样要脱离会话** |
|
||||
| 唯一合格载体 | **计划任务 + 守护循环**(本文档第三~五节) | 同左(⛔ **排期代替不了载体** —— 排期只是"谁按点喊一次",喊完会话就结束) |
|
||||
| 怎么验它真活着 | 心跳 `pid` 活 ∧ `ts` 距今 < 90 s + `round` 递增 | 同左,⚠️ **且要静置 ≥ 12 分钟**(⛔ 短观察证明不了事) |
|
||||
| 谁负责"目标完成后关它" | 守护循环见 `guard.stop` 即 `exit 0`(第五节之二) | 同左 —— ⛔ **排期不会帮你关** |
|
||||
|
||||
### 🔴 两个都踩过的坑(**与场景无关**,因为根子在载体)
|
||||
|
||||
1. **"一次性工具调用起的子进程"在两种场景下都不合格** ——
|
||||
实测测试3 开工时 pid 69080 已死;本区两个后台任务(`collabd --supervise` 活 842 分钟、
|
||||
`board.py --serve` 活 634 分钟)父链都是
|
||||
`bash → bash → bash → sandbox-cli → WorkBuddy.exe`(**会话树内**),
|
||||
⛔ `.workbuddy/collab/` 里**既无 `.ps1` 也无 keeper** ⇒ **没走本文档这套**。
|
||||
⚠️ **它们现在活着,只因为发起它们的那条会话进程树至今没被回收**
|
||||
⇒ 「**存活时长 = 发起会话的存活时长**」(测试3 `S4` 第六节原话),
|
||||
**⛔ 不能用它论证"这种起法也能长期"**(该文明确写「这条『反例』**不成立**」)。
|
||||
2. **"排期是 `once` 且已过期"=哑排期** —— 实测 `[主]-会话协作测试3-主会话` 是
|
||||
`schedule_type=once`/`scheduled_at=2026-10-03T20:33`/`next_run_at=None`/
|
||||
`last_run_at=None` ⇒ **它一次都没被触发过**,而界面上 `status=ACTIVE` **看起来像在跑**。
|
||||
⚠️ **`status=ACTIVE` ≠ "在跑"**;判"真会触发"要看 `schedule_type='recurring'` ∧ `next_run_at` 非空。
|
||||
|
||||
## 一、为什么必须换载体(⛔ 别再自己发明)
|
||||
|
||||
| 载体 | 能不能长期 | 实证 |
|
||||
|---|---|---|
|
||||
| 工具调用起的子进程 | ❌ | 父进程一退出就被回收(活不过当轮/当会话) |
|
||||
| 宿主排期拉的会话起 | ❌ | 测试3 开工时 pid 69080 已死(心跳停在 22:55:31) |
|
||||
| `CREATE_BREAKAWAY_FROM_JOB` | ❌ | **`PermissionError(13,'拒绝访问。')`** —— 本机被作业对象拒绝,**这条路封死** |
|
||||
| `cmd /c start /B` | ❌ | 本机沙箱拦截 |
|
||||
| 排期到点起一次会话 | ❌ | **跑完即退** ⇒ 静默窗内无人(`once` 更糟:可能**永不触发**) |
|
||||
| **计划任务 + 守护循环** | ✅ | **实测 23:16:37 起活到 23:29 仍在跑**,`round` 1→74;死亡后 **5–6 秒**自动恢复 |
|
||||
|
||||
**关键差别=父链**:计划任务创建的进程由 **Task Scheduler 服务**创建,
|
||||
**不在 WorkBuddy 会话的 Job Object 里** ⇒ 不会被会话回收。
|
||||
|
||||
## 二、装法(照抄,⛔ 别自创参数)
|
||||
|
||||
### ① 包装脚本 `<工作区>\.workbuddy\collab\start-supervise.ps1`
|
||||
|
||||
⚠️ **必须是「永不返回的守护循环」版**。⛔ **别写成"跑一次就退"** —— 那样常驻死掉时脚本
|
||||
**正常返回 0** ⇒ 任务被判「成功」⇒ `Restart*` 永不触发(测试3 实测:活 12 分钟后再不复活)。
|
||||
完整可用版见第五节(含 `-Wait`/心跳判据/退避/**停止标志**四项要点)。
|
||||
|
||||
```powershell
|
||||
$ErrorActionPreference = "Continue"
|
||||
# 🔴 坑①:必须设,否则 _wb_db() 退回读 0 字节空库 ⇒ 闸二永久失效
|
||||
$env:CODEBUDDY_CONFIG_DIR = "E:\ProgramData\.workbuddy"
|
||||
$env:COLLABD_CONFIG = "<工作区>\.workbuddy\collab\collabd.config.json" # 🔴 坑②
|
||||
$env:PYTHONIOENCODING = "utf-8" # 中文日志不乱码
|
||||
$env:PYTHONUNBUFFERED = "1" # ⛔ 否则重定向到文件时 Python 块缓冲、诊断不可见
|
||||
$root = "<工作区>"; Set-Location $root
|
||||
|
||||
$hbPath = "<工作区>\.workbuddy\collab\logs\supervise-heartbeat.json"
|
||||
$stopFlag = "<工作区>\tmp\supervise-inbox\guard.stop" # 🔴 目标完成信号(第五节之二)
|
||||
$logPath = "<工作区>\tmp\keeper.log"
|
||||
$staleSec = 90 # 与 collabd.supervise_alive() 同口径
|
||||
|
||||
function Say([string]$m) { Add-Content -LiteralPath $logPath `
|
||||
-Value ("[" + (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") + "] " + $m) -Encoding UTF8 }
|
||||
function Get-HbAge { try { $o = (Get-Content -LiteralPath $hbPath -Raw -Encoding UTF8) | ConvertFrom-Json
|
||||
$ts = [double]$o.ts; if ($ts -le 0) { return -1 }; return [int]((Get-Date).ToString("U") - $ts)
|
||||
} catch { return -1 } }
|
||||
|
||||
$backoff = 5
|
||||
while ($true) {
|
||||
if (Test-Path -LiteralPath $stopFlag) { Say "guard.stop present => stands down"; exit 0 } # 🔴
|
||||
$age = Get-HbAge
|
||||
if ($age -ge 0 -and $age -lt $staleSec) { Start-Sleep -Seconds 5; continue } # 别人在跑 ⇒ 让位
|
||||
Say ("spawn collabd --supervise (backoff=" + $backoff + "s)")
|
||||
# 🔴 -Wait 必须:& 对 pythonw.exe(GUI 子系统)**不阻塞** ⇒ 会叠出多个常驻
|
||||
Start-Process -FilePath "<pythonw.exe>" `
|
||||
-ArgumentList @("-u", "<工作区>\.workbuddy\collab\collabd.py", "--supervise") `
|
||||
-WorkingDirectory $root -WindowStyle Hidden `
|
||||
-RedirectStandardOutput "<工作区>\.workbuddy\collab\logs\supervise.out.log" `
|
||||
-RedirectStandardError "<工作区>\.workbuddy\collab\logs\supervise.err.log" `
|
||||
-PassThru -Wait | Out-Null
|
||||
Say ("collabd exited (heartbeat age=" + (Get-HbAge) + "s)")
|
||||
$w = $backoff
|
||||
while ($w -gt 0) { if (Test-Path -LiteralPath $stopFlag) { Say "stop flag during backoff"; exit 0 }
|
||||
Start-Sleep -Seconds 5; $w -= 5 }
|
||||
if ($backoff -lt 60) { $backoff = [Math]::Min(60, $backoff * 2) }
|
||||
}
|
||||
```
|
||||
⚠️ **落盘必须带 BOM**(坑③):`[System.IO.File]::WriteAllText($p,$text,[System.Text.UTF8Encoding]::new($true))`。
|
||||
|
||||
### ② 建任务(⚠️ 一律 PowerShell 的 `ScheduledTasks` 模块)
|
||||
|
||||
⛔ **`schtasks.exe` 在本机沙箱被黑名单拦截**(Security Center → Command Security)
|
||||
⇒ **建/查/改/停计划任务全部走 `Get-/New-/Set-/Start-ScheduledTask` + `Out-File` 落盘再读**
|
||||
(⛔ 别指望命令回显)。
|
||||
|
||||
```powershell
|
||||
$a = New-ScheduledTaskAction -Execute "C:\windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
|
||||
-Argument '-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "<工作区>\.workbuddy\collab\start-supervise.ps1"'
|
||||
$t = New-ScheduledTaskTrigger -AtLogOn -User "Administrator"
|
||||
# 🔴 三个设置缺一不可(少一个就前功尽弃):
|
||||
$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) `
|
||||
-MultipleInstances IgnoreNew -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1)
|
||||
New-ScheduledTask -TaskName "collabd-supervise-<区名>" -Action $a -Trigger $t -Settings $s -Principal $p
|
||||
# 改动作不影响已配的触发器 ⇒ 更新时只传 -Action
|
||||
Set-ScheduledTask -TaskName "..." -Action $a
|
||||
```
|
||||
|
||||
| 设置 | 值 | 漏了会怎样 |
|
||||
|---|---|---|
|
||||
| `ExecutionTimeLimit` | `0`(无时限) | 默认 72 h ⇒ 到点被杀 |
|
||||
| `MultipleInstances` | `IgnoreNew` | 重复起 ⇒ 双写台账 |
|
||||
| `RestartCount`/`RestartInterval` | `999`/`1 min` | 死了不重拉 |
|
||||
| Trigger | `AtLogOn`(Interactive・最高权限) | 开机不启动 |
|
||||
|
||||
⚠️ **动作一律用 `powershell.exe`(不是 `pythonw.exe`)** —— 动作里直接跑解释器没有"设环境变量"这一步。
|
||||
(用户长期记忆里那条"`python.exe` 会闪黑窗"针对的是**交互式起进程**;计划任务走 `Hidden`+无控制台,不闪。)
|
||||
|
||||
## 三、🔴 三个坑(都可复现,逐个都是"秒退"或"看着在其实没跑")
|
||||
|
||||
### 坑① 🔴 不设 `CODEBUDDY_CONFIG_DIR` ⇒ 读到 **0 字节空库**
|
||||
|
||||
- 计划任务**不继承**会话的环境变量 ⇒ `_wb_db()` 退回 `Path.home()/.workbuddy/workbuddy.db`
|
||||
(本机那个是 **0 字节、无 `sessions` 表**)。
|
||||
- 症状:每 20 秒刷一次 `all_sessions_idle 读库失败 no such table: sessions`
|
||||
⇒ 闸二(所有会话都结束)**永久失效** ⇒ fail-safe 判"有会话在跑" ⇒ **检查会话永远建不出来**。
|
||||
- ✅ **正解=用机制自带的配置项 `host_db`**(`collabd.py` 里它本就**优先于**环境变量),
|
||||
指向真库 `E:/ProgramData/.workbuddy/workbuddy.db`。
|
||||
⇒ **这比设环境变量更可靠**(配置跟着工作区走,不依赖谁起的它)。
|
||||
|
||||
### 坑② 🔴 配置查找靠 `cwd` ⇒ 传错 cwd 当场拒跑
|
||||
|
||||
`_cfg_candidates()` = `COLLABD_CONFIG` → `<cwd>/.workbuddy/collab/collabd.config.json`。
|
||||
⛔ **不要把 `cwd` 设成 `collab/` 目录** —— 那会拼出多一级 `.workbuddy` ⇒ 当场拒跑。
|
||||
✅ 设成**工作区根** + 同时显式给 `COLLABD_CONFIG`(双保险)。
|
||||
(`ensure_supervise()` 内部原先传 `cwd=脚本所在目录` —— 已由测试3 主会话修成 `cwd=str(WS)`。)
|
||||
|
||||
### 坑③ 🔴 PowerShell 5.1 读**无 BOM** 的 UTF-8 `.ps1` ⇒ 中文路径全毁 ⇒ **秒退**
|
||||
|
||||
- 症状:计划任务 `LastTaskResult=1` **秒退**,心跳毫无动静。
|
||||
- 定位:`Get-Content -Encoding Byte -TotalCount 3` ⇒ `36 69 114`(ASCII 的 `$Er`)⇒ **无 BOM**。
|
||||
PowerShell 5.1 对无 BOM 文件按 **ANSI(GBK)** 解码 ⇒ 脚本里 `会话协作测试3` 变乱码 ⇒ 路径不存在。
|
||||
- ✅ 落盘**必须带 BOM**:
|
||||
`[System.IO.File]::WriteAllText($p,$text,[System.Text.UTF8Encoding]::new($true))`(`$true` = 带 BOM)。
|
||||
- 📌 **本机所有含中文路径的 `.ps1` 一律带 BOM**,否则一律秒退。
|
||||
- ⚠️ 判据:**`LastTaskResult`** —— `0` = 成功;`1` = 秒退(八成是 BOM)。
|
||||
|
||||
## 四、验收(三样都齐才算成,⛔ 打印"已启动"不算)
|
||||
|
||||
```powershell
|
||||
Get-ScheduledTask -TaskName "collabd-supervise-<区名>" | Select-Object TaskName,State
|
||||
Get-ScheduledTaskInfo -TaskName "collabd-supervise-<区名>" | Select-Object LastRunTime,LastTaskResult
|
||||
```
|
||||
🔴 `LastTaskResult` **必须 0**。
|
||||
|
||||
**存活唯一机读判据**(=机制里那条,⛔ 别用"日志在更新"代替):
|
||||
`.workbuddy/collab/logs/supervise-heartbeat.json` 里 **`pid` 活着 ∧ `ts` 距今 < 90 秒**。
|
||||
|
||||
```powershell
|
||||
# pid 是否真在进程表(⛔ tasklist 走 bash 会被当路径;用 PowerShell)
|
||||
$p = Get-Process -Id <pid> -ErrorAction SilentlyContinue # $null = 已死
|
||||
```
|
||||
|
||||
**静置观察**(≥ 4 分钟):`round` 持续递增、`argv0` 逐字等于本区 `collabd.py`、
|
||||
`已有活的常驻 ⇒ 本实例退出(幂等)` 出现一次(证明没双写)。
|
||||
|
||||
## 五、🔴 载体是**一个**永不返回的守护循环(⛔ 不是"两层")
|
||||
|
||||
⚠️ **旧版这里写的是"必须两层=计划任务 + 周期性复活排期",🔴 已被实测推翻** ——
|
||||
真正常态兜底**就在这个计划的守护循环里**,⛔ 不需要另一条排期。
|
||||
|
||||
**实测 2026-10-03**:单纯"计划任务跑一次脚本"**救不了"被外部收走"** ——
|
||||
测试3 常驻 `23:16:37` 起、`23:28:47` 停(活 12 分钟),而计划任务
|
||||
`LastRunTime` 仍 `23:17:03`、`LastTaskResult=0`、`State=Ready` ⇒ **它没重拉**。
|
||||
|
||||
**根因**:`RestartCount/RestartInterval` **只在本任务自己非零退出时生效**。
|
||||
而脚本里若是**前台阻塞**调用常驻,常驻死掉 ⇒ 脚本**正常返回 0** ⇒
|
||||
任务调度器判定「成功」⇒ **`Restart*` 永不触发**(测试3 复盘原话:「自愈机制被**成功退出**这三个字废掉了」)。
|
||||
|
||||
⇒ ✅ **正解=守护循环版脚本**(⛔ 别用"跑一次就退"):
|
||||
```
|
||||
while ($true) {
|
||||
if (Test-Path $stopFlag) { Say "…stands down"; exit 0 } # 🔴 见第五节之二
|
||||
if (心跳新鲜(<90s)) { 让位等待; continue } # 活性判据=心跳,⛔ 不是退出码
|
||||
Start-Process -Wait -PassThru pythonw collabd.py --supervise # 🔴 -Wait 必须
|
||||
# 退避 5→10→20→40→60s 封顶;每轮开头都再查一次 $stopFlag
|
||||
}
|
||||
```
|
||||
| 要点 | 为什么 |
|
||||
|---|---|
|
||||
| `while ($true)` 包住 | 任务永远 `Running` ⇒ 调度器**不介入**、自己管复活 |
|
||||
| 🔴 **`Start-Process -Wait`** | ⛔ **`&` 对 `pythonw.exe`(GUI 子系统)不阻塞**、立即返回 ⇒ 循环误判"刚起的已退出"⇒ **叠出多个常驻**(测试3 实测 5/10/20/40s 连续重拉) |
|
||||
| 活性判据=**心跳新鲜度**(`<90s`),⛔ 不是退出码 | `collabd` 发现已有活常驻会**幂等 exit 0** ⇒ 按退出码重拉会造出风暴 |
|
||||
| 退避封顶 60s | 真崩时别刷屏 |
|
||||
| `.NET Process` + `add_OutputDataReceived` | ⚠️ PowerShell 5.1 **无消息泵 ⇒ 回调不触发**、日志被静默吞掉 ⇒ 用 `Start-Process -RedirectStandardOutput` |
|
||||
|
||||
**实测效果**:23:40 第一次死亡 **5 秒恢复**;23:47 单变量 kill 演练 **6 秒恢复**、新 pid、`pythonw` 实例数 `count=1`(无叠加)。
|
||||
|
||||
### 之二 🔴🔴 守护循环**必须看停止标志**(2026-10-04 补,本文档初版漏了)
|
||||
|
||||
**症状**:目标已完成(`--set-life 已完成` ⇒ `supervise_stop()` 写下 `guard.stop`、常驻已优雅退出),
|
||||
**但守护循环仍在每 60 秒重拉一次、每次秒退** ⇒ 实测**空转 370 次/6 小时**
|
||||
(`keeper.log` 连续 `exited (exit=0, heartbeat unreadable)`)。
|
||||
|
||||
**根因**:守卫只问「心跳新鲜吗」,**⛔ 不问「目标还活着吗」** ⇒
|
||||
**"完成即收工"只做到"停掉常驻",没做到"别再拉它"** —— 那是同一件事的两半。
|
||||
|
||||
✅ **正解=循环开头(+退避等待中)都先查停止标志**,在则**退出循环**:
|
||||
|
||||
```powershell
|
||||
$stopFlag = "<工作区>\tmp\supervise-inbox\guard.stop"
|
||||
while ($true) {
|
||||
if (Test-Path -LiteralPath $stopFlag) { Say "guard.stop present => stands down"; exit 0 }
|
||||
... # 起常驻
|
||||
$waitLeft = $backoff
|
||||
while ($waitLeft -gt 0) { # 🔴 退避期间也可能出现标志
|
||||
if (Test-Path -LiteralPath $stopFlag) { Say "…stands down early"; exit 0 }
|
||||
Start-Sleep -Seconds 5; $waitLeft -= 5
|
||||
}
|
||||
}
|
||||
```
|
||||
📌 退出前**必须 `Say` 一行**(⛔ 否则又变成"静默消失",同族红线:读到了就要说)。
|
||||
|
||||
**实测(2026-10-04 07:03,单变量:只换脚本、没动任务)**:
|
||||
新 keeper 起来 **0 秒**识别标志 ⇒ `keeper stands down` ⇒ 任务 `State=Ready`(⛔ 不是 `Running`)
|
||||
⇒ 重拉次数 **372 → 372**(一没涨)⇒ **空转彻底止住**。
|
||||
⚠️ 验「是否被回收」必须**只做单一变量**(⛔ 不许同时停/起任务)—— 否则因果会搞错。
|
||||
|
||||
⚠️ **验收要静置 ≥ 12 分钟**(⛔ 短于 10 分钟证明不了任何事 —— 常驻就死在第 12 分钟)。
|
||||
|
||||
## 六、⚠️ 还没有它就不算长期(两条,别自欺)
|
||||
|
||||
1. **静默基准**:检查会话要 `静默 ≥ 20 分钟` 才建,而基准取台账 `tasks.json` 的 mtime
|
||||
⇒ **新建/改动任何排期都会把计时归零** ⇒ 刚派完棒必然等满 20 分钟(实测 0.2/4.3/…/12.6 分钟,**从未越过 20**)。
|
||||
2. **闸二会自锁(正确行为,不是 bug)**:`_all_sessions_idle()` 要求"所有会话都结束",
|
||||
而**发起这一切的那条会话自己也在里面** ⇒ 只要它活着,检查会话就建不出来。
|
||||
⇒ **要让检查会话跑起来,必须先结束发起会话**(fail-safe 方向=宁可不建,不误建)。
|
||||
|
||||
## 七、还有一条(已由机制收口,⛔ 别再自己写)
|
||||
|
||||
- **目标完成 ⇒ 关后台+执行检查**:`collabd.py --set-life 已完成` 现在**真的会停常驻**
|
||||
(`supervise_stop()`:先写 `guard.stop` 走优雅退出 → 等 45 s → 不死才 `taskkill /T /F`)。
|
||||
⛔ **受阻 / 已暂停不关**(口径如此)。
|
||||
- **"完成"可逆**:`ensure_supervise()` 续命前**清掉 `guard.stop`** ⇒ 新目标/目标调整后能再起来。
|
||||
⛔ 不清就永远起不来。
|
||||
- **跨区自愈**:本区常驻每 6 轮顺带扫一遍 `peer_workspaces`,**只补"目标进行中"的区**
|
||||
(已完成/受阻/有停止标志的 ⛔ 一律不拉)⇒ 但**计划任务是更靠前的第一道**。
|
||||
|
||||
## 八、按场景对照(选哪条 / 怎么验 / 常见坑)
|
||||
|
||||
| 场景 | 主会话怎么来 | 常驻载体 | 目标完成后谁关 | 常见坑 |
|
||||
|---|---|---|---|---|
|
||||
| **A · 用户手动创建主会话** | 用户自己开 `[主]-…`,盯着推进 | 计划任务 + 守护循环 | keeper 见 `guard.stop` → `exit 0` | 🔴 用户一收工就没人拉 ⇒ 若没装载体,常驻会在会话结束时**静默死掉**(看板还显示"在线") |
|
||||
| **B · 定时任务创建主会话** | 排期 `recurring` 到点拉 `[主]-…` | 同上(⛔ **排期代替不了载体**) | 同上 | 🔴 **`once` 排期过期即哑**(`next_run_at=None`、界面仍显示 ACTIVE)⇒ **静默窗内无人**;🔴 排期跑完即 `completed`,**下一跳之前是空窗** |
|
||||
|
||||
**共同判据(两种场景都一样,⛔ 每次改完都验)**:
|
||||
1. `LastTaskResult` = `0`(`1` ⇒ 秒退,八成是 BOM);
|
||||
2. 任务 `State` = **`Running`**(⛔ `Ready` 只说明"此刻没在跑",**不代表机制坏**,
|
||||
但要配合 ③ 才算稳);
|
||||
3. 心跳 `pid` 活 ∧ `ts` 距今 **< 90 s** ∧ `round` 递增;
|
||||
4. **静置 ≥ 12 分钟**再看 ③(⛔ 短观察证明不了事 —— 常驻常死在第 12 分钟);
|
||||
5. 目标完成后:keeper 日志出现 `stands down` + **重拉计数不再增长**(验"真的停了",
|
||||
⛔ 不许只看"心跳停了"—— 那可能是死了而不是收工)。
|
||||
|
||||
## 九、四个区各自的任务名(⛔ 名字别撞)
|
||||
|
||||
`collabd-supervise-<工作区名>`,例:`collabd-supervise-ws3`。
|
||||
⚠️ 一个区一个任务;⛔ 多个区**共用一个**任务名会被 `IgnoreNew` 挡掉第二个。
|
||||
|
||||
---
|
||||
📌 **本文档的事实全部来自实测**(计划任务状态/`LastTaskResult=0`/心跳 13 分钟/BOM 字节
|
||||
`239 187 191`/三条封死路的原始错误)。⛔ 任何"应该行得通但没实测"的写法都别往里加。
|
||||
Reference in new issue
Block a user