@dommaker/harness 1.1.1 → 1.2.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 +10 -0
- package/README.md +2 -4
- package/bin/harness.js +12 -37
- package/dist/cli/commands/check.d.ts +2 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +7 -32
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +2 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +6 -37
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +1 -12
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +0 -2
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/status.js +2 -2
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/context/index.d.ts +9 -6
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +18 -20
- package/dist/context/index.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +3 -2
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +25 -47
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +29 -32
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +34 -126
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +2 -1
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +3 -0
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +25 -0
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +24 -0
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +0 -2
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/prompts.d.ts.map +1 -1
- package/dist/core/constraints/definitions/prompts.js +0 -5
- package/dist/core/constraints/definitions/prompts.js.map +1 -1
- package/dist/core/constraints/index.d.ts +1 -2
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +1 -7
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +1 -2
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +2 -5
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/effective-constraints.d.ts +24 -4
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +28 -7
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/effective-set.d.ts +30 -0
- package/dist/core/effective-set.d.ts.map +1 -0
- package/dist/core/effective-set.js +35 -0
- package/dist/core/effective-set.js.map +1 -0
- package/dist/core/index.d.ts +13 -6
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +44 -21
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +3 -6
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +47 -61
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/failure/constraint-handler.d.ts +1 -1
- package/dist/failure/constraint-handler.js +1 -1
- package/dist/gates/checker-gate.d.ts +1 -4
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +3 -11
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/definitions.d.ts +9 -38
- package/dist/gates/definitions.d.ts.map +1 -1
- package/dist/gates/definitions.js +24 -13
- package/dist/gates/definitions.js.map +1 -1
- package/dist/gates/effective-gates.d.ts.map +1 -1
- package/dist/gates/effective-gates.js +14 -18
- package/dist/gates/effective-gates.js.map +1 -1
- package/dist/gates/index.d.ts +1 -1
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +0 -3
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/index.d.ts +43 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +180 -143
- package/dist/index.js.map +1 -1
- package/dist/knowledge/query.d.ts +15 -0
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js +23 -0
- package/dist/knowledge/query.js.map +1 -1
- package/dist/monitoring/analyzer-base.d.ts +0 -4
- package/dist/monitoring/analyzer-base.d.ts.map +1 -1
- package/dist/monitoring/analyzer-base.js +0 -21
- package/dist/monitoring/analyzer-base.js.map +1 -1
- package/dist/monitoring/index.d.ts +6 -12
- package/dist/monitoring/index.d.ts.map +1 -1
- package/dist/monitoring/index.js +13 -29
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +2 -68
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +0 -4
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +0 -14
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/index.d.ts +2 -2
- package/dist/presets/index.d.ts.map +1 -1
- package/dist/presets/index.js +3 -4
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/standard.d.ts +10 -22
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +15 -75
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +1 -29
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +1 -1
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/index.d.ts +0 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -4
- package/dist/types/index.js.map +1 -1
- package/dist/types/project-config.d.ts +4 -8
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/trace.d.ts +5 -18
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/CONTEXT.md +4 -5
- package/src/__tests__/checker-extra.test.ts +33 -139
- package/src/__tests__/checker.test.ts +0 -376
- package/src/__tests__/project-config-loader-extra.test.ts +42 -29
- package/src/__tests__/public-exports.test.ts +138 -0
- package/src/__tests__/trace-analyzer-extra.test.ts +6 -86
- package/src/__tests__/trace-analyzer.test.ts +4 -5
- package/src/__tests__/trace-collector.test.ts +0 -11
- package/src/__tests__/traces.test.ts +3 -22
- package/src/cli/commands/CONTEXT.md +2 -2
- package/src/cli/commands/__tests__/check-skip-output.test.ts +10 -7
- package/src/cli/commands/__tests__/check.test.ts +29 -69
- package/src/cli/commands/__tests__/constraints-report.test.ts +1 -1
- package/src/cli/commands/__tests__/knowledge.test.ts +90 -1
- package/src/cli/commands/__tests__/registry.test.ts +8 -6
- package/src/cli/commands/__tests__/status-extra.test.ts +6 -6
- package/src/cli/commands/__tests__/status.test.ts +6 -6
- package/src/cli/commands/check.ts +8 -34
- package/src/cli/commands/definitions.ts +2 -0
- package/src/cli/commands/init.ts +6 -37
- package/src/cli/commands/knowledge.ts +1 -16
- package/src/cli/commands/report.ts +0 -2
- package/src/cli/commands/status.ts +2 -2
- package/src/context/index.ts +29 -6
- package/src/core/CONTEXT.md +4 -3
- package/src/core/__tests__/effective-set.test.ts +80 -0
- package/src/core/constraints/capabilities-parser.ts +43 -49
- package/src/core/constraints/checker.ts +51 -145
- package/src/core/constraints/checkers/index.ts +2 -1
- package/src/core/constraints/checkers/types.ts +42 -0
- package/src/core/constraints/definitions/guidelines.ts +0 -2
- package/src/core/constraints/definitions/prompts.ts +0 -5
- package/src/core/constraints/index.ts +2 -17
- package/src/core/constraints/usage-report.ts +5 -8
- package/src/core/effective-constraints.ts +35 -7
- package/src/core/effective-set.ts +56 -0
- package/src/core/index.ts +80 -6
- package/src/core/project-config-loader.ts +53 -68
- package/src/failure/CONTEXT.md +1 -1
- package/src/failure/constraint-handler.ts +1 -1
- package/src/gates/CONTEXT.md +3 -3
- package/src/gates/checker-gate.ts +4 -12
- package/src/gates/definitions.ts +27 -50
- package/src/gates/effective-gates.ts +14 -21
- package/src/gates/index.ts +1 -1
- package/src/hooks/bootstrap.ts +0 -3
- package/src/index.ts +384 -148
- package/src/knowledge/CONTEXT.md +2 -2
- package/src/knowledge/__tests__/query.test.ts +64 -0
- package/src/knowledge/query.ts +28 -0
- package/src/monitoring/CONTEXT.md +7 -13
- package/src/monitoring/__tests__/traces.test.ts +0 -10
- package/src/monitoring/analyzer-base.ts +0 -22
- package/src/monitoring/index.ts +6 -12
- package/src/monitoring/trace-analyzer.ts +2 -73
- package/src/monitoring/traces.ts +0 -20
- package/src/presets/__tests__/standard.test.ts +48 -69
- package/src/presets/index.ts +3 -5
- package/src/presets/standard.ts +14 -79
- package/src/spec/CONTEXT.md +5 -13
- package/src/types/constraint.ts +1 -51
- package/src/types/index.ts +0 -12
- package/src/types/project-config.ts +4 -10
- package/src/types/trace.ts +5 -30
- package/dist/core/constraints/interceptor.d.ts +0 -39
- package/dist/core/constraints/interceptor.d.ts.map +0 -1
- package/dist/core/constraints/interceptor.js +0 -166
- package/dist/core/constraints/interceptor.js.map +0 -1
- package/dist/dashboard/data.d.ts +0 -31
- package/dist/dashboard/data.d.ts.map +0 -1
- package/dist/dashboard/data.js +0 -81
- package/dist/dashboard/data.js.map +0 -1
- package/dist/dashboard/index.d.ts +0 -4
- package/dist/dashboard/index.d.ts.map +0 -1
- package/dist/dashboard/index.js +0 -20
- package/dist/dashboard/index.js.map +0 -1
- package/dist/dashboard/stats.d.ts +0 -20
- package/dist/dashboard/stats.d.ts.map +0 -1
- package/dist/dashboard/stats.js +0 -115
- package/dist/dashboard/stats.js.map +0 -1
- package/dist/dashboard/types.d.ts +0 -110
- package/dist/dashboard/types.d.ts.map +0 -1
- package/dist/dashboard/types.js +0 -8
- package/dist/dashboard/types.js.map +0 -1
- package/dist/monitoring/constraint-doctor.d.ts +0 -76
- package/dist/monitoring/constraint-doctor.d.ts.map +0 -1
- package/dist/monitoring/constraint-doctor.js +0 -189
- package/dist/monitoring/constraint-doctor.js.map +0 -1
- package/dist/monitoring/diagnosis-rules.d.ts +0 -18
- package/dist/monitoring/diagnosis-rules.d.ts.map +0 -1
- package/dist/monitoring/diagnosis-rules.js +0 -119
- package/dist/monitoring/diagnosis-rules.js.map +0 -1
- package/dist/monitoring/knowledge-doctor.d.ts +0 -78
- package/dist/monitoring/knowledge-doctor.d.ts.map +0 -1
- package/dist/monitoring/knowledge-doctor.js +0 -266
- package/dist/monitoring/knowledge-doctor.js.map +0 -1
- package/dist/monitoring/knowledge-evolver.d.ts +0 -72
- package/dist/monitoring/knowledge-evolver.d.ts.map +0 -1
- package/dist/monitoring/knowledge-evolver.js +0 -205
- package/dist/monitoring/knowledge-evolver.js.map +0 -1
- package/dist/monitoring/performance-analyzer.d.ts +0 -92
- package/dist/monitoring/performance-analyzer.d.ts.map +0 -1
- package/dist/monitoring/performance-analyzer.js +0 -343
- package/dist/monitoring/performance-analyzer.js.map +0 -1
- package/dist/monitoring/performance-collector.d.ts +0 -103
- package/dist/monitoring/performance-collector.d.ts.map +0 -1
- package/dist/monitoring/performance-collector.js +0 -298
- package/dist/monitoring/performance-collector.js.map +0 -1
- package/dist/safety/index.d.ts +0 -6
- package/dist/safety/index.d.ts.map +0 -1
- package/dist/safety/index.js +0 -22
- package/dist/safety/index.js.map +0 -1
- package/dist/safety/input-guardrail.d.ts +0 -36
- package/dist/safety/input-guardrail.d.ts.map +0 -1
- package/dist/safety/input-guardrail.js +0 -118
- package/dist/safety/input-guardrail.js.map +0 -1
- package/dist/safety/output-guardrail.d.ts +0 -39
- package/dist/safety/output-guardrail.d.ts.map +0 -1
- package/dist/safety/output-guardrail.js +0 -147
- package/dist/safety/output-guardrail.js.map +0 -1
- package/dist/safety/sandbox.d.ts +0 -45
- package/dist/safety/sandbox.d.ts.map +0 -1
- package/dist/safety/sandbox.js +0 -134
- package/dist/safety/sandbox.js.map +0 -1
- package/dist/safety/tool-guardrail.d.ts +0 -51
- package/dist/safety/tool-guardrail.d.ts.map +0 -1
- package/dist/safety/tool-guardrail.js +0 -147
- package/dist/safety/tool-guardrail.js.map +0 -1
- package/dist/safety/types.d.ts +0 -104
- package/dist/safety/types.d.ts.map +0 -1
- package/dist/safety/types.js +0 -6
- package/dist/safety/types.js.map +0 -1
- package/dist/spec/annotation-checker.d.ts +0 -47
- package/dist/spec/annotation-checker.d.ts.map +0 -1
- package/dist/spec/annotation-checker.js +0 -279
- package/dist/spec/annotation-checker.js.map +0 -1
- package/dist/types/enforcement.d.ts +0 -42
- package/dist/types/enforcement.d.ts.map +0 -1
- package/dist/types/enforcement.js +0 -6
- package/dist/types/enforcement.js.map +0 -1
- package/dist/types/monitoring-types.d.ts +0 -53
- package/dist/types/monitoring-types.d.ts.map +0 -1
- package/dist/types/monitoring-types.js +0 -10
- package/dist/types/monitoring-types.js.map +0 -1
- package/dist/types/performance.d.ts +0 -204
- package/dist/types/performance.d.ts.map +0 -1
- package/dist/types/performance.js +0 -12
- package/dist/types/performance.js.map +0 -1
- package/dist/verification/index.d.ts +0 -4
- package/dist/verification/index.d.ts.map +0 -1
- package/dist/verification/index.js +0 -20
- package/dist/verification/index.js.map +0 -1
- package/dist/verification/loop.d.ts +0 -30
- package/dist/verification/loop.d.ts.map +0 -1
- package/dist/verification/loop.js +0 -114
- package/dist/verification/loop.js.map +0 -1
- package/dist/verification/rules-based.d.ts +0 -40
- package/dist/verification/rules-based.d.ts.map +0 -1
- package/dist/verification/rules-based.js +0 -130
- package/dist/verification/rules-based.js.map +0 -1
- package/dist/verification/types.d.ts +0 -69
- package/dist/verification/types.d.ts.map +0 -1
- package/dist/verification/types.js +0 -6
- package/dist/verification/types.js.map +0 -1
- package/src/__tests__/annotation-checker.test.ts +0 -183
- package/src/__tests__/constraint-doctor.test.ts +0 -271
- package/src/__tests__/doctor-extra.test.ts +0 -155
- package/src/__tests__/extend-exceptions.test.ts +0 -149
- package/src/__tests__/interceptor.test.ts +0 -89
- package/src/__tests__/performance-analyzer.test.ts +0 -499
- package/src/__tests__/performance-collector.test.ts +0 -276
- package/src/__tests__/presets.test.ts +0 -65
- package/src/core/constraints/__tests__/interceptor.test.ts +0 -267
- package/src/core/constraints/interceptor.ts +0 -217
- package/src/dashboard/CONTEXT.md +0 -21
- package/src/dashboard/__tests__/data.test.ts +0 -111
- package/src/dashboard/__tests__/stats.test.ts +0 -159
- package/src/dashboard/data.ts +0 -103
- package/src/dashboard/index.ts +0 -3
- package/src/dashboard/stats.ts +0 -127
- package/src/dashboard/types.ts +0 -130
- package/src/monitoring/__tests__/knowledge-doctor.test.ts +0 -278
- package/src/monitoring/__tests__/knowledge-evolver.test.ts +0 -368
- package/src/monitoring/__tests__/performance-analyzer.test.ts +0 -137
- package/src/monitoring/__tests__/performance-collector.test.ts +0 -331
- package/src/monitoring/constraint-doctor.ts +0 -228
- package/src/monitoring/diagnosis-rules.ts +0 -132
- package/src/monitoring/knowledge-doctor.ts +0 -322
- package/src/monitoring/knowledge-evolver.ts +0 -289
- package/src/monitoring/performance-analyzer.ts +0 -400
- package/src/monitoring/performance-collector.ts +0 -326
- package/src/safety/CONTEXT.md +0 -23
- package/src/safety/__tests__/input-guardrail.test.ts +0 -105
- package/src/safety/__tests__/output-guardrail.test.ts +0 -108
- package/src/safety/__tests__/sandbox.test.ts +0 -158
- package/src/safety/__tests__/tool-guardrail.test.ts +0 -127
- package/src/safety/index.ts +0 -5
- package/src/safety/input-guardrail.ts +0 -133
- package/src/safety/output-guardrail.ts +0 -170
- package/src/safety/sandbox.ts +0 -148
- package/src/safety/tool-guardrail.ts +0 -176
- package/src/safety/types.ts +0 -124
- package/src/spec/__tests__/annotation-checker.test.ts +0 -294
- package/src/spec/annotation-checker.ts +0 -320
- package/src/types/enforcement.ts +0 -48
- package/src/types/monitoring-types.ts +0 -68
- package/src/types/performance.ts +0 -295
- package/src/verification/CONTEXT.md +0 -23
- package/src/verification/__tests__/loop.test.ts +0 -303
- package/src/verification/__tests__/rules-based.test.ts +0 -240
- package/src/verification/index.ts +0 -3
- package/src/verification/loop.ts +0 -137
- package/src/verification/rules-based.ts +0 -152
- package/src/verification/types.ts +0 -81
|
@@ -65,22 +65,21 @@ describe('TraceAnalyzer - 补充覆盖', () => {
|
|
|
65
65
|
describe('compareWithPrevious', () => {
|
|
66
66
|
it('应该计算环比变化', () => {
|
|
67
67
|
const current: any[] = [
|
|
68
|
-
{ constraintId: 'test', passRate: 0.8, failRate: 0.2,
|
|
68
|
+
{ constraintId: 'test', passRate: 0.8, failRate: 0.2, level: 'iron_law' },
|
|
69
69
|
];
|
|
70
70
|
const previous: any[] = [
|
|
71
|
-
{ constraintId: 'test', passRate: 0.6, failRate: 0.4,
|
|
71
|
+
{ constraintId: 'test', passRate: 0.6, failRate: 0.4, level: 'iron_law' },
|
|
72
72
|
];
|
|
73
73
|
|
|
74
74
|
const result = analyzer.compareWithPrevious(current, previous);
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
expect(result[0]?.changeFromLastPeriod?.passRateDelta).toBeCloseTo(0.2);
|
|
77
77
|
expect(result[0]?.changeFromLastPeriod?.failRateDelta).toBeCloseTo(-0.2);
|
|
78
|
-
expect(result[0]?.changeFromLastPeriod?.bypassRateDelta).toBeCloseTo(-0.1);
|
|
79
78
|
});
|
|
80
79
|
|
|
81
80
|
it('无历史数据应该不设置变化', () => {
|
|
82
81
|
const current: any[] = [
|
|
83
|
-
{ constraintId: 'new_test', passRate: 0.8, failRate: 0.2,
|
|
82
|
+
{ constraintId: 'new_test', passRate: 0.8, failRate: 0.2, level: 'iron_law' },
|
|
84
83
|
];
|
|
85
84
|
const previous: any[] = [];
|
|
86
85
|
|
|
@@ -93,7 +92,7 @@ describe('TraceAnalyzer - 补充覆盖', () => {
|
|
|
93
92
|
describe('saveSummary/loadSummary', () => {
|
|
94
93
|
it('应该保存和加载汇总', () => {
|
|
95
94
|
const summaries: any[] = [
|
|
96
|
-
{ constraintId: 'test', passRate: 0.8, failRate: 0.2,
|
|
95
|
+
{ constraintId: 'test', passRate: 0.8, failRate: 0.2, level: 'iron_law', totalChecks: 10 },
|
|
97
96
|
];
|
|
98
97
|
|
|
99
98
|
analyzer.saveSummary(summaries);
|
|
@@ -157,7 +156,6 @@ describe('TraceAnalyzer - 补充覆盖', () => {
|
|
|
157
156
|
level: 'iron_law',
|
|
158
157
|
passRate: 0.8,
|
|
159
158
|
failRate: 0.2,
|
|
160
|
-
bypassRate: 0.1,
|
|
161
159
|
totalChecks: 100,
|
|
162
160
|
recentTrend: 'stable',
|
|
163
161
|
},
|
|
@@ -190,46 +188,6 @@ describe('TraceAnalyzer - 补充覆盖', () => {
|
|
|
190
188
|
});
|
|
191
189
|
});
|
|
192
190
|
|
|
193
|
-
describe('findMostCommon', () => {
|
|
194
|
-
it('应该找出最常见元素', () => {
|
|
195
|
-
// 通过 bypassReasons 统计来触发
|
|
196
|
-
const traces: ExecutionTrace[] = [
|
|
197
|
-
{ constraintId: 'common_test', level: 'iron_law', timestamp: 1000, result: 'pass' },
|
|
198
|
-
{ constraintId: 'common_test', level: 'iron_law', timestamp: 2000, result: 'pass', bypassReason: 'reason_a' },
|
|
199
|
-
{ constraintId: 'common_test', level: 'iron_law', timestamp: 3000, result: 'pass', bypassReason: 'reason_a' },
|
|
200
|
-
{ constraintId: 'common_test', level: 'iron_law', timestamp: 4000, result: 'pass', bypassReason: 'reason_b' },
|
|
201
|
-
];
|
|
202
|
-
|
|
203
|
-
const summaries = analyzer.summarize(traces);
|
|
204
|
-
|
|
205
|
-
// 验证汇总结果存在
|
|
206
|
-
expect(summaries.length).toBeGreaterThan(0);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('应该通过 exceptionApplied 触发 findMostCommon', () => {
|
|
210
|
-
const traces: ExecutionTrace[] = [
|
|
211
|
-
{ constraintId: 'exc_test', level: 'guideline', timestamp: 1000, result: 'pass', exceptionApplied: 'simple_typo' },
|
|
212
|
-
{ constraintId: 'exc_test', level: 'guideline', timestamp: 2000, result: 'pass', exceptionApplied: 'simple_typo' },
|
|
213
|
-
{ constraintId: 'exc_test', level: 'guideline', timestamp: 3000, result: 'pass', exceptionApplied: 'config_error' },
|
|
214
|
-
];
|
|
215
|
-
|
|
216
|
-
const summaries = analyzer.summarize(traces);
|
|
217
|
-
const summary = summaries.find(s => s.constraintId === 'exc_test');
|
|
218
|
-
|
|
219
|
-
expect(summary?.exceptionCount).toBe(3);
|
|
220
|
-
expect(summary?.mostCommonException).toBe('simple_typo');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('无 exception 时 mostCommonException 应该是 undefined', () => {
|
|
224
|
-
const traces: ExecutionTrace[] = [
|
|
225
|
-
{ constraintId: 'no_exc', level: 'iron_law', timestamp: 1000, result: 'pass' },
|
|
226
|
-
];
|
|
227
|
-
|
|
228
|
-
const summaries = analyzer.summarize(traces);
|
|
229
|
-
expect(summaries[0]?.mostCommonException).toBeUndefined();
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
191
|
describe('detectAnomalies - 更多异常类型', () => {
|
|
234
192
|
it('应该检测 rising_fail_rate', () => {
|
|
235
193
|
// 前半段全 fail,后半段全 pass → rising 趋势
|
|
@@ -253,44 +211,6 @@ describe('TraceAnalyzer - 补充覆盖', () => {
|
|
|
253
211
|
const risingFail = anomalies.find(a => a.type === 'rising_fail_rate');
|
|
254
212
|
expect(risingFail).toBeDefined();
|
|
255
213
|
});
|
|
256
|
-
|
|
257
|
-
it('应该检测 rising_bypass_rate', () => {
|
|
258
|
-
// 前半段全 fail,后半段混合 pass+bypass → rising 趋势
|
|
259
|
-
// bypassRate 需要 > 0.1,且 trend === 'rising'
|
|
260
|
-
const traces: ExecutionTrace[] = [];
|
|
261
|
-
for (let i = 0; i < 6; i++) {
|
|
262
|
-
traces.push({ constraintId: 'rising_bypass', level: 'guideline', timestamp: 1000 + i * 100, result: 'fail' });
|
|
263
|
-
}
|
|
264
|
-
for (let i = 6; i < 14; i++) {
|
|
265
|
-
// 后半段有 pass 也有 bypass,使 passRate 上升(rising),同时 bypassRate > 0.1
|
|
266
|
-
traces.push({ constraintId: 'rising_bypass', level: 'guideline', timestamp: 1000 + i * 100, result: i % 2 === 0 ? 'pass' : 'bypassed' });
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const summaries = analyzer.summarize(traces);
|
|
270
|
-
const anomalies = analyzer.detectAnomalies(summaries);
|
|
271
|
-
|
|
272
|
-
const risingBypass = anomalies.find(a => a.type === 'rising_bypass_rate');
|
|
273
|
-
expect(risingBypass).toBeDefined();
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('应该检测 exception_overuse', () => {
|
|
277
|
-
const traces: ExecutionTrace[] = [];
|
|
278
|
-
for (let i = 0; i < 10; i++) {
|
|
279
|
-
traces.push({
|
|
280
|
-
constraintId: 'exc_overuse',
|
|
281
|
-
level: 'guideline',
|
|
282
|
-
timestamp: 1000 + i * 100,
|
|
283
|
-
result: 'pass',
|
|
284
|
-
exceptionApplied: i < 8 ? 'simple_typo' : undefined,
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const summaries = analyzer.summarize(traces);
|
|
289
|
-
const anomalies = analyzer.detectAnomalies(summaries);
|
|
290
|
-
|
|
291
|
-
const excOveruse = anomalies.find(a => a.type === 'exception_overuse');
|
|
292
|
-
expect(excOveruse).toBeDefined();
|
|
293
|
-
});
|
|
294
214
|
});
|
|
295
215
|
|
|
296
216
|
describe('createAnalyzer 工厂函数', () => {
|
|
@@ -300,7 +220,7 @@ describe('TraceAnalyzer - 补充覆盖', () => {
|
|
|
300
220
|
});
|
|
301
221
|
|
|
302
222
|
it('应该支持自定义配置', () => {
|
|
303
|
-
const a = createAnalyzer({ thresholds: {
|
|
223
|
+
const a = createAnalyzer({ thresholds: { failRate: 0.05 } });
|
|
304
224
|
expect(a).toBeDefined();
|
|
305
225
|
});
|
|
306
226
|
});
|
|
@@ -75,11 +75,11 @@ describe('TraceAnalyzer', () => {
|
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
describe('detectAnomalies', () => {
|
|
78
|
-
it('
|
|
78
|
+
it('应该检测失败率异常', () => {
|
|
79
79
|
const traces: ExecutionTrace[] = [
|
|
80
|
-
{ constraintId: 'test', level: 'guideline', timestamp: 1000, result: '
|
|
81
|
-
{ constraintId: 'test', level: 'guideline', timestamp: 2000, result: '
|
|
82
|
-
{ constraintId: 'test', level: 'guideline', timestamp: 3000, result: '
|
|
80
|
+
{ constraintId: 'test', level: 'guideline', timestamp: 1000, result: 'fail' },
|
|
81
|
+
{ constraintId: 'test', level: 'guideline', timestamp: 2000, result: 'fail' },
|
|
82
|
+
{ constraintId: 'test', level: 'guideline', timestamp: 3000, result: 'fail' },
|
|
83
83
|
{ constraintId: 'test', level: 'guideline', timestamp: 4000, result: 'pass' },
|
|
84
84
|
];
|
|
85
85
|
|
|
@@ -120,7 +120,6 @@ describe('TraceAnalyzer', () => {
|
|
|
120
120
|
it('应该支持自定义阈值', () => {
|
|
121
121
|
const customAnalyzer = new TraceAnalyzer(collector, {
|
|
122
122
|
thresholds: {
|
|
123
|
-
bypassRate: 0.1,
|
|
124
123
|
failRate: 0.2,
|
|
125
124
|
},
|
|
126
125
|
});
|
|
@@ -104,17 +104,6 @@ describe('TraceCollector', () => {
|
|
|
104
104
|
});
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
describe('recordBypass', () => {
|
|
108
|
-
it('应该记录绕过 trace', () => {
|
|
109
|
-
collector.recordBypass('no_bypass_checkpoint', 'iron_law', 'Emergency fix');
|
|
110
|
-
|
|
111
|
-
const traces = collector.read();
|
|
112
|
-
expect(traces.length).toBe(1);
|
|
113
|
-
expect(traces[0].result).toBe('bypassed');
|
|
114
|
-
expect(traces[0].bypassReason).toBe('Emergency fix');
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
107
|
describe('read', () => {
|
|
119
108
|
it('应该返回所有 traces', () => {
|
|
120
109
|
collector.record({
|
|
@@ -57,7 +57,7 @@ describe('TraceCollector', () => {
|
|
|
57
57
|
test('should filter traces by constraint ID', () => {
|
|
58
58
|
collector.recordPass('constraint_a', 'iron_law');
|
|
59
59
|
collector.recordFail('constraint_b', 'guideline');
|
|
60
|
-
collector.
|
|
60
|
+
collector.recordFail('constraint_a', 'iron_law');
|
|
61
61
|
|
|
62
62
|
const traces = collector.read({ constraintId: 'constraint_a' });
|
|
63
63
|
expect(traces.length).toBe(2);
|
|
@@ -226,7 +226,7 @@ describe('TraceAnalyzer', () => {
|
|
|
226
226
|
constraintId: 'constraint_b',
|
|
227
227
|
level: 'iron_law',
|
|
228
228
|
timestamp: Date.now() - i * 60 * 1000,
|
|
229
|
-
result: i < 3 ? 'pass' : '
|
|
229
|
+
result: i < 3 ? 'pass' : 'fail',
|
|
230
230
|
});
|
|
231
231
|
}
|
|
232
232
|
|
|
@@ -243,26 +243,7 @@ describe('TraceAnalyzer', () => {
|
|
|
243
243
|
|
|
244
244
|
const summaryB = summaries.find(s => s.constraintId === 'constraint_b');
|
|
245
245
|
expect(summaryB).toBeDefined();
|
|
246
|
-
expect(summaryB!.
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test('should detect high bypass rate anomaly', () => {
|
|
250
|
-
// 记录高绕过率的 traces
|
|
251
|
-
for (let i = 0; i < 10; i++) {
|
|
252
|
-
collector.record({
|
|
253
|
-
constraintId: 'problematic_constraint',
|
|
254
|
-
level: 'guideline',
|
|
255
|
-
timestamp: Date.now(),
|
|
256
|
-
result: i < 3 ? 'pass' : 'bypassed', // 70% 绕过率
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const traces = collector.read();
|
|
261
|
-
const summaries = analyzer.summarize(traces);
|
|
262
|
-
const anomalies = analyzer.detectAnomalies(summaries);
|
|
263
|
-
|
|
264
|
-
expect(anomalies.length).toBeGreaterThan(0);
|
|
265
|
-
expect(anomalies.some(a => a.type === 'high_bypass_rate')).toBe(true);
|
|
246
|
+
expect(summaryB!.failRate).toBeCloseTo(0.4, 1);
|
|
266
247
|
});
|
|
267
248
|
|
|
268
249
|
test('should detect rising fail rate anomaly', () => {
|
|
@@ -10,7 +10,7 @@ H5(#44)起:
|
|
|
10
10
|
## 核心导出
|
|
11
11
|
- `COMMAND_DEFINITIONS`(definitions.ts)— 全部非门禁命令定义(纯数据模块,禁止 import 命令实现)
|
|
12
12
|
- 各命令文件:check / validate / passes-gate / init / report / status / spec / sync-docs / knowledge / sdd / failure / posteval-plan / release / analyze-sessions / update-user-model / constraints / doc-freshness-check / spec-baseline-check
|
|
13
|
-
- 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts
|
|
13
|
+
- 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`,形状同为 `CommandDefinition`,ADR-0007)
|
|
14
14
|
|
|
15
15
|
`constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom 落 custom-constraints.yml 条目 retired 段(#82 D6 一处真相)+ KnowledgeStore 沉淀 + CLAUDE.md 注入段同步)。
|
|
16
16
|
|
|
@@ -31,4 +31,4 @@ H5(#44)起:
|
|
|
31
31
|
## 注意事项
|
|
32
32
|
- 新增命令需同步更新 CLAUDE.md / CAPABILITIES.md / src/CONTEXT.md
|
|
33
33
|
- knowledge 命令包含 13 个子操作(list/search/import/decay/stats/upsert/sync-status/sync-rag/audit/snapshot/migrate/index/health)
|
|
34
|
-
-
|
|
34
|
+
- 特殊路由(选项条件、子命令兜底、退出码处理、裸跑语义)表达在定义表的 optionRoutes / subcommands / subcommandStrict / bareRunsAction / afterRun 字段,bin 是纯通用引擎、不含单命令知识
|
|
@@ -34,13 +34,16 @@ jest.mock('../../../core/constraints/checker', () => ({
|
|
|
34
34
|
},
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
|
-
// Mock
|
|
38
|
-
jest.mock('../../../core/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
// Mock effective-constraints(ADR-0001:check 经 getMergedConstraintsConfig 走生效集链路)
|
|
38
|
+
jest.mock('../../../core/effective-constraints', () => ({
|
|
39
|
+
getMergedConstraintsConfig: jest.fn().mockReturnValue({
|
|
40
|
+
ironLaws: {},
|
|
41
|
+
guidelines: {},
|
|
42
|
+
prompts: {},
|
|
43
|
+
custom: [],
|
|
44
|
+
disabled: [],
|
|
45
|
+
unknownIds: [],
|
|
46
|
+
}),
|
|
44
47
|
}));
|
|
45
48
|
|
|
46
49
|
// Mock chalk
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { check, listLaws } from '../check';
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import { constraintChecker } from '../../../core/constraints/checker';
|
|
8
|
-
import {
|
|
8
|
+
import { getMergedConstraintsConfig } from '../../../core/effective-constraints';
|
|
9
9
|
import { execAsync } from '../../../utils/exec';
|
|
10
10
|
|
|
11
11
|
// Mock fs
|
|
@@ -45,13 +45,16 @@ jest.mock('../../../core/constraints/checker', () => ({
|
|
|
45
45
|
},
|
|
46
46
|
}));
|
|
47
47
|
|
|
48
|
-
// Mock
|
|
49
|
-
jest.mock('../../../core/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
// Mock effective-constraints(ADR-0001:check 经 getMergedConstraintsConfig 走生效集链路)
|
|
49
|
+
jest.mock('../../../core/effective-constraints', () => ({
|
|
50
|
+
getMergedConstraintsConfig: jest.fn().mockReturnValue({
|
|
51
|
+
ironLaws: {},
|
|
52
|
+
guidelines: {},
|
|
53
|
+
prompts: {},
|
|
54
|
+
custom: [],
|
|
55
|
+
disabled: [],
|
|
56
|
+
unknownIds: [],
|
|
57
|
+
}),
|
|
55
58
|
}));
|
|
56
59
|
|
|
57
60
|
// Mock chalk
|
|
@@ -65,7 +68,7 @@ jest.mock('chalk', () => ({
|
|
|
65
68
|
|
|
66
69
|
const mockFs = fs as jest.Mocked<typeof fs> & { readdirSync: jest.Mock };
|
|
67
70
|
const mockChecker = constraintChecker as jest.Mocked<typeof constraintChecker>;
|
|
68
|
-
const
|
|
71
|
+
const mockGetMergedConfig = getMergedConstraintsConfig as jest.MockedFunction<typeof getMergedConstraintsConfig>;
|
|
69
72
|
const mockExecAsync = execAsync as jest.MockedFunction<typeof execAsync>;
|
|
70
73
|
|
|
71
74
|
describe('check command', () => {
|
|
@@ -73,6 +76,15 @@ describe('check command', () => {
|
|
|
73
76
|
|
|
74
77
|
beforeEach(() => {
|
|
75
78
|
jest.clearAllMocks();
|
|
79
|
+
// clearAllMocks 不清实现,显式恢复默认 merged config,避免用例间泄漏
|
|
80
|
+
mockGetMergedConfig.mockReturnValue({
|
|
81
|
+
ironLaws: {},
|
|
82
|
+
guidelines: {},
|
|
83
|
+
prompts: {},
|
|
84
|
+
custom: [],
|
|
85
|
+
disabled: [],
|
|
86
|
+
unknownIds: [],
|
|
87
|
+
});
|
|
76
88
|
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
77
89
|
process.exitCode = 0;
|
|
78
90
|
});
|
|
@@ -123,12 +135,14 @@ describe('check command', () => {
|
|
|
123
135
|
});
|
|
124
136
|
|
|
125
137
|
it('应该加载自定义约束', async () => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
mockGetMergedConfig.mockReturnValue({
|
|
139
|
+
ironLaws: {},
|
|
140
|
+
guidelines: {},
|
|
141
|
+
prompts: {},
|
|
142
|
+
custom: ['custom'],
|
|
143
|
+
disabled: ['disabled_constraint'],
|
|
144
|
+
unknownIds: [],
|
|
145
|
+
});
|
|
132
146
|
|
|
133
147
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
134
148
|
passed: true,
|
|
@@ -399,23 +413,6 @@ describe('check command', () => {
|
|
|
399
413
|
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('记录已足够'));
|
|
400
414
|
});
|
|
401
415
|
|
|
402
|
-
it('应该在绕过率高时提示查看详情', async () => {
|
|
403
|
-
// 20 条记录,15 条 bypassed (75% > 30%)
|
|
404
|
-
const traces = [
|
|
405
|
-
...Array(5).fill('{"result":"pass"}'),
|
|
406
|
-
...Array(15).fill('{"result":"bypassed"}'),
|
|
407
|
-
].join('\n');
|
|
408
|
-
mockFs.existsSync.mockImplementation((p: any) => {
|
|
409
|
-
if (p.includes('traces.log')) return true;
|
|
410
|
-
if (p.includes('.state.json')) return false;
|
|
411
|
-
return false;
|
|
412
|
-
});
|
|
413
|
-
mockFs.readFileSync.mockReturnValue(traces);
|
|
414
|
-
|
|
415
|
-
await check({ preset: 'default', staged: false });
|
|
416
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('异常趋势'));
|
|
417
|
-
});
|
|
418
|
-
|
|
419
416
|
it('应该不重复显示已显示的提示', async () => {
|
|
420
417
|
const traces = Array(50).fill('{"result":"pass"}').join('\n');
|
|
421
418
|
mockFs.existsSync.mockReturnValue(true);
|
|
@@ -445,43 +442,6 @@ describe('check command', () => {
|
|
|
445
442
|
expect(output).not.toContain('────────────────');
|
|
446
443
|
});
|
|
447
444
|
|
|
448
|
-
it('应该在绕过率低于 30% 时不提示', async () => {
|
|
449
|
-
// 20 条记录,2 条 bypassed (10% < 30%)
|
|
450
|
-
const traces = [
|
|
451
|
-
...Array(18).fill('{"result":"pass"}'),
|
|
452
|
-
...Array(2).fill('{"result":"bypassed"}'),
|
|
453
|
-
].join('\n');
|
|
454
|
-
mockFs.existsSync.mockImplementation((p: any) => {
|
|
455
|
-
if (p.includes('traces.log')) return true;
|
|
456
|
-
if (p.includes('.state.json')) return false;
|
|
457
|
-
return false;
|
|
458
|
-
});
|
|
459
|
-
mockFs.readFileSync.mockReturnValue(traces);
|
|
460
|
-
|
|
461
|
-
await check({ preset: 'default', staged: false });
|
|
462
|
-
const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
|
|
463
|
-
expect(output).not.toContain('异常趋势');
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
it('应该处理无效 JSON trace 行', async () => {
|
|
467
|
-
// 20 条记录,部分无效 JSON,部分 bypassed
|
|
468
|
-
const traces = [
|
|
469
|
-
...Array(5).fill('invalid json'),
|
|
470
|
-
...Array(5).fill('{"result":"pass"}'),
|
|
471
|
-
...Array(10).fill('{"result":"bypassed"}'),
|
|
472
|
-
].join('\n');
|
|
473
|
-
mockFs.existsSync.mockImplementation((p: any) => {
|
|
474
|
-
if (p.includes('traces.log')) return true;
|
|
475
|
-
if (p.includes('.state.json')) return false;
|
|
476
|
-
return false;
|
|
477
|
-
});
|
|
478
|
-
mockFs.readFileSync.mockReturnValue(traces);
|
|
479
|
-
|
|
480
|
-
await check({ preset: 'default', staged: false });
|
|
481
|
-
// 15 条有效,10 条 bypassed,bypassRate = 10/20 = 0.5 > 0.3
|
|
482
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('异常趋势'));
|
|
483
|
-
});
|
|
484
|
-
|
|
485
445
|
it('应该保存状态文件当有提示时', async () => {
|
|
486
446
|
const traces = Array(50).fill('{"result":"pass"}').join('\n');
|
|
487
447
|
mockFs.existsSync.mockImplementation((p: any) => {
|
|
@@ -170,7 +170,7 @@ describe('buildConstraintsUsageReport', () => {
|
|
|
170
170
|
const candidates = diagnoseRetireCandidates([
|
|
171
171
|
{
|
|
172
172
|
id: 'x', level: 'guideline',
|
|
173
|
-
total: 60, pass: 0, fail: 0, skip: 60,
|
|
173
|
+
total: 60, pass: 0, fail: 0, skip: 60,
|
|
174
174
|
evaluated: 0, failRate: 0,
|
|
175
175
|
},
|
|
176
176
|
]);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* knowledge 命令测试 — knowledgeAudit
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { knowledgeAudit, knowledgeStats, knowledgeHealth } from '../knowledge';
|
|
5
|
+
import { knowledgeAudit, knowledgeStats, knowledgeHealth, knowledgeSearch } from '../knowledge';
|
|
6
6
|
|
|
7
7
|
// Mock chalk
|
|
8
8
|
jest.mock('chalk', () => ({
|
|
@@ -233,3 +233,92 @@ describe('knowledgeHealth CLI', () => {
|
|
|
233
233
|
expect(parsed.summary.staleEntries).toBe(1);
|
|
234
234
|
});
|
|
235
235
|
});
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* knowledgeSearch 回归测试 — harness#63
|
|
239
|
+
* bug:先按 token budget 截断再文本匹配,新条目(lastReferenced 为空、排序队尾)
|
|
240
|
+
* 在语料超过 budget 时永远进不了匹配阶段。
|
|
241
|
+
*/
|
|
242
|
+
describe('knowledgeSearch CLI (harness#63)', () => {
|
|
243
|
+
const fs = require('fs');
|
|
244
|
+
const os = require('os');
|
|
245
|
+
const path = require('path');
|
|
246
|
+
const { FileKnowledgeStore } = require('../../../knowledge/store');
|
|
247
|
+
|
|
248
|
+
let tmpProject: string;
|
|
249
|
+
let knowledgeDir: string;
|
|
250
|
+
let consoleSpy: jest.SpyInstance;
|
|
251
|
+
|
|
252
|
+
const FILLER_COUNT = 25; // 25 × ~800 tokens ≈ 20000 > maxTokens 10000
|
|
253
|
+
const TARGET_ID = 'PIT-020';
|
|
254
|
+
|
|
255
|
+
function makeEntry(overrides: Record<string, any>): any {
|
|
256
|
+
return {
|
|
257
|
+
id: 'X-000',
|
|
258
|
+
type: 'pitfall',
|
|
259
|
+
title: '条目',
|
|
260
|
+
content: '内容',
|
|
261
|
+
maturity: 'proven',
|
|
262
|
+
layer: 'project',
|
|
263
|
+
created: '2026-08-01T00:00:00.000Z',
|
|
264
|
+
lastReferenced: '2026-08-01T00:00:00.000Z',
|
|
265
|
+
contributors: [],
|
|
266
|
+
projects: [],
|
|
267
|
+
tags: [],
|
|
268
|
+
applicablePhases: [],
|
|
269
|
+
sourceReferences: [],
|
|
270
|
+
referencedBy: [],
|
|
271
|
+
executionResults: [],
|
|
272
|
+
consumptionMode: 'reference',
|
|
273
|
+
origin: 'agent',
|
|
274
|
+
...overrides,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
beforeEach(() => {
|
|
279
|
+
tmpProject = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-k63-'));
|
|
280
|
+
knowledgeDir = path.join(tmpProject, '.harness', 'knowledge');
|
|
281
|
+
fs.mkdirSync(knowledgeDir, { recursive: true });
|
|
282
|
+
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
283
|
+
|
|
284
|
+
const store = new FileKnowledgeStore({ baseDir: knowledgeDir });
|
|
285
|
+
// 高成熟度 + 有 lastReferenced 的填充条目排在前面,吃满 token budget
|
|
286
|
+
for (let i = 0; i < FILLER_COUNT; i++) {
|
|
287
|
+
store.save(makeEntry({
|
|
288
|
+
id: `FILL-${String(i).padStart(3, '0')}`,
|
|
289
|
+
title: `填充条目 ${i}`,
|
|
290
|
+
content: '填'.repeat(399) + '共同词',
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
// 目标条目:新条目,draft + lastReferenced 为空,排在队尾
|
|
294
|
+
store.save(makeEntry({
|
|
295
|
+
id: TARGET_ID,
|
|
296
|
+
title: '新条目',
|
|
297
|
+
maturity: 'draft',
|
|
298
|
+
lastReferenced: '',
|
|
299
|
+
content: 'autolink 相关条目 共同词',
|
|
300
|
+
}));
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
afterEach(() => {
|
|
304
|
+
consoleSpy.mockRestore();
|
|
305
|
+
fs.rmSync(tmpProject, { recursive: true, force: true });
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
async function searchJson(query: string, limit: number): Promise<any> {
|
|
309
|
+
await knowledgeSearch(query, { projectPath: tmpProject, json: true, limit });
|
|
310
|
+
const output = consoleSpy.mock.calls.map((c: any[]) => c[0]).join('\n');
|
|
311
|
+
return JSON.parse(output);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
it('语料超过 token budget 时,排在队尾的新条目仍可被搜到', async () => {
|
|
315
|
+
const result = await searchJson('autolink', 200);
|
|
316
|
+
expect(result.total).toBe(1);
|
|
317
|
+
expect(result.entries[0].id).toBe(TARGET_ID);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('超高频关键词的返回数 = 实际匹配数(截断只发生在匹配之后)', async () => {
|
|
321
|
+
const result = await searchJson('共同词', 200);
|
|
322
|
+
expect(result.total).toBe(FILLER_COUNT + 1);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
@@ -51,11 +51,10 @@ describe('命令注册表闭环', () => {
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
it('全部实现引用可解析(module 存在且 export 是函数)', () => {
|
|
54
|
-
const refs = collectRefs(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
54
|
+
const refs = collectRefs([
|
|
55
|
+
...COMMAND_DEFINITIONS,
|
|
56
|
+
...GATE_DEFINITIONS.map(d => d.cli),
|
|
57
|
+
]);
|
|
59
58
|
expect(refs.length).toBeGreaterThan(0);
|
|
60
59
|
|
|
61
60
|
for (const ref of refs) {
|
|
@@ -107,7 +106,10 @@ describe('命令注册表闭环', () => {
|
|
|
107
106
|
}
|
|
108
107
|
for (const child of def.children || []) walk(child);
|
|
109
108
|
};
|
|
110
|
-
for (const def of
|
|
109
|
+
for (const def of [
|
|
110
|
+
...COMMAND_DEFINITIONS,
|
|
111
|
+
...GATE_DEFINITIONS.map(d => d.cli),
|
|
112
|
+
]) walk(def);
|
|
111
113
|
// sync-docs --check 失败路径确实会请求退出
|
|
112
114
|
expect(exitSpy).toHaveBeenCalled();
|
|
113
115
|
} finally {
|
|
@@ -81,7 +81,7 @@ describe('status command - 补充覆盖', () => {
|
|
|
81
81
|
];
|
|
82
82
|
const mockAnalyze = {
|
|
83
83
|
summarize: jest.fn().mockReturnValue([
|
|
84
|
-
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 0.2,
|
|
84
|
+
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 0.2, failRate: 0.8, totalChecks: 10 },
|
|
85
85
|
]),
|
|
86
86
|
detectAnomalies: jest.fn().mockReturnValue(mockAnomalies),
|
|
87
87
|
};
|
|
@@ -119,8 +119,8 @@ describe('status command - 补充覆盖', () => {
|
|
|
119
119
|
mockFs.readFileSync.mockReturnValue(mockTraces.map(t => JSON.stringify(t)).join('\n'));
|
|
120
120
|
|
|
121
121
|
const mockSummaries = [
|
|
122
|
-
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 1,
|
|
123
|
-
{ constraintId: 'no_completion_without_verification', level: 'iron_law', passRate: 0.8,
|
|
122
|
+
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 1, failRate: 0, totalChecks: 10 },
|
|
123
|
+
{ constraintId: 'no_completion_without_verification', level: 'iron_law', passRate: 0.8, failRate: 0.2, totalChecks: 5 },
|
|
124
124
|
];
|
|
125
125
|
const mockAnalyze = {
|
|
126
126
|
summarize: jest.fn().mockReturnValue(mockSummaries),
|
|
@@ -141,7 +141,7 @@ describe('status command - 补充覆盖', () => {
|
|
|
141
141
|
mockFs.readFileSync.mockReturnValue(mockTraces.map(t => JSON.stringify(t)).join('\n'));
|
|
142
142
|
|
|
143
143
|
const mockSummaries = [
|
|
144
|
-
{ constraintId: 'capability_sync', level: 'guideline', passRate: 0.9,
|
|
144
|
+
{ constraintId: 'capability_sync', level: 'guideline', passRate: 0.9, failRate: 0.1, totalChecks: 20 },
|
|
145
145
|
];
|
|
146
146
|
const mockAnalyze = {
|
|
147
147
|
summarize: jest.fn().mockReturnValue(mockSummaries),
|
|
@@ -164,8 +164,8 @@ describe('status command - 补充覆盖', () => {
|
|
|
164
164
|
mockFs.readFileSync.mockReturnValue(mockTraces.map(t => JSON.stringify(t)).join('\n'));
|
|
165
165
|
|
|
166
166
|
const mockSummaries = [
|
|
167
|
-
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 1,
|
|
168
|
-
{ constraintId: 'capability_sync', level: 'guideline', passRate: 0.9,
|
|
167
|
+
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 1, failRate: 0, totalChecks: 10 },
|
|
168
|
+
{ constraintId: 'capability_sync', level: 'guideline', passRate: 0.9, failRate: 0.1, totalChecks: 20 },
|
|
169
169
|
];
|
|
170
170
|
const mockAnalyze = {
|
|
171
171
|
summarize: jest.fn().mockReturnValue(mockSummaries),
|
|
@@ -87,7 +87,7 @@ describe('status command', () => {
|
|
|
87
87
|
mockFs.readFileSync.mockReturnValue(mockTraces.map(t => JSON.stringify(t)).join('\n'));
|
|
88
88
|
|
|
89
89
|
const mockSummarize = jest.fn().mockReturnValue([
|
|
90
|
-
{ constraintId: 'test1', level: 'iron_law', passRate: 1,
|
|
90
|
+
{ constraintId: 'test1', level: 'iron_law', passRate: 1, failRate: 0, totalChecks: 1 },
|
|
91
91
|
]);
|
|
92
92
|
const mockAnalyze = {
|
|
93
93
|
summarize: mockSummarize,
|
|
@@ -106,7 +106,7 @@ describe('status command', () => {
|
|
|
106
106
|
mockFs.readFileSync.mockReturnValue(mockTraces.map(t => JSON.stringify(t)).join('\n'));
|
|
107
107
|
|
|
108
108
|
const mockSummarize = jest.fn().mockReturnValue([
|
|
109
|
-
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 1,
|
|
109
|
+
{ constraintId: 'no_bypass_checkpoint', level: 'iron_law', passRate: 1, failRate: 0, totalChecks: 5 },
|
|
110
110
|
]);
|
|
111
111
|
const mockAnalyze = {
|
|
112
112
|
summarize: mockSummarize,
|
|
@@ -131,7 +131,7 @@ describe('status command', () => {
|
|
|
131
131
|
];
|
|
132
132
|
const mockAnalyze = {
|
|
133
133
|
summarize: jest.fn().mockReturnValue([
|
|
134
|
-
{ constraintId: 'test1', level: 'iron_law', passRate: 0.2,
|
|
134
|
+
{ constraintId: 'test1', level: 'iron_law', passRate: 0.2, failRate: 0.8, totalChecks: 5 },
|
|
135
135
|
]),
|
|
136
136
|
detectAnomalies: jest.fn().mockReturnValue(mockAnomalies),
|
|
137
137
|
};
|
|
@@ -150,7 +150,7 @@ describe('status command', () => {
|
|
|
150
150
|
mockFs.readFileSync.mockReturnValue(mockTraces.map(t => JSON.stringify(t)).join('\n'));
|
|
151
151
|
|
|
152
152
|
const mockSummarize = jest.fn().mockReturnValue([
|
|
153
|
-
{ constraintId: 'test1', level: 'iron_law', passRate: 0.8,
|
|
153
|
+
{ constraintId: 'test1', level: 'iron_law', passRate: 0.8, failRate: 0.2, totalChecks: 10 },
|
|
154
154
|
]);
|
|
155
155
|
const mockAnalyze = {
|
|
156
156
|
summarize: mockSummarize,
|
|
@@ -184,7 +184,7 @@ describe('status command', () => {
|
|
|
184
184
|
|
|
185
185
|
const mockAnalyze = {
|
|
186
186
|
summarize: jest.fn().mockReturnValue([
|
|
187
|
-
{ constraintId: 'test_guide', level: 'guideline', passRate: 0.9,
|
|
187
|
+
{ constraintId: 'test_guide', level: 'guideline', passRate: 0.9, failRate: 0.1, totalChecks: 10 },
|
|
188
188
|
]),
|
|
189
189
|
detectAnomalies: jest.fn().mockReturnValue([]),
|
|
190
190
|
};
|
|
@@ -276,7 +276,7 @@ describe('status command', () => {
|
|
|
276
276
|
|
|
277
277
|
const mockAnalyze = {
|
|
278
278
|
summarize: jest.fn().mockReturnValue([
|
|
279
|
-
{ constraintId: 'test_guide', level: 'guideline', passRate: 0.6,
|
|
279
|
+
{ constraintId: 'test_guide', level: 'guideline', passRate: 0.6, failRate: 0.4, totalChecks: 10 },
|
|
280
280
|
]),
|
|
281
281
|
detectAnomalies: jest.fn().mockReturnValue([]),
|
|
282
282
|
};
|