Skip to content

release-v0.1.11

Choose a tag to compare

@Moowningstar Moowningstar released this 31 Oct 12:10
· 5 commits to main since this release

0.1.11

先说清楚索引怎么了,再告诉你这次改动破坏了什么。

0.1.10 之后有一个未发布的修正:插件不再往没有归属元数据的 .gitnexus 里写东西。0.1.11 把这个修正发出去,并补上它欠的两半——面板终于能把「索引不是我的」「索引属于别的检出」和「还没有索引」分开说;以及一个新的驻守进程,在你改代码时盯着改动集,回答「哪些引用断了」。


新增

存储状态不再是布尔值

上游 GitNexus 的存储解析器有六个状态:missing、empty、owned、unowned、foreign、invalid_storage。插件以前只问一句「gitnexus.json 或 meta.json 在不在」,于是 unowned 被当成「还没有索引」,foreign 被当成「索引健康」——后者更危险:一个被复制或移动的检出会读到别人的图。

0.1.11 新增 src/gitnexus/storage-state.ts,同步判定、纯函数决策,额外区分第七种状态 unknown:读不到字节不等于存储坏了,上游把两者都叫 invalid_storage,我们分开,因为「无法读取」不该触发全量重建的建议。

  • 三处写守卫(prepareAnalyzeCacheDirs、forgetUnownedLeftovers、scratchDirFor)从「文件存在」改为「存储可用」。
  • formatReport 的 NOT FOUND 拆成 NOT FOUND(真的没有索引,跑 gitnexus analyze)和 BLOCKED (<state>)(存储存在但不可用,唯一可用操作是全量重建 --force)。
  • 图投影在 projectGraph / projectViaHelper 之前加 assertUsableStorage:图的 lbug 文件本身与清单无关,没有这道闸,一个移动过的检出会投影出上一个检出的图。
  • 失败信息补上 foreign 与 invalid_storage 的中文说明,unowned 原有的那句保持不变。

面板的第三屏:unowned/foreign/invalid_storage/unknown 不再显示「该项目还没有 GitNexus 索引」,而是单独一屏说明状态、来源与下一步;「初始化索引」按钮被隐藏(它会立刻失败),「全量重建」成为当前适用操作。GET /api/gitnexus/coverage 的 repo 多一个 storage 字段,旧宿主不带它时客户端回退到原来的两屏行为。

检查计数变成了断言

README 曾写 1254 项,实际是 1258——这类漂移以前没人守。现在 test/claims.mjs 读两份 README 里的那句话,三个 runner 各自拿自己的总数和它对齐;不一致时那次运行直接失败,并打印两边各自的数字。两份 README 还必须互相一致。test/suites/claims.mjs 有四条用例证明守卫本身没瞎:缺句子要报错、不一致要报错、多一项要报错、空 fixture 不能因为匹配不到而“通过”。

驻守追踪器:改动集破坏了什么

一个按项目启动的监听器(插件进程内,最多同时盯三个项目),监听工作区与提交变化,从上一次分析里回答依赖风险,不重跑索引、不跑编译器:

  • src/gitnexus/watch/signals.ts:无渲染层的信号图(signal/derived/effect/batch/untracked)。相等写入不推进版本、不触发效果;derived 只在被读取时计算,按来源版本去重,失败对同一签名记忆到 invalidate()。借的是 Angular Signals 的逻辑,不做它的渲染。
  • src/gitnexus/watch/stamps.ts:fs.watch(recursive) + 去抖(800 ms,最小间隔 5 s,事件风暴降级到 10 s,EMFILE/ENOSPC 退回 5 s 轮询);提交与索引戳直接读 .git/HEAD、refs、gitnexus.json 的 mtime/size,不 spawn git。实测一次嵌套写入在 Windows 上最多触发 6 个事件,所以去抖不是可选项。
  • src/gitnexus/risk-query.ts:在 helper 进程里跑三条白名单查询,把结果分成三桶——确定断裂(文件已删除、其它文件仍引用它的符号,附调用者)、暴露(被改动文件的符号仍被引用,明确写“不是编译结论”)、盲区(excluded、索引没记录、超出上限)。上限 300 文件 / 6000 符号 / 12000 边 / 4000 社区,截断时如实写 truncated。
  • src/bin/risk-helper.ts:请求走文件而不是 stdin——src/subprocess.ts 以 stdin: 'ignore' 起子进程,这是既成事实,不是偏好。
  • src/gitnexus/watch/registry.ts:单飞、60 s 空闲回收、5 分钟无消费者停止计算、analyze.lock 存在时跳过;改动集 certain: false 时只写「改动集不完整,未能评估依赖」,不猜。
  • 交付面三处:GET /api/gitnexus/risk(只读快照,自己不起查询)、gitnexus_risk 工具、以及三个图工具答复顶部多出来的一行——紧跟在原有的索引新鲜度那行后面,干净时不说话。
  • 面板新增「依赖风险」卡片,并点名文件:驻守进程刚看到被写入的路径(去重、5 分钟窗口、上限 40,写同一文件多次只留最新一次)与改动集中与索引不一致的文件(+/~/- 标记,只展示前 8 个,计数是完整的)。两者刻意分开:前者是事件级的「正在被写」,一个字节都没变的保存也会出现;后者是哈希级的「与索引不同」,只和上一次评估一样新。结论行来自宿主,卡片不自己推导;确定断裂默认展示前 5 条、暴露前 3 条,每条带 file:line、引用数与是否跨社区;底部永久写明这是结构性判断、不是编译结论、也不代表测试通过或失败。

