# 发布说明 v0.4.2(质量门禁收口) [← 返回 README](../README.zh.md) | 相关:[v0.4.1 发布说明](v0.4.1.md) **发布日期**:2026-09-16 | **区间**:`v0.4.1 → v0.4.2` **规模**:代码修复 1 commit + 锁文件元数据 1 commit + 发布 1 commit; 源码与测试 8 文件 +106 / −54,无新增模块,测试套件与用例数不变(6 套 101 例全绿) v0.4.2 是一次**纯代码质量**版本:清零 PR #7 上 Sonar「New Code 可靠性评级 = D」 门禁的全部 15 项告警(12×S2871、1×S3776、1×S7778、1×S1199)。 **没有任何用户可见的行为变化**——每一处修改要么是把代码原本就在做的语义显式写出来, 要么是内部拆分。 --- ## 修复 ### 字符串排序全部改用显式比较器(12 项 CRITICAL,S2871) `Array.prototype.sort()` 不带比较器的写法被静态分析判为可靠性缺陷(默认序随元素 `toString` 漂移)。新增 `src/model.ts` 导出的具名比较器: ```ts export function byCodeUnit(a: string, b: string): number { if (a < b) return -1; if (a > b) return 1; return 0; } ``` 它就是 UTF-16 码元序——与 `sort()` 默认行为**逐字节等价**,所以全部排序结果的既有 顺序不变、测试零改动。刻意不用 `localeCompare`:诊断/快照的键集顺序、告警门控签名、 `list` 输出序都是 wire 可见的,不能交给宿主机的 locale 决定。落点: `src/registry.ts` 6 处(canonicalConfig、两个签名函数、两路 watcher 键、blocked 集)、 `src/cli.ts` 1 处(profile 枚举)、五个测试文件 8 处(含未被标记的同类,一并对齐)。 ### `scanProject` 认知复杂度 24 → 约 6(S3776) 两处无损拆分(`src/registry.ts`): - 同服务剔除告警 + 变更门控(签名比对、告警循环)抽为方法 `warnIdentityShadows(key, projectRoot, identityShadows): boolean`,返回 `shadowChanged` 供诊断条件使用; - scan 诊断的「值不值得写」大 || 条件与载荷字段抽为纯函数 `buildScanDiag(input): Record | null`,返回 `null` 即 「干净且无配置」——**零配置项目不留痕**的约定原样保持,写入的载荷逐字段等价。 ### 其余两项 MINOR(S7778 / S1199) - `src/cli.ts` `collectLayers` 末尾两次 `layers.push` 合并为单次调用(实参自左向右 求值,六层影子优先序不变)。 - `test/test-model.mjs` 第 17 用例去掉冗余裸块。 --- ## 依赖元数据 `pnpm-lock.yaml` 的 `@deepseek-ai/cordis` importer specifier 由 `^4.0.1` 同步为 `^4.0.2`——`package.json` 自 v0.4.1 起声明的就是 `^4.0.2`(解析版本一直是 `4.0.2`), 此前锁文件差一行元数据导致 pnpm 每次运行都要求重锁。不改依赖解析结果。 ## 升级 无破坏性变更,无需任何操作:不写不改任何配置文件,装载、deny、诊断、CLI 输出与 v0.4.1 完全一致。