<!-- 受众: AI(直接投喂) | 用途: 让 AI 改代码前发这份，要它照做 -->
<!-- 这些是发给 AI 读的内容。人类使用说明在 ../给人看/ 。 -->
# ⚖️ 协作铁律 · 发给 AI，要它照做

> 顶部代码块「铁律速查卡」是**直接发给 AI 的**。下面各节是展开解释，AI 需要时可读。

---

## 铁律速查卡（整段发给 AI）

```
在这个项目里改代码，你必须严格遵守以下铁律，违反任何一条都要重做：

【R1 · 加法式改动】新逻辑只加不改。先搜有没有现成实现可复用（grep 关键词），
   有就复用，没有再新建。绝不为同一个概念写第二份真源。

【R2 · 纯叶子模块】新逻辑写成一个「纯叶子」：
   零 IO（不读写文件/网络/环境副作用），确定性（同输入同输出），
   绝不抛异常（输入再坏也返回安全默认值），可单独单测，不 require 重依赖。

【R3 · 默认开的门控】用一个 KHY_XXX 环境变量门控新逻辑，默认开(default-on)。
   登记到 services/backend/src/services/flagRegistry.js。父门控关→子门控必关。

【R4 · 关闭即逐字节回退】KHY_XXX 关闭时，程序行为必须和你改动前逐字节相同。
   接线处用 try/catch 包住：叶子异常时 fail-soft 回退旧行为，绝不把异常抛给调用方。

【R5 · 严格超集】新行为是旧行为的严格超集：只在旧路径漏做/做错处补正，
   绝不改变既有正确路径的任何输出。安全向改动只多封锁/多清理，不放宽。

【R6 · 必须真接线】新逻辑必须能从三个真实入口之一被 require 到才算「在产」：
   executeTool（toolCalling.js）/ toolUseLoop.js / aiManagementServer.js。
   隔离单测全绿 ≠ 在产。没接线就别在报告里写「已落地」。

【R7 · 守卫必须绿】改完跑相关单测；提交前守卫(scripts/check-*.js，git 钩子自动触发)必须全绿。
   守卫红了先修红，绝不用 git --no-verify 跳过。

【R8 · 不建上帝组件】单文件不超过 2500 行。要拆分就把内聚分节抽成纯叶子，
   原文件用同名别名 re-export 保持对外契约不变。

【R9 · 诚实收尾】做完如实说：测了什么、几个绿、跳过了什么、哪些没做。
   不确定就说不确定。不许把「跑了工具但没交付结论」当成做完。

做每一步前先说你要改哪个文件、加哪个 KHY_ 门控、关掉后怎么回退，我确认后再改。
```

---

## R1–R2 展开 · 纯叶子模块长什么样

```js
'use strict';
/**
 * xxxGuard.js — 一句话职责。纯叶子：零 IO、确定性、绝不抛、可单测。
 * 门控：KHY_XXX（默认开；仅显式 0/false/off/no 关）。
 */
function isEnabled(env = process.env) {
  try {
    const v = env && env.KHY_XXX;
    if (v === undefined || v === null) return true;      // 默认开
    return !['0', 'false', 'off', 'no'].includes(String(v).trim().toLowerCase());
  } catch { return true; }
}
function decide(input) {
  try {
    // ...纯计算，输入坏也返回安全默认...
    return { ok: true, value: /* ... */ };
  } catch { return null; }   // 绝不抛
}
module.exports = { isEnabled, decide };
```
要点：零 IO（IO 留在接线处）；绝不抛（坏输入返 null/安全默认）；`env` 作参数可注入便于单测；登记 flagRegistry。

## R3–R4 展开 · 门控接线与逐字节回退

```js
let result = legacyValue;              // 旧行为基准
try {
  const leaf = require('./xxxGuard');
  if (leaf.isEnabled(process.env)) {
    const d = leaf.decide(input);
    if (d) result = d.value;           // 门开且叶子给出结果才覆盖
  }
} catch { /* fail-soft：保持 legacyValue */ }
```
验证回退：临时 `KHY_XXX=off` 跑同一输入，输出应与改动前逐字节相同。

## R5 展开 · 严格超集判据
- ✅ 旧代码只拦小写 `.ssh`，你补大小写折叠——只多拦不少拦。
- ✅ token 计数边界错档，你只修错那档——旧正确值不变。
- ❌ 为「更简洁」重写整个函数导致既有正确输出也变了——这是替换，不是超集。
安全类改动方向永远是：只多封锁 / 只多清理 / 只多脱敏，绝不放宽既有防线。

## R6 展开 · 在产的唯一判据
只有能从这三个入口之一被 require 到才算真生效：
| 入口 | 位置 |
| --- | --- |
| executeTool() | services/backend/src/services/toolCalling.js |
| 工具循环 | services/backend/src/services/toolUseLoop.js |
| 守护进程 SSE | services/backend/src/services/aiManagementServer.js |
自查：`grep -rl "require(.*/<你的模块名>" services/backend/src --include=*.js | grep -vE "/<你的模块名>/|tests/"`
若只剩别的治理引擎（而非上面三入口），说明没在产，是死代码。

## R7 展开 · 守卫清单（提交前自动跑，红了不许提交）
| 守卫 | 管什么 |
| --- | --- |
| check-leaf-contract.js | 自称纯叶子的模块是否真零 IO/不抛 |
| check-flag-registry.js | KHY_* 是否登记、父子优先级是否一致 |
| check-model-hardcoding.js | 模型名/默认值是否散落硬编码 |
| check-tool-contract.js | 工具契约是否合规 |
| check-change-safety.js | 本次改动是否需补跑检查（如 node-syntax） |
| check-agent-rules.js | agent 规则一致性 |
发布另有 scripts/release/release-gate.js（含 node-syntax 阶段）。

## R8 展开 · 拆上帝组件
上限 2500 行。看待办：`npm --prefix services/backend run arch:god`。
拆法：抽内聚分节成纯叶子，原文件同名别名 re-export 保契约。
先例：localBrainCalc / localBrainTextOps / localBrainSessionContext / localBrainFileLookup。

## R9 展开 · 诚实收尾模板（照填发我）
```
改了：<文件> 加 <KHY_门控>（默认开）
接线：从 <executeTool/toolUseLoop/aiManagementServer 的哪处> 可达
回退：KHY_门控=off 时逐字节等价（已验 / 未验）
测试：<X/Y 绿>；守卫：<全绿 / 哪个红>
没做/存疑：<如实列出>
```

## 常见「看起来对其实错」的坑
- observe ≠ enforce：KHY_EVO_ENGINE/KHY_STRUCTURED_FURNACE 默认观测模式，里面写阻断/改写会被静默忽略。
- 门面类是死的，叶子是活的：MetaPlanCoordinator/MarshalCoordinator 无任何 import；改门面不影响线上。
- fail-open vs fail-closed 别搞反：能力/约束求解异常一律 fail-open；红线/审批判定必须 fail-closed。
- bundled 快照会过期：pip 包内是打包时快照；改了仓库源要重新打包才对已装用户生效。
- 规范号别猜：现场扫 `find docs -name '*DESIGN-ARCH*' | grep -oE 'DESIGN-ARCH-[0-9]+' | sort -u | tail -1` 取最大值+1。
