@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
|
@@ -5,29 +5,38 @@
|
|
|
5
5
|
* bootstrapHarnessSync uses synchronous fs reads and is more straightforward.
|
|
6
6
|
*
|
|
7
7
|
* Uses temp directories to provide harness config.
|
|
8
|
+
*
|
|
9
|
+
* ADR-0027(#170):hooks 管线面删除后本层只剩组合根职责——加载项目配置、
|
|
10
|
+
* 装配 checker / SessionManager / TraceCollector。`hookDefinitions` / `hookConfigs`
|
|
11
|
+
* 两参数与 `hooks` / `pipeline` 两字段随之消失,形状由编译期 + 运行期双钉冻结
|
|
12
|
+
* (手法照 ADR-0022 对 AC-007 的改写)。
|
|
8
13
|
*/
|
|
9
14
|
|
|
10
15
|
import * as fs from 'fs';
|
|
11
16
|
import * as path from 'path';
|
|
12
17
|
import * as yaml from 'js-yaml';
|
|
13
18
|
import { bootstrapHarnessSync, bootstrapHarness } from '../bootstrap';
|
|
14
|
-
import type { HookConfig, HookDefinition } from '../types';
|
|
15
|
-
|
|
16
|
-
function makeHookConfig(name: string, overrides: Partial<HookConfig> = {}): HookConfig {
|
|
17
|
-
return { name, enabled: true, errorStrategy: 'warn', ...overrides };
|
|
18
|
-
}
|
|
19
19
|
|
|
20
20
|
function setupTempDir(dir: string): string {
|
|
21
21
|
const harnessDir = path.join(dir, '.harness');
|
|
22
22
|
fs.mkdirSync(harnessDir, { recursive: true });
|
|
23
23
|
const config = {
|
|
24
24
|
preset: 'standard',
|
|
25
|
-
custom_constraints_file: 'custom-constraints.yml',
|
|
26
25
|
};
|
|
27
26
|
fs.writeFileSync(path.join(harnessDir, 'config.yml'), yaml.dump(config), 'utf-8');
|
|
28
27
|
return harnessDir;
|
|
29
28
|
}
|
|
30
29
|
|
|
30
|
+
/** 已删字段的编译期钉:回灌(重新声明该字段)即 TS2578 红。纯读,不产生任何落盘副作用。 */
|
|
31
|
+
function assertPipelineSurfaceUnreachable(result: ReturnType<typeof bootstrapHarnessSync>): void {
|
|
32
|
+
// @ts-expect-error hooks 字段随 ADR-0027 管线面删除
|
|
33
|
+
const gone = result.hooks;
|
|
34
|
+
expect(gone).toBeUndefined();
|
|
35
|
+
// @ts-expect-error pipeline 字段随 ADR-0027 管线面删除
|
|
36
|
+
const gone2 = result.pipeline;
|
|
37
|
+
expect(gone2).toBeUndefined();
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
describe('bootstrapHarness', () => {
|
|
32
41
|
let tempDir: string;
|
|
33
42
|
|
|
@@ -44,10 +53,11 @@ describe('bootstrapHarness', () => {
|
|
|
44
53
|
const result = await bootstrapHarness(tempDir);
|
|
45
54
|
expect(result).toHaveProperty('checker');
|
|
46
55
|
expect(result).toHaveProperty('sessions');
|
|
47
|
-
expect(result).toHaveProperty('hooks');
|
|
48
|
-
expect(result).toHaveProperty('pipeline');
|
|
49
56
|
expect(result).toHaveProperty('projectPath', tempDir);
|
|
50
57
|
expect(result).toHaveProperty('mergedConstraints');
|
|
58
|
+
expect(Object.keys(result).sort()).toEqual(
|
|
59
|
+
['checker', 'mergedConstraints', 'projectPath', 'sessions'],
|
|
60
|
+
);
|
|
51
61
|
});
|
|
52
62
|
|
|
53
63
|
it('initializes with the provided project path', async () => {
|
|
@@ -55,33 +65,8 @@ describe('bootstrapHarness', () => {
|
|
|
55
65
|
expect(result.projectPath).toBe(tempDir);
|
|
56
66
|
});
|
|
57
67
|
|
|
58
|
-
it('registers hook definitions when provided (with configs)', async () => {
|
|
59
|
-
const hookDef: HookDefinition = {
|
|
60
|
-
name: 'async-hook',
|
|
61
|
-
phase: 'before',
|
|
62
|
-
execute: async () => ({ passed: true }),
|
|
63
|
-
};
|
|
64
|
-
const result = await bootstrapHarness(tempDir, [hookDef], [makeHookConfig('async-hook')]);
|
|
65
|
-
expect(result.hooks.listNames()).toContain('async-hook');
|
|
66
|
-
expect(result.hooks.get('async-hook')?.errorStrategy).toBe('warn');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('throws when definitions are provided without configs (#159)', async () => {
|
|
70
|
-
const hookDef: HookDefinition = {
|
|
71
|
-
name: 'async-hook',
|
|
72
|
-
phase: 'before',
|
|
73
|
-
execute: async () => ({ passed: true }),
|
|
74
|
-
};
|
|
75
|
-
await expect(bootstrapHarness(tempDir, [hookDef])).rejects.toThrow(/HookConfig/);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('does not register hooks when no definitions provided', async () => {
|
|
79
|
-
const result = await bootstrapHarness(tempDir, []);
|
|
80
|
-
expect(result.hooks.listNames()).toEqual([]);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
68
|
it('uses process.cwd() when no project path is given', async () => {
|
|
84
|
-
const result = await bootstrapHarness(
|
|
69
|
+
const result = await bootstrapHarness();
|
|
85
70
|
expect(result).toHaveProperty('projectPath');
|
|
86
71
|
expect(result.projectPath).toBeDefined();
|
|
87
72
|
});
|
|
@@ -99,7 +84,7 @@ describe('bootstrapHarness', () => {
|
|
|
99
84
|
const result = await bootstrapHarness(noConfigDir);
|
|
100
85
|
expect(result).toHaveProperty('checker');
|
|
101
86
|
expect(result).toHaveProperty('projectPath', noConfigDir);
|
|
102
|
-
expect(result.mergedConstraints).toHaveProperty('
|
|
87
|
+
expect(result.mergedConstraints).toHaveProperty('constraints');
|
|
103
88
|
} finally {
|
|
104
89
|
fs.rmSync(noConfigDir, { recursive: true, force: true });
|
|
105
90
|
}
|
|
@@ -122,10 +107,9 @@ describe('bootstrapHarnessSync', () => {
|
|
|
122
107
|
const result = bootstrapHarnessSync(tempDir);
|
|
123
108
|
expect(result).toHaveProperty('checker');
|
|
124
109
|
expect(result).toHaveProperty('sessions');
|
|
125
|
-
expect(result).toHaveProperty('hooks');
|
|
126
|
-
expect(result).toHaveProperty('pipeline');
|
|
127
110
|
expect(result).toHaveProperty('projectPath', tempDir);
|
|
128
111
|
expect(result).toHaveProperty('mergedConstraints');
|
|
112
|
+
assertPipelineSurfaceUnreachable(result);
|
|
129
113
|
});
|
|
130
114
|
|
|
131
115
|
it('initializes with the provided project path', () => {
|
|
@@ -136,38 +120,13 @@ describe('bootstrapHarnessSync', () => {
|
|
|
136
120
|
it('initializes ConstraintChecker with merged constraints', () => {
|
|
137
121
|
const result = bootstrapHarnessSync(tempDir);
|
|
138
122
|
// Check that mergedConstraints has the expected structure
|
|
139
|
-
expect(result.mergedConstraints).toHaveProperty('
|
|
140
|
-
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('registers hook definitions when provided (with configs)', () => {
|
|
144
|
-
const hookDef: HookDefinition = {
|
|
145
|
-
name: 'test-hook',
|
|
146
|
-
phase: 'before',
|
|
147
|
-
execute: async () => ({ passed: true }),
|
|
148
|
-
};
|
|
149
|
-
const result = bootstrapHarnessSync(tempDir, [hookDef], [makeHookConfig('test-hook')]);
|
|
150
|
-
expect(result.hooks.listNames()).toContain('test-hook');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('throws when definitions are provided without configs (#159)', () => {
|
|
154
|
-
const hookDef: HookDefinition = {
|
|
155
|
-
name: 'test-hook',
|
|
156
|
-
phase: 'before',
|
|
157
|
-
execute: async () => ({ passed: true }),
|
|
158
|
-
};
|
|
159
|
-
expect(() => bootstrapHarnessSync(tempDir, [hookDef])).toThrow(/HookConfig/);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('does not register hooks when no definitions provided', () => {
|
|
163
|
-
const result = bootstrapHarnessSync(tempDir, []);
|
|
164
|
-
expect(result.hooks.listNames()).toEqual([]);
|
|
165
|
-
});
|
|
123
|
+
expect(result.mergedConstraints).toHaveProperty('constraints');
|
|
124
|
+
});
|
|
166
125
|
|
|
167
126
|
it('uses process.cwd() when no project path is given', () => {
|
|
168
127
|
// We cannot easily test process.cwd() fallback without mocking cwd,
|
|
169
128
|
// but we can verify the function accepts undefined
|
|
170
|
-
const result = bootstrapHarnessSync(undefined
|
|
129
|
+
const result = bootstrapHarnessSync(undefined);
|
|
171
130
|
// Should not throw with process.cwd() — harness directory may not exist
|
|
172
131
|
expect(result).toHaveProperty('projectPath');
|
|
173
132
|
expect(result.projectPath).toBeDefined();
|
|
@@ -182,16 +141,6 @@ describe('bootstrapHarnessSync', () => {
|
|
|
182
141
|
expect(fs.existsSync(sessionDir)).toBe(true);
|
|
183
142
|
});
|
|
184
143
|
|
|
185
|
-
it('creates a working HookPipeline', () => {
|
|
186
|
-
const hookDef: HookDefinition = {
|
|
187
|
-
name: 'pipeline-hook',
|
|
188
|
-
phase: 'after',
|
|
189
|
-
execute: async () => ({ passed: true, data: 'ok' }),
|
|
190
|
-
};
|
|
191
|
-
const result = bootstrapHarnessSync(tempDir, [hookDef], [makeHookConfig('pipeline-hook')]);
|
|
192
|
-
expect(result.pipeline).toBeDefined();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
144
|
it('loads from .harness/config.yml when present', () => {
|
|
196
145
|
// Write a custom config with specific settings
|
|
197
146
|
const harnessDir = path.join(tempDir, '.harness');
|
package/src/hooks/bootstrap.ts
CHANGED
|
@@ -8,21 +8,22 @@
|
|
|
8
8
|
* Consumer 用法:
|
|
9
9
|
* ```typescript
|
|
10
10
|
* const harness = await bootstrapHarness('/path/to/project');
|
|
11
|
-
* // harness.checker, harness.
|
|
11
|
+
* // harness.checker, harness.sessions, harness.mergedConstraints
|
|
12
12
|
* ```
|
|
13
13
|
*
|
|
14
14
|
* checker 的 trace 记录器在此接线(harness#88:core 不上行依赖 monitoring),
|
|
15
15
|
* 且锚在本函数收到的 projectPath 上(#139:落点跟根走,不落调用方 cwd)。
|
|
16
|
+
*
|
|
17
|
+
* ADR-0027(#170):hooks 管线面(registry/pipeline/config/types)双仓零生产消费者,
|
|
18
|
+
* 整体删除;本层的 `hookDefinitions` / `hookConfigs` 两参数与 `hooks` / `pipeline`
|
|
19
|
+
* 两字段随之消失——生产唯一调用方本就无参调用、不访问被删字段。
|
|
16
20
|
*/
|
|
17
21
|
|
|
18
22
|
import { ConstraintChecker } from '../core/constraints/checker';
|
|
19
23
|
import { SessionManager } from '../context/session-manager';
|
|
20
24
|
import { ProjectConfigLoader } from '../core/project-config-loader';
|
|
21
25
|
import { TraceCollector } from '../monitoring/traces';
|
|
22
|
-
import { HookRegistry } from './registry';
|
|
23
|
-
import { HookPipeline } from './pipeline';
|
|
24
26
|
import type { MergedConstraintsConfig } from '../types/project-config';
|
|
25
|
-
import type { HookConfig, HookDefinition } from './types';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* 异步加载项目配置(S9:异步 I/O)
|
|
@@ -50,46 +51,19 @@ export interface HarnessBootstrap {
|
|
|
50
51
|
checker: ConstraintChecker;
|
|
51
52
|
/** 会话管理器 */
|
|
52
53
|
sessions: SessionManager;
|
|
53
|
-
/** Hook 注册表 */
|
|
54
|
-
hooks: HookRegistry;
|
|
55
|
-
/** Hook 管线 */
|
|
56
|
-
pipeline: HookPipeline;
|
|
57
54
|
/** 项目路径 */
|
|
58
55
|
projectPath: string;
|
|
59
56
|
/** 合并后的约束配置 */
|
|
60
57
|
mergedConstraints: MergedConstraintsConfig;
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
/**
|
|
64
|
-
* 注册初始 hook(定义必须配对配置声明表——#159 起 HookConfig 是
|
|
65
|
-
* enabled / errorStrategy 的唯一声明点,缺表即抛错)
|
|
66
|
-
*/
|
|
67
|
-
function registerInitialHooks(
|
|
68
|
-
hooks: HookRegistry,
|
|
69
|
-
hookDefinitions: HookDefinition[] | undefined,
|
|
70
|
-
hookConfigs: HookConfig[] | undefined
|
|
71
|
-
): void {
|
|
72
|
-
if (!hookDefinitions || hookDefinitions.length === 0) return;
|
|
73
|
-
if (!hookConfigs) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
'[harness] bootstrap 失败:注册 hook 定义必须同时提供 HookConfig 声明表' +
|
|
76
|
-
'(enabled / errorStrategy 的唯一声明点)。'
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
hooks.registerAll(hookDefinitions, hookConfigs);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
60
|
/**
|
|
83
61
|
* 初始化 harness 运行环境(异步,不阻塞事件循环)
|
|
84
62
|
*
|
|
85
63
|
* @param projectPath 项目根路径
|
|
86
|
-
* @param hookDefinitions 可选,初始化时注册的 hook
|
|
87
|
-
* @param hookConfigs 可选,与 hookDefinitions 配对的配置声明表(注册定义时必填)
|
|
88
64
|
*/
|
|
89
65
|
export async function bootstrapHarness(
|
|
90
|
-
projectPath?: string
|
|
91
|
-
hookDefinitions?: HookDefinition[],
|
|
92
|
-
hookConfigs?: HookConfig[]
|
|
66
|
+
projectPath?: string
|
|
93
67
|
): Promise<HarnessBootstrap> {
|
|
94
68
|
const resolvedPath = projectPath || process.cwd();
|
|
95
69
|
|
|
@@ -101,17 +75,10 @@ export async function bootstrapHarness(
|
|
|
101
75
|
const checker = new ConstraintChecker(new TraceCollector({ projectPath: resolvedPath }));
|
|
102
76
|
|
|
103
77
|
const sessions = new SessionManager(resolvedPath);
|
|
104
|
-
const hooks = new HookRegistry();
|
|
105
|
-
const pipeline = new HookPipeline(hooks);
|
|
106
|
-
|
|
107
|
-
// 3. 注册初始 hook
|
|
108
|
-
registerInitialHooks(hooks, hookDefinitions, hookConfigs);
|
|
109
78
|
|
|
110
79
|
return {
|
|
111
80
|
checker,
|
|
112
81
|
sessions,
|
|
113
|
-
hooks,
|
|
114
|
-
pipeline,
|
|
115
82
|
projectPath: resolvedPath,
|
|
116
83
|
mergedConstraints,
|
|
117
84
|
};
|
|
@@ -121,11 +88,11 @@ export async function bootstrapHarness(
|
|
|
121
88
|
* 同步 bootstrap(兼容不支持 top-level await 的环境)
|
|
122
89
|
*
|
|
123
90
|
* 配置加载仍为同步(readFileSync),其他组件初始化同上。
|
|
91
|
+
*
|
|
92
|
+
* @param projectPath 项目根路径
|
|
124
93
|
*/
|
|
125
94
|
export function bootstrapHarnessSync(
|
|
126
|
-
projectPath?: string
|
|
127
|
-
hookDefinitions?: HookDefinition[],
|
|
128
|
-
hookConfigs?: HookConfig[]
|
|
95
|
+
projectPath?: string
|
|
129
96
|
): HarnessBootstrap {
|
|
130
97
|
const resolvedPath = projectPath || process.cwd();
|
|
131
98
|
|
|
@@ -136,16 +103,10 @@ export function bootstrapHarnessSync(
|
|
|
136
103
|
const checker = new ConstraintChecker(new TraceCollector({ projectPath: resolvedPath }));
|
|
137
104
|
|
|
138
105
|
const sessions = new SessionManager(resolvedPath);
|
|
139
|
-
const hooks = new HookRegistry();
|
|
140
|
-
const pipeline = new HookPipeline(hooks);
|
|
141
|
-
|
|
142
|
-
registerInitialHooks(hooks, hookDefinitions, hookConfigs);
|
|
143
106
|
|
|
144
107
|
return {
|
|
145
108
|
checker,
|
|
146
109
|
sessions,
|
|
147
|
-
hooks,
|
|
148
|
-
pipeline,
|
|
149
110
|
projectPath: resolvedPath,
|
|
150
111
|
mergedConstraints,
|
|
151
112
|
};
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hooks 模块
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* ADR-0027(#170)起本层只剩 `bootstrap.ts` 一个面:通用 hook 管线
|
|
5
|
+
* (registry / pipeline / config / types 四文件)双仓零生产消费者,整体删除。
|
|
6
|
+
* 目录名沿用历史,职责是 harness 运行环境的组合根。
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
HookErrorStrategy,
|
|
11
|
-
HookConfig,
|
|
12
|
-
HookDefinition,
|
|
13
|
-
EffectiveHook,
|
|
14
|
-
HookResult,
|
|
15
|
-
HookExecutionRecord,
|
|
16
|
-
PipelineResult,
|
|
17
|
-
} from './types';
|
|
18
|
-
export { HookRegistry, assertHookRegistryClosed } from './registry';
|
|
19
|
-
export { HookPipeline } from './pipeline';
|
|
20
|
-
export { toErrorStrategy } from './config';
|
|
21
|
-
export {
|
|
22
|
-
bootstrapHarness,
|
|
23
|
-
bootstrapHarnessSync,
|
|
24
|
-
type HarnessBootstrap,
|
|
25
|
-
} from './bootstrap';
|
|
9
|
+
export { bootstrapHarness, bootstrapHarnessSync } from './bootstrap';
|
|
10
|
+
export type { HarnessBootstrap } from './bootstrap';
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 通用工程约束框架
|
|
5
5
|
*
|
|
6
|
-
* 约束体系(ADR-
|
|
7
|
-
* - check:可执行检查(
|
|
8
|
-
* -
|
|
6
|
+
* 约束体系(ADR-0029 severity 显式模型):
|
|
7
|
+
* - 全部约束 kind='check':可执行检查(error 阻断 / warning 告警)
|
|
8
|
+
* - 纯文本提示层已整体关停(ADR-0029)
|
|
9
9
|
*
|
|
10
10
|
* 门禁系统:
|
|
11
11
|
* - PassesGate:测试门控
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
* - CheckpointValidator:检查点验证
|
|
17
17
|
*
|
|
18
18
|
* 公共导出(ADR-0003):显式清单,禁 export *。
|
|
19
|
-
* 收录标准:属 harness 定位(约束数据 / 执行引擎 /
|
|
19
|
+
* 收录标准:属 harness 定位(约束数据 / 执行引擎 / 知识基建)、
|
|
20
20
|
* 实现真实可用、无同名冲突。内部 seam
|
|
21
21
|
* (ConstraintChecker / constraintChecker / ProjectConfigLoader)不进公共清单。
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
// ========================================
|
|
25
|
-
//
|
|
25
|
+
// 约束类型(severity 显式模型)
|
|
26
26
|
// ========================================
|
|
27
27
|
export {
|
|
28
28
|
ConstraintViolationError,
|
|
@@ -31,7 +31,7 @@ export type {
|
|
|
31
31
|
Constraint,
|
|
32
32
|
ConstraintId,
|
|
33
33
|
ConstraintKind,
|
|
34
|
-
|
|
34
|
+
ConstraintSeverity,
|
|
35
35
|
ConstraintTrigger,
|
|
36
36
|
ConstraintContext,
|
|
37
37
|
ConstraintResult,
|
|
@@ -43,9 +43,7 @@ export type {
|
|
|
43
43
|
// 约束数据(内置定义 + 生效集)
|
|
44
44
|
// ========================================
|
|
45
45
|
export {
|
|
46
|
-
|
|
47
|
-
GUIDELINES,
|
|
48
|
-
PROMPTS,
|
|
46
|
+
CONSTRAINTS,
|
|
49
47
|
getAllConstraints,
|
|
50
48
|
getConstraint,
|
|
51
49
|
findConstraintsByTrigger,
|
|
@@ -67,17 +65,10 @@ export {
|
|
|
67
65
|
export type { CheckConstraintsOptions } from './core/constraints/checker';
|
|
68
66
|
|
|
69
67
|
// ========================================
|
|
70
|
-
//
|
|
68
|
+
// 约束缓存
|
|
71
69
|
// ========================================
|
|
72
70
|
export { CheckCache } from './core/constraints/check-cache';
|
|
73
71
|
export type { CheckCacheConfig, CheckSamplingConfig } from './core/constraints/check-cache';
|
|
74
|
-
export { renderConstraintsByTrigger } from './core/constraints/agent-prompt-renderer';
|
|
75
|
-
export type { RenderConstraintsByTriggerOptions } from './core/constraints/agent-prompt-renderer';
|
|
76
|
-
export {
|
|
77
|
-
CONSTRAINTS_START_MARKER,
|
|
78
|
-
CONSTRAINTS_END_MARKER,
|
|
79
|
-
renderConstraintsSection,
|
|
80
|
-
} from './core/constraints/injection-renderer';
|
|
81
72
|
|
|
82
73
|
// ========================================
|
|
83
74
|
// 检查点与验证器
|
|
@@ -342,25 +333,13 @@ export { getCriticalArtifacts, verifyReleaseArtifacts } from './release';
|
|
|
342
333
|
export type { ArtifactIntegrityResult } from './release';
|
|
343
334
|
|
|
344
335
|
// ========================================
|
|
345
|
-
//
|
|
336
|
+
// Harness 运行环境引导(bootstrap 单面;hooks 管线面已随 ADR-0027 删除)
|
|
346
337
|
// ========================================
|
|
347
338
|
export {
|
|
348
|
-
HookRegistry,
|
|
349
|
-
assertHookRegistryClosed,
|
|
350
|
-
HookPipeline,
|
|
351
|
-
toErrorStrategy,
|
|
352
339
|
bootstrapHarness,
|
|
353
340
|
bootstrapHarnessSync,
|
|
354
341
|
} from './hooks';
|
|
355
342
|
export type {
|
|
356
|
-
HookDefinition,
|
|
357
|
-
HookConfig,
|
|
358
|
-
EffectiveHook,
|
|
359
|
-
HookErrorStrategy,
|
|
360
|
-
HookExecutionRecord,
|
|
361
|
-
HookPhase,
|
|
362
|
-
HookResult,
|
|
363
|
-
PipelineResult,
|
|
364
343
|
HarnessBootstrap,
|
|
365
344
|
} from './hooks';
|
|
366
345
|
|
|
@@ -385,7 +364,6 @@ export type {
|
|
|
385
364
|
export type {
|
|
386
365
|
ProjectConfig,
|
|
387
366
|
MergedConstraintsConfig,
|
|
388
|
-
CustomConstraintDefinition,
|
|
389
367
|
GovernanceConfig,
|
|
390
368
|
CapabilitiesConfig,
|
|
391
369
|
ChangelogConfig,
|
|
@@ -42,7 +42,7 @@ describe('TraceCollector', () => {
|
|
|
42
42
|
const disabledCollector = new TraceCollector({ enabled: false });
|
|
43
43
|
disabledCollector.record({
|
|
44
44
|
constraintId: 'test',
|
|
45
|
-
|
|
45
|
+
severity: 'error',
|
|
46
46
|
timestamp: Date.now(),
|
|
47
47
|
result: 'pass',
|
|
48
48
|
});
|
|
@@ -54,7 +54,7 @@ describe('TraceCollector', () => {
|
|
|
54
54
|
it('should append trace to file', () => {
|
|
55
55
|
collector.record({
|
|
56
56
|
constraintId: 'test',
|
|
57
|
-
|
|
57
|
+
severity: 'error',
|
|
58
58
|
timestamp: Date.now(),
|
|
59
59
|
result: 'pass',
|
|
60
60
|
});
|
|
@@ -64,7 +64,7 @@ describe('TraceCollector', () => {
|
|
|
64
64
|
|
|
65
65
|
describe('recordPass()', () => {
|
|
66
66
|
it('should record pass trace', () => {
|
|
67
|
-
collector.recordPass('test_constraint', '
|
|
67
|
+
collector.recordPass('test_constraint', 'error');
|
|
68
68
|
const call = mockFs.appendFileSync.mock.calls[0];
|
|
69
69
|
const content = JSON.parse(call[1] as string);
|
|
70
70
|
expect(content.result).toBe('pass');
|
|
@@ -73,7 +73,7 @@ describe('TraceCollector', () => {
|
|
|
73
73
|
|
|
74
74
|
describe('recordFail()', () => {
|
|
75
75
|
it('should record fail trace', () => {
|
|
76
|
-
collector.recordFail('test_constraint', '
|
|
76
|
+
collector.recordFail('test_constraint', 'error');
|
|
77
77
|
const call = mockFs.appendFileSync.mock.calls[0];
|
|
78
78
|
const content = JSON.parse(call[1] as string);
|
|
79
79
|
expect(content.result).toBe('fail');
|
|
@@ -91,8 +91,8 @@ describe('TraceCollector', () => {
|
|
|
91
91
|
|
|
92
92
|
it('should parse all traces from file', () => {
|
|
93
93
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
94
|
-
'{"constraintId":"a","
|
|
95
|
-
'{"constraintId":"b","
|
|
94
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
|
|
95
|
+
'{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n'
|
|
96
96
|
);
|
|
97
97
|
const traces = collector.read();
|
|
98
98
|
expect(traces.length).toBe(2);
|
|
@@ -100,19 +100,19 @@ describe('TraceCollector', () => {
|
|
|
100
100
|
|
|
101
101
|
it('should apply constraintId filter', () => {
|
|
102
102
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
103
|
-
'{"constraintId":"a","
|
|
104
|
-
'{"constraintId":"b","
|
|
103
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
|
|
104
|
+
'{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n'
|
|
105
105
|
);
|
|
106
106
|
const traces = collector.read({ constraintId: 'a' });
|
|
107
107
|
expect(traces.length).toBe(1);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
it('should apply
|
|
110
|
+
it('should apply severity filter', () => {
|
|
111
111
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
112
|
-
'{"constraintId":"a","
|
|
113
|
-
'{"constraintId":"b","
|
|
112
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
|
|
113
|
+
'{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n'
|
|
114
114
|
);
|
|
115
|
-
const traces = collector.read({
|
|
115
|
+
const traces = collector.read({ severity: 'warning' });
|
|
116
116
|
expect(traces.length).toBe(1);
|
|
117
117
|
});
|
|
118
118
|
});
|
|
@@ -121,7 +121,7 @@ describe('TraceCollector', () => {
|
|
|
121
121
|
it('should read recent traces', () => {
|
|
122
122
|
const now = Date.now();
|
|
123
123
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
124
|
-
`{"constraintId":"new","
|
|
124
|
+
`{"constraintId":"new","severity":"error","timestamp":${now - 1000},"result":"pass"}\n`
|
|
125
125
|
);
|
|
126
126
|
const traces = collector.readRecent(1);
|
|
127
127
|
expect(traces.length).toBe(1);
|
|
@@ -131,7 +131,7 @@ describe('TraceCollector', () => {
|
|
|
131
131
|
describe('readByConstraint()', () => {
|
|
132
132
|
it('should read by constraint', () => {
|
|
133
133
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
134
|
-
'{"constraintId":"a","
|
|
134
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n'
|
|
135
135
|
);
|
|
136
136
|
const traces = collector.readByConstraint('a');
|
|
137
137
|
expect(traces.length).toBe(1);
|
|
@@ -143,7 +143,7 @@ describe('TraceCollector', () => {
|
|
|
143
143
|
mockFs.statSync.mockReturnValueOnce({ size: 11 * 1024 * 1024 } as any);
|
|
144
144
|
collector.record({
|
|
145
145
|
constraintId: 'test',
|
|
146
|
-
|
|
146
|
+
severity: 'error',
|
|
147
147
|
timestamp: Date.now(),
|
|
148
148
|
result: 'pass',
|
|
149
149
|
});
|
|
@@ -155,7 +155,7 @@ describe('TraceCollector', () => {
|
|
|
155
155
|
it('should return stats', () => {
|
|
156
156
|
mockFs.existsSync.mockReturnValueOnce(true);
|
|
157
157
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
158
|
-
'{"constraintId":"a","
|
|
158
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n'
|
|
159
159
|
);
|
|
160
160
|
mockFs.statSync.mockReturnValueOnce({ size: 1024 } as any);
|
|
161
161
|
const stats = collector.getStats();
|
|
@@ -166,9 +166,9 @@ describe('TraceCollector', () => {
|
|
|
166
166
|
describe('坏行容错(harness#82 裁决 4:原「抛」改 skip,签名与返回类型不变)', () => {
|
|
167
167
|
it('read() 单行损坏只丢该行,合法 trace 照常返回', () => {
|
|
168
168
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
169
|
-
'{"constraintId":"a","
|
|
169
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
|
|
170
170
|
'{"constraintId":"b","broken"\n' +
|
|
171
|
-
'{"constraintId":"c","
|
|
171
|
+
'{"constraintId":"c","severity":"warning","timestamp":200,"result":"fail"}\n'
|
|
172
172
|
);
|
|
173
173
|
const traces = collector.read();
|
|
174
174
|
expect(traces.map(t => t.constraintId)).toEqual(['a', 'c']);
|
|
@@ -176,9 +176,9 @@ describe('TraceCollector', () => {
|
|
|
176
176
|
|
|
177
177
|
it('read() 坏行不阻断过滤', () => {
|
|
178
178
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
179
|
-
'{"constraintId":"a","
|
|
179
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
|
|
180
180
|
'not-json\n' +
|
|
181
|
-
'{"constraintId":"a","
|
|
181
|
+
'{"constraintId":"a","severity":"error","timestamp":200,"result":"fail"}\n'
|
|
182
182
|
);
|
|
183
183
|
const traces = collector.read({ constraintId: 'a' });
|
|
184
184
|
expect(traces).toHaveLength(2);
|
|
@@ -187,8 +187,8 @@ describe('TraceCollector', () => {
|
|
|
187
187
|
it('getStats() 首末行坏时不抛:totalLines 保持原始行数,时间戳取合法记录', () => {
|
|
188
188
|
mockFs.readFileSync.mockReturnValueOnce(
|
|
189
189
|
'not-json\n' +
|
|
190
|
-
'{"constraintId":"a","
|
|
191
|
-
'{"constraintId":"b","
|
|
190
|
+
'{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
|
|
191
|
+
'{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n' +
|
|
192
192
|
'also-bad\n'
|
|
193
193
|
);
|
|
194
194
|
mockFs.statSync.mockReturnValueOnce({ size: 2048 } as any);
|
|
@@ -94,8 +94,8 @@ export function summarizeTraces(traces: ExecutionTrace[]): TraceSummary[] {
|
|
|
94
94
|
const summaries: TraceSummary[] = [];
|
|
95
95
|
|
|
96
96
|
for (const [constraintId, group] of grouped) {
|
|
97
|
-
//
|
|
98
|
-
const
|
|
97
|
+
// 提取严重性(从第一条 trace)
|
|
98
|
+
const severity = group[0].severity;
|
|
99
99
|
|
|
100
100
|
// 计算时间范围
|
|
101
101
|
const timeRange = timeRangeOf(group.map(t => t.timestamp));
|
|
@@ -120,7 +120,7 @@ export function summarizeTraces(traces: ExecutionTrace[]): TraceSummary[] {
|
|
|
120
120
|
|
|
121
121
|
summaries.push({
|
|
122
122
|
constraintId,
|
|
123
|
-
|
|
123
|
+
severity,
|
|
124
124
|
timeRange,
|
|
125
125
|
totalChecks,
|
|
126
126
|
passCount,
|
|
@@ -155,7 +155,7 @@ export function detectTraceAnomalies(
|
|
|
155
155
|
anomalies.push({
|
|
156
156
|
type: 'rising_fail_rate',
|
|
157
157
|
constraintId: summary.constraintId,
|
|
158
|
-
|
|
158
|
+
severity: summary.severity,
|
|
159
159
|
message: `约束 ${summary.constraintId} 失败率 ${Math.round(summary.failRate * 100)}% 且趋势上升`,
|
|
160
160
|
data: {
|
|
161
161
|
currentRate: summary.failRate,
|
|
@@ -172,7 +172,7 @@ export function detectTraceAnomalies(
|
|
|
172
172
|
anomalies.push({
|
|
173
173
|
type: 'low_pass_rate',
|
|
174
174
|
constraintId: summary.constraintId,
|
|
175
|
-
|
|
175
|
+
severity: summary.severity,
|
|
176
176
|
message: `约束 ${summary.constraintId} 通过率 ${Math.round(summary.passRate * 100)}%,低于 30%`,
|
|
177
177
|
data: {
|
|
178
178
|
currentRate: summary.passRate,
|
|
@@ -358,14 +358,13 @@ export class TraceAnalyzer {
|
|
|
358
358
|
lines.push('');
|
|
359
359
|
|
|
360
360
|
for (const summary of summaries) {
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
lines.push(`${levelEmoji} **${summary.constraintId}**`);
|
|
361
|
+
const severityEmoji = {
|
|
362
|
+
error: '🔴',
|
|
363
|
+
warning: '🟡',
|
|
364
|
+
info: '🔵',
|
|
365
|
+
}[summary.severity];
|
|
366
|
+
|
|
367
|
+
lines.push(`${severityEmoji} **${summary.constraintId}**`);
|
|
369
368
|
lines.push(` - Checks: ${summary.totalChecks}`);
|
|
370
369
|
lines.push(` - Pass: ${Math.round(summary.passRate * 100)}%`);
|
|
371
370
|
lines.push(` - Fail: ${Math.round(summary.failRate * 100)}%`);
|