@agile-team/wl-skills-gate 0.0.0-stage → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/LICENSE +18 -0
  3. package/README.md +78 -2
  4. package/bin/wl-skills-gate.js +125 -0
  5. package/docs/01-quick-start.md +44 -0
  6. package/docs/02-config.md +88 -0
  7. package/docs/03-reports.md +49 -0
  8. package/docs/04-fix-loop.md +51 -0
  9. package/docs/05-adapters.md +43 -0
  10. package/docs/06-gitlab.md +94 -0
  11. package/docs/07-jenkins.md +62 -0
  12. package/docs/08-hooks.md +18 -0
  13. package/docs/09-selftest-coverage.md +34 -0
  14. package/docs/10-enforcement.md +46 -0
  15. package/docs/11-hardening-review.md +50 -0
  16. package/docs/12-closed-loop-review.md +90 -0
  17. package/docs/13-quality-cost-and-isolation.md +112 -0
  18. package/docs/14-low-token-execution.md +110 -0
  19. package/docs/15-java-report-triage.md +50 -0
  20. package/docs/README.md +48 -0
  21. package/fixtures/cases/01-clean/expected.json +1 -0
  22. package/fixtures/cases/01-clean/inputs/report.json +6 -0
  23. package/fixtures/cases/01-clean/quality/policy.yml +8 -0
  24. package/fixtures/cases/02-new-error/expected.json +1 -0
  25. package/fixtures/cases/02-new-error/inputs/report.json +6 -0
  26. package/fixtures/cases/02-new-error/quality/policy.yml +8 -0
  27. package/fixtures/cases/03-corrupt-report/expected.json +1 -0
  28. package/fixtures/cases/03-corrupt-report/inputs/report.json +1 -0
  29. package/fixtures/cases/03-corrupt-report/quality/policy.yml +8 -0
  30. package/fixtures/cases/04-missing-file/expected.json +1 -0
  31. package/fixtures/cases/04-missing-file/quality/policy.yml +8 -0
  32. package/fixtures/cases/05-waived/expected.json +1 -0
  33. package/fixtures/cases/05-waived/inputs/report.json +6 -0
  34. package/fixtures/cases/05-waived/quality/policy.yml +8 -0
  35. package/fixtures/cases/05-waived/quality/waivers.yml +8 -0
  36. package/fixtures/cases/06-expired-waiver/expected.json +1 -0
  37. package/fixtures/cases/06-expired-waiver/inputs/report.json +6 -0
  38. package/fixtures/cases/06-expired-waiver/quality/policy.yml +8 -0
  39. package/fixtures/cases/06-expired-waiver/quality/waivers.yml +8 -0
  40. package/fixtures/cases/07-baseline-pass/expected.json +1 -0
  41. package/fixtures/cases/07-baseline-pass/inputs/report.json +6 -0
  42. package/fixtures/cases/07-baseline-pass/quality/baseline.json +13 -0
  43. package/fixtures/cases/07-baseline-pass/quality/policy.yml +8 -0
  44. package/fixtures/cases/08-baseline-new/expected.json +1 -0
  45. package/fixtures/cases/08-baseline-new/inputs/report.json +7 -0
  46. package/fixtures/cases/08-baseline-new/quality/baseline.json +13 -0
  47. package/fixtures/cases/08-baseline-new/quality/policy.yml +8 -0
  48. package/fixtures/cases/09-forged-passed/expected.json +1 -0
  49. package/fixtures/cases/09-forged-passed/inputs/report.json +8 -0
  50. package/fixtures/cases/09-forged-passed/quality/policy.yml +8 -0
  51. package/fixtures/cases/10-malformed-finding/expected.json +1 -0
  52. package/fixtures/cases/10-malformed-finding/inputs/report.json +6 -0
  53. package/fixtures/cases/10-malformed-finding/quality/policy.yml +8 -0
  54. package/fixtures/cases/11-missing-tool/expected.json +1 -0
  55. package/fixtures/cases/11-missing-tool/quality/policy.yml +8 -0
  56. package/fixtures/cases/12-invalid-waiver/expected.json +1 -0
  57. package/fixtures/cases/12-invalid-waiver/inputs/report.json +6 -0
  58. package/fixtures/cases/12-invalid-waiver/quality/policy.yml +8 -0
  59. package/fixtures/cases/12-invalid-waiver/quality/waivers.yml +6 -0
  60. package/fixtures/cases/13-unknown-tool/expected.json +1 -0
  61. package/fixtures/cases/13-unknown-tool/quality/policy.yml +6 -0
  62. package/fixtures/cases/14-duplicate-check-id/expected.json +1 -0
  63. package/fixtures/cases/14-duplicate-check-id/quality/policy.yml +11 -0
  64. package/fixtures/cases/15-missing-project/expected.json +1 -0
  65. package/fixtures/cases/15-missing-project/quality/policy.yml +6 -0
  66. package/fixtures/cases/16-enforce-warnings/expected.json +1 -0
  67. package/fixtures/cases/16-enforce-warnings/inputs/report.json +6 -0
  68. package/fixtures/cases/16-enforce-warnings/quality/policy.yml +9 -0
  69. package/fixtures/cases/17-degraded-required-false/expected.json +1 -0
  70. package/fixtures/cases/17-degraded-required-false/quality/policy.yml +11 -0
  71. package/fixtures/cases/18-duplicate-occurrences/expected.json +1 -0
  72. package/fixtures/cases/18-duplicate-occurrences/inputs/report.json +7 -0
  73. package/fixtures/cases/18-duplicate-occurrences/quality/policy.yml +8 -0
  74. package/fixtures/cases/19-policy-json/expected.json +1 -0
  75. package/fixtures/cases/19-policy-json/inputs/report.json +6 -0
  76. package/fixtures/cases/19-policy-json/quality/policy.json +7 -0
  77. package/fixtures/cases/20-blocker-waiver/expected.json +1 -0
  78. package/fixtures/cases/20-blocker-waiver/inputs/report.json +6 -0
  79. package/fixtures/cases/20-blocker-waiver/quality/policy.yml +8 -0
  80. package/fixtures/cases/20-blocker-waiver/quality/waivers.yml +8 -0
  81. package/fixtures/cases/21-zero-base/expected.json +1 -0
  82. package/fixtures/cases/21-zero-base/inputs/report.json +6 -0
  83. package/fixtures/cases/21-zero-base/quality/policy.yml +8 -0
  84. package/fixtures/cases/22-hash-mismatch/expected.json +1 -0
  85. package/fixtures/cases/22-hash-mismatch/inputs/report.json +6 -0
  86. package/fixtures/cases/22-hash-mismatch/quality/policy.yml +8 -0
  87. package/fixtures/cases/23-release-floor/expected.json +1 -0
  88. package/fixtures/cases/23-release-floor/quality/policy.yml +9 -0
  89. package/fixtures/cases/24-fast-empty/expected.json +1 -0
  90. package/fixtures/cases/24-fast-empty/quality/policy.yml +8 -0
  91. package/fixtures/cases/25-cmd-json/expected.json +1 -0
  92. package/fixtures/cases/25-cmd-json/quality/policy.yml +8 -0
  93. package/fixtures/cases/26-junit/expected.json +1 -0
  94. package/fixtures/cases/26-junit/inputs/junit.xml +7 -0
  95. package/fixtures/cases/26-junit/quality/policy.yml +8 -0
  96. package/fixtures/cases/27-sarif/expected.json +1 -0
  97. package/fixtures/cases/27-sarif/inputs/report.sarif +18 -0
  98. package/fixtures/cases/27-sarif/quality/policy.yml +8 -0
  99. package/lib/adapters/cmd-json.js +59 -0
  100. package/lib/adapters/eslint.js +69 -0
  101. package/lib/adapters/java-xml.js +146 -0
  102. package/lib/adapters/junit.js +72 -0
  103. package/lib/adapters/raw-json.js +27 -0
  104. package/lib/adapters/sarif.js +57 -0
  105. package/lib/adapters/typecheck.js +37 -0
  106. package/lib/collect.js +87 -0
  107. package/lib/commit-message.js +14 -0
  108. package/lib/config.js +218 -0
  109. package/lib/cost.js +102 -0
  110. package/lib/evidence.js +156 -0
  111. package/lib/exec.js +69 -0
  112. package/lib/finding-input.js +36 -0
  113. package/lib/fingerprint.js +6 -0
  114. package/lib/fix.js +177 -0
  115. package/lib/gate.js +13 -0
  116. package/lib/inspect.js +142 -0
  117. package/lib/java-risk-policy.js +44 -0
  118. package/lib/java-source-map.js +36 -0
  119. package/lib/native-input.js +60 -0
  120. package/lib/promote.js +147 -0
  121. package/lib/report-input.js +24 -0
  122. package/lib/report-producers.js +61 -0
  123. package/lib/report.js +153 -0
  124. package/lib/risk.js +15 -0
  125. package/lib/run.js +409 -0
  126. package/lib/source-state.js +100 -0
  127. package/lib/util.js +36 -0
  128. package/lib/validate-report.js +72 -0
  129. package/lib/verify-artifact.js +24 -0
  130. package/lib/xml.js +77 -0
  131. package/lib/yaml-lite.js +121 -0
  132. package/package.json +48 -4
  133. package/schemas/quality-summary.schema.json +270 -0
  134. package/scripts/lib-tests/closure-regressions.test.mjs +188 -0
  135. package/scripts/lib-tests/collect.test.mjs +81 -0
  136. package/scripts/lib-tests/config.test.mjs +103 -0
  137. package/scripts/lib-tests/cost.test.mjs +94 -0
  138. package/scripts/lib-tests/fingerprint.test.mjs +30 -0
  139. package/scripts/lib-tests/hardening.test.mjs +390 -0
  140. package/scripts/lib-tests/inspect.test.mjs +152 -0
  141. package/scripts/lib-tests/java-triage.test.mjs +149 -0
  142. package/scripts/lib-tests/report-producers.test.mjs +90 -0
  143. package/scripts/lib-tests/shared-adapters.test.mjs +157 -0
  144. package/scripts/lib-tests/validate-report.test.mjs +37 -0
  145. package/scripts/lib-tests/yaml-lite.test.mjs +34 -0
  146. package/scripts/run-fixtures.mjs +156 -0
  147. package/scripts/run-integration.mjs +156 -0
  148. package/templates/hooks/commit-msg +11 -0
  149. package/templates/hooks/pre-commit +15 -0
  150. package/templates/hooks/pre-push +10 -0
  151. package/templates/quality/generated.gitignore +7 -0
  152. package/templates/quality/java-static-checks.yml +31 -0
  153. package/templates/quality/policy.yml +20 -0
  154. package/templates/quality/rules.lock.json +9 -0
  155. package/templates/quality/waivers.yml +8 -0
  156. package/templates/scripts-quality/run.mjs +13 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,111 @@
