@dommaker/harness 1.9.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +9 -12
- package/dist/cli/commands/check.d.ts +3 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +41 -78
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -10
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +4 -57
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +9 -23
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +39 -121
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +3 -8
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +8 -23
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +7 -12
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/init.d.ts +0 -32
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -149
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +14 -17
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +1 -3
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
- package/dist/cli/commands/scaffold-templates.js +2 -53
- package/dist/cli/commands/scaffold-templates.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +0 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +0 -10
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/commands/status.js +10 -10
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts +0 -2
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +4 -23
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +16 -4
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +15 -24
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +33 -72
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +4 -3
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +1 -1
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +4 -8
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.js +4 -5
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +1 -1
- package/dist/core/constraints/checkers/types.js +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +9 -25
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.js +10 -81
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +7 -9
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +13 -16
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/index.d.ts +2 -5
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +3 -13
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +10 -38
- package/dist/core/constraints/injection-writer.d.ts.map +1 -1
- package/dist/core/constraints/injection-writer.js +17 -54
- package/dist/core/constraints/injection-writer.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +1 -10
- package/dist/core/constraints/run-env.d.ts.map +1 -1
- package/dist/core/constraints/run-env.js +1 -22
- package/dist/core/constraints/run-env.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +6 -8
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +6 -11
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/effective-constraints.d.ts +12 -22
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +13 -30
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -9
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +4 -42
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +16 -125
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +2 -2
- package/dist/core/validators/passes-gate.js +6 -6
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/index.d.ts +9 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -18
- package/dist/index.js.map +1 -1
- package/dist/knowledge/lint.d.ts +17 -0
- package/dist/knowledge/lint.d.ts.map +1 -1
- package/dist/knowledge/lint.js +52 -0
- package/dist/knowledge/lint.js.map +1 -1
- package/dist/knowledge/store.d.ts +7 -0
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +48 -13
- package/dist/knowledge/store.js.map +1 -1
- package/dist/knowledge/types.d.ts +5 -1
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js +5 -1
- package/dist/knowledge/types.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +11 -12
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +4 -4
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +8 -8
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/standard.d.ts +8 -10
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +10 -16
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +24 -35
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +8 -7
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +5 -5
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +8 -66
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/project-config.js +3 -2
- package/dist/types/project-config.js.map +1 -1
- package/dist/types/trace.d.ts +8 -10
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/CONTEXT.md +2 -2
- package/src/__tests__/checker-extra.test.ts +37 -61
- package/src/__tests__/checker.test.ts +10 -115
- package/src/__tests__/effective-constraints.test.ts +18 -83
- package/src/__tests__/iron-laws.test.ts +71 -90
- package/src/__tests__/passes-gate-check.test.ts +2 -2
- package/src/__tests__/project-config-loader-extra.test.ts +22 -123
- package/src/__tests__/project-config-loader.test.ts +5 -33
- package/src/__tests__/public-exports.test.ts +8 -7
- package/src/__tests__/public-type-surface.test.ts +7 -5
- package/src/__tests__/public-value-surface.test.ts +1 -7
- package/src/__tests__/skip-semantics.test.ts +21 -37
- package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
- package/src/__tests__/trace-analyzer.test.ts +21 -21
- package/src/__tests__/trace-collector.test.ts +22 -22
- package/src/__tests__/traces.test.ts +27 -28
- package/src/__tests__/usage-report-public-export.test.ts +21 -0
- package/src/cli/commands/CONTEXT.md +12 -12
- package/src/cli/commands/__tests__/check-read-count.test.ts +4 -15
- package/src/cli/commands/__tests__/check-skip-output.test.ts +14 -14
- package/src/cli/commands/__tests__/check.test.ts +14 -31
- package/src/cli/commands/__tests__/constraints-report-json-flag.test.ts +101 -0
- package/src/cli/commands/__tests__/constraints-report.test.ts +12 -80
- package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
- package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
- package/src/cli/commands/__tests__/init-ondisk.test.ts +13 -21
- package/src/cli/commands/__tests__/init.test.ts +1 -16
- package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +1 -1
- package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +1 -1
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
- package/src/cli/commands/__tests__/registry.test.ts +1 -1
- package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
- package/src/cli/commands/__tests__/report.test.ts +1 -1
- package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
- package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
- package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
- package/src/cli/commands/__tests__/status.test.ts +12 -12
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
- package/src/cli/commands/check.ts +43 -82
- package/src/cli/commands/constraints-report.ts +4 -59
- package/src/cli/commands/constraints-retire.ts +49 -154
- package/src/cli/commands/constraints.ts +11 -26
- package/src/cli/commands/definitions.ts +7 -12
- package/src/cli/commands/init.ts +8 -179
- package/src/cli/commands/report.ts +19 -23
- package/src/cli/commands/scaffold-templates.ts +1 -53
- package/src/cli/commands/scaffold.ts +0 -11
- package/src/cli/commands/status.ts +10 -10
- package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
- package/src/core/CONTEXT.md +9 -9
- package/src/core/constraints/__tests__/capability-counts.test.ts +5 -5
- package/src/core/constraints/__tests__/collect-constraints.test.ts +13 -8
- package/src/core/constraints/__tests__/git-evidence.test.ts +4 -7
- package/src/core/constraints/__tests__/injection-writer.test.ts +8 -61
- package/src/core/constraints/__tests__/run-env.test.ts +5 -34
- package/src/core/constraints/__tests__/trace-injection.test.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +17 -5
- package/src/core/constraints/checker.ts +39 -88
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +5 -8
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +1 -53
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +5 -25
- package/src/core/constraints/checkers/docs-freshness.ts +1 -1
- package/src/core/constraints/checkers/governance-presence.ts +4 -3
- package/src/core/constraints/checkers/index.ts +4 -10
- package/src/core/constraints/checkers/iron-flags.ts +3 -12
- package/src/core/constraints/checkers/no-test-simplification.ts +1 -1
- package/src/core/constraints/checkers/types.ts +1 -1
- package/src/core/constraints/definitions/guidelines.ts +8 -25
- package/src/core/constraints/definitions/iron-laws.ts +9 -82
- package/src/core/constraints/definitions.ts +14 -15
- package/src/core/constraints/index.ts +4 -19
- package/src/core/constraints/injection-writer.ts +16 -61
- package/src/core/constraints/run-env.ts +1 -33
- package/src/core/constraints/usage-report.ts +9 -16
- package/src/core/effective-constraints.ts +14 -45
- package/src/core/index.ts +3 -10
- package/src/core/project-config-loader.ts +20 -145
- package/src/core/validators/passes-gate.ts +6 -6
- package/src/failure/CONTEXT.md +1 -1
- package/src/hooks/__tests__/bootstrap.test.ts +3 -5
- package/src/index.ts +28 -18
- package/src/knowledge/CONTEXT.md +4 -3
- package/src/knowledge/__tests__/lint.test.ts +84 -0
- package/src/knowledge/__tests__/store.test.ts +70 -0
- package/src/knowledge/lint.ts +60 -1
- package/src/knowledge/store.ts +48 -11
- package/src/knowledge/types.ts +7 -1
- package/src/monitoring/CONTEXT.md +1 -1
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
- package/src/monitoring/__tests__/traces.test.ts +22 -22
- package/src/monitoring/trace-analyzer.ts +12 -13
- package/src/monitoring/traces.ts +8 -8
- package/src/presets/__tests__/standard.test.ts +12 -16
- package/src/presets/standard.ts +14 -23
- package/src/types/__tests__/types.test.ts +3 -3
- package/src/types/constraint.ts +26 -40
- package/src/types/passes-gate.ts +5 -5
- package/src/types/project-config.ts +8 -77
- package/src/types/trace.ts +8 -11
- package/dist/cli/commands/posteval-plan.d.ts +0 -15
- package/dist/cli/commands/posteval-plan.d.ts.map +0 -1
- package/dist/cli/commands/posteval-plan.js +0 -86
- package/dist/cli/commands/posteval-plan.js.map +0 -1
- package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
- package/dist/core/constraints/agent-prompt-renderer.js +0 -55
- package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
- package/dist/core/constraints/definitions/prompts.d.ts +0 -12
- package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
- package/dist/core/constraints/definitions/prompts.js +0 -400
- package/dist/core/constraints/definitions/prompts.js.map +0 -1
- package/dist/core/constraints/injection-drift.d.ts +0 -61
- package/dist/core/constraints/injection-drift.d.ts.map +0 -1
- package/dist/core/constraints/injection-drift.js +0 -131
- package/dist/core/constraints/injection-drift.js.map +0 -1
- package/dist/core/constraints/injection-renderer.d.ts +0 -23
- package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
- package/dist/core/constraints/injection-renderer.js +0 -44
- package/dist/core/constraints/injection-renderer.js.map +0 -1
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
- package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
- package/src/cli/commands/__tests__/posteval-plan.test.ts +0 -204
- package/src/cli/commands/posteval-plan.ts +0 -98
- package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
- package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
- package/src/core/constraints/agent-prompt-renderer.ts +0 -67
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
- package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
- package/src/core/constraints/definitions/prompts.ts +0 -414
- package/src/core/constraints/injection-drift.ts +0 -142
- package/src/core/constraints/injection-renderer.ts +0 -44
package/src/knowledge/lint.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import type { KnowledgeStore } from './store';
|
|
14
14
|
import { ReferenceTracker } from './reference-tracker';
|
|
15
15
|
import type { KnowledgeEntry, LintIssue, LintIssueType, MaturityLevel } from './types';
|
|
16
|
+
import { MATURITY_LEVELS, STORAGE_LAYERS } from './types';
|
|
16
17
|
|
|
17
18
|
const MAX_SOURCE_REFS = 20;
|
|
18
19
|
|
|
@@ -46,6 +47,7 @@ export class KnowledgeLinter {
|
|
|
46
47
|
issues.push(...this.checkOutdated(entries));
|
|
47
48
|
issues.push(...this.checkDuplicates(entries));
|
|
48
49
|
issues.push(...this.checkContradictions(entries));
|
|
50
|
+
issues.push(...this.checkInvalidEnums(entries));
|
|
49
51
|
|
|
50
52
|
// 自动修复
|
|
51
53
|
let fixed = 0;
|
|
@@ -60,6 +62,7 @@ export class KnowledgeLinter {
|
|
|
60
62
|
outdated: 0,
|
|
61
63
|
duplicate: 0,
|
|
62
64
|
index_inconsistent: 0,
|
|
65
|
+
invalid_enum: 0,
|
|
63
66
|
};
|
|
64
67
|
for (const issue of issues) {
|
|
65
68
|
summary[issue.type]++;
|
|
@@ -204,6 +207,55 @@ export class KnowledgeLinter {
|
|
|
204
207
|
return issues;
|
|
205
208
|
}
|
|
206
209
|
|
|
210
|
+
/**
|
|
211
|
+
* 检查存量条目的 maturity/layer 枚举值域(E1 复盘修正 M1 收口:
|
|
212
|
+
* 写入闸只挡新写,存量脏值由本检查进 lint 报告;缺字段在存量侧算脏值)。
|
|
213
|
+
* 与 validateEntry 共用同一判定正本(invalidEnumIssues),autoFix 不动此类
|
|
214
|
+
* issue——枚举映射需人工裁定,不自动改。
|
|
215
|
+
*/
|
|
216
|
+
checkInvalidEnums(entries: KnowledgeEntry[]): LintIssue[] {
|
|
217
|
+
const issues: LintIssue[] = [];
|
|
218
|
+
for (const entry of entries) {
|
|
219
|
+
issues.push(...this.invalidEnumIssues(entry, true, entry.id));
|
|
220
|
+
}
|
|
221
|
+
return issues;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* maturity/layer 枚举判定正本。missingIsDirty=false 时字段缺省跳过
|
|
226
|
+
* (pre-ingest 调用方只传四字段的兼容面);存量扫描传 true(缺字段即脏)。
|
|
227
|
+
*/
|
|
228
|
+
private invalidEnumIssues(
|
|
229
|
+
fields: { maturity?: string; layer?: string },
|
|
230
|
+
missingIsDirty: boolean,
|
|
231
|
+
entryId?: string
|
|
232
|
+
): LintIssue[] {
|
|
233
|
+
const issues: LintIssue[] = [];
|
|
234
|
+
if ((missingIsDirty || fields.maturity !== undefined) && !(MATURITY_LEVELS as readonly string[]).includes(fields.maturity as string)) {
|
|
235
|
+
issues.push({
|
|
236
|
+
type: 'invalid_enum',
|
|
237
|
+
entryId,
|
|
238
|
+
severity: 'high',
|
|
239
|
+
description: fields.maturity === undefined
|
|
240
|
+
? `maturity is missing (declared values: ${MATURITY_LEVELS.join(' | ')}).`
|
|
241
|
+
: `maturity "${fields.maturity}" is not a declared value (${MATURITY_LEVELS.join(' | ')}).`,
|
|
242
|
+
suggestion: 'Map to a declared maturity (e.g. pending → draft) before writing; the store write gate rejects undeclared values.',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if ((missingIsDirty || fields.layer !== undefined) && !(STORAGE_LAYERS as readonly string[]).includes(fields.layer as string)) {
|
|
246
|
+
issues.push({
|
|
247
|
+
type: 'invalid_enum',
|
|
248
|
+
entryId,
|
|
249
|
+
severity: 'high',
|
|
250
|
+
description: fields.layer === undefined
|
|
251
|
+
? `layer is missing (declared values: ${STORAGE_LAYERS.join(' | ')}).`
|
|
252
|
+
: `layer "${fields.layer}" is not a declared value (${STORAGE_LAYERS.join(' | ')}).`,
|
|
253
|
+
suggestion: 'Map to a declared layer or drop the field; the store write gate rejects undeclared values.',
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return issues;
|
|
257
|
+
}
|
|
258
|
+
|
|
207
259
|
/**
|
|
208
260
|
* 检查矛盾(同主题相反结论)
|
|
209
261
|
*
|
|
@@ -260,10 +312,17 @@ export class KnowledgeLinter {
|
|
|
260
312
|
* - Title too vague (common stop words only) → flag
|
|
261
313
|
* - Contradicts a proven entry with same tags → reject
|
|
262
314
|
* - Near-duplicate of existing entry (title similarity > 80%) → flag merge
|
|
315
|
+
* - maturity/layer 未声明枚举值 → reject(E1 复盘修正 M1;字段缺省则跳过,
|
|
316
|
+
* 兼容只传 title/content/tags/type 的 pre-ingest 调用方;存量报告由调用方
|
|
317
|
+
* 把条目字段显式传入触发)
|
|
263
318
|
*/
|
|
264
|
-
validateEntry(entry: { title: string; content: string; tags: string[]; type: string }): LintIssue[] {
|
|
319
|
+
validateEntry(entry: { title: string; content: string; tags: string[]; type: string; maturity?: string; layer?: string }): LintIssue[] {
|
|
265
320
|
const issues: LintIssue[] = [];
|
|
266
321
|
|
|
322
|
+
// Undeclared maturity/layer enum values(判定正本 = invalidEnumIssues;字段缺省跳过,
|
|
323
|
+
// 兼容只传四字段的 pre-ingest 调用方;存量扫描走 run() → checkInvalidEnums)
|
|
324
|
+
issues.push(...this.invalidEnumIssues(entry, false));
|
|
325
|
+
|
|
267
326
|
// Content too short
|
|
268
327
|
if ((entry.content || '').length < 20) {
|
|
269
328
|
issues.push({
|
package/src/knowledge/store.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
SnapshotSurvival,
|
|
19
19
|
StoreUpdate,
|
|
20
20
|
} from './types';
|
|
21
|
+
import { MATURITY_LEVELS, STORAGE_LAYERS } from './types';
|
|
21
22
|
|
|
22
23
|
const DEFAULT_DIR = '.harness/knowledge';
|
|
23
24
|
const INDEX_FILE = 'index.json';
|
|
@@ -109,12 +110,36 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
save(entry: KnowledgeEntry): void {
|
|
113
|
+
this.assertEnumFields(entry);
|
|
112
114
|
this.writeEntryFile(entry);
|
|
113
115
|
this.updateIndexEntry(entry);
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
/**
|
|
119
|
+
* 写入闸(E1 复盘修正 M1):maturity/layer 是声明枚举(值域正本在 types.ts),
|
|
120
|
+
* 未声明值拒写报错,不静默降级。save/saveAll/applyAll 三条落盘路径共用;
|
|
121
|
+
* update 经 save 继承同一闸。存量脏条目(绕过本 store 直写文件的)在被
|
|
122
|
+
* 任一写入路径触及时抛错——先修脏值再写。
|
|
123
|
+
*/
|
|
124
|
+
private assertEnumFields(entry: KnowledgeEntry): void {
|
|
125
|
+
if (!MATURITY_LEVELS.includes(entry.maturity)) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`[harness] 知识条目 ${entry.id} 的 maturity 值 "${entry.maturity}" 未声明` +
|
|
128
|
+
`(允许: ${MATURITY_LEVELS.join(' | ')}),拒写`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (!STORAGE_LAYERS.includes(entry.layer)) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`[harness] 知识条目 ${entry.id} 的 layer 值 "${entry.layer}" 未声明` +
|
|
134
|
+
`(允许: ${STORAGE_LAYERS.join(' | ')}),拒写`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
116
139
|
saveAll(entries: KnowledgeEntry[]): void {
|
|
117
140
|
if (entries.length === 0) return;
|
|
141
|
+
// 整批预校验(写入闸):任一脏值全批拒写,不落半个批次
|
|
142
|
+
for (const entry of entries) this.assertEnumFields(entry);
|
|
118
143
|
const index = this.readIndex();
|
|
119
144
|
const position = new Map(index.map((e, i) => [e.id, i]));
|
|
120
145
|
for (const entry of entries) {
|
|
@@ -133,25 +158,37 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
133
158
|
|
|
134
159
|
applyAll(updates: StoreUpdate[]): void {
|
|
135
160
|
if (updates.length === 0) return;
|
|
161
|
+
// 先合并再整批预校验(写入闸):任一合并结果脏值全批拒写,不落半个批次。
|
|
162
|
+
// 批内同 id 多条按序累积合并(后者基于前者的合并结果,与逐条 update 同语义)。
|
|
163
|
+
const merged: KnowledgeEntry[] = [];
|
|
164
|
+
const pending = new Map<string, number>(); // id → merged 下标
|
|
165
|
+
for (const { id, partial } of updates) {
|
|
166
|
+
const idx = pending.get(id);
|
|
167
|
+
const base = idx !== undefined ? merged[idx] : this.get(id);
|
|
168
|
+
if (!base) continue;
|
|
169
|
+
const next: KnowledgeEntry = { ...base, ...partial, id };
|
|
170
|
+
if (idx !== undefined) {
|
|
171
|
+
merged[idx] = next;
|
|
172
|
+
} else {
|
|
173
|
+
pending.set(id, merged.length);
|
|
174
|
+
merged.push(next);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (merged.length === 0) return;
|
|
178
|
+
for (const entry of merged) this.assertEnumFields(entry);
|
|
136
179
|
const index = this.readIndex();
|
|
137
180
|
const position = new Map(index.map((e, i) => [e.id, i]));
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const updated: KnowledgeEntry = { ...existing, ...partial, id };
|
|
143
|
-
this.writeEntryFile(updated);
|
|
144
|
-
const indexEntry = this.toIndexEntry(updated);
|
|
145
|
-
const idx = position.get(id);
|
|
181
|
+
for (const entry of merged) {
|
|
182
|
+
this.writeEntryFile(entry);
|
|
183
|
+
const indexEntry = this.toIndexEntry(entry);
|
|
184
|
+
const idx = position.get(entry.id);
|
|
146
185
|
if (idx !== undefined) {
|
|
147
186
|
index[idx] = indexEntry;
|
|
148
187
|
} else {
|
|
149
|
-
position.set(id, index.length);
|
|
188
|
+
position.set(entry.id, index.length);
|
|
150
189
|
index.push(indexEntry);
|
|
151
190
|
}
|
|
152
|
-
applied++;
|
|
153
191
|
}
|
|
154
|
-
if (applied === 0) return;
|
|
155
192
|
this.writeIndex(index);
|
|
156
193
|
}
|
|
157
194
|
|
package/src/knowledge/types.ts
CHANGED
|
@@ -15,6 +15,12 @@ export type MaturityLevel = 'draft' | 'verified' | 'proven' | 'archived' | 'acti
|
|
|
15
15
|
|
|
16
16
|
export type StorageLayer = 'personal' | 'team' | 'tech' | 'domain' | 'project' | 'system';
|
|
17
17
|
|
|
18
|
+
/** MaturityLevel 的运行时值域正本(store 写入闸与 lint 枚举校验共用) */
|
|
19
|
+
export const MATURITY_LEVELS: readonly MaturityLevel[] = ['draft', 'verified', 'proven', 'archived', 'active', 'deprecated'];
|
|
20
|
+
|
|
21
|
+
/** StorageLayer 的运行时值域正本(store 写入闸与 lint 枚举校验共用) */
|
|
22
|
+
export const STORAGE_LAYERS: readonly StorageLayer[] = ['personal', 'team', 'tech', 'domain', 'project', 'system'];
|
|
23
|
+
|
|
18
24
|
/** How agent consumes this knowledge — drives injection strategy and lifecycle */
|
|
19
25
|
export type ConsumptionMode = 'rule' | 'reference' | 'context' | 'signal';
|
|
20
26
|
|
|
@@ -101,7 +107,7 @@ export interface QueryFilter {
|
|
|
101
107
|
|
|
102
108
|
// ── Lint ────────────────────────────────────────────────────
|
|
103
109
|
|
|
104
|
-
export type LintIssueType = 'orphan' | 'contradiction' | 'outdated' | 'duplicate' | 'index_inconsistent';
|
|
110
|
+
export type LintIssueType = 'orphan' | 'contradiction' | 'outdated' | 'duplicate' | 'index_inconsistent' | 'invalid_enum';
|
|
105
111
|
|
|
106
112
|
export interface LintIssue {
|
|
107
113
|
type: LintIssueType;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- 零 Token 成本:不调用 LLM
|
|
23
23
|
- **凡以 `skip` 策略读 JSONL,坏行计数必须到达该消费面的用户可见输出,或在该调用点显式记名豁免并写明理由(harness#100)**——静默吞掉「数据不全」不是一种可选项。承载方式纯增量:新报告入口带计数,旧入口退化为丢计数的薄包装(#82 裁决 4 的兼容约束继续成立),不另立第二份计数概念、不把 `JsonlReadResult` 提升进包根(ADR-0003)
|
|
24
24
|
- 机器可检:`src/utils/__tests__/jsonl-skip-disposition.test.ts` 三道闸——每个 `readJsonl` 调用点必须判得出策略(判不出即形状逃逸,失败)、skip 读点集合冻结(新增未声明即失败,站点消失却不删条目也失败)、每个读点上方 6 行内有 `计数去向:` 声明且邻居声明不得顶替
|
|
25
|
-
- 三类去向(逐条对得上代码):**报告体内部的降级位**随报告体走 stdout——`constraints report` 的坏行提示紧挨既有的 `traceFileExists` 说明;**不挤动既有输出的诊断告知**走 stderr——`status`(`logError`,stdout 的 `记录数` 口径与字节不变)、`failure list`(#96 定稿的 `console.error`)、`constraints retire`(交互与 `--yes` 直达两条路径各自告知);**结构化面用字段**——`constraints report --json` 的 `skippedLines`、`--export` markdown 的警示行、studio 端点响应体(配套票 studio#451)
|
|
25
|
+
- 三类去向(逐条对得上代码):**报告体内部的降级位**随报告体走 stdout——`constraints report` 的坏行提示紧挨既有的 `traceFileExists` 说明;**不挤动既有输出的诊断告知**走 stderr——`status`(`logError`,stdout 的 `记录数` 口径与字节不变)、`failure list`(#96 定稿的 `console.error`)、`constraints retire`(交互与 `--yes` 直达两条路径各自告知);**结构化面用字段**——`constraints report --json-output` 的 `skippedLines`、`--export` markdown 的警示行、studio 端点响应体(配套票 studio#451)
|
|
26
26
|
|
|
27
27
|
## 注意事项
|
|
28
28
|
- 追踪数据供 `harness constraints report` 统计与退役候选诊断消费(观测用途,不做自动降级)
|
|
@@ -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)}%`);
|
package/src/monitoring/traces.ts
CHANGED
|
@@ -38,8 +38,8 @@ const DEFAULT_CONFIG: TraceCollectorConfig = {
|
|
|
38
38
|
* ```typescript
|
|
39
39
|
* const collector = new TraceCollector({ projectPath: '/path/to/project' });
|
|
40
40
|
* collector.record({
|
|
41
|
-
* constraintId: '
|
|
42
|
-
*
|
|
41
|
+
* constraintId: 'no_completion_without_verification',
|
|
42
|
+
* severity: 'error',
|
|
43
43
|
* timestamp: Date.now(),
|
|
44
44
|
* result: 'fail',
|
|
45
45
|
* });
|
|
@@ -92,12 +92,12 @@ export class TraceCollector {
|
|
|
92
92
|
*/
|
|
93
93
|
recordPass(
|
|
94
94
|
constraintId: string,
|
|
95
|
-
|
|
95
|
+
severity: 'error' | 'warning',
|
|
96
96
|
options?: Partial<ExecutionTrace>
|
|
97
97
|
): void {
|
|
98
98
|
this.record({
|
|
99
99
|
constraintId,
|
|
100
|
-
|
|
100
|
+
severity,
|
|
101
101
|
timestamp: Date.now(),
|
|
102
102
|
result: 'pass',
|
|
103
103
|
...options,
|
|
@@ -109,12 +109,12 @@ export class TraceCollector {
|
|
|
109
109
|
*/
|
|
110
110
|
recordFail(
|
|
111
111
|
constraintId: string,
|
|
112
|
-
|
|
112
|
+
severity: 'error' | 'warning',
|
|
113
113
|
options?: Partial<ExecutionTrace>
|
|
114
114
|
): void {
|
|
115
115
|
this.record({
|
|
116
116
|
constraintId,
|
|
117
|
-
|
|
117
|
+
severity,
|
|
118
118
|
timestamp: Date.now(),
|
|
119
119
|
result: 'fail',
|
|
120
120
|
...options,
|
|
@@ -184,8 +184,8 @@ export class TraceCollector {
|
|
|
184
184
|
return false;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
//
|
|
188
|
-
if (filter.
|
|
187
|
+
// 严重性
|
|
188
|
+
if (filter.severity && trace.severity !== filter.severity) {
|
|
189
189
|
return false;
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* presets 纯数据测试
|
|
3
3
|
*
|
|
4
|
-
* 预设只保留数据(ADR-
|
|
4
|
+
* 预设只保留数据(ADR-0029:键按 severity 命名;筛选逻辑统一在 mergeConstraints),
|
|
5
5
|
* 这里锁定数据形状与 strict→standard 别名语义。
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -16,43 +16,39 @@ describe('预设数据', () => {
|
|
|
16
16
|
describe('STRICT_PRESET', () => {
|
|
17
17
|
it('应该启用所有约束', () => {
|
|
18
18
|
expect(STRICT_PRESET.name).toBe('strict');
|
|
19
|
-
expect(STRICT_PRESET.
|
|
20
|
-
expect(STRICT_PRESET.
|
|
21
|
-
expect(STRICT_PRESET.prompts).toBeNull();
|
|
19
|
+
expect(STRICT_PRESET.errors).toBeNull();
|
|
20
|
+
expect(STRICT_PRESET.warnings).toBeNull();
|
|
22
21
|
});
|
|
23
22
|
});
|
|
24
23
|
|
|
25
24
|
describe('STANDARD_PRESET', () => {
|
|
26
25
|
it('应该启用所有约束', () => {
|
|
27
26
|
expect(STANDARD_PRESET.name).toBe('standard');
|
|
28
|
-
expect(STANDARD_PRESET.
|
|
29
|
-
expect(STANDARD_PRESET.
|
|
30
|
-
expect(STANDARD_PRESET.prompts).toBeNull();
|
|
27
|
+
expect(STANDARD_PRESET.errors).toBeNull();
|
|
28
|
+
expect(STANDARD_PRESET.warnings).toBeNull();
|
|
31
29
|
});
|
|
32
30
|
});
|
|
33
31
|
|
|
34
32
|
describe('RELAXED_PRESET', () => {
|
|
35
33
|
it('应该只启用核心约束', () => {
|
|
36
34
|
expect(RELAXED_PRESET.name).toBe('relaxed');
|
|
37
|
-
expect(RELAXED_PRESET.
|
|
38
|
-
expect(RELAXED_PRESET.
|
|
39
|
-
expect(RELAXED_PRESET.prompts).toEqual([]);
|
|
35
|
+
expect(RELAXED_PRESET.errors).toHaveLength(1);
|
|
36
|
+
expect(RELAXED_PRESET.warnings).toHaveLength(1);
|
|
40
37
|
});
|
|
41
38
|
|
|
42
39
|
it('应该包含 no_completion_without_verification', () => {
|
|
43
|
-
expect(RELAXED_PRESET.
|
|
40
|
+
expect(RELAXED_PRESET.errors).toContain('no_completion_without_verification');
|
|
44
41
|
});
|
|
45
42
|
|
|
46
|
-
it('应该包含
|
|
47
|
-
expect(RELAXED_PRESET.
|
|
43
|
+
it('应该包含 no_hardcoded_credentials', () => {
|
|
44
|
+
expect(RELAXED_PRESET.warnings).toContain('no_hardcoded_credentials');
|
|
48
45
|
});
|
|
49
46
|
});
|
|
50
47
|
|
|
51
48
|
describe('PRESETS_BY_NAME', () => {
|
|
52
49
|
it('strict 是 standard 的别名(同为全部启用)', () => {
|
|
53
|
-
expect(PRESETS_BY_NAME['strict']?.
|
|
54
|
-
expect(PRESETS_BY_NAME['strict']?.
|
|
55
|
-
expect(PRESETS_BY_NAME['strict']?.prompts).toBeNull();
|
|
50
|
+
expect(PRESETS_BY_NAME['strict']?.errors).toBeNull();
|
|
51
|
+
expect(PRESETS_BY_NAME['strict']?.warnings).toBeNull();
|
|
56
52
|
});
|
|
57
53
|
|
|
58
54
|
it('三个预设名均可解析', () => {
|
package/src/presets/standard.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* 约束预设(纯数据)
|
|
3
3
|
*
|
|
4
4
|
* 预设定义哪些内置约束被启用。筛选与合并逻辑统一在
|
|
5
|
-
* core/project-config-loader.ts 的 mergeConstraints
|
|
5
|
+
* core/project-config-loader.ts 的 mergeConstraints(生效集链路),
|
|
6
6
|
* 本文件只保留预设数据,不再含函数。
|
|
7
7
|
*
|
|
8
|
-
* ADR-
|
|
8
|
+
* ADR-0029:桶按 severity 命名(errors / warnings),取代三层命名。
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -15,14 +15,11 @@ export interface PresetConfig {
|
|
|
15
15
|
/** 预设名称 */
|
|
16
16
|
name: string;
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
18
|
+
/** 启用的 severity='error' 约束 ID 列表(null 表示全部启用) */
|
|
19
|
+
errors: string[] | null;
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/** 启用的提示 ID 列表(null 表示全部启用) */
|
|
25
|
-
prompts: string[] | null;
|
|
21
|
+
/** 启用的 severity='warning' 约束 ID 列表(null 表示全部启用) */
|
|
22
|
+
warnings: string[] | null;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
/**
|
|
@@ -32,40 +29,34 @@ export interface PresetConfig {
|
|
|
32
29
|
*/
|
|
33
30
|
export const STRICT_PRESET: PresetConfig = {
|
|
34
31
|
name: 'strict',
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
prompts: null, // 全部启用
|
|
32
|
+
errors: null, // 全部启用
|
|
33
|
+
warnings: null, // 全部启用
|
|
38
34
|
};
|
|
39
35
|
|
|
40
36
|
/**
|
|
41
37
|
* 标准预设
|
|
42
38
|
*
|
|
43
|
-
*
|
|
39
|
+
* 全部约束启用
|
|
44
40
|
*/
|
|
45
41
|
export const STANDARD_PRESET: PresetConfig = {
|
|
46
42
|
name: 'standard',
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
prompts: null, // 全部启用
|
|
43
|
+
errors: null, // 全部启用
|
|
44
|
+
warnings: null, // 全部启用
|
|
50
45
|
};
|
|
51
46
|
|
|
52
47
|
/**
|
|
53
48
|
* 宽松预设
|
|
54
49
|
*
|
|
55
|
-
*
|
|
50
|
+
* 仅启用核心 error 级与凭证扫描
|
|
56
51
|
*/
|
|
57
52
|
export const RELAXED_PRESET: PresetConfig = {
|
|
58
53
|
name: 'relaxed',
|
|
59
|
-
|
|
54
|
+
errors: [
|
|
60
55
|
'no_completion_without_verification',
|
|
61
|
-
'incremental_progress',
|
|
62
|
-
'no_implementation_without_requirement',
|
|
63
56
|
],
|
|
64
|
-
|
|
65
|
-
'no_bypass_checkpoint',
|
|
57
|
+
warnings: [
|
|
66
58
|
'no_hardcoded_credentials',
|
|
67
59
|
],
|
|
68
|
-
prompts: [], // 禁用提示
|
|
69
60
|
};
|
|
70
61
|
|
|
71
62
|
/**
|
|
@@ -17,7 +17,7 @@ describe('ConstraintViolationError', () => {
|
|
|
17
17
|
it('应该使用 result.message 作为错误消息', () => {
|
|
18
18
|
const error = new ConstraintViolationError({
|
|
19
19
|
id: 'test',
|
|
20
|
-
|
|
20
|
+
severity: 'error',
|
|
21
21
|
satisfied: false,
|
|
22
22
|
message: '测试违规消息',
|
|
23
23
|
checkedAt: new Date(),
|
|
@@ -31,7 +31,7 @@ describe('ConstraintViolationError', () => {
|
|
|
31
31
|
it('应该在 message 为空时使用默认消息', () => {
|
|
32
32
|
const error = new ConstraintViolationError({
|
|
33
33
|
id: 'test',
|
|
34
|
-
|
|
34
|
+
severity: 'error',
|
|
35
35
|
satisfied: false,
|
|
36
36
|
checkedAt: new Date(),
|
|
37
37
|
});
|
|
@@ -42,7 +42,7 @@ describe('ConstraintViolationError', () => {
|
|
|
42
42
|
it('应该在 message 为空字符串时使用默认消息', () => {
|
|
43
43
|
const error = new ConstraintViolationError({
|
|
44
44
|
id: 'test',
|
|
45
|
-
|
|
45
|
+
severity: 'error',
|
|
46
46
|
satisfied: false,
|
|
47
47
|
message: '',
|
|
48
48
|
checkedAt: new Date(),
|