@dommaker/harness 1.8.1 → 1.10.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 +16 -0
- package/README.md +10 -13
- package/dist/cli/commands/check.d.ts +3 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +41 -78
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -10
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +4 -57
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +9 -23
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +39 -121
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +3 -8
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +8 -23
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/definitions.js +2 -2
- package/dist/cli/commands/init.d.ts +0 -32
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -149
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +14 -17
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +0 -2
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
- package/dist/cli/commands/scaffold-templates.js +2 -37
- package/dist/cli/commands/scaffold-templates.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +0 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +0 -10
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/commands/status.js +10 -10
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts +0 -2
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +4 -23
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +22 -0
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +113 -8
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +36 -4
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +16 -4
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +15 -24
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +33 -72
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +4 -3
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +1 -1
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +4 -8
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.js +4 -5
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +1 -1
- package/dist/core/constraints/checkers/types.js +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +9 -25
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.js +10 -81
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +7 -9
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +13 -16
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/index.d.ts +2 -5
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +3 -13
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +10 -38
- package/dist/core/constraints/injection-writer.d.ts.map +1 -1
- package/dist/core/constraints/injection-writer.js +17 -54
- package/dist/core/constraints/injection-writer.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +1 -10
- package/dist/core/constraints/run-env.d.ts.map +1 -1
- package/dist/core/constraints/run-env.js +1 -22
- package/dist/core/constraints/run-env.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +5 -7
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +5 -10
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/effective-constraints.d.ts +12 -22
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +13 -30
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -9
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +4 -42
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +16 -125
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +2 -2
- package/dist/core/validators/passes-gate.js +6 -6
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +9 -12
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +9 -31
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/index.d.ts +5 -7
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -10
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +9 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -23
- package/dist/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +11 -12
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +4 -4
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +8 -8
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/standard.d.ts +8 -10
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +10 -16
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +24 -35
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +8 -7
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +5 -5
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +8 -66
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/project-config.js +3 -2
- package/dist/types/project-config.js.map +1 -1
- package/dist/types/trace.d.ts +8 -10
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/CONTEXT.md +3 -3
- package/src/__tests__/checker-extra.test.ts +37 -61
- package/src/__tests__/checker.test.ts +10 -115
- package/src/__tests__/effective-constraints.test.ts +18 -83
- package/src/__tests__/iron-laws.test.ts +71 -90
- package/src/__tests__/passes-gate-check.test.ts +2 -2
- package/src/__tests__/project-config-loader-extra.test.ts +22 -123
- package/src/__tests__/project-config-loader.test.ts +5 -33
- package/src/__tests__/public-exports.test.ts +31 -11
- package/src/__tests__/public-type-surface.test.ts +2 -13
- package/src/__tests__/public-value-surface.test.ts +1 -7
- package/src/__tests__/skip-semantics.test.ts +21 -37
- package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
- package/src/__tests__/trace-analyzer.test.ts +21 -21
- package/src/__tests__/trace-collector.test.ts +22 -22
- package/src/__tests__/traces.test.ts +27 -28
- package/src/cli/commands/CONTEXT.md +12 -9
- package/src/cli/commands/__tests__/check-read-count.test.ts +4 -15
- package/src/cli/commands/__tests__/check-skip-output.test.ts +14 -14
- package/src/cli/commands/__tests__/check.test.ts +14 -31
- package/src/cli/commands/__tests__/constraints-report.test.ts +12 -80
- package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
- package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
- package/src/cli/commands/__tests__/init-ondisk.test.ts +12 -20
- package/src/cli/commands/__tests__/init.test.ts +0 -11
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
- package/src/cli/commands/__tests__/registry.test.ts +39 -3
- package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
- package/src/cli/commands/__tests__/report.test.ts +1 -1
- package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
- package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
- package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
- package/src/cli/commands/__tests__/status.test.ts +12 -12
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
- package/src/cli/commands/__tests__/sync-docs-table-layout.test.ts +259 -0
- package/src/cli/commands/check.ts +43 -82
- package/src/cli/commands/constraints-report.ts +4 -59
- package/src/cli/commands/constraints-retire.ts +49 -154
- package/src/cli/commands/constraints.ts +11 -26
- package/src/cli/commands/definitions.ts +2 -2
- package/src/cli/commands/init.ts +8 -179
- package/src/cli/commands/report.ts +19 -23
- package/src/cli/commands/scaffold-templates.ts +1 -37
- package/src/cli/commands/scaffold.ts +0 -11
- package/src/cli/commands/status.ts +10 -10
- package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +139 -9
- package/src/cli/commands/sync-docs/index.ts +41 -4
- package/src/core/CONTEXT.md +9 -9
- package/src/core/constraints/__tests__/capability-counts.test.ts +5 -5
- package/src/core/constraints/__tests__/collect-constraints.test.ts +13 -8
- package/src/core/constraints/__tests__/git-evidence.test.ts +4 -7
- package/src/core/constraints/__tests__/injection-writer.test.ts +8 -61
- package/src/core/constraints/__tests__/run-env.test.ts +5 -34
- package/src/core/constraints/__tests__/trace-injection.test.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +17 -5
- package/src/core/constraints/checker.ts +39 -88
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +5 -8
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +1 -53
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +5 -25
- package/src/core/constraints/checkers/docs-freshness.ts +1 -1
- package/src/core/constraints/checkers/governance-presence.ts +4 -3
- package/src/core/constraints/checkers/index.ts +4 -10
- package/src/core/constraints/checkers/iron-flags.ts +3 -12
- package/src/core/constraints/checkers/no-test-simplification.ts +1 -1
- package/src/core/constraints/checkers/types.ts +1 -1
- package/src/core/constraints/definitions/guidelines.ts +8 -25
- package/src/core/constraints/definitions/iron-laws.ts +9 -82
- package/src/core/constraints/definitions.ts +14 -15
- package/src/core/constraints/index.ts +4 -19
- package/src/core/constraints/injection-writer.ts +16 -61
- package/src/core/constraints/run-env.ts +1 -33
- package/src/core/constraints/usage-report.ts +8 -15
- package/src/core/effective-constraints.ts +14 -45
- package/src/core/index.ts +3 -10
- package/src/core/project-config-loader.ts +20 -145
- package/src/core/validators/passes-gate.ts +6 -6
- package/src/failure/CONTEXT.md +1 -1
- package/src/hooks/CONTEXT.md +12 -22
- package/src/hooks/__tests__/bootstrap.test.ts +24 -75
- package/src/hooks/bootstrap.ts +9 -48
- package/src/hooks/index.ts +5 -20
- package/src/index.ts +9 -31
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
- package/src/monitoring/__tests__/traces.test.ts +22 -22
- package/src/monitoring/trace-analyzer.ts +12 -13
- package/src/monitoring/traces.ts +8 -8
- package/src/presets/__tests__/standard.test.ts +12 -16
- package/src/presets/standard.ts +14 -23
- package/src/types/__tests__/types.test.ts +3 -3
- package/src/types/constraint.ts +26 -40
- package/src/types/passes-gate.ts +5 -5
- package/src/types/project-config.ts +8 -77
- package/src/types/trace.ts +8 -11
- package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
- package/dist/core/constraints/agent-prompt-renderer.js +0 -55
- package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
- package/dist/core/constraints/definitions/prompts.d.ts +0 -12
- package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
- package/dist/core/constraints/definitions/prompts.js +0 -400
- package/dist/core/constraints/definitions/prompts.js.map +0 -1
- package/dist/core/constraints/injection-drift.d.ts +0 -61
- package/dist/core/constraints/injection-drift.d.ts.map +0 -1
- package/dist/core/constraints/injection-drift.js +0 -131
- package/dist/core/constraints/injection-drift.js.map +0 -1
- package/dist/core/constraints/injection-renderer.d.ts +0 -23
- package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
- package/dist/core/constraints/injection-renderer.js +0 -44
- package/dist/core/constraints/injection-renderer.js.map +0 -1
- package/dist/hooks/config.d.ts +0 -30
- package/dist/hooks/config.d.ts.map +0 -1
- package/dist/hooks/config.js +0 -34
- package/dist/hooks/config.js.map +0 -1
- package/dist/hooks/pipeline.d.ts +0 -54
- package/dist/hooks/pipeline.d.ts.map +0 -1
- package/dist/hooks/pipeline.js +0 -173
- package/dist/hooks/pipeline.js.map +0 -1
- package/dist/hooks/registry.d.ts +0 -70
- package/dist/hooks/registry.d.ts.map +0 -1
- package/dist/hooks/registry.js +0 -141
- package/dist/hooks/registry.js.map +0 -1
- package/dist/hooks/types.d.ts +0 -113
- package/dist/hooks/types.d.ts.map +0 -1
- package/dist/hooks/types.js +0 -9
- package/dist/hooks/types.js.map +0 -1
- package/src/__tests__/hooks-pipeline.test.ts +0 -201
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
- package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
- package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
- package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
- package/src/core/constraints/agent-prompt-renderer.ts +0 -67
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
- package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
- package/src/core/constraints/definitions/prompts.ts +0 -414
- package/src/core/constraints/injection-drift.ts +0 -142
- package/src/core/constraints/injection-renderer.ts +0 -44
- package/src/hooks/__tests__/config.test.ts +0 -19
- package/src/hooks/__tests__/pipeline.test.ts +0 -195
- package/src/hooks/__tests__/registry.test.ts +0 -115
- package/src/hooks/config.ts +0 -33
- package/src/hooks/pipeline.ts +0 -202
- package/src/hooks/registry.ts +0 -166
- package/src/hooks/types.ts +0 -120
package/dist/hooks/registry.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* HookRegistry — hook 注册/注销/查询
|
|
4
|
-
*
|
|
5
|
-
* 纯簿记,无执行逻辑。线程不安全(Node.js 单线程)。
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.HookRegistry = void 0;
|
|
9
|
-
exports.assertHookRegistryClosed = assertHookRegistryClosed;
|
|
10
|
-
/**
|
|
11
|
-
* 注册表闭环双向校验(H5,复制 checker 闭环模式)
|
|
12
|
-
*
|
|
13
|
-
* 声明(HookConfig,consumer 的 per-hook 配置表)↔ 实现(注册的
|
|
14
|
-
* HookDefinition)加载期双向校验,任一不满足即抛错,拒绝静默缺失:
|
|
15
|
-
* - 引用未注册 → 抛错(声明了配置但没有注册实现 = 死配置)
|
|
16
|
-
* - 注册无定义 → 抛错(注册了实现但没有配置声明 = 死代码)
|
|
17
|
-
* - 重复声明 / 重复注册 → 抛错
|
|
18
|
-
*
|
|
19
|
-
* 断言限构建/测试期调用(harness 无内置 hook 定义,加载期闭环由
|
|
20
|
-
* consumer 在其注册点/测试中调用本函数),不进入运行时热路径,
|
|
21
|
-
* 不破坏 --help/--version 懒加载。消灭「hook 定义不注册 = 死代码」
|
|
22
|
-
* 类 prompt 规矩(studio 侧随动)。
|
|
23
|
-
*
|
|
24
|
-
* @throws 任一方向不满足时抛错
|
|
25
|
-
*/
|
|
26
|
-
function assertHookRegistryClosed(configs, hooks) {
|
|
27
|
-
const configNames = new Set();
|
|
28
|
-
for (const config of configs) {
|
|
29
|
-
if (configNames.has(config.name)) {
|
|
30
|
-
throw new Error(`[harness] hook 注册表闭环校验失败:hook 配置 "${config.name}" 重复声明。`);
|
|
31
|
-
}
|
|
32
|
-
configNames.add(config.name);
|
|
33
|
-
}
|
|
34
|
-
const hookNames = new Set();
|
|
35
|
-
for (const hook of hooks) {
|
|
36
|
-
if (hookNames.has(hook.name)) {
|
|
37
|
-
throw new Error(`[harness] hook 注册表闭环校验失败:hook "${hook.name}" 重复注册。`);
|
|
38
|
-
}
|
|
39
|
-
hookNames.add(hook.name);
|
|
40
|
-
}
|
|
41
|
-
for (const config of configs) {
|
|
42
|
-
if (!hookNames.has(config.name)) {
|
|
43
|
-
throw new Error(`[harness] hook 注册表闭环校验失败:hook 配置 "${config.name}" 引用的实现未注册。` +
|
|
44
|
-
`请注册同名 HookDefinition,或删除该配置声明。`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
for (const hook of hooks) {
|
|
48
|
-
if (!configNames.has(hook.name)) {
|
|
49
|
-
throw new Error(`[harness] hook 注册表闭环校验失败:hook "${hook.name}" 注册了实现但没有对应配置声明。` +
|
|
50
|
-
`请补齐 HookConfig 声明,或移除该实现。`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
class HookRegistry {
|
|
55
|
-
hooks = new Map();
|
|
56
|
-
/**
|
|
57
|
-
* 注册 hook(同名覆盖)
|
|
58
|
-
*
|
|
59
|
-
* 有效 `enabled` / `errorStrategy` 由 HookConfig 填充——配置表是这两个
|
|
60
|
-
* 语义的唯一声明点(#159),定义与配置名称不一致即抛错。
|
|
61
|
-
*/
|
|
62
|
-
register(hook, config) {
|
|
63
|
-
if (hook.name !== config.name) {
|
|
64
|
-
throw new Error(`[harness] hook 注册失败:定义 "${hook.name}" 与配置 "${config.name}" 名称不一致。`);
|
|
65
|
-
}
|
|
66
|
-
this.hooks.set(hook.name, {
|
|
67
|
-
...hook,
|
|
68
|
-
enabled: config.enabled,
|
|
69
|
-
errorStrategy: config.errorStrategy,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* 批量注册(按 name 配对配置;有实现缺配置声明即抛错)
|
|
74
|
-
*/
|
|
75
|
-
registerAll(hooks, configs) {
|
|
76
|
-
const configByName = new Map(configs.map(c => [c.name, c]));
|
|
77
|
-
for (const hook of hooks) {
|
|
78
|
-
const config = configByName.get(hook.name);
|
|
79
|
-
if (!config) {
|
|
80
|
-
throw new Error(`[harness] hook 注册失败:hook "${hook.name}" 没有对应的 HookConfig 声明。`);
|
|
81
|
-
}
|
|
82
|
-
this.register(hook, config);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* 注销 hook
|
|
87
|
-
*/
|
|
88
|
-
unregister(name) {
|
|
89
|
-
return this.hooks.delete(name);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* 获取单个 hook(含有效 enabled / errorStrategy)
|
|
93
|
-
*/
|
|
94
|
-
get(name) {
|
|
95
|
-
return this.hooks.get(name);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* 获取指定时机、已启用的 hook,按优先级排序
|
|
99
|
-
*/
|
|
100
|
-
getEnabled(phase) {
|
|
101
|
-
return Array.from(this.hooks.values())
|
|
102
|
-
.filter(h => h.phase === phase && h.enabled)
|
|
103
|
-
.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* 列出所有 hook 名称
|
|
107
|
-
*/
|
|
108
|
-
listNames() {
|
|
109
|
-
return Array.from(this.hooks.keys());
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* 列出所有 hook(含禁用)
|
|
113
|
-
*/
|
|
114
|
-
listAll() {
|
|
115
|
-
return Array.from(this.hooks.values());
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* 启用/禁用 hook
|
|
119
|
-
*/
|
|
120
|
-
setEnabled(name, enabled) {
|
|
121
|
-
const hook = this.hooks.get(name);
|
|
122
|
-
if (!hook)
|
|
123
|
-
return false;
|
|
124
|
-
hook.enabled = enabled;
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* 清空所有 hook
|
|
129
|
-
*/
|
|
130
|
-
clear() {
|
|
131
|
-
this.hooks.clear();
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Hook 数量
|
|
135
|
-
*/
|
|
136
|
-
get size() {
|
|
137
|
-
return this.hooks.size;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
exports.HookRegistry = HookRegistry;
|
|
141
|
-
//# sourceMappingURL=registry.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hooks/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks/types.d.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hooks 类型定义
|
|
3
|
-
*
|
|
4
|
-
* 通用 hook 管线:注册、排序、错误隔离、采样。
|
|
5
|
-
* 无业务逻辑,consumer 自行定义 hook 名称和语义。
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Hook 执行时机
|
|
9
|
-
*/
|
|
10
|
-
export type HookPhase = 'before' | 'after' | 'around';
|
|
11
|
-
/**
|
|
12
|
-
* Hook 错误处理策略(有效集合:'block' | 'warn')
|
|
13
|
-
*
|
|
14
|
-
* #159 起原 'ignore' 退出有效面:策略的唯一声明点是 HookConfig(必填),
|
|
15
|
-
* 不存在「未声明 strategy」的运行时形态。
|
|
16
|
-
*/
|
|
17
|
-
export type HookErrorStrategy = 'block' | 'warn';
|
|
18
|
-
/**
|
|
19
|
-
* Hook 配置(G7 归一后形状:consumer 侧的 per-hook 声明表)
|
|
20
|
-
*
|
|
21
|
-
* 声明与实现构成注册表闭环(assertHookRegistryClosed 双向校验):
|
|
22
|
-
* - 声明(HookConfig)是「定义」侧:引用未注册实现 → 抛错
|
|
23
|
-
* - 注册(HookDefinition)是「实现」侧:注册无对应声明 → 抛错
|
|
24
|
-
*
|
|
25
|
-
* #159 起本表是 `enabled` / `errorStrategy` 的**唯一声明点**:
|
|
26
|
-
* HookDefinition 不再携带这两个字段,有效值在注册环节由本表填充
|
|
27
|
-
* (见 EffectiveHook),声明与实现两处值矛盾在构造上不可能出现。
|
|
28
|
-
*
|
|
29
|
-
* 对应 studio 侧 per-hook 运行时配置(hooks/config.ts 的 DEFAULTS)语义:
|
|
30
|
-
* blocking:true ↔ errorStrategy 'block'(失败阻断管线),
|
|
31
|
-
* blocking:false ↔ errorStrategy 'warn'(记录警告继续)——无损映射见
|
|
32
|
-
* config.ts 的 toErrorStrategy。
|
|
33
|
-
*/
|
|
34
|
-
export interface HookConfig {
|
|
35
|
-
/** Hook 唯一名称(须与注册的 HookDefinition.name 一致) */
|
|
36
|
-
name: string;
|
|
37
|
-
/** 是否启用(false 时不进入管线) */
|
|
38
|
-
enabled: boolean;
|
|
39
|
-
/** 错误策略:block=失败阻断管线,warn=记录警告继续 */
|
|
40
|
-
errorStrategy: HookErrorStrategy;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Hook 定义
|
|
44
|
-
*
|
|
45
|
-
* Consumer 自行选择 name(如 'beforeAgentExecute'),harness 只提供管线。
|
|
46
|
-
* `enabled` / `errorStrategy` 不在此处声明——唯一声明点是 HookConfig(#159)。
|
|
47
|
-
*/
|
|
48
|
-
export interface HookDefinition<C = unknown, R = unknown> {
|
|
49
|
-
/** Hook 唯一名称(consumer 定义语义) */
|
|
50
|
-
name: string;
|
|
51
|
-
/** 执行时机 */
|
|
52
|
-
phase: HookPhase;
|
|
53
|
-
/** 优先级(越小越先执行,默认 100) */
|
|
54
|
-
priority?: number;
|
|
55
|
-
/** 采样率 0-1(1=100% 执行,0.1=10% 采样) */
|
|
56
|
-
sampleRate?: number;
|
|
57
|
-
/** Hook 执行函数 */
|
|
58
|
-
execute: (context: C) => Promise<HookResult<R>>;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* 有效 hook = HookDefinition + 注册环节由 HookConfig 填充的有效值
|
|
62
|
-
*
|
|
63
|
-
* 管线与注册表的判定(启用过滤、block/warn 分派)只读本类型,
|
|
64
|
-
* 值来源单一(HookConfig),不存在第二声明点。
|
|
65
|
-
*/
|
|
66
|
-
export interface EffectiveHook<C = unknown, R = unknown> extends HookDefinition<C, R> {
|
|
67
|
-
/** 是否启用(来自 HookConfig.enabled) */
|
|
68
|
-
enabled: boolean;
|
|
69
|
-
/** 错误策略(来自 HookConfig.errorStrategy) */
|
|
70
|
-
errorStrategy: HookErrorStrategy;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Hook 执行结果
|
|
74
|
-
*/
|
|
75
|
-
export interface HookResult<R = unknown> {
|
|
76
|
-
/** 是否通过(blocking hook 必须返回 false 才阻断) */
|
|
77
|
-
passed: boolean;
|
|
78
|
-
/** 结果数据 */
|
|
79
|
-
data?: R;
|
|
80
|
-
/** 错误信息 */
|
|
81
|
-
error?: string;
|
|
82
|
-
/** 元数据(consumer 自行定义) */
|
|
83
|
-
metadata?: Record<string, unknown>;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Hook 执行记录
|
|
87
|
-
*/
|
|
88
|
-
export interface HookExecutionRecord {
|
|
89
|
-
hookName: string;
|
|
90
|
-
phase: HookPhase;
|
|
91
|
-
startedAt: number;
|
|
92
|
-
completedAt: number;
|
|
93
|
-
durationMs: number;
|
|
94
|
-
passed: boolean;
|
|
95
|
-
error?: string;
|
|
96
|
-
sampled?: boolean;
|
|
97
|
-
/** 未执行标记之二:enabled:false 被 runOne 跳过(实现体零调用,镜像 sampled 先例) */
|
|
98
|
-
skipped?: boolean;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* 管线执行结果
|
|
102
|
-
*/
|
|
103
|
-
export interface PipelineResult {
|
|
104
|
-
/** 是否全部通过(blocking hook 任一失败 = false) */
|
|
105
|
-
passed: boolean;
|
|
106
|
-
/** 各 hook 执行记录 */
|
|
107
|
-
records: HookExecutionRecord[];
|
|
108
|
-
/** 失败的 blocking hook 名称列表 */
|
|
109
|
-
blockedBy: string[];
|
|
110
|
-
/** 警告 hook 名称列表 */
|
|
111
|
-
warnings: string[];
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/hooks/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,aAAa,EAAE,iBAAiB,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACtD,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;CACjD;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,CAAE,SAAQ,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;IACnF,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,aAAa,EAAE,iBAAiB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW;IACX,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAC;IAChB,kBAAkB;IAClB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,6BAA6B;IAC7B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,mBAAmB;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB"}
|
package/dist/hooks/types.js
DELETED
package/dist/hooks/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/hooks/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HookRegistry + HookPipeline 测试(Phase 1)
|
|
3
|
-
*
|
|
4
|
-
* #159 起 `enabled` / `errorStrategy` 由 HookConfig 唯一声明,
|
|
5
|
-
* 注册时以配置填充有效值;HookDefinition 不再携带这两个字段。
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect, beforeEach } from '@jest/globals';
|
|
8
|
-
import { HookRegistry } from '../hooks/registry';
|
|
9
|
-
import { HookPipeline } from '../hooks/pipeline';
|
|
10
|
-
import type { HookConfig, HookDefinition } from '../hooks/types';
|
|
11
|
-
|
|
12
|
-
interface TestContext {
|
|
13
|
-
value: string;
|
|
14
|
-
calls: string[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function makeHook(
|
|
18
|
-
name: string,
|
|
19
|
-
opts: Partial<HookDefinition<TestContext, unknown>> = {}
|
|
20
|
-
): HookDefinition<TestContext, unknown> {
|
|
21
|
-
return {
|
|
22
|
-
name,
|
|
23
|
-
phase: 'before',
|
|
24
|
-
priority: 100,
|
|
25
|
-
execute: async (ctx) => {
|
|
26
|
-
ctx.calls.push(name);
|
|
27
|
-
return { passed: true };
|
|
28
|
-
},
|
|
29
|
-
...opts,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function makeConfig(name: string, overrides: Partial<HookConfig> = {}): HookConfig {
|
|
34
|
-
return { name, enabled: true, errorStrategy: 'block', ...overrides };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
describe('HookRegistry', () => {
|
|
38
|
-
let registry: HookRegistry<TestContext, unknown>;
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
registry = new HookRegistry();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('应注册 hook', () => {
|
|
45
|
-
registry.register(makeHook('test'), makeConfig('test'));
|
|
46
|
-
expect(registry.get('test')).toBeDefined();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('同名注册应覆盖', () => {
|
|
50
|
-
registry.register(makeHook('test', { priority: 50 }), makeConfig('test'));
|
|
51
|
-
registry.register(makeHook('test', { priority: 90 }), makeConfig('test'));
|
|
52
|
-
expect(registry.get('test')?.priority).toBe(90);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('应获取已启用的 hook 按优先级排序', () => {
|
|
56
|
-
registry.register(makeHook('low', { priority: 200 }), makeConfig('low'));
|
|
57
|
-
registry.register(makeHook('high', { priority: 10 }), makeConfig('high'));
|
|
58
|
-
registry.register(makeHook('mid', { priority: 100 }), makeConfig('mid'));
|
|
59
|
-
registry.register(makeHook('disabled'), makeConfig('disabled', { enabled: false }));
|
|
60
|
-
|
|
61
|
-
const enabled = registry.getEnabled('before');
|
|
62
|
-
expect(enabled.map(h => h.name)).toEqual(['high', 'mid', 'low']);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('应注销 hook', () => {
|
|
66
|
-
registry.register(makeHook('test'), makeConfig('test'));
|
|
67
|
-
expect(registry.unregister('test')).toBe(true);
|
|
68
|
-
expect(registry.get('test')).toBeUndefined();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('应设置启用/禁用', () => {
|
|
72
|
-
registry.register(makeHook('test'), makeConfig('test'));
|
|
73
|
-
expect(registry.setEnabled('test', false)).toBe(true);
|
|
74
|
-
expect(registry.getEnabled('before').length).toBe(0);
|
|
75
|
-
expect(registry.setEnabled('test', true)).toBe(true);
|
|
76
|
-
expect(registry.getEnabled('before').length).toBe(1);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('应正确统计数量', () => {
|
|
80
|
-
expect(registry.size).toBe(0);
|
|
81
|
-
registry.register(makeHook('a'), makeConfig('a'));
|
|
82
|
-
registry.register(makeHook('b'), makeConfig('b'));
|
|
83
|
-
expect(registry.size).toBe(2);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('HookPipeline', () => {
|
|
88
|
-
let registry: HookRegistry<TestContext, unknown>;
|
|
89
|
-
let pipeline: HookPipeline<TestContext>;
|
|
90
|
-
|
|
91
|
-
beforeEach(() => {
|
|
92
|
-
registry = new HookRegistry();
|
|
93
|
-
pipeline = new HookPipeline(registry);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('应执行所有 before hook', async () => {
|
|
97
|
-
const ctx: TestContext = { value: '', calls: [] };
|
|
98
|
-
registry.register(makeHook('h1'), makeConfig('h1'));
|
|
99
|
-
registry.register(makeHook('h2'), makeConfig('h2'));
|
|
100
|
-
|
|
101
|
-
const result = await pipeline.run('before', ctx);
|
|
102
|
-
|
|
103
|
-
expect(result.passed).toBe(true);
|
|
104
|
-
expect(ctx.calls).toEqual(['h1', 'h2']);
|
|
105
|
-
expect(result.records.length).toBe(2);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('blocking hook 失败应停止后续', async () => {
|
|
109
|
-
const ctx: TestContext = { value: '', calls: [] };
|
|
110
|
-
registry.register(makeHook('pass1'), makeConfig('pass1'));
|
|
111
|
-
registry.register(makeHook('block', {
|
|
112
|
-
execute: async () => ({ passed: false, error: 'blocked' }),
|
|
113
|
-
}), makeConfig('block', { errorStrategy: 'block' }));
|
|
114
|
-
registry.register(makeHook('never_runs'), makeConfig('never_runs'));
|
|
115
|
-
|
|
116
|
-
const result = await pipeline.run('before', ctx);
|
|
117
|
-
|
|
118
|
-
expect(result.passed).toBe(false);
|
|
119
|
-
expect(result.blockedBy).toEqual(['block']);
|
|
120
|
-
// block hook 的 execute 被覆盖,不 push calls
|
|
121
|
-
expect(ctx.calls).toEqual(['pass1']);
|
|
122
|
-
expect(ctx.calls).not.toContain('never_runs');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('warn hook 失败应继续但记录警告', async () => {
|
|
126
|
-
const ctx: TestContext = { value: '', calls: [] };
|
|
127
|
-
registry.register(makeHook('warn_hook', {
|
|
128
|
-
execute: async () => ({ passed: false, error: 'warning' }),
|
|
129
|
-
}), makeConfig('warn_hook', { errorStrategy: 'warn' }));
|
|
130
|
-
registry.register(makeHook('should_still_run'), makeConfig('should_still_run'));
|
|
131
|
-
|
|
132
|
-
const result = await pipeline.run('before', ctx);
|
|
133
|
-
|
|
134
|
-
expect(result.passed).toBe(true);
|
|
135
|
-
expect(result.warnings).toEqual(['warn_hook']);
|
|
136
|
-
expect(ctx.calls).toContain('should_still_run');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('hook 抛异常应被捕获', async () => {
|
|
140
|
-
const ctx: TestContext = { value: '', calls: [] };
|
|
141
|
-
registry.register(makeHook('throws', {
|
|
142
|
-
execute: async () => { throw new Error('crash'); },
|
|
143
|
-
}), makeConfig('throws', { errorStrategy: 'warn' }));
|
|
144
|
-
registry.register(makeHook('after_crash', { priority: 200 }), makeConfig('after_crash'));
|
|
145
|
-
|
|
146
|
-
const result = await pipeline.run('before', ctx);
|
|
147
|
-
|
|
148
|
-
expect(result.records[0].error).toContain('crash');
|
|
149
|
-
expect(ctx.calls).toContain('after_crash');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('runFull 应在 before/after 之间执行操作', async () => {
|
|
153
|
-
const ctx: TestContext = { value: '', calls: [] };
|
|
154
|
-
registry.register(makeHook('before_hook'), makeConfig('before_hook'));
|
|
155
|
-
|
|
156
|
-
const afterHook = makeHook('after_hook', { phase: 'after' });
|
|
157
|
-
registry.register(afterHook, makeConfig('after_hook'));
|
|
158
|
-
|
|
159
|
-
const { pipelineResult, operationResult } = await pipeline.runFull(ctx, async () => {
|
|
160
|
-
ctx.calls.push('operation');
|
|
161
|
-
return 'done';
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
expect(pipelineResult.passed).toBe(true);
|
|
165
|
-
expect(operationResult).toBe('done');
|
|
166
|
-
expect(ctx.calls).toEqual(['before_hook', 'operation', 'after_hook']);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('before hook 失败不应执行操作', async () => {
|
|
170
|
-
const ctx: TestContext = { value: '', calls: [] };
|
|
171
|
-
registry.register(makeHook('block', {
|
|
172
|
-
execute: async () => ({ passed: false }),
|
|
173
|
-
}), makeConfig('block', { errorStrategy: 'block' }));
|
|
174
|
-
|
|
175
|
-
const { pipelineResult, operationResult } = await pipeline.runFull(ctx, async () => {
|
|
176
|
-
ctx.calls.push('should_not_run');
|
|
177
|
-
return 'nope';
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
expect(pipelineResult.passed).toBe(false);
|
|
181
|
-
expect(operationResult).toBeUndefined();
|
|
182
|
-
expect(ctx.calls).not.toContain('should_not_run');
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('空注册表应返回通过', async () => {
|
|
186
|
-
const result = await pipeline.run('before', { value: '', calls: [] });
|
|
187
|
-
expect(result.passed).toBe(true);
|
|
188
|
-
expect(result.records).toEqual([]);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('应忽略配置禁用的 hook', async () => {
|
|
192
|
-
const ctx: TestContext = { value: '', calls: [] };
|
|
193
|
-
registry.register(makeHook('enabled'), makeConfig('enabled'));
|
|
194
|
-
registry.register(makeHook('disabled'), makeConfig('disabled', { enabled: false }));
|
|
195
|
-
|
|
196
|
-
await pipeline.run('before', ctx);
|
|
197
|
-
|
|
198
|
-
expect(ctx.calls).toEqual(['enabled']);
|
|
199
|
-
expect(ctx.calls).not.toContain('disabled');
|
|
200
|
-
});
|
|
201
|
-
});
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* check 命令注入漂移警告测试(ADR-0001 决策 7,P6)
|
|
3
|
-
*
|
|
4
|
-
* 警告不阻断:漂移项目跑 check 仍 0 退出;版本漂移单独显眼警告;
|
|
5
|
-
* 无漂移零输出。使用真实临时目录(漂移检测读真实 fs),
|
|
6
|
-
* 仅 mock checker(直接判过)与 git 调用。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as fs from 'fs';
|
|
10
|
-
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
11
|
-
import * as path from 'path';
|
|
12
|
-
import { check } from '../check';
|
|
13
|
-
import { renderConstraintsSection } from '../../../core/constraints/injection-renderer';
|
|
14
|
-
import { getEffectiveConstraints } from '../../../core/effective-constraints';
|
|
15
|
-
import { createProjectFixture } from '../../../test-setup/project-fixture';
|
|
16
|
-
|
|
17
|
-
// 命令 per-run 构造 checker(harness#88):构造替身即控制 checkConstraints 返回值
|
|
18
|
-
const mockChecker = { checkConstraints: jest.fn() };
|
|
19
|
-
jest.mock('../../../core/constraints/checker', () => ({
|
|
20
|
-
ConstraintChecker: jest.fn(function () {
|
|
21
|
-
return mockChecker;
|
|
22
|
-
}),
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
|
-
// 命令 per-run 锚根构造收集器(harness#139):构造替身即拦截 trace 落盘
|
|
26
|
-
jest.mock('../../../monitoring/traces', () => ({
|
|
27
|
-
TraceCollector: jest.fn(function () {
|
|
28
|
-
return { record: jest.fn() };
|
|
29
|
-
}),
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
jest.mock('../../../utils/exec', () => ({
|
|
33
|
-
execAsync: jest.fn().mockResolvedValue({ stdout: '', stderr: '' }),
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
jest.mock('child_process', () => ({
|
|
37
|
-
exec: jest.fn(),
|
|
38
|
-
execSync: jest.fn(() => Buffer.from('')),
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
42
|
-
const REAL_VERSION = require('../../../../package.json').version as string;
|
|
43
|
-
|
|
44
|
-
function writeClaudeMd(root: string, version: string): void {
|
|
45
|
-
const section =
|
|
46
|
-
'## Governance Rules\n' + renderConstraintsSection(getEffectiveConstraints(root), version);
|
|
47
|
-
fs.writeFileSync(path.join(root, 'CLAUDE.md'), `# Test Project\n\n${section}`, 'utf-8');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
describe('check 命令注入漂移警告', () => {
|
|
51
|
-
let io: CapturingIO;
|
|
52
|
-
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
|
|
55
|
-
io = captureIO();
|
|
56
|
-
jest.clearAllMocks();
|
|
57
|
-
mockChecker.checkConstraints.mockResolvedValue({
|
|
58
|
-
passed: true,
|
|
59
|
-
ironLaws: [],
|
|
60
|
-
guidelines: [],
|
|
61
|
-
warningCount: 0,
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const outputText = () => io.outText();
|
|
66
|
-
|
|
67
|
-
it('版本漂移:黄色警告块 + ⚠️⚠️ 版本行,但不阻断(exit 未调用,仍判通过)', async () => {
|
|
68
|
-
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
69
|
-
writeClaudeMd(root, '0.0.1-old');
|
|
70
|
-
|
|
71
|
-
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
72
|
-
|
|
73
|
-
const output = outputText();
|
|
74
|
-
expect(output).toContain('约束注入漂移');
|
|
75
|
-
expect(output).toContain('⚠️⚠️');
|
|
76
|
-
expect(output).toContain('agent 上下文中的规则与已安装 harness 版本不一致');
|
|
77
|
-
expect(output).toContain('0.0.1-old');
|
|
78
|
-
expect(output).toContain('npx @dommaker/harness init');
|
|
79
|
-
// 不阻断:检查仍通过、未调用 process.exit
|
|
80
|
-
expect(output).toContain('约束检查通过');
|
|
81
|
-
expect(result.kind).toBe('ok');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('内容漂移:手改一条 → 警告块含缺失/多余计数,exit 未调用', async () => {
|
|
85
|
-
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
86
|
-
writeClaudeMd(root, REAL_VERSION);
|
|
87
|
-
const claudeMdPath = path.join(root, 'CLAUDE.md');
|
|
88
|
-
const content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
89
|
-
const originalLine = content.split('\n').find(l => l.startsWith('- **'))!;
|
|
90
|
-
fs.writeFileSync(claudeMdPath, content.replace(originalLine, originalLine.replace(/: .+$/, ': 篡改')), 'utf-8');
|
|
91
|
-
|
|
92
|
-
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
93
|
-
|
|
94
|
-
const output = outputText();
|
|
95
|
-
expect(output).toContain('内容漂移: 缺失 1 条 / 多余 1 条');
|
|
96
|
-
expect(output).toContain('约束检查通过');
|
|
97
|
-
expect(result.kind).toBe('ok');
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('无漂移:零警告输出(不增加噪音)', async () => {
|
|
101
|
-
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
102
|
-
writeClaudeMd(root, REAL_VERSION);
|
|
103
|
-
|
|
104
|
-
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
105
|
-
|
|
106
|
-
const output = outputText();
|
|
107
|
-
expect(output).not.toContain('注入漂移');
|
|
108
|
-
expect(output).toContain('约束检查通过');
|
|
109
|
-
expect(result.kind).toBe('ok');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('未注入(无 CLAUDE.md):不警告', async () => {
|
|
113
|
-
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
114
|
-
|
|
115
|
-
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
116
|
-
|
|
117
|
-
expect(outputText()).not.toContain('注入漂移');
|
|
118
|
-
expect(result.kind).toBe('ok');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// ── 生效集来源与漂移比对的同一性(ADR-0023 步骤 4.5)──────────────────
|
|
122
|
-
// 改前 CLI 给 -p 塞了缺省值 'standard',「没传」与「传了」不可区分,
|
|
123
|
-
// 于是「--preset 仅在无自定义配置时覆盖」这条规则恒被触发:项目 config.yml 的
|
|
124
|
-
// preset 静默失效,而漂移侧按 config.yml 算 → 两边稳定对不上(该报的漂移不报)。
|
|
125
|
-
|
|
126
|
-
it('config.yml 的 preset 不再被 CLI 缺省顶掉:不带 -p 按 relaxed 执法且零漂移', async () => {
|
|
127
|
-
const root = createProjectFixture({
|
|
128
|
-
name: 'harness-check-drift-relaxed',
|
|
129
|
-
config: 'preset: relaxed\n',
|
|
130
|
-
});
|
|
131
|
-
writeClaudeMd(root, REAL_VERSION); // getEffectiveConstraints(root) 走 config.yml = relaxed
|
|
132
|
-
|
|
133
|
-
const result = await check({ staged: false, projectPath: root }, io);
|
|
134
|
-
|
|
135
|
-
// relaxed 裁掉的内置条目会出现在「已禁用约束」行——改前 CLI 塞了 standard,这行根本不出现
|
|
136
|
-
const output = outputText();
|
|
137
|
-
expect(output).toContain('已禁用约束:');
|
|
138
|
-
expect(output).not.toContain('内容漂移');
|
|
139
|
-
expect(output).toContain('约束检查通过');
|
|
140
|
-
expect(result.kind).toBe('ok');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('显式 -p 时,漂移比对用的就是本 run 实际执法的那份', async () => {
|
|
144
|
-
const root = createProjectFixture({
|
|
145
|
-
name: 'harness-check-drift-override',
|
|
146
|
-
config: 'preset: relaxed\n',
|
|
147
|
-
});
|
|
148
|
-
writeClaudeMd(root, REAL_VERSION); // 段里是 relaxed 的少数条目
|
|
149
|
-
|
|
150
|
-
await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
151
|
-
|
|
152
|
-
// standard 不裁条目(无「已禁用约束」行),且期望段 = 本 run 执法的 standard
|
|
153
|
-
// → 段里条目只少不多,报「缺失 N 条 / 多余 0 条」
|
|
154
|
-
const output = outputText();
|
|
155
|
-
expect(output).not.toContain('已禁用约束:');
|
|
156
|
-
expect(output).toMatch(/内容漂移: 缺失 [1-9]\d* 条 \/ 多余 0 条/);
|
|
157
|
-
});
|
|
158
|
-
});
|