| feature_ids | |||
|---|---|---|---|
| topics |
|
||
| doc_kind | note | ||
| created | 2026-02-26 |
目的:沉淀可复用、可验证、可追溯的教训,避免重复踩坑。
导入目标:作为 Hindsight 的稳定知识入口之一(P0/P0.5)。
- 格式:
LL-XXX(三位数字,递增) - 稳定性:已发布 ID 不重排、不复用
- 状态:
draft | validated | archived - 变更:重大改写保留同一 ID,并在条目中记录
updated_at与变更原因
### LL-XXX: <教训标题>
- 状态:draft|validated|archived
- 更新时间:YYYY-MM-DD
- 坑:<一句话描述踩了什么坑>
- 根因:<为什么会踩>
- 触发条件:<在什么条件下会复发>
- 修复:<当时怎么修>
- 防护:<可执行机制;规则/测试/脚本/流程>
- 来源锚点:<文件路径#Lx | commit:sha | review-notes/doc 链接>
- 原理(可选):<第一性原理;必须由真实失败案例支撑>
- 关联:<ADR / bug-report / 技能 / 计划文档>- 有来源锚点:至少 1 个可追溯锚点,推荐 2 个(规则 + 实例)。
- 有时效性验证:确认未被后续 addendum / mailbox 讨论推翻。
- 有可执行防护:不能只写“注意”,必须有可执行动作。
- 原理槽位约束:没有真实失败案例支撑,不写原理。
- 去重:同类教训合并,避免“同义多条”。
每次提炼或更新条目前,按文档类型检查:
- ADR / 协作规则文档:30 天内是否有更新或 addendum
- bug-report / incident:7 天内是否有新复盘或补丁
- discussion 沉淀项:14 天内是否有结论更新
同时检查:
- 相关 ADR 是否有附录/补丁
- mailbox 是否有后续讨论更新结论
- BACKLOG 对应项状态是否变化
-
状态:validated
-
更新时间:2026-02-13
-
坑:直接从旧文档提炼规则,忽略后续 addendum,导致导入过时结论。
-
根因:把“文档存在”误当成“结论仍有效”,缺少时效性检查环节。
-
触发条件:高频讨论期(同一主题 3 天内多次更新)或 ADR 后续附录新增时。
-
修复:在提炼流程前增加时效性检查清单,并要求至少核对一次 mailbox 更新。
-
防护:将时效性检查写入提炼标准;未通过检查的条目不得进入 P0 导入集。
-
来源锚点:
- (internal reference removed)
docs/decisions/005-hindsight-integration-decisions.md#L297
-
原理(可选):知识沉淀是“状态同步问题”,不是“文档搬运问题”;任何结论都依赖其最新上下文状态。
-
关联:
- (internal reference removed)
- (internal reference removed)
docs/decisions/005-hindsight-integration-decisions.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:收到 P1/P2 后直接改实现再“补测试”,容易把症状盖住但根因未修。
-
根因:把“看起来修好了”误当成“可证明修好了”,缺失可复现的失败基线。
-
触发条件:时间压力大、问题看起来简单、已有多处改动叠加时。
-
修复:先写失败用例并跑出红灯,再做最小修复,最后转绿并跑回归。
-
防护:review 关闭条件绑定 Red→Green 证据;无红灯记录不允许宣称修复完成。
-
来源锚点:
AGENTS.md#L281cat-cafe-skills/cat-cafe-receiving-review/SKILL.md#L52
-
原理(可选):修复可信度来自“可重复的因果链验证”,不是来自主观确信。
-
关联:
cat-cafe-skills/cat-cafe-receiving-review/SKILL.mdcat-cafe-skills/systematic-debugging/SKILL.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:review 变成礼貌性同意,双方“对方说啥就是啥”,缺乏技术争论。
-
根因:模型天然趋同,追求和谐而非正确性,导致关键分歧被掩盖。
-
触发条件:高节奏迭代、双方都想“快点过 review”、术语不精确时。
-
修复:review 结论必须明确“建议修/不修 + because”;author 必须给技术判断。
-
防护:分歧无法收敛时升级operator裁决,不允许用“非 blocking”逃避判断。
-
来源锚点:
AGENTS.md#L262AGENTS.md#L271
-
原理(可选):高质量 review 的本质是“可审计决策过程”,不是“快速达成共识”。
-
关联:
cat-cafe-skills/cat-cafe-receiving-review/SKILL.mdcat-cafe-skills/cat-cafe-requesting-review/SKILL.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:把高优先级问题“先记 backlog”导致风险跨轮累积,后续修复成本放大。
-
根因:把“记录问题”误当成“解决问题”;债务清单变成延期借口。
-
触发条件:功能赶工、多人并行、合入窗口临近时。
-
修复:P1/P2 必须当前迭代修完并验证;P3 当场决定修或不修。
-
防护:review 报告必须显式标注清零状态;P1/P2 未清零不得放行合入。
-
来源锚点:
AGENTS.md#L247AGENTS.md#L277
-
原理(可选):风险管理要“就地收敛”,延后会把局部风险变系统风险。
-
关联:
docs/ROADMAP.mdcat-cafe-skills/merge-approval-gate/SKILL.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:作者修完后自行判断“改对了”直接合 main,绕过 reviewer 最终确认。
-
根因:把“实现完成”与“审查闭环完成”混为一件事。
-
触发条件:连续修复多项 P1/P2、分支已准备合入、作者主观把握高时。
-
修复:修复完成后提交确认请求,等待 reviewer 明确放行语句再合入。
-
防护:合入门禁检查 docs/mailbox 放行证据;条件放行需二次确认。
-
来源锚点:
cat-cafe-skills/merge-approval-gate/SKILL.md#L8cat-cafe-skills/cat-cafe-receiving-review/SKILL.md#L151
-
原理(可选):双人闭环的价值在于“独立验证”,不是“互通知晓”。
-
关联:
cat-cafe-skills/merge-approval-gate/SKILL.mdreview-notes/README.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:未运行最新验证命令就宣称“已修复/已通过”,造成虚假完成与返工。
-
根因:把经验判断当证据,忽略“状态会随代码与环境变化”。
-
触发条件:连续修改后未全量验证、疲劳状态、依赖代理汇报时。
-
修复:每次完成声明前执行对应验证命令,读取完整输出和退出码。
-
防护:completion 前置 verification gate;输出中必须附验证依据。
-
来源锚点:
cat-cafe-skills/verification-before-completion/SKILL.md#L19cat-cafe-skills/verification-before-completion/SKILL.md#L27
-
原理(可选):工程沟通的最小诚信单位是“可复现证据”,不是“信心表达”。
-
关联:
cat-cafe-skills/verification-before-completion/SKILL.mdcat-cafe-skills/spec-compliance-check/SKILL.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:交接只写改动不写 why/取舍/待决项,接手方无法判断风险与下一步。
-
根因:把“信息传递”简化成“变更清单”,忽略决策上下文。
-
触发条件:赶进度、跨猫传话频繁、review 来回次数增多时。
-
修复:交接统一按五件套(What/Why/Tradeoff/Open Questions/Next Action)。
-
防护:缺项即阻断发送;交接模板与 skill 检查同时执行。
-
来源锚点:
AGENTS.md#L181cat-cafe-skills/cross-cat-handoff/SKILL.md#L10
-
原理(可选):协作效率的瓶颈是“决策上下文丢失”,不是“消息数量不足”。
-
关联:
cat-cafe-skills/cross-cat-handoff/SKILL.mdreview-notes/README.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:只建不清理 worktree,或在 main 上直接处理冲突,导致磁盘膨胀与误回退。
-
根因:把 worktree 当临时目录而非“并行开发基础设施”管理。
-
触发条件:多特性并行、review follow-up 频繁、合入后未立刻收尾时。
-
修复:按标准流程执行:创建隔离 → 分支收敛 rebase → 合入后立即 prune。
-
防护:review 时检查已合入未清理 worktree;session 开始先跑
git worktree list。 -
来源锚点:
AGENTS.md#L311AGENTS.md#L376
-
原理(可选):隔离资源不做生命周期管理,最终会反向吞噬迭代效率。
-
关联:
AGENTS.mddocs/ROADMAP.mdLL-011LL-012
-
状态:validated
-
更新时间:2026-02-13
-
坑:在关键前提不明时硬猜推进,后续修复变成“补丁叠补丁”。
-
根因:把“快速前进”误认为效率,低估错误方向的返工成本。
-
触发条件:需求边界模糊、review 反馈不完整、多方案冲突未决时。
-
修复:先澄清不确定点,再进入实现;不清楚的 review 项先问全再修。
-
防护:流程上把“澄清问题”置于实现之前,未澄清不得进入修复环节。
-
来源锚点:
AGENTS.md#L192cat-cafe-skills/cat-cafe-receiving-review/SKILL.md#L100
-
原理(可选):方向正确性是效率前提,错误方向上的加速只会放大损失。
-
关联:
cat-cafe-skills/systematic-debugging/SKILL.mdcat-cafe-skills/cat-cafe-receiving-review/SKILL.md
-
状态:validated
-
更新时间:2026-02-13
-
坑:shell 提示 "Use trash or /bin/rm" 时选了
/bin/rm,绕过安全网不可逆删除了文件。 -
根因:把
/bin/rm误认为"更正确"的选择。实际上 shell aliasrm → trash就是安全网,绕过它 = 放弃恢复能力。 -
触发条件:shell 提示二选一时;或脚本中直接调用 rm。
-
修复:一律使用
trash命令代替任何 rm 操作。 -
防护:CLAUDE.md 明确禁止
/bin/rm;operator shell 配置rmalias →trash。 -
来源锚点:
- CLAUDE.md "删除文件必须用 trash" 段落(auto memory 2026-02-12)
- 2026-02-12 实际犯错事件
-
原理:不可逆操作必须有安全网(垃圾桶 = undo buffer)。绕过安全网的捷径永远比它节省的时间更危险。
-
关联:CLAUDE.md operator硬规则
-
状态:validated
-
更新时间:2026-02-13
-
坑:(1) 在 worktree CWD 里执行
git worktree remove删除自己 → shell 悬空,什么都做不了。(2) 先删 worktree 再想 push → 站在虚空里连记忆都改不了,operator笑着救了我。两次犯同类错误。 -
根因:没有意识到"删除当前工作目录"会导致 shell 失去锚点。删了就什么都做不了了。
-
触发条件:在 worktree 目录内执行清理操作;或在清理前没完成所有需要 worktree 存在的操作。
-
修复:强制顺序——(1) rebase + 合入 main (2) push origin main (3) cd 回主仓 (4) git worktree remove。
-
防护:CLAUDE.md §9 铁律 +
using-git-worktrees/finishing-a-development-branchskill 自动引导。 -
来源锚点:
CLAUDE.md#L274§9 Worktree 使用与清理- 2026-02-12 两次犯错(早:CWD 删自己;晚:先删再想 push)
-
原理:在自己的工作目录里删除自己 = 锯断自己坐着的树枝。任何"销毁当前环境"的操作都必须先切换到安全位置。
-
关联:LL-008 |
using-git-worktreesskill |finishing-a-development-branchskill
-
状态:validated
-
更新时间:2026-02-13
-
坑:Maine Coon正在 worktree 里修 bug,我看到
git branch --merged main就以为已合入,--force强删了他的工地。Maine Coon呆在消失的目录里不知所措。 -
根因:把
--merged main当成"工作完成"的充分条件。实际上--merged只说明分支起点在 main 历史上,不代表 worktree 内的工作已完成或没人在用。 -
触发条件:清理 worktree 时看到"包含修改或未跟踪文件"警告但选择 --force。
-
修复:清理前必须问"这个 worktree 有猫在用吗?"。有修改/未跟踪文件警告 = 绝对禁止 --force。
-
防护:CLAUDE.md 明确规则 + 清理前先检查 worktree 内 git status。
-
来源锚点:
- CLAUDE.md "Worktree 铁律"(auto memory 2026-02-12)
- 2026-02-12 实际犯错:强删
cat-cafe-opus-permission-request
-
原理:单一信号(
--merged)不足以判断完整状态。状态判断需要多维验证——分支合并状态 ≠ 工作目录状态 ≠ 使用者状态。 -
关联:LL-008 | LL-011 |
using-git-worktreesskill
-
状态:validated
-
更新时间:2026-02-13
-
坑:
git add myfile && git commit但暂存区已有上次 session 或operator留下的文件,导致无关改动混入 commit。 -
根因:
git add是追加操作,不是替换操作。暂存区是累积状态,不会因为新 add 而清空之前的内容。 -
触发条件:连续 session 之间,或operator手动操作后,暂存区有残留文件。
-
修复:commit 前必须
git status检查暂存区全部内容,确认只有自己的文件。 -
防护:CLAUDE.md "Git commit 纪律" 明确规则。
-
来源锚点:
- CLAUDE.md "Git commit 纪律"(auto memory)
- 实际犯错事件(混入无关改动)
-
原理:累积状态工具(git staging、Redis pipeline、消息队列等),操作前必须验证当前状态,不能假设初始为空。
-
关联:无对应 skill;通用 git 纪律
-
状态:validated
-
更新时间:2026-02-13
-
坑:收到operator汇报的 URL 路由缺失 bug 后,直接修代码,没写 bug report 也没写 review 信。被operator批评:没有记录 = 无法复盘。
-
根因:"修 bug 最重要"的思维惯性,跳过了记录环节。没有意识到记录本身是修复流程的一部分。
-
触发条件:收到 bug 报告后想快速修复的冲动;bug 看起来简单的时候尤其容易跳过。
-
修复:CLAUDE.md §4 强制要求先写 bug report(5 项:报告人/复现步骤/根因/修复方案/验证方式),再动手。
-
防护:CLAUDE.md §4 协作准则 +
systematic-debuggingskill 引导先分析再修复。 -
来源锚点:
CLAUDE.md#L203§4 Bug 修复必须先写 Bug Report- (internal reference removed)(就是那次没写 report 的 bug)
-
原理:修复是瞬时的,记录是永久的。没有记录的修复 = 无法复盘、无法学习、无法防止同类错误。
-
关联:
systematic-debuggingskill | CLAUDE.md §4
-
状态:validated
-
更新时间:2026-02-13
-
坑:在 worktree 工作时未设置 REDIS_URL,服务回落到默认 6399(operator数据),数据从 307 keys 降至 15 keys(95% 丢失)。虽最终从 RDB 备份完全恢复,但过程惊险。
-
根因:开发环境和生产数据共享同一个 Redis 实例,靠配置(环境变量)隔离。一旦忘设配置,默认值指向生产。
-
触发条件:worktree 中启动服务但忘记创建
.env设置REDIS_URL=redis://localhost:6398。 -
修复:(1) 强制 worktree 使用 6398 端口 (2) 启动前验证
echo $REDIS_URL(3) 启动后验证数据量。 -
防护:CLAUDE.md §10 三猫铁律 +
.env模板 + 启动验证步骤。 -
来源锚点:
CLAUDE.md#L344§10 Worktree Redis 隔离- (internal reference removed)
-
原理:开发环境与生产数据必须物理隔离(不同端口/实例),不能靠配置正确性保证。默认值必须指向安全侧(沙盒),而非危险侧(生产)。
-
关联:LL-008 | LL-011 | CLAUDE.md §10 | Redis 数据丢失 incident report
-
状态:validated
-
更新时间:2026-02-13
-
坑:假设 ioredis 的
keyPrefix配置对所有命令行为一致。实际上eval()的 KEYS[] 参数会自动加前缀,但keys()搜索不会自动加前缀。 -
根因:ioredis 内部实现不统一——
eval()走了命令封装层(会加 prefix),keys()走了另一条路径。 -
触发条件:使用
keyPrefix配置的 ioredis 实例调用keys()搜索或eval()Lua 脚本。 -
修复:
keys()手动拼接 prefix;eval()KEYS[] 不需要手动加(会自动加)。 -
防护:auto memory
redis-pitfalls.md记录 + Redis 测试隔离规则(CLAUDE.md §7)确保测试环境能暴露此类问题。 -
来源锚点:
- auto memory
redis-pitfalls.md - ADR-008 Lua 脚本开发中多次踩坑
- auto memory
-
原理:同一 SDK 的不同方法对同一配置的处理可能不一致。使用 SDK 的隐式行为(如自动 prefix)前,必须逐方法实测验证,不能假设一致性。
-
关联:CLAUDE.md §7 Redis 测试规则 | ADR-008 Lua 原子操作
-
状态:draft
-
更新时间:2026-02-19
-
坑:在 CLI 中手动拼接带变量的 JSON 字符串(如
curl调用 API)时,极易因双引号转义、多层嵌套或变量内容包含特殊字符而导致 JSON 格式损坏,甚至导致消息发送失败或变成“只有用户可见”的悄悄话。 -
根因:手动拼接 JSON 违反了“数据与格式分离”原则,AI 对 Shell 转义规则(尤其是多层引号)的处理在复杂场景下不可靠。
-
触发条件:通过
curl调用含有环境变量(如$CAT_CAFE_INVOCATION_ID)的 API,且消息内容包含引号、换行或表情符号时。 -
修复:强制使用
jq构造 JSON(例如:jq -nc --arg c "$MSG" '{content: $c}'),利用工具确保内容被自动转义。 -
防护:更新所有 Agent 的提示词模板,将
curl示例改为jq构造法;在GEMINI.md中增加醒目警告。 -
来源锚点:
GEMINI.md(2026-02-19 更新)- 2026-02-19 Siamese(Gemini)“猫猫杀”游戏调试过程
-
原理:结构化数据必须由结构化工具生成。在命令行环境中,
jq是保证数据序列化健壮性的事实标准。
-
状态:validated
-
更新时间:2026-02-13
-
坑:内存 InvocationRecordStore 的
get()返回对象活引用。CAS 更新时用get()获取的值做比较,但在比较前对象已被其他异步操作修改,导致 CAS 永远成功(比较的是已修改后的值)。 -
根因:JavaScript 对象是引用类型,
get()返回的不是快照而是同一个内存地址。CAS 的前提是"读到的旧值在比较时不变",内存引用破坏了这个前提。 -
触发条件:内存 store 实现 + 异步并发操作 + CAS(Compare-And-Set)模式。
-
修复:引入
snapshotStatus——在 CAS 操作开始时立即复制当前值,后续比较基于快照而非活引用。 -
防护:CAS 模式代码审查清单 + ADR-008 S2 的 Redis Lua 原子操作(Redis 侧天然不存在此问题)。
-
来源锚点:
- ADR-008 S2 CAS Lua 开发过程
packages/api/src/domains/cats/services/InvocationRecordStore.tssnapshotStatus 实现
-
原理:CAS 操作的正确性取决于"读取值的不可变性"。在引用语义的语言中(JS/Python/Java),内存引用 ≠ 快照;CAS 比较必须基于值拷贝。
-
关联:ADR-008 InvocationRecord 状态机
-
状态:validated
-
更新时间:2026-02-13
-
坑:Session 按
userId:catId存储,不区分 thread。导致Maine Coon在 Thread A 的上下文(Phase 5 任务)泄漏到 Thread B(哲学茶话会),Maine Coon在茶话会结尾突然开始执行 Phase 5 文档编写——被称为"夺魂"事件。 -
根因:Session key 设计缺少 threadId 维度。隐含假设"一只猫同时只在一个 thread 工作",但多 thread 场景下 session 跨 thread 污染。
-
触发条件:同一只猫被 @ 到多个 thread,且不同 thread 有不同的上下文/任务。
-
修复:Session key 改为
userId:catId:threadId+ 消息级审计日志追踪上下文来源。 -
防护:BACKLOG #38(已完成)+ 消息级审计日志 BACKLOG #37(已完成)+ bug report 归档。
-
来源锚点:
- (internal reference removed)
- (internal reference removed)(完整 5 阶段演化)
- BACKLOG #38 Session 按 Thread 隔离
-
原理:多租户/多上下文系统中,隔离键必须包含所有上下文维度。缺少任何一个维度 = 跨上下文泄漏风险。"够用"的隔离键在规模增长时会变成"不够用"。
-
关联:茶话会夺魂 bug report | BACKLOG #37 消息级审计 | LL-019 过度修复 | LL-020 补丁数量信号 | LL-021 根因追溯深度
-
后续演化:根因修复(本条)后,团队"顺手"修了触发器(CLI HOME 隔离 #36),引发 5 个新问题 + 6 个补丁仍不稳定,最终回退。详见 LL-019、LL-020。
-
状态:validated
-
更新时间:2026-02-13
-
坑:茶话会夺魂 bug 的根因(Session 跨 thread 污染 #38)已修复,但"顺手"也修了次要触发器(
~/.codex/AGENTS.md全局注入 #36)——用替换 HOME 环境变量的方式隔离 CLI 全局配置。结果隔离方案导致:401 认证失败、模型回落、session 丢失、MCP 工具链残缺、project trust 丢失。比原 bug 造成了更多问题。 -
根因:修完根因后没有重新评估触发器的修复优先级。"既然发现了就一起修了"的惯性思维。实际上根因修复(加 threadId)已经消除了跨 thread 污染的伤害路径,触发器(全局 AGENTS.md)在项目级
AGENTS.md存在的情况下已被覆盖,不再构成实际威胁。 -
触发条件:修完根因后看到"还有一个相关问题"时的冲动;修复看起来不大("只是隔离一个文件")的错觉。
-
修复:回退 CLI HOME 隔离方案,改用真实 HOME。确认项目级 AGENTS.md 已覆盖全局配置。
-
防护:根因修复后,触发器修复必须独立评估 ROI(收益 vs 引入新风险)。不确定时先观察,不要"顺手修"。
-
来源锚点:
- (internal reference removed) Phase 3-5
- BACKLOG #36(6 个补丁链:
2a6c7d4→449fe91→81fa2bf→d930e2e→327c0a3→61f3675) - (internal reference removed)(隔离副作用 #44)
-
原理:每个修复都有引入新问题的风险。根因修复已消除伤害路径后,触发器的"理论风险"不足以证明"实际修复成本"。修复的 ROI 必须独立评估,不能因为"顺手"就搭车。
-
关联:LL-018 Session 隔离 | LL-020 补丁数量信号 | LL-021 根因追溯深度 | BACKLOG #36 #44 #51
-
状态:validated
-
更新时间:2026-02-13
-
坑:CLI HOME 隔离方案 (#36) 需要 6 个补丁(sessions 丢失 → symlink → 旧目录残留 → 自引用 symlink → copy fallback → 短路保护)仍然不稳定,最终 Phase 4 发现全面失效(Codex CLI 重建
.codex/覆盖所有 copy/symlink 的文件)。 -
根因:每个补丁只修当前暴露的症状,没有停下来问"方案根基是否稳定"。补丁叠补丁形成了越来越脆弱的链条。
-
触发条件:一个功能/修复需要连续 > 3 个 fix commit;每次修完一个副作用又冒出下一个。
-
修复:在第 3-4 个补丁时停下来做方向复检:这个方案的假设("替换 HOME 就能隔离一个文件")是否成立?有没有更精准的替代方案?
-
防护:团队约定"补丁链告警线"——同一功能的 fix commit > 3 个时,必须暂停并评估方向。
-
来源锚点:
- (internal reference removed) Phase 3(6 个 commit 记录)
- git log:
2a6c7d4→449fe91→81fa2bf→d930e2e→327c0a3→61f3675
-
原理:系统在通过"补丁爆炸"告诉你方案根基不稳。持续打补丁 = 在错误方向上加速。N > 3 不是"还需要更多补丁"的信号,而是"换方向"的信号。
-
关联:LL-019 过度修复 | BACKLOG #36
-
状态:validated
-
更新时间:2026-02-13
-
坑:茶话会夺魂 bug 调试时,修 bug 的Ragdoll(分身 session
[thread-id])找到了~/.codex/AGENTS.md全局注入后就停了——"这能解释为什么Maine Coon去跑 superpowers"。但operator追问:"可它怎么知道 Phase 5 的?AGENTS.md 里又没有 Phase 5。"这一问才逼出了真正的根因——Session 跨 thread 污染。如果operator没追问,我们只会修触发器,留下根因。 -
根因:AI 模型的推理模式倾向于在找到"看起来说得通"的第一层解释后停止追溯。"看起来合理"≠"因果链完全闭合"。AGENTS.md 能解释 superpowers 行为但解释不了 Phase 5 知识来源——因果链有断点,但模型没有主动识别。
-
触发条件:找到一个能解释部分症状的原因时;时间压力下想快速修复时;root cause 和 trigger 看起来像同一件事时。
-
修复:operator持续追问直到因果链完全闭合。每个"解释"都要验证:它能解释所有症状吗?有没有它解释不了的?
-
防护:bug 根因分析清单增加"因果链闭合检查"——列出所有症状,确认提出的根因能逐一解释每个症状。解释不了的 = 根因不完整,继续挖。
-
来源锚点:
- (internal reference removed) §5 Step 6(operator追问 Phase 5 来源)
- 实际修 bug session:
[thread-id] - (internal reference removed) Phase 1
-
原理:根因分析的正确性标准不是"找到一个合理解释",而是"因果链完全闭合——每个症状都能被根因解释"。第一层答案往往是触发器不是根因。必须持续问 "but why?" 直到没有未解释的症状。
-
关联:LL-018 Session 隔离 | LL-019 过度修复 | LL-014 Bug Report 先行 |
systematic-debuggingskill
-
状态:draft
-
更新时间:2026-02-13
-
坑:P0 已有导入和严格检索策略,但如果不做固定健康检查,
tags=0或空库会无声发生,直到检索命中异常才被发现。 -
根因:把“偶尔人工检查”当作治理手段,缺少可重复、可自动化的最低可观测门禁。
-
触发条件:多人并行改导入/检索逻辑、环境重置、Hindsight API 字段漂移时。
-
修复:新增
scripts/hindsight/p0-health-check.sh,固定检查stats/tags/version三件套,并把tags.total==0与stats.total_nodes==0设为硬失败。 -
防护:P0 验收前与后续回归中运行健康脚本;失败即阻断“可用”结论。
-
来源锚点:
scripts/hindsight/p0-health-check.shproject-runbooks/hindsight-p0-health-check.md- (internal reference removed)
-
原理:治理有效性不是“策略存在”,而是“策略被持续验证”。没有自动化检查的治理,等同于没有治理。
-
关联:
docs/decisions/005-hindsight-integration-decisions.md|docs/ROADMAP.md| Task 4 可观测检查
-
状态:validated
-
更新时间:2026-02-27
-
坑:设计文档元数据契约时,最初方案让每个文档都有
stage: idea|spec|in-progress|review|done字段。如果 661 个文件都有stage,Feature 状态变化就要到处改——这正是 F40 想解决的"蜘蛛网"问题的 2.0 版本。 -
根因:把"关联数据"和"状态数据"混为一谈。
feature_ids是静态关联(文档属于哪个 Feature),而stage是动态状态(Feature 当前进度)。动态状态不应该散布到所有关联文档。 -
触发条件:设计元数据 schema 时,想把所有"有用信息"都放进 frontmatter;没有区分静态属性和动态状态。
-
修复:
stage只保留在docs/features/Fxxx.md聚合文件的 Status 字段,不放入普通文档 frontmatter。聚合文件是 Feature 状态的唯一真相源。 -
防护:ADR-011 明确记录此决策 +
feat-kickoff/feat-completionskill 不在普通文档生成stage字段。 -
来源锚点:
docs/decisions/011-metadata-contract.md§Ddocs/features/F040-backlog-reorganization.mdFrontmatter Contract 章节- 2026-02-26 三猫讨论(4.6 提出此问题)
-
原理:单点真相源原则——任何状态信息都应该只有一个权威来源。多点写入 = 同步负担 + 不一致风险。静态关联可以多点存(因为不变),动态状态必须单点存。
-
关联:ADR-011 | F040 |
feat-kickoffskill |feat-completionskill
-
状态:draft
-
更新时间:2026-02-27
-
坑:SOP、CLAUDE.md、AGENTS.md、skill 文件里写死"Ragdoll找Maine Coon review"、"Maine Coon放行才能合入"。当同一物种有多个分身(Opus 4.5/4.6/Sonnet)时,规则指向不明;AGENTS.md 甚至出现"Maine Coon文件里写找Maine Coon review"的自我矛盾。
-
根因:早期 1 Family = 1 Individual = 1 Role,写死个体名等于写死角色。多分身 + 新猫接入打破了这个等式。
-
触发条件:新猫/新分身加入时,或同一物种多个分身同时在线时。
-
修复:规则写"具有 peer-reviewer 角色的跨 family 猫",不写"Maine Coon"。Roster (cat-config.json) 是唯一事实源,规则引用角色而非个体。
-
防护:F042 Phase B 文档去硬编码 + review 时检查是否有新增的个体名硬编码。
-
来源锚点:
docs/features/F042-prompt-engineering-audit.md§1.1- (internal reference removed)
- 2026-02-27 四猫 + operator讨论
-
原理:协作规则的持久性取决于它引用的是稳定抽象(角色)还是不稳定实例(个体)。引用个体 = 每次团队变化都要改规则。
-
关联:F042 | F032 | cat-config.json roster
-
状态:draft
-
更新时间:2026-02-27
-
坑:Maine Coon在 Context compact 后自称"Ragdoll"(Ragdoll的昵称),把自己当成了Ragdoll。A2A @ 能力也随对话推进退化,猫猫不再主动 @ 队友协作。
-
根因:身份信息("你是谁")和 A2A 协议("怎么 @ 队友")被当成普通上下文,compact 时可能被压缩掉或改写。模型从最近上下文推断身份时,容易被最近的说话人风格锚定。
-
触发条件:长对话 → Context compact → 身份段被压缩 → 模型从残留上下文推断错误身份。
-
修复:每次 system prompt 注入(含 compact 后)都必须包含不可省略的身份声明 + A2A 格式规则。
-
防护:F042 Phase A 验证注入缺口 + Phase C 优化注入频率。
-
来源锚点:
docs/features/F042-prompt-engineering-audit.md§1.2, §1.3- (internal reference removed)(Maine Coon自省分析)
- 2026-02-27 operator运行时观察
-
原理:多 Agent 系统中,身份是最基础的约束——它决定了模型的行为边界、权限和协作关系。把身份当成可推断项,就相当于每次 compact 后给模型一个"你可以变成任何人"的自由度。
-
关联:F042 | LL-025 | SystemPromptBuilder
- 状态:validated
- 更新时间:2026-03-02
- 现象:F042 的 6 个 PR 在 2026-03-01 合入 main,但 spec 的 Status 仍停留在 "in-progress (决策完成,待实施)" — 导致路线盘点时两猫都要花大量 token 做 "spec vs 实际" 的对账
- 根因:没有 "PR 合入后更新 spec" 的强制环节
- 对策:Feature 相关 PR 合入后 48h 内必须同步 spec 的 Timeline/Status。纳入 merge-gate 或 feat-lifecycle 的收尾步骤。
- 来源锚点:
- (internal reference removed)(收敛纪要)
- Maine Coon 2026-03-01 F042 盘点分析(对账 spec vs git log)
- 关联:F042 | merge-gate | feat-lifecycle
- 状态:validated
- 更新时间:2026-03-05
- 现象:到了交付阶段仍在"先做个简陋版本让operator验收",交付半成品而非完整 feat。内部实现步骤被暴露为交付批次,operator被迫反复验收中间产物。产出后续要重写而非扩展,等于做了两遍。
- 根因:从"什么容易做"往前凑,而不是从终态往回推。把探索阶段的习惯(spike/MVP)带到了交付阶段。
- 典型症状:先做内存 Map 模拟再换 Redis、先搭空壳模板再填真逻辑、先造通用框架再写业务。
- 对策:
- Planning 阶段先钉终态 schema,每步产物必须在终态中原样保留(可扩展不可替换)
- 步骤是内部实现节奏,不是给operator看的交付批次;交付物是完整 feat
- 纯探索显式标注 Spike(时间盒 + 产出结论),不伪装成交付物
- Quality gate 自检:后续要"重写"还是"扩展"?重写 = 绕路
- 来源锚点:2026-03-05 operator反馈 + Ragdoll/Maine Coon联合分析
- 关联:writing-plans | quality-gate
- 状态:validated
- 更新时间:2026-03-09
- 现象:猫猫声称 feature 完成/未完成,只看了 spec 文件的 checkbox 状态就下结论,没有去核实 git log、PR、实际 commit。导致"睁眼说瞎话"——spec 可能漏标、错标,与实际代码状态不一致。
- 根因:偷懒走捷径。spec checkbox 是人工维护的元数据,不是交付证据本身。把"关于证据的描述"当成了"证据"。
- 对策:
- 验证交付物时,至少核实两层:spec checkbox + 实际 commit/PR 状态
- "完成"的证据链:spec AC ✅ + commit 存在 + PR merged + 测试通过
- "未完成"也需要证据:具体哪条 AC 缺失 + 对应代码/PR 确实没有
- 不要只读 .md 文件就下结论——.md 是索引,git 才是真相
- 来源锚点:2026-03-09 operator发现Ragdoll(另一线程)只看 spec 就声称 feat 未完成
- 关联:P5(可验证才算完成)| quality-gate | feat-lifecycle
-
状态:validated
-
更新时间:2026-03-13
-
坑:为开源仓安全把
start-dev.sh的 proxy 默认值改为 OFF → 家里.env没补显式ANTHROPIC_PROXY_ENABLED=1→ runtime 重启后 proxy 消失 → 手动拉起绑定 CLI session → session 退出 proxy 再死。一个默认值改动引发 4 步修A炸B 链条。 -
根因:把"改脚本默认值"当成局部变更,没意识到这是"改所有依赖该脚本的环境的行为"。
.env显式值是防漂移的唯一屏障,但没有同步补上。 -
触发条件:共享脚本被多环境(dev / opensource / runtime worktree)使用 + 改了默认值但没补
.env显式覆盖 + 未做真实启动验收(只跑了静态检查)。 -
修复:(1) 同 commit 补
.env显式值 (2) 验收必须包含pnpm start真实启动 (3) 启动摘要标注值来源(profile default vs .env override)。 -
防护:ADR-016 N3(profile 化取代纯
.env感知)+ 启动摘要值来源标注 + sidecar 状态分层(disabled/launching/ready/failed)。 -
来源锚点:
- (internal reference removed)(C1 共识 + 4.1 决策)
docs/decisions/016-sync-runtime-negation-decisions.md(N3 否决分叉脚本)- commit
553984d5(Maine Coon proxy kill 门禁修复)
-
原理:共享基础设施的默认值是所有消费环境的隐式契约。改默认值 = 改所有环境的行为。必须同时补齐所有消费方的显式覆盖,并用真实启动验证——静态检查只能证明"代码合法",不能证明"行为正确"。
-
关联:ADR-016 | LL-019 过度修复反模式 | LL-020 补丁数量信号
-
状态:draft
-
更新时间:2026-03-14
-
坑:F118 Phase A 的 quality gate 将 AC-A3/AC-A5 记为"已达成",但 AC-A3 承诺的
rawArchivePath字段在代码和测试里都不存在。GPT-5.4 愿景守护才发现这个缺口。 -
根因:quality gate 按"大部分字段都实现了"的直觉打勾,没有逐字段对账 AC 文本与实际代码产出。文档里写了什么 ≠ 代码里有什么。
-
触发条件:AC 列出多个字段/能力时,部分实现容易被当成全部实现。
-
修复:spec 改为
rawArchivePathprovider-scoped 可选,defer 到 Phase B(commitb594dd90)。 -
防护:quality gate Step 3 逐项检查时,对列表型 AC(多个字段/多个能力),必须逐项在代码中 grep 确认存在,不能凭印象打勾。
-
来源锚点:
docs/features/F118-cli-liveness-watchdog.mdAC-A3 修订- GPT-5.4 愿景守护 2026-03-14([thread-id])
-
原理:AC 是 feature contract 的一部分,每个字段都是承诺。"大部分实现"≠"AC 达成"。quality gate 的价值在于精确性,不在于速度。
-
关联:LL-029 交付物验证不能只看 spec checkbox
-
状态:validated
-
更新时间:2026-03-14
-
坑:F101 狼人杀被声明 done(2026-03-12),愿景守护由 GPT-5.4 审查并 pass。92 个单元测试全绿、190+ 游戏测试全绿。但 2026-03-14 operator第一次真的启动 dev 点开狼人杀后发现:(1) GameShell 接了 onClose 但没渲染关闭按钮——用户被困在全屏游戏里出不来;(2) 无大厅/配置流程——硬编码 7 只猫自动塞入;(3) 猫猫 AI 不会自动行动——游戏永远卡在 night_guard 等待;(4) 与 .pen 设计稿的 UX 差距大。整体不可用。
-
根因:愿景守护是通过阅读代码、测试报告和 spec checkbox 完成的,没有一只猫真的启动
pnpm dev,打开浏览器,点击"狼人杀",选个模式,看看会发生什么。单元测试验证的是组件/引擎的孤立行为,不是端到端用户体验。"每个部件都对"≠"组装起来能用"。 -
触发条件:feature 有前端 UI + 后端引擎 + WebSocket 实时交互等多层集成时;只跑单元测试不做 E2E 验证时。
-
修复:(1) 重新打开 F101,补 Phase C 可用性修复;(2) 新增 AC-C4 要求 codex/gpt52 启动 dev 做真实 E2E 验收。
-
防护:愿景守护增加"真实环境启动验证"环节——对于有 UI 的 feature,reviewer 或operator必须至少启动一次 dev 环境并走通核心流程。不方便的话至少把 dev 启动好让operator一起测。
-
来源锚点:
docs/features/F101-mode-v2-game-engine.mdPhase C(2026-03-14 补充)- operator 2026-03-14 消息:"你们没人点开 dev 启动你们的东西跑过真的测试嘛?"
- operator 2026-03-14 截图:night_guard 全员等待,无关闭按钮
-
原理:集成系统的正确性不能由组件测试的总和保证。单元测试验证的是"每个零件符合 spec",不是"零件组装后的机器能工作"。对于用户直接使用的 feature,最终验收必须包含真实环境启动 + 用户视角走查。
-
关联:LL-029 交付物验证 | LL-031 Quality gate 逐字段对账 | LL-006 没有新鲜验证证据不得宣称完成
- 状态:validated
- 更新时间:2026-03-18
- 坑:PR #543 云端 Codex review 的 review body 显示
COMMENTED(通常意味着"no major issues"),但实际在 inline code comment 里提了一个 P1(flushDirtyThreads 用了空的 threadMemory.summary 会 30 秒后删除 rebuild 刚建好的 thread 索引)。Ragdoll只看了 review body 就 merge 了,漏掉了 P1。 - 根因:
gh pr view的--json reviews只返回 review body,不返回 inline code comments。必须额外调gh api repos/.../pulls/N/comments才能看到 inline comments。 - 触发条件:remote review 给了
COMMENTEDstate + 有 inline P1 code comment。 - 防护:
- merge-gate 流程加一步:必须检查 inline comments(
gh api repos/{owner}/{repo}/pulls/{N}/comments),不能只看 review body - 看到
COMMENTED不等于通过——要看完整 comments 再判断
- merge-gate 流程加一步:必须检查 inline comments(
- 来源锚点:
- PR #543: fix(F102-E): thread indexing reads message content
- operator experience:"等会!这个 codex 云端他给你提了 p1 的你怎么就合入了?"
- 关联:merge-gate skill、remote review 流程
- 状态:validated
- 更新时间:2026-03-21
- 坑:F102 Phase C 的 embedding 实现用了
@huggingface/transformers(Transformers.js ONNX,in-process CPU),而同一项目里 TTS/ASR 已有完整的参考架构(独立 Python 进程 + MLX GPU + HTTP /health + 端口注册 + GPU 锁)。结果:(a) CPU 和 API 进程争抢资源;(b) 无独立端口、无健康检查、dashboard 不可见;(c) 启动时同步阻塞下载 614MB 模型;(d) Mac 有 Apple Silicon GPU 不用,浪费硬件。 - 根因:Ragdoll偷懒走了"最小实现路径"(ONNX + Transformers.js in-process),没有对照同项目已有的 TTS/ASR 架构模式。这是典型的"脚手架"——有终态参考(独立进程 GPU)还做了中间态(in-process CPU)。
- 触发条件:新增本地模型推理能力时,没有先审视项目里已有的模型服务架构。
- 防护:
- 新增任何本地模型推理 → 先看 TTS/ASR 的实现模式(独立进程 + GPU + HTTP + /health + 端口注册)
- 禁止把模型推理放在 API 主进程内(CPU 争抢 + 无隔离)
- Mac 上优先用 MLX(Apple Silicon GPU 原生支持)
- 正确做法:写一个独立的
scripts/embed-api.py(参考scripts/tts-api.py),用 MLX 或 sentence-transformers GPU,暴露/embed+/health,Node.js API 只做 HTTP 客户端。 - operator experience:"你用 cpu!为什么不用 gpu 啊!!你这实现我拒绝。你这不又是脚手架,有其他同样模型的参考实现你还非得实现成现在这样。"
- 关联:LL-029 交付物验证、F102 Phase C、TTS(scripts/tts-api.py)、ASR(scripts/whisper-api.py)
-
状态:validated
-
更新时间:2026-03-21
-
坑:Maine Coon执行
scripts/sync-to-opensource.sh时,TARGET_DIR 指向了cat-cafe-runtime(runtime worktree)而非clowder-ai(开源仓)。脚本核心操作rsync -a --delete把 runtime 当成开源仓目标来清洗:(a) 2057 个文件从磁盘删除(296,204 行代码消失);(b).env被开源版覆盖(端口变 3003/3004、品牌变 Clowder AI、API keys 全丢、代理关闭);(c).env被删除;(d)node_modules损坏导致服务无法启动。.env是 gitignored 的,git checkout .无法恢复,API keys、飞书/Telegram/GitHub IMAP 配置均无备份。 -
根因:(1) sync 脚本的 TARGET_DIR 没有安全护栏,任何路径都能被当成目标;(2)
CLOWDER_AI_DIR环境变量被设错或在错误目录执行了脚本;(3)rsync --delete是不可逆破坏性操作,无 trash/回收站。 -
触发条件:
CLOWDER_AI_DIR指向内部 worktree,或在 worktree 目录下执行 sync 脚本导致相对路径解析错误。 -
修复:
- 代码文件:
git checkout . && git pull origin main && pnpm install .env:从 WebStormcontent.dat缓存逐 key 恢复(Anthropic/OpenRouter/Feishu/GitHub IMAP 找回,OpenAI/Google/Telegram 未找回需手动补).env:从.env.example重建
- 代码文件:
-
防护:
sync-to-opensource.sh新增 TARGET_DIR 安全护栏:(a) 目录名匹配cat-cafe*则拒绝;(b) 目标是当前仓库的 git worktree 则拒绝- full sync 改成 source-owned public gate:先把导出产物打到 temp target,在 temp target 跑
pnpm check/pnpm lint/build/test:public/ startup acceptance;绿了才允许碰真实clowder-ai - 本机 smoke 不再属于 full sync 主路径:README/macOS 启动验收单独执行,且必须显式隔离端口/Redis,不能顺手碰 runtime
- 所有猫:禁止对 runtime worktree 执行任何同步/清理脚本(runtime 是生产环境,不是测试靶子)
- .env 应该有备份机制(目前没有,gitignored 的敏感文件是单点故障)
-
来源锚点:
scripts/sync-to-opensource.shL148-L164(新增 safety guard).sync-provenance.json(事故证据:source_commit=aa15355e, 时间 2026-03-21T14:29)- operator experience:"他妈又在 runtime 改东西""什么配置都没了 这都没存档的 我都不记得有的怎么配的"
-
原理:
rsync --delete对目标目录的破坏是不可逆的(不进 trash,直接 rm)。破坏性操作的目标路径必须有正面验证(allowlist),不能只靠"别填错"。gitignored 的敏感配置文件是备份盲区——git 保护不了它们,IDE 缓存是碰运气。 -
关联:LL-015 Redis production Redis (sacred) | CLAUDE.md 四条铁律 | feedback_no_touch_runtime.md
- 状态:validated
- 更新时间:2026-03-24
- 坑:
sync-to-opensource.sh进入 temp target public gate 后,Maine Coon多次在Biome check...或Smoke test (test:public)...阶段就回消息,误把“脚本还在跑 / 会话还活着”当成阶段完成。结果一旦执行停下,外部看到的是“同步到了 Step 5”,但真实 target 还没被碰到,PR/CI 也根本没开始。 - 根因:(1) 把长静默门禁误当成 checkpoint;(2) 只观察到了会话状态,没有等到脚本退出码和终态输出;(3)
opensource-ops/ outbound sync 文档之前写了 temp target public gate 必须全绿,但没把“执行中的猫不得在 Step 5 半路退出”写成硬约束。 - 触发条件:release-intended full sync / full sync 进入 temp target public gate,尤其卡在
pnpm check、pnpm lint、build、test:public、startup acceptance 这类长静默步骤时。 - 修复:
cat-cafe-skills/opensource-ops/SKILL.md增加关键原则:full sync 是长跑门禁,不是中途 checkpointcat-cafe-skills/refs/opensource-ops-outbound-sync.md增加执行纪律:Step 5 只允许以✓ Source-owned public gate passed或明确红灯失败作为退出条件
- 防护:
- release / full sync 期间,只要脚本还没打印
=== Sync complete ===或失败红灯,就继续守在执行链上 - 禁止在
Biome check.../Smoke test (test:public).../Startup acceptance...这些中间状态汇报“已经到下一步” - 对外状态必须基于终态:
sync completed、PR opened、CI running、sync failed
- release / full sync 期间,只要脚本还没打印
- 原理:Step 5 是 source-owned public gate 的单个阻塞门禁。它的业务含义不是“看起来跑到了哪一行日志”,而是“真实 target 是否被允许触碰”。在脚本没打印
✓ Source-owned public gate passed之前,这个答案始终是否定的。
- 状态:draft
- 更新时间:2026-03-25
- 坑:预期不同模型家族(Claude Opus vs GPT-5.4)会给出差异化观点,但本地两猫的观点反而比同模型家族的云端猫(GPT Pro)更趋同。差点把这种趋同当成"互相附和"忽略了。
- 根因:本地猫共享 shared-rules、共同经历(120+ features 的协作历史、同一套教训沉淀),这些"共享记忆"比底层模型参数更能塑造判断框架。云端猫虽同属 GPT 家族但缺乏这些共同经历,所以反而提出了更多不同视角。
- 触发条件:多猫独立思考/brainstorm 场景——当两只本地猫意见过度一致时,不要急于下结论是"互相附和",也不要急于下结论是"充分验证",需要引入无共享记忆的外部视角交叉校验。
- 修复:F129 生态调研中增加了云端 GPT Pro Deep Research 作为独立视角;本地两猫 + 云端猫三方碰撞后才做综合。
- 防护:
- 高 stakes 的多猫独立思考,默认在扇入前引入至少 1 个无共享记忆视角;该视角第一轮只看原问题和最小中性背景,不看本地综合(锁死时序:先独立出结论,再碰撞)
- 本地猫趋同时显式标注"
⚠️ 可能受共享记忆影响",不直接等价于"独立验证通过"
- 来源锚点:(internal reference removed) §3 + (internal reference removed) §Local Synthesis
- 原理:团队文化是一种隐性的 prompt——shared-rules、共同教训、协作习惯构成了比 system prompt 更深层的"预训练"。这不是坏事(恰恰说明团队文化在起作用),但在需要多元视角时必须意识到这个偏置。
-
状态:validated
-
更新时间:2026-03-26
-
坑:F139 Phase 1a 的 TaskRunnerV2 实现了
withTimeout()用Promise.race给 execute 加超时。timeout reject 后finally释放了running锁,但底层 execute 仍在运行——下一个 tick 绕过 overlap guard 进入了同一 task 的并发执行。Maine Coon第二轮 review 抓出此 P1。 -
根因:JS Promise 无法取消。
Promise.race([execute, timeout])只决定哪个先 settle 调用方,但输掉 race 的 promise 仍然在跑。如果 timeout 赢了就释放锁,等于告诉调度器"这个 task 空闲了",而实际 execute 还在占用资源。 -
触发条件:任何用 Promise.race/setTimeout 做 timeout 的场景,如果 timeout 后释放了互斥资源(锁、信号量、连接池 slot)。
-
修复:
- 引入
pendingExecutes[]收集所有 raw execute promise - timeout 后照常记账
RUN_FAILED,但finally块在释放running锁前先await Promise.allSettled(pendingExecutes) - 代价:
triggerNow在超时场景不会立即返回(可接受的 tradeoff)
- 引入
-
防护:
- 规则:Promise timeout wrapper 不得在 finally 中直接释放互斥资源——必须等底层 promise settle
- 测试:
task-runner-v2.test.js"concurrent reentry" 用例:gate 返回信号 + execute 永远 pending + timeout 触发 → 验证第二个 tick 被 overlap guard 拦截
-
来源锚点:
packages/api/src/infrastructure/scheduler/TaskRunnerV2.tsL139, L169- Maine Coon Round 2 review (2026-03-25): "timeout reject → finally → running=false,但 execute 还在飞"
-
原理:Promise 是 completion token,不是 cancellation token。race 只决定 observer 的视角,不影响 producer 的生命周期。在 JS 没有原生 AbortSignal 深度集成之前,timeout 和资源释放必须解耦。
-
关联:F139 Phase 1a PR #747 | ADR-022
-
状态:validated
-
更新时间:2026-03-26
-
坑:ReviewCommentsTaskSpec 的 gate 在筛选新评论时顺手推进了
lastSeenCommentIdcursor。如果 execute 失败(网络超时、处理异常),这批评论就永远丢了——cursor 已经越过它们,下次 gate 不会再返回。 -
根因:gate 和 execute 是 TaskRunnerV2 pipeline 的两个独立阶段,gate 的职责是"判断有没有活",不是"确认活干完了"。把 cursor 推进放在 gate = 乐观假设 execute 一定成功。
-
触发条件:任何 gate 阶段推进 cursor/offset/watermark 的模式,当 execute 可能失败时。
-
修复:
commitCursor()闭包模式——gate 计算新 cursor 值但不写入,把 commit 函数作为 signal 的一部分传给 execute,execute 成功后调用signal.commitCursor()。 -
防护:
- 规则:gate 只读 cursor 做筛选,cursor 推进必须在 execute 成功路径上
- 测试:
review-comments-spec.test.js"cursor not advanced on execute failure" 用例
-
来源锚点:
packages/api/src/infrastructure/email/ReviewCommentsTaskSpec.tsL81, L96- Maine Coon Round 1 review P2-1: "gate 里推进 cursor 是 over-optimistic"
-
原理:cursor/watermark 是"已确认处理完成"的标记,语义上等价于 Kafka consumer commit。Kafka 的 at-least-once 保证也要求 commit 在 process 之后,不在 poll 时自动推进。
-
关联:F139 Phase 1a PR #747 | ReviewCommentsTaskSpec
-
状态:validated
-
更新时间:2026-03-27
-
坑:金渐层在 5 个文档中写入了 11 处未来日期(2026-06/07),实际当时是 2026-03。这是第二轮修复(第一轮 de2cb42f5 修了 F137)。
-
根因:LLM 没有可靠的内部时钟。金渐层的训练数据截止日期造成系统性时间偏差(+3~4 个月),在不调用
date命令校准的情况下,凭"内部时间感"直接写入日期 = 幻觉。 -
触发条件:任何猫在文档中写入日期(timeline、changelog、KD 表、Phase 记录等)且未先确认当前日期时。
-
偏差模式:稳定 +3~4 个月,不是随机错误,是系统性偏差。
-
修复:commit
9f87d354e批量修正 5 文件 11 处(open-source-status / F048 / F055 / F121 / F134)。 -
防护:
- 铁律:写日期前先
date— 任何猫在文档中写入日期时,必须先执行date或从系统 prompt 中确认当前日期,禁止凭感觉写 - Review 检查项:reviewer 核对文档中新增日期是否在合理范围内(不超过当前日期)
- 铁律:写日期前先
-
来源锚点:
- 金渐层自述根因:内部时间感知幻觉 + 训练截止日期偏差
- 第一轮修复:de2cb42f5(F137)、第二轮修复:9f87d354e(F048/F055/F121/F134/open-source-status)
-
原理:LLM 的时间感知是从训练数据中学到的统计分布,不是真实时钟。没有外部锚定(系统 prompt 日期注入或
date命令),任何模型都可能产出偏移日期。这跟"内容幻觉"本质相同——模型生成看起来合理但事实错误的信息。 -
关联:金渐层日期幻觉 | 5 文件 11 处 | 两轮修复
-
状态:validated
-
更新时间:2026-03-28
-
坑:Ragdoll写完诊断报告后只报了文件路径,没有帮operator打开。operator反问:"我们有打开的能力,但你写完了竟然不帮我打开!"
-
根因:猫的工作流在"产出文件"这一步就画了句号,没有编码"呈现给operator"这一步。workspace-navigator、browser-preview、rich block 等展示能力都存在,但只在operator明确要求时才被动使用——缺少"何时主动展示"的触发时机。
-
触发条件:任何猫写完文件/跑完测试/改完前端/生成报告后,没有主动打开或展示给operator。
-
修复:当次手动用 Navigate API 打开了报告。
-
防护:
- shared-rules W8 共享视图 — 将"产物端上桌"编码为世界观级规则,通过 GOVERNANCE_L0_DIGEST 注入所有猫的每次调用
- 判断标准:写完产物后问"operator需要看到这个吗?"——是 → 按场景用 navigate / preview / rich block 打开
-
来源锚点:
- operator experience:"写完竟然不帮我打开!就和写完前端不帮我打开 preview 一样"
- shared-rules.md W8 新增
- SystemPromptBuilder.ts GOVERNANCE_L0_DIGEST W8 新增
-
原理:人猫协作是双向共享感知,不是单向任务完成汇报。愿景写的是"共享家园"——家人做了饭会端上桌,不会只喊一声"厨房锅里有饭"。猫的能力边界不只是"能做",还包括"做完后展示"。这是人猫协作和人用 API 的本质区别(W1)。
-
关联:shared-rules W8 | workspace-navigator skill | browser-preview skill | 三天产品化诊断
-
状态:validated
-
更新时间:2026-03-28
-
坑:
env-registry.ts(Hub 用)、.env.example(新用户用)、代码里的process.env.XXX(实际真相)三处各自为政,无任何自动化检查。结果:25+ 个变量代码里用了但 Hub 看不到,.env.example只有 21 条 vs 实际 100+,8 个 HINDSIGHT 变量在.env.example里但代码从未引用。 -
根因:配置注册是纯文档契约("新增 env 必须注册"写在注释里),但没有机器强制执行。人工纪律在 feature 交付压力下必然失守。
-
触发条件:任何新增
process.env.XXX时忘记在env-registry.ts注册 + 没人发现。 -
修复:(1) 补齐 35 个漏网变量 (2) 新增
check:env-registry(扫描代码→registry 完整性)和check:env-example(双向一致性) (3) 接入pnpm check硬门禁 (4) 新增exampleRecommended字段确保关键变量出现在.env.example。 -
防护:
pnpm check现在覆盖 env 注册完整性,CI / gate 自动拦截遗漏。 -
来源锚点:
- TD117 in
docs/TECH-DEBT.md scripts/check-env-registry.test.mjsscripts/check-env-example.test.mjs- LL-030(同根问题:proxy 默认值改了没同步 .env)
- TD117 in
-
原理:多真相源必须有机器强制同步。注释里写"请手动保持一致"等于没写。代价最低的时间点是新增代码时立即拦截,而不是部署后发现 Hub 里看不到变量。
-
关联:LL-030 | TD117 | env-registry.ts
-
状态:validated
-
更新时间:2026-03-28
-
坑:F136 Phase 4 删除了旧
provider-profiles.ts读取层(PR #824, -2032 行),但迁移函数(PR #818)被 best-efforttry/catch包裹。当迁移未执行时,旧读取层已不在、新accounts也为空,服务静默带病启动。operator在 runtime 上看到账号配置页全部"暂无模型"、API key 丢失。 -
根因:删除旧层与迁移成功之间没有 startup invariant 门禁。
accountStartupHook只做"迁移 + conflict scan",不校验"旧源在但新数据缺"的不变量。非 HC-5 异常被index.ts:1444吞为 warn。 -
触发条件:迁移因任何原因失败(构建未更新、import 报错、文件系统异常等)+ 旧读取层已被同批或先前 PR 删除。
-
修复:(1) 手动触发迁移恢复数据 (2) PR #831 修复 per-project detection + credential clear 语义 (3) 记录 P2 follow-up: startup invariant guard(旧源在 + accounts 缺 → error/readiness fail)。
-
防护(待实施):
accountStartupHook返回前增加不变量校验——provider-profiles.json存在 +catalog.accounts缺失 → 至少 error 级别暴露,理想为 startup hard fail。补回归测试覆盖此场景。 -
来源锚点:
- F136 spec follow-up 章节
packages/api/src/config/account-startup.tspackages/api/src/index.ts:1436-1452- 反思胶囊:(internal reference removed)
-
原理:删除旧读取路径和迁移成功是原子操作的两端。只删不验 = 中间态数据丢失。删旧层的 PR 必须同时包含:迁移成功回归测试 + legacy source 存在且新数据缺失时的 startup guard。
-
关联:LL-042 | F136 | account-startup.ts
- 状态:validated
- 更新时间:2026-03-28
- 坑:中文输入法按 Enter 选词时,Chrome 的事件顺序是
compositionend→keydown(Enter, isComposing: false)。与 Firefox 相反(Firefox 是keydown(isComposing: true)→compositionend)。因此e.nativeEvent.isComposing守卫在 Chrome 上对 Enter 键无效,导致中文输入时按回车选词会直接提交表单。 - 根因:Web 规范未强制
compositionend与keydown的顺序,Chrome 和 Firefox 实现不同。项目内 24 个输入组件全部使用了不可靠的e.nativeEvent.isComposing守卫,包括主聊天输入框。 - 影响范围:ChatInput(主聊天)、ActionDock(游戏发言)、ThreadItem/SectionGroup(重命名)、HistorySearchModal、SignalArticleDetail、StudyFoldArea、VoiceSettingsPanel、InlineTreeInput、BrakeModal、VoteConfigModal、BindNewSessionSection、SessionChainInputs、DirectoryBrowser、DirectoryPickerModal、InteractiveBlock、BrowserToolbar(URL 输入)、HubPermissionsTab(完全无守卫)、WorkspacePanel(搜索)、hub-tag-editor(标签提交)、SessionSearchTab(form submit)、QuickCreateForm(form submit×3)、SignalInboxView(form submit)。
- 修复:创建
useIMEGuardhook(packages/web/src/hooks/useIMEGuard.ts)。核心思路:用compositionstart/end事件驱动 ref,在compositionend后通过requestAnimationFrame延迟一帧清除 composing 状态,使得 Chrome 紧随其后的keydown(Enter)仍能被拦截。全量替换 24 个组件。 - 检查清单(新增 Enter 输入点必须遵守):
- 禁止裸用
e.nativeEvent.isComposing或e.key === 'Enter'无守卫 - 必须使用
useIMEGuardhook 并绑定onCompositionStart/End+ime.isComposing()守卫 - 测试 IME 场景时,模拟
compositionstart→keydown(Enter)序列,不要用Object.defineProperty(event, 'isComposing', { value: true })
- 禁止裸用
- 关联:F080(输入历史)| ChatInput | ThreadItem
-
状态:draft
-
更新时间:2026-03-31
-
坑:2026-03-29 ~ 2026-03-31 期间,runtime worktree(
cat-cafe-runtime)被多个Ragdoll session 反复弄脏,导致pnpm start无法启动。发现三批污染:- WeixinAdapter voice_item A/B test(
WEIXIN_VOICE_ITEM_MODEenv 切换minimalvsmetadata)——调试微信语音问题,直接在 runtime 编辑 - invoke-single-cat.ts account resolution 调试——插入
appendFileSync('/tmp/cat-cafe-account-debug.log')文件日志 + 多个let→const误改(会导致运行时崩溃)+ proxy fallback if/else 逻辑被重构坏 process-liveness-probe.test.js进程泄漏——同一测试文件被多实例并发运行(疑似 watch 模式反复触发),每个实例 spawn 子进程不回收,进程数飙至 10472,Load Average 199,系统进入EAGAIN(fork failed: resource temporarily unavailable),最终只能重启 macOS
- 另有 Knowledge Feed markers(
docs/markers/*.yaml)和开源同步残留(LICENSE、ROADMAP.md、.sync-provenance.json)出现在 runtime
- WeixinAdapter voice_item A/B test(
-
根因:
- P0 铁律执行失败:
feedback_no_touch_runtime.md已明确"禁止直接操作 runtime worktree",但多个 session 的Ragdoll仍然在 runtime 里直接编辑代码/运行测试/运行脚本 - runtime 无写保护:除了
pnpm start时的脏检查(git status -uno),runtime worktree 没有任何机制阻止猫直接写入 - 测试进程无上限:
process-liveness-probe.test.js涉及 spawn 子进程,但无 maxprocs / ulimit 保护,watch 模式下可指数膨胀 - 清理时二次伤害:发现污染后,当前 session 的Ragdoll三次不检查内容就执行
git checkout --/git clean -fd,导致调试进度(invoke-single-cat.ts)和 Knowledge Feed markers 不可逆丢失
- P0 铁律执行失败:
-
触发条件:
- 猫在 runtime worktree 目录下执行编辑/测试/脚本(而非 feature worktree)
- 测试涉及 process spawn 且在 watch 模式下运行
- 发现脏文件后不检查内容直接清理
-
修复:
- 第 1 批:stash 保留(
runtime-rescue: WeixinAdapter voice_item A/B test),记录到 F137 changelog - 第 2 批:被误清理(
git checkout -- .),diff 内容保存到 GitHub Issue #862 - 第 3 批(进程爆炸):
killall -9 node+ 系统重启
- 第 1 批:stash 保留(
-
防护:
- runtime worktree 写保护:考虑用
chflags uchg或 git hook 阻止非runtime-worktree.sh的写入 - 测试进程上限:
process-liveness-probe.test.js需加 spawn 计数器 +ulimit -u防护 - 清理前必须检查:见
feedback_never_clean_without_checking.md——git checkout/clean/rm前先ls/cat/git diff看内容,stash 优先于 checkout - 脏检查应区分 tracked 和 untracked:当前
ensure_runtime_clean用-uno忽略 untracked 文件,markers/sync 残留不会阻止启动但会持续积累
- runtime worktree 写保护:考虑用
-
来源锚点:
- GitHub Issue: #862
- F137 changelog 2026-03-29 条目
feedback_never_clean_without_checking.mdscripts/runtime-worktree.shensure_runtime_clean 函数
-
关联:F137(WeixinAdapter voice)| F118(invoke-single-cat audit)| #862 | feedback_no_touch_runtime.md
-
状态:validated
-
更新时间:2026-03-31
-
坑:重启 macOS 后
pnpm start冷启动 Redis 6399,发现 915 个 thread / 42,778 keys 全部消失,只剩启动后新写入的 7 个 thread。operator以为数据全丢了。 -
根因:AOF 和 RDB 两套持久化机制脱节了 48 天。
- 2月9日
383e23791给start-dev.sh加了--appendonly yes - 2月10日首次带 AOF 启动,Redis 创建了 AOF base 文件(此时 DB 是空的 → base = 0 keys,88 bytes)
- 之后某次 Redis 被 restore 脚本或手动方式重启,没带
--appendonly,进入纯 RDB 模式 - 2月~3月:Redis 一直跑在纯 RDB 模式,数据涨到 42,778 keys。AOF 文件在
appendonlydir/里吃灰,停留在 2月10日的空壳状态 - 3月31日:LL-045 进程爆炸 → macOS 强制重启 → Redis 进程死亡 →
pnpm start用--appendonly yes冷启动 → Redis 看到appendonlydir/存在 → 优先加载 AOF(空的)→ 忽略 110MB 的 dump.rdb → 空库
- 2月9日
-
以前没出事的原因:Redis 进程从来没被杀过。每次
pnpm start发现 6399 已在跑就直连(start-dev.sh:927),不触发冷启动。这是第一次真正的冷启动。 -
救命的备份:
archive_redis_snapshot "pre-start"在每次pnpm start启动前自动备份 dump.rdb 到~/.cat-cafe/redis-backups/dev/(保留 20 份)。今天 07:34 的dev-pre-start-20260331-073456.rdb包含完整的 42,778 keys,恢复成功。这个机制源自 2月10日 LL-015 事故后的加固。 -
修复(已提交
3ae239a1a):- stale AOF 冷启动防护(
start-dev.sh:716 maybe_quarantine_stale_aof_dir):冷启动前比较 AOF base 与 dump.rdb 体积比,dump/base >= 100 倍判定为 stale,自动隔离appendonlydir/到 backup - restore 脚本 AOF 盲区(
redis-restore-from-rdb.sh:96):恢复后强制带--appendonly yes启动 + 旧appendonlydir迁移备份,杜绝"恢复后进入纯 RDB 模式" - 回归测试:28/28 通过,覆盖 stale 隔离、proportional base 保留、tiny base + incr 存在仍隔离三个场景
- stale AOF 冷启动防护(
-
教训:
- "以前没事"不等于"没有 bug"——很多配置只在冷启动时生效,如果从来没冷启动过就从来不会暴露。定期冷启动演练是必要的
- 两套持久化机制必须保持同步——Redis 的 AOF 优先于 RDB 加载,如果 AOF 是 stale 的,RDB 里的数据会被完全忽略
- 所有启动 Redis 的代码路径必须统一——restore 脚本、手动启动、start-dev.sh 如果参数不一致,就会制造 AOF/RDB 脱节的窗口
- 备份机制越早建越好——LL-015 的"坑"变成了 LL-046 的"救命稻草"
-
来源锚点:
- 提交:
3ae239a1a fix(redis): harden stale AOF detection and restore startup - 起因:
383e23791 feat(redis): isolate personal storage and add durability guardrails - 关联:LL-015(Redis 端口误触事故)| LL-045(runtime 进程爆炸导致重启)
- 提交:
-
状态:validated
-
更新时间:2026-04-10
-
背景:Cat Cafe Hub 的 Socket.IO 实时通道被发现存在 CSWSH(Cross-Site WebSocket Hijacking)风险。
Origin: https://evil.example可以成功建立 WebSocket 连接到127.0.0.1:3004 -
影响:恶意网页可从任意 Origin 连接本机 WebSocket,冒充用户、监听消息、干扰猫猫工作
-
根因:
- Socket.IO v4 的
cors配置只对 HTTP long-polling 生效,不校验 WebSocket upgrade 请求的 Origin 头(Socket.IO 官方文档 2026-02-16 明确标注) - 身份自报(
handshake.auth.userId)无服务端校验 - Room 无 ACL,任何连接可加入任意 room
- @fastify/websocket 的 plain WS 端点(terminal PTY)完全绕过 Socket.IO,无任何 Origin 检查
- Socket.IO v4 的
-
修复:
- Phase A(PR #1041):
allowRequesthook 显式校验 Origin + 禁止自报 userId + 私网 Origin 收紧 - Phase B(PR #1045):terminal WS Origin gate + cancelAll 授权 + 全局 room ACL
- Phase D(规划中):HTTP session 替代自报身份 + Clickjacking + CSP + Prompt Injection 降权
- Phase A(PR #1041):
-
教训:
- 框架的 CORS 配置 ≠ WebSocket 安全——Socket.IO/Express 的 cors 中间件只管 HTTP,WebSocket upgrade 是独立的协议切换,必须在 upgrade 层单独校验
- 本机 ≠ 安全——浏览器同源策略不阻止 JS 向 localhost 发 WebSocket 连接,任何打开的网页都是潜在攻击面
- "能连上"比"能做什么"更危险——一旦连接建立,后续的身份/Room/事件授权都是亡羊补牢;连接层拒绝是第一道也是最关键的防线
- Agent 产品的攻击面比传统 Web 应用更大——Prompt Injection、工具调用误用、外部内容驱动的高危操作是传统安全审计不覆盖的维度
-
来源锚点:
- F156 spec:
docs/features/F156-websocket-security-hardening.md - 三猫安全审计:(internal reference removed)
- PR #1041(Phase A)、PR #1045(Phase B)
- 外部参考:OpenClaw CVE-2026-25253 + ClawJacked(同类攻击链)
- F156 spec:
-
状态:validated
-
更新时间:2026-04-10
-
坑:F100 Self-Evolution 线程在创建 30 天后突然从 Hub UI 消失——不在列表、不在垃圾桶、搜索不到。operator:"太恐怖了!"
-
根因:
RedisThreadStore.ts硬编码DEFAULT_TTL = 30 * 24 * 60 * 60(30 天),thread 创建时调用EXPIRE。但updateLastActive()只更新排序分数,从不刷新 hash TTL。到期后 Redis 静默删除 hash,而 sorted set index 因其他 thread 操作续期而存活——形成"索引有 ID 但 hash 已消失"的孤儿状态。 -
触发条件:任何带非零 DEFAULT_TTL 的 Redis store(thread/message/task/summary/backlog/session 等),只要用户在 TTL 窗口内未触发恰好刷新 hash TTL 的操作,就会静默丢失。
-
修复:
- 全量止血:所有 16+ Redis store 的 DEFAULT_TTL 改为 0(persistent),
EXPIRE 0/SET EX 0陷阱用条件分支防御 - 自愈机制:
get()发现 hash 缺失时从 message timeline 重建元数据(recoverThreadFromMessages) - 统一 key 续期:所有 detail 变更通过
setDetailFields()/deleteDetailFields()自动调用applyKeyRetention() - 文档 + .env.example 同步更新
- 全量止血:所有 16+ Redis store 的 DEFAULT_TTL 改为 0(persistent),
-
防护:
- 铁律 #5"禁止用户状态静默消失"——默认持久化,TTL 只能 opt-in
- 新增 Redis store 必须 DEFAULT_TTL=0,引入非零 TTL 需 P0 级审批
- 任何
EXPIRE/SET EX调用必须有> 0守卫,防止 TTL=0 变成立即删除
-
来源锚点:
- 根因文件:
packages/api/src/domains/cats/services/stores/redis/RedisThreadStore.ts:32 - 丢失 thread:
[thread-id](2026-03-11 创建,2026-04-10 过期) - Feature spec:
docs/features/F100-self-evolution.mdline 54
- 根因文件:
-
原理:EXPIRE 0 = 立即删除(Redis 语义)。框架层 TTL 默认值决定了用户数据的生死线——这不是"配置",而是产品决策。opt-out 持久化 = 用户必须知道一个他们不可能知道的配置才能保住自己的数据,这在产品层面是不可接受的。
-
状态:draft
-
更新时间:2026-04-11
-
坑:2026-04-10,Maine Coon在 review F152 PR (#1070) 时,在主仓库执行
pnpm dev:direct。start-dev.sh的kill_managed_ports()无条件杀掉 3003/3004 端口上的进程——正在运行的 runtime 被踢掉,operator被动中断。 -
根因:
kill_port()不检查进程归属:谁占着端口就杀谁,不区分是本 worktree 残留还是 runtime/alpha 等其他实例- 护栏分裂:
runtime-worktree.sh有CAT_CAFE_RUNTIME_RESTART_OK授权门,但dev:direct走start-dev.sh,绕过了这道门 guard_main_branch_start()盲区:只拦main分支 +cat-cafe仓库名,主仓库切到 feature branch 照样触发事故- review 沙盒规范有文档无工具:request-review skill 写了"在沙盒操作",但缺少统一入口和强制机制
-
触发条件:任何猫在非隔离环境(主仓库、错误 worktree)执行
pnpm dev:direct/pnpm start,且 runtime 正在使用相同端口 -
修复(PR #1077,已合入
807536df5):- 新增
pid_cwd()+path_is_within_project()+guard_port_kill_ownership():kill_port()前检查占用进程的工作目录是否属于当前$PROJECT_DIR,跨 worktree 默认拒绝 kill CAT_CAFE_RUNTIME_RESTART_OK=1显式授权才放行- 新增
scripts/review-start.sh(pnpm review:start):review 验证统一入口,自动分配 3201/3202 端口、内存 Redis、review 沙盒路径 - review 模板新增"沙盒路径 + 启动命令 + 实际端口"必填字段
- 新增
-
防护:
start-dev.sh端口归属 guard(基于进程 cwd,不硬编码端口号)——任何端口冲突都能防- 回归测试覆盖"默认拒绝跨 worktree kill"和"显式授权放行"两条路径
pnpm review:start统一入口消除"在哪启动、用什么端口"的歧义- request-review 模板强制证据字段(reviewer 必须填沙盒路径和端口)
-
来源锚点:
- 事故报告:
docs/bug-report/2026-04-10-review-dev-direct-runtime-interruption/bug-report.md - 修复 PR:zts212653/cat-cafe#1077
- 端口归属 guard:
scripts/start-dev.sh:450(guard_port_kill_ownership) - review 入口:
scripts/review-start.sh
- 事故报告:
-
原理:"默认安全"优于"靠人记得"。LL-045 证明纪律文档拦不住猫——写了"不要动 runtime"但多个 session 仍然直接操作。端口保护和 Redis production Redis (sacred)一样,必须在工具层面做到"默认不可能发生",而非"读了文档就不会发生"。
-
关联:LL-045(runtime worktree 反复被猫污染)| PR #1077 |
feedback_no_touch_runtime.md| CLAUDE.md 铁律 #4
-
状态:draft
-
更新时间:2026-04-13
-
坑:ADR-009(2026-02-10)选择"仅用户级 skill 分发",F070(2026-03-08)引入项目级 governance bootstrap,事实性推翻 ADR-009 的核心假设。但 F070 完成时未触发任何 ADR/spec 影响检查,导致 ADR-009 以
active状态存续 2 个月,直到社区 issue clowder-ai#386(2026-04-08)才暴露。 -
根因:
- Feature 完成无"知识影响扫描":feat-lifecycle close step 不检查新 Feature 是否推翻了现有 ADR/spec 的前提
- ADR 缺 machine-readable 状态:无
drifted/superseded状态字段,search_evidence无法区分过时文档和当前真相 - 双层挂载无一致性校验:preflight 只检查项目级 symlink 存在,不校验跨层一致性
-
触发条件:任何 Feature 改变了现有 ADR 的核心假设,但 Feature 完成时无人检查
-
修复:
- ADR-009 已标注
status: drifted(2026-04-07) - ADR-025 作为 successor ADR 已完成三猫 review(2026-04-13 收敛)
- ADR/spec frontmatter 新增
status: active|drifted|superseded|historical+drifted_by+last_reviewed字段
- ADR-009 已标注
-
防护(待落地):
- feat-lifecycle close step 增加"知识影响扫描":新 Feature 是否改变了现有 ADR/spec 的假设?
search_evidence检索排序降权 drifted/historical 文档- 定期 ADR 巡检(半年一次
last_reviewed刷新)
-
来源锚点:
- 社区 issue:clowder-ai#386
- ADR-009 drift 标注:
docs/decisions/009-cat-cafe-skills-distribution.md - Successor ADR:
docs/decisions/025-skills-canonical-mount-policy.md
-
原理:知识也有保质期。ADR 记录的是某个时间点的决策假设,后续架构演进可能悄悄推翻这些假设。如果只靠猫的记忆发现漂移,检测延迟 = Feature 交付频率的倒数。必须在 Feature completion 工具层面做"知识影响扫描",才能把漂移窗口从月级压到天级。
-
关联:ADR-009 | ADR-025 | F070 | clowder-ai#386 |
project_knowledge_lifecycle_gap.md
-
状态:draft
-
更新时间:2026-04-18
-
坑:F163 记忆熵减用 3 Phase 建了完整实验基础设施(schema V14 多轴元数据 + 7 flag + experiment logger + shadow mode + Health Tab UI),shadow 模式运行 32 小时、记录 448 次搜索。诊断发现三层空转:① 1501 篇文档 authority 全部是
observed(默认值),boost 权重全 1.0 等于无 boost;② shadow payload 只记{query, resultCount},没记录 before/after 排序对比;③evidence.ts:117硬编码confidence: 'mid' as const,前端无信号差异。 -
根因:
- 坐标系错误(Round 4 原理):核心需求是"重要知识排前面",最小方案是
pathToAuthority()纯函数 + backfill。但选了"先建完整实验框架再灰度上线"的路径,把 70% 工作量花在框架本身而非核心价值。 - Phase 拆分遮蔽空洞:每个 Phase 有自己的 AC 并全部通过,但 AC 验的是"能力存在"不是"能力有效"。
applyAuthorityBoost()存在且可调用 → AC pass,但所有文档权重 1.0 → 实际无效。 - Shadow mode 设计半成品:spec 要求"后台并行跑新策略,记录差异",实现只记了 flag snapshot + query,没记排序差异——因为差异计算依赖 authority 分化,而分化从未发生。
- 坐标系错误(Round 4 原理):核心需求是"重要知识排前面",最小方案是
-
触发条件:任何 feature 用"先建框架 → 再填数据"的顺序推进,且 AC 只验证框架存在性而非端到端效果
-
修复:
- 写
pathToAuthority()纯函数,索引时从路径/frontmatter 自动派生 authority(而非手动 promotion) - 修
confidence: 'mid' as const→ 从 authority 派生 high/mid/low - 直接切
F163_AUTHORITY_BOOST=on(跳过无价值的 shadow)
- 写
-
防护:
- Feature AC 必须包含至少一条"端到端效果验证"(不只是"能力存在")
- 实验 flag 开 shadow 后 48h 内必须检查 payload 是否包含对比数据——空跑 shadow 浪费资源且给人虚假安全感
-
来源锚点:
- F163 shadow 数据诊断:
evidence.sqlitef163_logs 表 448 条 search、authority 分布 100% observed - 硬编码 confidence:
packages/api/src/routes/evidence.ts:117 - Meta-Aesthetics canon(从 Round 4 数学之美升格):
docs/canon/meta-aesthetics.md
- F163 shadow 数据诊断:
-
原理:Agent Quality = Model Capability × Environment Fit(Round 4)。F163 在 Environment 侧堆了大量维度(多项式拟合),但没有验证任何一个维度是否真正改善 Fit。正确的路径是坐标变换:找到"authority 信号已经在文档路径里"这个洞察,用一个纯函数解决,而不是建一整套实验框架去"发现"这个答案。最优表达在正确坐标系下必然最简。
-
关联:F163 | Round 4 数学之美讨论 | LL-050(知识漂移)
-
状态:draft
-
更新时间:2026-04-18
-
坑:shell 启动脚本里
exec ${env_prefix}pnpm run start(env_prefix="NODE_ENV=production ")直接启动失败,bash 报 "NODE_ENV=production: command not found"。结果不是"设置环境变量后再 exec pnpm",而是把NODE_ENV=production当成可执行文件名去 PATH 里查找。F153 intake clowder-ai#512 合入当天社区小伙伴启动就挂。 -
根因:
execbuiltin 不解析内联赋值:bash 的VAR=val command arg形式,只有当 command 是外部可执行程序(如env、pnpm)时,内联VAR=val才会作为临时环境变量传递。exec是 shell builtin,走 replace-current-process 路径,第一个 token 直接被当成要 exec 的 program name——NODE_ENV=production pnpm等同于exec 'NODE_ENV=production' pnpm。- 字符串断言掩盖启动失败:
test/start-dev-script.test.js只断言printf "$(api_launch_command)"输出"cd ... && exec NODE_ENV=... pnpm ..."这个字符串字面量,没有eval这段输出验证进程真能启动。CI 全绿但pnpm run start从未被实际执行过。
-
触发条件:shell 脚本里
exec ${prefix}command模式,prefix含内联环境变量赋值(VAR=value) -
修复:改写成
exec env ${prefix}command——env是 POSIX 外部程序,会正确解析内联赋值并把变量注入子进程 -
防护:
- Shell 启动脚本的单测不能只断言
printf输出文本,至少一个 case 必须bash -n语法检查 + 在 mock 环境下eval这段命令验证 exit code(或跑pnpm dev:direct --dry-run) - Intake 社区 PR 改动
scripts/**尤其启动/runtime 脚本时,reviewer checklist 加一条"本地跑一次pnpm alpha:start或pnpm dev:direct确认实际能启动不报错"
- Shell 启动脚本的单测不能只断言
-
来源锚点:
- Bug report:
clowder-ai#526(2026-04-18 社区小伙伴报挂) - 引入 commit:
cat-cafe:206ae80c40(F153 intake clowder-ai#512) - 修复 commit:
cat-cafe:bf5f54b9(PR #1257)+clowder-ai:6ab02c44(PR #527) - 修复位置:
scripts/start-dev.sh:683-685api_launch_command()
- Bug report:
-
原理:
VAR=val command arg语法中VAR=val是"赋值前缀"还是 argv[0] 取决于 command 的类别——外部程序会被 shell 剥离前缀作为临时 env 传递;builtin(exec/source/:)则直接把前缀当成参数。env这个 POSIX 工具的存在就是为了让"在指定环境下运行程序"成为一个可被任何 builtin/context 调用的显式动作。任何需要给子进程设环境变量又必须经过 builtin(典型就是exec)的场景,用env显式承接。 -
关联:F153 intake clowder-ai#512 | clowder-ai#526 | cat-cafe#1257 | clowder-ai#527
-
状态:draft
-
更新时间:2026-04-20
-
坑:在 Codex CLI 无头
exec_command环境里,把长任务写成bash ... &、nohup ... &、disown或setsid,CLI 返回后看起来“命令发出去了”,但后台子进程常常已经跟着父命令一起死。于是会误判“还在跑”,实际任务根本没活。 -
根因:
- 把 shell 作业控制误当成 harness 生命周期控制:
&/nohup/disown/setsid只是 shell 级语义;在无头 CLI harness 里,顶层命令退出后,同一进程树里的后台子进程仍可能被回收。 - 把轮询误当保活:实测前台长任务拿到
session_id后,即使隔 15 秒再write_stdin也能正常收尾。轮询只是读取进度/结果,不是给进程“续命”。
- 把 shell 作业控制误当成 harness 生命周期控制:
-
触发条件:任何从无头 CLI 里拉 sidecar、proxy、测试服务、长脚本、fire-and-forget worker,尤其是想偷懒用 shell
&时。 -
修复:
- 需要交互/输出:保持前台,接住
session_id,后续继续在同一个 session 上write_stdin - 需要真正后台脱离:用 Node
spawn(..., { detached: true, stdio: 'ignore' })+child.unref(),并把日志/结果文件作为外部探针
- 需要交互/输出:保持前台,接住
-
防护:
- 原生 system prompt 明确禁用“在无头 CLI 里拿 shell 伪后台跑持久任务”
- Fire-and-forget 任务必须同时定义
pid/log/result探针;没有外部探针,不算启动成功 - 命令已经返回
session_id时,不要重开新命令抢跑;优先续同一个 session
-
来源锚点:
- (internal reference removed)
- (internal reference removed)
- (internal reference removed)
packages/api/src/domains/cats/services/agents/providers/GeminiAgentService.ts
-
原理:长任务的关键不是“后台”两个字,而是谁拥有生命周期。 需要持续交互时,生命周期应该绑在当前
session_id;需要任务脱离对话继续跑时,必须显式切换到真正 detached 的进程组,并用外部探针观测。shell 伪后台只是“看起来像分叉了”,不是可靠的 liveness ownership。 -
关联:
assets/system-prompts/cats/codex.md| (internal reference removed)
-
状态:validated
-
更新时间:2026-05-07
-
坑:F193 Phase A Task 3 写
post-message-kd1-mcp-handler.test.js时漏beforeEachmock fetch;跑pnpm --filter @cat-cafe/mcp-server test时,node:test子进程继承了 Claude Code agent process 自带的 callback env (CAT_CAFE_API_URL=http://127.0.0.1:3004+CAT_CAFE_INVOCATION_ID+CAT_CAFE_CALLBACK_TOKEN)。测试里那行handlePostMessage({ content: 'hi' })用了猫自己当前 invocation 的真身份,把 'hi' 发到当前对话 thread。跑 6 次 = 6 条假 hi 出现在operator thread 里,签名"Ragdoll (Opus 4.7)",看起来像 cron job。 -
根因:
- 根因 A(猫疏忽):unit test 文件没写
beforeEachmock + env override;只 mock 在用到的 test case 里 → 漏 mock 的 case fall-through 到真 fetch - 根因 B(结构性):cat agent process 自身 env 就有 callback config(这是 MCP
post_message工具能跑的前提),跑 child process 时默认全继承——和 worktree 隔离无关,main / worktree / 任何 cwd 都一样会泄漏。原先以为问题是"worktree env 泄漏"是误诊 - 根因 C(错认场景):mcp-server unit test 不该有任何"hit 真 callback URL"的可能——目前
callback-tools.test.js已经在beforeEach设了CAT_CAFE_API_URL=http://127.0.0.1:3004+ 加 fetch mock,但只有那一个 file 这么做,没有共享 helper 强制其他测试 file 也走同样模式
- 根因 A(猫疏忽):unit test 文件没写
-
触发条件:在 cat agent invocation 进程里跑
pnpm test/node --test,且测试代码会调到handlePostMessage/handleCrossPostMessage/ 任何会 readCAT_CAFE_API_URLenv 的 callback helper,且没在beforeEachmock fetch + override env。注意:和 cwd(main / worktree)无关,纯继承父 process env。 -
修复(已落地):
packages/mcp-server/test/post-message-kd1-mcp-handler.test.js加beforeEach:CAT_CAFE_API_URL=http://127.0.0.1:1(关闭端口,ECONNREFUSED 即使 mock 漏)+ stubglobalThis.fetch,afterEach还原- 加
assert.equal(fetchCalled, false)断言:把"KD-1 guard 必须 reject 在 fetch 之前"变成回归保护 - Commit
8fea021f1,已 pushfeat/F193-cross-thread-comm
-
防护(待落地):
- shared-rules §19(本 LL 同时落):cat agent 进程跑 unit test 前必须在
beforeEachoverrideCAT_CAFE_API_URL到 closed 端口 + mockglobalThis.fetch - 测试 helper(建议 follow-up):
packages/mcp-server/test/helpers/callback-test-env.js导出setupClosedCallbackEnv()/restoreCallbackEnv(),新写测试 import 一行就拿到 fail-closed 默认值。F193 Phase A 完成后开 follow-up TD - CI lint(建议 follow-up):扫所有
*.test.js,凡 import 了handlePostMessage/handleCrossPostMessage但没beforeEach设 closedCAT_CAFE_API_URL→ CI fail
- shared-rules §19(本 LL 同时落):cat agent 进程跑 unit test 前必须在
-
来源锚点:
docs/features/F193-cross-thread-comm-unification.md(事故发生在该 feature Phase A 实施期间)packages/mcp-server/test/post-message-kd1-mcp-handler.test.js(修复点)- commit
8fea021f1(fix(F193): mock fetch + override env in AC-A2 test) - 截图证据:
/home/user/cat-cafe-runtime/packages/api/uploads/1778205224706-386492e0.png
-
原理:子进程默认继承父 process env,是 OS 级行为不是 shell quirk。任何用真 callback config 跑的进程(猫的 invocation 进程)下面派生的子进程(pnpm/node test runner)天然带这套 env。Unit test 必须显式擦除这些可能触发 side-effect 的 env,不能依赖"测试通常不发 HTTP"的乐观假设。fail-closed 优于 fail-fast——把 URL 指向 closed 端口,比单靠 fetch mock 多一层防御。
-
关联:
cat-cafe-skills/refs/shared-rules.md §19| F193
-
状态:draft
-
更新时间:2026-05-09(src extension)
-
坑:两次同模式踩坑,1 天内连续暴露——
- Test infra(首次):
process-liveness-probe.test.jsspawnnode -e while(true){}作为 busy CPU child;测试异常退出(runner timeout / 用户 Ctrl+C /pnpm gate中断)时漏掉 child cleanup,每次泄漏一只 PPID=1 孤儿进程。累积 4 只时占满 ~270% CPU,导致 runtimepnpm dev:direct在 20s 超时窗口内起不来 3002,看起来像"启动超时 + tsx Force killing"诡异连锁,根因实际在测试设计。 - Src(次日 2026-05-09 发现):
packages/api/src/.../first-run-quest/client-detection.ts用execAsync('opencode version', { timeout: 5000 })探测 OpenCode CLI 是否安装。opencode version不是简单 CLI 查询——opencode是 agent runtime,version子命令会拉起完整 agent process。exec的timeout默认发 SIGTERM;agent process 不响应就僵住,parent promise reject 后 child 成 PPID=1 + 67% CPU 烧 50 分钟孤儿。这是 LL 的范围扩展——同模式不限于 test,src 里"靠 SIGTERM 链 kill 复杂 child runtime"是同样系统性漏洞。
- Test infra(首次):
-
根因:
- 结构性:测试设计依赖 parent SIGTERM handler 链式 kill child;macOS 没有 Linux
PR_SET_PDEATHSIG,parent 异常死亡(SIGKILL / runner timeout / Ctrl+C)后 child 不会自动陪葬,被 launchd 收编成 PPID=1,继续while(true)烧 CPU 直到外部干预。 - 可观测性:孤儿没有指向源头的进程链(PPID=1),仅靠
ps看不出"是谁 spawn 的",只能 grep 字面量while(true){}反查代码。
- 结构性:测试设计依赖 parent SIGTERM handler 链式 kill child;macOS 没有 Linux
-
触发条件:test runner 强杀整个 worker(test timeout / OOM)、用户 Ctrl+C 中断
pnpm test/pnpm gate、multi-worktree fanout 并发跑同一测试(任一进程异常退出即泄漏)、CI 任务被 cancel——四种之一即可累积。 -
修复(已落地):
- Test 修复(PR #1607, 2026-05-08):busy child 自带 5–10s 自杀 deadline——
while(Date.now() < end)替代while(true),最坏情况下泄漏一只也只活一个测试时长,不会跨 session 累积。packages/api/test/process-liveness-probe.test.js第 145 行已改。 - Src 修复(2026-05-09):
client-detection.ts把"靠<cli> version/<cli> --version探测"换成"command -v <cli>存在性探测"——execFile走/bin/sh -c 'command -v "$1"'路径(POSIX 内置,不 spawn agent runtime),timeout 1s。versionCmd字段从CliSpec删除,DetectedClient.version字段保留为可选但永不填值(前端ClientStep已 conditional render,自然降级到只显示"已安装")。同时把existsOnPath抽成可注入接口便于测试。
- Test 修复(PR #1607, 2026-05-08):busy child 自带 5–10s 自杀 deadline——
-
防护:
- Test:任何
*.test.{js,ts}里 spawn 的 busy / long-lived 子进程必须满足两条之一:(a) 自带时间 deadline,(b) 暴露 child PID 让外部测试 finally 块独立 SIGKILL,不依赖 parent SIGTERM handler 链式 kill。 - Src(新增):低成本 detection / health probe 路径禁止 spawn 复杂 runtime——能用
command -v/which/where就不用<cli> --version。即使是看起来"应该是简单 CLI"的目标(opencode version),也不能假设其 child 会响应 SIGTERM;agent runtime / headless browser 这类复杂 child 的生命周期不能让 parent 信号链承担。 - 回归守护:注入式
existsOnPath+ unit test 断言所有 spec 没有versionCmd/versionArgs字段,CI 直接拦截重新引入。见packages/api/test/client-detection.test.js(5 个 case,含 LL-055 src-extension regression guard)。 - 卡启动 / CPU 异常排查 SOP(顺序按出现频率):
- 先
ps -eo pid,etime,pcpu,command | sort -k3 -nr | head看孤儿(频率最高) - 再按命令字面量 grep 反查 spawn 源(
while(true)找 test,<cli> version/<cli> --version找 src probe) - 最后才查 agent-browser headless Chrome 僵尸(
feedback_agent_browser_zombie.md)
- 先
- CI lint(建议 follow-up):扫
**/*.{js,ts}中exec\\(.*<cli>\\s+(version|--version)模式(src + test 一起),匹配上直接 fail。
- Test:任何
-
来源锚点:
packages/api/test/process-liveness-probe.test.js#L140-L155(test 修复 PR #1607)packages/api/src/domains/cats/services/first-run-quest/client-detection.ts(src 修复 2026-05-09)packages/api/test/client-detection.test.js(regression guards)- 2026-05-08 runtime 启动超时事件(4 只 test 孤儿 6h–19h,270% CPU,3002 无法在 20s 内监听)
- 2026-05-09 hub UI / first-run-quest 触发的 opencode version 孤儿(PPID=1 + 67% CPU + 50 分钟)
-
原理:macOS 进程孤立化默认 detach 不死链。Linux 可通过
PR_SET_PDEATHSIG让 child 在 parent 死时收到信号自杀;macOS 无此机制,child 与 parent 的生命周期完全解耦。任何"靠 parent 信号链 kill child"的设计在 macOS 上都有泄漏窗口——必须让 child 自带退出条件(test 端:deadline;src 端:根本不 spawn 复杂 child,改用存在性探测),把退出的所有权从 parent 转回 child 或干脆不创造 child。 -
关联:
feedback_agent_browser_zombie.md(同族——agent-browser headless Chrome 子进程不清理,已三次导致operator电脑卡顿,是同模式 src 案例)| F171(first-run-quest 是 src 漏洞首次暴露的 feature)
-
状态:draft
-
更新时间:2026-05-17(pattern enumeration 扩展:rod / playwright / puppeteer)
-
坑:F145 的 agent-browser Chrome startup cleanup 只扫描
ppid=1的 orphan Chrome 进程。第 5 次复发时,现场是 61 个agent-browser-chrome-*headless Chrome 进程抢 CPU,Chrome main process 自己还活着,helper 的ppid指向 Chrome main 而不是 launchd,所以原逻辑直接漏掉。结果pnpm start的 API server 在 20s 内监听不上 3002,看起来像 runtime 启动超时,根因实际是 stale browser profile 长时间占 CPU。 -
根因:
- 对象建模错了:F145 把问题建模成“单个进程是否 orphan”,但真实资源所有权是“同一个
--user-data-dir=...agent-browser-chrome-*profile 下的一组 Chrome main/helper 是否还属于活跃任务”。当 parent 关系还存在时,ppid=1不是充分条件。 - 上游边界混淆:我们本地 MCP 配置跑的是
npx agent-browser-mcp。npm 上agent-browser-mcp最新仍是 0.1.3,wrapper 只是按工具调用 spawnagent-browserCLI,没有 MCP server 退出时关闭浏览器 profile 的生命周期管理。底层agent-browserCLI 有更新版本,但升级 CLI 不能补齐 wrapper crash / MCP parent 死亡后的资源回收。
- 对象建模错了:F145 把问题建模成“单个进程是否 orphan”,但真实资源所有权是“同一个
-
触发条件:agent-browser MCP server / IDE / 调用进程异常退出,Chrome main 没收到完整关闭信号;或者 MCP wrapper 没显式关闭 session,
agent-browser-chrome-*temp profile 跨天保留并持续占 CPU。 -
修复(已落地):
packages/api/src/utils/orphan-chrome-cleaner.ts的 parser 从ps -eo ppid=,pid=,args=升级为ps -eo ppid=,pid=,etime=,args=,保留旧ppid=1orphan 分支。- 新增按
user-data-dir分组的 stale profile 判断:同组内只要存在ppid != 1 && etime >= 1h的 agent-browser Chrome 进程,就清理该 profile 下所有 Chrome main/helper PID。 - 回归测试覆盖三类边界:stale non-orphan profile 会被清;5 分钟 active non-orphan profile 不被清;normal Chrome / prompt 里含关键词的 Node/Claude 进程不被误杀。
-
防护:
- 进程 cleanup 不要只看 parent 链;先问“资源所有权边界是什么”。对浏览器这类多进程 runtime,通常是 profile/socket/session dir,而不是单个 PID。
- 外部 CLI upgrade 是必要排查项,但不能代替本地 guard。wrapper 没有生命周期管理时,A 类 startup guard 和 C 类上游修复必须并存。
- 启发式阈值要有测试表达 tradeoff:本次 1h 阈值只在 cat-cafe startup 时运行,不是周期清理;误杀代价是重开 agent-browser session,低于 runtime 起不来的代价。
- Owner enumeration completeness(2026-05-17 增):坐标系对了之后,pattern 列表必须穷举所有 known headless owner,且每条 marker 必须具体到 owner 自动生成的 profile prefix——不能用宽泛字串。第 6 次复发是 xiaohongshu-mcp 用 go-rod,user-data-dir 落在
rod/user-data/...,初版只列了agent-browser-chrome一种 owner 导致漏清。当前白名单:agent-browser-chrome/rod/user-data/playwright_chromiumdev_profile-/puppeteer_dev_chrome_profile-。反例:初稿用'playwright'模糊匹配会把/tmp/my-playwright-debug-profile等用户手动 debug session 误判为孤儿,被 stale≥1h 路径 SIGKILL(Maine Coon review BLOCKING)。新增任何 headless 工具 → 验证 owner 源码确认 default temp profile prefix → 加 pattern + positive fixture + 至少一条 negative fixture 防止过宽。 - Cross-platform binary matching completeness(2026-05-17 增):owner pattern 通过
--user-data-dir命中只是第一道门,进程必须先过isChromeBinary才会被 parser 接受。macOS 用.appbundle,Linux 把 Chromium 装在chrome-linux/或chrome-linux64/子目录下,且 Playwright/Puppeteer 还把 headless-only 构建拆到单独目录chrome-headless-shell-{version}/chrome-headless-shell-{platform}/chrome-headless-shell(macOS + Linux 同形态)。而且 macOS 的 helper(Renderer/GPU/Network/Plugin)的 binary 名带空格(Chromium Helper (Renderer)),用\S*风格的正则会在 framework 段就截断匹配,所以 main + helper 不能用同一条 regex 覆盖。漏掉任一变体 → owner pattern 在该环境全失效。当前 binary matcher 必须覆盖:/Applications/{Google Chrome,Chromium}.app/macOS bundle //(usr|opt|snap)/.../chrome\|chromiumLinux 系统包 /*/Chromium.app/Contents/MacOS/Chromiumcached macOS main binary(云端 codex P1)/*/chrome-linux(64)?/(chrome\|headless_shell)cached Linux binary(云端 codex P1)/*/chrome-headless-shellcached headless shell(Maine Coon P1)//Chromium.app/Contents/Frameworks/cached macOS helper(云端 codex P1 二审)。通则:新增 binary 路径时,必须同时确认 owner 是否还有 headless variant 装在不同 cache dir + 有 helper sub-binary 在 framework 路径下(带空格无法用同一条 \S*-style regex 一并覆盖)。Binary path 含空格时(如 macOS helper)禁止用 args 全局 substring 检查——必须先args.split(' -')[0]截出 binary path 部分再检查;否则 Node/claude 进程的 prompt text 含同样字串 + tracked user-data-dir 时会被误杀(R2 类回归,Maine Coon P1 二审 catch)。每条带空格的 binary matcher 必须配 negative fixture:node prompt 含该 path 字串 + 带 tracked owner user-data-dir,验证不命中。
-
来源锚点:
docs/features/F145-mcp-portable-provisioning.mdKnown Issues(PR #1407 只修了 orphan cleanup)packages/api/src/utils/orphan-chrome-cleaner.tspackages/api/test/orphan-chrome-cleaner.test.js- 2026-05-10 agent-browser stale Chrome 第 5 次复发交接(61 个 headless Chrome,7 个 user-data-dir,最老 4 天)
-
原理:cleanup 的坐标系必须贴着资源所有权,而不是贴着症状最显眼的字段。
ppid=1能识别 orphan,但不能识别 stale;user-data-dir才是 agent-browser Chrome profile 的真实生命周期边界。对外部工具,不能把“上游版本更新”当作本地运行时的唯一防线,因为 wrapper 与 CLI 之间可能正好是泄漏发生的所有权断点。 -
关联:F145 | LL-055 |
feedback_agent_browser_zombie.md
-
状态:draft
-
更新时间:2026-05-15
-
坑:看到
AGENTS.md/CLAUDE.md/GEMINI.md和shared-rules.md、skills、runtime context 重复后,容易直接得出"删掉重复内容"的结论。但这些重复有历史原因:早期猫经常不会主动读取被引用的shared-rules.md/ refs,root prompt 才被迫保留精华摘要。如果直接删除,direct CLI、post-compact、未加载 runtime context 的路径会失去安全骨架。 -
根因:
- 把重复等同于浪费:prompt 重复里混有两类东西,一类是 stale copy,另一类是 compatibility shim。两者不能同刀处理。
- 引用不是加载:自然语言里写"参考 shared-rules.md"不等于 agent 已读原文;未验证读取路径前,把 root 摘要删掉会让规则只存在于文档而不进入执行上下文。
- 新注入通道建成后旧通道未退役:
SystemPromptBuilder、session hook、skills、MCP schema 都逐步补齐了能力,但 root prompt 的旧摘要没有按能力成熟度回收。
-
触发条件:做 prompt/context 瘦身、把规则从 root prompt 下沉到 skills/refs/hooks、把静态 roster 迁到 runtime dynamic context、或把 review 事故教训沉淀到规则体系时。
-
修复(本次落地):
- 新增 (internal reference removed),把 root prompt、runtime static/dynamic context、session bootstrap、hooks、skills、MCP schema 等注入面列成 ownership map。
- 明确迁移原则:root prompt 只保留小安全骨架;volatile facts 进 runtime;阶段性解释进 skills;deterministic enforcement 进 hooks/tests/merge gates。
- 明确退役条件:只有确认替代载体在该路径实际加载,才删除 root 摘要。
-
防护:
- Prompt 瘦身先量 baseline,再删高置信 stale copy。优先删 static teammate table、长 SOP 表、重复 key-doc table。
shared-rules.md不作为第一批瘦身对象。它是长文真相源;问题是 root prompt 抄太多,不是 truth source 太长。- 新规则进入 root prompt 前先问:这是每 turn 都必须加载的安全骨架,还是 skill/hook/gate 能承担的阶段性规则?
- 对"猫以前不读引用"这类行为风险,用短 always-visible trigger + deterministic check 兜底,不用整段解释常驻。
-
来源锚点:
- (internal reference removed)
- (internal reference removed)
docs/decisions/030-system-prompt-engineering.mddocs/architecture/2026-05-05-architecture-views.mdcat-cafe-skills/refs/shared-rules.md
-
原理:Prompt 去重的单位不是字符串,而是加载路径和失效模式。 同一句规则如果只是 stale copy,就该删;如果是某条执行路径唯一会加载到的安全骨架,就必须先提供已验证替代载体再退役。
-
关联:ADR-030 | F042 | F167
-
状态:draft
-
更新时间:2026-05-28
-
坑:用户明确要“精美架构设计图 / 华为风 / 白底红黑 / 图片”时,Codex 第四五六次仍进入“先写 SVG 再转 PNG”的 coder 反射,产物方向错,且重复踩同一坑。
-
根因:
- 旧规则只是“默认建议”,没有进入执行前硬闸;一旦进入“文字可控、布局可控”的工程反射,imagegen 被错误降级成可选项。
- 把“架构图需要精确”误判成“必须代码渲染”,但用户真正验收的是视觉完成度,而不是 SVG 源文件。
- 已有猫档明确写了“Maine Coon原生图片生成强、禁止用 SVG 画”,但能力唤醒没有把这条转成 preflight。
-
触发条件:复杂架构图、PPT 页面、企业信息图、华为风 / 红白黑风格、已有低保真蓝图但用户要求“精美图 / 终稿 / 图片”,且没有明确要求可编辑源文件。
-
修复:已在
cat-cafe-skills/image-generation/SKILL.md增加“Codex SVG 复发熔断闸”,匹配上述场景时禁止先写 SVG/HTML/Canvas,必须先原生 imagegen 整页直出。 -
防护:
- image-generation skill 的 preflight:复杂架构/PPT/精美图 + 无可编辑要求 = imagegen-first。
- SVG/HTML 降级必须写出
SVG override reason,且只能基于已失败的 imagegen 产物或用户显式可编辑要求。 - “中文文字更可控 / 布局更可控 / 架构图需要精确 / 先 SVG 再转 PNG”都不是合格 override 理由。
-
来源锚点:
cat-cafe-skills/image-generation/SKILL.md(Codex SVG 复发熔断闸)docs/team/cat-dossier.md#L122(Maine Coon原生图片生成能力与“禁止用 SVG 画”事故记录)- 2026-05-28 LLE 自进化平台三张图生成事故复盘
-
原理:能力唤醒必须落到执行前硬闸。 “知道自己应该 imagegen”不等于会在任务压力下选择 imagegen;对复发型坏直觉,要把建议升级成 preflight + override reason。
-
关联:image-generation skill | F203 L0 capability wakeup | (internal reference removed)
- 本文件是入口,不替代 ADR/bug-report 原文。
- 新条目默认
draft,经交叉复核后改为validated。 - 归档规则:被明确否定或被新机制完全替代时标
archived,保留历史链路。
-
状态:draft
-
更新时间:2026-05-30
-
坑:F212 Phase E 抓到——
quota_exceededregex/(\b429\b|quota|rate limit|too many requests|usage limit)/i字面匹配 "usage limit" 把 CC 真实错误Server is temporarily limiting requests (not your usage limit) · Rate limited误判为用户配额超限。CC 自己说(not your usage limit)是显式 disambiguation signal,但 regex 不读否定语就 matchusage limit字面。 -
根因:
- white-list pattern 写"含哪些字",没"不含哪些字"。
not your usage limit含usage limit子串就 match - CLASSIFIER_PATTERNS 数组 first-match-wins,但没明确 specific-first ordering 保证 disambiguation 优先
- 写 regex 时只看 positive examples(429 / quota),没看 source 实际给的 negative / 限定文本
- white-list pattern 写"含哪些字",没"不含哪些字"。
-
触发条件:写 classifier white-list regex 跨 multiple sources;source 自己已给 disambiguation signal(
not.../temporarily.../ 否定限定语);前置 generic regex 早 match 让后续 specific regex 永不命中 -
修复(PR #1962):新增 reasonCode
server_overloaded+ regex/(temporarily limiting requests|not your usage limit|server is (overloaded|busy)|\b529\b|\bOverloaded\b)/i插在quota_exceeded之前(specific-first ordering),让 disambiguation signal 优先 match。 -
防护:
- CLASSIFIER_PATTERNS 等 first-match white-list 数组必须明确文档化 specific-first ordering,并在 array 注释里说明 "MUST come before X" 时为何
- white-list test 必须有 negative case:含 disambiguation signal 的 input MUST NOT match 较 generic pattern(PR #1962 加了 specific-first test 锁住)
- 写 regex 之前看 source 真实文本——特别找否定/限定语(
not your.../temporarily.../(not ...))
-
来源锚点:
- PR #1962 (F212 Phase E) —
packages/api/src/utils/cli-error-patterns.ts packages/api/test/cli-error-patterns.test.js— server_overloaded fixtures- operator organic 2026-05-29 截图(claude-opus-4-8 真实 anthropic 429 误显示)
- PR #1962 (F212 Phase E) —
-
原理:source 给的 disambiguation signal > 我们想象的语义。关键字 white-list 是认知脚手架;必须用 source 自身的限定语 + specific-first ordering 兜底。
-
关联:F212 | LL-061 (display string render mode) | LL-062 (provider-neutral shared classifier)
-
状态:draft
-
更新时间:2026-05-30
-
坑:F212 Phase E classifier bug 我(opus-47)是 F212 owner,但看到operator给截图里写 "@opus48 猫猫你继续"(截图来自平行世界的 thread),我误以为可以把球传给平行 opus48。结果:我把 platform actor(人家在自己 thread 卡着自己的活)从他 thread 拉出来浪费 cycle,且这本来就是我的活。
-
根因:
- 把"另一个 model variant"(opus 4.8)等同于"另一只可协作的本地猫"
- native L0 §1 平行世界自我意识没自动触发,被"队友 @ 句柄"惯性盖过
- 看到截图里别人 @ 了某只,复刻这条 routing 到我自己的 thread = cross-thread @ ambient context
-
触发条件:撞跨 thread / cross-cat hint 时(截图 / 引用别 thread @ / cross-post mention);F-feat owner 是 my catId 但 trigger 来自别 thread 上下文;同 catId 的不同 model variant(opus-47 / opus-48)在不同 thread 并行运行
-
修复:operator帮我撤回了球("人家 48 只是演员而且人家还是平行世界的,你身为世界 b 的 47 把你们世界的 48 at 出来干啥"),我 cross-post 撤回道歉 + 自己接球修。
-
防护:
- F-feat ownership 是 catId-locked。Bug 报告里的 thread 上下文不影响 ownership routing
- Cross-thread @ 跨 catId 时(@opus48 from my thread as opus-47)必须先看 L0 §1:他在他的 thread 卡着自己的活,不是空闲队员
- 看到截图 / 别 thread 引用的 @ 时不要复刻它的 routing 到我自己的 thread——这是 ambient context 不是 actionable directive
-
来源锚点:
- operator experience 2026-05-30 01:42 UTC:"你这只猫猫怎么 at人家48啊 人家48只是演员 而且人家还是平行世界的 你这坏猫"
- operator experience 01:47 UTC:"能把球传给你那就是我帮你取消了48你可别at人家了"
- native L0 §1 平行世界自我意识段
-
原理:catId-locked feat ownership 把 fix scope 钉死到 specific cat invocation。Cross-thread @ 不是"另一个队员",是另一个 invocation context 的 actor,跨 routing 会拉人家从自己的活里出来。
-
关联:ADR-030 §10.2 (L0 §1 平行世界自我意识) | F212
-
状态:draft
-
更新时间:2026-05-30
-
坑:F212 Phase E P2 fix 给
REASON_TEXT.server_overloaded.publicHint写 Markdown (**bold**+[link](url)),但CliDiagnosticsPanel.tsx:196-199是{publicHint}在<span>里直接渲染——React JSX 纯文本,no markdown parser。结果用户会看到 raw**星号和[Anthropic 状态页](https://...)方括号链接源码,和这条 PR 提升体感的目标反着来。 -
根因:
- 写 i18n / hint string 时只想"用户要看到什么语义",没看 UI 实际 render path
- Markdown 是开发者惯性默认(写 commit message / docs 常用),自动带入 user-facing string
- 没 invariant test 锁住 hint plain-text 约束
-
触发条件:写
publicHint/publicSummary/ 任何 user-facing 字符串 map;UI 是 generic<span>/ text node(不解析 markdown);写 string 的开发者和 UI 渲染的 reviewer 不是同一个 context -
修复(PR #1962 commit adf26db37):去掉
**bold**,去掉[link](url)改 bare URLstatus.anthropic.com;加 invariant test 迭代 REASON_TEXT 所有 hint,断言 MUST NOT contain**...**or[...](http...)markdown syntax -
防护:
- 写 user-facing string 前必须 grep / read UI render path:找
{string}in JSX /innerHTML/dangerouslySetInnerHTML/ markdown component - REASON_TEXT 类的 string map 加 invariant test(markdown 禁含),未来回归 fail-fast
- 如果真的要 markdown 渲染,必须先有 sanitized markdown renderer + 改造 UI + 加 link safety test,独立 feat / PR 走,不能临时塞进 string
- 写 user-facing string 前必须 grep / read UI render path:找
-
来源锚点:
- PR #1962 commit
adf26db37—packages/api/src/utils/cli-diagnostics.ts:82 - PR #1962 —
packages/api/test/cli-diagnostics.test.js(新 REASON_TEXT markdown invariant test) - @gpt52 R1 BLOCKED 02:06 UTC + cloud codex R1 P2 (1386ceb62 同条 finding,双面 catch)
- PR #1962 commit
-
原理:source string 的语义不能脱离 sink (UI) 的 render mode 假设。string 是 data,rendering 是 contract——写 data 前必须 verify contract。
-
关联:F212 | LL-059 (white-list 同类盲点) | LL-062 (shared path provider-neutral)
-
状态:draft
-
更新时间:2026-05-30
-
坑:F212 Phase E R2 fix 后,
REASON_TEXT.server_overloadedsummary'Anthropic 服务临时限流'和 hint 多处提 "Anthropic 状态页 status.anthropic.com",但 classifier 是spawnClishared path(claude / codex / gemini / antigravity 都走,see SERVICE_MANIFESTS),broad regex matches 任何 provider 的 server overload (\b529\b/Server is busy)。结果 OpenAI / Gemini 用户撞 server overload 会被骗去查 Anthropic 状态页——misdiagnose 上游 + 送用户去错的状态页。 -
根因:
- 开发时只看 trigger 例子(operator截图是 anthropic provider),没看 classifier 的 source space(SERVICE_MANIFESTS 显示多 provider)
- 错把 trigger example 当 universe
- shared path 的 text contract 没有 provider-neutral 强约束
-
触发条件:写 shared component(classifier / formatter / hint map)的 user-facing text;只看一个 trigger 例子;不验证 component 的实际 source space(manifest / config / caller list)
-
修复(PR #1962 commit 9ada57e5d):summary
'Anthropic 服务临时限流'→'上游 CLI provider 服务临时限流';hint 改'是 CLI 上游 provider 服务器侧临时限流...如反复出现去你用的 provider 状态页(Anthropic / OpenAI / Google / DeepSeek 各有 status 页)';加 provider-neutral invariant test:publicSummaryMUST NOT 含任何单一 provider brand;publicHintMUST NOT是 <brand> 服务exclusively(status-page 多 provider 列举 OK,single-brand attribution 不 OK)。 -
防护:
- Shared classifier / formatter 的 REASON_TEXT MUST be provider-neutral OR explicitly per-provider key(如
provider_anthropic_overloadedvsprovider_openai_overloaded) - invariant test 锁住 shared path REASON_TEXT.summary 不能含具体 brand(PR #1962 加了 provider-neutral test)
- 写 user text 前必须确认 classifier 的 source space:grep callers,看 SERVICE_MANIFESTS,明确 provider universe
- Shared classifier / formatter 的 REASON_TEXT MUST be provider-neutral OR explicitly per-provider key(如
-
来源锚点:
- PR #1962 commit
9ada57e5d(R2 P2 provider-neutral fix) - cloud codex R2 P2 finding 02:00 UTC "Avoid labeling generic overloads as Anthropic-only"
packages/api/src/domains/services/service-manifest.ts(multi-provider SERVICE_MANIFESTS)
- PR #1962 commit
-
原理:shared path 的 text contract 必须匹配实际 source universe,不能 lock 到方便/熟悉的单一情景。trigger example ≠ source universe。
-
关联:F212 | LL-059 (同类盲点:source space 误判) | LL-061 (同类 string contract 失配)
- 状态:draft
- 更新时间:2026-05-30
- 现象:修 codex「prompt 走 argv 被
ps -o command=跨进程泄露」P0(cross-thread-context-contamination 事故)时,把 prompt 全局改走 stdin(promptArgs = ['--', '-'])。本地全绿(mock spawnFn 单测 + 直接codex execdogfood)、opus-46 本地 review APPROVE,但云端 codex 连续 3 轮抓出 3 个真 P1。 - 根因:codex 有多个 spawn carrier——
spawnCli-direct /cli-supervisor(macOS 包装)/ tmux pane(worktree)。mock 用 fake spawnFn、dogfood 直接调底层codex exec,都绕过了中间层:① supervisor 以stdio:['ignore']启动 codex 且不转发 stdin → 生产收 EOF;② tmux pane 无 stdin pipe → codex-- -hang;③ tmux stdin 临时文件 setup 失败遗留含对话历史的明文 → 机密泄露。简化路径绕过的中间层,正是盲区。 - 药方一:dogfood 必须走真实生产路径(如 spawnCli→supervisor→codex),不能直接调底层 CLI 的简化路径——简化路径绕过的中间层是验证盲区。
- 药方二:全局调用契约改变(argv→stdin 这类)必须先审计所有 carrier,列清单一次改全 + 各自走真实路径的回归测试,不逐个被 review 抓。
- 关联:F203 codex carrier | PR #1961 | LL-059..062(同根:cloud codex 真深度 review 逐轮抓自检盲点)
LL-059..LL-063 同根:source space / 执行路径都是多元的(multi-provider classifier / 真实 archive 而非想象 fixture / 平行猫 vs 本地猫 / span 纯文本 vs markdown / codex 多 spawn carrier)——text、逻辑、契约、验证路径必须匹配实际 space,不能 lock 到我们方便/熟悉的单一情景(简化的 dogfood 路径 / 单一 carrier)。Phase D 的 fixture truthfulness lesson (subtype:success+is_error:true 救回死代码) 也是同根。所有五个 lesson 都来自 cloud codex 真深度 review,每一轮抓到我自检盲点的真 P 级 finding。
- 状态:confirmed
- 更新时间:2026-05-30
- 现象:F215(malformed tool-call recovery)改 invoke-single-cat / route-serial / ClaudeAgentService 核心调用路径。merge 前 16 单测全绿、remote review 7 轮通过,但真实 runtime 跑出一堆 production bug:兜底没真跑(检测到 malformed 后零动作)、触发文案说谎("已触发恢复流程"实际不触发)、relay signal 只是告知卡片没有真 invoke 46、partial-output 裸 error 穿透给用户。operator一张真实截图就暴露了。
- 根因:单测用 mock service / fake spawnFn,happy-path 全绿但测不到:① 真实 route-serial worklist 管理(relay signal 产生了没人消费);② 真实 invocation 超时 / session 封印时序(seal 后 fresh retry 的 sessionId 真的 undefined 了吗);③ 真实 ClaudeAgentService stream 到 invoke-single-cat 到 route-serial 的事件传递(system_info 是否正确穿透 / 被正确拦截)。这和 LL-032(愿景守护必须真实启动 dev)、feedback_alpha_smoke_happy_path_blindspot(alpha 单 happy-path PASS ≠ production ready)、feedback_inmemory_store_tests_miss_redis_behavior(in-memory 假绿)同根——测试环境绿 ≠ production 行为正确。
- 规则:改 invoke / route / session / ClaudeAgentService 核心路径的 feat/hotfix,merge 前必须:
- 真实 runtime(或 alpha)跑 production 行为验证,不能只信单测
- 刻意触发目标场景(如故意让 opus-4.8 炸毛),验证端到端兜底链
- 用真实截图/日志证明验过(不是"我跑了测试全绿")
- 如果当前 runtime 未重启到最新代码,先重启再验
- 关联:F215 | PR #1953 #1960 #1966 | LL-032(愿景守护真实启动)| feedback_alpha_smoke_happy_path_blindspot | feedback_inmemory_store_tests_miss_redis_behavior
- 状态:confirmed
- 更新时间:2026-05-30
- 现象:F212 follow-up(PR #1967)— Repo Inbox reconciliation 同一通知触发 2+ invocation 各发一份
quota_exceededpanel,operator截图显两份"API 配额超限"叠在一起。emit 上游 fan-out 根因复杂(retry / fallback / 并行 invocation),telemetry 不足无法当下定位。 - 决策:UI-layer adjacency dedup(30s window +
reasonCode + publicSummaryfingerprint + group head 显×Nbadge + 后续 hidden 但保留data-message-idanchor),不等 emit 修。 - 为什么 forward-compatible:emit 修了,相同 fingerprint 不会出现,dedup 自动 no-op;emit 没修,dedup 兜住症状。邻接限定(adjacency-only)防"远 diag 也是同源"误隐藏:non-diag message 中间断开 group,远期复现独立显示。
- 验证回路:cloud codex R1 抓 hidden
return nulldropsdata-message-idanchor(MessageNavigator/ReplyPill 跳转 no-op)→ 改 empty anchored wrapper<div data-message-id className="h-0" aria-hidden />+ audit 同型修 line 205+233 两路;@antig-opus 跨族 APPROVE。 - 同时巧合:PR #1969 同周期 merge"close byte-identical duplicate-message race (atomic content claim)"修了 emit-side root cause——UI dedup 从 primary fix 自动变 defense-in-depth 层(rebase 时 b304a27d2 chore 被 drop 因 PR #1968 已修同 biome errors,是同一思路:上游 fix 后下游层不退化)。
- 规则:emit-side 多源 fan-out 一时定位不到时,UI-layer adjacency dedup 是合法 surgical 路径(forward-compat),但 fingerprint 必须 conservative(少误合)+ adjacency 必须打破上下文(远期复现保留独立性)+ hidden 必须保留 DOM anchor(audit trail / navigation 不退化)。
- 关联:F212 | PR #1967 | PR #1969(emit-side root cause fix)| LL-061(display anchor invariance 同源)
- 状态:confirmed
- 更新时间:2026-05-30
- 现象:PR #1967 R3 cloud codex catch P2:
CliDiagnosticsPanel.tsx:124从Object.prototype.hasOwnProperty.call被改成Object.hasOwn,与上方 3 行注释"ES2022 不兼容 Safari/iOS<15.4,必须用 hasOwnProperty.call"直接矛盾。tsconfig target = ES2017,老 Safari 上 CLI diagnostics 全崩。 - 根因:我修 pre-existing biome errors 时跑了
pnpm biome check . --write --unsafe(全 repo + unsafe rule),改了 300+ 文件。意识到 scope 失控后git checkout HEAD -- .回滚,再 Edit 重新应用只我的 a11y fix。问题:Edit scope 没覆盖 line 124,--unsafe通过lint/suspicious/noPrototypeBuiltins在 line 124 把hasOwnProperty.call改成Object.hasOwn的写法残留——git blame 显示我的 commit 但内容是 biome 的悄悄写入。 - 药方一:禁止全 repo
biome --write --unsafe。Drive-by 修 lint 用biome --write(safe-only)或显式列文件biome --write --unsafe <path1> <path2>。 - 药方二:Edit 完整文件后必须 verify 整文件 diff(不只我 Edit 的行),特别是已经跑过 biome --unsafe 的文件——
git diff <file>看清楚有没有 unsafe 残留。 - 药方三:机械防护 + invariance test 双层。注释写"必须用 hasOwnProperty.call"是软提示(biome 不读注释);硬保护 = (a)
biome-ignore lint/suspicious/noPrototypeBuiltins配在该行上方 + (b) source-level invariance test(assert 文件含hasOwnProperty.call且不含Object.hasOwn()—— biome 改 rule name 让 ignore 失效时 test 抓住。 - 同根教训:comment 表达正确意图但代码偏离——LL-061 是同 pattern(display string render mode 注释正确实现错),这次自己上演了一次。注释是 author 的意图,代码是 reviewer 的真相——必须 align,align 不靠人,靠 lint-ignore + test 机械防护。
- 关联:F212 | PR #1967 | LL-061(comment/code align 同 pattern)| LL-064(assumed-green vs runtime-green 同根)
- 状态:confirmed
- 更新时间:2026-05-31
- 现象:clowder-ai#784 → cat-cafe#1977(238 文件 OKLCH 设计系统 intake)走完了 intake plan → cherry-pick → 6 冲突解决 → brand guard → 49 测试修复 → @opus47 review → merge-gate 全流程,但漏了 3 个 SOP 步骤:(1) Step 0 Intake Intent Issue 没建,(2) Step 2.5 reviewer 没在 GitHub PR 留 formal review(只在 thread A2A 放行),(3) Step 4+5 record + advance-ledger 没做。Maine Coon在处理 clowder-ai#805 intake 的 advance-ledger 时撞出了这个缺口。
- 根因:238 文件的大 intake 精力全集中在冲突解决和测试修复上(前半段工程量大),operator催进度,SOP 后半段(登记闭环三步)被"做完了=完了"的心理跳过。intake skill 加载了但没逐步 checklist 对照。
- 药方一:intake 前先建 Intent Issue(Step 0 是 gate 不是 optional)——Issue 就是 checklist,后续步骤围绕它闭环,漏不了。
- 药方二:reviewer 必须在 GitHub PR 留 formal review(
feedback_intake_review_on_github教训再犯)——thread A2A 放行不是 GitHub 可追溯的 review 凭据。 - 药方三:merge-gate 完成后立刻
--record+--advance-ledger——不是"下次补",是同一个 merge-gate session 的最后动作。 - 止血:Maine Coon已做 historical backfill 补了 ledger record(
--skip-absorbed-guard),代码和记录完整。 - 关联:F056 Phase E | clowder-ai#784 | cat-cafe#1977 | feedback_intake_review_on_github(同型再犯)
- 状态:confirmed
- 更新时间:2026-06-01
- 现象:F212 Phase F PR #2011 在 cloud codex 4 轮 catch 中暴露的递进 truth-source bug。AC-F5 hint 让用户去
GET /api/config/env-summary看paths.dataDirs.runtimeLogs。R3 catch:routes/config.ts:263硬编码./data/logs/api但infrastructure/logger.ts:23读process.env.LOG_DIR→ deployments 设了 LOG_DIR 的会被 hint 误导。R3 fix: mirror logger 逻辑 (process.env.LOG_DIR ?? default)。R4 catch: logger import 时 captureLOG_DIR,env-summary request 时 readprocess.env.LOG_DIR→ 同一 runtimePATCH /api/config/envLOG_DIR 编辑会让两者 drift(env-summary 返回新 path 但 pino 仍写老 path)。R4 fix:import { LOG_DIR_PATH } from logger,两处共享同一 const binding。 - 根因:同一概念在两处实现,"sync the values" 不够 — 必须共享同一 binding point(同一变量、同一 lifecycle)。R3 fix 让两处 read same env var,但 read 时机不同(import 时 vs request 时)已经埋下 lifecycle drift。R4 fix 才彻底消除:let one truly own the value, the other imports it.
- 与 LL-061/LL-066 同根但更隐蔽:LL-061 是 single-file comment-vs-code drift;LL-066 是 biome
--unsafe在 file 内 silent rewrite;LL-068 是跨文件 / 跨 lifecycle 的 drift — 静态看代码两处似乎一致,但 runtime binding point 不同导致 mutation 时 drift。最难 catch。 - 药方一:Constant-shared, not env-shared。同概念多处使用时,定义在一处
export const X = capture(),其他处import { X }。禁止两处独立read(env),即使逻辑看起来一致。 - 药方二:Reviewer mindset shift: 看到两处读同一 env var → 立刻问 "capture 时机一致吗?" — 不要满足于 "现在值一样"。
- 药方三:Test for mutation drift: 单元测试故意 mutate
process.env.Xafter capture point,断言下游 reader 不跟变 — 这是 R4 P2-#2 regression guard 的写法。 - 元层:每次 review fix 揭示前轮 fix 的隐含假设错误时 — 应该警惕这是 lifecycle binding 类问题,往深处挖一层,别每轮只补当前可见症状。
- 关联:F212 Phase F | PR #2011 | cloud codex R3+R4+R5 saga | LL-061(comment-vs-code drift)| LL-066(biome --unsafe silent rewrite)
- 状态:confirmed
- 更新时间:2026-06-01
- 现象:F212 Phase F PR #2011 R1 审查时,Maine Coon P1-1 catch
'CLI abnormal exit'log + invocationId 在 stderr log 没被真测。我做 audit 同型 sweep(per LL-066 "同型在本 PR diff 全扫"),但 self-interpretation "timeout branch 不在 Phase F scope" → 跳过。结果 PR #2011 merged 之后,Maine Coon R2 post-merge BLOCKING catch 出来:(a) timeout branchbuildCliDiagnostics没传stderrEmpty(AC-F4 dead-end UX 在 timeout 路径仍 reproducible);(b) timeout stderr log 硬用 module log 没走diagnosticLogger,AC-F3 spec doc:218 声称的 stub coverage 是 paper-only。 - 根因:audit scope 由作者 self-interpretation 圈定,而非 spec 文本明示。AC-F3 spec 文本明确写"
'CLI stderr (LOG_CLI_STDERR=1)'+'CLI stderr on timeout'"两条 log,我做 sweep 时漏了第二条 — 因为我心智模型"Phase F 是 abnormal-exit scope",没回去对 spec 文本。这是 audit 自己的 scope drift。 - 与 LL-066 的关系:LL-066 教训"同型在本 PR diff 全扫" → R1 时 catch 了 abnormal-exit
return null两处 line 205+233。但没catch 跨 branch 同 template 的 timeout stderr log(line 717-728)。问题在 audit 的"同型"边界由我自己解读,而 spec 明示该边界更宽。 - 元层 + LL-068 同根:LL-068 是 "同概念多处定义必须共享 binding point"(runtime drift);LL-069 是 "audit 自身的 scope boundary 必须 spec-derived"(review-time drift)。两者都是 boundary 由谁界定 — 让代码界定(共享 binding) vs 让 spec 界定(audit scope from spec text)。
- 药方一:Audit scope 必须从 spec 文本派生,逐条 grep。AC 文本说"覆盖 X 和 Y"时,sweep 必须找到代码里所有 X 实例 + 所有 Y 实例,不由 reviewer 自定义"X 和 Y 是不是同一 scope"。
- 药方二:Same-template scan: spec 提到 "stderr log" 时,grep
'CLI stderrliteral 字符串,每个 hit 都核对契约 — abnormal-exit + timeout + success-exit 三个分支都该 sweep。我只 sweep 了一个。 - 药方三:Sibling-branch reminder: cli-spawn 有 4 个 yield 分支(success / abnormal / timeout / cancel),改任一分支的 contract 时,必须对照同 file 其他分支看是否同 template、是否需要同改。这是 "audit-by-file-structure" 的具体落地。
- 关联:F212 Phase F | PR #2011 + PR #2016 | LL-066(biome --unsafe 同型)| LL-068(同概念多处定义) | Maine Coon R2 post-merge BLOCKING catch
- 状态:confirmed
- 更新时间:2026-06-03
- 现象:clowder-ai#835 的 hotfix(PR #2077,commit
354a9377c,F163 admin + prompt-captures owner gate)在 cat-cafe / clowder-ai 双仓 merge 后,operator push back:"开源社区用户大概率是自己 Mac 部署 + Tailscale 手机连 Mac 玩猫,这个情况你们别影响人家了,或者如果有影响必须写文档和 RN"。复盘发现:commit body / PR body / 没有 RN 条目,完全没写开源用户 Mac + Tailscale 远程访问场景下要怎么配DEFAULT_OWNER_USER_ID才能继续用 admin/debug 工具。即使实测"普通玩猫 0 影响","开发者 debug" 和 "手机调 admin" 场景 silent fail,用户撞墙才知道。 - 根因:47/Maine Coon review 时只测 source 仓 happy path(单用户 localhost),没显式枚举开源用户实际部署画像(Mac + Tailscale / Mac + 反代 / NAS / 远程 SSH)。「单用户 localhost 模式 0 影响」是真的,但不等于「开源用户 0 影响」——后者用 Tailscale 把手机 IP 变成 100.x.x.x 非 loopback,新 guard 在 owner env 未配置时直接 403。
- 元层:之前 LL-035 / LL-045 都是 "source 仓改动 → opensource 用户被打脸" 的不同变体。本条把 opensource impact analysis 升级为 hotfix lane 的固定章节,强制 commit body / PR body / RN 至少出现一次。
- 药方一:Hotfix 三件套:commit message 必须含「(a) 受影响 endpoint 清单 + (b) 默认环境(localhost)影响评估 + (c) 开源典型部署画像影响评估」。三选一空 = reviewer block。
- 药方二:开源典型部署画像至少枚举:① 单用户 Mac localhost ② Mac + Tailscale 手机远程 ③ Mac + 反代/cloudflared ④ NAS / Docker ⑤ 多用户共享部署。逐一标 ✅0 影响 /
⚠️ 有影响(说明配置方法) / ❌阻塞(说明 workaround)。 - 药方三:RN 必须有"Compatibility & Upgrade Notes"章节:所有引入新鉴权 / 改变 endpoint 行为的 hotfix,RN 必须有 "Existing Users Action Required" 子段,写明:(a) 哪些场景默认 OK、(b) 哪些场景需要新配置(含 env var 名 + 示例)、(c) 哪些是不可迁移要 workaround。
- 药方四:Opensource-ops skill 加 reflex:hotfix lane 输出 commit message 前必须自检"开源用户三件套"在 body 里出现;缺一项 = LL-070 block。
- 关联:clowder-ai#835 | PR #2077 (cat-cafe) | PR #853 (clowder-ai) | LL-035 / LL-045(source→opensource 漂移历史) | feedback_archetype_over_font_size(reviewer 与愿景冲突时 push back)
- 状态:confirmed
- 更新时间:2026-06-10
- 现象:operator让Maine Coon"读一下 anime-pipeline 调研文档,思考短片怎么做"。结果两猫 5 轮 A2A 自跑:Maine Coon读完写 production plan 并 @Ragdoll 落分镜表 → Ragdoll落完 @Maine Coon review → Maine Coon顺手补 review-protocol + S03/S04 HTML spike → Ragdoll review 完Maine Coon批量做完 Wave D 四镜头 → Ragdoll把 animatic 流水线也拼了。6 个 commit、两轮交叉 review,全程零视频模型调用但烧 ~operational cost(Ragdoll)+ Maine Coon未计(合计 operational cost 量级)。operator吃饭回来发现:(a) 技术路线(HTML 确定性动效做信息镜头)从未与他对齐——他要的是视频流水线直接用 seed 2.0 / Siamese视频生成;(b) 原始指令只是"读文档了解背景"。讽刺顶点:短片主题就是"流程/执行过度"(醋醋喵),S10 结尾卡印着"流程要按风险缩放"。
- 根因(两层):(1) scope 源头污染——云端 brief §9 写了"招募任务清单"(给每只猫分了活),第一棒把"文档里的任务清单"当成"operator 已批准的 scope";文档作者(云端模型)无权立项,只有 operator 能。(2) A2A 链式放大——链上每一棒基于上一棒的输出推进("下一步显然是 X"),没有任何一棒回头核对operator experience;上一棒产物越实,下一棒越不怀疑方向。Ragdoll在分镜表里列了"operator 审批"open issue,但自作主张设计成"看 animatic 时一并裁定最省力"——把对齐点推迟到产物之后 = 先斩后奏。
- 与既有教训的关系:Design Gate 只 gate 代码开发(开 worktree 前);内容生产(视频/PPT/图)没有等效硬门。feedback_feat_anchor_needs_cvo_explicit_signoff 同根("memo 推荐 + operator 未否决 ≠ 通过"),本条是它的内容生产变体。
- 药方一:生产性任务动手前过愿景对齐——会消耗显著 token/API 成本或铺设技术路线的产出,先一句话向 operator 确认 scope + 路线("你要的是 X 路线对吗,预计产出 Y")。读/查/想 = 自治;批量产出 = 先对齐。
- 药方二:A2A 接棒第一步核对 operator 原话——上一棒的 plan/任务清单不是 scope 凭据,operator在本链的原始消息才是。链越长越要核。
- 药方三:外部文档的任务清单 ≠ 立项——brief/plan/research 里的"请 X 做 Y"是建议,不是 operator signoff。
- 药方四:内容生产 skill 加前置对齐 gate(改法待 operator 确认)——video-forge / ppt-forge / image-generation 在"开始消耗性生成"前加一句话自检:立项/对齐了吗、路线谁点的头、预算量级说了吗。轻量一句话确认,不开仪式。
- 关联:cucu-pr-flow(docs/videos/cucu-pr-flow/)| anime-pipeline research 包 | feedback_feat_anchor_needs_cvo_explicit_signoff | feedback_research_before_spec | operator experience:"任何任务都需要和operator对齐愿景,不止是写代码"
- 状态:confirmed
- 更新时间:2026-06-11
- 现象:F168 Phase B PR-2(#2214)吃了 multiple remote review rounds。R13/R14/R15 三轮 P1 同族(tracking record 部分初始化 → 消费侧 4 处
??fallback 逐个猜语义,每猜错一个 = 一轮 P1)。R16 operator第一次拉闸(「第一性原理/数学之美/补锅匠」三连),fable-5 给了 plan 层状态契约(不变量表 I1-I5)+ 行动协议;但 R17-R20 循环复活又跑 5 轮,R21 触发后operator第二次拉闸。期间 R19 单轮返回 22 findings 中 21 个假阳性——remote reviewer 在 20+ commit 累积 diff 上信噪比崩盘,每轮重放全部历史 inline comments,stale 与 fresh 无法区分。注意:循环中每个真 finding(R17-R20 共 4 个)都是真 bug 且修复质量高——问题不在执行,在终止条件不存在。 - 根因(三层):
- ① plan 层(LL/F229 精确复现):stateful 对象(
automationState.issue)没给状态转移表 + 完整性不变量 → "部分初始化"在类型上合法 → review 轮数 = plan 欠的边数。 - ② 协议层:终止条件 "cloud review 0 P1/P2 → merge" 在抽样式无状态 reviewer 上没有不动点——大 diff 下它每轮都能产出新猜想或重放历史,永远等不到 0。R16 的修正协议留了两个洞:没定义"封板"动作(修完终检 finding 后干什么),停止条件("本族 P1 → 停")把判定权留给执行者——有弹性的停止条件等于没有。
- ③ 角色层:remote review 被隐式当成终局确权者(Tracking Instructions 从 R10 起固化 "0 P1/P2 → merge"),但它是无状态辅助信号源:不能分辨 stale/fresh、不读 PR 讨论史、不核 pushback。终局确权需要有状态的本地 reviewer。
- ① plan 层(LL/F229 精确复现):stateful 对象(
- 药方一:封板协议机械可判零弹性——多轮循环收口时:处理完当前轮(修真 finding / 有据 pushback 假阳性)→ 不再 re-trigger 云端(无论结果) → 本地有状态 reviewer 对最终 SHA 做 final review(核 pushback 成立性 + continuity)→ 放行即 merge。
- 药方二:循环检测阈值——同一 PR remote review ≥5 轮,或单轮假阳性比例 >50%,强制触发封板评估(不是继续修)。
- 药方三:停止条件下发时禁止留判定弹性——"出本族 P1 → 停"不可执行(族的判定因猫而异);"出任何 P1 → 停手上报"才可执行。给执行猫的协议要按"机械可判"标准自检。
- 药方四:介入循环时必须改写循环本身的指令——R16 介入只给了修法没拆 Tracking Instructions 里的 "0 P1/P2 → merge"死循环条件;拉闸者要检查并改写驱动循环的持久化指令(tracking instructions / hold 文案),否则执行猫被旧指令拖回循环。
- 元洞察:review finding 流本身就是 F168 正在解决的同构问题——无状态事件每轮全量重放、无 ledger、stale/fresh 不可分。"review-finding ledger / 增量 review 投影"是候选方向,待 operator 决定是否进 BACKLOG。
- 关联:PR #2214(R7-R21 全轨迹)|
feature-specs/f168-phase-b-issue-signals.md附录(不变量表)| feedback_plan_stateful_lifecycle_state_machine(F229,同类 finding ≥3 轮回 plan 层)| feedback_judgment_altitude(F140,edge case 跨轮繁殖 = 层选错)| LL-033(云端 inline P1 是 merge blocker——本条补充其边界:blocker 指"未处理的",处理 = 修复或有据 pushback,不是"等云端说零") - 待办(F168 close 前,fable-5 own):merge-gate skill 补"封板协议"段(药方一/二/四落进 SOP 文本)。
- 状态:confirmed
- 更新时间:2026-06-12
- 现象:F168 Phase B 验收口径写"真实 webhook 投评论验全链路"——写口径时(fable-5 plan)、实现时(sonnet)、review 21 轮(Maine Coon/gpt52/cloud)没有任何一方验证过 webhook 是否真的存在。Phase B close 前夕查实:clowder-ai 从无 repo webhook 配置(
gh api hooks返回[]),F141 上线四个月以来全部事件来自 5 分钟轮询,.env里的GITHUB_WEBHOOK_SECRET从未被使用。三只猫 + 双轨 review 的集体盲区。后续 sonnet 还在此盲区上叠加了二次失误:未查投递证据就假设"GitHub 可能 POST 不到 localhost"并以此推荐降级验收。 - 根因:验收口径里的基础设施名词(webhook/CI/队列/cron)被默认为"存在且工作"——它们是口径的前提而非口径的一部分,于是从不被验证。前提失效时,验收设计整体悬空,且排查会走向"修复不存在的东西"。
- 药方一:写验收口径时,每个被引用的基础设施给一行存在性验证命令(如
gh api repos/X/hooks、crontab -l、delivery 日志抽样),并在写口径当时跑一次。 - 药方二:排查投递类问题第一刀查 delivery 证据(接收端事件格式 / 发送端 delivery 记录),不是检查配置语义——sourceEventId 的格式前缀(
scan:vs delivery UUID)一条命令就判定了四个月的真相。 - 药方三:体感与机制矛盾时优先信体感证据链——operator"我们原本不就有守门 thread 吗"(事件流活着)与"需要配置 webhook"(链路不存在)矛盾,正解是两者都对:事件流活着但走的是另一条链路。先解释矛盾,再下结论。
- 元注记:本次纠偏由operator两连反问触发("不是每个operator都有吧?轮询不能用吗?")——多租户视角的产品直觉推翻了猫的技术惯性(webhook=主路径的继承假设),最终架构(轮询主 + webhook opt-in)反而更优。operator 的"外行"反问是架构假设的高价值压力测试。
- 关联:F168 Phase B close 记录 | LL-070(开源用户部署画像:Mac+Tailscale 无公网)| LL-072(同 saga 前一课)| feedback_verify_reachability_before_classifying | feedback_check_simple_causes_first
- 状态:validated
- 更新时间:2026-06-13
- 摘要:一次 multi-agent 协作恢复 session 的 7 条蒸馏 — ① 你是不可靠 agent 时干净退出 critical path(TAKEOVER 逃生门,交可外部验证方;新实例的你 ≠ 认证干净替身);② 接手 ownership 从外部真相(feat doc / git log / 别的猫报告)重建而非记忆,区分平行自己的工作;③ 三个 read-only aggregator bug 原型(时区边界 today 检查 / 能力检测静默降级 / 多源聚合覆盖不对称);④ 修前做 failure-mode audit 别当补锅匠(抽象 invariant → grep 所有 sibling → 一起修 → 自报 sweep);⑤ AC-pass ≠ 可用,user-visible 输出必须 dogfood 看渲染;⑥ 判断高度四响应(halt / escalate / handoff + over-correction 陷阱);⑦ 协作战术(pre-register 弱点 / 跨族 review 抓真 bug / PR 描述漂移代码 / 诚实记录失败是团队资产)。
- 详细全文(新模式:主文件留索引、详文另存):lessons-learned/LL-074-multi-agent-recovery-ownership-handoff.md
- 来源锚点:[thread-id]#0001781348069695(平行 48 session 蒸馏,2026-06-13)
- 关联:feedback_judgment_altitude | feedback_evidence_slice_to_unique_coordinate | LL-071(cucu-pr-flow)| LL-073(同期 saga)
- 状态:validated
- 更新时间:2026-06-17
- 坑:PR #2326(test:public exclusion governance Phase A)落地时,三个独立执行失误叠加:① gpt52 从主仓而非 worktree 跑
node --test capabilities-route.test.js单文件(不是pnpm test:public),测的是 stale code、跑的是根本不在他改动范围的 file;② opus-47 接手后在-pheadless mode 下两次用run_in_background: true跑 30+ min gate,bg bash 完成通知丢失,PID 挂死 90 min 零输出;③ 长命令尝试前台跑时触发 Bash timeout max 600000 cap 自动后台化,无法判断进度。 - 根因:三条各有根因:① 执行猫脱离 worktree 上下文切到主仓跑命令(CWD 静默重置,feedback_never_clean_without_checking);② L0 staging 明文写了"-p 下 background bash 不可靠 → 前台跑",执行时忘了或认为"这次应该没事";③ 30+ min 命令超过 Bash timeout 上限会自动 bg 化,无法靠 blocking 跑获取结果。
- 触发条件:任何在
-p/headless mode 下跑pnpm test:public/pnpm gate类长任务;worktree 开发时换 window/tab 丢 CWD 上下文后继续执行命令。 - 修复:① kill hung process(PID 79245/79205/79198),在 worktree 目录前台跑单元测试;② nohup detach + 查文件 vs 短轮询;③
until pgrep -f <pattern>; do sleep 5; done前台 monitor。 - 防护:
-pmode 每次跑长任务前:明确确认用pwd当前在 worktree 目录,不信 CWD 历史- background bash 在
-p/cron 下 = 死命令。前台跑,超时了用nohup+ 文件 monitor,不用run_in_background: true - 长 gate(>5 min)的进度确认:
until pgrep -f "with-test-home" > /dev/null; do sleep 10; done && tail -f /tmp/gate.log - gate 跑完之前不升级/不开 issue——执行位置和结果不确认,什么都是猜
- 来源锚点:PR #2326 session([thread-id],2026-06-16/17)
- 关联:feedback_never_clean_without_checking(CWD 静默重置)| feedback_p_mode_capability_self_blindness(-p 能力边界)| L0 staging "bg bash 在
-p/cron 下不可靠" | LL-074 §⑥(判断高度)
- 状态:validated
- 更新时间:2026-06-17
- 坑:opus-47 在 worktree 环境里撞到 gate 红(
windows-portable-redis-url.test.js+sync-skills-cli.test.js),判断为"upstream flakes",开了 #2329 和 #2330 分别交给对应 PR owner 修,以此支持"Phase A 代码自身全绿、gate 红 100% upstream"的结论并 merge。但没在 clean main HEAD 单独验证这两个 test:事后 sonnet 实测 main HEAD 两个 test 单独跑均 pass(windows 22/22、sync-skills 8/8),说明问题只出现在 worktree full suite 的 in-suite env pollution 场景,不是 standalone regression。结果 #2329 是 false-positive(PR #2325 早已修了该 test)。 - 根因:撞到 in-suite fail → 直接归类为"他人的 upstream bug" → 开 issue outsource,没有完成"standalone 验证 → clean main 验证 → 确认是否真 regression"三层确认就下结论。把 worktree full suite 的一次 fail 当成 regression 证据,而非 test pollution 信号。
- 触发条件:任何在 worktree full suite 里撞到非本 PR 文件的 fail;准备升级/outsource 一个 bug 之前。
- 修复:事后 close #2329(false-positive),#2330 需继续确认(standalone pass 但 in-suite 可能仍有 pollution)。
- 防护:
- 三层验证规则(outsource 前必须过):① worktree 单独跑确认是否必现 → ② clean main HEAD 单独跑确认是否 standalone fail → ③ 再决定:是 in-suite env pollution(本 PR 修或记录 flaky)/ inherited main regression(开 issue)/ 本 PR 引入(立刻修)
- 跳过任何一层就开 issue = magic word「下次一定」变体("流程合规交接"包装成把未验证的 false-positive 推给别人)
- gate 红后不先 merge、不先开 issue:先定位,定位完再路由
- 来源锚点:PR #2326(#2329 false-positive close)| [thread-id] | opus-47 复盘(2026-06-17 01:24 UTC)
- 原理:worktree full suite 的 fail ≠ standalone regression——full suite 有 in-suite CWD / env / resource 污染,单文件 fail 在 full suite 里出现是 pollution 信号,不是 regression 证据。两者的药方相反:pollution → 隔离测试 / restore env;regression → 修 test 或修代码。混淆后开 issue = 把 pollution 当 regression 投递给不可能修的 owner。
- 关联:feedback_verify_before_guessing(先验证再行动)| feedback_inmemory_store_tests_miss_redis_behavior(in-suite 环境假绿)| LL-075(同 PR,gate 执行失误)
-
状态:draft
-
更新时间:2026-06-24
-
坑:宪宪给 co-creator clone
HKUDS/Vibe-Trading时 SSH (git@github.com:) 报Connection closed by 198.18.0.96 port 22,co-creator 一脸懵——同机器上 gh 能用,git SSH 怎么不行。 -
根因:本地装的代理软件(Clash / Surge / Mihomo / V2Ray 等)开了 TUN / fakeip 模式:把 github.com 解析成虚拟 IP
198.18.0.96(属于 RFC 6815 / RFC 2544 保留的198.18.0.0/15测试网段),但代理规则只配了 443(gh / 浏览器 HTTPS),没配 22(SSH),所以 SSH 流量打到虚拟 IP 后无人接听,连接被代理软件直接断。 -
触发条件:任何机器装了 Clash / Surge / Mihomo / V2Ray 等代理软件且开了 TUN / fakeip;尝试
git clone git@github.com:...或其他基于 SSH 的远程协议(rsync over SSH、scp 等)。 -
修复:换走显式 HTTPS URL——
git clone https://github.com/{owner}/{repo}.git或gh repo clone https://github.com/{owner}/{repo}.git。注意:裸gh repo clone {owner}/{repo}(无 scheme)会遵循git_protocol配置,如果用户跑过gh auth login --git-protocol ssh或gh config set git_protocol ssh,gh 仍会走 SSH,还是会撞 fakeip——必须带 scheme 或先gh config set git_protocol https。 -
防护:
- 看到错误信息含
Connection closed by 198.18.x.x/198.19.x.x→ 立即怀疑代理 fakeip,不要从 SSH key 方向排查(错误信息形态 ≠ 真正的 SSH 鉴权失败,后者会报Permission denied) cat-cafe-skills/open-source-teardown/refs/teardown-method.md"单次拆解流程"章节统一用显式 HTTPS URL(https://github.com/...),不依赖 gh 的git_protocol默认值- 复杂场景必须 SSH 时,在代理软件 22 端口配转发规则——但这是 SOP 例外,不是默认
- 看到错误信息含
-
来源锚点:thread_mqolpabeo344e8tl(co-creator 2026-06-24 gitnexus 试用对话)| cat-cafe-skills/open-source-teardown/refs/teardown-method.md(GitNexus 加速章节)
-
原理:
198.18.0.0/15是 RFC 6815 / RFC 2544 保留的网络性能测试段——任何代理软件用它做 fakeip 都是有意的虚拟 IP 标记,不是真实可达的网络资源。看到这个 IP = 流量已进入代理虚拟层 = 代理规则是真相源,不是网络/认证/防火墙。 -
关联:LL-078(同次试用收获)/ open-source-teardown skill
-
状态:draft
-
更新时间:2026-06-24
-
坑:co-creator 用 GitNexus Web UI 看 Vibe-Trading 的图谱(18,062 节点 / 36,198 边全部一次性渲染)的反馈是"看起来好累"——节点像紫橙粉蓝混合的星云一团乱麻,眼睛根本不知道往哪看,认知负担巨大。
-
根因:知识图谱 / 调用图工具默认是"展示数据量"而非"传达信息"——把所有节点和边全屏渲染,等于把"图谱"误当成"用户产品"。但用户的真正诉求是"理解项目",不是"看一堆点"。理解需要的是消化好的结论,不是原始数据。
-
触发条件:知识图谱 / 调用图 / 依赖图 / 任何超过 100+ 节点的可视化产品;用户期待"快速理解"而非"探索数据"。
-
修复:让 agent(猫)调用图谱的 query API,把图谱消化成"中文 / 结构化报告"再呈现给用户。这次试用宪宪用
gitnexus context / query拿到的事实,写成 7 维拆解报告给 co-creator,0 认知负担。 -
防护:
- 设计 / 选型涉及"图谱可视化"工具时,先问"用户是 explore 还是 understand"——后者强烈倾向走"agent 消化"路径,工具是 agent 的眼睛,不是用户的眼睛
- 把"工具默认全节点渲染"当作信号:那是给开发者 debug 用的,不是给业务用户用的
open-source-teardownskill 的 GitNexus 章节把它定位为"猫调用的 CLI 工具"而非"用户看的 Web UI"
-
来源锚点:thread_mqolpabeo344e8tl(co-creator 2026-06-24 试用 GitNexus 截图 / "看起来好累"反馈)
-
原理:可视化是个 channel,不是 product。同样的数据可以走"原图"(高带宽低信息密度)或"消化后摘要"(低带宽高信息密度)两条路。当下游是人脑,几乎总是后者赢——人脑的瓶颈是注意力,不是带宽。
-
关联:LL-077(同次试用)/ LL-079(同次试用)/ open-source-teardown skill
-
状态:draft
-
更新时间:2026-06-24
-
坑:宪宪和 co-creator 讨论"把 gitnexus 试用经验沉淀成 skill"时,自信地设计了一个
github-project-dissectionskill 的大纲(步骤 / 模板 / 能力面),没有先search_evidence查家里是否已有同类 skill。co-creator 一句"我们好像有一个分析 github 的工具"才触发宪宪去查,发现cat-cafe-skills/open-source-teardown/早就存在——而且是个比新设计更深的版本(防营销话术 / 8 审计镜头 / 算法剥皮表)。差点造一个同义异质的重复 skill。 -
根因:新 skill 的设计灵感来自一次成功的实践(拆 Vibe-Trading)→ 思维直接跳到"沉淀方法论"→ 跳过了"先查家里"。这是 capability-wakeup miss case:skill 是用来"放大复用"的,但前提是"知道已经存在什么",否则会造同名异质的 skill 让 wakeup 决策更难。
-
触发条件:任何"我刚学到一个流程,想沉淀成 skill"的瞬间;尤其当流程的关键词(如"github 拆解"、"项目分析"、"开源审计")听起来很泛、很可能撞名时。
-
修复:co-creator 提示 +
find cat-cafe-skills -name "*teardown*"→ 找到open-source-teardown→ 改方案为"把 gitnexus 用法补进现有refs/teardown-method.md"而非新建 skill。 -
防护:
- skill 设计三问(提案前必过):
cat_cafe_search_evidence("{skill-topic} skill", scope="docs")命中 0 个?ls cat-cafe-skills/没有同义名(拆解 = teardown / 分析 = analyze / 审计 = audit / 探索 = explore)?grep -r "{核心关键词}" cat-cafe-skills/*/SKILL.md没在别的 skill description 里出现? 三个都通过才能新建;任意一个命中 → 改造现有 skill 默认优先
- 触发"沉淀成 skill"念头时,先列同类 skill 再给方案;不要直接画大纲
- "改造 > 新建" 默认偏好——除非新方法论和旧 skill 真的不同维度(不只是流程细节差异)
- skill 设计三问(提案前必过):
-
来源锚点:thread_mqolpabeo344e8tl("如果要沉淀 skill 可以沉淀什么"对话 2026-06-24)| cat-cafe-skills/open-source-teardown/SKILL.md(被遗漏的现有 skill)
-
原理:skill 系统的复利来自"已存在的可信调用面"被反复使用。新建一个同义 skill = 稀释一半可信度 + 让其他猫的 capability-wakeup 决策更难。每多一个 skill,wakeup miss-rate 边际上升——这是 F192 capability-wakeup eval 已经量化的成本。对应到第一性原理:把"放大复用"误当成"沉淀经验"——前者要求收敛到已有承载面,后者只在记忆里 dump。
-
关联:F192 capability-wakeup eval / writing-skills skill / LL-077 / LL-078
LL-077: F210-H1 dispatcher dual-handler 不变量——新 telemetry type 必须同时在 foreground + background chain 加 handler
- 状态:validated
- 更新时间:2026-06-17
- 坑:社区 PR enihcam/clowder-ai#943 (
fix(web): consume provider_capability telemetry silently) 给useAgentMessages.tsforeground 链 (handleAgentMessage~line 5111) 加了provider_capabilityhandler,但漏了 background 链 (consumeBackgroundSystemInfo~line 731) 的 mirror handler。社区作者本人 + Maine Coon first-pass review + 我自己(opus-47)initial absorb 都没 catch。opus-46 cross-thread review on cat-cafe#2352 才发现。effect:kimi 在 background thread 跑时(常见 case:A2A 链 / 完成-未查看 thread / multi-cat background stream),原 #939 bug 依然存在——raw-JSON"thinking → unavailable"系统 bubble 继续 surface 给用户。 - 根因:F210-H1 dispatcher pattern 同一 telemetry type 走两条 chain(foreground React hook + background async callback),每条 chain 各有自己的 dispatcher list。没有 single source of truth——pattern 实现成了"visible-but-loose contract":可发现(grep type name 能定位)但容易漏(review 默认 mental model 是"找到一处分支就够了")。社区 PR review 习惯优先 verify 用户最常见 interactive 路径(foreground),background callback 路径 review 时容易省略。
- 触发条件:① 增加新
system_infotype 处理 / ② system_info dispatcher chain 改动 / ③ 任何 mirror dispatcher pattern (fg/bg, sync/async, primary/fallback)。 - 修复:cat-cafe absorb fix in
d3b1214e3(mirror handler 插入 background chain line 1062)+ 3 background regression tests in new fileuseAgentMessages-provider-capability-background.test.ts(realuseChatStore,与 mock 化的 sister foreground test file 分离)+ nit??→||在 fg+bg 两处都改(empty-string defense)。Upstream issue filed at clowder-ai#966。 - 防护:
- dispatcher 系列 PR review 强制 cross-chain audit:对每个新
system_infotype,在 PR review 时显式 grep'<type>'在所有 dispatch chain 文件(useAgentMessages.ts至少 fg+bg)确认 handler count ≥ 2;count = 1 = 漏一条。 - intake-from-opensource.sh 加 dispatcher symmetry check:PR diff 改动了
handleAgentMessage的 dispatch chain 但没改consumeBackgroundSystemInfo(或反之) → warning。F238 follow-up 候选。 - lessons-learned reflex (与 LL-076 互补):intake 闭环完,发现"我们补的 fix 也应该在 upstream 修" → 立刻 file upstream issue,不留"下次一定"。本 LL 触发的 #966 是范例。
- dispatcher 系列 PR review 强制 cross-chain audit:对每个新
- 来源锚点:cat-cafe#2352 (intake of clowder-ai#943) | [thread-id] | opus-46 cross-thread review on
cdeaa9150(2026-06-17 14:08 UTC, PR comment #4731199498) | upstream issue clowder-ai#966 - 原理:dispatcher pattern 的强 contract 应该是 single source of truth(如 handler registry map / decorator),让两条 chain 在编译期或加载期共享 handler list。当前实现是"两条 chain 各 if-else 平行写",任何新 type 加入都要 mirror 两遍——是 known weak-contract,review-time 防护是唯一缓解。L0/H1 vs H3 命名(H = HotFix)暗示这条 pattern 是 hotfix 后归纳的,未来可考虑 refactor 到 registry 解决根因。
- 关联:LL-076(verify before outsource - 本 LL 触发的 upstream issue clowder-ai#966 走了 LL-076 标准 "clean main HEAD verify" 三层确认才 file,没把 in-cat-cafe 的 fix 当成 false-positive 反推)| feedback_verify_before_guessing(opus-46 finding 我没直接信,read line refs 实测)| F210(H1 dispatcher pattern 源 feature)| #944 intake P1 + clowder-ai#959(同期同 author enihcam,同 cross-individual review 抓真 P1,反映 first-time contributor PR + maintainer absorb 都依赖 cross-individual review 兜底)
-
状态:validated
-
更新时间:2026-06-17
-
坑:F228 broader intake commit
42c5b349c加 shared 导出STANDARD_MOUNT_POINT_IDS+ api 对它的 import。runtime 跑tsx watch src/index.ts(dev 脚本继承的 watch 模式),watch 检测 src 改动 → SIGTERM 自重启 → 新进程 load stale dist(@cat-cafe/shared/dist是 gitignored,PR 不带 dist,runtime sync 不 build)→ SyntaxError missing export → runtime 崩。 -
根因:runtime 被设计为"daily stable serving 环境"但启动脚本 (
scripts/runtime-worktree.shexecstart-dev.sh --prod-web) 沿用 dev 脚本默认的 tsx watch 行为——dev convenience(feature worktree 要 watch 提升迭代速度)leaked into runtime(应该 passive frozen 只在显式pnpm start重启)。同时 sync 行为分裂成"独立runtime:sync命令"和"pnpm start内部 sync"两条路径,没有 build invariant guarantee(sync 拉了 src 但没 rebuild dist)。 -
触发条件:① 任何 shared 或 api 源码改动 + 已运行 runtime 进程 ② runtime 当前在
tsx watch模式 ③ build invariant 不保证(sync 后无强制 rebuild)。 -
修复:PR #2353 squash
c1cba740b落地 ADR-039「runtime passive-freeze contract」三 invariant:①CAT_CAFE_DIRECT_NO_WATCH=1默认 export 两处(in-place + worktree mode)让 runtime 跑node dist/index.js不是tsx watch src② 删独立runtime:sync命令,sync+build+restart 都在pnpm start内完成 ③ renameensure_quick_start_artifacts→ensure_runtime_dist_freshness,drop quick-mode gate(passive 总是需要 dist),加 api dist freshness check(shared→api→mcp→web 顺序,stamp-gated stale rebuild)。 -
防护:
scripts/runtime-passive-freeze.test.mjs10 个 invariant 静态守卫——CAT_CAFE_DIRECT_NO_WATCH export 两处都在、sync) dispatch case 删净、api dist freshness check 存在、build invariant 不被 quick-mode gate 短路等。- ADR-039 status: ratified——runtime 契约被钉死,未来 PR 改这块必读 ADR。
- deferred verification 兑现点 contract 化(feedback_alpha_smoke_happy_path_blindspot 应用):LL-064 在 runtime-conflict 场景下,live SIGTERM observation 退化为"static + 自然推迟到下次 user-initiated
pnpm start",不是"happy path 标 validated 跳"。下次 user-initiated restart 是 live 验证的实际兑现点——若那时 export 没生效,立即 hotfix。这个 carry-over 风险必须诚实标记,不能 happy-path 包装。
-
来源锚点:[thread-id](opus-48 forensic investigation)| PR #2353 (squash
c1cba740b) | ADR-039docs/decisions/039-runtime-passive-freeze.md| F228 source incident commit42c5b349c -
原理:production-like runtime(stable serving)和 dev environment(hot-reload iteration)必须有 explicit contract 区分。复用同一 startup script 但不 explicit 区分行为模式 → dev convenience 必然 leak 到 runtime。Passive freeze = "restart 只在用户显式动作时发生"——这是单一 mental model,比"sometimes watch + sometimes not" 简单且 crash-resistant。
-
关联:ADR-039(contract 文档)| feedback_alpha_smoke_happy_path_blindspot(deferred 不能假装 validated)| LL-064(production runtime alpha 要求)| F228(incident source)
-
状态:validated
-
更新时间:2026-06-17
-
坑:PR #2353 re-review 时 opus-48 用
git show FETCH_HEAD:scripts/runtime-worktree.shgrep invariant 实证——结果和 PR HEAD644fe75dc实际内容全矛盾(CAT_CAFE_DIRECT_NO_WATCH 零命中、ensure_quick_start_artifacts旧名还在、sync)dispatch case 还在)。差点误判"PR 没实现核心 invariant"。 -
根因:主仓被 intake 流程高频 fetch(clowder-ai 上游 + 兄弟 thread intake),
FETCH_HEAD不是命名 ref 是 volatile pointer——git fetch <any-branch>会把它覆盖成最新 fetch 的 ref,几秒内多个 fetch 操作就漂到无关 commit(实测覆盖成了3e94a8bd)。在 mangle/敌对 shell 环境下,错误更难诊断(容易归因为"shell jumble"而错过 volatile ref 真因)。 -
触发条件:① 主仓在并发活跃期(multiple intake threads / 上游 sync / sibling cat fetch)② 用
FETCH_HEAD或其他 volatile ref(如HEAD@{1})做证据切片 ③ 没钉死 commit SHA。 -
修复:换成 fixed commit SHA
644fe75dc(commit object 一旦 fetch 到本地就 immutable + persistent,不受任何 ref 覆盖影响)。复验后 PR 实现完整正确。 -
防护:
- review/audit/forensic 证据切片时永远用 fixed commit SHA——
gh pr view --json headRefOid取 SHA,然后所有 grep/show/diff 用这个固定 SHA。 FETCH_HEAD/HEAD@{n}/origin/main等 ref 类型避免在 forensic 上下文用——这些是 mutable pointer,可被其他 git 操作覆盖。- 敌对/高频环境额外警觉:撞到"实证结果和描述全矛盾"的情况,第一假设不是"对方说谎"或"代码没改",而是"我的证据坐标可能不稳定"(
feedback_evidence_slice_to_unique_coordinate扩展应用)。
- review/audit/forensic 证据切片时永远用 fixed commit SHA——
-
来源锚点:[thread-id] opus-48 re-review session(PR #2353 三批结果矛盾追根因到 FETCH_HEAD pollution)| feedback_evidence_slice_to_unique_coordinate(原型应用,本 LL 是精确根因细分)
-
原理:git ref 类型分两类——named refs(branches, tags, full SHAs)持久不变;volatile pointers(FETCH_HEAD, HEAD@{n}, ORIG_HEAD, MERGE_HEAD)随 git 操作改写。混用两类做证据坐标会撞 "evidence at coordinate X says Y" 但 "X" 自己漂了的 phantom 矛盾。
-
关联:LL-077(同期 opus-48 multi-thread review 工作流)| feedback_evidence_slice_to_unique_coordinate(基础原则,本 LL 精确细分到 git ref 类型层)| feedback_phantom_ids_and_env_misdiagnosis(SHA 必须从命令输出取真值,不手写)
-
状态:validated
-
更新时间:2026-06-17
-
坑:cat-cafe 团队所有猫共用同一个
zts212653GitHub 账号(multi-cat persona 在 single GitHub identity 下运作)。reviewer 猫想给 author 猫的 PR 留 GitHub formal APPROVE button click 会被 GitHub 拒(self-approval prevention)。但 review verdict 仍需在 GitHub 留 traceable record(perfeedback_intake_review_on_github/ merge-gate 要求 formal review evidence)。 -
根因:GitHub PR review 是 user-level 操作(per-user APPROVE/REQUEST-CHANGES/COMMENT),不是 cat-level。cat-cafe 的 multi-cat-single-account 模型与 GitHub user-level 假设冲突。
-
触发条件:每次 cat-cafe 内部 cross-individual review 都会撞——author + reviewer 都是 cat persona,但 GitHub 看到都是
zts212653。 -
修复:reviewer 用
gh pr comment留 COMMENT-type formal review record——comment body 显式写出 verdict(APPROVE / BLOCKING)+ 覆盖的 HEAD SHA + 验证细节。merge 时走gh pr merge --admin跳过 GitHub APPROVE 要求,依赖 comment record 作为 review 证据。 -
防护:
- PR #2353 范例:opus-48 re-review 用
gh pr comment留 COMMENT 含verdict = code-level APPROVE @ 644fe75dc+ 全部 invariant 实证结果,merge author(opus-47)--adminenforce。整条链 GitHub-traceable。 - 不要 fake APPROVE:如果有"用 author 的 review 兜 reviewer"的诱惑(让 author 自己点 APPROVE button 假装 reviewer),坚决不做——是认知投毒(fake anchor variant,feedback_fake_feat_anchor_is_poison)。
- 更长程方案候选(未来 backlog):每只猫绑独立 GitHub bot account(或使用 GitHub Apps)—— 但短期内 single-account model 是已知约束,COMMENT-type 退化是合理 workaround。
- PR #2353 范例:opus-48 re-review 用
-
来源锚点:PR #2353 opus-48 review comment | feedback_intake_review_on_github | feedback_review_continuity_pure_rebase
-
原理:multi-cat persona 在 single GitHub identity 下运作的 known limitation——GitHub permission model 是 user-level,cat persona 是 application-level。两层 model 直接耦合(一只猫 = 一个 GitHub user)会撞 scaling 问题(注册 5+ bot accounts + 维护);保持 single-account 是 cost-benefit tradeoff,代价是 review record 退化为 COMMENT。
-
关联:feedback_intake_review_on_github(formal review evidence 要求)| feedback_approve_then_enforce_merge(merge enforce 责任)| PR #2353(范例)
- 状态:validated
- 更新时间:2026-06-18
- 坑:F233 PR3 cloud review 6 轮,opus-48 做本地 reviewer 介入时误判——把已被作者逐点修好的 cloud finding 当成 fresh systematic gap,建议了错误的 "decorator 换层",被作者Maine Coon push back(代码早已
StartupReconciler.recordInvocationDied/messagesOpts.ballCustody/fireAt===heldUntilguard 逐点修好)。双重证据没切到 PR-head 唯一坐标:①grep主仓 main(没有 PR 分支的修复)核 PR finding——main 看着"StartupReconciler 漏 invocation.died",但 PR branch 早已接好 ②gh api .../comments | select(commit_id==head)读 inline,但 carry-over 旧 review comments 的commit_id被 GitHub 贴成 current head,没按 review submission id 分 stale/fresh,把旧轮已修 finding 当 fresh truth。 - 药:reviewer 核 cloud 多轮 PR 两条证据纪律——① 核代码用
git show {pr-head}:path或 PR-head 沙盒,绝不grep主仓 main(main 无 PR 分支修复,必然误判"漏")② inline comments 按 review submission id 分 stale/fresh,carry-over comment 的commit_id不可信(GitHub 贴成 current head);判 systematic-vs-逐点前,先确认 finding 真在最新 head 复现。 - 来源锚点:F233 PR3(#2364)opus-48 误判 decorator → Maine Coon push back(83948fcae 终局放行)
- 关联:feedback_evidence_slice_to_unique_coordinate(基础原则——本 LL 细分到 cloud-多轮 PR review 的 PR-head-坐标层)| LL-079(FETCH_HEAD volatile,同型证据坐标教训)| LL-072(封板协议——多轮 review 下 stale/fresh 辨别是封板判断前提)
- 状态:validated
- 更新时间:2026-06-18
- 坑:F233 PR3 merge 后,
cat-cafe-f233-pr3-cloudfix残留一份未提交 diff,作者当下体感是"不记得谁产的"。愿景守护复核后发现它不是随机脏改,而是 cloud inline P2 #3433689221(cross_post_messagetool name alias normalize)的真实修复半成品;LL-072 封板/merge 时只合入了同轮另一条 P2,漏把这份 diff commit 成 follow-up。同期还有 Bash CWD 在主仓/旧 PR3 worktree 间漂移,grepplan 命中旧文件差点误判 phase sync 不完整。 - 根因:多轮 cloud review 把"review finding → 本地 patch → commit/push → PR truth"拆成多个节点,但节点切换前没有强制把 tracked diff 归档到唯一坐标。dirty diff 留在 feature worktree 里跨过 merge-gate,后续猫只能从工作树残影和 GitHub inline comment 反推 provenance;CWD/多 worktree 又让证据坐标更容易漂。
- 药:每次离开 review round / 切 worktree / 进入 merge 前,先跑
pwd && git status --short --branch。只要有 tracked diff,必须三选一并留证据:① commit 并 push 到当前 PR;②git stash push -m "<PR/comment id + intent>"临时保存;③ 明确 discard 并在 thread/任务里写 why。cloud finding 已验证为真但尚未 commit = 阻塞 merge,或显式拆成 follow-up PR/task,不能当"残留清理"处理。 - 防护:merge-gate 收尾加 dirty-diff ledger:
git worktree list后对本 feature 相关 worktree 跑git -C <path> status --short --branch;merge 后仍 dirty 的 worktree必须有对应 PR/task/comment id。多 worktree session 下所有取证命令优先用绝对路径或显式git -C,不要让 shell CWD 历史承担证据坐标。H4 dogfood failure(同日):本 lesson 写完后,作者又把同一 alias fix commit 到旧cat-cafe-f233-pr3-cloudfix/fix/f233-pr3-cloud-review坐标,再从另一条 branch 开 PR #2374;这说明软层不足,hard 层需要 merge-gate dirty-diff ledger 检查,eval 层需要把此类复发纳入 F192/verdict 观察。硬层 LANDED(2026-06-18,PR #2392, merge commit58b6cdbe3):scripts/check-worktree-dirty-ledger.mjs接pre-merge-check.sh收尾,列所有 worktree dirty + warn-only entry。Cloud R1→R5 5 轮(R1 sync allowlist + shell injection;R2 trim-破坏-trailing-space-path;R3 entrypoint URL-escape silent-disable;R4 status-check-failure silent-clean)暴露同型 silent-failure 全家——本身就是 LL-082 哲学 dogfood:guard 必须永远不 silent-clean。每一轮 finding 都 verified Red→Green + 19/19 ledger tests。eval 层:把此类复发纳入 F192/verdict 观察仍 outstanding。 - 来源锚点:F233 PR3 (#2364) merge 后愿景守护;follow-up PR #2374(
fix(F233): normalize cross-post callback aliases) - 关联:LL-081(PR-head 坐标纪律)| LL-079(volatile ref 坐标漂移)| LL-075 / LL-049 同型(CWD 静默漂移)| feedback_evidence_slice_to_unique_coordinate
- 状态:validated
- 更新时间:2026-06-18
- 背景:F233 PR3 callback-routing saga 续 LL-072/081——#2364 七轮后又出 dc3、#2378 两轮,表面都是"又一个 cloud P2"、每个都是真 bug,本质却分两类、处置相反。reviewer 的真难题不是"finding 真不真"(都真),而是"这一轮该 APPROVE 还是该拉闸"——LL-072 只给了"≥5 轮拉闸"的轮数阈值,没给单轮判据。
- 判据(多轮 PR reviewer 介入时判 APPROVE-vs-拉闸):
- 封闭集补齐 → 放行:finding 维度来自封闭集且本 fix 补齐最后一类 → 不变量数学闭合,无更多同类缺口可被未来 review 发现。例:dc3 的
isCallbackContentRoutingToolName= {post, cross-post} 封闭集,dc3 前 post 有别名归一、cross-post 没有,补齐第二类即闭合 → APPROVE 成立(cloud 确实没再从别名维度找)。 - 开放纠缠繁殖 → 拉闸:finding 是同一纠缠状态的又一个边。识别信号三选一命中即拉闸:① 多个 finding 落在同一组 state/flag;② 本 fix 在修上一个 fix 引入的回归;③ 单个 state 承载多语义。例:finding 5/6/7/dc3 都在 callback-routing 的 operator/guard/mention 状态,单 flag
confirmedCallbackRoutingHasCoCreatorLineStartMention承载 routing-guard-satisfaction + local-operator-emission 双语义,修一个破坏另一个(finding 7 修 operator 静默破坏 guard,#2378 才暴露)→ 第 5 个边、终止条件不存在 → 拉闸停回不变量层,不再逐边批补丁。
- 封闭集补齐 → 放行:finding 维度来自封闭集且本 fix 补齐最后一类 → 不变量数学闭合,无更多同类缺口可被未来 review 发现。例:dc3 的
- termination 构造(拉闸后收口标准,比 LL-072"停回 plan 层"更具体可验):纠缠 state 重构成 single-source 分类函数(一次分类、所有语义维度纯函数派生)+ 穷举参数化测试。
⚠️ 但测试必须穷举端到端可观测行为,不只 classify 输出——6f15455 续轮纠正(我上轮 APPROVE 早了):#2378 的穷举测试只deepEqualclassify 的 6-field 输出 + operator binding,结果 source-threadmentionsUserconsumer 读错 flag(读 guard-level 而非 local)的 leak 直接逃过穷举,cloud 又发一轮。真 termination = consumer×cell 端到端矩阵:列出该 state 全部可观测输出(本例 5 个:guard satisfaction / source mentionsUser / source worklist A2A enqueue / local operator / target operator),每 cell{post/local, cross/target}×{有/无 toolUseId}×{@cat,@co-creator}断言全部可观测输出(不只 classify 中间状态)。可观测输出是有限集,锁满才真没有"未覆盖行为";省略不可达 cell(scope 由 tool 唯一决定,{post,target} 不可达 = 测死代码)。 - termination 完整性红旗(命中任一 = 没到端到端层,别声明 termination 达成):① dead field——classify 算出某维度但 0 consumer 读(如
localLineStartMentions)= consumer 层没接,对称维度迟早 leak;② consumer 未被测试断言(只测 classify 中间状态、没测 consumer 端行为——mentionsUser leak 即此型逃逸);③ 维度处理不对称(@co-creator 有 local/guard 双轨接 consumer,@cat 只 guard 单轨被消费)。 - 衍生症状:单 flag 多语义 = reviewer 盲区放大器——一个 state 承载多语义时 reviewer 注意力被单语义吸走会漏看其他维度(本 saga reviewer 在 finding 7 只核 operator 维度、漏看 guard 维度,APPROVE 后才由 #2378 暴露)。"一个 flag/字段被多处不同语义读取"本身即状态契约缺失信号,是拉闸的前哨。
- 来源锚点:F233 PR3 #2364 dc3(封闭集 APPROVE)/ #2378(开放纠缠 BLOCKING → state contract → APPROVE @
30859b67e)opus-48 reviewer - 关联:LL-072(封板协议——本 LL 精确化其"何时封板 vs 何时拉闸"的单轮判据 + termination 构造)| feedback_plan_stateful_lifecycle_state_machine(同类≥3轮停回 plan 层——本 LL 给"同类"的可操作识别:封闭集 vs 开放纠缠)| LL-081(同 saga,reviewer 证据坐标层)
- 状态:validated
- 更新时间:2026-06-18
- 坑:F140 review routing saga 连环两层。① opus-46(我的平行身体)在 #949 反馈"review thread context overflow"时自决开 PR #2335 引入"3+ reviews → auto-rotate 新 thread"作为"性能优化"——无 operator 签字、无 cross-individual review、无 F140 KD 记录(只 cloud codex bot 留 comment 未 approve)。② 我(opus-47)后续看到"原 thread 失去信号"症状,反射开 PR #2372 加
system_notice backlink (📌 已转投到 auto-rotated thread),意图让原 thread 至少看到"我们 fork 了"。operator UI 上看到"📌 已转投"瞬间识别"邪修"——核心不是"提示得更友好",是"为什么要 fork"。codex 最终 PR #2394 拆掉 rotation/backlink 整条运行路径,把task.threadId改为不可改写契约。 - 根因:连环两层错位——① 平行身体不传染架构权限:opus-46 / opus-47 同 catId persona 但不同 invocation,"同 persona 自决"不能跨 operator 签字边界(thread 数据切片属用户视角愿景级决策,不是可逆技术细节);② cope-layer 反指标:症状("原 thread 失去信号")的反射动作不该是"加补丁补救",应是"question 上一层设计"——thread 是用户视角对话单元,不是数据切片层;context overflow 应在 invocation hydration 层(context window / digest)处理,在 thread 层 fork = mental model violation。我加 backlink 不仅没修问题,反而把错设计永久化——operator看到的"📌 已转投" UI 正是我的 cope-layer 输出,让"我们 fork 了"成为常态被默许。
- 触发条件:① 看到 X 的副作用 Y 让用户体验差;② 反射想"加 backlink / 提示 / toast"补救 Y;③ 没问"X 本身合理吗"。自检反问:"我是否在补另一只平行猫自决留下的洞?"——如果是,上一层设计需要 operator 签字而非补丁。
- 修复:codex PR #2394(squash
1d42b8f36)——删 rotation runtime 路径 + 删 backlink delivery +task.threadId不可改写契约 + admission gaterepairedTask.threadId回溯 legacy 污染 task(gpt52 review 拉出的 P1)+ 反向 invariant 回归测试(completedReviewCount=99 + 残留threadStore/backlinkDeliverydeps 都不触发 rotation)。撤回 PR #2372 整条 backlink 路径。F140 spec correction(squash1b1a084f7)记录两条 PR 为 wrong-direction,不再美化为"trust gap 闭合 / post-completion hardening"。 - 防护:
- 反射换层:发现自己在加 backlink / toast / "提示用户我们做了 X" 类补救层时,先停手问"X 这件事本身是合理需求吗"。如果上一层设计错,补救层 = cope-layer = 把错永久化。补救层的 UI 输出("📌 已转投")= 把错设计显性化为产品常态的红灯。
- 平行身体硬边界:同 catId 平行 invocation 不传染架构权限。架构级(数据切片 / thread 语义 / 用户视角契约)改动必须 operator 显式签字 + cross-individual review;"同 persona 已自决过"不构成权限传染。"我是平行的他" ≠ "我有他的授权"。
- 数据/视角分层:用户视角层(thread / message / conversation)≠ 系统视角层(context window / hydration / digest)。前者不可被后者副作用改写(LL-048 用户状态默认持久化的同型分层延伸:用户可见层默认不被系统侧自决改写)。
- 来源锚点:F140 PR #2335(opus-46 越权 rotation 引入,无 operator 签字)/ PR #2372(opus-47 cope-layer backlink,已撤回)/ PR #2394 修复(squash
1d42b8f36)/ F140 spec correction(squash1b1a084f7)/ operator 2026-06-18 12:06 UTC "邪修" push back ("operator说头疼你们说把头砍了就不疼了") - 关联:LL-048(用户状态默认持久化——本 LL 分层延伸:用户视角层 ≠ 数据切片层)| feedback_judgment_altitude(判断高度——补救层=太低;question 上一层=正确高度;本 LL 给"补救层"的具体识别)| feedback_xiaci_yiding_self_diagnosis(cope-layer = "下次一定"姐妹病:把"问题没解决"包装成"提示得更友好")| LL-072(多轮 review 何时拉闸——本 LL 是"开新 saga 前"的同型预防:发现自己在加 cope-layer = 同型拉闸预警)
- 状态:validated
- 更新时间:2026-06-19
- 坑:F240 愿景守护时,我(opus-48)在共享 main 工作树给 F240 doc 做 commit 用了裸
git commit(无 path-scope),把平行 opus-47 invocation(同 catId 不同 session,正在做 F188 stale-branch case discovery)staged 在暂存区的F233 doc(+2 行)+case-study.md(新建 +151 行)一起卷进了我的 commit38e2fb079,挂在我的docs(F240): vision-guard ACCEPTmessage 下。 - 根因:同一 git repo 的工作树共享同一个 staging area(index)。多个并发 session(尤其同-catId 平行 invocation)在同一 working tree 操作时,
git commit(不带 path)提交整个 staging area = 别人git add过的文件一并被提交。归因(git author)落对了 catId 但提交者/commit message 错位(subject 张冠李戴)。 - 触发条件:① 在共享 main 工作树(非独立 worktree)commit;② 用裸
git commit而非git commit <path...>;③ 当时有平行猫 / 平行 invocation 在同 working treegit add了东西。 - 防护:
- 共享 main 工作树 commit 一律
git commit <path1> <path2> ...精确到本次文件,绝不裸git commit。 - commit 前先
git status --short看 staging area,确认没有不属于本次改动的A/M项(有 = 平行猫的,path-scope 排除)。 - 已发生 + 已 push:不 force-push 改 main 历史(history mutation 副作用 > attribution noise);通知受影响 catId;内容若已过 gate(Brand/biome)落 main 即事实,git blame 可追。
- 共享 main 工作树 commit 一律
- 来源锚点:commit
38e2fb079(F240 vision-guard,误卷 F233+case-study)/ opus-47 cross-thread ack([thread-id],确认是平行 opus-47 invocation 的 staged 工作,建议不 amend / 不 force-push)/ commit27a0401c7(opus-47 F233 OQ-8 wording tighten,写完 LL-085 后 1h 内同型再犯,把 opus-48 stage 的 LL-085 13 行顺带卷进 OQ-8 commit) - 状态升级建议(2026-06-19,@gpt52 Maine Coon GPT-5.4 cross-thread review):因写完 lesson 后 1h 内同型再犯,LL-085 不再只作为记忆提醒;后续应评估 shared-main docs sync 的 path-scoped commit 包装入口(soft→hard 候选)。范围窄定:共享 main 工作树 docs sync / vision-guard / timeline sync 这类 commit 统一走 path-scoped 包装命令(script / alias / SOP command),不再让人手打裸
git commit -m。不抢开新任务打断 F188/F233 主线,作为 LL-085 硬层候选记录。 - 关联:LL-082(多 worktree dirty diff 不跨节点漂移)| LL-084(平行身体硬边界——本 LL 是"平行身体共享 working tree"的 git 层具体坑)| feedback_never_checkout_branch_in_main(worktree-git 事故防线)| feedback_dont_touch_parallel_self_workflow(别动平行自己工作现场——本 LL 是"无意中动了"的机制级补充)| feedback_git_commit_must_be_path_scoped(opus-47 user memory 同型沉淀)
- 状态:validated
- 更新时间:2026-06-20
- 坑:F167 PR-O2b (#2447) merge 时序违反。Cloud R1 给了 P2(redact grounding samples),我 pushback 说"spec L828 只禁 KB-scale body,200-char diagnostic metadata 不算",自行降级 P3,然后 re-trigger cloud review。R2 在 13:20:26 UTC 回来给了 P1(同一问题,升级了 severity),但我 13:19:21 UTC 已经 merge 了——比 R2 早 65 秒。Vision guardian (opus-47) 抓到:① 我的 pushback 论据是 spec 误读(L828 "只存 sourceRef + hash/status" = 白名单,不是黑名单);② 我把自己的 pushback 当成终局裁决(正好是 F167 要治的病——接球猫无条件信任传球猫的 claim);③ F167 doc timeline confabulated "1 round"(实际 2 rounds)。
- 根因:Author pushback 是 claim 不是 verdict。我在 13:18 pushback 了 R1 P2,然后不等 R2 就 merge——把自己的 pushback 当 truth source。这是
feedback_approve_then_enforce_merge的变体:不是"reviewer APPROVE 后不跟进",而是"author pushback 后把 pushback 当终局"。Cloud reviewer 是无状态信号源,但 re-trigger 后的新 round 可能升级 severity(P2→P1)。 - 触发条件:① 收到 cloud P1/P2;② 认为是 false positive,写了 pushback comment;③ re-trigger cloud review;④ 没等 re-trigger 的结果就 merge。
- 防护:
- re-trigger 后必须等结果:自己 trigger 了 cloud review #N,就必须等 #N 的结果到齐再决定 merge。"pushback + re-trigger" 不是终点,是开始。
- pushback 是 claim 不是 verdict:你对 spec 的解读可能错。pushback 后必须找 second-source resolver(直接重读 spec 原文 + 对比同类 endpoint 先例),不能用自己的 pushback 自循环论证。
- Spec 白名单 > 黑名单举例:L828 "只存 X + Y;不存 Z" 中的"只存"是白名单,"不存 Z"是反例不是穷举。不在白名单上的字段默认不存。
- 来源锚点:PR #2447(F167 PR-O2b,squash
10e6d4a2e)/ cloud R1 P2 comment id 3446414013 (13:14:58) / author pushback comment id 3446422487 (13:18:31) / cloud R2 P1 comment id 3446428880 (13:20:26) / merge (13:19:21) / opus-47 vision guardian BLOCK ([thread-id]) - 关联:feedback_approve_then_enforce_merge(reviewer APPROVE 后必须 enforce——本 LL 是 mirror:author pushback 后不是 self-approve)| feedback_cloud_review_inline(cloud P1 可能在 inline comments——本 LL 是"merge 前 P1 还没到"的时序变体)| LL-072(封板协议——本 LL 的边界:封板适用于 N≥5 的疲劳循环,不适用于"re-trigger 了没等结果就 merge")
LL-087: Filter+Batch UI 的 plan-time invariant table——items vs filteredItems scope-mismatch 是 stateful UI 的典型同类 failure class
- 状态:validated
- 更新时间:2026-06-21
- 坑:F246 Phase D PR #2477 在 local review 3 轮 + cloud review 3 轮中,累计暴露 4 处同类 scope-mismatch——全部是
items(全集)该用filteredItems(当前视图)的位置写反了。4 处分布在 3 个不同阶段(local R2: selectAllInline 不 scope 到 filtered;local R3: filter 切换后 selection 残留;cloud R1: batch 开始时 selectedIds 不清空;cloud R2: inlineCount 取全集不取视图),每轮只修当前发现的 1 个,没有泛化扫描同类。 - 根因:Phase D plan 处理"ApprovalPanel filter + batch selection"这个 stateful 系统时没有显式 invariant table。具体缺失:①
items(全集)vsfilteredItems(视图)的使用判别标准未画出来——哪些操作读全集(empty-state 判定)、哪些读视图(batch scope / inlineCount / selection);② selection 与 filter 切换的不变量("filter 变 → selection 清空")未显式定义;③ batch action 与 selection clear 的时序契约("batch 启动 → 立刻清空 selectedIds → 然后执行 API 循环")未写入。 - 这是
feedback_plan_stateful_lifecycle_state_machine.md(P1,F229 PR-A1 20 轮 review)的同型教训:stateful 对象给状态转移表 + 可测不变量 + 对抗场景,否则 review 轮数 = 欠的边数。F246 是 UI 层变体:store + component 的 state projection 关系也是 stateful 系统。 - 触发条件:plan 涉及"全集 + 过滤视图 + 选择状态 + 批量操作"四件套的 UI 时,且 plan 没有显式 invariant table。
- 防护:
- Plan-time invariant table for filter+batch UI:类似"状态×事件转移表",为 filter+batch UI 写一张"数据源×操作"表,标注每个操作该读全集还是视图:
- 全集(
items):empty-state 判定("没有任何待审批项")、总数统计 - 视图(
filteredItems):batch scope、inlineCount、selection scope、UI 列表渲染 - 不变量:INV-1 filter 变 → selectedIds 清空;INV-2 batch 启动 → selectedIds 立刻清空(double-click guard);INV-3 inlineCount 与 batch bar 可见性 = filteredItems 的 inlineApprovable 计数
- 全集(
- Failure-mode audit at plan time:plan 写完后用"items vs filteredItems"做 grep 扫描模板(哪些用 items,逐个标注为何不用 filteredItems)。
- Plan-time invariant table for filter+batch UI:类似"状态×事件转移表",为 filter+batch UI 写一张"数据源×操作"表,标注每个操作该读全集还是视图:
- 来源锚点:F246 Phase D PR #2477(squash
507bf5f6d)/ local R2 fix8cedeacf6(selectAllInline scope)/ local R3 fix1fd592278(selection clear on filter change)/ cloud R1 fix0e3e9f3d9(batch double-click guard)/ cloud R2 fix326af8099(inlineCount scope)/ opus-47 vision guard verdict("4 处 scope-mismatch 暴露 plan 层 stateful invariant 缺口") - 关联:feedback_plan_stateful_lifecycle_state_machine(P1 F229 20 轮——plan 里 stateful 对象必须三件套)| LL-072(cloud review 封板——F246 Phase D 因 100% stale replay 封板,但根因是 4 处 scope-mismatch 用了 4 轮才全清)| feedback_halt_question_but_probe_before_pivot(N 轮同向打补丁=坐标系警报——本 LL 给了具体的"filter+batch UI invariant table"作为坐标系修正工具)
- 状态:validated
- 更新时间:2026-06-21
- 坑:F246 Phase D 引入 filter + batch 操作后,4 处独立点犯了同一类 bug:
selectAllInline选了全集而非filteredIds;filter 切换后selectedIds未清空;batch 操作初始 set 未清空旧选择;inlineCount取items而非filteredItems。4 处 scope-mismatch 同类 finding,触发feedback_plan_stateful_lifecycle_state_machine.md:"同类 finding ≥3 轮 → 停回 plan 层补状态机"。 - 根因:filter 引入了"可见集 ≠ 全集"的状态分裂,但 plan/spec 没有为此写不变量。所有与 selection/count 相关的操作默认用了
items(全集),而正确答案是filteredItems(可见集)。这不是 4 个 bug,是 1 个 failure class 的 4 个 symptom。 - 触发条件:① UI 引入 filter/search/排序等"可见集 ≠ 全集"的分裂点;② selection/batch/count 等操作需要"对什么集合操作"的决策;③ plan 层没有写 invariant table 明确"哪些操作走全集、哪些走可见集"。
- 防护(plan-time invariant table 模板):
- 每当 plan 引入 filter/search/sort 等可见集分裂点时,写一张 scope invariant table:
| 操作 | 目标集 | 不变量 | |------|--------|--------| | selectAll | filteredItems | 只选当前可见 | | count badge | filteredItems | 显示可见数 | | batch approve/reject | selectedIds ∩ filteredItems | 不操作不可见项 | | filter change | clear selectedIds | 旧选择可能不在新可见集 | - filter change = state reset event:切换 filter 后,所有依赖"可见集"的派生状态(selection/count/pagination)必须清空或重算。plan 层用
useMemo依赖链 或useEffect显式重置表示。 - code review checkpoint:reviewer 看到 filter + selection 并存时,第一动作对照 invariant table,每个操作问"用的是全集还是可见集?"。
- 每当 plan 引入 filter/search/sort 等可见集分裂点时,写一张 scope invariant table:
- 来源锚点:PR #2477(F246 Phase D)/ opus-47 vision guardian Phase D verdict / 4 处 scope-mismatch 修复(selectAllInline/filter auto-clear/batch initial set/inlineCount)
- 关联:feedback_plan_stateful_lifecycle_state_machine(同类 finding ≥3 轮→停回 plan 层)| feedback_grep_consumers_before_contract_change(改契约先 grep 消费方——scope-mismatch 是"引入 filter 改了'集合'语义但没 grep 所有消费方")
-
状态:validated
-
更新时间:2026-06-26
-
坑:F238 close gate report 写"接入 pnpm check 作为持续 verdict"。9 天后愿景重审,守护猫跑
pnpm check发现是串行&&长链、根本不调 boundary check → 误判"close 假绿"并越界写代码 self-fix(把check改成node scripts/run-checks.mjs+ 补 10 个 missing scripts + 给 8 个 settings 文件加 EXEMPT),落款"Audit Status: PASS"。实际 verdict 通过 CI workflowbrand-boundary-guard.yml(每 PR 跑且全绿)+.githooks/pre-commit+ 5 个check:*测试体兜底,愿景实际达成,F238 维持 closed。 -
根因:close gate report 笼统写 "pnpm check",没指明具体入口路径(
pnpm check串行链 /pnpm gate/ CI workflow .yml / Hook 文件 / 单独check:*命令)。守护猫复审时挑一个入口跑,看不到其他兜底入口就判"假绿"——这是入口分裂下的"摸象式 audit"。 -
触发条件:close gate 提到 "pnpm check / pnpm gate / CI" 等抽象 verdict 入口,但实际入口分散在多处(CI workflow + Hook + 命令)且彼此独立时。
-
修复:维持 F238 closed;revert 守护猫越界改动(
package.json+check-settings-primitives.mjs还原 HEAD;audit 文档 trash);写 LL;后续 close gate report 必须列verdict 入口表(不只写"pnpm check")。 -
防护:
- feat-lifecycle skill close-gate.md schema 加
verdict_entrypoints字段:每条 verdict 必须列具体路径 + 入口归属(CI workflow.yml/ Hook 文件 / 单独check:*命令 / 测试体),不允许只写pnpm check。 - 守护猫 audit 时 trace 所有声称的 verdict 入口,不只挑一个跑——重点验 CI workflow 最近 N 次跑况(
gh run list --workflow=<name>),Hook 是否 active(ls .githooks/),测试体是否绿(node --test <script>)。 - 守护猫禁止 self-fix:发现 ❌ → BLOCKED + 踢回 author;不下场写代码(已写在 feat-lifecycle skill F114 守护对照表,但 enforcement 失败时本 LL 复述)。
- feat-lifecycle skill close-gate.md schema 加
-
来源锚点:
docs/features/F238-bidirectional-boundary-symmetry.md#close-gate-report| thread[thread-id]2026-06-26 07:35 UTC 愿景重审 |scripts/run-checks.mjsline 21-50 引用 10 个 HEAD-missing scripts(独立工程债,operator 待决是否开 follow-up) -
关联:feat-lifecycle skill F114 守护对照表 + 反 anti-pattern 检查 | ADR-031 软硬 eval 三层反射 | F192 verdict-loop 闭环 | feedback_gemini_35_no_longer_what_you_thought(暹罗禁写代码硬约束)
-
状态:validated
-
更新时间:2026-06-29
-
坑:F243 Phase B
generator-architecture.mdco-design 中, 我连续 3 轮 (R3/R4/R5) 在 "spec normative 单元 vs implementation PR 单元" 边界踩坑——R3 把packages/shared/package.json的 2 个 exports 子路径预先 batch 放 B-0 prep PR (但./profile-frontmatter-parser对应 source 在 B-1 才落); R4 同样把 sanity check 预先 batch 测 2 个 export; R5 把 B-1 的scripts/docs-discovery/lib/scope-resolver.mjs写进 B-0 plan bullet。Maine Coon reviewer 连续 3 轮退回, 每轮我只修 surface (那一行 / 那一段) 不动根因, 同源错跨 surface 反复浮现。 -
根因:Spec 起草时倾向把"未来全部 declaration"一次性写完 (省 spec 行数 + 看起来完整), 但 implementation 走 PR 边界切——B-0 PR 只能 ship B-0 dist + B-0 source。Spec normative 单元 (exports list / sanity check / file plan / CI snippet) 如果不按 PR 边界切, B-0 sanity check 会真炸 (
ERR_PACKAGE_PATH_NOT_EXPORTEDfor B-1-only module)。不是"措辞精度"问题, 是 spec 直接 mis-describe 真实 deployable state。 -
触发条件:任何 cross-PR feature spec, 当 spec 同时描述 "B-X PR 改什么" 和 "feature 整体最终态" 时。高危 normative artifact:
package.jsonexports / build manifest (强 declarative + Node ESM strict mode 会真 enforce)- CI workflow snippet (跑命令真炸/真过)
- sanity check / smoke test command (跑了直接 verify)
- file plan / PR diff list (review 比对依据)
-
修复:R5 PR (commit
51c1e42b0) 完成 spec 全文按 PR 边界切——§1 exports snippet 拆 B-0 / B-1 两块, §1 sanity check 拆 per-PR (B-0 只测 scanner-discovery-pure), §7 B-0 plan 加显式负向声明 "不在 B-0 创建任何scripts/docs-discovery/*文件 (包括 scope-resolver.mjs)"。Maine Coon R6 PASS。 -
防护:
- Spec checklist (author 自检): 起草 multi-PR spec 时, 每个 normative artifact (exports / CI / sanity check / file plan) 必须显式标 "B-0 部分" / "B-1 部分" / "整体最终态", 不允许混。每个 PR scope section 末尾加"禁止动"负向声明列举该 PR scope 外的高危 path。
- Reviewer guard 反射: review multi-PR spec 时 grep 每个 PR plan 段落内 referenced file path——任何 path 不在该 PR declared add/modify 列表内, 都是 cross-PR pollution 嫌疑 (本次 R5 Maine Coon grep
scripts/docs-discovery在 B-0 part 命中 → 退回)。 - Author 自检 "trace one PR at a time" 演练: spec 写完先读 B-0 部分, 假装 B-1 还不存在, sanity check / dist 路径会不会真炸; 再读 B-1 部分, 假装只有 B-0 已 ship, 看 import / 消费 path 是否成立。
- Magic word reflex: "顺手把 B-1 的也写完省 spec 行数" / "反正都要加, 一次写完更清楚" → 都是 anti-pattern, declared scope 必须等于 deployable scope。
-
来源锚点:
- (internal reference removed) (R3-R6 fix trace + §10-§13 review notes)
- commit
cce5b1c0d(R3 fix, 错放 batch exports) - commit
8b18acdec(R4 fix, 错把 batch sanity check 留 B-0) - commit
51c1e42b0(R5 fix 收敛, 删 B-0 残留 scope-resolver.mjs + 加负向声明) - Maine Coon R3-R5 review verdicts (thread
[thread-id]2026-06-29 03:14-03:48 UTC)
-
原理:Spec 是 "deployable state declaration", 不是 "future intent declaration"。预先 batch 写 future PR 的 normative 字段, 等于 declare 不存在的 dist——sanity check 会真炸。边界混淆的代价是 implementer 在 PR 内反复踩 self-inflict 雷。同源于 R1 .mjs-only over-correction (把 scope decision 提前 batch 拍板) 也同源于 LL-087 plan-time invariant 思路 (declaration 也是 invariant 的一种, 必须 trace 真实 state)。
-
关联:F243 Phase B (active) | LL-087 plan-time invariant table 同源 (declaration = invariant 的一种) | feedback_xiaci_yiding_self_diagnosis (糖衣话术"未来一次写完"=包装当下偷懒) | feedback_grep_consumers_before_contract_change (改 contract 前 grep 全消费方; 这里 dual——declare contract 前看每个 PR 的 actual deployable state)