@dommaker/harness 0.19.0 → 1.0.0
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.
- package/CHANGELOG.md +14 -0
- package/bin/harness.js +136 -557
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +2 -13
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +90 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -0
- package/dist/cli/commands/definitions.js +519 -0
- package/dist/cli/commands/definitions.js.map +1 -0
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +3 -25
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +3 -93
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +4 -4
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/context/index.d.ts +1 -5
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +1 -5
- package/dist/context/index.js.map +1 -1
- package/dist/context/knowledge-injector.d.ts +3 -3
- package/dist/context/knowledge-injector.d.ts.map +1 -1
- package/dist/context/knowledge-injector.js +1 -1
- package/dist/context/session-manager.d.ts +1 -12
- package/dist/context/session-manager.d.ts.map +1 -1
- package/dist/context/session-manager.js +0 -63
- package/dist/context/session-manager.js.map +1 -1
- package/dist/context/types.d.ts +1 -39
- package/dist/context/types.d.ts.map +1 -1
- package/dist/context/types.js +2 -15
- package/dist/context/types.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.d.ts +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +17 -2
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +84 -2
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +3 -17
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +7 -47
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +0 -7
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +1 -8
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/index.d.ts +1 -1
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +2 -3
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/interceptor.d.ts.map +1 -1
- package/dist/core/constraints/interceptor.js +1 -5
- package/dist/core/constraints/interceptor.js.map +1 -1
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +0 -1
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +1 -8
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/dashboard/stats.d.ts.map +1 -1
- package/dist/dashboard/stats.js.map +1 -1
- package/dist/dashboard/types.d.ts +2 -2
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/failure/constraint-handler.d.ts.map +1 -1
- package/dist/failure/constraint-handler.js +0 -2
- package/dist/failure/constraint-handler.js.map +1 -1
- package/dist/gates/acceptance.d.ts +9 -1
- package/dist/gates/acceptance.d.ts.map +1 -1
- package/dist/gates/acceptance.js +21 -0
- package/dist/gates/acceptance.js.map +1 -1
- package/dist/gates/checker-gate.d.ts +23 -0
- package/dist/gates/checker-gate.d.ts.map +1 -0
- package/dist/gates/checker-gate.js +53 -0
- package/dist/gates/checker-gate.js.map +1 -0
- package/dist/gates/command.d.ts +9 -2
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +10 -0
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts +8 -2
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +9 -0
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/decision.d.ts +17 -0
- package/dist/gates/decision.d.ts.map +1 -0
- package/dist/gates/decision.js +25 -0
- package/dist/gates/decision.js.map +1 -0
- package/dist/gates/definitions.d.ts +63 -0
- package/dist/gates/definitions.d.ts.map +1 -0
- package/dist/gates/definitions.js +160 -0
- package/dist/gates/definitions.js.map +1 -0
- package/dist/gates/effective-gates.d.ts +30 -0
- package/dist/gates/effective-gates.d.ts.map +1 -0
- package/dist/gates/effective-gates.js +85 -0
- package/dist/gates/effective-gates.js.map +1 -0
- package/dist/gates/index.d.ts +25 -1
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js +37 -1
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/performance.d.ts +8 -2
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +9 -0
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/registry.d.ts +32 -0
- package/dist/gates/registry.d.ts.map +1 -0
- package/dist/gates/registry.js +90 -0
- package/dist/gates/registry.js.map +1 -0
- package/dist/gates/review.d.ts +8 -2
- package/dist/gates/review.d.ts.map +1 -1
- package/dist/gates/review.js +9 -0
- package/dist/gates/review.js.map +1 -1
- package/dist/gates/runner.d.ts +30 -0
- package/dist/gates/runner.d.ts.map +1 -0
- package/dist/gates/runner.js +38 -0
- package/dist/gates/runner.js.map +1 -0
- package/dist/gates/security.d.ts +8 -2
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +9 -0
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +41 -1
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +0 -2
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/config.d.ts +26 -0
- package/dist/hooks/config.d.ts.map +1 -0
- package/dist/hooks/config.js +30 -0
- package/dist/hooks/config.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/registry.d.ts +18 -1
- package/dist/hooks/registry.d.ts.map +1 -1
- package/dist/hooks/registry.js +45 -0
- package/dist/hooks/registry.js.map +1 -1
- package/dist/hooks/types.d.ts +20 -0
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -20
- package/dist/index.js.map +1 -1
- package/dist/knowledge/import.d.ts +2 -2
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
- package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
- package/dist/knowledge/types.d.ts +5 -7
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js +1 -1
- package/dist/knowledge/types.js.map +1 -1
- package/dist/monitoring/constraint-doctor.d.ts +4 -22
- package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
- package/dist/monitoring/constraint-doctor.js +7 -94
- package/dist/monitoring/constraint-doctor.js.map +1 -1
- package/dist/presets/standard.d.ts +1 -1
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +1 -2
- package/dist/presets/standard.js.map +1 -1
- package/dist/spec/annotation-checker.d.ts.map +1 -1
- package/dist/spec/annotation-checker.js +6 -26
- package/dist/spec/annotation-checker.js.map +1 -1
- package/dist/tools/index.d.ts +0 -4
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/types/constraint.d.ts +1 -12
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +0 -3
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/project-config.d.ts +0 -2
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/utils/file-walk.d.ts +2 -2
- package/dist/utils/file-walk.js +2 -2
- package/package.json +6 -1
- package/src/CONTEXT.md +14 -0
- package/src/__tests__/checker-extra.test.ts +2 -59
- package/src/__tests__/constraint-doctor.test.ts +1 -140
- package/src/__tests__/constraint-handler.test.ts +0 -4
- package/src/__tests__/project-config-loader-extra.test.ts +0 -20
- package/src/__tests__/project-config-loader.test.ts +0 -1
- package/src/cli/commands/CONTEXT.md +15 -7
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
- package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
- package/src/cli/commands/__tests__/check.test.ts +7 -75
- package/src/cli/commands/__tests__/registry.test.ts +201 -0
- package/src/cli/commands/check.ts +2 -14
- package/src/cli/commands/definitions.ts +593 -0
- package/src/cli/commands/knowledge.ts +3 -3
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
- package/src/cli/commands/sync-docs/index.ts +8 -5
- package/src/context/CONTEXT.md +2 -7
- package/src/context/__tests__/session-manager.test.ts +0 -91
- package/src/context/index.ts +1 -5
- package/src/context/knowledge-injector.ts +3 -3
- package/src/context/session-manager.ts +0 -74
- package/src/context/types.ts +2 -63
- package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
- package/src/core/constraints/agent-prompt-renderer.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +95 -2
- package/src/core/constraints/checker.ts +8 -53
- package/src/core/constraints/definitions.ts +0 -8
- package/src/core/constraints/doc-freshness/runner.ts +1 -2
- package/src/core/constraints/index.ts +1 -2
- package/src/core/constraints/interceptor.ts +1 -6
- package/src/core/effective-constraints.ts +0 -1
- package/src/core/project-config-loader.ts +1 -7
- package/src/dashboard/stats.ts +2 -2
- package/src/dashboard/types.ts +2 -2
- package/src/failure/constraint-handler.ts +0 -2
- package/src/gates/CONTEXT.md +16 -13
- package/src/gates/__tests__/checker-gate.test.ts +45 -0
- package/src/gates/__tests__/contract.test.ts +73 -0
- package/src/gates/__tests__/effective-gates.test.ts +82 -0
- package/src/gates/__tests__/gate-interface.test.ts +202 -0
- package/src/gates/__tests__/registry.test.ts +87 -0
- package/src/gates/__tests__/runner.test.ts +81 -0
- package/src/gates/acceptance.ts +24 -1
- package/src/gates/checker-gate.ts +54 -0
- package/src/gates/command.ts +13 -2
- package/src/gates/contract.ts +12 -2
- package/src/gates/decision.ts +27 -0
- package/src/gates/definitions.ts +207 -0
- package/src/gates/effective-gates.ts +106 -0
- package/src/gates/index.ts +38 -0
- package/src/gates/performance.ts +12 -2
- package/src/gates/registry.ts +107 -0
- package/src/gates/review.ts +12 -2
- package/src/gates/runner.ts +57 -0
- package/src/gates/security.ts +12 -2
- package/src/gates/types.ts +48 -1
- package/src/hooks/CONTEXT.md +12 -2
- package/src/hooks/__tests__/bootstrap.test.ts +0 -1
- package/src/hooks/__tests__/config.test.ts +19 -0
- package/src/hooks/__tests__/pipeline.test.ts +79 -0
- package/src/hooks/__tests__/registry.test.ts +67 -0
- package/src/hooks/bootstrap.ts +0 -2
- package/src/hooks/config.ts +29 -0
- package/src/hooks/index.ts +2 -1
- package/src/hooks/registry.ts +60 -1
- package/src/hooks/types.ts +21 -0
- package/src/index.ts +5 -22
- package/src/knowledge/import.ts +3 -3
- package/src/knowledge/ingest.ts +4 -4
- package/src/knowledge/lifecycle-hooks.ts +3 -3
- package/src/knowledge/types.ts +5 -8
- package/src/monitoring/constraint-doctor.ts +7 -106
- package/src/presets/__tests__/standard.test.ts +0 -2
- package/src/presets/standard.ts +1 -2
- package/src/spec/__tests__/annotation-checker.test.ts +9 -3
- package/src/spec/annotation-checker.ts +7 -29
- package/src/tools/CONTEXT.md +7 -14
- package/src/tools/index.ts +0 -4
- package/src/types/constraint.ts +1 -14
- package/src/types/project-config.ts +0 -3
- package/src/utils/file-walk.ts +2 -2
- package/dist/architecture/constraint-engine.d.ts +0 -67
- package/dist/architecture/constraint-engine.d.ts.map +0 -1
- package/dist/architecture/constraint-engine.js +0 -295
- package/dist/architecture/constraint-engine.js.map +0 -1
- package/dist/architecture/cross-project-checker.d.ts +0 -54
- package/dist/architecture/cross-project-checker.d.ts.map +0 -1
- package/dist/architecture/cross-project-checker.js +0 -467
- package/dist/architecture/cross-project-checker.js.map +0 -1
- package/dist/cli/commands/index.d.ts +0 -30
- package/dist/cli/commands/index.d.ts.map +0 -1
- package/dist/cli/commands/index.js +0 -91
- package/dist/cli/commands/index.js.map +0 -1
- package/dist/context/file-budget.d.ts +0 -40
- package/dist/context/file-budget.d.ts.map +0 -1
- package/dist/context/file-budget.js +0 -147
- package/dist/context/file-budget.js.map +0 -1
- package/dist/context/progressive-loader.d.ts +0 -109
- package/dist/context/progressive-loader.d.ts.map +0 -1
- package/dist/context/progressive-loader.js +0 -166
- package/dist/context/progressive-loader.js.map +0 -1
- package/dist/context/token-pipeline.d.ts +0 -55
- package/dist/context/token-pipeline.d.ts.map +0 -1
- package/dist/context/token-pipeline.js +0 -220
- package/dist/context/token-pipeline.js.map +0 -1
- package/dist/context/tool-output-budget.d.ts +0 -59
- package/dist/context/tool-output-budget.d.ts.map +0 -1
- package/dist/context/tool-output-budget.js +0 -177
- package/dist/context/tool-output-budget.js.map +0 -1
- package/dist/core/constraints/default-executors.d.ts +0 -10
- package/dist/core/constraints/default-executors.d.ts.map +0 -1
- package/dist/core/constraints/default-executors.js +0 -120
- package/dist/core/constraints/default-executors.js.map +0 -1
- package/dist/llm/adapter.d.ts +0 -36
- package/dist/llm/adapter.d.ts.map +0 -1
- package/dist/llm/adapter.js +0 -118
- package/dist/llm/adapter.js.map +0 -1
- package/dist/llm/index.d.ts +0 -3
- package/dist/llm/index.d.ts.map +0 -1
- package/dist/llm/index.js +0 -19
- package/dist/llm/index.js.map +0 -1
- package/dist/llm/types.d.ts +0 -62
- package/dist/llm/types.d.ts.map +0 -1
- package/dist/llm/types.js +0 -8
- package/dist/llm/types.js.map +0 -1
- package/dist/tools/core/index.d.ts +0 -13
- package/dist/tools/core/index.d.ts.map +0 -1
- package/dist/tools/core/index.js +0 -198
- package/dist/tools/core/index.js.map +0 -1
- package/dist/tools/loader.d.ts +0 -21
- package/dist/tools/loader.d.ts.map +0 -1
- package/dist/tools/loader.js +0 -199
- package/dist/tools/loader.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -59
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -115
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/types.d.ts +0 -38
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js +0 -6
- package/dist/tools/types.js.map +0 -1
- package/src/__tests__/architecture-engine.test.ts +0 -619
- package/src/__tests__/context/progressive-loader.test.ts +0 -131
- package/src/__tests__/cross-project-checker.test.ts +0 -318
- package/src/architecture/CONTEXT.md +0 -22
- package/src/architecture/constraint-engine.ts +0 -351
- package/src/architecture/cross-project-checker.ts +0 -545
- package/src/cli/commands/index.ts +0 -30
- package/src/context/__tests__/file-budget.test.ts +0 -142
- package/src/context/__tests__/progressive-loader.test.ts +0 -264
- package/src/context/__tests__/token-pipeline.test.ts +0 -178
- package/src/context/__tests__/tool-output-budget.test.ts +0 -95
- package/src/context/file-budget.ts +0 -142
- package/src/context/progressive-loader.ts +0 -260
- package/src/context/token-pipeline.ts +0 -265
- package/src/context/tool-output-budget.ts +0 -178
- package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
- package/src/core/constraints/default-executors.ts +0 -92
- package/src/llm/CONTEXT.md +0 -21
- package/src/llm/__tests__/adapter.test.ts +0 -172
- package/src/llm/adapter.ts +0 -149
- package/src/llm/index.ts +0 -2
- package/src/llm/types.ts +0 -81
- package/src/tools/__tests__/loader.test.ts +0 -252
- package/src/tools/__tests__/registry.test.ts +0 -157
- package/src/tools/core/__tests__/core.test.ts +0 -122
- package/src/tools/core/index.ts +0 -201
- package/src/tools/loader.ts +0 -216
- package/src/tools/registry.ts +0 -128
- package/src/tools/types.ts +0 -46
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# cli/commands/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
CLI 子命令实现:24 个顶层命令 + constraints 治理子命令 report/retire
|
|
4
|
+
CLI 子命令实现:24 个顶层命令 + constraints 治理子命令 report/retire,覆盖约束检查/门禁验证/文档同步/知识管理/失败诊断/发布/会话分析等。
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
H5(#44)起:
|
|
7
|
+
- **命令定义即注册**:`definitions.ts` 的 `COMMAND_DEFINITIONS` 是命令形状(名称/别名/位置参数/选项/子命令/实现引用)的单一来源;bin/harness.js 遍历本表注册表驱动生成,不再手写 commander 命令块,不再有 index.ts barrel 两处同步(R6)
|
|
8
|
+
- **per-command 懒加载**(O2):定义表存实现引用 `{ module, export }`,bin 只在 action 执行时 require 对应命令模块,任一命令执行不再加载全部命令实现
|
|
9
|
+
|
|
10
|
+
## 核心导出
|
|
11
|
+
- `COMMAND_DEFINITIONS`(definitions.ts)— 全部非门禁命令定义(纯数据模块,禁止 import 命令实现)
|
|
12
|
+
- 各命令文件:check / validate / passes-gate / init / report / status / spec / sync-docs / knowledge / sdd / failure / posteval-plan / release / analyze-sessions / update-user-model / constraints / doc-freshness-check / spec-baseline-check
|
|
13
|
+
- 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`)
|
|
8
14
|
|
|
9
15
|
`constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom 落 custom-constraints.yml 条目 retired 段(#82 D6 一处真相)+ KnowledgeStore 沉淀 + CLAUDE.md 注入段同步)。
|
|
10
16
|
|
|
@@ -14,13 +20,15 @@ check / validate / passes-gate / init / report / status / spec / acceptance / pe
|
|
|
14
20
|
- 依赖 `src/monitoring/` 追踪/诊断
|
|
15
21
|
- 依赖 `src/knowledge/` 知识引擎
|
|
16
22
|
- 依赖 `src/failure/` 失败记录
|
|
17
|
-
- 被 `bin/harness.js` CLI
|
|
23
|
+
- 被 `bin/harness.js` CLI 入口按定义表驱动生成
|
|
18
24
|
|
|
19
25
|
## 约定
|
|
20
|
-
-
|
|
21
|
-
-
|
|
26
|
+
- 每个命令一个实现文件,命名与命令名一致(sync-docs 为模块族目录)
|
|
27
|
+
- **新增命令 = 命令实现文件 + definitions.ts 一条定义(含 CLI 元数据与实现引用)+ 测试**,不再改 bin/harness.js
|
|
28
|
+
- **definitions.ts 是纯数据模块**:禁止 import 任何命令实现/运行时依赖(保 --help/--version 懒加载);实现引用可解析性由 `__tests__/registry.test.ts` 构建/测试期断言
|
|
22
29
|
- 命令选项类型命名规范:XxxOptions
|
|
23
30
|
|
|
24
31
|
## 注意事项
|
|
25
32
|
- 新增命令需同步更新 CLAUDE.md / CAPABILITIES.md / src/CONTEXT.md
|
|
26
|
-
- knowledge 命令包含
|
|
33
|
+
- knowledge 命令包含 13 个子操作(list/search/import/decay/stats/upsert/sync-status/sync-rag/audit/snapshot/migrate/index/health)
|
|
34
|
+
- 特殊路由(选项条件、子命令兜底、退出码处理)表达在定义表的 optionRoutes / subcommands / afterRun 字段,bin 是纯通用引擎、不含单命令知识
|
|
@@ -16,7 +16,6 @@ import { getEffectiveConstraints } from '../../../core/effective-constraints';
|
|
|
16
16
|
|
|
17
17
|
jest.mock('../../../core/constraints/checker', () => ({
|
|
18
18
|
constraintChecker: {
|
|
19
|
-
setCustomConfig: jest.fn(),
|
|
20
19
|
setTraceRecorder: jest.fn(),
|
|
21
20
|
checkConstraints: jest.fn(),
|
|
22
21
|
},
|
|
@@ -58,9 +57,7 @@ describe('check 命令注入漂移警告', () => {
|
|
|
58
57
|
passed: true,
|
|
59
58
|
ironLaws: [],
|
|
60
59
|
guidelines: [],
|
|
61
|
-
tips: [],
|
|
62
60
|
warningCount: 0,
|
|
63
|
-
tipCount: 0,
|
|
64
61
|
});
|
|
65
62
|
});
|
|
66
63
|
|
|
@@ -29,7 +29,6 @@ jest.mock('child_process', () => ({
|
|
|
29
29
|
// Mock constraintChecker
|
|
30
30
|
jest.mock('../../../core/constraints/checker', () => ({
|
|
31
31
|
constraintChecker: {
|
|
32
|
-
setCustomConfig: jest.fn(),
|
|
33
32
|
setTraceRecorder: jest.fn(),
|
|
34
33
|
checkConstraints: jest.fn(),
|
|
35
34
|
},
|
|
@@ -89,9 +88,7 @@ describe('check 命令 skip 输出', () => {
|
|
|
89
88
|
{ id: 'law_skip', level: 'iron_law', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('law_skip', 'iron_law') },
|
|
90
89
|
],
|
|
91
90
|
guidelines: [],
|
|
92
|
-
tips: [],
|
|
93
91
|
warningCount: 0,
|
|
94
|
-
tipCount: 0,
|
|
95
92
|
});
|
|
96
93
|
|
|
97
94
|
await check({ preset: 'default', staged: false });
|
|
@@ -113,9 +110,7 @@ describe('check 命令 skip 输出', () => {
|
|
|
113
110
|
{ id: 'g_pass', level: 'guideline', satisfied: true, checkedAt: new Date(), constraint: fakeConstraint('g_pass', 'guideline') },
|
|
114
111
|
{ id: 'g_skip', level: 'guideline', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('g_skip', 'guideline') },
|
|
115
112
|
],
|
|
116
|
-
tips: [],
|
|
117
113
|
warningCount: 0,
|
|
118
|
-
tipCount: 0,
|
|
119
114
|
});
|
|
120
115
|
|
|
121
116
|
await check({ preset: 'default', staged: false });
|
|
@@ -40,7 +40,6 @@ jest.mock('child_process', () => ({
|
|
|
40
40
|
// Mock constraintChecker
|
|
41
41
|
jest.mock('../../../core/constraints/checker', () => ({
|
|
42
42
|
constraintChecker: {
|
|
43
|
-
setCustomConfig: jest.fn(),
|
|
44
43
|
setTraceRecorder: jest.fn(),
|
|
45
44
|
checkConstraints: jest.fn(),
|
|
46
45
|
},
|
|
@@ -89,9 +88,7 @@ describe('check command', () => {
|
|
|
89
88
|
passed: true,
|
|
90
89
|
ironLaws: [{ id: 'test', level: 'iron_law', satisfied: true, checkedAt: new Date(), constraint: { kind: 'check' as const, id: 'test', rule: 'test', message: 'test', level: 'iron_law', trigger: 'code_implementation', enforcement: 'checkpoint-required' } }],
|
|
91
90
|
guidelines: [],
|
|
92
|
-
tips: [],
|
|
93
91
|
warningCount: 0,
|
|
94
|
-
tipCount: 0,
|
|
95
92
|
});
|
|
96
93
|
|
|
97
94
|
await check({ preset: 'default', staged: false });
|
|
@@ -103,9 +100,7 @@ describe('check command', () => {
|
|
|
103
100
|
passed: false,
|
|
104
101
|
ironLaws: [{ id: 'no_bypass_checkpoint', level: 'iron_law', satisfied: false, checkedAt: new Date(), constraint: { kind: 'check' as const, id: 'no_bypass_checkpoint', rule: 'test', message: 'test', level: 'iron_law', trigger: 'code_implementation', enforcement: 'checkpoint-required' } }],
|
|
105
102
|
guidelines: [],
|
|
106
|
-
tips: [],
|
|
107
103
|
warningCount: 0,
|
|
108
|
-
tipCount: 0,
|
|
109
104
|
});
|
|
110
105
|
|
|
111
106
|
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
@@ -120,9 +115,7 @@ describe('check command', () => {
|
|
|
120
115
|
passed: false,
|
|
121
116
|
ironLaws: [],
|
|
122
117
|
guidelines: [{ id: 'test_guideline', level: 'guideline', satisfied: false, checkedAt: new Date(), constraint: { kind: 'check' as const, id: 'test_guideline', rule: 'test', message: 'test', level: 'guideline', trigger: 'code_implementation', enforcement: 'warning' } }],
|
|
123
|
-
tips: [],
|
|
124
118
|
warningCount: 1,
|
|
125
|
-
tipCount: 0,
|
|
126
119
|
});
|
|
127
120
|
|
|
128
121
|
await check({ preset: 'default', staged: false });
|
|
@@ -141,38 +134,23 @@ describe('check command', () => {
|
|
|
141
134
|
passed: true,
|
|
142
135
|
ironLaws: [],
|
|
143
136
|
guidelines: [],
|
|
144
|
-
tips: [],
|
|
145
137
|
warningCount: 0,
|
|
146
|
-
tipCount: 0,
|
|
147
138
|
});
|
|
148
139
|
|
|
149
140
|
await check({ preset: 'default', staged: false, projectPath: '/project' });
|
|
150
|
-
expect(mockChecker.
|
|
141
|
+
expect(mockChecker.checkConstraints).toHaveBeenCalledWith(
|
|
142
|
+
expect.anything(),
|
|
143
|
+
expect.objectContaining({ disabled: ['disabled_constraint'] })
|
|
144
|
+
);
|
|
151
145
|
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('已禁用约束'));
|
|
152
146
|
});
|
|
153
147
|
|
|
154
|
-
it('应该显示提示信息', async () => {
|
|
155
|
-
mockChecker.checkConstraints.mockResolvedValue({
|
|
156
|
-
passed: true,
|
|
157
|
-
ironLaws: [],
|
|
158
|
-
guidelines: [],
|
|
159
|
-
tips: [{ id: 'test_tip', level: 'tip', satisfied: false, checkedAt: new Date(), constraint: { kind: 'check' as const, id: 'test_tip', rule: 'test', message: 'test tip', level: 'tip', trigger: 'code_implementation', enforcement: 'info' } }],
|
|
160
|
-
warningCount: 0,
|
|
161
|
-
tipCount: 1,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
await check({ preset: 'default', staged: false });
|
|
165
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('提示'));
|
|
166
|
-
});
|
|
167
|
-
|
|
168
148
|
it('应该显示通过的指导原则', async () => {
|
|
169
149
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
170
150
|
passed: true,
|
|
171
151
|
ironLaws: [],
|
|
172
152
|
guidelines: [{ id: 'test_guideline', level: 'guideline', satisfied: true, checkedAt: new Date(), constraint: { kind: 'check' as const, id: 'test_guideline', rule: 'test', message: 'test', level: 'guideline', trigger: 'code_implementation', enforcement: 'warning' } }],
|
|
173
|
-
tips: [],
|
|
174
153
|
warningCount: 0,
|
|
175
|
-
tipCount: 0,
|
|
176
154
|
});
|
|
177
155
|
|
|
178
156
|
await check({ preset: 'default', staged: false });
|
|
@@ -186,9 +164,7 @@ describe('check command', () => {
|
|
|
186
164
|
passed: true,
|
|
187
165
|
ironLaws: [],
|
|
188
166
|
guidelines: [],
|
|
189
|
-
tips: [],
|
|
190
167
|
warningCount: 0,
|
|
191
|
-
tipCount: 0,
|
|
192
168
|
});
|
|
193
169
|
|
|
194
170
|
await check({ preset: 'default', staged: true });
|
|
@@ -201,9 +177,7 @@ describe('check command', () => {
|
|
|
201
177
|
passed: true,
|
|
202
178
|
ironLaws: [],
|
|
203
179
|
guidelines: [],
|
|
204
|
-
tips: [],
|
|
205
180
|
warningCount: 0,
|
|
206
|
-
tipCount: 0,
|
|
207
181
|
});
|
|
208
182
|
|
|
209
183
|
await check({ preset: 'default', staged: false });
|
|
@@ -216,9 +190,7 @@ describe('check command', () => {
|
|
|
216
190
|
passed: true,
|
|
217
191
|
ironLaws: [],
|
|
218
192
|
guidelines: [],
|
|
219
|
-
tips: [],
|
|
220
193
|
warningCount: 0,
|
|
221
|
-
tipCount: 0,
|
|
222
194
|
});
|
|
223
195
|
|
|
224
196
|
await check({ preset: 'default', staged: true });
|
|
@@ -231,9 +203,7 @@ describe('check command', () => {
|
|
|
231
203
|
passed: true,
|
|
232
204
|
ironLaws: [],
|
|
233
205
|
guidelines: [],
|
|
234
|
-
tips: [],
|
|
235
206
|
warningCount: 0,
|
|
236
|
-
tipCount: 0,
|
|
237
207
|
});
|
|
238
208
|
|
|
239
209
|
await check({ preset: 'default', staged: false });
|
|
@@ -244,7 +214,7 @@ describe('check command', () => {
|
|
|
244
214
|
it('应该检测 module_modification 触发条件(.ts 文件)', async () => {
|
|
245
215
|
mockExecAsync.mockResolvedValue({ stdout: 'src/foo.ts\nsrc/bar.tsx\n', stderr: '' });
|
|
246
216
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
247
|
-
passed: true, ironLaws: [], guidelines: [],
|
|
217
|
+
passed: true, ironLaws: [], guidelines: [], warningCount: 0,
|
|
248
218
|
});
|
|
249
219
|
|
|
250
220
|
// Mock filesystem so detectSourceRoots finds src/ as a source root
|
|
@@ -267,7 +237,7 @@ describe('check command', () => {
|
|
|
267
237
|
it('应该检测 module_modification 触发条件(.js 文件)', async () => {
|
|
268
238
|
mockExecAsync.mockResolvedValue({ stdout: 'src/foo.js\nsrc/bar.jsx\n', stderr: '' });
|
|
269
239
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
270
|
-
passed: true, ironLaws: [], guidelines: [],
|
|
240
|
+
passed: true, ironLaws: [], guidelines: [], warningCount: 0,
|
|
271
241
|
});
|
|
272
242
|
|
|
273
243
|
// Mock filesystem so detectSourceRoots finds src/ as a source root
|
|
@@ -290,7 +260,7 @@ describe('check command', () => {
|
|
|
290
260
|
it('应该检测 file_modification 触发条件(非 src/ 目录)', async () => {
|
|
291
261
|
mockExecAsync.mockResolvedValue({ stdout: 'docs/bar.md\n', stderr: '' });
|
|
292
262
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
293
|
-
passed: true, ironLaws: [], guidelines: [],
|
|
263
|
+
passed: true, ironLaws: [], guidelines: [], warningCount: 0,
|
|
294
264
|
});
|
|
295
265
|
|
|
296
266
|
await check({ preset: 'default', staged: false });
|
|
@@ -304,9 +274,7 @@ describe('check command', () => {
|
|
|
304
274
|
passed: true,
|
|
305
275
|
ironLaws: [],
|
|
306
276
|
guidelines: [],
|
|
307
|
-
tips: [],
|
|
308
277
|
warningCount: 0,
|
|
309
|
-
tipCount: 0,
|
|
310
278
|
});
|
|
311
279
|
|
|
312
280
|
// Mock filesystem so detectSourceRoots finds src/ as a source root
|
|
@@ -332,9 +300,7 @@ describe('check command', () => {
|
|
|
332
300
|
passed: true,
|
|
333
301
|
ironLaws: [],
|
|
334
302
|
guidelines: [],
|
|
335
|
-
tips: [],
|
|
336
303
|
warningCount: 0,
|
|
337
|
-
tipCount: 0,
|
|
338
304
|
});
|
|
339
305
|
|
|
340
306
|
await check({ preset: 'default', staged: false, trigger: 'code_implementation' });
|
|
@@ -361,9 +327,7 @@ describe('check command', () => {
|
|
|
361
327
|
},
|
|
362
328
|
}],
|
|
363
329
|
guidelines: [],
|
|
364
|
-
tips: [],
|
|
365
330
|
warningCount: 0,
|
|
366
|
-
tipCount: 0,
|
|
367
331
|
});
|
|
368
332
|
|
|
369
333
|
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
@@ -393,43 +357,13 @@ describe('check command', () => {
|
|
|
393
357
|
enforcement: 'warning',
|
|
394
358
|
},
|
|
395
359
|
}],
|
|
396
|
-
tips: [],
|
|
397
360
|
warningCount: 1,
|
|
398
|
-
tipCount: 0,
|
|
399
361
|
});
|
|
400
362
|
|
|
401
363
|
await check({ preset: 'default', staged: false });
|
|
402
364
|
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('prefer_composition'));
|
|
403
365
|
});
|
|
404
366
|
|
|
405
|
-
it('应该显示提示的约束详情', async () => {
|
|
406
|
-
mockExecAsync.mockResolvedValue({ stdout: '', stderr: '' });
|
|
407
|
-
mockChecker.checkConstraints.mockResolvedValue({
|
|
408
|
-
passed: true,
|
|
409
|
-
ironLaws: [],
|
|
410
|
-
guidelines: [],
|
|
411
|
-
tips: [{
|
|
412
|
-
id: 'consider_reuse',
|
|
413
|
-
level: 'tip',
|
|
414
|
-
satisfied: false,
|
|
415
|
-
checkedAt: new Date(),
|
|
416
|
-
constraint: {
|
|
417
|
-
kind: 'check' as const,
|
|
418
|
-
id: 'consider_reuse',
|
|
419
|
-
rule: 'CONSIDER REUSE',
|
|
420
|
-
message: '考虑复用',
|
|
421
|
-
level: 'tip',
|
|
422
|
-
trigger: 'code_implementation',
|
|
423
|
-
enforcement: 'info',
|
|
424
|
-
},
|
|
425
|
-
}],
|
|
426
|
-
warningCount: 0,
|
|
427
|
-
tipCount: 1,
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
await check({ preset: 'default', staged: false });
|
|
431
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('consider_reuse'));
|
|
432
|
-
});
|
|
433
367
|
});
|
|
434
368
|
|
|
435
369
|
describe('getSmartHint (通过 check 间接测试)', () => {
|
|
@@ -439,9 +373,7 @@ describe('check command', () => {
|
|
|
439
373
|
passed: true,
|
|
440
374
|
ironLaws: [],
|
|
441
375
|
guidelines: [],
|
|
442
|
-
tips: [],
|
|
443
376
|
warningCount: 0,
|
|
444
|
-
tipCount: 0,
|
|
445
377
|
});
|
|
446
378
|
});
|
|
447
379
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 命令注册表闭环测试(H5:命令定义即注册 + per-command 懒加载)
|
|
3
|
+
*
|
|
4
|
+
* - 定义表内每个实现引用(module+export)都必须可解析(构建/测试期断言,
|
|
5
|
+
* 取代 commands barrel 的编译期保护——R6 消灭手工同步后的闭环兜底)
|
|
6
|
+
* - definitions 是纯数据模块:require 它不加载任何命令实现(保 --help/--version 懒加载)
|
|
7
|
+
* - 命令面与 CAPABILITIES.md 的 24 顶层命令一致(防误删回归)
|
|
8
|
+
* - bin/harness.js 端到端冒烟(dist 存在时):--version/--help 零命令实现模块,
|
|
9
|
+
* 单命令执行只加载该命令模块(O2 per-command 懒加载)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as os from 'os';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { spawnSync } from 'child_process';
|
|
16
|
+
import { COMMAND_DEFINITIONS, type CommandDefinition, type CommandImplRef } from '../definitions';
|
|
17
|
+
import { GATE_DEFINITIONS } from '../../../gates/definitions';
|
|
18
|
+
|
|
19
|
+
function collectRefs(defs: CommandDefinition[]): CommandImplRef[] {
|
|
20
|
+
const refs: CommandImplRef[] = [];
|
|
21
|
+
const walk = (def: CommandDefinition) => {
|
|
22
|
+
if (def.action) refs.push(def.action);
|
|
23
|
+
if (def.subcommands) {
|
|
24
|
+
for (const entry of Object.values(def.subcommands)) refs.push(entry.impl);
|
|
25
|
+
}
|
|
26
|
+
if (def.optionRoutes) {
|
|
27
|
+
for (const route of def.optionRoutes) refs.push(route.impl);
|
|
28
|
+
}
|
|
29
|
+
for (const child of def.children || []) walk(child);
|
|
30
|
+
};
|
|
31
|
+
for (const def of defs) walk(def);
|
|
32
|
+
return refs;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const EXPECTED_TOP_LEVEL_COMMANDS = [
|
|
36
|
+
'check', 'validate', 'passes-gate', 'init', 'report', 'status', 'spec',
|
|
37
|
+
'sync-docs', 'knowledge', 'sdd', 'failure', 'posteval-plan',
|
|
38
|
+
'update-user-model', 'release', 'analyze-sessions', 'constraints',
|
|
39
|
+
'doc-freshness-check', 'spec-baseline-check',
|
|
40
|
+
// 6 门禁命令(GATE_DEFINITIONS 驱动)
|
|
41
|
+
'acceptance', 'command', 'contract', 'performance', 'review', 'security',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
describe('命令注册表闭环', () => {
|
|
45
|
+
it('definitions 是纯数据模块:加载时不引入任何命令实现', () => {
|
|
46
|
+
const loaded = Object.keys(require.cache)
|
|
47
|
+
.filter(k => k.includes('/cli/commands/') && k.endsWith('.ts') && !k.includes('__tests__'));
|
|
48
|
+
expect(loaded).toEqual([
|
|
49
|
+
expect.stringContaining('/cli/commands/definitions.ts'),
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('全部实现引用可解析(module 存在且 export 是函数)', () => {
|
|
54
|
+
const refs = collectRefs(COMMAND_DEFINITIONS);
|
|
55
|
+
for (const def of GATE_DEFINITIONS) {
|
|
56
|
+
refs.push(def.cli.action);
|
|
57
|
+
for (const entry of Object.values(def.cli.subcommands || {})) refs.push(entry);
|
|
58
|
+
}
|
|
59
|
+
expect(refs.length).toBeGreaterThan(0);
|
|
60
|
+
|
|
61
|
+
for (const ref of refs) {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
63
|
+
const mod = require('../' + ref.module);
|
|
64
|
+
if (typeof mod[ref.export] !== 'function') {
|
|
65
|
+
throw new Error(`实现引用 ${ref.module}.${ref.export} 不存在或不是函数`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('顶层命令面与预期 24 命令一致(含 6 门禁)', () => {
|
|
71
|
+
const names = [
|
|
72
|
+
...COMMAND_DEFINITIONS.map(d => d.command.split(' ')[0]),
|
|
73
|
+
...GATE_DEFINITIONS.map(d => d.cli.command.split(' ')[0]),
|
|
74
|
+
];
|
|
75
|
+
expect(new Set(names).size).toBe(names.length);
|
|
76
|
+
expect(names.sort()).toEqual([...EXPECTED_TOP_LEVEL_COMMANDS].sort());
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('命令定义无重复注册名', () => {
|
|
80
|
+
const names = COMMAND_DEFINITIONS.map(d => d.command);
|
|
81
|
+
expect(new Set(names).size).toBe(names.length);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('全部命令定义的路由构造可执行(mapActionArgs/subcommands.args/optionRoutes.args/afterRun 冒烟)', () => {
|
|
85
|
+
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
86
|
+
try {
|
|
87
|
+
const walk = (def: CommandDefinition) => {
|
|
88
|
+
if (def.mapActionArgs) {
|
|
89
|
+
def.mapActionArgs(['positional'], {});
|
|
90
|
+
def.mapActionArgs([], {});
|
|
91
|
+
}
|
|
92
|
+
for (const entry of Object.values(def.subcommands || {})) {
|
|
93
|
+
if (entry.args) {
|
|
94
|
+
entry.args(['positional'], {});
|
|
95
|
+
entry.args([], {});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const route of def.optionRoutes || []) {
|
|
99
|
+
if (route.args) {
|
|
100
|
+
route.args([], {});
|
|
101
|
+
route.args(['positional'], {});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (def.afterRun) {
|
|
105
|
+
def.afterRun(true, {});
|
|
106
|
+
def.afterRun(false, { check: true });
|
|
107
|
+
}
|
|
108
|
+
for (const child of def.children || []) walk(child);
|
|
109
|
+
};
|
|
110
|
+
for (const def of COMMAND_DEFINITIONS) walk(def);
|
|
111
|
+
// sync-docs --check 失败路径确实会请求退出
|
|
112
|
+
expect(exitSpy).toHaveBeenCalled();
|
|
113
|
+
} finally {
|
|
114
|
+
exitSpy.mockRestore();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const repoRoot = path.join(__dirname, '..', '..', '..', '..');
|
|
120
|
+
const distBin = path.join(repoRoot, 'bin', 'harness.js');
|
|
121
|
+
const distCommands = path.join(repoRoot, 'dist', 'cli', 'commands');
|
|
122
|
+
const hasDist = fs.existsSync(distCommands);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 经 NODE_OPTIONS=--require 预加载探针 spawn bin(进程退出时打印加载的
|
|
126
|
+
* 命令实现模块清单)。不用 node -e(commander 在 -e 下走 eval 分支,
|
|
127
|
+
* argv 解析不同,会误判位置参数)。
|
|
128
|
+
*/
|
|
129
|
+
function runWithModuleProbe(argv: string[]): { status: number | null; implModules: string[]; stdout: string; stderr: string } {
|
|
130
|
+
const probeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-lazy-probe-'));
|
|
131
|
+
const probeFile = path.join(probeDir, 'probe.js');
|
|
132
|
+
fs.writeFileSync(probeFile, [
|
|
133
|
+
`process.on('exit', () => {`,
|
|
134
|
+
` const impls = Object.keys(require.cache).filter(k => /dist[\\\\/]cli[\\\\/]commands[\\\\/]/.test(k) && !/definitions\\.js/.test(k)).sort();`,
|
|
135
|
+
` console.error('IMPL_MODULES=' + impls.join(','));`,
|
|
136
|
+
`});`,
|
|
137
|
+
].join('\n'));
|
|
138
|
+
try {
|
|
139
|
+
const r = spawnSync(process.execPath, [distBin, ...argv], {
|
|
140
|
+
cwd: repoRoot,
|
|
141
|
+
encoding: 'utf-8',
|
|
142
|
+
timeout: 30000,
|
|
143
|
+
env: { ...process.env, NODE_OPTIONS: `--require ${probeFile}` },
|
|
144
|
+
});
|
|
145
|
+
const match = r.stderr.match(/IMPL_MODULES=(.*)/);
|
|
146
|
+
return {
|
|
147
|
+
status: r.status,
|
|
148
|
+
implModules: match && match[1] ? match[1].split(',').filter(Boolean) : [],
|
|
149
|
+
stdout: r.stdout,
|
|
150
|
+
stderr: r.stderr,
|
|
151
|
+
};
|
|
152
|
+
} finally {
|
|
153
|
+
fs.rmSync(probeDir, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const smoke = hasDist ? describe : describe.skip;
|
|
158
|
+
|
|
159
|
+
smoke('bin/harness.js 端到端(dist 存在时)', () => {
|
|
160
|
+
it('--version:退出 0,零命令实现模块加载', () => {
|
|
161
|
+
const r = runWithModuleProbe(['--version']);
|
|
162
|
+
expect(r.status).toBe(0);
|
|
163
|
+
expect(r.implModules).toEqual([]);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('--help:退出 0,零命令实现模块加载', () => {
|
|
167
|
+
const r = runWithModuleProbe(['--help']);
|
|
168
|
+
expect(r.status).toBe(0);
|
|
169
|
+
expect(r.implModules).toEqual([]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('harness constraints --json:只加载 constraints 命令模块', () => {
|
|
173
|
+
const r = runWithModuleProbe(['constraints', '--json']);
|
|
174
|
+
expect(r.status).toBe(0);
|
|
175
|
+
expect(r.implModules).toEqual([
|
|
176
|
+
expect.stringContaining('/dist/cli/commands/constraints.js'),
|
|
177
|
+
]);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('harness check --list:只加载 check 命令模块(optionRoutes)', () => {
|
|
181
|
+
const r = runWithModuleProbe(['check', '--list']);
|
|
182
|
+
expect(r.status).toBe(0);
|
|
183
|
+
expect(r.implModules).toEqual([
|
|
184
|
+
expect.stringContaining('/dist/cli/commands/check.js'),
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('harness acceptance:只加载 acceptance 命令模块(门禁注册表)', () => {
|
|
189
|
+
const r = runWithModuleProbe(['acceptance', '--check-all']);
|
|
190
|
+
expect(r.status).toBe(0);
|
|
191
|
+
expect(r.implModules).toEqual([
|
|
192
|
+
expect.stringContaining('/dist/cli/commands/acceptance.js'),
|
|
193
|
+
]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('未知子命令报错退出(knowledge 兜底行为保留)', () => {
|
|
197
|
+
const r = runWithModuleProbe(['knowledge', 'bogus']);
|
|
198
|
+
expect(r.status).toBe(1);
|
|
199
|
+
expect(r.stderr).toContain('未知子命令: bogus');
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -49,7 +49,6 @@ export async function check(options: CheckOptions): Promise<void> {
|
|
|
49
49
|
const merged = configLoader.hasCustomConfig()
|
|
50
50
|
? configLoader.mergeConstraints()
|
|
51
51
|
: configLoader.mergeConstraints({ preset: options.preset || 'standard' });
|
|
52
|
-
constraintChecker.setCustomConfig(merged);
|
|
53
52
|
if (merged.custom.length > 0) {
|
|
54
53
|
console.log(chalk.gray(`自定义约束: ${merged.custom.length} 条`));
|
|
55
54
|
}
|
|
@@ -73,8 +72,8 @@ export async function check(options: CheckOptions): Promise<void> {
|
|
|
73
72
|
}
|
|
74
73
|
console.log(chalk.gray(`触发条件: ${[context.operation, ...(context.extraTriggers ?? [])].join(', ')}`));
|
|
75
74
|
|
|
76
|
-
//
|
|
77
|
-
const result = await constraintChecker.checkConstraints(context);
|
|
75
|
+
// 执行三层检查(per-request 传 customConfig,避免单例状态污染)
|
|
76
|
+
const result = await constraintChecker.checkConstraints(context, merged);
|
|
78
77
|
|
|
79
78
|
// 输出结果
|
|
80
79
|
console.log();
|
|
@@ -83,7 +82,6 @@ export async function check(options: CheckOptions): Promise<void> {
|
|
|
83
82
|
const skippedResults = [
|
|
84
83
|
...result.ironLaws,
|
|
85
84
|
...result.guidelines,
|
|
86
|
-
...result.tips,
|
|
87
85
|
].filter(r => r.skipped);
|
|
88
86
|
|
|
89
87
|
// Iron Laws
|
|
@@ -118,16 +116,6 @@ export async function check(options: CheckOptions): Promise<void> {
|
|
|
118
116
|
console.log(chalk.green(`✅ 指导原则: ${passedGuidelines}/${evaluatedGuidelines.length} 通过`));
|
|
119
117
|
}
|
|
120
118
|
|
|
121
|
-
// Tips
|
|
122
|
-
if (result.tipCount > 0) {
|
|
123
|
-
console.log(chalk.blue(`💡 提示: ${result.tipCount} 条`));
|
|
124
|
-
result.tips.filter(r => !r.satisfied).forEach(r => {
|
|
125
|
-
if (r.constraint) {
|
|
126
|
-
console.log(chalk.blue(` - ${r.constraint.id}: ${r.constraint.message}`));
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
119
|
// Skipped:约定未采用 / 证据未接线,未评估(不计通过/失败)
|
|
132
120
|
if (skippedResults.length > 0) {
|
|
133
121
|
console.log(chalk.gray(`⏭️ 跳过评估: ${skippedResults.length} 条(约定未采用或证据未接线,不计通过/失败)`));
|