# 会话机制 · 规矩与判据(照它做,⛔ 别自己另发明) > ⚠️ 本文件是**作业规矩**;机制全貌 ⇒ `references/architecture.md`;坑 ⇒ `references/pitfalls.md`。 > 🔴 与项目层 `CODEBUDDY.md §1` 冲突时**以项目层为准**(那里有裁决顺序)。 ## 1 提问判据(唯一一条) > **只问「超过现有判断方法边界」的问题。** - **边界内 ⇒ 自决策,不要问**:技术选型 / 实现路径 / 命名与数据结构 / 调参 / 部署与同步 / 排查方法 / 版本依赖 / 兼容降级 / 方案取舍 / 文档技术内容。 **「部署上线」属此项 ⇒ 做完即上线,不要问**;生产变更**直接做**,只需**动手前一句话说明**。 - **边界外 ⇒ 必须问**:① 业务目标与优先级 ② 花钱与资源承诺 ③ 对外承诺 ④ 需用户提供的凭据或审批 ⑤ 无客观优劣的偏好 ⑥ 影响面超出本平台 ⑦ 红线门禁 ⑧ 判不准。 - **判据**:有没有**客观可判的优劣**?有 ⇒ 自决策;没有 ⇒ 问。 - **提报用户标准 = 存在真取舍**:候选**只有优点或只有缺点 ⇒ 自己拍掉**;各有优劣才提报用户,且**逐项写优点 + 缺点**。 - ⛔ **不许捆包**:要问红线**只问那一句**;技术方案自己定好、当**已定项**陈述。 - ⛔ **禁用征询句收尾**(「要我…吗 / 请确认 / 你看怎么办」)⇒ 按三问重判,没命中就**删掉、自己做完**。 - 🔴 **提报用户内容必须自包含**:① 一句话说清要决定什么(⛔ 不用指代)② 为什么要你定(影响谁 / 断多久 / 花多少钱)③ 每候选写优点 + 缺点,末行给倾向 ④ 一轮一问 ⑤ ⛔ 不出现包名 / 路径 / 变量名。 ## 2 回复排版(🔴 用户 2026-10-01 定稿,照抄即可) - **骨架**:`# 大类`(**已完成** / **待处理任务**)→ `## 任务名` → 每件事两段:**当前状态** + **待处理事项**。 - 🔴 **大类标题必须比任务名大一号**(大类用 `#`、任务名用 `##`;⛔ 不许同号,否则层级压平)。 - **当前状态**:每条一个**圆点**,**一句陈述句说重点**;复杂情况放**句末圆括号**。 - **待处理事项**:用**序号**(`1、2、`,⛔ 不是 `1.`);每条写完整句子。 - **附件**:写在该板块**最末**一行(`附件:<路径>`)。 - **大类顺序**:**已完成的放最前**,`待处理任务`放**最后**。 - **三禁**:⛔ 表格(=省略讲理)|⛔ 长散文(不是写小说)|⛔ 碎标签堆叠。 - ⚠️ 本条只管**给人读的回复**;注释 / 日志 / 解析字段不受限。 ## 3 锁 - **开工先抢锁**(是"抢"不是"看");**抢不到 ⇒ 停手 + 报告**(红线 R9:⛔ 不删锁、⛔ 不接管)。 - 🔴 **R9 的唯一例外(用户 2026-10-09 授权)**:「工作区项目机制对应的会话**异常**导致未解锁 ⇒ **检查会话**可自解除**孤儿锁**」。 判据**不靠猜**:`scripts/lock/orphan-lock.py` 去宿主库 `sessions` 表读持有者真身状态 —— **只有** `terminated`/`error`/`archived`,或 `completed` 且静默 ≥30 分钟 ⇒ 可解除;`working` ⇒ **一律不动**。 ⛔ 唯一执行面就是这支脚本(`--dry-run` 先看;⛔ 别自己写 `rm -rf` 删锁);⛔ 例外**只给检查会话**、**只覆盖这一种情形**。 - 🔴 **默认一律带域**(`--domains <本工作区域>/`);⛔ 别省略 —— 省略=**全局独占**,别人连域锁都抢不了。 - 🔴 **只有真正"全平台共用"的改动**(配置 / 加解密 / 隔离 / 锁与钩子**本身**、技能文件)才用**独占**。 - 🔴 **释放必须反序且带会话名**:`--release` → `--release-exec "<会话名>"`;⛔ 不带名 ⇒ 拒释放。 - 🔴 **锁的生命周期 = 任务的生命周期**:⛔ 禁抢锁做一半、不解锁就结束回合(带锁结束=把所有人挡在门外);中途要停 ⇒ 先释放。 - 🔴 **域键算法 shell/hook 必须逐字一致**,否则**静默失效**。 ## 4 日志闸(会话"活多久"的物理边界) - 本工作区闸值:文件**软 5 MiB / 硬 8 MiB**;工具调用**软 200 / 硬 250**。命中 ⇒ 开接续会话。 - 🔴 **宿主侧还有一个 10 MiB 硬上限**:撞顶即**拒写** ⇒ **界面静默哑掉、用户零感知**,而投递方仍记 `ok:true`(**假绿**)。 - 🔴 **病根是"调用次数多",不是"每次调用贵"**;增长只与"工具在跑"成正比(实测:活跃 ≈630 KB/分 ↔ 空闲 34 分钟 0 行)。 - **压增长三条硬纪律**:① 大输出先落盘、只读关键行 ② 命令层限流(`| head -30` / `grep -c` 代替裸 `grep`)③ 让脚本内部聚合、只 print 摘要。⛔ 禁 `cat` 大文件、无 `head` 的 `grep -r`。 - ⛔ **不要在会话里起常驻后台长跑任务**:它每次输出都把会话反复唤醒 ⇒ 永远回不到 idle ⇒ 用户看到"卡死"(已复现多次)。✅ 正确起法=**会话后台任务 + stdout 全重定向到文件**;或干脆做成**独立进程**(见 §6)。 ## 5 钩子与脚本的写法约束 - 🔴 **hook 脚本必须走 `sys.stdout.buffer.write(bytes)`** 输出 —— 否则文案含非 ASCII 符号时抛 `UnicodeEncodeError` ⇒ stdout 为空 ⇒ **静默放行**。 - 🔴 **不要按位置推导根目录**:钩子脚本被搬一次就会**静默指错**(历史事故:台账写到别处、测试却全绿)⇒ 用外置的 `roots.env`(本包由 `install.py` 生成)。 - ⚠️ `settings.json` 的 hook 条目**没有 `env` 字段** ⇒ ⛔ 别指望用 env 给钩子传参。 - 🔴 **作用域⛔ 不许静默排除**:被跳过必须**留下痕迹**(写跳过清单 + stderr),否则"没动作"与"坏了"长得一模一样。 - ⚠️ **钩子脚本内容改动即时生效**;但 `settings.json` 的 hooks 条目是**应用启动时快照** ⇒ 改条目要**完全重启**(关窗 ≠ 退出)。 ## 6 常驻的精确边界 - 🔴 **「协作与投递一直运行(常驻)」是已定案项**(09-29 定案,2026-10-01 用户再确认)⇒ ⛔ **不得拿"进程数=0"去否它**。 - 🔴 **⛔ 禁的是"在会干活的会话里起"**:会话自己的后台任务会**压制该会话的 idle 钩子**(实测被僵尸任务压 **6h20m**) ⇒ 载体应用**专用容器会话**;且**输出必须完全静默**(`stdout` 全重定向到文件,⛔ 否则反复唤醒宿主 ⇒ 看着像卡死)。 - ✅ **独立进程不在禁令内**(输出不接回任何会话 ⇒ 不唤醒宿主);但它**读不到网关口令 ⇒ 不能投递**,只能做只读判定 / 告警。 - **存活边界一句话**:「**后台任务跟着应用活,不跟着会话活**」⇒ 要"关了应用也还在"必须做成独立进程(⚠️ 但那就没口令了)。 ## 7 其他硬约束 - 🔴 **技术讨论不谈法规**:⛔ 不引条文当论据、⛔ 不主动提合规、⛔ 不前置提报用户;只在你问起、或对象就是"对外承诺 / 资质 / 合同"时才谈。 - 🔴 **红线 R5 / R7 / R9 / R10 永远硬约束**(内容见项目层 `CODEBUDDY.md §3` 红线全表)。 - 🎯 **要解决问题,不将就妥协**:降级 / 延期 / 静默兜底**都不算解决**;🟢 **只做正向迭代**。 - 🔴 **接手前人结论先做最小取证**,⛔ 别拿旧结论当既成事实;**取现状一律按 mtime 取最新那份**。 ## 8 🔴 验证通过的机制 ⇒ **必须写进技能**(2026-10-05 用户明令) > 用户原话:「**这个也是会话规则,验证通过的机制要写进技能中**」。 - 🔴 **一条机制实测跑通之后,落点只能是技能**(`SKILL.md` 或 `references/`),⛔ **不许只留在工作区日志或当次对话里**。 - **判据**:换一个新会话、**不读任何历史**,只看技能能不能照着做出来。做不到 ⇒ 等于没立。 - **⚠️ 写的位置决定会不会被读到**:`SKILL.md` 第一屏/「必读三篇」/索引表 ⇒ **每次都会读到**;长文档正文底部 ⇒ 基本没人会翻到(等于没写)。 - ⛔ **反例(10-05,同一件事栽到第四次)**:常驻的起法只写在当天日志里 ⇒ 之后每一轮都重新折腾一遍、每一轮都得出同样的结论。 ✅ 正解:结论**同时**落三处 —— `SKILL.md` 第一屏(会被读)+ `pitfalls.md` 一条(有来龙去脉)+ 本档一条规矩(约束以后怎么写)。 - 🔴 **配套**:凡"换会话容易忘、忘了就会重来"的结论,**优先放第一屏**,⛔ 不要塞进长文档。 ## 9 🔴 只写正向范围,⛔ 不写「不用于 XXXX」(2026-10-07 用户令) **技能与文档的「用途/范围」段一律写"用于什么"**,⛔ 不列"不用于 A/B/C"。 - **为什么(机制性,不只"占地方")**:技能的 `description` 与首屏是**自动匹配面**(技能靠描述匹配被加载) ⇒ 在里面列"不用于 X",等于**把 X 这些词喂进匹配面** ⇒ **反而更容易被不该命中的请求选中** (实测形态:`draw-ui` 描述里列了「不用于普通插画/海报/故事板」⇒ 用户说"做个海报"时它更可能被选上)。 - **例外(用户 2026-10-07 明确给出)**:**经验沉淀可以写"如何避免踩坑"** —— 禁令**不删**,但写成「**正向目标 + 依据(曾栽过什么)**」的形态 (例:「本方法只出产品视角」+「曾因照英文商业框架产出而跑偏」)。 - ⛔ **别与"事实陈述"混**:「配置里不含本工作区」「该目录不含 `board.py`」是在**描述事实**, 不在本条管辖内 —— 一刀切会把它们误删。 ## 10 🔴 遇阻碍 ⇒ 把目标状态改成「阻碍」⇒ 机制暂停 ⇒ 让用户决策(2026-10-09 用户令) **口径**(用户原话:「**其他类型 阻碍 遇到阻碍修改目标状态 暂停机制 让用户决策**」): 阻碍**解不了**时,正确动作不是反复重试,而是**改目标状态把机制停住、交回用户**。 - **什么叫"阻碍"(三条全中)**:① 队列里那条 `blocked` **确认解不了** (含跑过孤儿锁判定仍判「不动」的);② 它是在**等人**,不是等某个会话;③ 原因已写进台账与 `NEED-USER.md`。 - **谁去做**:🔴 **结果检查会话** —— 它是**唯一能看见 `blocked`** 的那条腿(靠"队列非空"触发); 「目标检查会话」虽有权改状态,却要**队列为空**才触发 ⇒ 队列里躺着 `blocked` 时它**永不触发**。 ⇒ 不给结果检查这一个出口,两条腿会互相锁死、只剩空转(2026-10-09 `contentm_agent` 实测: 连开 **11 棒、约 5 小时、零派活**,每 30 分钟一棒)。 - **怎么做**:`collabd.py --set-life 阻碍 --by "<会话名>" --check-why "<一句现状>"` ⇒ 闸① 随即生效、**不再建检查棒**(这就是"暂停机制")。 ⚠️ 「为什么」的旗标是 **`--check-why`**,⛔ 不是 `--why`(那是 `--retire` 的,写错会被**静默忽略**)。 ⚠️ 写完**必须回读** `goal.json` 确认(本项目栽过「命令不存在却 rc=0 静默放行」)。 - **怎么恢复**:用户处置完 ⇒ **主会话**跑 `--set-life 进行中` ⇒ 机制自动复跑。 ⛔ 阻碍时**不关**后台任务与协作程序(用户口径第②条),只是不派活、不建检查。 - ⛔ **别越界**:「已完成」不归结果检查 —— 那是「目标检查会话」的活(判据是完成度,不是队列)。