@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
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sync-docs 表格排版回归测试(harness#171)
|
|
3
|
+
*
|
|
4
|
+
* 症状:撤下 CAPABILITIES.md 的模块登记行时清空该行而非删行,残留空行把
|
|
5
|
+
* CommonMark 表格切断。判定面 = 「上一行与下一行都是 `|` 表格行、本行为空」不得存在。
|
|
6
|
+
* 正本修复在写路径(整行删除 + 存量收拢 + 空表收掉),--check 侧必须报待清理
|
|
7
|
+
* (check/fix 同规则才收敛,ADR-0009 口径)。
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as os from 'os';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
13
|
+
import { normalizeCapabilitiesTableLayout } from '../sync-docs/capabilities-syncer';
|
|
14
|
+
import { syncDocs } from '../sync-docs';
|
|
15
|
+
|
|
16
|
+
let io: CapturingIO;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
io = captureIO();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const HEADER = '| 模块 | 文件 | 说明 |\n|------|------|------|\n';
|
|
22
|
+
|
|
23
|
+
/** 「上一行与下一行都是表格行、本行为空」的行数 —— 本票的判定面 */
|
|
24
|
+
function countBlankRowsInsideTable(content: string): number {
|
|
25
|
+
const lines = content.split('\n');
|
|
26
|
+
let n = 0;
|
|
27
|
+
for (let i = 1; i < lines.length - 1; i++) {
|
|
28
|
+
if (
|
|
29
|
+
lines[i].trim() === '' &&
|
|
30
|
+
/^\s*\|/.test(lines[i - 1]) &&
|
|
31
|
+
/^\s*\|/.test(lines[i + 1])
|
|
32
|
+
) {
|
|
33
|
+
n++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** 建一个 file 模式的最小工程:srcFiles 存在,CAPABILITIES.md 由 rows 拼出 */
|
|
40
|
+
function makeProject(
|
|
41
|
+
name: string,
|
|
42
|
+
srcFiles: string[],
|
|
43
|
+
doc: string
|
|
44
|
+
): string {
|
|
45
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), `sync-docs-layout-${name}-`));
|
|
46
|
+
fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
|
|
47
|
+
for (const f of srcFiles) {
|
|
48
|
+
const file = path.join(dir, 'src', f);
|
|
49
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
50
|
+
fs.writeFileSync(file, 'export const x = 1;\n');
|
|
51
|
+
}
|
|
52
|
+
fs.writeFileSync(path.join(dir, 'CAPABILITIES.md'), doc);
|
|
53
|
+
return dir;
|
|
54
|
+
}
|
|
55
|
+
const read = (dir: string) => fs.readFileSync(path.join(dir, 'CAPABILITIES.md'), 'utf-8');
|
|
56
|
+
const dataRow = (name: string, file: string, desc = 'D') => `| ${name} | ${file} | ${desc} |`;
|
|
57
|
+
|
|
58
|
+
describe('sync-docs CAPABILITIES.md 表格排版(#171)', () => {
|
|
59
|
+
it('撤中间登记行:整行删除,表格保持连续', async () => {
|
|
60
|
+
const dir = makeProject(
|
|
61
|
+
'middle',
|
|
62
|
+
['a.ts', 'c.ts'],
|
|
63
|
+
`# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n${dataRow('b', 'src/b.ts')}\n${dataRow('c', 'src/c.ts')}\n`
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
await syncDocs({ projectPath: dir }, io);
|
|
67
|
+
|
|
68
|
+
const out = read(dir);
|
|
69
|
+
expect(out).not.toContain('src/b.ts');
|
|
70
|
+
expect(countBlankRowsInsideTable(out)).toBe(0);
|
|
71
|
+
expect(out).toBe(`# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n${dataRow('c', 'src/c.ts')}\n`);
|
|
72
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('撤目录条目行(studio 撤登记形状):同样整行删除', async () => {
|
|
76
|
+
const dir = makeProject(
|
|
77
|
+
'dir-entry',
|
|
78
|
+
['cli/cli.ts', 'harness/h.ts'],
|
|
79
|
+
`# Capabilities\n\n${HEADER}` +
|
|
80
|
+
`${dataRow('cli', 'src/cli/', 'C')}\n` +
|
|
81
|
+
`${dataRow('hooks', 'src/hooks/', 'H')}\n` +
|
|
82
|
+
`${dataRow('harness', 'src/harness/', 'A')}\n`
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
await syncDocs({ projectPath: dir }, io);
|
|
86
|
+
|
|
87
|
+
const out = read(dir);
|
|
88
|
+
expect(out).not.toContain('src/hooks/');
|
|
89
|
+
expect(countBlankRowsInsideTable(out)).toBe(0);
|
|
90
|
+
expect(out).toContain(`${dataRow('cli', 'src/cli/', 'C')}\n${dataRow('harness', 'src/harness/', 'A')}`);
|
|
91
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('撤最后一行:不留文件末尾空行', async () => {
|
|
95
|
+
const dir = makeProject(
|
|
96
|
+
'last',
|
|
97
|
+
['a.ts'],
|
|
98
|
+
`# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n${dataRow('gone', 'src/gone.ts')}\n`
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
await syncDocs({ projectPath: dir }, io);
|
|
102
|
+
|
|
103
|
+
expect(read(dir)).toBe(`# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n`);
|
|
104
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('存量脏行(无其它漂移):--check 报待清理,写模式收拢且收拢后 check 转绿', async () => {
|
|
108
|
+
const dirty = `# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n\n${dataRow('z', 'src/z.ts')}\n`;
|
|
109
|
+
const dir = makeProject('legacy', ['a.ts', 'z.ts'], dirty);
|
|
110
|
+
|
|
111
|
+
expect(countBlankRowsInsideTable(read(dir))).toBe(1);
|
|
112
|
+
const check = await syncDocs({ projectPath: dir, check: true }, io);
|
|
113
|
+
expect(check).toEqual({ kind: 'fail', reason: expect.stringContaining('表格') });
|
|
114
|
+
|
|
115
|
+
await syncDocs({ projectPath: dir }, io);
|
|
116
|
+
const out = read(dir);
|
|
117
|
+
expect(countBlankRowsInsideTable(out)).toBe(0);
|
|
118
|
+
expect(out).toContain('src/a.ts');
|
|
119
|
+
expect(out).toContain('src/z.ts');
|
|
120
|
+
expect(await syncDocs({ projectPath: dir, check: true }, io)).toEqual({ kind: 'ok' });
|
|
121
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('表格被撤空:连表头/分隔行一起收掉', async () => {
|
|
125
|
+
const dir = makeProject(
|
|
126
|
+
'empty-table',
|
|
127
|
+
[],
|
|
128
|
+
`# Capabilities\n\n${HEADER}${dataRow('gone1', 'src/gone1.ts')}\n${dataRow('gone2', 'src/gone2.ts')}\n`
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
await syncDocs({ projectPath: dir }, io);
|
|
132
|
+
|
|
133
|
+
const out = read(dir);
|
|
134
|
+
expect(out).not.toContain('|------|');
|
|
135
|
+
expect(out).not.toContain('模块 | 文件');
|
|
136
|
+
expect(out).not.toMatch(/\n{3,}/);
|
|
137
|
+
expect(await syncDocs({ projectPath: dir, check: true }, io)).toEqual({ kind: 'ok' });
|
|
138
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('撤登同时有新增:不得把仍要使用的表头收掉', async () => {
|
|
142
|
+
const dir = makeProject(
|
|
143
|
+
'remove-and-add',
|
|
144
|
+
['b.ts', 'fresh.ts'],
|
|
145
|
+
`# Capabilities\n\n${HEADER}${dataRow('a', 'src/gone.ts')}\n${dataRow('b', 'src/b.ts')}\n`
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
await syncDocs({ projectPath: dir }, io);
|
|
149
|
+
|
|
150
|
+
const out = read(dir);
|
|
151
|
+
expect(out).toContain('模块 | 文件 | 说明');
|
|
152
|
+
expect(out).toContain('fresh.ts');
|
|
153
|
+
expect(countBlankRowsInsideTable(out)).toBe(0);
|
|
154
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('表格外的空行不动(段落分隔不是脏行)', async () => {
|
|
158
|
+
const doc = `# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n\n## 说明\n\n散文。\n`;
|
|
159
|
+
const dir = makeProject('prose', ['a.ts'], doc);
|
|
160
|
+
|
|
161
|
+
const check = await syncDocs({ projectPath: dir, check: true }, io);
|
|
162
|
+
expect(check).toEqual({ kind: 'ok' });
|
|
163
|
+
expect(read(dir)).toBe(doc);
|
|
164
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('代码块内「表头+分隔行无数据行」的示例不被收掉', async () => {
|
|
168
|
+
const doc =
|
|
169
|
+
`# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n\n` +
|
|
170
|
+
`登记格式:\n\n\`\`\`markdown\n| 模块 | 文件 | 说明 |\n|------|------|------|\n\`\`\`\n\n散文。\n`;
|
|
171
|
+
const dir = makeProject('fenced-empty-table', ['a.ts'], doc);
|
|
172
|
+
|
|
173
|
+
expect(await syncDocs({ projectPath: dir, check: true }, io)).toEqual({ kind: 'ok' });
|
|
174
|
+
|
|
175
|
+
await syncDocs({ projectPath: dir }, io);
|
|
176
|
+
expect(read(dir)).toBe(doc);
|
|
177
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('代码块内的表格样式行不被收拢(含块内空行)', async () => {
|
|
181
|
+
const doc =
|
|
182
|
+
`# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n\n` +
|
|
183
|
+
`示例:\n\n\`\`\`\n| x | y |\n\n| z | w |\n\`\`\`\n`;
|
|
184
|
+
const dir = makeProject('fenced-blank-row', ['a.ts'], doc);
|
|
185
|
+
|
|
186
|
+
expect(await syncDocs({ projectPath: dir, check: true }, io)).toEqual({ kind: 'ok' });
|
|
187
|
+
|
|
188
|
+
await syncDocs({ projectPath: dir }, io);
|
|
189
|
+
expect(read(dir)).toBe(doc);
|
|
190
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('两张空表相邻:一次收拢即干净(规则互相制造触发点,须跑到不动点)', () => {
|
|
194
|
+
// 规则①先吃掉两张空表之间的那个空行(两侧都是表格行),规则②在同一轮里
|
|
195
|
+
// 只看得到「表头+分隔行后面还有表格行」的前一张,收得掉后一张。不迭代就收不干净。
|
|
196
|
+
const first = normalizeCapabilitiesTableLayout(`${HEADER}\n${HEADER}`);
|
|
197
|
+
|
|
198
|
+
expect(first).toEqual({ content: '', blankLines: 1, emptyTables: 2 });
|
|
199
|
+
// 不动点:再跑一次零改动,`--check` 才不会修完还红
|
|
200
|
+
expect(normalizeCapabilitiesTableLayout(first.content)).toEqual({
|
|
201
|
+
content: '',
|
|
202
|
+
blankLines: 0,
|
|
203
|
+
emptyTables: 0,
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('收掉空表后不留三个以上连续换行(写模式收干净)', async () => {
|
|
208
|
+
const dir = makeProject(
|
|
209
|
+
'no-triple-newline',
|
|
210
|
+
[],
|
|
211
|
+
`# Capabilities\n\n${HEADER}${dataRow('gone', 'src/gone.ts')}\n\n## 说明\n\n散文。\n`
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
await syncDocs({ projectPath: dir }, io);
|
|
215
|
+
|
|
216
|
+
const out = read(dir);
|
|
217
|
+
expect(out).not.toMatch(/\n{3,}/);
|
|
218
|
+
expect(out).toContain('## 说明');
|
|
219
|
+
expect(out).toContain('散文。');
|
|
220
|
+
expect(await syncDocs({ projectPath: dir, check: true }, io)).toEqual({ kind: 'ok' });
|
|
221
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('围栏内示例行引用已删文件:随幽灵行一起删掉,且 check 收敛(取舍见实现注释)', async () => {
|
|
225
|
+
// 登记条目由 capabilities-parser 全文扫描得出(ADR-0009),块内行也算登记项;
|
|
226
|
+
// 只让幽灵行删除豁免围栏,这个示例会永远被报成已删模块且修不掉。
|
|
227
|
+
const doc =
|
|
228
|
+
`# Capabilities\n\n${HEADER}${dataRow('a', 'src/a.ts')}\n\n` +
|
|
229
|
+
`登记格式示例:\n\n\`\`\`markdown\n${HEADER}${dataRow('gone', 'src/gone.ts')}\n\`\`\`\n`;
|
|
230
|
+
const dir = makeProject('fenced-ghost-entry', ['a.ts'], doc);
|
|
231
|
+
|
|
232
|
+
expect(await syncDocs({ projectPath: dir, check: true }, io)).toMatchObject({ kind: 'fail' });
|
|
233
|
+
expect(io.outText()).toContain('src/gone.ts');
|
|
234
|
+
|
|
235
|
+
await syncDocs({ projectPath: dir }, io);
|
|
236
|
+
|
|
237
|
+
const out = read(dir);
|
|
238
|
+
expect(out).not.toContain('src/gone.ts');
|
|
239
|
+
expect(out).toContain(dataRow('a', 'src/a.ts'));
|
|
240
|
+
// 只吃掉引用已删文件的那一行,块内其余正文留着(不是「把围栏里所有行一起删」)
|
|
241
|
+
expect(out).toContain('```markdown\n' + HEADER);
|
|
242
|
+
expect(countBlankRowsInsideTable(out)).toBe(0);
|
|
243
|
+
expect(await syncDocs({ projectPath: dir, check: true }, io)).toEqual({ kind: 'ok' });
|
|
244
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('CRLF 文档撤行:整行连行尾一起删除', async () => {
|
|
248
|
+
const doc = `# Capabilities\r\n\r\n${HEADER.replace(/\n/g, '\r\n')}${dataRow('a', 'src/a.ts')}\r\n${dataRow('gone', 'src/gone.ts')}\r\n`;
|
|
249
|
+
const dir = makeProject('crlf', ['a.ts'], doc);
|
|
250
|
+
|
|
251
|
+
await syncDocs({ projectPath: dir }, io);
|
|
252
|
+
|
|
253
|
+
const out = read(dir);
|
|
254
|
+
expect(out).not.toContain('src/gone.ts');
|
|
255
|
+
expect(out.endsWith(`${dataRow('a', 'src/a.ts')}\r\n`)).toBe(true);
|
|
256
|
+
expect(countBlankRowsInsideTable(out)).toBe(0);
|
|
257
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
258
|
+
});
|
|
259
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* harness check 命令
|
|
3
3
|
*
|
|
4
|
-
* 检查约束是否满足(
|
|
4
|
+
* 检查约束是否满足(severity='error' 阻断 / severity='warning' 告警)
|
|
5
5
|
* 工单 23:触发条件与证据检测迁至 core/constraints/context-builder
|
|
6
|
-
*
|
|
6
|
+
* 约束集统一走 getMergedConstraintsConfig 生效集链路(preset/config 禁用)
|
|
7
7
|
* harness#88:本命令是 trace 记录器的组合根——core 不上行依赖 monitoring,
|
|
8
8
|
* 真实收集器在此经构造参数接线;#139:收集器锚根构造,trace 落点跟 --project-path 走
|
|
9
9
|
*/
|
|
@@ -11,13 +11,11 @@
|
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import { ConstraintChecker } from '../../core/constraints/checker';
|
|
14
|
-
import {
|
|
15
|
-
import { getMergedConstraintsConfig
|
|
14
|
+
import { CONSTRAINTS } from '../../core/constraints/definitions';
|
|
15
|
+
import { getMergedConstraintsConfig } from '../../core/effective-constraints';
|
|
16
16
|
import { buildConstraintContext } from '../../core/constraints/context-builder';
|
|
17
17
|
import { createGitEvidence, type GitEvidence } from '../../core/constraints/git-evidence';
|
|
18
18
|
import { createRunEnv, type RunEnv } from '../../core/constraints/run-env';
|
|
19
|
-
import { detectInjectionDrift } from '../../core/constraints/injection-drift';
|
|
20
|
-
import { GOVERNANCE_HEADING } from '../../core/constraints/injection-writer';
|
|
21
19
|
import { TraceCollector } from '../../monitoring/traces';
|
|
22
20
|
import { readJsonl } from '../../utils/jsonl';
|
|
23
21
|
import { DEFAULT_TRACE_FILE, type ExecutionTrace } from '../../types/trace';
|
|
@@ -100,14 +98,11 @@ export async function check(
|
|
|
100
98
|
const runEnv = options.runEnv ?? createRunEnv(projectPath);
|
|
101
99
|
const stateIO = options.stateIO ?? fileStateIO(projectPath);
|
|
102
100
|
|
|
103
|
-
//
|
|
101
|
+
// 生效约束集:内置 → preset → config.yml 禁用。
|
|
104
102
|
// --preset 仅在没有项目自定义配置时覆盖 config.yml 的 preset(工单 23 语义:
|
|
105
103
|
// 项目自定义配置优先于 CLI 预设),优先级规则收在 getMergedConstraintsConfig 一处。
|
|
106
104
|
// CLI 不给 -p 缺省值:没传 = 尊重 config.yml(ADR-0023 步骤 4.5,缺省值曾让两者不可区分)
|
|
107
105
|
const merged = getMergedConstraintsConfig(runEnv, { preset: options.preset });
|
|
108
|
-
if (merged.custom.length > 0) {
|
|
109
|
-
log(io, chalk.gray(`自定义约束: ${merged.custom.length} 条`));
|
|
110
|
-
}
|
|
111
106
|
if (merged.disabled.length > 0) {
|
|
112
107
|
log(io, chalk.gray(`已禁用约束: ${merged.disabled.join(', ')}`));
|
|
113
108
|
}
|
|
@@ -132,7 +127,7 @@ export async function check(
|
|
|
132
127
|
}
|
|
133
128
|
log(io, chalk.gray(`触发条件: ${[context.operation, ...(context.extraTriggers ?? [])].join(', ')}`));
|
|
134
129
|
|
|
135
|
-
//
|
|
130
|
+
// 执行约束检查(per-request 传 customConfig,避免单例状态污染;证据同 run 同源)
|
|
136
131
|
// trace 记录器经构造参数接线(harness#88):一次命令一个 checker 实例
|
|
137
132
|
// #139:收集器锚根构造——不传 projectPath 时 trace 会落进调用方 cwd,B 侧读不到
|
|
138
133
|
const checker = new ConstraintChecker(new TraceCollector({ projectPath }));
|
|
@@ -141,20 +136,20 @@ export async function check(
|
|
|
141
136
|
// 输出结果
|
|
142
137
|
log(io);
|
|
143
138
|
|
|
144
|
-
// skipped(约定未采用/证据未接线)单独列示,不进 pass/fail
|
|
139
|
+
// skipped(约定未采用/证据未接线)单独列示,不进 pass/fail 统计
|
|
145
140
|
const skippedResults = [
|
|
146
|
-
...result.
|
|
147
|
-
...result.
|
|
141
|
+
...result.errors,
|
|
142
|
+
...result.warnings,
|
|
148
143
|
].filter(r => r.skipped);
|
|
149
144
|
|
|
150
|
-
//
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
log(io, chalk.green(`✅
|
|
155
|
-
} else if (
|
|
156
|
-
log(io, chalk.red(`❌
|
|
157
|
-
|
|
145
|
+
// severity='error'(违规即阻断)
|
|
146
|
+
const evaluatedErrors = result.errors.filter(r => !r.skipped);
|
|
147
|
+
const errorViolations = evaluatedErrors.filter(r => !r.satisfied);
|
|
148
|
+
if (errorViolations.length === 0 && evaluatedErrors.length > 0) {
|
|
149
|
+
log(io, chalk.green(`✅ error 级约束: 全部通过 (${evaluatedErrors.length} 条)`));
|
|
150
|
+
} else if (errorViolations.length > 0) {
|
|
151
|
+
log(io, chalk.red(`❌ error 级约束违规: ${errorViolations.length} 条`));
|
|
152
|
+
errorViolations.forEach(r => {
|
|
158
153
|
if (r.constraint) {
|
|
159
154
|
log(io, chalk.red(` - ${r.constraint.id}: ${r.constraint.message}`));
|
|
160
155
|
log(io, chalk.red(` ${r.constraint.rule}`));
|
|
@@ -162,31 +157,31 @@ export async function check(
|
|
|
162
157
|
}
|
|
163
158
|
});
|
|
164
159
|
log(io);
|
|
165
|
-
log(io, chalk.red('🛑
|
|
160
|
+
log(io, chalk.red('🛑 error 级约束检查失败,请修复后再提交'));
|
|
166
161
|
return {
|
|
167
162
|
kind: 'fail',
|
|
168
|
-
reason: `
|
|
163
|
+
reason: `error-severity constraint violated: ${errorViolations.map(r => r.constraint?.id ?? 'unknown').join(', ')}`,
|
|
169
164
|
};
|
|
170
165
|
}
|
|
171
166
|
|
|
172
|
-
//
|
|
167
|
+
// severity='warning'(告警不阻断)
|
|
173
168
|
if (result.warningCount > 0) {
|
|
174
|
-
log(io, chalk.yellow(`⚠️
|
|
175
|
-
result.
|
|
169
|
+
log(io, chalk.yellow(`⚠️ warning 级约束警告: ${result.warningCount} 条`));
|
|
170
|
+
result.warnings.filter(r => !r.satisfied).forEach(r => {
|
|
176
171
|
if (r.constraint) {
|
|
177
172
|
log(io, chalk.yellow(` - ${r.constraint.id}: ${r.constraint.message}`));
|
|
178
173
|
logEvidence(io, r, 'yellow');
|
|
179
174
|
}
|
|
180
175
|
});
|
|
181
|
-
} else if (result.
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
log(io, chalk.green(`✅
|
|
176
|
+
} else if (result.warnings.length > 0) {
|
|
177
|
+
const evaluatedWarnings = result.warnings.filter(r => !r.skipped);
|
|
178
|
+
const passedWarnings = evaluatedWarnings.filter(r => r.satisfied).length;
|
|
179
|
+
log(io, chalk.green(`✅ warning 级约束: ${passedWarnings}/${evaluatedWarnings.length} 通过`));
|
|
185
180
|
}
|
|
186
181
|
|
|
187
182
|
// 提示:通过但带证据(harness#119)——与本次变更无因果的仓库级漂移在此露出,
|
|
188
183
|
// 不判违规、不改 exit code,只保证「看得见且能自己修」
|
|
189
|
-
const hints = [...result.
|
|
184
|
+
const hints = [...result.errors, ...result.warnings].filter(
|
|
190
185
|
r => !r.skipped && r.satisfied && (r.evidence?.length ?? 0) > 0
|
|
191
186
|
);
|
|
192
187
|
if (hints.length > 0) {
|
|
@@ -202,29 +197,6 @@ export async function check(
|
|
|
202
197
|
});
|
|
203
198
|
}
|
|
204
199
|
|
|
205
|
-
// 注入漂移校验(ADR-0001 决策 7):黄色警告块,不改 exit code、不影响门禁结果。
|
|
206
|
-
// 无漂移/未注入零输出;漂移检测自身异常静默吞掉,绝不影响 check。
|
|
207
|
-
try {
|
|
208
|
-
// 生效集直接用本 run 那一份:一遍算完,且比对对象就是本次实际执法的规则集(ADR-0023 步骤 4.5)
|
|
209
|
-
const drift = detectInjectionDrift(runEnv, undefined, constraintsFromMerged(merged));
|
|
210
|
-
if (drift.hasDrift) {
|
|
211
|
-
log(io);
|
|
212
|
-
log(io, chalk.yellow(`⚠️ 检测到 ${drift.injectionFile ?? '治理文档'} 约束注入漂移(仅警告,不阻断):`));
|
|
213
|
-
if (drift.versionDrift) {
|
|
214
|
-
log(io, chalk.yellow(` ⚠️⚠️ 注入段版本 (${drift.versionDrift.actual}) ≠ 已安装 harness 版本 (${drift.versionDrift.expected}):agent 上下文中的规则与已安装 harness 版本不一致`));
|
|
215
|
-
}
|
|
216
|
-
if (drift.contentDrift) {
|
|
217
|
-
log(io, chalk.yellow(` 内容漂移: 缺失 ${drift.contentDrift.missing.length} 条 / 多余 ${drift.contentDrift.extra.length} 条(条目级差异见 harness constraints report)`));
|
|
218
|
-
}
|
|
219
|
-
if (drift.duplicateHeading) {
|
|
220
|
-
log(io, chalk.yellow(` 检测到重复的 "${GOVERNANCE_HEADING}" 章节`));
|
|
221
|
-
}
|
|
222
|
-
log(io, chalk.yellow(` 修复: ${drift.fixHint}`));
|
|
223
|
-
}
|
|
224
|
-
} catch {
|
|
225
|
-
// 漂移检测失败不影响 check 结果
|
|
226
|
-
}
|
|
227
|
-
|
|
228
200
|
log(io);
|
|
229
201
|
log(io, chalk.green('✅ 约束检查通过'));
|
|
230
202
|
|
|
@@ -288,37 +260,26 @@ async function getSmartHint(projectPath: string, stateIO: StateIO): Promise<stri
|
|
|
288
260
|
}
|
|
289
261
|
|
|
290
262
|
/**
|
|
291
|
-
*
|
|
263
|
+
* 列出所有约束(按 severity 分组)
|
|
292
264
|
*/
|
|
293
265
|
/** optionRoutes 一律以 (options) 调用;--list 不消费检查选项,形参仅占位 */
|
|
294
266
|
export function listLaws(_options: Partial<CheckOptions> = {}, io: CommandIO = processIO): CommandResult {
|
|
295
267
|
log(io, chalk.blue('\n📜 所有约束:\n'));
|
|
296
268
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
log(io,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
log(io);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
// Prompts(ADR-0001:纯注入层,不执行检查)
|
|
316
|
-
log(io, chalk.blue('🔵 提示 (Prompts) - 纯文本注入,不参与检查:\n'));
|
|
317
|
-
Object.values(PROMPTS).forEach(constraint => {
|
|
318
|
-
log(io, chalk.blue(` ${constraint.id}`));
|
|
319
|
-
log(io, chalk.gray(` ${constraint.rule}`));
|
|
320
|
-
log(io, chalk.gray(` ${constraint.message}`));
|
|
321
|
-
log(io);
|
|
322
|
-
});
|
|
269
|
+
const groups: Array<{ severity: 'error' | 'warning'; heading: string; paint: (s: string) => string }> = [
|
|
270
|
+
{ severity: 'error', heading: '🔴 error 级 - 违规即阻断,无例外:', paint: chalk.red },
|
|
271
|
+
{ severity: 'warning', heading: '🟡 warning 级 - 违规发警告但不阻止:', paint: chalk.yellow },
|
|
272
|
+
];
|
|
273
|
+
for (const group of groups) {
|
|
274
|
+
log(io, group.paint(group.heading + '\n'));
|
|
275
|
+
Object.values(CONSTRAINTS)
|
|
276
|
+
.filter(c => c.severity === group.severity)
|
|
277
|
+
.forEach(constraint => {
|
|
278
|
+
log(io, group.paint(` ${constraint.id}`));
|
|
279
|
+
log(io, chalk.gray(` ${constraint.rule}`));
|
|
280
|
+
log(io, chalk.gray(` ${constraint.message}`));
|
|
281
|
+
log(io);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
323
284
|
return { kind: 'ok' };
|
|
324
285
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* harness constraints report —— 约束使用报告(ADR-0001 决策 3/4
|
|
2
|
+
* harness constraints report —— 约束使用报告(ADR-0001 决策 3/4,ADR-0029 收窄)
|
|
3
3
|
*
|
|
4
4
|
* 只读。面向"人审约束"场景:
|
|
5
|
-
* - check
|
|
5
|
+
* - check 约束统计表(total/pass/fail/skip、fail 率、首次/最近触发)
|
|
6
6
|
* - 退役候选诊断(零触发/零拦截/不可评估/高噪),retire 交互模式的数据源
|
|
7
|
-
* - 当前生效 prompt 注入清单
|
|
8
7
|
* - 配置健康(unknownIds 残留提示)
|
|
9
8
|
* - --export [file]:脱敏 markdown 摘要(不含项目路径)
|
|
10
9
|
*/
|
|
@@ -21,8 +20,6 @@ import {
|
|
|
21
20
|
type DiagnoseThresholds,
|
|
22
21
|
} from '../../core/constraints/usage-report';
|
|
23
22
|
import { getConstraintsMeta } from './constraints';
|
|
24
|
-
import { detectInjectionDrift, type InjectionDrift } from '../../core/constraints/injection-drift';
|
|
25
|
-
import { GOVERNANCE_HEADING } from '../../core/constraints/injection-writer';
|
|
26
23
|
import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
27
24
|
|
|
28
25
|
export interface ConstraintsReportOptions {
|
|
@@ -75,7 +72,7 @@ export function renderExportMarkdown(
|
|
|
75
72
|
}
|
|
76
73
|
lines.push('');
|
|
77
74
|
|
|
78
|
-
lines.push('##
|
|
75
|
+
lines.push('## 统计表');
|
|
79
76
|
lines.push('');
|
|
80
77
|
lines.push('| id | total | pass | fail | skip | fail 率 | 首次触发 | 最近触发 |');
|
|
81
78
|
lines.push('| --- | ---: | ---: | ---: | ---: | ---: | --- | --- |');
|
|
@@ -109,43 +106,6 @@ export function renderExportMarkdown(
|
|
|
109
106
|
return lines.join('\n');
|
|
110
107
|
}
|
|
111
108
|
|
|
112
|
-
/**
|
|
113
|
-
* 渲染"注入漂移"小节的文本行(ADR-0001 决策 7:详细条目级差异进 report)
|
|
114
|
-
*
|
|
115
|
-
* 返回纯文本行数组(不含 chalk 着色),调用方逐行 console.log。
|
|
116
|
-
*/
|
|
117
|
-
export function renderInjectionDriftLines(drift: InjectionDrift): string[] {
|
|
118
|
-
const lines: string[] = [];
|
|
119
|
-
if (drift.notInjected) {
|
|
120
|
-
lines.push(' CLAUDE.md / AGENTS.md 均无约束注入标记段(未注入,不算漂移);可运行 npx @dommaker/harness init 注入');
|
|
121
|
-
if (drift.duplicateHeading) {
|
|
122
|
-
lines.push(` 另检测到多个 "${GOVERNANCE_HEADING}" 标题(历史遗留重复章节),建议手工清理后重跑 npx @dommaker/harness init`);
|
|
123
|
-
}
|
|
124
|
-
return lines;
|
|
125
|
-
}
|
|
126
|
-
if (!drift.hasDrift) {
|
|
127
|
-
lines.push(' 无漂移');
|
|
128
|
-
return lines;
|
|
129
|
-
}
|
|
130
|
-
if (drift.versionDrift) {
|
|
131
|
-
lines.push(` 版本漂移: 注入段版本 (${drift.versionDrift.actual}) ≠ 当前 harness 版本 (${drift.versionDrift.expected})`);
|
|
132
|
-
}
|
|
133
|
-
if (drift.contentDrift) {
|
|
134
|
-
lines.push(' 内容漂移(条目级差异):');
|
|
135
|
-
for (const l of drift.contentDrift.missing) {
|
|
136
|
-
lines.push(` 缺失: ${l}`);
|
|
137
|
-
}
|
|
138
|
-
for (const l of drift.contentDrift.extra) {
|
|
139
|
-
lines.push(` 多余: ${l}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (drift.duplicateHeading) {
|
|
143
|
-
lines.push(` 重复章节: 标记段之外还存在另一个 "${GOVERNANCE_HEADING}" 标题`);
|
|
144
|
-
}
|
|
145
|
-
lines.push(` 修复: ${drift.fixHint}`);
|
|
146
|
-
return lines;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
109
|
/**
|
|
150
110
|
* CLI handler: harness constraints report
|
|
151
111
|
*/
|
|
@@ -173,7 +133,7 @@ export async function constraintsReport(options: ConstraintsReportOptions = {},
|
|
|
173
133
|
log(io);
|
|
174
134
|
|
|
175
135
|
// 统计表
|
|
176
|
-
log(io, chalk.bold('
|
|
136
|
+
log(io, chalk.bold('统计表:'));
|
|
177
137
|
for (const s of report.stats) {
|
|
178
138
|
log(io, ` ${renderStatsRow(s)}`);
|
|
179
139
|
}
|
|
@@ -191,13 +151,6 @@ export async function constraintsReport(options: ConstraintsReportOptions = {},
|
|
|
191
151
|
}
|
|
192
152
|
log(io);
|
|
193
153
|
|
|
194
|
-
// 注入清单
|
|
195
|
-
log(io, chalk.bold(`当前生效 prompt 注入(${report.activePromptIds.length} 条):`));
|
|
196
|
-
for (const id of report.activePromptIds) {
|
|
197
|
-
log(io, ` ${id}`);
|
|
198
|
-
}
|
|
199
|
-
log(io);
|
|
200
|
-
|
|
201
154
|
// 配置健康
|
|
202
155
|
if (report.lint.unknownIds.length > 0) {
|
|
203
156
|
log(io, chalk.yellow('⚠️ 配置健康:'));
|
|
@@ -207,14 +160,6 @@ export async function constraintsReport(options: ConstraintsReportOptions = {},
|
|
|
207
160
|
log(io);
|
|
208
161
|
}
|
|
209
162
|
|
|
210
|
-
// 注入漂移(ADR-0001 决策 7:条目级差异 + 修复指引;--export 摘要不含本地漂移状态)
|
|
211
|
-
log(io, chalk.bold('注入漂移:'));
|
|
212
|
-
const drift = detectInjectionDrift(projectRoot);
|
|
213
|
-
for (const line of renderInjectionDriftLines(drift)) {
|
|
214
|
-
log(io, drift.hasDrift ? chalk.yellow(line) : line);
|
|
215
|
-
}
|
|
216
|
-
log(io);
|
|
217
|
-
|
|
218
163
|
// --export
|
|
219
164
|
if (options.export !== undefined && options.export !== false) {
|
|
220
165
|
const version = getConstraintsMeta().version;
|