修正

  • 未归属存储的三处写入(0.1.10 之后提交、本次随版本发布):scratchDirFor 在任意 git 捕获时创建 .gitnexus/plugin-home/tmp;prepareAnalyzeCacheDirs 在 .gitnexus 存在时预建缓存;失败的运行不清理重定向的 GITNEXUS_HOME。三者现在都先问存储可用性。
  • 风险查询复用图工具的自愈路径:孤立的 lbug.shadow 边车以前会让依赖评估报 Runtime exception: Found shadow file ...(图工具会自愈并正常回答),现在同样隔离并重试一次。
  • 盲区原因不再永远是 no-symbols:索引清单里有文件列表时,能区分「不在索引里」与「在索引里但没有符号」。
  • 卡片不再卡在「评估中…」:GET /api/gitnexus/risk 以前先起一次评估再读快照,而 settle() 在第一个 await 之前就把 busy 置真,于是每 4 秒一次的轮询永远读到「正在查询」。现在先读已完成的快照、再在后台安排下一次;卡片也改成「有结论就显示结论」,正在跑的后台刷新不会把已完成的判断换成「评估中」。
  • 一张卡只说一件事:需要注意 里的 uncovered-files(「N 个文件不在索引中」)与 index-holds-retired-paths(「N 个索引文件已不存在」),和「依赖风险」卡片已经在列的 盲区 · 不在索引里、相对索引的改动 是同一个事实的两种计数法,读者要在两处对同一件事。这两条告警现在随风险卡渲染,而且只在追踪器没有活的改动集时出现——有实时改动集时以它为准,免得让人去比较两套计数口径;需要注意 只留扫描自身能力、版本不匹配、活动窗口、存储状态这四类与「本次改动」无关的事实。

写入前就告诉模型:改动辐射面

前几项都在事后说话——卡片、图工具、答复顶行。可开发者真正要决定的是「这个文件到底该不该动」,而字节写完就晚了。所以同一套测量也放在 edit / write 之前:

  • src/gitnexus/precheck.ts:从一次风险查询里归纳出「被多少个文件引用、总引用数、跨社区符号数、最重的几个符号」,三个阈值(≥5 个文件、≥2 处跨社区、>40 处引用)满足任一条即算「改动面较大」。describeImpact 同时给操作者与模型看,所以两边不会对同一次测量说法不一致。
  • src/index.ts 挂 tools/pre-execute:只有 edit/write、只有能定位到已归属索引仓库的文件、只有本会放行的调用才可能被拦。命中阈值返回 ask,displayReason 是中英双语的确认文案(reason 进审计,也是模型被拒时看到的理由)。
  • 同一处还注册 systemPrompt.context:一行当前改动集(改/增/删 + 结构结论),干净时返回空串。模型不必先调工具就知道辐射面。
  • 三条失败即退场的规矩写进代码注释与 AGENTS.md:没有审批服务(ask 在那种环境等于拒绝)、没有 ctx.on、测量失败或超过 4 秒——一律原样放行。一个测不出来的门禁绝不能变成挡住工作的门禁。precheck: false 同时关掉这两半。
  • 用 4 秒上限 + 1 分钟缓存换速度:给每次编辑加二十秒的门禁,一天之内就会被关掉,然后什么都保护不了。

验证

  • npm run check:三个 tsconfig 项目全部通过。
  • npm test:引擎 994、插件 90、客户端 345,合计 1429,0 失败;两份 README 的计数与本次运行一致(由守卫断言)。
  • node lib/bin/gitnexus-coverage.js . --fail-on-uncovered --fail-on-missing:退出码 0。
  • 新增用例:storage-state(33)、signals(20)、risk-format(26)、risk-query(10,含真实索引上的断裂与盲区)、watch(13)、claims(4)、precheck(19);客户端新增 11 条(第三屏 5、风险卡片相关 6),插件新增 8 条(第六个工具与第十二条路由 4 条、门禁监听注册 4 条)。

已知限制

  • 不是编译器。确定断裂只覆盖「文件删了、符号仍被引用」这一种结构事实;修改文件只算暴露。没有行级归因:差量本身就是文件级的(src/gitnexus/delta.ts 读哈希与路径,不含行号)。
  • 依赖评估最多处理 300 个改动文件;更大的改动集会被截断并在文本里写明。
  • 追踪器同时最多盯 3 个项目,5 分钟没人读就停止计算;下次读取会重新开始。
  • 写入前门禁只认 edit / write 两个工具的 file_path:用 shell 重写文件的改动不会被拦(去猜 bash 命令的目标文件,只会把警告指向错误的文件)。4 秒内没测完就放行,不提示——这是有意的取舍,宁可漏一次也不让编辑卡住。
  • 门禁的判定是「结构性影响面」,不是「这次改动会坏」。它提高被看见的概率,不证明正确性;真正能回答「坏没坏」的仍然是编译与测试。
  • 宿主未挂载 /api/gitnexus/risk 时,卡片会明说「宿主未挂载追踪路由」,而不是显示空卡片;工具与文本的风险行不受影响。
  • 存储状态判定按上游 resolver 的口径实现,但 allowedStates 的具体映射(analyze 与只读入口分别接受哪些状态)没有逐行复核——本版本只依赖「能不能写」与「能不能读」两个结论。
  • .docs/ 下的一切仍然不发布;npm pack 只带 lib/、两README、LICENSE、NOTICE 与 patch 文件。