WeKnora
Tencent
Open-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:Fishsb/dsh-project-nav
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
核心理念(唯一上位约束)
所有开发动作必须从架构出发。 架构不出错,开发过程中出现一点问题也只是局部小问题;反之,架构错了,局部补得再好也是在错误的骨架上堆砌。
它解决的问题:AI 写单个文件没问题,但在引用之间没有全局思想—— 改一个文件不知道牵动谁,于是只能在一个文件里越堆越长。本插件把「谁属于谁」和「谁引用谁」一起摊给模型看。
v0.10.0 是架构换代:模型从「包含树」升级为「包含树 + 依赖图」。
依赖边由 import 静态扫描派生(不是登记、不是手写、不会过期),同时闸门六 → 七,新增影响面闸。
设计契约见 ARCHITECTURE.md。
一条 append-only 事件流(唯一事实源)+ 一个由它折叠出的架构模型(可丢弃缓存)+ 一层按需产出(零落盘)。 模型 = 包含树 + 依赖图;依赖图由磁盘 import 派生。闸门是对模型的查询,产出是模型的重渲染。
┌────────────────────────────────────────────────────────────────┐
│ ① 事件流 .internal/events.jsonl ← 唯一事实源(append-only)│
│ commit{锚点,scope,arch=,phase} · decide{ADR} · node{} · set{} │
└───────────────────────────┬────────────────────────────────────┘
│ 纯函数折叠(I1)
┌───────────────────────────▼────────────────────────────────────┐
│ ② 模型 .internal/runtime/arch-model.json ← 可丢弃(I3) │
│ 包含树(项目/模块/功能/工件) + 依赖图 + 证据 + 向量 + 计数 │
│ 依赖图 = import 静态扫描派生(磁盘实况,与 STALE/缺口同层) │
└───────────────────────────┬────────────────────────────────────┘
│ 全部派生(I2)
┌──────────────────────┴──────────────────────┐
▼ ▼
nav_graph 按需直出 【在场层】每轮注入 agent 上下文
(闸门=查询 / 影响面 / 健康) (systemPrompt 常驻·单一注入路径)
└──────────── 零落盘:无任何投影文件(0.12.0)────────────┘
三条不变式(可机检)
| 不变式 | 验法 | |
|---|---|---|
| I1 | 单源:模型每条属性都能由「事件流 + 磁盘实况」复算,无第二手写真相 | 复算 == 缓存(依赖图两路径必须一致,否则缓存就是第二个答案) |
| I2 | 渲染:一切机器可读产出都是对模型的纯函数派生(直出 / 注入),治理面零落盘 | 渲染调用后治理根无任何新文件 |
| I3 | 可丢弃:删掉整个 .internal/runtime/ → 治理零损失(依赖图同办) |
删后跑全量查询,结果一致 |
会因代码变更而"过期"的文档,不该是文档 —— 该是投影。
投影 = 零手写、随时重生、没有"过期"这回事(因为没人维护它)。于是手写面只剩两样:
| 面 | 载体 | 何时改 |
|---|---|---|
| 契约 | ARCHITECTURE.md · AGENTS.md |
只在架构换代 / 纪律变更时 |
| 产出 | nav_graph 按需直出 · 在场层注入(0.12.0:不再物化成文件) |
永不手写,随时重算 |
⚠ 0.12.0 换代(ADR-268):投影的"物化"整体退场 —— PROJECT.md 标记区 / .internal/ARCH-MODEL.md /
地图 HTML 三个落盘投影已删除。判因:它们的唯一读者是人,而定案是治理面只服务 agent;
实测全仓零机器读者,且 ARCH-MODEL.md 自述的唯一理由「进仓以便 git diff 审查」在治理根 D:\FF
不成立(那不是 git 仓库)。其 agent 形态的替代物是在场层(见 §3.1)。
.internal/arch/*.md 曾是要维护的资产(sha1 指纹 + 新鲜度机检 + 行号重锚),
0.10.0 起降级为按需临时投影:不盖指纹、不做机检。要让它被按任务检索到,
登记成普通 artifact 节点(nav_node layer=artifact when=…),走 nav_graph mode=docs。
工具数下降不是目标,是"闸门变查询、产出变在场"的结果。
| 工具 | 模型操作 | 典型用法 |
|---|---|---|
nav_graph |
读:落点 / 影响面 / 缺口 / 覆盖度 / 文档路由 / 健康快照(含文件职责)/ 地图 | nav_graph mode=impact target=core/model.js |
nav_commit |
写:登记改动意图(锚点 + scope + arch= 一句话),跑七闸;自动按证据收上一笔 |
nav_commit task="加一层校验" anchor=PN-F01 arch="架构不变" features=PN-F01 |
nav_decide |
写:架构决策(挂节点,登记即重置该节点补丁计数) | nav_decide anchor=PN-F01 reason=… decision=… |
nav_node |
写:节点 upsert / 退役并级联 / 参考文档工件 | nav_node target=E-F01 name=编辑器 files=src/a.js |
nav_set |
写:主线向量(doing / next / notDoing / exit) | nav_set doing="收口 shoucang" notDoing="pmg 融合" |
病根(0.11.0 自述的实测):七闸只在 nav_commit 内跑 ⇒ 不调用它 = 完全绕行无痕迹,
治理根登记率仅 3%(4747 文件 / 4600 未登记)。根因不是闸门不够,而是治理只在被调用时存在。
落盘投影曾是"给人读"的产物;只服务 agent 之后,正确的替代物不是另一个文件,而是 把治理放进 agent 的上下文。⚠ 只有一条注入路径(0.12.0 修正,ADR-277):
| 出口 | 机制 | 触发 |
|---|---|---|
| 常驻摘要 | ctx.systemPrompt.section({ text: (c) => … }) |
每轮组装求值(text 是函数,不是静态串) |
为什么删掉了第二条(
tools/post-execute+additionalContexts):0.12.0 初版曾有,实测是纯重复 ——text每轮组装即求值,故每次请求的系统提示里已是最新治理;再在写类工具后注入同一份文本等于零新增信息 (真实会话日志实测:system/message每轮 1 份,而agent/inbox/spliced含同一文本 24 条、累计 ~31KB)。 更要紧的是它创建消息,而注入消息的source是承重字段:0.12.0 初版漏传source时, 下游插件的agent/pre-step无保护地读message.source.kind⇒ TypeError ⇒ 整轮turn/end {kind:'error'}(界面:"Cannot read properties of undefined (reading 'kind')")。systemPrompt.section从不创建消息 ⇒ 结构上不可能触发它。 故删路径比补source更根本: 补字段是修一个可能再次写错的点,删掉它让整类错误无处发生。
三条硬约束(缺一即是回退):
foldOnly,实测 5.1ms),绝不在组装期跑
buildModel(含磁盘扫描,实测 177ms,每轮白付)。runtime/、不建锁、不追加事件 ⇒ I1/I3 完好。任何"先落盘再注入"的实现都是回退。为什么不与 0.11.0 的"观测层自我否决"冲突:那里否决的是订阅并记录活动事实(信息纯可派生、 只能落
runtime/⇒ 违反 I1/I3)。在场层不记录任何事实,它把已可派生的状态注入上下文。 同一份信息,被消费 ≠ 被存储。为什么不做成
pre-execute拒绝闸:治理在场的正确形态是可见,不是阻断—— 未经登记的写入被当场拒 ⇒ agent 无法开发,直接违背"让 AI 更好地把控和开发"这个目标本身。 这与 §4/§10 记录的判例同源:代理指标做成闸门必然退化成狼来了。
nav_graph mode=impact这是本插件存在的理由。动手前问一句「我改的东西,谁在引用」:
File: project-nav/core/model.js — 1 个落点文件
↓ 我引用谁(1 个落点有外部依赖):
project-nav/core/model.js → project-nav/core/log.js, project-nav/core/paths.js, project-nav/core/scope.js
↑ 谁引用我 = 影响面(8 个文件 · 5 个节点):
project-nav/core/commit.js ← 被 project-nav/core/model.js 引用
project-nav/core/format.js ← 被 project-nav/core/model.js 引用
project-nav/host/index.js ← 被 project-nav/core/model.js 引用
...
⚠ 上例取自治理根实测输出(
D:\FF,2026-09-25;本仓不是治理根,故路径带project-nav/前缀)。 判据同 §4「文件职责」那条示例必须取自真实输出:图省事写一个"应该长这样"的样例 = 训练人相信错的东西。方向读法(本节讲的就是方向,实测最易被读反,故写全):↑ 段每行的行首文件 = 引用方。 上例三行读作「
core/commit.js/core/format.js/host/index.js引用了core/model.js」——不是反过来。 ⚠ 行尾那句「← 被X引用」里的X是被查询的文件本身(不是引用方):按字面读会读反 —— 那样上例就变成"model.js引用了commit.js",与上一段 ↓(model.js只引用log/paths/scope)直接冲突。判例(本节旧示例):它列着
bootstrap.mjs—— 0.9 自举期的一次性脚本。方向为bootstrap.mjs引用了core/model.js(依据:该文件里有import { buildModel, loadModel } from './core/model.js';反向不成立 ——core/model.js对bootstrap零命中), 故它当时确实列在core/model.js的影响面里。脚本已删,此行随之失效。
nav_commit,它记下 scope 内每个文件的 {size, mtimeMs, sha1} 作为证据。nav_commit mode=archive id=ACT-N reason=…。nav_commit 内的模型查询)| 闸门 | 问题 | 判据 | 强度 |
|---|---|---|---|
| 锚点闸 | 架构节点真实存在吗? | 节点在模型中,或锚定 .internal/arch/*.md |
拒 |
| 范围闸 | 撞主线反面吗?撞别人在途 scope 吗? | notDoing 命中 → 拒;与他在途重叠 → 告警 |
拒/告警 |
| 主线闸 | scope 里的模块在主线上吗? | 未被 doing/next 引用 → 告警 |
告警 |
| 影响面闸 | scope 的落点被别的节点引用了吗? | 跨节点下游未纳入 scope → 告警(测试文件不算意外下游) | 告警 |
| 计数闸 | 同一锚点又在反复打补丁? | 自上次决策以来 ≥ 3 次 → 强制先出决策 | 拒 |
| 决策闸 | 这次改动需要架构变更吗? | arch= 缺失 → 告警要求一句话回答 |
告警 |
| 完结闸 | 有该收而未收的意图吗? | 开新笔时按证据自动收旧;异常才报 | 自动 + 报异常 |
闸门是查询而不是流程,因此不可能产生"孤儿状态",也无法被"另开一条路"绕过——写入只有一个入口。
nav_graph mode=health 里除了闸门压力,还有一节文件职责:一个落点文件被几个不同架构节点登记。
文件职责(登记落点派生 · 只读):143 个落点文件 · ⚠ 1 个被 ≥3 个节点共用
⚠ deepseek/prompt-enhancer-release/lib/index.cjs — 归属 3 · 被引 2 · 引用 9
⚠ 示例必须取自真实输出:本节先前引用的示例里写着
nav_query/nav_update/nav_docs/nav_status/nav_set_vector五个工具名,而它们在 0.10.0 之后已不存在(工具面收敛为 6 个)。 门面上的失效示例与代码里的失效守卫同害:它训练人相信一份错的契约。 上例为治理根实测输出,跑nav_graph mode=health即得。
为什么不用行数红线:行数是代理指标。长文件未必坏,短文件照样能混三个职责;而一旦把行数做成闸门, 它必然退化成"狼来了"——与 0.10.0 修掉的计数闸退化(每笔改动被计两次、阈值 3 实际 ~1.5 就触发)是同一类错。
判据是结构,不是长度:归属数 = 这个文件被几个节点声明为落点(1 个 = 职责单一);被引/引用 = 依赖度数。
它们全部由落点表 + 依赖图派生,零手写、删 runtime/ 可无损重建。
函数只报告不拒绝——拆不拆是架构判断(走 ADR),不是阈值判断。
要拆就先改 ARCHITECTURE.md §9 落点表:新增文件 = 架构变更。
| 层 | 路径 | 生命周期 | 版本控制 |
|---|---|---|---|
| 事件流 | .internal/events.jsonl |
永久 | 是(唯一事实源) |
| 运行时 | .internal/runtime/(模型缓存 · 依赖图 · 在途 · 锁 · 诊断) |
短命 | 否(gitignore,可丢弃可重建) |
⚠ 原第三层「渲染投影」已随 0.12.0 换代退场(无文件可进仓)。
不存在第三层:任何新的长期状态先回答"它是事件,还是渲染?",两者都不是就不该存在。 依赖图不是第三层——它是磁盘实况,与 STALE 探测、缺口扫描在同一位置计算。
适用范围:被治理项目的
.gitignore必须只排除 runtime,不能整目录排除.internal/—— 否则事件流不进版本控制,新 clone 读不到任何决策,"决策可传播"就是一句空话。 本仓不是治理根(治理根是D:\FF,事件流在那边)⇒ 本仓的.gitignore是整目录忽略.internal/, 与本条不冲突;判据见AGENTS.md§7。
# 1) 打包(在插件仓根)—— 产物名含版本号,从 package.json 派生
npm pack --cache .npm-cache # 产出 dsh-external-project-nav-<ver>.tgz
# 2) 装进 profile:直接跑装脚本(它自己从 package.json 读版本、摊开 tgz、定点改声明、自证)
powershell -NoProfile -ExecutionPolicy Bypass -File D:\FF\project-nav\install.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File D:\FF\project-nav\verify-install.ps1
# 3) 重启 dsh —— 装与重启是两条时间线,重启前线上仍是旧版
⚠ profile 里不能跑 npm install(
link:协议不被接受)。用install.ps1直接tar展开。 脚本版本无关(版本从package.json读)—— 换代不再新增脚本。详见AGENTS.md§4。verify-install.ps1验五级:声明面 / 实体面 / 树 = 包 = 装 / 架构面 / 运行时面。 最后一级是必需的——静态对得上 ≠ 能加载。
配置(profile 里的插件项):
| 键 | 默认 | 说明 |
|---|---|---|
root |
'' → 进程 cwd |
被治理工作区根(其 .internal/ 存事件流)。建议显式设置 |
npm test # 四个套件(项数跑一次即得,不写在这里——那是会漂移的数字)
npm run test:node-runner # 同一批用例走 node --test
| 套件 | 覆盖 |
|---|---|
test/core.test.mjs |
事件流 / 折叠 / scope 解析 / 七闸 / 依赖图扫描与影响面 / 收口 |
test/architecture.test.mjs |
不变量 I1·I2·I3(含依赖图双路径一致与可重建)· 验收判据 A1 收口不依赖会话 · A4 七闸在写入路径上强制 · A5 模型面字段数 ≤4 · A6 工具面 =5(A2 真相可自检 · A3 决策可传播 —— A3 的独立机检件随 0.12.0 落盘投影退场,真相自检现由 I1 的事件流 seq 完整性代守)· 信息可达性(节点索引完整性 / 确定性渲染零 diff / 源码静默截断扫描 / json 按 mode 对应) |
test/concurrency.test.mjs |
F1 并发追加不丢 / F2 破锁竞态 / token 校验 / 重入 / 无锁残留 |
test/host.test.mjs |
真 host 代码 + 桩 ctx:装配面 5 工具、闸门接线、nav_graph mode=impact 端到端、归属归一回归 |
沙箱提示:
node --test会用管道 spawn 子进程,在某些受限沙箱下报spawn EPERM。npm test直接执行测试文件(文件被直接运行时node:test同样执行),因此不受影响。
每次更新一律 +0.0.1,不因"加功能"跳中间位(lk 2026-09-10 定调)。 例外:架构换代才允许跳位,且必须在变更日志里写明"换代"二字。
0.12.4 → 0.12.5:修 bug + 补机检(非换代) —— 工具仍 5 · 闸仍 7 · kind 仍 4 · 数据面仍 2 层 · 零新增文件。
本轮是全量检查的第二波:治理根实测暴露两条「读数不可信」与一条「新修复自己引入的假事实」。
① 落点解析基准错位(僵尸在途的根因):resolveScope 对字面量只做「治理根相对」一种解析,
而登记面普遍用本仓相对(如 src/x.ts 真实在 shoucang/src/x.ts)⇒ 恒判 does not exist ⇒
证据恒 exists:false ⇒ diffEvidence 判 unchanged ⇒ 收口永不发生。实测治理根 15 笔僵尸在途(龄 190–9164 分钟)。
现补确定优先级链:① 根相对优先 ② 唯一同后缀命中取真实路径 ③ 歧义写 ambiguous 绝不瞎选 ④ 零命中仍登记(供 appeared)。
落地后真跑:一次性收口 18 笔,在途 22 → 4,事件流 append-only 保持、seq 连续、无重复。
② 解析漂移会造「假事实」(本轮新增修复自己引入的,被独立复核席抓到并治掉):有了后缀兜底之后,
「文本 → 实体的归属关系」会变,而旧 diffEvidence 把它当成了「文件变了」。两种实测形态:
· 登记一个尚不存在的字面量(合法:新文件本该以 appeared 被收口),随后别处出现同名无关文件
⇒ 记 appeared:'<别处的文件>' ⇒ 收口该笔,并在 append-only 事件流写下因果错误的事实;
· 先唯一命中、随后出现同名 ⇒ 决议退化为歧义 ⇒ 真路径从 files 里消失 ⇒ 记 vanished ⇒ 没有任何文件变化却判 changed。
现 diffEvidence 增 rerouted 通道(按归属分组比对),归属变了 ⇒ 既不算 modified 也不算 vanished/appeared,
reconcile 收到即不收口且原因可读(与「读不到 ≠ 改了」同族:证据面不稳 ⇒ 判据不可判)。
⚠ 这条修复自己配了常驻守卫(test/core.test.mjs:两种形态 + 两条反向「真改/真删仍要能发现」),并变异验证过能红 ——
首版判据写错过一次(用后缀关系去认归属,漏掉了「字面量与内容键同现」这个真形态),是探针把它挡下来的。
③ 未登记读数不可加和:health 把「登记条目」与「磁盘文件」并排一行像是在做减法,实测 156 + 3460 ≠ 3605(差 -2 且无处可查)。
现改三桶自证:磁盘 = 命中 + 未登记 + 显式跳过,并给出跨口径恒等式 登记条目 + 未登记 − 磁盘 = 不在盘 + 重复条目 + glob − 跳过(实测 11 ≡ 11)。
walkFiles 的 max 命中时显式标「已截断…未登记数是下界」(本仓「无静默截断」纪律)。
④ inflight 缓存 ⟷ 模型的不一致完全无声:writeInflight/clearInflight 双双 catch {},且 health 从不比对两个数
(缓存 0 个 / 模型 1 笔时那行整行消失)。现补 inflightDrift(纯数集比对)+ health 常驻一行 + 收口回执报「清理失败」;
读不到 ≠ 不一致:目录读不到时单列 unreadable 原因,不输出差集明细(判据不可得时给差集就是造差异)。
⑤ 计数闸把「零改动 / 已归档」也算补丁:isPatchRecord 不看 outcome ⇒ archived(显式归档)与 aborted(迁移期已中止)都计入,
阈值 3 被噪声稀释。现按 outcome 判:archived / aborted 不算,changed / migrated+done / 无 outcome 照算。
压力表 9 → 7 条,没有任何 3/3 掉下来(pn-f01 的 3/3 靠真补丁撑着)。⚠ 第三档(「outcome 有 changed 但只动了非代码面」)刻意不做:
实测触面表 288 条里 48 条(17%)因同一解析问题按治理根治不到,用一张 17% 不可达的表当判据会把真代码改动静默清零。
该修复已配常驻守卫(三档 × 反向,四种变异全红)。
⑥ 两处「承诺了却取不回」:gaps 打印「全量 = nav_graph mode=json」,而 skippedByGlob 不在任何 json 投影里(取回 0 条)⇒
归位到 gaps 自己的 json(读数在哪产生就由哪取回),不放默认分支以免把 json 变最肥路径;
test/tools-list.mjs 的参数抽取器把排版当结构(^\s{6}),缩进一改即静默空集 ⇒ 改为花括号配平 + depth===1,
输出与旧实现逐字符相同、纯格式化变异下不再归零。
⚠ 方法论:本轮有一条判据首版写错(用后缀关系认归属,漏掉「字面量与内容键同现」这个真形态),
是探针把它挡下来的 —— 与上一波那条同源:「写着」不等于「能红」,而「能红」也要先证明它红在对的地方。0.12.3 → 0.12.4:修 bug + 补机检(非换代) —— 工具仍 5 · 闸仍 7 · kind 仍 4 · 数据面仍 2 层 · 零新增文件。
本轮做了一次全量检查(并发只读勘察 → 并行修复 → 独立席变异复核),结论收敛成一条方法论判据:
「写着」不等于「能红」 —— 判据的字节在场,不等于破坏实现时它会失败。
① 三处判据恒真(掩盖类,最优先):在场层「零落盘」两处 —— before 取在 seed 之后(锁已建 runtime/,恒 true)、
快照只收文件不收目录(目录落盘看不见);「确定性渲染」只比同毫秒内两次求值(对 Date.now() 只在跨毫秒时偶然变红)。
⇒ 修法不是加断言,而是换取样窗口 + 快照覆盖目录与文件 + 武装自证(armSnapshotProbe:先证明快照真能发现新写入)。
② 证据读失败被判成「改了」(core/scope.js → core/commit.js):sha1 读失败返回 null,「读不到」与「内容变了」同形
⇒ reconcile 写 closed 事件静默收口 —— 而该文件自己写着「收口证据会说谎,这是最不能接受的形态」。
现改为三态(可读 / 读不到 / 不存在):读不到不算 changed、单列 unreadable 原因、绝不写 closed。
③ 模型缓存写而不读:loadModel 默认 useCache:false 却无条件 persistModel ⇒ 每笔工具调用白写约 1MB 且无人读。
读路径启用后补第二关 contentDigest(自证「这份缓存仍是写者写下的那份」);并修正一处被掩盖的旧缺口:
warm 路径原样带出缓存里的磁盘实况(stale/unregistered),删落点/加文件时事件流一字不动 ⇒ 曾报假绿,现两路共用同一重算实现。
④ 四处契约失效内容:套件表写「6 工具」(实况 5);引用的「投影索引完整性」随 0.12.0 落盘投影退场(实为改名为「节点索引完整性」,判据未变);
A1–A6 被三处引用而全仓无定义;§4 边界表缺「node / npm 不在本机 PATH」这条最常踩的事实。
⑤ verify-install.ps1 第⑤级(唯一能判「能不能加载」的一级)用裸 node ⇒ 本机 node 不在 PATH 时中断而非报错,
汇总与 exit 1 都不执行 ⇒ 调用方看到「无 FAIL」,假绿。现三级回退解析 node,全落空则显式 FAIL 并给办法。
⑥ 独立复核(变异实测,非同席自审)抓到、并被随后修复的:修复方向对,但三处新判据仍是空的 ——
host 侧零落盘因取样窗口污染恒真(同一变异下旧序绿、新序红);三态短路被改回无守卫;A5 是非空上界遇空集恒真;kind 4 种 无机检。
现全部补成能红的判据,各自附先红证据(破坏实现 ⇒ 判据变红)。测试项数不写在这里(会漂移,跑一次即得)。
⚠ 外部事件:仓内曾出现一个名字被破坏的目录('D' + U+F03A,非 D:),内含 FF\.internal\runtime\ 一棵影子治理树(空模型 eventCount:0)。
根因是 host/index.js 的 root = config?.root ? resolve(config.root) : process.cwd():isAbsolute('D<U+F03A>/FF') 为 false
⇒ resolve() 当相对路径拼到 cwd,写出 <cwd>/D<U+F03A>/FF/… 而无任何异常。残留已移出仓库(备份 ~/.dsh/tmp/pn-shadow-root-20260925);
触发源未复现(现配置 root 为纯 ASCII,全盘 15167 个文本文件零命中)。root 合法性校验未加 —— 它改 host/ 启动语义,留作具名决策。
⚠ 未做:kind 4 种 已补机检但未补契约排除条款;test/tools-list.mjs 的 ^\s{6} 缩进耦合仍会把失配报成红(不再静默)而非根治;
更正(0.12.5 发布之后的施工,本仓工作区实测):core/paths.js 的 PLANE.LEGACY/legacyDir(零消费者)
与 host/index.js 的未用 import 源码已清。实为 5 个具名 ——
readEvents/materialize/resolveScope/evidenceOf/renderTreeText,不是先前写的"两个"。
(实测本仓:git grep -n -E 'PLANE\.LEGACY|legacyDir' 命中 5 处 ——
README.md 本段 2 处为叙述,另 3 处均为注释:test/architecture.test.mjs 的「平面契约」
用例(同日在场判据那处)与其「BOM 门禁」用例里的引述,以及 verify-install.ps1 第 ④ 级的
数据面判据(同样在引述同一根因);core/ 与 host/ 去注释后零命中,即无代码消费者。)0.8.6 → 0.9.0:换骨架(决策丢弃、事实 F1–F9 保留)。0.9.2 → 0.10.0:换代 —— 模型从「包含树」升级为「包含树 + 依赖图」,闸门六 → 七。0.10.0 → 0.10.1:修 bug(非换代)—— 计数闸曾把 closed 收口回执也当补丁数,每笔改动被计两次、
阈值 3 实际在 ~1.5 笔就触发;另修 .d.ts 被当代码扫、退役锚点计数仍显示。0.10.1 → 0.10.2:修 bug + 补一条只读信号(非换代)——
① 锚点归一修漏:normalizeAnchor 只走 nodeId(layer, a),而 nodeId 会再 key() 一次,
于是 module:pn-m03 这类完整节点 id 被折成 module:module:pn-m03 → 恒 NULL。
症状是「工具描述写着锚点可为节点 id,照写的反而全被锚点闸拒」——描述与实现不符会把人训练成猜别名。
② nav_graph mode=health 新增文件职责一节:一个文件被几个不同架构节点登记为落点
(由落点表与依赖图派生,只读、不拒、不设行数红线——行数是代理指标,做成闸门必然退化成狼来了)。0.10.2 → 0.10.3:仓清理(非换代、非功能) —— 删除三个不进包的死投影
(PROJECT.md / .internal/ARCH-MODEL.md / .internal/events.jsonl):产自 0.9 自举期,
而本仓不是治理根(治理根 D:\FF,事件流在那边)⇒ 永不被 nav_render 重算 ⇒ 是历史快照而非投影,
且带着假事实(主线仍写"安装 0.9.0"、引用已删的 HANDOFF.md / core/legacy.js)。
包内容与 0.10.2 逐字节相同(三者都不在 files 内),本版仅版本号与文档变化。.gitignore / test/ / 本地脚本等)—— 一律 +0.0.1,不设「太小不必升」的判断口子;
只有架构换代才跳位。理由:可判定性高于省一个版本号;且 0.10.0–0.10.2 的发布链缺档
正是「靠判断决定要不要发布」的产物 —— 留判断口子 = 重造同类缺陷。0.10.3 → 0.10.4:仓清理(非换代、非功能) —— .gitignore 收敛三条已失效的 .internal
强制入仓规则(本仓非治理根;PROJECT.md / .internal/ARCH-MODEL.md / .internal/events.jsonl
已随死投影清理删除)。不进包(files 不含 .gitignore)⇒ 除版本号外包内容与 0.10.3 相同。0.10.4 → 0.10.5:纪律变更(非换代、非功能) —— 治理开销从"理念"落成 ARCHITECTURE §2「信息可达性」判据
(压缩去冗余可以,静默丢信息不行,字节上限刻意不设)。实现:① 工具描述与输出文本去重
(mode 语义三处 → 一处、retire 级联预写删除、commit 成功回显压成一行全勾清单、教义复述收敛);
② format=json 不再无视 mode 回全量(与所读 mode 一一对应;mode=json 仍是真全量且不 slice);
③ gaps 的未登记文件按顶层目录聚合(计数守恒,一条不丢);④ ARCH-MODEL.md 的 ADR 节改全量一行索引
(老决策不再整条消失,实测投影体积大头是此节的逐条展开);⑤ 全仓定长 slice 补"共N/+N + 取回路径";
⑥ 投影去时间戳 ⇒ 确定性渲染(模型不变 ⇒ 字节不变 ⇒ 重渲染零写盘),nav_render 自报体积(信号非裁决);
⑦ 新增四条机检(节点索引完整性 / 确定性渲染 / 源码静默截断扫描 / json 按 mode 对应)。0.10.5 → 0.10.6:加功能(非换代) —— 「先查后造」的可见化三册(A 极简路线,圆桌会议定案):
① PN-S1 查证申报可见:plan 此前写而不渲(治理根 45 条非空 plan 对模型完全不可见)⇒
补三个出口 —— renderCommitResult(写入回执;启用那个零引用的 model 形参,取值必须走
model.commits 而非 res.commit〔后者只有 {id,seq,at}〕)· renderModelDoc 收口表新增一列
(取 src.plan:闭笔事件结构性带 plan:'',照抄 c.plan 会得到"永远空白"的假绿)·
nav_graph mode=json 的 openCommits[] 补 plan;renderHealth 在途行顺带。渲染形态
<原文>(N 段)/(未填) —— 裸串无法区分"填了"与"填得完整",只渲原文会让判据永不失败(假绿)。
② PN-S2 零命中给候选:locate 未知分支补确定性候选(2-gram 重叠打分 + 落点文件后缀命中),
零扫盘(不调 walkFiles,实测 80ms/4471 文件);零候选时明说"候选 0 条",不凑数。
③ PN-S3 既有决策点名:本锚点已有 ADR 时在回执里点名(带理由摘要);无决策时零输出。
⚠ 架构面零变更:闸门仍七、事件 kind 仍 4、工具仍 6、无新增文件、未改两份契约
(PN-S3 的施工落点由草案的 gates.js 移到可见路径:通过的闸其 detail 不渲染,挂闸上等于死文本)。
新增机检 10 条(可见性三出口 marker〔变异验证:三处打回原状即三条变红〕/ 未附申报不阻断 /
闭笔后可追回 / json 见 plan / 未知 kind 必抛 / 零命中给候选 / 零候选不凑 / 后缀命中 /
有决策点名 / 无决策不点名),测试项数跑一次即得(不写死在这里 —— 那是会漂移的数字,见 §7)。0.10.6 → 0.11.0:换代 —— 治理面从「被调用的工具包」升级为「在场的治理层」。
判据来自实测:插件当时零事件监听、零外来治理探测,七闸只在 nav_commit 内跑 ⇒ 不调用它 = 完全绕行、
无痕迹。治理根实测登记率仅 3%(4747 文件 / 4600 未登记),而它治理的 shoucang 自建了 **105 个治理脚本runtime/scan-cache.json):scanImports 是纯磁盘重活(2730 代码文件 ≈ 360ms),
每次调用白付;缓存指纹 = 文件清单 + size/mtimeMs,实测 loadModel 193ms → 70ms(省 64%)。
⚠ 判据取 fromCache 布尔,不用耗时(耗时是代理指标会漂)。
② 治理主权(governanceSovereignty):纯磁盘派生,报外来治理脚本与并行账本;只报告不处置
(删除属危险操作,且产品/构建脚本与治理脚本同名相似)。artifact 的 when 写 exempt 即退出告警
(对标 Allstar 的 opt-out 策略,撤销后复现)。
③ 治理活力(governanceVitality):mtime + 事件流派生"改动是否晚于治理登记"(容差 60s,否则狼来了)。
④ 健康三层:nav_graph mode=health 分 覆盖 / 主权 / 活力 报告。
⚠ 架构面变更:闸门仍七、事件 kind 仍 4、工具仍 6、无新增文件;scope → log 依赖收紧
(扫描缓存要落 runtime/,而 runtime 文件的唯一写法是 rewriteVerified,复制第二份写法即下一个两处真相)。
⚠ 一处架构自我否决:原方案设"观测层"(订阅 tools/post-execute / fs/write-intent)记录活动事实,
实测证明其信息量为零(主权与绕过信号纯由磁盘+事件流派生),且订阅产出只能落 runtime/(可丢)
⇒ 取消,三层收敛为两层。这是减法:能被丢掉的那份不可能是真相(I1)。
另修三处门面漂移:版本三处不一致(0.10.6/0.10.4/0.10.5)、README 写死测试项数(实测不符)、
示例里的 5 个已删工具名(nav_query 等)——三条均已加机检守卫(含变异验证)。
⚠ 配置面对账(2026-09-21 复核):本条目曾声称 profile 里的
autoBindWorkspace / boundaryWorkspaces 两个死键「已删除」—— 实测那是假记录:
两个键一直留在 profile 的 cordis.patch.yml 里(且 schemastery 会把未知键保留并传给插件*,
并非静默丢弃)。现已真正清除(备份 `cordis.patch.yml.bak-deadkeys2-2026`)。
判例同 0.10.3("死投影携带假事实"):文档声称一件事已做,而实测它没做 ——
效果与"训练人相信一份错的东西"相同。声称"已删除"必须先实测它不在。0.11.0 → 0.12.0:换代 —— 治理面收敛为「只服务 agent」。
判因(实测):落盘投影(PROJECT.md 标记区 / .internal/ARCH-MODEL.md / 地图 HTML)的
唯一读者是人,而定案是治理面只服务 agent;全仓 grep 零机器读者;且 ARCH-MODEL.md 自述的
唯一理由「进仓以便 git diff 审查」在治理根 D:\FF 不成立(实测它不是 git 仓库)。
架构面变更:① 工具 6→5(nav_render 退场 —— 它唯一的操作是重生成落盘投影,
落盘面消失后它无事可做;留一个空工具比删掉更坏,它会让工具面说谎);
② 数据面 3→2 层(原「渲染投影」层退场);③ 落盘产出面整体消失;④ 新增在场层。
在场层是落盘投影的 agent 形态替代物(§3.1):ctx.systemPrompt.section 常驻摘要
(text 是函数,每轮组装求值)。它直接治 0.11.0 自述的病根
——「七闸只在 nav_commit 内跑 ⇒ 不调用它 = 完全绕行无痕迹」(治理根登记率仅 3%)。
⚠ 不是重开 0.11.0 否决的"观测层":那里否决的是记录活动事实(信息纯可派生、只能落
runtime/ ⇒ 违反 I1/I3);在场层不记录任何事实,它把已可派生的状态注入上下文 ——
同一份信息,被消费 ≠ 被存储。
⚠ 不是 pre-execute 拒绝闸:治理在场的正确形态是可见,不是阻断 ——
未经登记的写入被当场拒 ⇒ agent 无法开发,直接违背"让 AI 更好地把控和开发"这一目标本身。
三条硬约束(缺一即回退):只吃纯事件流折叠(foldOnly 实测 5.1ms vs 全量 buildModel
177ms,35×)· 零落盘(不建 runtime、不加事件)· 不知道的不说(磁盘实况不在廉价模型里,
故摘要不报 STALE/缺口 —— 报一个没算过的数就是假绿)。
不变式的收紧:I2 从「手改渲染物会被覆盖」变为「治理面零落盘」(更强的机检判据:
调用渲染后治理根无任何新文件)。I1/I3 完好(在场层零状态)。
⚠ 一处对旧实现的自我纠正:renderModelDoc 里那个"回查 closes 指向的原始笔以取 plan"
实为 no-op —— 折叠后 closes 恒指向原笔自身(实测 68/68),回查等于原地打转。
(原句写的是"21 条中 closes !== seq 者 0 条":该计数取的是折叠层;事件流层恰好相反 ——
带 closes 的记录 68 条中 closes !== seq 者 68 条。分层说明见 ARCHITECTURE §5。)0.12.0 → 0.12.1:修 bug(非换代) —— 在场层收敛为唯一一路(ADR-277)。
0.12.0 初版有两条注入路径,其中 tools/post-execute + additionalContexts 是纯重复:
systemPrompt.section 的 text 每轮组装即求值,故每次请求的系统提示里已是最新治理,
再在写类工具后注入同一份文本等于零新增信息(真实会话日志实测:system/message 每轮 1 份,
而 agent/inbox/spliced 含同一文本 24 条、累计 ~31KB)⇒ 违反 §2①「零信息重复必删」。
更要紧的是它创建消息:注入消息的 source 是承重字段(类型必填),0.12.0 初版漏传时
下游 dsh-repeat-tool-reminder 的 agent/pre-step 无保护地读 message.source.kind ⇒ TypeError
⇒ 整轮 turn/end {kind:'error'}(界面:"Cannot read properties of undefined (reading 'kind')")。
systemPrompt.section 从不创建消息 ⇒ 结构上不可能触发它。 ⇒ 删路径比补 source 更根本:
补字段是修一个可能再次写错的点,删掉它让整类错误无处发生。
架构面:工具仍 5、闸门仍七、kind 仍 4、零新增文件、零新增状态;inject 仍 ['tools','systemPrompt']。
新增机检:host 内不得出现 additionalContexts / createUserMessage / 'tools/post-execute'
(源码级 + 行为级双重断言,含变异验证)。0.12.1 → 0.12.2:记录面对账(非换代、非功能) —— 清掉 profile 里两个真正的死配置键
(autoBindWorkspace / boundaryWorkspaces,全装机零消费者、插件 Config 只消费 root),
并修正 README 的一条假记录:0.11.0 条目曾声称这两个键「已删除」,实测它们一直留在
profile 的 cordis.patch.yml 里(且 schemastery 会保留未知键并传给插件,并非静默丢弃)。
判例同 0.10.3("死投影携带假事实"):文档声称一件事已做而实测没做,效果是同一种 ——
训练人相信一份错的东西。新增机检:README 若提到这两个键,必须是对账叙述而非既成事实句,
且 core/ + host/ 源码不得再出现它们(含变异验证)。
本次包内容确有变化(实测逐文件 SHA 比对 0.12.1 包):ARCHITECTURE.md(在 files 内)
与 README.md(不在 files 内,但 npm 总是自动包含它)都改了 ⇒ 须走完整发布链。
⚠ 施工中我曾误判为"不进包、除版本号外相同"——「不在 files 里」不等于「不进包」:
README / LICENSE / package.json 是 npm 的自动成员。这类判断只能靠解包逐文件比对,
不能靠读 files 白名单推断(本仓第 N 次同类教训:判据打在实测上,不打在推断上)。0.12.2 → 0.12.3:修 bug(非换代) —— 主线向量的缺席不可观测(ADR-372)。
病根不是"少显示一行":同一份向量曾在四个渲染站点各自手写,对"未填"给出四种语义
—— nav_set 回执四字段全显式 (unset);renderHealth / renderPresence 只显式 doing/next,
notDoing/exitCondition 整行消失;renderTreeText 干脆不渲染 exitCondition。
于是「新增字段漏改一处」与「某字段从未被渲染」都不产生任何信号
(实测:全仓 exitCondition 13 处命中中消费面仅 2 处打印、七闸无一读取它)。
这与本仓 2026-09-23 刚治过的 ACT-341「空扫不得判绿」同族:失败不可观测。
修法:格式化收敛为单一权威 vectorFields()(core/render.js 导出,四站点全走它);
缺席一律显式 (unset)。判据从模型派生(Object.keys(model.vector) 排除 updatedAt)
⇒ 向量长出新字段而无人渲染时自动失败,无需人维护。
架构面:工具仍 5、闸门仍七、kind 仍 4、零新增文件、零新增状态、vector 形状不变;
显示态与数据/判定态分离 —— JSON 快照缺席仍是空串(''),gates.js 谓词仍取空串,
(unset) 只存在于给 agent 看的文本里。新增机检用例带变异验证(删掉一处理染 ⇒ 必须变红)。ARCHITECTURE.md——它是本仓的架构契约,不是说明书。nav_graph 按需直出、在场层注入上下文。
任何"先写个文件"的实现都是回退(0.12.0,ADR-268)。BSD-3-Clause © Fishsb (lk)
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: documentation、knowledge-graph。