返回目录
文件与数据 插件

dsh-project-nav

Fishsb/dsh-project-nav

Anti-drift project governance plugin for DSH — bidirectional feature-map, mainline vector, architecture-first protocol | DSH 项目反漂移治理插件

Stars
0
Forks
0
Issues
0
更新
12 天前

PROJECT TOPICS

项目标签

INSTALL REFERENCE

安装参考

未验证
dsh plugin --profile web add github:Fishsb/dsh-project-nav

该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。

PROJECT README

README

🧭 dsh-project-nav

面向 DeepSeek Harness(DSH)的项目反漂移治理插件

version license dsh-tools node

每个任务从架构出发 · 每个文件都有落点 · 一切皆可回溯


核心理念(唯一上位约束)

所有开发动作必须从架构出发。 架构不出错,开发过程中出现一点问题也只是局部小问题;反之,架构错了,局部补得再好也是在错误的骨架上堆砌。

它解决的问题:AI 写单个文件没问题,但在引用之间没有全局思想—— 改一个文件不知道牵动谁,于是只能在一个文件里越堆越长。本插件把「谁属于谁」和「谁引用谁」一起摊给模型看。

v0.10.0 是架构换代:模型从「包含树」升级为「包含树 + 依赖图」。 依赖边由 import 静态扫描派生(不是登记、不是手写、不会过期),同时闸门六 → 七,新增影响面闸。 设计契约见 ARCHITECTURE.md。


1. 一句话架构

一条 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/ → 治理零损失(依赖图同办) 删后跑全量查询,结果一致

2. 治理面最小化(第一性判据)

会因代码变更而"过期"的文档,不该是文档 —— 该是投影。

投影 = 零手写、随时重生、没有"过期"这回事(因为没人维护它)。于是手写面只剩两样:

面 载体 何时改
契约 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。

3. 五个工具(11 → 5)

工具数下降不是目标,是"闸门变查询、产出变在场"的结果。

工具 模型操作 典型用法
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 融合"

3.1 在场层:治理不再只等被调用(0.12.0)

病根(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 更根本: 补字段是修一个可能再次写错的点,删掉它让整类错误无处发生。

三条硬约束(缺一即是回退):

  1. 走廉价路径:只吃纯事件流折叠(foldOnly,实测 5.1ms),绝不在组装期跑 buildModel(含磁盘扫描,实测 177ms,每轮白付)。
  2. 零写盘:不落 runtime/、不建锁、不追加事件 ⇒ I1/I3 完好。任何"先落盘再注入"的实现都是回退。
  3. 不知道的不说:磁盘实况(STALE / 缺口 / 依赖图)不在廉价模型里,故摘要不报它们 —— 报一个没算过的数就是假绿。

为什么不与 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=…。

4. 七个闸门(全部是 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 落点表:新增文件 = 架构变更。

5. 数据面:2 层(3 → 2,0.12.0)

层 路径 生命周期 版本控制
事件流 .internal/events.jsonl 永久 是(唯一事实源)
运行时 .internal/runtime/(模型缓存 · 依赖图 · 在途 · 锁 · 诊断) 短命 否(gitignore,可丢弃可重建)

⚠ 原第三层「渲染投影」已随 0.12.0 换代退场(无文件可进仓)。

不存在第三层:任何新的长期状态先回答"它是事件,还是渲染?",两者都不是就不该存在。 依赖图不是第三层——它是磁盘实况,与 STALE 探测、缺口扫描在同一位置计算。

适用范围:被治理项目的 .gitignore 必须只排除 runtime,不能整目录排除 .internal/—— 否则事件流不进版本控制,新 clone 读不到任何决策,"决策可传播"就是一句空话。 本仓不是治理根(治理根是 D:\FF,事件流在那边)⇒ 本仓的 .gitignore 是整目录忽略 .internal/, 与本条不冲突;判据见 AGENTS.md §7。

6. 安装

# 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/ 存事件流)。建议显式设置

7. 测试

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 同样执行),因此不受影响。

8. 版本规则

每次更新一律 +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 内),本版仅版本号与文档变化。
  • 口径澄清(2026-09-12):「每次更新」= 任何合并进 main 的改动,含不进包的仓清理 (.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 个治理脚本
    • 3 本并行账本 ⇒ 插件根本不知道项目在自己建治理。本次换代补齐四件: ① 响应性(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 看的文本里。新增机检用例带变异验证(删掉一处理染 ⇒ 必须变红)。

9. 开发纪律

  1. 改代码前先读 ARCHITECTURE.md——它是本仓的架构契约,不是说明书。
  2. 改了架构先改契约;契约之外不新增文件(新增即架构变更)。
  3. 治理面零落盘:产出只有两种形态 —— nav_graph 按需直出、在场层注入上下文。 任何"先写个文件"的实现都是回退(0.12.0,ADR-268)。
  4. 会因代码变更而过期的文档,不该是文档 —— 该是投影。(§2)
  5. 事故事实(F1–F9)不可丢弃:它们是需求,只有实现方式可以换。

License

BSD-3-Clause © Fishsb (lk)

CLASSIFICATION EVIDENCE

分类依据

项目类型插件
功能分类文件与数据
规则置信度高

系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: documentation、knowledge-graph。