📘 Khy-OS 文档站 🧭 新手先读:核心概念

⚖️ 协作铁律 · 发给 AI,要它照做

<!-- 受众: 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 展开 · 纯叶子模块长什么样#

'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 展开 · 门控接线与逐字节回退#

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
守护进程 SSEservices/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.jsKHY_* 是否登记、父子优先级是否一致
check-model-hardcoding.js模型名/默认值是否散落硬编码
check-tool-contract.js工具契约是否合规
check-change-safety.js本次改动是否需补跑检查(如 node-syntax)
check-agent-rules.jsagent 规则一致性

发布另有 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。