1
+ # 0.6.1 — 2026-10-12
2
+
3
+ - npm 发布访问策略改为 public;公开获取与使用授权分开,保留 UNLICENSED,不授予开源许可。
4
+ - 新增独立 LICENSE:权利人为 AGILE TEAM,未经事先书面授权不得使用、复制、修改或再分发;授权申请通过仓库 Issues,不伪造或推定授权记录。
5
+ - LICENSE 明确纳入 npm 包;README、当前手册与模板版本锁同步 0.6.1。保留 0.6.0 历史记录和标签,不修改业务解析、风险分级或门禁裁决代码。
6
+
7
+ # 0.6.0 — 2026-10-12
8
+
9
+ - 汇总 0.5.2 源码修订:共享工程包报告转换、Java XML 本轮一次生产、只读分页 inspect 与离线 cost;保留原 CLI/模块能力和平台权限边界。
10
+ - Java 报告补齐 P3C/原引擎身份、原生优先级与成员签名;成员指纹防止同一行、同一说明的不同字段被合并。
11
+ - Gate 单源风险表新增三项安全/正确性底线;locale 显式预警和 SpotBugs 四类上下文复核保留发现,不自动排除整类。
12
+ - 严格 sourceRoots 支持多模块 main/test 实际文件定位;缺文件、歧义、符号链接和越界失败关闭。报告单独保留 parser 版本,未知外部工具版本不虚报验证。
13
+ - 自测使用包内实际版本生成临时 rules.lock,避免发布升版后夹具仍锁旧版本。更新安装/迁移/版本冻结文档,去除 standalone npm 包中的工作区兄弟路径链接。
14
+ - 发布准备保留现有 restricted/UNLICENSED 设置;包级与实际 tgz 自测可证明本地能力,不替代 registry、Git、平台权限与生产接线回执。
15
+
16
+ # 0.5.2 — 2026-10-11~12(源码修订,未发布)
17
+
18
+ - cmd-json 集中转换 Kit/UI/BD 原生完整 JSON,校验执行与覆盖证据、保留等级与质量底线;项目不再各自维护同类转换。
19
+ - captureStdout 以文件描述符接收大报告,防止生产者立即退出导致管道 JSON 截断;仍拒绝旧文件、超限及无证据的非零退出。
20
+ - java-xml 解析 Checkstyle/PMD/SpotBugs 原 XML,区分确定缺陷和治理提示;分析异常、依赖缺类、缺报告及生产失败返回 invalid。
21
+ - Java XML 补齐显式 format=p3c、producer/原引擎版本/原生优先级类别rank/主类和方法字段签名;稳定成员context避免同一generic说明合并不同字段。本地parser版本单独归档,不假装外部工具已核验。
22
+ - Gate 单源 Java 风险表保留原四底线,增加 HardCodedCryptoKey/InsecureCryptoIv/BrokenNullCheck 不可基线error;locale显式预警,SpotBugs四类表征/构造风险保留context-review warning及理由,不自动识别@Service并豁免整类。
23
+ - 新增严格java-xml sourceRoots,按明确多模块main/test源目录探测真实文件;不存在、同名歧义、符号链接和越界失败关闭。旧合成parser夹具补真实源码文件,未降低运行时校验。
24
+ - 同步当前接入文档:三档成本、Java 静态/单元/集成隔离、历史治理与预警;既有业务项目接入未自动升级,平台通知仍待验收。
25
+ - Java XML 新增显式 producer 本轮一次生产:清除同组全部旧文件、完整报告检查与消费前哈希核验;下一轮重新生产,失败/缺报告不重试伪装成功。
26
+ - 新增离线 cost:工件去重、项目/档位和检查分位耗时、生产次数及候选重复;可导入来源标明的宿主 usage,缺失不填零,缓存输入不重复计数。
27
+ - 阻止 displayOnly、裁剪和未完成分页结果回流为门禁输入。
28
+ - 增加只读 inspect:默认摘要、规则/等级/状态过滤、UTF-8 字节预算与绑定报告/筛选的分页游标;翻页只读工件,不重复扫描,过滤不改变原结论和全量计数。
29
+ - 明确确定性脚本、AI 上下文与 Runner 成本分别统计;完整报告留存、按需读取,重复检查/告警由共享接线收敛,不削减 release 必测。增加 7 项视图预算、分页、防漂移、只读与 CLI 回归验证。
30
+
31
+ # 0.5.1 — 2026-10-09
32
+
33
+ - ESLint 全量显式 targets/args;保存实际覆盖清单,拒绝空扫描、全忽略与增量漏文件,兼容中文/空格及物理路径别名;检查参数禁止修复模式。
34
+ - release 参数及证明核验均要求全量;已跟踪源码不因生成目录/报告排除而消失,报告目录防覆盖,路径与特殊文件失败关闭。
35
+ - 保留适配器执行失败的原诊断;受控检查提前要求基线/豁免批准摘要,promote 全链传递;依赖准备留脱敏诊断。
36
+ - 晋级以明确旧 HEAD 的 lease 拒绝最后时刻目标并发;仍核验候选父提交并只推进批准的快进。
37
+ - 修复符号链接/HEAD 漂移失败关闭;安全恢复后重新扫描并清空尝试 fixed 结果,不完整恢复要求人工处理。
38
+ - 93 单元 + 29 fixture + 17 集成通过;文档细化覆盖、漏跑、乱序/复发、恢复授权和修复发布准入。未发布 npm,真实平台接线仍待验收。
39
+
40
+ # 0.5.0 — 2026-10-08
41
+
42
+ - 将 unsigned 检查结果与独立 Ed25519 签发分离;强制核验项目、真实 SHA、档位、环境、批准摘要、公钥、签发身份、作业和时效。
43
+ - 新增 manifest,发布证明绑定真实制品/配置摘要;部署验证读取实际字节。
44
+ - promote 只生成候选和 bundle;promote-apply 以新 bare 仓库核验签名、非作者 Review、父提交与最新远端 HEAD 后更新。禁用旧 --push/--allow-any,失败也归档。
45
+ - 质量底线不可基线/豁免;合并取最高风险,基线风险/发生数增长重新阻断,增量不标历史问题消失,拒绝自批例外。
46
+ - JUnit 核验完整 XML、实际计数和必测,跳过/零用例阻断;清旧报告、修正非零退出契约,暂存/工作区不一致拒绝。
47
+ - 修复计划 v2 绑定文件/报告/策略/HEAD,忽略报告任意命令,白名单外变更/命令失败/回归回滚,扫描通过仅进入待复验。
48
+ - 新增 Code Quality 导出、共用 commit-message 验证;docs 随包发布,自测在临时目录执行,清理 fixture 生成报告。
49
+ - 同步真实权限边界和 0.4 迁移说明;D1~D4 明确首期,平台查询、通知和双仓组合仍保留待接入。
50
+
51
+ # Changelog
52
+
53
+ ## 0.4.0(2026-10-08)
54
+
55
+ 代码/逻辑/验证/报告四层全面闭环(平台接线与自动扫描脚本除外):
56
+ - 新适配器:`cmd-json`(任意命令输出统一 findings,工程包/扫描器通用入口)、`junit`(失败/错误=error、跳过=warning、零用例=error,正文 3.5/5.6)、`sarif`(SARIF v2.x 映射);共用 finding-input 解析,无胶水重复。
57
+ - eslint:`--base/--head` 增量(git diff 范围过滤)、`cache: true` 缓存参数;`command` 覆盖与 staged/增量范围同时出现直接 invalid。
58
+ - `verify-artifact` 子命令(正文 13.3 C3):证明结论/SHA/受控哈希/reportDigest 防篡改/eventId 关联/时效/报告内阻断问题复核,任一不符 exit 2。
59
+ - `promote` 子命令(正文 5.2/13.2 B4 引擎件):流向校验(dev-工号-模组→dev、相邻顺序晋级,跨级须 --allow-any 留痕)、临时 worktree 候选合并、候选上重跑门禁、目标 HEAD 并发复核、受控 push(非 force)、promotion-record 与候选证据归档。
60
+ - 报告新增自包含 `quality-report.html`(与 Markdown 同口径)。
61
+ - 验证闭环:新增 5 组 lib 单元测试(node:test);`fix --apply` 集成用例(修复→复扫→closed→beforeArchive/changedFiles);本地 bare 仓库集成测试 4 场景(晋级推进/违规拦截/流向拒绝/证据核对);新适配器 fixture 25~27;self-test 全量 29 项。
62
+ - 修复:run.js 适配器注册表补齐三个新工具(缺失时误报 invalid)。
63
+
64
+ ## 0.3.1(2026-10-07)
65
+
66
+ 二次深挖闭环(回归缺陷 + 零消费者断点):
67
+ - **修复 0.3.0 引入的回归**:模板 policy 移出 fast 档类型检查后 fast 变空 → pre-commit 必挂。模板/示例将 FE-ESLINT 恢复进 fast(配合 `--scope staged` 只查暂存区);新增 fixture 24-fast-empty 固化"空档位即 invalid"语义(已实测复现回归并验证修复)。
68
+ - **schema 从摆设变契约**:新增 `validate-report` 子命令按 schema 校验报告结构;self-test driver 对每个 case 的 summary 执行 schema 校验,报告与 schema 漂移从此有回归(实测通过/篡改两路径)。
69
+ - attestation 补 `eventId`(成功与 failFast 两路径),证明与报告可关联。
70
+ - hooks 模板:缺少 `scripts/quality/run.mjs` 薄入口时给出复制指引并 exit 2(原为晦涩的 cannot open)。
71
+ - `--scope` 白名单校验(仅 staged),任意值不再静默无效。
72
+ - `enforceWarnings` 布尔强转,json 变体传字符串 "false" 不再被当 true。
73
+ - self-test 25 项全绿。
74
+
75
+ ## 0.3.0(2026-10-07)
76
+
77
+ 对照落地执行文档逐章核对后的冲突修复(A/B/C/D 四类 14 项,均附 fixture 或实证):
78
+ - blocker 豁免默认拒绝(`blockerWaivers: reject`,正文 3.4):命中有效豁免仍阻断并标记 `waiverRejected`(fixture 20)。
79
+ - `required: false` 必须登记 `naReason`(可选 `alternative`),缺失即 policy invalid(正文 3.6,fixture 17 强化)。
80
+ - 模板 policy:类型检查移出 fast 档(10s 预算,附录 B.2 #3);新增 `commitTypes`、`releaseRequires`、`blockerWaivers` 键。
81
+ - 全零 `--base` SHA 拒绝(正文 13.2,fixture 21)。
82
+ - commit-msg 钩子类型白名单改读 policy `commitTypes`,缺省回落内置表(收敛"第四套白名单"口径分裂,已实测 wip/feat 两路径)。
83
+ - 新增 `--expect-policy-hash` / `--expect-rules-hash` 运行期摘要校验,不符即 invalid;docs/10 声明的"版本锁第三处咬合"落地(fixture 22 + 实测匹配/不匹配两路径)。
84
+ - release 档最低检查集 `releaseRequires`,缺任一即 invalid(正文 3.1"不能关闭的条件",fixture 23 + 满足路径实测)。
85
+ - attestation 字段改名 `compareBaseSha/compareHeadSha`(消除与晋级语义的混淆),新增 expected/match 字段。
86
+ - baseline 元数据(`generatedAt/ruleSetVersion/fileDigest`)进报告(正文 3.3)。
87
+ - fix-report 新增 `changedFiles`(git status,排除门禁产物目录;已实测);快照/自动回滚登记层级Ⅱ。
88
+ - summary 新增 `run.eventId`(正文 7.2 幂等入库键);finding 新增可选 `suggestion`(正文 4.2 修复建议,已实测进 markdown)。
89
+ - 信任边界写入 docs/06/10:策略作者可执行任意命令,Runner 不得持特权凭据(正文 12.6)。
90
+ - self-test 24 项全绿;driver 支持 per-case `profile/args`。
91
+
92
+ ## 0.2.0(2026-10-07)
93
+
94
+ 自审修复(缺陷均有实证复现):
95
+ - 指纹合并不再吞位置:同指纹多处命中保留 `occurrences` 行号清单,报告展示"行 10/20,共 2 处";eslint 指纹改由 规则+文件+消息 决定。
96
+ - yaml-lite:重复键直接报错(防配置静默漂移);未加引号值支持行内 `#` 注释截断,`src/#x.vue` 类值不受影响。
97
+ - 豁免记录按索引判定有效性,同 id(或同缺 id)记录不再连坐。
98
+ - eslint 适配器:配置 `command` 覆盖时禁止 `--scope staged`(语义不明确即 invalid)。
99
+ - fix --apply:复扫前把修复前 summary 归档为 `repair/summary-before-<planId>.json`;fix-report 新增 `failedCommands`。
100
+ - 薄入口模板:包未安装时给出安装指引并 exit 2(原为原始 MODULE_NOT_FOUND、exit 1)。
101
+ - self-test driver:FAIL 输出 stderr 尾部;支持 `occurrencesAtLeast` 断言;新增 fixture 13~19(未知工具/重复检查 id/缺 project/enforceWarnings/required=false 降级/多处命中/JSON 配置变体),共 20 项;fixture 产物加入 .gitignore。
102
+
103
+ ## 0.1.0(2026-10-07)
104
+
105
+ - 首个骨架版本:run/collect/gate/fix 统一入口,三档 profile,统一退出码 0/1/2。
106
+ - 失败关闭:配置缺失、工具缺失、报告损坏/缺失/结构错误、finding 缺字段、超时均判 invalid。
107
+ - 稳定指纹(项目+规则+文件+结构特征)、有期限豁免、存量基线、到期回退、基线待复验移除。
108
+ - 伪造结论防护:输入结果的 decision/status/passed 字段一律忽略,按 findings 重算。
109
+ - 统一报告:quality-summary.json(schemaVersion=1 最小集)、quality-report.md、gate-attestation.json。
110
+ - 13 个 fixture 自测(self-test)覆盖通过/阻断/invalid/豁免/基线/伪造/修复计划。
111
+ - 模板:quality/ 配置、scripts/quality/run.mjs 薄入口、hooks(pre-commit/commit-msg/pre-push,含耗时打点)。
package/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ 授权声明 / Authorization Notice
2
+
3
+ Copyright (c) 2026 AGILE TEAM. All rights reserved.
4
+ 权利人:AGILE TEAM。保留所有权利。
5
+
6
+ 本软件及其随附的源码、文档、模板和其他资料采用 UNLICENSED 授权状态。
7
+ 未经权利人 AGILE TEAM 事先书面授权,不得使用、复制、修改或再分发本软件及其随附资料。
8
+ 公开仓库、公开 npm 包或其他公开获取渠道不授予使用、复制、修改或再分发授权。
9
+ 取得书面授权后,仅可在该书面授权明确约定的范围内使用;公开获取不构成授权记录。
10
+
11
+ 如需申请授权,请通过仓库 Issues 联系权利人:
12
+ https://github.com/ChenyCHENYU/wl-skills-gate/issues
13
+
14
+ This software and its accompanying source code, documentation, templates and other materials are UNLICENSED.
15
+ Prior written authorization from the rights holder, AGILE TEAM, is required to use, copy, modify or redistribute them.
16
+ Availability through a public repository, public npm package or any other public channel does not grant such authorization.
17
+ Any authorized use is limited to the scope expressly stated in that prior written authorization.
18
+ To request authorization, contact the rights holder through the repository Issues linked above.
package/README.md CHANGED
@@ -1,3 +1,79 @@
1
- # Temporary Holding Version
1
+ # wl-skills-gate
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 统一质量门禁引擎,当前版本 **0.6.1**。对应《工程化与代码质量管控体系-落地执行(V3)》V3.2 主体、V3.3 附录 B/C 及质量成本与隔离增补;包配置为 npm 公开发布;公开获取不授予使用授权,使用前须取得 AGILE TEAM 事先书面授权,详见 [LICENSE](LICENSE)。发布流程核验版本与工件摘要。
4
+
5
+ 包集中维护检查执行、结果归一、门禁裁决、修复计划和证据验证。项目引用固定版本;平台分别配置检查、Review、证明签发、分支更新、部署、台账与通知的权限。代码自测通过不代表内网接线已验收。
6
+
7
+ 完整手册见 [docs/README.md](docs/README.md),当前接入建议见 [质量分级、执行成本与测试隔离](docs/13-quality-cost-and-isolation.md) 和 [低 token 执行](docs/14-low-token-execution.md)。11/12 手册保留为历史验证记录,既有 test-qa 接入尚未自动升级。
8
+
9
+ 日常目标是提前发现缺陷并减少返工:静态检查不修改业务源码,自动修复需独立明确执行;构建和测试在隔离 Runner 运行。新增缺陷与质量底线阻断,历史问题按审批治理,警告保留责任与时限。源码未变不代表没有外部系统副作用。
10
+
11
+ 共享 `cmd-json` 已支持 Kit/UI/BD 原生 JSON 转换及文件描述符接收大报告;`java-xml` 统一解析 Checkstyle、PMD、P3C、SpotBugs,保留真实原生身份并由 Gate 单源风险表分级。七项 PMD 安全/正确性底线不可基线化;SpotBugs 表征泄露与构造期异常保留为需上下文复核的 warning,不自动豁免整类。多模块源定位须声明项目内 `sourceRoots`,缺失/歧义/符号链接拒绝,不能任取同名文件。详见 [Java 原生报告分级与复核](docs/15-java-report-triage.md)。项目无需各自维护这些解析脚本。类型、关键测试、权限/数据底线仍不能放进历史基线。
12
+
13
+ 质量扫描默认由确定性脚本执行,包内没有大模型 API 调用。Hook、CI 裁决、报告归档和通知模板无需 AI;策略中的自定义命令仍须由平台审查。AI 阅读命令输出、规则和源码会消耗上下文,不能将“脚本没有调用模型”说成整个 AI 会话零 token。完整报告留作工件,AI 先用 `inspect` 看摘要,再按需分页读取问题;不因阅读分页重跑扫描。Java XML 可显式共用本轮一次生产;离线 cost 从既有工件汇总耗时,宿主实际 usage 不可获取时保持未知。Kit/UI 的 resultCursor 源码优化同步完成,业务安装态尚未升级。
14
+
15
+ ## 能力与边界
16
+
17
+ | 能力 | 命令 | 当前边界 |
18
+ |---|---|---|
19
+ | 三档检查 | `run --profile fast/standard/release` | 退出码 0/1/2;失败也归档;release 要求真实干净 HEAD、合法环境、最低检查集与实际工具版本锁 |
20
+ | 报告 | `validate-report` | JSON/Markdown/HTML;Code Quality JSON 自动导出;JUnit 保留真实生产者 XML,不能拿转换后的计数替代原始测试报告 |
21
+ | 精简查询 | `inspect <quality-summary.json>` | 默认摘要;按规则/等级/状态分页;默认 12,000 UTF-8 字节上限;只读现有报告,保留原判定与全量计数,不能替代可信门禁证据 |
22
+ | 成本统计 | `cost <artifact-dir> --out <metrics.json>` | 离线汇总各档/各检查 P50/P95、共享生产次数、候选重复;真实 usage 可选导入,缺失保持未知,不调用模型 |
23
+ | 机械修复 | `fix --plan`、`fix --apply --confirm` | standard 全量报告;文件快照、白名单、防漂移、异常回滚、强制复扫;只进入待复验,不能自动关闭业务问题 |
24
+ | 候选检查 | `promote` | 在无特权 Runner 中合并、准备依赖、检查并归档 bundle;不更新共享分支 |
25
+ | 受控晋级 | `promote-apply` | 核验签名证明和独立 Review,在新 bare 仓库中核验最新远端 HEAD 后核验父提交后以批准的旧 HEAD 条件更新;不运行项目脚本或 Hook |
26
+ | 制品清单 | `manifest` | 绑定真实 HEAD、环境、实际文件/目录及可选配置、数据脚本摘要 |
27
+ | 证明签发 | `attest` | 受控独立作业签发 Ed25519 证明;仅签通过的 standard/release 证据 |
28
+ | Review 签发 | `review-sign` | 受控 Reviewer 作业,身份取自平台认证;非作者、候选绑定、阻断问题已处置 |
29
+ | 部署验证 | `verify-artifact` | 固定公钥、签发者和作业 ID、SHA、release 档、环境、策略/基线/例外、时效、真实制品摘要全部核验 |
30
+ | 自测 | `self-test`、`npm test` | self-test 只跑 29 个 fixture;npm test 额外跑单元与本地 Git/签名/制品集成 |
31
+
32
+ ## 先只读试运行
33
+
34
+ 取得事先书面授权并按授权范围使用后,可从公共 npm registry 安装固定版本:
35
+
36
+ ```bash
37
+ # 公开获取不构成使用授权;先申请授权,再按其约定使用。
38
+ pnpm add -D --save-exact @agile-team/wl-skills-gate@0.6.1
39
+ # 按真实技术栈配置 quality/policy.yml 和 rules.lock.json;工具需实际安装。
40
+ pnpm exec wl-skills-gate run --profile standard
41
+ pnpm exec wl-skills-gate validate-report quality-reports/quality-summary.json
42
+ # 给人工/AI 查看现有报告,不再执行扫描;完整报告仍独立归档。
43
+ pnpm exec wl-skills-gate inspect quality-reports/quality-summary.json
44
+ pnpm exec wl-skills-gate inspect quality-reports/quality-summary.json --view findings --status new --limit 10
45
+ ```
46
+
47
+ Gate 自身零依赖。前端仍需 ESLint/TypeScript 和工程检查器;后端 Runner 仍需 JDK/Maven/Node 与实际质量 profile。只有已安装且适用的规则包进入版本锁,不安装无关兄弟包。
48
+
49
+ 退出码:0=passed;1=blocked;2=invalid。1/2 都停止后续晋级和部署,报告用 `when: always` 归档。无效证据不能当成“没有问题”。
50
+
51
+ ## 0.5.x → 0.6 迁移
52
+
53
+ - 保留原 CLI、主模块和直接 lib 路径;Gate 仍可独立安装,扫描器和规则包按真实技术栈分别配置。
54
+ - P3C 使用 `format: p3c`;XML 的 PMD 引擎版本与 P3C 提供者版本分开记录,未提供的版本保持未知。
55
+ - SpotBugs 多模块配置显式 `sourceRoots`。未配置只兼容实际存在的单模块 `src/main/java`;缺失、歧义和符号链接定位返回 invalid。
56
+ - 成员身份进入 Java 指纹,旧成员基线可能需要重新审查。七项 PMD 底线不可基线/豁免;SpotBugs 四类上下文复核 warning 保留发现,不自动排除。
57
+ - `rules.lock.json` 的 Gate 版本须与实际安装版本一致;模板中的检查器版本占位值仍需替换为真实版本。
58
+
59
+ ## 打包与版本冻结
60
+
61
+ 使用 Node ≥20 执行 `npm test` 后运行 `npm pack --ignore-scripts --json --pack-destination <工件目录>`;对这个 tgz 独立安装、调用 CLI/自测并校验哈希,再提交和发布同一产物。`files` 包含所有运行模块、schema、模板、手册、fixture 与自测脚本,不依赖兄弟包源码。
62
+
63
+ npm 发布访问策略为 `public`,授权状态仍为 `UNLICENSED`。未经 AGILE TEAM 事先书面授权,不得使用、复制、修改或再分发;公开获取不等于使用授权。授权申请通过 [仓库 Issues](https://github.com/ChenyCHENYU/wl-skills-gate/issues),完整条款见 [LICENSE](LICENSE)。源仓库为 [ChenyCHENYU/wl-skills-gate](https://github.com/ChenyCHENYU/wl-skills-gate),默认分支 `main`;仓库推送与 npm 发布由发布流程分别核验。平台接线、真实权限、部署和群通知仍按下文单独验收。
64
+
65
+ ## 0.4 → 0.5 迁移
66
+
67
+ - `promote --push`、`--allow-any` 禁用,更新分支改走隔离的 `promote-apply`。
68
+ - `run` 生成的是 `unsigned-gate-result`;部署拒收 unsigned。签发与检查使用不同凭据和执行环境。
69
+ - `verify-artifact` 必须提供固定公钥、受控期望、环境、清单与实际制品根目录,详见 [Jenkins](docs/07-jenkins.md)。
70
+ - `cmd-json` 默认成功退出码为 `[0]`;工具“发现问题返回 1”需显式登记 `[0,1]`,且报告有相应问题证据。
71
+ - 基线项增加 `severity`、`occurrenceCount`。blocker、类型/关键测试等质量底线不能基线化或豁免。
72
+ - 旧修复计划需重新生成。报告中的任意 `fixCommand` 不再执行;仅使用内置 ESLint 修复或政策登记的 fixer。
73
+ - 必需测试跳过、零用例、计数与明细不一致会阻断。部分暂存存在未暂存修改时,快检拒绝。
74
+
75
+ ## 平台待接入能力
76
+
77
+ 受保护分支、固定 CI/Runner、签发密钥与授权、平台作业来源查询、独立 Review、双仓发布组合、真实业务/UI/性能验证、台账与飞书/企业微信通知均需平台接线和实测。本包不自动创建这些权限或服务。
78
+
79
+ D1~D4(台账、责任路由、通知、SLA 升级)属于首期,与前后端样板同步验收。先证明一个真实问题从发现到复验关闭,再推广项目数量。
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { mkdirSync, writeFileSync } from "node:fs";
4
+ import { runGate } from "../lib/run.js";
5
+ import { buildFixPlan, applyFixPlan } from "../lib/fix.js";
6
+ import { verifyArtifact } from "../lib/verify-artifact.js";
7
+ import { promote, applyPromotion } from "../lib/promote.js";
8
+ import { signGateProof, createArtifactManifest, signReviewRecord } from "../lib/evidence.js";
9
+ import { validateCommitMessage } from "../lib/commit-message.js";
10
+
11
+ const [command, ...rest] = process.argv.slice(2);
12
+ const FLAGS = new Set(["--confirm", "--push", "--allow-any"]);
13
+
14
+ function options(allowed) {
15
+ const out = { positional: [] };
16
+ for (let i = 0; i < rest.length; i++) {
17
+ const name = rest[i];
18
+ if (!name.startsWith("--")) { out.positional.push(name); continue; }
19
+ if (!allowed.includes(name) || Object.hasOwn(out, name)) throw new Error(`未知或重复参数: ${name}`);
20
+ if (FLAGS.has(name)) { out[name] = true; continue; }
21
+ const value = rest[++i];
22
+ if (!value || value.startsWith("--")) throw new Error(`参数 ${name} 缺少值`);
23
+ out[name] = value;
24
+ }
25
+ if (out.positional.length > 1) throw new Error("位置参数过多");
26
+ return out;
27
+ }
28
+
29
+ function writeJson(file, value) {
30
+ if (!file) throw new Error("缺少 --out");
31
+ mkdirSync(path.dirname(path.resolve(file)), { recursive: true });
32
+ writeFileSync(file, JSON.stringify(value, null, 2));
33
+ console.log(`[${command}] 已写入 ${file}`);
34
+ }
35
+
36
+ const PROOF_FLAGS = ["--summary", "--sha", "--project", "--env", "--expect-policy-hash", "--expect-rules-hash",
37
+ "--expect-baseline-hash", "--expect-waivers-hash",
38
+ "--expect-pipeline-id", "--expect-job-id",
39
+ "--public-key", "--expect-issuer", "--max-age-minutes", "--manifest", "--artifact-root"];
40
+
41
+ function proofOptions(o) {
42
+ return { attestationFile: o.positional[0], summaryFile: o["--summary"], sha: o["--sha"], project: o["--project"],
43
+ env: o["--env"], expectPolicyHash: o["--expect-policy-hash"], expectRulesHash: o["--expect-rules-hash"],
44
+ expectBaselineHash: o["--expect-baseline-hash"], expectWaiversHash: o["--expect-waivers-hash"],
45
+ expectPipelineId: o["--expect-pipeline-id"], expectJobId: o["--expect-job-id"],
46
+ publicKeyFile: o["--public-key"], expectIssuer: o["--expect-issuer"],
47
+ maxAgeMinutes: o["--max-age-minutes"] === undefined ? 1440 : Number(o["--max-age-minutes"]),
48
+ manifestFile: o["--manifest"], artifactRoot: o["--artifact-root"] };
49
+ }
50
+
51
+ try {
52
+ if (command === "run") {
53
+ process.exitCode = (await runGate({ argv: rest })).exitCode;
54
+ } else if (command === "cost") {
55
+ const o = options(["--usage", "--out"]);
56
+ if (!o.positional[0] || !o["--out"]) throw new Error("cost 必须提供工件目录和 --out;不默认将完整统计塞入控制台");
57
+ if (path.basename(o["--out"]) === "quality-summary.json" || (o["--usage"] && path.resolve(o["--out"]) === path.resolve(o["--usage"]))) throw new Error("成本输出不能覆盖 quality-summary 或 usage 输入");
58
+ const { collectCost } = await import("../lib/cost.js");
59
+ writeJson(o["--out"], collectCost(o.positional[0], { usageFile: o["--usage"] }));
60
+ } else if (command === "inspect") {
61
+ const o = options(["--view", "--status", "--severity", "--rule", "--limit", "--max-bytes", "--cursor"]);
62
+ const { inspectReport } = await import("../lib/inspect.js");
63
+ const r = inspectReport(o.positional[0], { view: o["--view"], status: o["--status"], severity: o["--severity"],
64
+ rule: o["--rule"], limit: o["--limit"], maxBytes: o["--max-bytes"], cursor: o["--cursor"] });
65
+ process.stdout.write(r.text);
66
+ process.exitCode = r.exitCode;
67
+ } else if (command === "fix") {
68
+ const o = options(["--plan", "--apply", "--root", "--profile", "--confirm"]);
69
+ if (!!o["--plan"] === !!o["--apply"]) throw new Error("fix 必须且只能选择 --plan 或 --apply");
70
+ if (o["--plan"]) console.log(`[fix] 计划: ${buildFixPlan(o["--plan"], { root: o["--root"] }).planFile}`);
71
+ else {
72
+ const r = await applyFixPlan(o["--apply"], { root: o["--root"], profile: o["--profile"], confirm: !!o["--confirm"] });
73
+ console.log(`[fix] scanResolved=${r.fixReport.scanResolved} rolledBack=${r.fixReport.rolledBack} nextState=${r.fixReport.nextState} 报告=${r.reportFile}`);
74
+ process.exitCode = r.exitCode;
75
+ }
76
+ } else if (command === "validate-report") {
77
+ const o = options([]);
78
+ const { validateReportFile } = await import("../lib/validate-report.js");
79
+ const r = validateReportFile(o.positional[0]);
80
+ if (!r.ok) throw new Error(r.errors.join("; "));
81
+ console.log("[validate-report] 通过");
82
+ } else if (command === "commit-message") {
83
+ const o = options(["--root"]);
84
+ const r = validateCommitMessage(o.positional[0], path.resolve(o["--root"] || "."));
85
+ if (!r.ok) { console.error(`[commit-message] ${r.reason}`); process.exitCode = 1; }
86
+ } else if (command === "manifest") {
87
+ const o = options(["--root", "--project", "--env", "--artifacts", "--configs", "--out"]);
88
+ createArtifactManifest({ root: path.resolve(o["--root"] || "."), project: o["--project"], env: o["--env"],
89
+ artifacts: o["--artifacts"]?.split(","), configFiles: o["--configs"]?.split(",") || [], outFile: o["--out"] });
90
+ console.log(`[manifest] ${o["--out"]}`);
91
+ } else if (command === "attest") {
92
+ const o = options([...PROOF_FLAGS, "--profile", "--private-key", "--issuer", "--pipeline-id", "--job-id", "--promotion-record", "--out"]);
93
+ writeJson(o["--out"], signGateProof({ ...proofOptions(o), summaryFile: o["--summary"] || o.positional[0],
94
+ profile: o["--profile"], privateKeyFile: o["--private-key"], issuer: o["--issuer"],
95
+ pipelineId: o["--pipeline-id"], jobId: o["--job-id"], promotionRecordFile: o["--promotion-record"] }));
96
+ } else if (command === "review-sign") {
97
+ const o = options(["--private-key", "--reviewer-id", "--author-id", "--project", "--sha", "--out"]);
98
+ writeJson(o["--out"], signReviewRecord({ recordFile: o.positional[0], privateKeyFile: o["--private-key"],
99
+ reviewerId: o["--reviewer-id"], authorId: o["--author-id"], project: o["--project"], sha: o["--sha"] }));
100
+ } else if (command === "verify-artifact") {
101
+ const r = verifyArtifact(proofOptions(options(PROOF_FLAGS)));
102
+ if (!r.ok) throw new Error(r.errors.join("; "));
103
+ console.log("[verify-artifact] 签名、质量证据及实际制品通过");
104
+ } else if (command === "promote") {
105
+ const o = options(["--root", "--source", "--target", "--remote", "--profile", "--push", "--allow-any", "--expect-policy-hash", "--expect-rules-hash", "--expect-baseline-hash", "--expect-waivers-hash", "--prepare-command"]);
106
+ const r = await promote({ root: o["--root"] || ".", source: o["--source"], target: o["--target"], remote: o["--remote"],
107
+ profile: o["--profile"], push: !!o["--push"], allowAny: !!o["--allow-any"],
108
+ expectPolicyHash: o["--expect-policy-hash"], expectRulesHash: o["--expect-rules-hash"],
109
+ expectBaselineHash: o["--expect-baseline-hash"], expectWaiversHash: o["--expect-waivers-hash"], prepareCommand: o["--prepare-command"] });
110
+ console.log(`[promote] ${r.status} record=${r.recordFile}`);
111
+ for (const reason of r.reasons) console.error(reason);
112
+ process.exitCode = r.exitCode;
113
+ } else if (command === "promote-apply") {
114
+ const o = options([...PROOF_FLAGS, "--record", "--remote-url", "--bundle", "--review", "--review-public-key", "--out"]);
115
+ const r = applyPromotion({ ...proofOptions(o), recordFile: o["--record"], remoteUrl: o["--remote-url"],
116
+ bundleFile: o["--bundle"], reviewFile: o["--review"], reviewPublicKeyFile: o["--review-public-key"], outDir: o["--out"] });
117
+ console.log(`[promote-apply] ${r.status}`);
118
+ for (const reason of r.reasons) console.error(reason);
119
+ process.exitCode = r.exitCode;
120
+ } else if (command === "self-test") {
121
+ if (rest.length) throw new Error("self-test 不接受额外参数");
122
+ const { runAllFixtures } = await import("../scripts/run-fixtures.mjs");
123
+ process.exitCode = await runAllFixtures();
124
+ } else throw new Error("用法: wl-skills-gate <run|inspect|cost|fix|validate-report|commit-message|manifest|attest|review-sign|verify-artifact|promote|promote-apply|self-test>,详见 docs/README.md");
125
+ } catch (e) { console.error(`[${command || "wl-skills-gate"}] ${e.message}`); process.exitCode = 2; }
@@ -0,0 +1,44 @@
1
+ # 01 快速开始
2
+
3
+ 对应 3.1/4.1、A1/A3。需要 Node ≥20,业务检查器与项目依赖需另行准备。
4
+
5
+ ## 包仓库自测
6
+
7
+ ```bash
8
+ npm test
9
+ node bin/wl-skills-gate.js self-test
10
+ ```
11
+
12
+ 自测使用临时项目/临时 Git 仓库,退出后清理,fixture 源目录不写报告。self-test 只覆盖 fixture;完整回归用 npm test。
13
+
14
+ ## 业务项目接入
15
+
16
+ 使用前须取得 AGILE TEAM 事先书面授权,并遵循该授权约定范围;公开 npm 包可下载不构成使用许可。详见 [LICENSE](../LICENSE),申请通过 [仓库 Issues](https://github.com/ChenyCHENYU/wl-skills-gate/issues)。
17
+
18
+ ```bash
19
+ # 取得书面授权后,从公共 npm registry 安装固定版本。
20
+ pnpm add -D --save-exact @agile-team/wl-skills-gate@0.6.1
21
+ mkdir -p quality
22
+ # 从安装包 templates/quality 取模板,按真实检查器和工具版本修改。
23
+ pnpm exec wl-skills-gate run --profile standard
24
+ ```
25
+
26
+ 首次只读试运行先确认真实工具被调用、发现一条预设错误、报告定位正确、退出码非零;随后集成现有 Hook,禁止覆盖原有检查。
27
+
28
+ ## 三档
29
+
30
+ | 档位 | 位置 | 约束 |
31
+ |---|---|---|
32
+ | fast | 本地提交反馈 | staged 与 worktree 有差异时拒绝;暂存文件列表不代表所有适配器都支持增量 |
33
+ | standard | 推送前、个人分支 CI、候选检查 | 完整适用检查;受控作业显式提供真实 --sha |
34
+ | release | 构建发布验证 | Git HEAD 确定且源码干净、合法 --env、releaseRequires、实际 tools 版本锁;完整范围 |
35
+
36
+ `--base/--head` 必须成对、非空;head 必须是 checkout 的完整 HEAD SHA。当前只有 ESLint 根据范围选文件,其他适配器按各自政策执行,不能宣称统一增量。增量基线未知时采用批准的全量策略或判 invalid。
37
+
38
+ 可选中央摘要参数:`--expect-policy-hash`、`--expect-rules-hash`、`--expect-baseline-hash`、`--expect-waivers-hash`。这些值由批准登记表提供,不能对候选内的任意文件临时算一个值就称“已批准”。
39
+
40
+ `--sha` 不能用来替报告贴标签;它必须与真实 HEAD 相同。release 的 `--env` 支持 dev/sit/uat/pre/prd,源代码不能在检查期间变化。
41
+
42
+ 0=passed,1=blocked,2=invalid;1/2 都阻断。归档整个 quality-reports,失败也归档。
43
+
44
+ 受控 --sha / release 的全部必需检查还须实际查询工具版本并匹配 rules.lock.tools;仅 JSON 自报版本不能签发正式证明。ESLint/typecheck 可查询对应 bin --version,其他 adapter 需登记受控 versionCommand。
@@ -0,0 +1,88 @@
1
+ # 02 策略、版本、基线和例外
2
+
3
+ 对应 3.3/3.4/3.6、A1。项目 quality/ 支持 policy.yml/.yaml/.json、waivers.yml/.yaml/.json、baseline.json、rules.lock.json。
4
+
5
+ 0.6.1:`cmd-json.format` 可选 wl-kit/wl-ui/wl-bd,`captureStdout: true` 必须同时声明 file;`java-xml` 必填 format=checkstyle/pmd/p3c/spotbugs 与 file,可用相同 producer/command/timeoutMs 在本轮共用一次生产,见 [检查片段](../templates/quality/java-static-checks.yml) 和 [14 执行约束](14-low-token-execution.md)。仅 java-xml 允许 `sourceRoots`,须为无重复、实际存在且没有符号链接的项目内相对目录数组;SpotBugs 未配置时只兼容 `src/main/java`,不搜索整个仓库。详见 [Java 分级与定位](15-java-report-triage.md)。完整配置示例、风险分级和测试拆分见 [13 质量成本与隔离](13-quality-cost-and-isolation.md)。`enforceWarnings: false` 只控制 Gate 警告,不改变原生 Maven 插件的失败行为。
6
+
7
+ policy 必需。没有 baseline/waivers 表示没有登记任何基线/例外,摘要为 `sha256("absent")`;存在的文件损坏时失败关闭。受控 `--sha`、release 和正式签发时,有文件就必须提供批准的 `--expect-baseline-hash` / `--expect-waivers-hash`,空文件也一样;本地非受控诊断可不传。
8
+
9
+ ## policy
10
+
11
+ ```yaml
12
+ schemaVersion: 1
13
+ project: wl-ui-produce
14
+ enforceWarnings: false
15
+ blockerWaivers: reject
16
+ releaseRequires: [TS-TYPECHECK, FE-ESLINT]
17
+ checks:
18
+ - id: TS-TYPECHECK
19
+ tool: typecheck
20
+ bin: node_modules/.bin/vue-tsc
21
+ args: --noEmit
22
+ versionCommand: node_modules/.bin/vue-tsc --version
23
+ profiles: [standard, release]
24
+ - id: FE-ESLINT
25
+ tool: eslint
26
+ targets: [src]
27
+ versionCommand: node_modules/.bin/eslint --version
28
+ profiles: [fast, standard, release]
29
+ ```
30
+
31
+ 检查 id 唯一。required 默认 true。确不适用的观察检查才可 `required:false + naReason`;类型、测试检查不可降级。releaseRequires 非空、引用已声明且在 release 中必需的检查,漏项判 invalid。
32
+
33
+ 项目声明不是中央底线。平台批准策略和适用表,固定摘要;作者不能通过删除检查、改成 observe 或修改期望变量改变强制口径。不能凭包中有 adapter 认定九维度已全部覆盖。
34
+
35
+ ## 实际版本锁
36
+
37
+ rules.lock.json 示例:
38
+
39
+ ```json
40
+ {
41
+ "lockVersion": 1,
42
+ "wl-skills-gate": "0.5.1",
43
+ "tools": {
44
+ "TS-TYPECHECK": "<实际版本命令完整输出>",
45
+ "FE-ESLINT": "<实际版本命令完整输出>"
46
+ }
47
+ }
48
+ ```
49
+
50
+ tools 以 check id 为键。执行 versionCommand 时使用其真实输出,随后与锁比对。release 所有必需检查均要有版本锁;实际 gate 版本也核验。raw/cmd 工具的自报版本不足以证明执行器真实性,生产接入需受控执行器、版本命令、固定镜像/依赖锁和覆盖证据。
51
+
52
+ ## 基线
53
+
54
+ ```json
55
+ {
56
+ "generatedAt": "2026-10-08T00:00:00Z",
57
+ "ruleSetVersion": "approved-rule-set",
58
+ "items": [{
59
+ "fingerprint": "<完整指纹>", "ruleId": "ESLINT/example", "file": "src/legacy.ts",
60
+ "severity": "error", "occurrenceCount": 1,
61
+ "firstSeenSha": "<首次发现SHA>", "owner": "409322"
62
+ }]
63
+ }
64
+ ```
65
+
66
+ 只承接经审批的历史静态 error/warning。blocker、类型/编译、关键测试、权限/数据/环境等质量底线不进入基线;其他执行器用 `baselineAllowed:false` 标出不可基线化规则。
67
+
68
+ 同指纹合并取最高严重程度,发生次数超过基线 occurrenceCount、风险提高时重新判新增。基线摘要需与批准版本一致。完整且所有检查有效的全量扫描才标 resolvedPending;增量扫描不以“没看到”认定消失;最终移除仍需独立复验,复发按新增。
69
+
70
+ ## 例外
71
+
72
+ 每条包含字符串 id/ruleId/reason/owner/approver/expiresAt,可选 path。自批、非法日期、缺字段记录为无效,按无豁免处理;正式签发拒绝含无效例外的报告。例外文件必须是数组。到期自动阻断,质量底线不能豁免,blockerWaivers 只接受 reject。
73
+
74
+ 姓名字段不能证明实际审批。平台需核验审批来源,并向签发/更新作业传批准的 waiversHash;自行修改文件、重新算摘要不能取得批准。
75
+
76
+ ## 配置格式与修复白名单
77
+
78
+ yaml-lite 仅支持简单键值、一级列表、流式数组、引号与注释,重复键和不支持的语法失败关闭。嵌套 fixer 结构使用 policy.json,见 04。未使用的工具包不强制安装或登记。
79
+
80
+ 受控 --sha / release 的全部必需检查还须实际查询工具版本并匹配 rules.lock.tools;仅 JSON 自报版本不能签发正式证明。ESLint/typecheck 可查询对应 bin --version,其他 adapter 需登记受控 versionCommand。
81
+
82
+ ## ESLint 范围配置(0.5.1)
83
+
84
+ `targets` 是非空、无重复的项目相对目录/文件/glob 数组,缺省 `["."]`;`args` 是批准的额外 CLI 参数。全量检查传入 targets;staged/range 检查以 NUL 分隔的 Git 文件清单为准,保留中文与空格。ESLint 的 `command` 覆盖仅供全量接入,不能配增量或 promote;生产优先用 `bin + targets + args`。检查 args 禁止 `--fix` / `--fix-dry-run`,机械修复使用独立入口。
85
+
86
+ ESLint 8 的 eslintrc 项目可按实际需要配置 `args: --ext .js,.jsx,.ts,.tsx,.vue`;flat config 通过文件匹配配置纳入扩展名,不照抄 `--ext`。参数和扩展名必须结合项目配置验收。[ESLint 8 官方 CLI](https://eslint.org/docs/v8.x/use/command-line-interface)。
87
+
88
+ 扫描数大于零只证明没有空跑;应检查哪些目录/扩展名、哪些排除项获批准、历史数量变化是否合理,仍由平台适用登记和覆盖对账承担。不要因检查了一个 JS 文件就认定 Vue/TS 项目全部覆盖。
@@ -0,0 +1,49 @@
1
+ # 03 报告、签名证明和制品清单
2
+
3
+ 参数非法或输出目录覆盖源码/经项目内符号链接时,Gate 在新建安全临时目录保存 invalid 报告,位置见控制台及 summary.run.outDir。CI 除常规 quality-reports 归档外必须保留作业日志;拒绝路径不能回读工作区中的旧绿报告当作本次结果。
4
+
5
+ 对应 4.2、A2/C3/C4。quality-summary.json 是本次事实快照,最新问题状态以主台账为准,不回改历史报告。
6
+
7
+ run 包含 project/profile/commitSha/eventId/targetEnv/startedAt/sourceBound/sourceDigest;policy 包含策略、规则、基线、例外摘要和 requiredCheckIds;checks 记录实际状态/必需性/版本;findings 记录指纹、风险、位置、发生明细和处置状态;decision 为 passed/blocked/invalid。
8
+
9
+ Java 显式共享生产时,checks[].production 保存本轮 eventId、producer、commandHash、reportHash、生产耗时与 reused;顶层 productions 记录本轮各组 commandRuns、status、选中成员与报告数。reused 仅指同轮其他消费者共用,不能当作上一轮报告复用。旧报告没有该字段表示生产次数不可获取。离线 cost 统计读取原工件,不改变历史状态,详见 [14](14-low-token-execution.md)。
10
+
11
+ 生成后自动做 schema 校验。签发和部署还做语义核验:检查非空、无重复、必需项全部 ok、声明数一致、无失效检查和未处置强制问题。只有 JSON 结构合法不足以放行。
12
+
13
+ ## 按需查看,不重新扫描
14
+
15
+ `inspect quality-reports/quality-summary.json` 默认输出有字节预算的 JSON 摘要,包含原结论、完整问题计数和检查状态。`--view findings` 才展开明细,支持 `--status`、`--severity`、`--rule` 精确过滤及 `--limit/--max-bytes/--cursor` 分页;查看下一页只读取已有报告。详见 [低 token 执行](14-low-token-execution.md)。
16
+
17
+ 输出标记 `displayOnly: true`,只作阅读视图;原 JSON/原生 XML、签名、规则及源码证据继续完整归档。过滤后的零条问题仍保留整份报告的 blocked/invalid 结论与退出码。结构校验不认证来源,也不证明报告仍对应当前源码;不能将 inspect 作为流水线放行或问题关闭依据。
18
+
19
+ ## unsigned 结果
20
+
21
+ run 产生 gate-attestation.json,kind=`unsigned-gate-result`,仅供定位和后续受控签发。该文件未认证,部署拒收。摘要用于关联,不表示可信来源。
22
+
23
+ ## 受控证明
24
+
25
+ attest 在独立受控作业中将通过的 standard/release 报告签为 kind=`gate-proof`,Ed25519 签名覆盖规范化完整证明,包括项目、真实候选 SHA、档位、环境、事件、报告摘要、策略/规则、issuer/pipelineId/jobId 和时间。
26
+
27
+ 签发前必须由平台查询确认输入来自批准的流水线模板/作业/Runner,源码、档位、工具和报告工件正确;包的离线命令不能替代这次平台查询。私钥不能进入运行作者代码的检查环境。
28
+
29
+ 验证方固定公钥、issuer 和平台查询取得的 pipeline/job ID,并提供 SHA、项目、策略/规则摘要。baseline/waivers 有文件时另外提供批准摘要。缺参数、缺签名、错误身份、过期/未来时间、档位或环境不匹配均拒绝。
30
+
31
+ ## 制品清单
32
+
33
+ ```bash
34
+ wl-skills-gate manifest --project wl-ui-produce --env prd \
35
+ --artifacts dist --configs deploy/runtime.json,deploy/migration.sql \
36
+ --out quality-reports/artifact-manifest.json
37
+ ```
38
+
39
+ artifacts/configs 为项目内相对路径(逗号分隔)。记录真实干净 HEAD、与门禁一致的 sourceDigest、环境、文件字节或完整目录内容及目录内文件模式。空目录、符号链接、越界路径拒绝。release 签名绑定整个 manifest 摘要,verify-artifact 再读取即将部署的真实内容重算。
40
+
41
+ 构建后同一份制品进入验证和部署;重新构建、更换配置或制品需重新出清单/证明。验证后部署使用受控、不可被其他作业改写的工件。
42
+
43
+ ## 平台报告
44
+
45
+ 包导出 `code-quality/gl-code-quality-report.json` 供 GitLab codequality 消费。JUnit 由真实测试工具本次产出 XML,adapter 校验其结构/计数,再把原始 XML 配置为 GitLab/Jenkins 测试报告。多个 suite 文件需全部接入,不用一份 XML 代表全部测试。
46
+
47
+ ## 明确保留的缺口
48
+
49
+ 双仓 release-manifest 的前后端 SHA、契约、数据库变更、发布顺序和批准组合核验仍需平台实现;单仓签名证明不能替代。metrics、业务复核、待部署到现场生效的管理状态也由对应事实源提供。
@@ -0,0 +1,51 @@
1
+ # 04 机械修复与复验
2
+
3
+ 对应 6.1/6.2、A7。0.5 使用 schemaVersion=2 修复计划;旧计划需重新生成。
4
+
5
+ ```bash
6
+ wl-skills-gate run --profile standard
7
+ wl-skills-gate fix --plan quality-reports/quality-summary.json
8
+ # 审查计划后,在本地/个人工作区:
9
+ wl-skills-gate fix --apply quality-reports/repair/fix-plan.json --confirm
10
+ ```
11
+
12
+ 仅对 standard 全量报告生成机械计划,且全部检查有效。预览前核对报告对应的源码/HEAD、策略/规则/基线/例外,扫描后已有变化则先重新扫描。增量或 release 报告不能替代完整修复复扫。报告声明 autoFixable=true 且规则属于白名单才可自动执行;报告中任意 fixCommand 被忽略,权限/事务/数据等规则走人工。
13
+
14
+ 内置白名单支持 ESLint 可修复规则。其他机械 fixer 在 policy.json 登记:
15
+
16
+ ```json
17
+ {
18
+ "fixers": [{
19
+ "id": "approved-mechanical-fixer",
20
+ "ruleIds": ["STATIC/EXAMPLE"],
21
+ "files": ["src/example.ts"],
22
+ "command": "node approved-fixer.mjs"
23
+ }]
24
+ }
25
+ ```
26
+
27
+ finding 指明 fixerId、autoFixable、文件。fixer 必须为经过审查的机械命令,仅修改登记文件;该白名单不是操作系统沙箱,不授权任意 AI 命令、网络/数据库或 Git 分支写入。
28
+
29
+ ## 防漂移与回滚
30
+
31
+ 未跟踪生成目录排除 `.git/node_modules/quality-reports/target/dist/coverage`;已被 Git 跟踪的内容一律参与快照,即使处于上述目录;受控修复器不得借此修改依赖、构建产物或仓库元数据。
32
+
33
+ 计划包含完整动作、输入报告摘要、真实 HEAD、文件内容/模式快照、策略/规则摘要。planHash 覆盖整份计划,应用前重查所有输入;代码、策略、报告、HEAD、白名单或快照变化则零写入。
34
+
35
+ 执行前归档原始报告。命令失败/超时、白名单外文件变化、新增回归、复扫失效或复扫期间源码再次变化时恢复项目文件快照,保留失败与复扫报告。保留用户在预览前已有的文件内容,不用 git reset 覆盖整个工作区。
36
+
37
+ 受控机械命令不得写项目外资源或改变 Git 元数据;包的文件回滚不承诺恢复外部服务/数据库或 Git 元数据。遇到这些变化必须人工处理。源码有符号链接时降为人工,避免越界。
38
+
39
+ ## 报告与关闭边界
40
+
41
+ fix-report 包含 applied/failedCommands/unauthorizedFiles/changedFiles/rescanChangedFiles/fixed/remaining/regressions/beforeArchive/rolledBack/scanResolved。
42
+
43
+ 扫描复验通过时 `scanResolved=true, nextState=pending-verification, closed=false`。负责人和独立复核人验证原失败用例及影响范围,必要时确认目标环境生效后,由主台账关闭。CI 只检查和出计划,不自动修共享分支。
44
+
45
+ ## 0.5.1 的恢复结果
46
+
47
+ 修复后把普通文件替换为符号链接同样视为越界。可证明安全恢复时,恢复文件后再次运行 Gate,再核对源码摘要/HEAD;报告保存 attemptedRescan 和 rollbackVerification,撤销 fixed 清单,当前 quality-summary 反映恢复后的真实代码。rolledBack=true 只表示源码恢复已核验,不表示问题已解决。
48
+
49
+ HEAD 变化、快照异常或恢复不完整时,manualRecoveryRequired=true、退出 2;不自动 git reset,不声称已经回滚,不按尝试期间的绿报告关闭问题。保留原/后证据,由负责人查明原因再恢复、全量检查。文件恢复无法回滚外部资源、依赖缓存或其他进程。
50
+
51
+ 自动修复应在独占的隔离 worktree 执行;应用期间避免人工和其他进程并发编辑,防止文件恢复覆盖并发变更。将允许的修改提交后重新取得受控质量证据与独立 Review。