@dommaker/harness 1.9.0 → 1.11.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 +17 -0
- package/README.md +9 -12
- 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.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +7 -12
- package/dist/cli/commands/definitions.js.map +1 -1
- 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 +1 -3
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
- package/dist/cli/commands/scaffold-templates.js +2 -53
- 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/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 +6 -8
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +6 -11
- 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/index.d.ts +9 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -18
- package/dist/index.js.map +1 -1
- package/dist/knowledge/lint.d.ts +17 -0
- package/dist/knowledge/lint.d.ts.map +1 -1
- package/dist/knowledge/lint.js +52 -0
- package/dist/knowledge/lint.js.map +1 -1
- package/dist/knowledge/store.d.ts +7 -0
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +48 -13
- package/dist/knowledge/store.js.map +1 -1
- package/dist/knowledge/types.d.ts +5 -1
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js +5 -1
- package/dist/knowledge/types.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 +2 -2
- 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 +8 -7
- package/src/__tests__/public-type-surface.test.ts +7 -5
- 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/__tests__/usage-report-public-export.test.ts +21 -0
- package/src/cli/commands/CONTEXT.md +12 -12
- 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-json-flag.test.ts +101 -0
- 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 +13 -21
- package/src/cli/commands/__tests__/init.test.ts +1 -16
- package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +1 -1
- package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +1 -1
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
- package/src/cli/commands/__tests__/registry.test.ts +1 -1
- 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/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 +7 -12
- package/src/cli/commands/init.ts +8 -179
- package/src/cli/commands/report.ts +19 -23
- package/src/cli/commands/scaffold-templates.ts +1 -53
- 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/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 +9 -16
- 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/__tests__/bootstrap.test.ts +3 -5
- package/src/index.ts +28 -18
- package/src/knowledge/CONTEXT.md +4 -3
- package/src/knowledge/__tests__/lint.test.ts +84 -0
- package/src/knowledge/__tests__/store.test.ts +70 -0
- package/src/knowledge/lint.ts +60 -1
- package/src/knowledge/store.ts +48 -11
- package/src/knowledge/types.ts +7 -1
- package/src/monitoring/CONTEXT.md +1 -1
- 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/cli/commands/posteval-plan.d.ts +0 -15
- package/dist/cli/commands/posteval-plan.d.ts.map +0 -1
- package/dist/cli/commands/posteval-plan.js +0 -86
- package/dist/cli/commands/posteval-plan.js.map +0 -1
- 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/src/cli/commands/__tests__/check-drift.test.ts +0 -158
- package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
- package/src/cli/commands/__tests__/posteval-plan.test.ts +0 -204
- package/src/cli/commands/posteval-plan.ts +0 -98
- 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
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 治理链路的 io 注入不逃逸(harness#149
|
|
2
|
+
* 治理链路的 io 注入不逃逸(harness#149,ADR-0029 收窄)
|
|
3
3
|
*
|
|
4
|
-
* setupGovernance 手握 io
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 这类唯一提示写坏了没有测试会红。
|
|
4
|
+
* setupGovernance 手握 io,曾在调用点把它丢了(`setupClaudeMdOutputStyle(projectPath)`),
|
|
5
|
+
* 被调方一律 `io: CommandIO = processIO` 兜成真实 stdout。真机输出正常,但注入面的
|
|
6
|
+
* 测试看不见那些提示——`⚠️ …标记残缺…` 这类唯一提示写坏了没有测试会红。
|
|
8
7
|
*
|
|
9
8
|
* 本文件不 mock 任何 IO:在临时目录里真跑 init,断言治理段提示落在**注入面**。
|
|
10
|
-
*
|
|
9
|
+
* ADR-0029:约束注入段 writer(setupAgentsMdConstraints / setupClaudeMdConstraints /
|
|
10
|
+
* setupGovernanceConstraints)已随文本注入层关停删除,相关用例移除;
|
|
11
|
+
* Output Style 段(通用 marker 机制)保留。
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import * as fs from 'fs/promises';
|
|
@@ -63,49 +64,10 @@ describe('init 治理链路的提示落在注入面(harness#149)', () => {
|
|
|
63
64
|
expect(io.outText()).toContain('✅ 已在 CLAUDE.md 顶部写入 Output Style 段');
|
|
64
65
|
});
|
|
65
66
|
|
|
66
|
-
it('
|
|
67
|
-
const io = await runInit({ 'AGENTS.md': '# AGENTS.md\n\n既有导读\n' });
|
|
68
|
-
expect(io.outText()).toContain('✅ 已追加治理契约 PRESERVE:governance 段到 AGENTS.md');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('标记残缺(唯一提示):`标记残缺…请人工修复` 进捕获 stdout', async () => {
|
|
72
|
-
const io = await runInit({ 'AGENTS.md': '# AGENTS.md\n\n<!-- PRESERVE:governance -->\n残缺段\n' });
|
|
73
|
-
expect(io.outText()).toContain('⚠️ AGENTS.md 中 PRESERVE:governance 标记残缺(只有单边),跳过治理契约写入,请人工修复');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// #158 补钉:这类提示在 init.ts 实测有四处,#149 只钉住上面一条——其余三处各补一条
|
|
77
|
-
// 捕获面断言。四处分支不同文件/不同段位,fixture 各自单建,不共用残缺文件。
|
|
78
|
-
|
|
79
|
-
it('标记残缺四处之二:CLAUDE.md 的 HARNESS_OUTPUT_STYLE 单边 → Output Style 注入告警', async () => {
|
|
67
|
+
it('标记残缺(唯一提示):HARNESS_OUTPUT_STYLE 单边 → Output Style 注入告警进捕获 stdout', async () => {
|
|
80
68
|
const io = await runInit({ 'CLAUDE.md': '# CLAUDE.md\n\n<!-- HARNESS_OUTPUT_STYLE_START -->\n半残段\n' });
|
|
81
69
|
expect(io.outText()).toContain('⚠️ CLAUDE.md 中 HARNESS_OUTPUT_STYLE 标记残缺(单边或乱序),跳过 Output Style 注入,请人工修复');
|
|
82
70
|
});
|
|
83
|
-
|
|
84
|
-
it('标记残缺四处之三:AGENTS.md 的 PRESERVE 段内 HARNESS_CONSTRAINTS 单边 → 段内告警', async () => {
|
|
85
|
-
const broken = [
|
|
86
|
-
'# AGENTS.md',
|
|
87
|
-
'',
|
|
88
|
-
'<!-- PRESERVE:governance -->',
|
|
89
|
-
'手写契约',
|
|
90
|
-
'',
|
|
91
|
-
'<!-- HARNESS_CONSTRAINTS_START -->',
|
|
92
|
-
'半残注入',
|
|
93
|
-
'<!-- /PRESERVE:governance -->',
|
|
94
|
-
'',
|
|
95
|
-
].join('\n');
|
|
96
|
-
const io = await runInit({ 'AGENTS.md': broken });
|
|
97
|
-
expect(io.outText()).toContain('⚠️ AGENTS.md PRESERVE:governance 段内 HARNESS_CONSTRAINTS 标记残缺(单边或乱序),跳过治理契约写入,请人工修复');
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('标记残缺四处之四:CLAUDE.md 的 HARNESS_CONSTRAINTS 单边 → 治理约束注入告警(旧模型仓落点)', async () => {
|
|
101
|
-
const io = await runInit({ 'CLAUDE.md': '# CLAUDE.md\n\n<!-- HARNESS_CONSTRAINTS_START -->\n半残段\n' });
|
|
102
|
-
expect(io.outText()).toContain('⚠️ CLAUDE.md 中 HARNESS_CONSTRAINTS 标记残缺(单边或乱序),跳过治理约束注入,请人工修复');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('新仓创建 AGENTS.md 治理段:创建提示进捕获 stdout', async () => {
|
|
106
|
-
const io = await runInit({});
|
|
107
|
-
expect(io.outText()).toContain('✅ 已创建 AGENTS.md 并写入治理契约 PRESERVE:governance 段');
|
|
108
|
-
});
|
|
109
71
|
});
|
|
110
72
|
|
|
111
73
|
describe('writer 层 io 必传(源形状闸,防缺省兜底再回来)', () => {
|
|
@@ -115,17 +77,10 @@ describe('writer 层 io 必传(源形状闸,防缺省兜底再回来)', ()
|
|
|
115
77
|
expect(sites[0]).toMatch(/^export async function init\(/);
|
|
116
78
|
});
|
|
117
79
|
|
|
118
|
-
it('
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
'setupClaudeMdConstraints',
|
|
123
|
-
'setupGovernanceConstraints',
|
|
124
|
-
]) {
|
|
125
|
-
expect(INIT_SOURCE).toMatch(
|
|
126
|
-
new RegExp(`export async function ${writer}\\(projectPath: string, io: CommandIO\\)`),
|
|
127
|
-
);
|
|
128
|
-
}
|
|
80
|
+
it('Output Style writer 的 io 为必传形参', () => {
|
|
81
|
+
expect(INIT_SOURCE).toMatch(
|
|
82
|
+
/export async function setupClaudeMdOutputStyle\(projectPath: string, io: CommandIO\)/,
|
|
83
|
+
);
|
|
129
84
|
});
|
|
130
85
|
|
|
131
86
|
it('setupGovernance 的调用点逐个传 io(不留裸 projectPath 调用)', () => {
|
|
@@ -134,6 +89,5 @@ describe('writer 层 io 必传(源形状闸,防缺省兜底再回来)', ()
|
|
|
134
89
|
INIT_SOURCE.indexOf('/**\n * 目录 CONTEXT.md 骨架的 plan'),
|
|
135
90
|
);
|
|
136
91
|
expect(governanceBody).toMatch(/await setupClaudeMdOutputStyle\(projectPath, io\)/);
|
|
137
|
-
expect(governanceBody).toMatch(/await setupGovernanceConstraints\(projectPath, io\)/);
|
|
138
92
|
});
|
|
139
93
|
});
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* init.test.ts 把 fs/promises 整体 mock 掉,只能看到「调过 writeFile」,看不到
|
|
5
5
|
* 真写出了什么内容。本文件不 mock 任何 IO:在临时目录里真跑两遍 init——
|
|
6
|
-
* 第一遍断言
|
|
7
|
-
*
|
|
6
|
+
* 第一遍断言 8 个受管文件的落盘字节,第二遍(文件已在场且被用户改过)断言
|
|
7
|
+
* 8 站点各自的第三态、用户内容一字节不动、以及「打印片段 == 真正落盘的内容」。
|
|
8
|
+
* ADR-0029:custom-constraints.yml 示例站与 AGENTS.md 治理契约段 writer 已随
|
|
9
|
+
* 文本注入层关停移除,落盘集合同步收缩。
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import * as fs from 'fs/promises';
|
|
@@ -120,9 +122,9 @@ describe('init 真落盘(无 IO mock)', () => {
|
|
|
120
122
|
|
|
121
123
|
expect(await init({ ...INIT_OPTIONS, projectPath: root }, io)).toEqual({ kind: 'ok' });
|
|
122
124
|
|
|
123
|
-
// 冻结的整屏 = scaffold
|
|
124
|
-
// setupGovernance
|
|
125
|
-
//
|
|
125
|
+
// 冻结的整屏 = scaffold 8 站点 + 治理段 writer 的落盘提示。#149 前 io 在
|
|
126
|
+
// setupGovernance 调用点被丢,治理 writer 的提示逃到 process.stdout、不进捕获面;
|
|
127
|
+
// io 贯通后它回到捕获面,真机输出的字节不变。
|
|
126
128
|
expect(normalize(io.outText(), root)).toBe(`🚀 初始化 harness 配置...
|
|
127
129
|
配置目录: <P>/.harness
|
|
128
130
|
预设: standard
|
|
@@ -130,14 +132,12 @@ describe('init 真落盘(无 IO mock)', () => {
|
|
|
130
132
|
✅ 已创建配置文件: <P>/.harness/config.yml (v*)
|
|
131
133
|
✅ 已创建示例检查点文件: <P>/.harness/checkpoints.yml
|
|
132
134
|
✅ 已创建 Resolutions 文件: <P>/.harness/resolutions.json
|
|
133
|
-
✅ 已创建自定义约束示例: custom-constraints.yml
|
|
134
135
|
✅ 已创建 .git/hooks/pre-commit
|
|
135
136
|
✅ 已创建 .git/hooks/pre-push
|
|
136
137
|
✅ 已创建 .github/workflows/harness-check.yml
|
|
137
138
|
|
|
138
139
|
📋 设置治理文件...
|
|
139
140
|
✅ 已创建 CHANGELOG.md
|
|
140
|
-
✅ 已创建 AGENTS.md 并写入治理契约 PRESERVE:governance 段 (v*)
|
|
141
141
|
✅ 已创建 src/CONTEXT.md
|
|
142
142
|
治理检查已由 harness-check.yml 覆盖,跳过创建 harness-governance.yml
|
|
143
143
|
|
|
@@ -145,9 +145,8 @@ describe('init 真落盘(无 IO mock)', () => {
|
|
|
145
145
|
|
|
146
146
|
下一步:
|
|
147
147
|
1. 编辑 .harness/config.yml 自定义配置
|
|
148
|
-
2.
|
|
149
|
-
3.
|
|
150
|
-
4. 运行 harness status 查看状态
|
|
148
|
+
2. 正常开发:每次 git commit 查暂存的(增量快反馈),每次 git push 查整仓的(全量兜底)——重复是设计使然
|
|
149
|
+
3. 运行 harness status 查看状态
|
|
151
150
|
|
|
152
151
|
💡 提示: 使用 harness init --print-snippets 查看配置代码片段
|
|
153
152
|
`);
|
|
@@ -157,14 +156,13 @@ describe('init 真落盘(无 IO mock)', () => {
|
|
|
157
156
|
const hook = await read('.git/hooks/pre-commit');
|
|
158
157
|
expect(hook.startsWith('#!/bin/sh\n# Harness pre-commit hook\n')).toBe(true);
|
|
159
158
|
expect(hook).toContain('echo "🔍 Running harness checks..."');
|
|
160
|
-
expect(hook).toContain(
|
|
159
|
+
expect(hook).toContain('npx @dommaker/harness check --staged');
|
|
161
160
|
expect(hook.endsWith('echo "✅ All checks passed"\n')).toBe(true);
|
|
162
161
|
|
|
163
162
|
// 第 9 站点(#144):字节级冻结,改模板必须同步改这份基线(同 GITLAB_PLAIN 手法)
|
|
164
163
|
expect(await read('.git/hooks/pre-push')).toBe(PRE_PUSH_HOOK_BYTES);
|
|
165
164
|
|
|
166
165
|
expect(await read('.github/workflows/harness-check.yml')).toContain('name: Harness Check');
|
|
167
|
-
expect(await read('.harness/custom-constraints.yml')).toContain('custom_constraints:');
|
|
168
166
|
expect(await read('.harness/checkpoints.yml')).toContain('id: build-success');
|
|
169
167
|
expect(JSON.parse(await read('.harness/resolutions.json')).capability_sync).toBeDefined();
|
|
170
168
|
expect(await read('CHANGELOG.md')).toContain('Keep a Changelog');
|
|
@@ -186,11 +184,10 @@ describe('init 真落盘(无 IO mock)', () => {
|
|
|
186
184
|
await init({ ...INIT_OPTIONS, projectPath: root }, io);
|
|
187
185
|
const lines = io.outLines();
|
|
188
186
|
|
|
189
|
-
// 顺序 = init 的落盘顺序;harness-governance.yml
|
|
187
|
+
// 顺序 = init 的落盘顺序;harness-governance.yml(末站)因既有 workflow 覆盖而跳过
|
|
190
188
|
expectOrderedSubsequence(lines, [
|
|
191
189
|
'checkpoints.yml 已存在,跳过',
|
|
192
190
|
'resolutions.json 已存在,跳过',
|
|
193
|
-
'custom-constraints.yml 已存在',
|
|
194
191
|
'⚠️ .git/hooks/pre-commit 已存在',
|
|
195
192
|
'💡 请手动添加以下内容到文件末尾:',
|
|
196
193
|
'⚠️ .git/hooks/pre-push 已存在',
|
|
@@ -223,7 +220,6 @@ describe('init 真落盘(无 IO mock)', () => {
|
|
|
223
220
|
'.git/hooks/pre-commit': '#!/bin/sh\n# 我自己的钩子\n',
|
|
224
221
|
'.git/hooks/pre-push': '#!/bin/sh\n# 我自己的推送钩子\n',
|
|
225
222
|
'.github/workflows/harness-check.yml': 'name: 我自己的流水线\n',
|
|
226
|
-
'.harness/custom-constraints.yml': 'custom_constraints: {}\n',
|
|
227
223
|
'.harness/checkpoints.yml': 'checkpoints: []\n',
|
|
228
224
|
'.harness/resolutions.json': '{}\n',
|
|
229
225
|
'CHANGELOG.md': '# 我的更新日志\n',
|
|
@@ -274,9 +270,8 @@ describe('init 真落盘(无 IO mock)', () => {
|
|
|
274
270
|
expect(io.outText()).not.toContain('harness-governance.yml');
|
|
275
271
|
expect(await exists(path.join(root, '.github'))).toBe(false);
|
|
276
272
|
expect(await exists(path.join(root, '.gitlab-ci.yml'))).toBe(false);
|
|
277
|
-
// 治理文档面照常:CHANGELOG /
|
|
273
|
+
// 治理文档面照常:CHANGELOG / CONTEXT.md 不受 CI 豁免影响
|
|
278
274
|
expect(await exists(path.join(root, 'CHANGELOG.md'))).toBe(true);
|
|
279
|
-
expect(await exists(path.join(root, 'AGENTS.md'))).toBe(true);
|
|
280
275
|
});
|
|
281
276
|
|
|
282
277
|
it('无 .git 与无源码目录时仍按原措辞告知,不写任何文件', async () => {
|
|
@@ -556,9 +551,7 @@ describe('init --ci × -g 组合边界(harness#156,真落盘)', () => {
|
|
|
556
551
|
'.git/hooks/pre-push',
|
|
557
552
|
'.harness/checkpoints.yml',
|
|
558
553
|
'.harness/config.yml',
|
|
559
|
-
'.harness/custom-constraints.yml',
|
|
560
554
|
'.harness/resolutions.json',
|
|
561
|
-
'AGENTS.md',
|
|
562
555
|
'CHANGELOG.md',
|
|
563
556
|
'src/CONTEXT.md',
|
|
564
557
|
'src/index.ts',
|
|
@@ -591,9 +584,8 @@ harness:
|
|
|
591
584
|
version: *
|
|
592
585
|
`);
|
|
593
586
|
|
|
594
|
-
// 治理文档面(CHANGELOG /
|
|
587
|
+
// 治理文档面(CHANGELOG / CONTEXT.md)在 none 下照常生成,不受 CI 豁免影响
|
|
595
588
|
expect(await fs.readFile(path.join(root, 'CHANGELOG.md'), 'utf-8')).toContain('Keep a Changelog');
|
|
596
|
-
expect(await fs.readFile(path.join(root, 'AGENTS.md'), 'utf-8')).toContain('PRESERVE:governance');
|
|
597
589
|
expect(await fs.readFile(path.join(root, 'src/CONTEXT.md'), 'utf-8')).toBe(CONTEXT_MD_BYTES);
|
|
598
590
|
});
|
|
599
591
|
|
|
@@ -105,8 +105,7 @@ describe('init command', () => {
|
|
|
105
105
|
it('应该输出代码片段模式', async () => {
|
|
106
106
|
await init({ preset: 'standard', printSnippets: true }, io);
|
|
107
107
|
expect(io.outText()).not.toBe('');
|
|
108
|
-
|
|
109
|
-
expect(io.outText()).toContain("grep -E 'plans/.*\\.md$|\\.plan\\.md$'");
|
|
108
|
+
expect(io.outText()).toContain('npx @dommaker/harness check --staged');
|
|
110
109
|
});
|
|
111
110
|
});
|
|
112
111
|
|
|
@@ -153,10 +152,7 @@ describe('init command', () => {
|
|
|
153
152
|
const hookCall = writeCalls.find((c: any[]) => String(c[0]).includes('pre-commit'));
|
|
154
153
|
expect(hookCall).toBeDefined();
|
|
155
154
|
expect(hookCall![1]).toContain('npx @dommaker/harness check --staged');
|
|
156
|
-
expect(hookCall![1]).toContain('npx @dommaker/harness posteval-plan');
|
|
157
155
|
expect(hookCall![1]).not.toMatch(/npx harness /);
|
|
158
|
-
// plan 匹配模式须为转义点(#35:模板字面量 \. 运行期退化为 .)
|
|
159
|
-
expect(hookCall![1]).toContain("grep -E 'plans/.*\\.md$|\\.plan\\.md$'");
|
|
160
156
|
});
|
|
161
157
|
|
|
162
158
|
it('pre-commit 片段与落盘 hook 同源(#103:差异行并入共享片段,无第二份文本)', async () => {
|
|
@@ -308,17 +304,6 @@ describe('init command', () => {
|
|
|
308
304
|
});
|
|
309
305
|
});
|
|
310
306
|
|
|
311
|
-
describe('custom-constraints', () => {
|
|
312
|
-
it('应该跳过创建当已存在', async () => {
|
|
313
|
-
mockFs.mkdir.mockResolvedValue(undefined);
|
|
314
|
-
mockFs.writeFile.mockResolvedValue(undefined);
|
|
315
|
-
existingFiles.add(`${PROJECT}/.harness/custom-constraints.yml`);
|
|
316
|
-
|
|
317
|
-
await init({ preset: 'standard', projectPath: PROJECT }, io);
|
|
318
|
-
expect(io.outText()).toContain('custom-constraints.yml 已存在');
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
307
|
describe('governance', () => {
|
|
323
308
|
it('应该在配置中包含 governance 段', async () => {
|
|
324
309
|
mockFs.mkdir.mockResolvedValue(undefined);
|
|
@@ -57,7 +57,7 @@ const performanceCli = GATE_DEFINITIONS.find(g => g.id === 'performance')!.cli;
|
|
|
57
57
|
const reviewCli = GATE_DEFINITIONS.find(g => g.id === 'review')!.cli;
|
|
58
58
|
|
|
59
59
|
describe('constraints report 三阈值:装配点 fail-loud', () => {
|
|
60
|
-
const valid = {
|
|
60
|
+
const valid = { jsonOutput: false, zeroInterceptMin: '50', noiseFailRate: '0.8', noiseMinTotal: '20' };
|
|
61
61
|
|
|
62
62
|
it('缺省字符串 → 数值(int/int/float 各归其形)', () => {
|
|
63
63
|
expect(reportDef.mapActionArgs!([], valid)).toEqual([{
|
|
@@ -85,7 +85,7 @@ describe('passes-gate 重试选项已删除(A1)', () => {
|
|
|
85
85
|
it('采集面确实覆盖三类命令位(前提,防空扫致闸失效)', () => {
|
|
86
86
|
const rendered = ALL_CLI_SITES.map(s => `${s.slot} ${s.token}`);
|
|
87
87
|
expect(rendered).toContain('passes-gate --coverage-threshold'); // 通用表叶子命令的旗帜
|
|
88
|
-
expect(rendered).toContain('constraints report --json'); // children 递归(嵌套命令位)
|
|
88
|
+
expect(rendered).toContain('constraints report --json-output'); // children 递归(嵌套命令位)
|
|
89
89
|
expect(rendered).toContain('acceptance --task-id'); // 门禁表命令位(GATE_DEFINITIONS.cli)
|
|
90
90
|
expect(rendered).toContain('check list'); // 选项路由键(commander camelCase 键位)
|
|
91
91
|
});
|
|
@@ -210,7 +210,7 @@ describe('-p 锚定:执行与 IO 必须落在 projectPath(cwd ≠ projectPat
|
|
|
210
210
|
.slice(seeded)
|
|
211
211
|
.map(l => JSON.parse(l) as { constraintId: string; projectPath?: string });
|
|
212
212
|
expect(written.map(t => t.constraintId)).toEqual(
|
|
213
|
-
expect.arrayContaining(['
|
|
213
|
+
expect.arrayContaining(['no_completion_without_verification', 'no_hardcoded_credentials'])
|
|
214
214
|
);
|
|
215
215
|
expect(written.every(t => t.projectPath === projectSide)).toBe(true);
|
|
216
216
|
// 病灶:修复前这一支落在调用方 cwd(A),B 侧 mtime 不动
|
|
@@ -231,7 +231,7 @@ describe('-p 锚定:执行与 IO 必须落在 projectPath(cwd ≠ projectPat
|
|
|
231
231
|
expect(await status({ projectPath: projectSide }, io)).toEqual({ kind: 'ok' });
|
|
232
232
|
// 记录数取自 B 的正本行数:写侧漏到 A 就必然对不上(假绿),读到 A 也读不到 check 的落点
|
|
233
233
|
expect(io.outText()).toContain(`记录数: ${traceLinesOf(projectSide).length} 条`);
|
|
234
|
-
expect(io.outText()).toContain('
|
|
234
|
+
expect(io.outText()).toContain('no_completion_without_verification');
|
|
235
235
|
});
|
|
236
236
|
|
|
237
237
|
it('不带 --project-path 的项目内使用:落点仍是当下 cwd(存量使用者无感)', async () => {
|
|
@@ -35,7 +35,7 @@ function collectRefs(defs: CommandDefinition[]): CommandImplRef[] {
|
|
|
35
35
|
|
|
36
36
|
const EXPECTED_TOP_LEVEL_COMMANDS = [
|
|
37
37
|
'check', 'validate', 'passes-gate', 'init', 'report', 'status', 'spec',
|
|
38
|
-
'sync-docs', 'knowledge', 'sdd', 'failure',
|
|
38
|
+
'sync-docs', 'knowledge', 'sdd', 'failure',
|
|
39
39
|
'release', 'constraints',
|
|
40
40
|
'spec-baseline-check',
|
|
41
41
|
// 6 门禁命令(GATE_DEFINITIONS 驱动)
|
|
@@ -46,8 +46,8 @@ describe('report command - 补充覆盖', () => {
|
|
|
46
46
|
|
|
47
47
|
expect(output).toContain('# Harness 检查报告');
|
|
48
48
|
expect(output).toContain('## 约束检查');
|
|
49
|
-
expect(output).toContain('
|
|
50
|
-
expect(output).toContain('
|
|
49
|
+
expect(output).toContain('error 级');
|
|
50
|
+
expect(output).toContain('warning 级');
|
|
51
51
|
expect(output).toContain('@dommaker/harness');
|
|
52
52
|
});
|
|
53
53
|
});
|
|
@@ -71,8 +71,8 @@ describe('report command - 补充覆盖', () => {
|
|
|
71
71
|
expect(data).toHaveProperty('timestamp');
|
|
72
72
|
expect(data).toHaveProperty('constraints');
|
|
73
73
|
expect(data.constraints).toHaveProperty('total');
|
|
74
|
-
expect(data.constraints).toHaveProperty('
|
|
75
|
-
expect(data.constraints).toHaveProperty('
|
|
74
|
+
expect(data.constraints).toHaveProperty('errors');
|
|
75
|
+
expect(data.constraints).toHaveProperty('warnings');
|
|
76
76
|
expect(data.constraints).toHaveProperty('violations');
|
|
77
77
|
});
|
|
78
78
|
});
|
|
@@ -97,7 +97,7 @@ describe('report command', () => {
|
|
|
97
97
|
const parsed = JSON.parse(jsonStr);
|
|
98
98
|
expect(parsed.constraints).toBeDefined();
|
|
99
99
|
expect(parsed.constraints.total).toBeGreaterThan(0);
|
|
100
|
-
expect(parsed.constraints.
|
|
100
|
+
expect(parsed.constraints.errors).toBeGreaterThan(0);
|
|
101
101
|
expect(parsed.timestamp).toBeDefined();
|
|
102
102
|
expect(parsed.projectPath).toBeDefined();
|
|
103
103
|
});
|
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
preCommitHookFile,
|
|
27
27
|
prePushHookFile,
|
|
28
28
|
harnessCheckCiFile,
|
|
29
|
-
customConstraintsFile,
|
|
30
29
|
changelogFile,
|
|
31
30
|
contextDocFile,
|
|
32
31
|
governanceWorkflowFile,
|
|
@@ -72,12 +71,11 @@ class MemoryFs implements ScaffoldFileSystem {
|
|
|
72
71
|
const PROJECT = '/srv/project';
|
|
73
72
|
const USER_CONTENT = '用户自己的内容,不许覆盖\n';
|
|
74
73
|
|
|
75
|
-
/** 全部
|
|
74
|
+
/** 全部 8 站点,顺序 = init 的落盘顺序 */
|
|
76
75
|
function allSites(): ManagedFile[] {
|
|
77
76
|
return [
|
|
78
77
|
checkpointsFile(PROJECT),
|
|
79
78
|
resolutionsFile(PROJECT),
|
|
80
|
-
customConstraintsFile(PROJECT),
|
|
81
79
|
preCommitHookFile(PROJECT),
|
|
82
80
|
prePushHookFile(PROJECT),
|
|
83
81
|
harnessCheckCiFile(PROJECT, 'github', ['ci.yml']),
|
|
@@ -113,7 +111,7 @@ beforeEach(() => {
|
|
|
113
111
|
|
|
114
112
|
describe('writeManagedFile:三态判定', () => {
|
|
115
113
|
it('不在场 → created:建父目录、落盘模板正文、绿字告知', async () => {
|
|
116
|
-
const file =
|
|
114
|
+
const file = changelogFile(PROJECT, 'keep-a-changelog');
|
|
117
115
|
|
|
118
116
|
expect(await writeManagedFile(file, io, fs)).toBe('created');
|
|
119
117
|
|
|
@@ -179,7 +177,7 @@ describe('writeManagedFile:三态判定', () => {
|
|
|
179
177
|
});
|
|
180
178
|
|
|
181
179
|
it('runPlan 按顺序逐站点落盘并逐站点回报状态', async () => {
|
|
182
|
-
const plan = [
|
|
180
|
+
const plan = [checkpointsFile(PROJECT), changelogFile(PROJECT, 'keep-a-changelog')];
|
|
183
181
|
fs.seed(plan[1].target, USER_CONTENT);
|
|
184
182
|
|
|
185
183
|
expect(await runPlan(plan, io, fs)).toEqual(['created', 'exists']);
|
|
@@ -188,11 +186,10 @@ describe('writeManagedFile:三态判定', () => {
|
|
|
188
186
|
});
|
|
189
187
|
|
|
190
188
|
describe('init 的脚手架 plan 站点面', () => {
|
|
191
|
-
it('
|
|
189
|
+
it('8 站点齐备且目标路径逐字冻结(init 6 + validate 2 同源收口)', () => {
|
|
192
190
|
expect(allSites().map(f => f.target)).toEqual([
|
|
193
191
|
`${PROJECT}/.harness/checkpoints.yml`,
|
|
194
192
|
`${PROJECT}/.harness/resolutions.json`,
|
|
195
|
-
`${PROJECT}/.harness/custom-constraints.yml`,
|
|
196
193
|
`${PROJECT}/.git/hooks/pre-commit`,
|
|
197
194
|
`${PROJECT}/.git/hooks/pre-push`,
|
|
198
195
|
`${PROJECT}/.github/workflows/harness-check.yml`,
|
|
@@ -342,13 +339,6 @@ describe('对外文案逐字冻结(9 站点 × 落盘态 / 在场态)', () =
|
|
|
342
339
|
],
|
|
343
340
|
'name: Harness Check',
|
|
344
341
|
],
|
|
345
|
-
[
|
|
346
|
-
'custom-constraints.yml',
|
|
347
|
-
() => customConstraintsFile(PROJECT),
|
|
348
|
-
'✅ 已创建自定义约束示例: custom-constraints.yml',
|
|
349
|
-
['custom-constraints.yml 已存在'],
|
|
350
|
-
'',
|
|
351
|
-
],
|
|
352
342
|
[
|
|
353
343
|
'checkpoints.yml',
|
|
354
344
|
() => checkpointsFile(PROJECT),
|
|
@@ -18,8 +18,8 @@ import { status } from '../status';
|
|
|
18
18
|
const STATUS_SOURCE = fs.readFileSync(path.join(__dirname, '..', 'status.ts'), 'utf-8');
|
|
19
19
|
|
|
20
20
|
/** 1 pass + 1 fail:failRate 0.5、passRate 0.5 → 无异常;2 fail:passRate 0 → low_pass_rate */
|
|
21
|
-
const PASS = { constraintId: 'alpha',
|
|
22
|
-
const FAIL = { constraintId: 'beta',
|
|
21
|
+
const PASS = { constraintId: 'alpha', severity: 'error' as const, result: 'pass' as const };
|
|
22
|
+
const FAIL = { constraintId: 'beta', severity: 'warning' as const, result: 'fail' as const };
|
|
23
23
|
|
|
24
24
|
let io: CapturingIO;
|
|
25
25
|
beforeEach(() => {
|
|
@@ -47,7 +47,7 @@ describe('status 分析接线(ADR-0020)', () => {
|
|
|
47
47
|
|
|
48
48
|
await status({ projectPath: root, detail: true }, io);
|
|
49
49
|
|
|
50
|
-
expect(io.outText()).toContain('🔴
|
|
50
|
+
expect(io.outText()).toContain('🔴 error 级约束:');
|
|
51
51
|
expect(io.outText()).toContain('alpha');
|
|
52
52
|
expect(io.outText()).toContain('检查: 2 | 通过: 100% | 失败: 0%');
|
|
53
53
|
expect(io.outText()).not.toContain('个异常');
|
|
@@ -34,7 +34,7 @@ const mockFs = fs as jest.Mocked<typeof fs>;
|
|
|
34
34
|
/** trace 文件内容:一 trace 一行,缺省字段补齐成合法 ExecutionTrace(timestamp 递增) */
|
|
35
35
|
function traceFile(traces: Array<Partial<ExecutionTrace>>): string {
|
|
36
36
|
return traces
|
|
37
|
-
.map((t, i) => JSON.stringify({
|
|
37
|
+
.map((t, i) => JSON.stringify({ severity: 'error', timestamp: i + 1, result: 'pass', ...t }))
|
|
38
38
|
.join('\n');
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -77,7 +77,7 @@ describe('status command - 补充覆盖', () => {
|
|
|
77
77
|
it('有异常时应该显示详细信息和下一步建议', async () => {
|
|
78
78
|
// 两个约束各自全失败 → 各自判出一枚 low_pass_rate
|
|
79
79
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
80
|
-
...repeats('
|
|
80
|
+
...repeats('docs_freshness', 2, 'fail'),
|
|
81
81
|
...repeats('no_completion_without_verification', 2, 'fail'),
|
|
82
82
|
]));
|
|
83
83
|
|
|
@@ -102,9 +102,9 @@ describe('status command - 补充覆盖', () => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
describe('约束级别统计', () => {
|
|
105
|
-
it('应该显示
|
|
105
|
+
it('应该显示 error 级约束统计', async () => {
|
|
106
106
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
107
|
-
{ constraintId: '
|
|
107
|
+
{ constraintId: 'docs_freshness' },
|
|
108
108
|
{ constraintId: 'no_completion_without_verification' },
|
|
109
109
|
]));
|
|
110
110
|
|
|
@@ -112,35 +112,35 @@ describe('status command - 补充覆盖', () => {
|
|
|
112
112
|
|
|
113
113
|
const output = io.outText();
|
|
114
114
|
expect(output).toContain('📈 约束统计:');
|
|
115
|
-
expect(output).toContain('🔴
|
|
115
|
+
expect(output).toContain('🔴 error 级约束:');
|
|
116
116
|
expect(output).toContain('✅ no_completion_without_verification');
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
-
it('应该显示
|
|
119
|
+
it('应该显示 warning 级约束统计', async () => {
|
|
120
120
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
121
|
-
{ constraintId: 'capability_sync',
|
|
121
|
+
{ constraintId: 'capability_sync', severity: 'warning', result: 'pass' },
|
|
122
122
|
]));
|
|
123
123
|
|
|
124
124
|
await runStatus();
|
|
125
125
|
|
|
126
|
-
expect(io.outText()).toContain('🟡
|
|
126
|
+
expect(io.outText()).toContain('🟡 warning 级约束:');
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
describe('详细模式扩展', () => {
|
|
131
131
|
it('详细模式应该显示所有级别详情', async () => {
|
|
132
132
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
133
|
-
{ constraintId: '
|
|
134
|
-
{ constraintId: 'capability_sync',
|
|
135
|
-
{ constraintId: 'capability_sync',
|
|
133
|
+
{ constraintId: 'docs_freshness', severity: 'error' },
|
|
134
|
+
{ constraintId: 'capability_sync', severity: 'warning' },
|
|
135
|
+
{ constraintId: 'capability_sync', severity: 'warning' },
|
|
136
136
|
]));
|
|
137
137
|
|
|
138
138
|
await runStatus({ detail: true });
|
|
139
139
|
|
|
140
140
|
const output = io.outText();
|
|
141
141
|
expect(output).toContain('📈 约束统计:');
|
|
142
|
-
expect(output).toContain('🔴
|
|
143
|
-
expect(output).toContain('🟡
|
|
142
|
+
expect(output).toContain('🔴 error 级约束:');
|
|
143
|
+
expect(output).toContain('🟡 warning 级约束:');
|
|
144
144
|
expect(output).toContain('检查: 2 | 通过: 100% | 失败: 0%');
|
|
145
145
|
});
|
|
146
146
|
});
|
|
@@ -148,7 +148,7 @@ describe('status command - 补充覆盖', () => {
|
|
|
148
148
|
describe('JSON parse 异常处理', () => {
|
|
149
149
|
it('无效 JSON 行应该被过滤', async () => {
|
|
150
150
|
mockFs.readFileSync.mockReturnValue(
|
|
151
|
-
['invalid json', JSON.stringify({ constraintId: 'valid',
|
|
151
|
+
['invalid json', JSON.stringify({ constraintId: 'valid', severity: 'error', timestamp: 1, result: 'pass' }), 'also invalid'].join('\n')
|
|
152
152
|
);
|
|
153
153
|
|
|
154
154
|
await runStatus();
|
|
@@ -34,7 +34,7 @@ const mockFs = fs as jest.Mocked<typeof fs>;
|
|
|
34
34
|
/** trace 文件内容:一 trace 一行,缺省字段补齐成合法 ExecutionTrace(timestamp 递增) */
|
|
35
35
|
function traceFile(traces: Array<Partial<ExecutionTrace>>): string {
|
|
36
36
|
return traces
|
|
37
|
-
.map((t, i) => JSON.stringify({
|
|
37
|
+
.map((t, i) => JSON.stringify({ severity: 'error', timestamp: i + 1, result: 'pass', ...t }))
|
|
38
38
|
.join('\n');
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -99,7 +99,7 @@ describe('status command', () => {
|
|
|
99
99
|
it('应该显示记录数', async () => {
|
|
100
100
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
101
101
|
{ constraintId: 'test1' },
|
|
102
|
-
{ constraintId: 'test2',
|
|
102
|
+
{ constraintId: 'test2', severity: 'warning' },
|
|
103
103
|
]));
|
|
104
104
|
|
|
105
105
|
await runStatus();
|
|
@@ -107,14 +107,14 @@ describe('status command', () => {
|
|
|
107
107
|
expect(io.outText()).toContain('📈 约束统计:');
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
it('应该显示
|
|
110
|
+
it('应该显示 error 级约束统计', async () => {
|
|
111
111
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
112
|
-
{ constraintId: '
|
|
112
|
+
{ constraintId: 'no_completion_without_verification' },
|
|
113
113
|
]));
|
|
114
114
|
|
|
115
115
|
await runStatus({ detail: true });
|
|
116
|
-
expect(io.outText()).toContain('🔴
|
|
117
|
-
expect(io.outText()).toContain('✅
|
|
116
|
+
expect(io.outText()).toContain('🔴 error 级约束:');
|
|
117
|
+
expect(io.outText()).toContain('✅ no_completion_without_verification');
|
|
118
118
|
expect(io.outText()).toContain('检查: 1 | 通过: 100% | 失败: 0%');
|
|
119
119
|
});
|
|
120
120
|
});
|
|
@@ -162,14 +162,14 @@ describe('status command', () => {
|
|
|
162
162
|
});
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
describe('
|
|
166
|
-
it('应该显示
|
|
165
|
+
describe('warning 级约束统计', () => {
|
|
166
|
+
it('应该显示 warning 级约束统计', async () => {
|
|
167
167
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
168
|
-
{ constraintId: 'test_guide',
|
|
168
|
+
{ constraintId: 'test_guide', severity: 'warning' },
|
|
169
169
|
]));
|
|
170
170
|
|
|
171
171
|
await runStatus();
|
|
172
|
-
expect(io.outText()).toContain('🟡
|
|
172
|
+
expect(io.outText()).toContain('🟡 warning 级约束:');
|
|
173
173
|
expect(io.outText()).toContain('✅ test_guide');
|
|
174
174
|
});
|
|
175
175
|
});
|
|
@@ -221,11 +221,11 @@ describe('status command', () => {
|
|
|
221
221
|
});
|
|
222
222
|
|
|
223
223
|
describe('详细模式扩展', () => {
|
|
224
|
-
it('应该显示
|
|
224
|
+
it('应该显示 warning 级约束详细统计', async () => {
|
|
225
225
|
mockFs.readFileSync.mockReturnValue(traceFile([
|
|
226
226
|
...repeats('test_guide', 3, 'pass'),
|
|
227
227
|
...repeats('test_guide', 2, 'fail'),
|
|
228
|
-
].map(t => ({ ...t,
|
|
228
|
+
].map(t => ({ ...t, severity: 'warning' as const }))));
|
|
229
229
|
|
|
230
230
|
await runStatus({ detail: true });
|
|
231
231
|
expect(io.outText()).toContain('⚠️ test_guide');
|
|
@@ -126,7 +126,7 @@ describe('sync-docs --agents', () => {
|
|
|
126
126
|
expect(content).not.toContain('clean');
|
|
127
127
|
// 约束与治理
|
|
128
128
|
expect(content).toContain('preset: standard');
|
|
129
|
-
expect(content).toContain('
|
|
129
|
+
expect(content).toContain('约束清单:`CLAUDE.md` Governance Rules 块');
|
|
130
130
|
// 知识入口(只写目录存在性,不写易变的条目数——避免 --check 漂移竞态)
|
|
131
131
|
expect(content).toContain('项目知识库,用 `harness knowledge` 查询');
|
|
132
132
|
expect(content).not.toContain('知识库(');
|