@dommaker/harness 0.16.8 → 0.17.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 +41 -0
- package/README.md +33 -32
- package/bin/harness.js +34 -20
- package/dist/cli/commands/check.d.ts +3 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +69 -36
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +42 -0
- package/dist/cli/commands/constraints-report.d.ts.map +1 -0
- package/dist/cli/commands/constraints-report.js +233 -0
- package/dist/cli/commands/constraints-report.js.map +1 -0
- package/dist/cli/commands/constraints-retire.d.ts +71 -0
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -0
- package/dist/cli/commands/constraints-retire.js +384 -0
- package/dist/cli/commands/constraints-retire.js.map +1 -0
- package/dist/cli/commands/constraints.d.ts +1 -1
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +4 -4
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +10 -4
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +20 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +81 -59
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.js +4 -4
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +3 -4
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +19 -2
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +79 -19
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +2 -0
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +83 -20
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +1 -5
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +14 -0
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +30 -0
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +13 -5
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +62 -25
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +10 -0
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +73 -11
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +3 -0
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +8 -2
- package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.d.ts +1 -0
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +39 -0
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +8 -4
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +29 -46
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.d.ts +6 -7
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.js +10 -17
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
- package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts +13 -0
- package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-hardcoded-credentials.js +91 -0
- package/dist/core/constraints/checkers/no-hardcoded-credentials.js.map +1 -0
- package/dist/core/constraints/checkers/types.d.ts +15 -4
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +16 -4
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/context-builder.d.ts +6 -3
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +29 -20
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts +6 -2
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +33 -574
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.d.ts +6 -2
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.js +48 -231
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
- package/dist/core/constraints/definitions/prompts.d.ts +12 -0
- package/dist/core/constraints/definitions/prompts.d.ts.map +1 -0
- package/dist/core/constraints/definitions/prompts.js +405 -0
- package/dist/core/constraints/definitions/prompts.js.map +1 -0
- package/dist/core/constraints/definitions.d.ts +15 -9
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +18 -12
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/index.d.ts +3 -2
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +8 -2
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-drift.d.ts +48 -0
- package/dist/core/constraints/injection-drift.d.ts.map +1 -0
- package/dist/core/constraints/injection-drift.js +140 -0
- package/dist/core/constraints/injection-drift.js.map +1 -0
- package/dist/core/constraints/injection-renderer.d.ts +23 -0
- package/dist/core/constraints/injection-renderer.d.ts.map +1 -0
- package/dist/core/constraints/injection-renderer.js +44 -0
- package/dist/core/constraints/injection-renderer.js.map +1 -0
- package/dist/core/constraints/interceptor.d.ts.map +1 -1
- package/dist/core/constraints/interceptor.js +3 -2
- package/dist/core/constraints/interceptor.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +95 -0
- package/dist/core/constraints/usage-report.d.ts.map +1 -0
- package/dist/core/constraints/usage-report.js +206 -0
- package/dist/core/constraints/usage-report.js.map +1 -0
- package/dist/core/effective-constraints.d.ts +47 -0
- package/dist/core/effective-constraints.d.ts.map +1 -0
- package/dist/core/effective-constraints.js +60 -0
- package/dist/core/effective-constraints.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +28 -3
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +74 -8
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/dashboard/data.d.ts +1 -9
- package/dist/dashboard/data.d.ts.map +1 -1
- package/dist/dashboard/data.js +7 -15
- package/dist/dashboard/data.js.map +1 -1
- package/dist/dashboard/types.d.ts +13 -1
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -13
- package/dist/index.js.map +1 -1
- package/dist/monitoring/index.d.ts +0 -2
- package/dist/monitoring/index.d.ts.map +1 -1
- package/dist/monitoring/index.js +0 -2
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/knowledge-evolver.d.ts +1 -1
- package/dist/monitoring/knowledge-evolver.js +1 -1
- package/dist/monitoring/trace-analyzer.d.ts +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +16 -10
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +2 -1
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +5 -3
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/standard.d.ts +3 -1
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +18 -8
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +51 -11
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +8 -4
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/monitoring-types.d.ts +2 -63
- package/dist/types/monitoring-types.d.ts.map +1 -1
- package/dist/types/monitoring-types.js +2 -2
- package/dist/types/project-config.d.ts +32 -1
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/trace.d.ts +16 -7
- package/dist/types/trace.d.ts.map +1 -1
- package/dist/types/trace.js +8 -0
- package/dist/types/trace.js.map +1 -1
- package/package.json +1 -1
- package/src/CONTEXT.md +2 -2
- package/src/__tests__/checker-extra.test.ts +356 -331
- package/src/__tests__/checker.test.ts +87 -724
- package/src/__tests__/constraint-doctor.test.ts +5 -5
- package/src/__tests__/doctor-extra.test.ts +164 -0
- package/src/__tests__/effective-constraints.test.ts +147 -0
- package/src/__tests__/extend-exceptions.test.ts +3 -3
- package/src/__tests__/iron-laws.test.ts +115 -91
- package/src/__tests__/no-hardcoded-credentials.test.ts +90 -0
- package/src/__tests__/presets.test.ts +7 -7
- package/src/__tests__/project-config-loader-extra.test.ts +4 -4
- package/src/__tests__/project-config-loader.test.ts +40 -1
- package/src/__tests__/skip-semantics.test.ts +322 -0
- package/src/cli/commands/CONTEXT.md +5 -3
- package/src/cli/commands/__tests__/check-drift.test.ts +127 -0
- package/src/cli/commands/__tests__/check-skip-output.test.ts +128 -0
- package/src/cli/commands/__tests__/check.test.ts +14 -25
- package/src/cli/commands/__tests__/constraints-report.test.ts +327 -0
- package/src/cli/commands/__tests__/constraints-retire.test.ts +283 -0
- package/src/cli/commands/__tests__/init-injection.test.ts +234 -0
- package/src/cli/commands/__tests__/status-extra.test.ts +11 -11
- package/src/cli/commands/__tests__/status.test.ts +1 -1
- package/src/cli/commands/__tests__/sync-docs.test.ts +163 -0
- package/src/cli/commands/check.ts +72 -38
- package/src/cli/commands/constraints-report.ts +234 -0
- package/src/cli/commands/constraints-retire.ts +443 -0
- package/src/cli/commands/constraints.ts +5 -5
- package/src/cli/commands/index.ts +2 -1
- package/src/cli/commands/init.ts +94 -66
- package/src/cli/commands/report.ts +6 -6
- package/src/cli/commands/status.ts +3 -4
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +90 -15
- package/src/cli/commands/sync-docs/index.ts +87 -24
- package/src/cli/commands/validate.ts +1 -5
- package/src/core/CONTEXT.md +7 -4
- package/src/core/constraints/__tests__/injection-drift.test.ts +164 -0
- package/src/core/constraints/__tests__/interceptor.test.ts +4 -0
- package/src/core/constraints/capabilities-parser.ts +28 -0
- package/src/core/constraints/checker.ts +73 -28
- package/src/core/constraints/checkers/capability-sync.ts +89 -18
- package/src/core/constraints/checkers/context-doc-sync.ts +9 -2
- package/src/core/constraints/checkers/docs-freshness.ts +43 -0
- package/src/core/constraints/checkers/index.ts +38 -71
- package/src/core/constraints/checkers/iron-flags.ts +13 -49
- package/src/core/constraints/checkers/no-hardcoded-credentials.ts +93 -0
- package/src/core/constraints/checkers/types.ts +27 -5
- package/src/core/constraints/context-builder.ts +28 -19
- package/src/core/constraints/definitions/guidelines.ts +33 -599
- package/src/core/constraints/definitions/iron-laws.ts +48 -239
- package/src/core/constraints/definitions/prompts.ts +419 -0
- package/src/core/constraints/definitions.ts +18 -11
- package/src/core/constraints/index.ts +10 -1
- package/src/core/constraints/injection-drift.ts +139 -0
- package/src/core/constraints/injection-renderer.ts +44 -0
- package/src/core/constraints/interceptor.ts +3 -2
- package/src/core/constraints/usage-report.ts +254 -0
- package/src/core/effective-constraints.ts +85 -0
- package/src/core/index.ts +4 -1
- package/src/core/project-config-loader.ts +82 -9
- package/src/dashboard/__tests__/data.test.ts +5 -12
- package/src/dashboard/data.ts +9 -19
- package/src/dashboard/types.ts +18 -1
- package/src/index.ts +0 -12
- package/src/knowledge/CONTEXT.md +2 -2
- package/src/monitoring/CONTEXT.md +3 -4
- package/src/monitoring/index.ts +0 -2
- package/src/monitoring/knowledge-evolver.ts +1 -1
- package/src/monitoring/trace-analyzer.ts +15 -10
- package/src/monitoring/traces.ts +10 -6
- package/src/presets/__tests__/standard.test.ts +11 -9
- package/src/presets/standard.ts +24 -15
- package/src/types/constraint.ts +59 -14
- package/src/types/index.ts +1 -3
- package/src/types/monitoring-types.ts +2 -84
- package/src/types/project-config.ts +37 -1
- package/src/types/trace.ts +18 -7
- package/src/verification/CONTEXT.md +1 -1
- package/dist/cli/commands/flow.d.ts +0 -22
- package/dist/cli/commands/flow.d.ts.map +0 -1
- package/dist/cli/commands/flow.js +0 -275
- package/dist/cli/commands/flow.js.map +0 -1
- package/dist/constraints/lifecycle-runner.d.ts +0 -52
- package/dist/constraints/lifecycle-runner.d.ts.map +0 -1
- package/dist/constraints/lifecycle-runner.js +0 -151
- package/dist/constraints/lifecycle-runner.js.map +0 -1
- package/dist/constraints/registry.d.ts +0 -27
- package/dist/constraints/registry.d.ts.map +0 -1
- package/dist/constraints/registry.js +0 -128
- package/dist/constraints/registry.js.map +0 -1
- package/dist/constraints/types.d.ts +0 -53
- package/dist/constraints/types.d.ts.map +0 -1
- package/dist/constraints/types.js +0 -8
- package/dist/constraints/types.js.map +0 -1
- package/dist/core/constraints/checkers/always-pass.d.ts +0 -17
- package/dist/core/constraints/checkers/always-pass.d.ts.map +0 -1
- package/dist/core/constraints/checkers/always-pass.js +0 -23
- package/dist/core/constraints/checkers/always-pass.js.map +0 -1
- package/dist/core/constraints/checkers/guideline-flags.d.ts +0 -9
- package/dist/core/constraints/checkers/guideline-flags.d.ts.map +0 -1
- package/dist/core/constraints/checkers/guideline-flags.js +0 -13
- package/dist/core/constraints/checkers/guideline-flags.js.map +0 -1
- package/dist/core/constraints/checkers/no-any-type.d.ts +0 -6
- package/dist/core/constraints/checkers/no-any-type.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-any-type.js +0 -42
- package/dist/core/constraints/checkers/no-any-type.js.map +0 -1
- package/dist/core/constraints/checkers/no-excuse-patterns.d.ts +0 -6
- package/dist/core/constraints/checkers/no-excuse-patterns.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-excuse-patterns.js +0 -25
- package/dist/core/constraints/checkers/no-excuse-patterns.js.map +0 -1
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts +0 -6
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js +0 -34
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js.map +0 -1
- package/dist/core/constraints/checkers/no-performative-agreement.d.ts +0 -8
- package/dist/core/constraints/checkers/no-performative-agreement.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-performative-agreement.js +0 -23
- package/dist/core/constraints/checkers/no-performative-agreement.js.map +0 -1
- package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts +0 -6
- package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-simplification-without-approval.js +0 -30
- package/dist/core/constraints/checkers/no-simplification-without-approval.js.map +0 -1
- package/dist/core/constraints/checkers/test-coverage.d.ts +0 -6
- package/dist/core/constraints/checkers/test-coverage.d.ts.map +0 -1
- package/dist/core/constraints/checkers/test-coverage.js +0 -33
- package/dist/core/constraints/checkers/test-coverage.js.map +0 -1
- package/dist/core/constraints/checkers/yagni.d.ts +0 -8
- package/dist/core/constraints/checkers/yagni.d.ts.map +0 -1
- package/dist/core/constraints/checkers/yagni.js +0 -96
- package/dist/core/constraints/checkers/yagni.js.map +0 -1
- package/dist/core/constraints/definitions/tips.d.ts +0 -7
- package/dist/core/constraints/definitions/tips.d.ts.map +0 -1
- package/dist/core/constraints/definitions/tips.js +0 -62
- package/dist/core/constraints/definitions/tips.js.map +0 -1
- package/dist/evolution/auto-evolve.d.ts +0 -35
- package/dist/evolution/auto-evolve.d.ts.map +0 -1
- package/dist/evolution/auto-evolve.js +0 -64
- package/dist/evolution/auto-evolve.js.map +0 -1
- package/dist/evolution/index.d.ts +0 -2
- package/dist/evolution/index.d.ts.map +0 -1
- package/dist/evolution/index.js +0 -18
- package/dist/evolution/index.js.map +0 -1
- package/dist/monitoring/constraint-evolver.d.ts +0 -120
- package/dist/monitoring/constraint-evolver.d.ts.map +0 -1
- package/dist/monitoring/constraint-evolver.js +0 -410
- package/dist/monitoring/constraint-evolver.js.map +0 -1
- package/src/__tests__/constraint-evolver.test.ts +0 -405
- package/src/__tests__/doctor-evolver.test.ts +0 -381
- package/src/cli/commands/__tests__/flow.test.ts +0 -343
- package/src/cli/commands/flow.ts +0 -277
- package/src/constraints/__tests__/lifecycle-runner.test.ts +0 -246
- package/src/constraints/__tests__/registry.test.ts +0 -196
- package/src/constraints/lifecycle-runner.ts +0 -184
- package/src/constraints/registry.ts +0 -134
- package/src/constraints/types.ts +0 -59
- package/src/core/constraints/checkers/always-pass.ts +0 -22
- package/src/core/constraints/checkers/guideline-flags.ts +0 -30
- package/src/core/constraints/checkers/no-any-type.ts +0 -42
- package/src/core/constraints/checkers/no-excuse-patterns.ts +0 -24
- package/src/core/constraints/checkers/no-fuzzy-completion-claim.ts +0 -33
- package/src/core/constraints/checkers/no-performative-agreement.ts +0 -22
- package/src/core/constraints/checkers/no-simplification-without-approval.ts +0 -29
- package/src/core/constraints/checkers/test-coverage.ts +0 -35
- package/src/core/constraints/checkers/yagni.ts +0 -65
- package/src/core/constraints/definitions/tips.ts +0 -63
- package/src/evolution/CONTEXT.md +0 -22
- package/src/evolution/__tests__/auto-evolve.test.ts +0 -319
- package/src/evolution/auto-evolve.ts +0 -98
- package/src/evolution/index.ts +0 -1
- package/src/monitoring/constraint-evolver.ts +0 -501
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* context_doc_sync:关键目录必须有 CONTEXT.md(工单 21)
|
|
3
|
+
*
|
|
4
|
+
* ADR-0001 存在性探测:项目未配置 governance.context_files 约定
|
|
5
|
+
* (未启用或无 required_dirs)时返回 'skip',不计 pass/fail。
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
8
|
import { existsSync } from 'fs';
|
|
@@ -17,10 +20,14 @@ export const contextDocSync: ConstraintCheck = {
|
|
|
17
20
|
const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
|
|
18
21
|
|
|
19
22
|
if (!contextFiles?.enabled || !Array.isArray(contextFiles.required_dirs)) {
|
|
20
|
-
return
|
|
23
|
+
return 'skip'; // 未配置 context_files 约定,跳过评估
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
const requiredDirs = contextFiles.required_dirs as string[];
|
|
27
|
+
if (requiredDirs.length === 0) {
|
|
28
|
+
return 'skip'; // 无目标目录,约定未生效
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
for (const dir of requiredDirs) {
|
|
25
32
|
const contextPath = join(projectPath, dir, 'CONTEXT.md');
|
|
26
33
|
if (!existsSync(contextPath)) {
|
|
@@ -30,7 +37,7 @@ export const contextDocSync: ConstraintCheck = {
|
|
|
30
37
|
|
|
31
38
|
return true;
|
|
32
39
|
} catch {
|
|
33
|
-
return
|
|
40
|
+
return 'skip'; // 配置不存在或解析失败 = 无法判定约定,跳过
|
|
34
41
|
}
|
|
35
42
|
},
|
|
36
43
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* docs_freshness:CAPABILITIES.md 文件表 + 能力清单格式 + CLAUDE.md + CHANGELOG(工单 21)
|
|
3
3
|
*
|
|
4
|
+
* 0. 存在性探测(ADR-0001):项目无任何 freshness 配置/目标时 skip,不计 pass/fail
|
|
4
5
|
* 1. 文件表格式:CAPABILITIES.md 中列出的文件必须仍存在(防过期引用)
|
|
5
6
|
* 2. 能力清单格式:FreshnessRunner 配置驱动检查
|
|
6
7
|
* 3. 无配置时注入内置默认配置(等价旧硬编码行为)
|
|
@@ -71,11 +72,53 @@ function checkCapabilitiesFreshness(projectPath: string): boolean {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
/**
|
|
76
|
+
* ADR-0001 存在性探测:项目是否存在任何 freshness 配置/检查目标。
|
|
77
|
+
*
|
|
78
|
+
* 判定信号(任一命中即视为采用了文档新鲜度约定):
|
|
79
|
+
* - 配置了 governance.doc_freshness.checks
|
|
80
|
+
* - 配置了 governance.context_files(enabled + 非空 required_dirs)
|
|
81
|
+
* - 项目根存在 CAPABILITIES.md
|
|
82
|
+
* - 项目根存在 CHANGELOG.md / CHANGELOG
|
|
83
|
+
*
|
|
84
|
+
* 全部缺失 → 项目未采用该约定,调用方应 skip 而非评估内置默认配置。
|
|
85
|
+
*/
|
|
86
|
+
function hasFreshnessTargets(projectPath: string): boolean {
|
|
87
|
+
if (existsSync(join(projectPath, 'CAPABILITIES.md'))) return true;
|
|
88
|
+
if (existsSync(join(projectPath, 'CHANGELOG.md')) || existsSync(join(projectPath, 'CHANGELOG'))) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const raw = loadRawProjectConfig(projectPath);
|
|
94
|
+
const governance = raw?.governance as Record<string, unknown> | undefined;
|
|
95
|
+
|
|
96
|
+
const freshnessConfig = governance?.doc_freshness as DocFreshnessConfig | undefined;
|
|
97
|
+
if (freshnessConfig?.checks && freshnessConfig.checks.length > 0) return true;
|
|
98
|
+
|
|
99
|
+
const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
|
|
100
|
+
if (
|
|
101
|
+
contextFiles?.enabled &&
|
|
102
|
+
Array.isArray(contextFiles.required_dirs) &&
|
|
103
|
+
(contextFiles.required_dirs as string[]).length > 0
|
|
104
|
+
) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
// 配置不可读视为无配置
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
74
114
|
export const docsFreshness: ConstraintCheck = {
|
|
75
115
|
id: 'docs_freshness',
|
|
76
116
|
async evaluate(env) {
|
|
77
117
|
const projectPath = env.projectPath;
|
|
78
118
|
|
|
119
|
+
// ADR-0001 存在性探测:项目无任何 freshness 配置/目标 → skip(不计 pass/fail)
|
|
120
|
+
if (!hasFreshnessTargets(projectPath)) return 'skip';
|
|
121
|
+
|
|
79
122
|
// Step 1: 文件表格式 — 检查列出的文件是否还存在
|
|
80
123
|
if (!checkCapabilitiesFreshness(projectPath)) return false;
|
|
81
124
|
|
|
@@ -1,104 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 约束检查器注册表(工单 21)
|
|
3
3
|
*
|
|
4
|
-
* checker.ts 编排层通过 getConstraintCheck(id)
|
|
5
|
-
*
|
|
4
|
+
* checker.ts 编排层通过 getConstraintCheck(id) 查找实现。
|
|
5
|
+
*
|
|
6
|
+
* 注册表闭环(ADR-0001):模块加载时校验全部 kind='check' 的内置约束
|
|
7
|
+
* 都有已注册 checker,且注册表中没有无对应 check 定义的孤儿实现;
|
|
8
|
+
* 任一不满足即抛错(加载期失败,不许静默 pass)。
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
import type { ConstraintCheck } from './types';
|
|
12
|
+
import { getAllConstraints } from '../definitions';
|
|
9
13
|
|
|
10
14
|
import {
|
|
11
|
-
noSelfApproval,
|
|
12
15
|
noCompletionWithoutVerification,
|
|
13
16
|
incrementalProgress,
|
|
14
|
-
verifyExternalCapability,
|
|
15
|
-
noImplementationWithoutRequirementReview,
|
|
16
17
|
noImplementationWithoutRequirement,
|
|
17
|
-
preferWorktree,
|
|
18
|
-
noClaimWithoutEvidence,
|
|
19
|
-
noDeleteWithoutContext,
|
|
20
|
-
twoStageReviewRequired,
|
|
21
18
|
} from './iron-flags';
|
|
22
|
-
import {
|
|
23
|
-
noFixWithoutRootCause,
|
|
24
|
-
noCodeWithoutTest,
|
|
25
|
-
simplestSolutionFirst,
|
|
26
|
-
noCreationWithoutReuseCheck,
|
|
27
|
-
noSkillWithoutTest,
|
|
28
|
-
} from './guideline-flags';
|
|
29
|
-
import {
|
|
30
|
-
surgicalChangesOnly,
|
|
31
|
-
noModelForDeterministic,
|
|
32
|
-
noConflictBlending,
|
|
33
|
-
readBeforeWrite,
|
|
34
|
-
followConventions,
|
|
35
|
-
firstPrinciplesFirst,
|
|
36
|
-
fixTheProblemNotTheGate,
|
|
37
|
-
diagnosisToFixGate,
|
|
38
|
-
readmeRequired,
|
|
39
|
-
docRequiredForPublicApi,
|
|
40
|
-
} from './always-pass';
|
|
41
19
|
import { noBypassCheckpoint } from './no-bypass-checkpoint';
|
|
42
20
|
import { noTestSimplification } from './no-test-simplification';
|
|
43
|
-
import { noFuzzyCompletionClaim } from './no-fuzzy-completion-claim';
|
|
44
|
-
import { noPerformativeAgreement } from './no-performative-agreement';
|
|
45
|
-
import { noExcusePatterns } from './no-excuse-patterns';
|
|
46
|
-
import { noAnyType } from './no-any-type';
|
|
47
21
|
import { capabilitySync } from './capability-sync';
|
|
48
|
-
import { noSimplificationWithoutApproval } from './no-simplification-without-approval';
|
|
49
|
-
import { testCoverageRequired } from './test-coverage';
|
|
50
22
|
import { contextDocSync } from './context-doc-sync';
|
|
51
23
|
import { docsFreshness } from './docs-freshness';
|
|
52
|
-
import {
|
|
24
|
+
import { noHardcodedCredentials } from './no-hardcoded-credentials';
|
|
53
25
|
|
|
54
26
|
const CHECKS: ConstraintCheck[] = [
|
|
55
27
|
// Iron Laws
|
|
56
|
-
noBypassCheckpoint,
|
|
57
|
-
noSelfApproval,
|
|
58
28
|
noCompletionWithoutVerification,
|
|
59
|
-
noTestSimplification,
|
|
60
29
|
incrementalProgress,
|
|
61
|
-
verifyExternalCapability,
|
|
62
|
-
noImplementationWithoutRequirementReview,
|
|
63
30
|
noImplementationWithoutRequirement,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
noClaimWithoutEvidence,
|
|
67
|
-
noDeleteWithoutContext,
|
|
68
|
-
noPerformativeAgreement,
|
|
69
|
-
twoStageReviewRequired,
|
|
31
|
+
noTestSimplification,
|
|
32
|
+
docsFreshness,
|
|
70
33
|
// Guidelines
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
noAnyType,
|
|
74
|
-
simplestSolutionFirst,
|
|
75
|
-
noCreationWithoutReuseCheck,
|
|
34
|
+
noHardcodedCredentials,
|
|
35
|
+
noBypassCheckpoint,
|
|
76
36
|
capabilitySync,
|
|
77
|
-
noSimplificationWithoutApproval,
|
|
78
|
-
noSkillWithoutTest,
|
|
79
|
-
testCoverageRequired,
|
|
80
37
|
contextDocSync,
|
|
81
|
-
docsFreshness,
|
|
82
|
-
noExcusePatterns,
|
|
83
|
-
yagniCheck,
|
|
84
|
-
// 行为类(promptInjection 驱动)恒通过
|
|
85
|
-
surgicalChangesOnly,
|
|
86
|
-
noModelForDeterministic,
|
|
87
|
-
noConflictBlending,
|
|
88
|
-
readBeforeWrite,
|
|
89
|
-
followConventions,
|
|
90
|
-
firstPrinciplesFirst,
|
|
91
|
-
fixTheProblemNotTheGate,
|
|
92
|
-
diagnosisToFixGate,
|
|
93
|
-
// Tips
|
|
94
|
-
readmeRequired,
|
|
95
|
-
docRequiredForPublicApi,
|
|
96
38
|
];
|
|
97
39
|
|
|
98
40
|
const registry = new Map<string, ConstraintCheck>(CHECKS.map(c => [c.id, c]));
|
|
99
41
|
|
|
42
|
+
// ========================================
|
|
43
|
+
// 注册表闭环校验(加载期)
|
|
44
|
+
// ========================================
|
|
45
|
+
|
|
46
|
+
const checkConstraints = getAllConstraints().filter(c => c.kind === 'check');
|
|
47
|
+
|
|
48
|
+
for (const c of checkConstraints) {
|
|
49
|
+
if (!registry.has(c.id)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`[harness] 约束注册表闭环校验失败:kind='check' 的约束 "${c.id}" 未注册 checker。` +
|
|
52
|
+
`请在 checkers/ 中实现并注册,或将该约束降级为 kind='prompt'。`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const id of registry.keys()) {
|
|
58
|
+
if (!checkConstraints.some(c => c.id === id)) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`[harness] 约束注册表闭环校验失败:checker "${id}" 没有对应的 kind='check' 约束定义。` +
|
|
61
|
+
`请在 definitions/ 中补齐定义,或从注册表移除。`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
100
66
|
/**
|
|
101
|
-
* 按约束 ID 查找检查实现;未注册返回 undefined
|
|
67
|
+
* 按约束 ID 查找检查实现;未注册返回 undefined
|
|
68
|
+
* (编排层对 kind='check' 未注册的情况抛错,kind='prompt' 不查表)
|
|
102
69
|
*/
|
|
103
70
|
export function getConstraintCheck(id: string): ConstraintCheck | undefined {
|
|
104
71
|
return registry.get(id);
|
|
@@ -111,4 +78,4 @@ export function registeredCheckCount(): number {
|
|
|
111
78
|
return registry.size;
|
|
112
79
|
}
|
|
113
80
|
|
|
114
|
-
export type { ConstraintCheck, CheckEnv } from './types';
|
|
81
|
+
export type { ConstraintCheck, CheckEnv, CheckOutcome } from './types';
|
|
@@ -1,62 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 铁律层轻量检查(工单 21):纯上下文证据标志判定,无 I/O
|
|
3
|
+
*
|
|
4
|
+
* ADR-0001 后仅存活的三个 flag:hasVerificationEvidence / hasSingleTask / hasRequirement
|
|
5
|
+
*
|
|
6
|
+
* 三态语义(ADR-0001 防爆措施):
|
|
7
|
+
* - undefined = flag 未接线 → skip(不评估,CLI pre-commit 等路径不误报违规)
|
|
8
|
+
* - 显式 false → fail;true → pass
|
|
3
9
|
*/
|
|
4
10
|
|
|
5
|
-
import {
|
|
11
|
+
import { contextEvidenceFlag } from './types';
|
|
6
12
|
|
|
7
|
-
export const
|
|
8
|
-
'no_self_approval',
|
|
9
|
-
ctx => ctx.hasTest === true
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
export const noCompletionWithoutVerification = contextFlag(
|
|
13
|
+
export const noCompletionWithoutVerification = contextEvidenceFlag(
|
|
13
14
|
'no_completion_without_verification',
|
|
14
|
-
ctx => ctx.hasVerificationEvidence
|
|
15
|
+
ctx => ctx.hasVerificationEvidence
|
|
15
16
|
);
|
|
16
17
|
|
|
17
|
-
export const incrementalProgress =
|
|
18
|
+
export const incrementalProgress = contextEvidenceFlag(
|
|
18
19
|
'incremental_progress',
|
|
19
|
-
|
|
20
|
-
ctx => ctx.hasSingleTask === true
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
export const verifyExternalCapability = contextFlag(
|
|
24
|
-
'verify_external_capability',
|
|
25
|
-
ctx => ctx.hasExternalCapabilityVerification === true
|
|
20
|
+
ctx => ctx.hasSingleTask
|
|
26
21
|
);
|
|
27
22
|
|
|
28
|
-
export const
|
|
29
|
-
'no_implementation_without_requirement_review',
|
|
30
|
-
ctx => ctx.hasRequirementReview === true
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
export const noImplementationWithoutRequirement = contextFlag(
|
|
23
|
+
export const noImplementationWithoutRequirement = contextEvidenceFlag(
|
|
34
24
|
'no_implementation_without_requirement',
|
|
35
|
-
ctx => ctx.hasRequirement
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
export const preferWorktree = contextFlag(
|
|
39
|
-
'prefer_worktree',
|
|
40
|
-
ctx => ctx.hasWorktree === true
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
export const noClaimWithoutEvidence = contextFlag(
|
|
44
|
-
'no_claim_without_evidence',
|
|
45
|
-
ctx =>
|
|
46
|
-
ctx.hasVerificationEvidence === true ||
|
|
47
|
-
(ctx.taskDescription || '').includes('test') ||
|
|
48
|
-
ctx.hasTest === true
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
export const noDeleteWithoutContext = contextFlag(
|
|
52
|
-
'no_delete_without_context',
|
|
53
|
-
ctx =>
|
|
54
|
-
ctx.hasRequirementReview === true ||
|
|
55
|
-
ctx.hasRequirement === true ||
|
|
56
|
-
ctx.isExistingDesign === true
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
export const twoStageReviewRequired = contextFlag(
|
|
60
|
-
'two_stage_review_required',
|
|
61
|
-
ctx => ctx.hasTwoStageReview === true
|
|
25
|
+
ctx => ctx.hasRequirement
|
|
62
26
|
);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* no_hardcoded_credentials:硬编码凭证扫描(ADR-0001:shell 扫描逻辑接入真 checker)
|
|
3
|
+
*
|
|
4
|
+
* 扫描 staged diff 的新增行(无 staged diff 时退化为变更文件内容),
|
|
5
|
+
* 命中常见私钥/密码/token 模式即 fail。
|
|
6
|
+
*
|
|
7
|
+
* 模板文件(.example/.sample/.template)与占位符值(your_xxx、process.env 引用等)豁免。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import type { ConstraintCheck } from './types';
|
|
13
|
+
|
|
14
|
+
/** 私钥/令牌指纹模式(命中即违规,无例外) */
|
|
15
|
+
const FINGERPRINT_PATTERNS: RegExp[] = [
|
|
16
|
+
// PEM 私钥头
|
|
17
|
+
/-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY(?: BLOCK)?-----/,
|
|
18
|
+
// AWS Access Key ID
|
|
19
|
+
/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/,
|
|
20
|
+
// GitHub token
|
|
21
|
+
/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/,
|
|
22
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/,
|
|
23
|
+
// Slack token
|
|
24
|
+
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
|
|
25
|
+
// Google API key
|
|
26
|
+
/\bAIza[0-9A-Za-z_-]{35}\b/,
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
/** 通用赋值模式:password/secret/token/api key = "字面量"(不要求前词边界,覆盖 dbPassword 等 camelCase) */
|
|
30
|
+
const ASSIGNMENT_PATTERN =
|
|
31
|
+
/(?:password|passwd|secret|api[_-]?key|access[_-]?token|auth[_-]?token)\b\s*[:=]\s*['"][^'"\n]{8,}['"]/i;
|
|
32
|
+
|
|
33
|
+
/** 占位符/环境变量引用等明显非真实值 */
|
|
34
|
+
const PLACEHOLDER_PATTERN =
|
|
35
|
+
/process\.env|your[_-]|<[^>]+>|\$\{|xxx+|placeholder|example|changeme|dummy|fake|test/i;
|
|
36
|
+
|
|
37
|
+
/** 允许占位符的模板文件后缀 */
|
|
38
|
+
const TEMPLATE_SUFFIXES = ['.example', '.sample', '.template', '.dist'];
|
|
39
|
+
|
|
40
|
+
function isTemplateFile(file: string): boolean {
|
|
41
|
+
return TEMPLATE_SUFFIXES.some(s => file.endsWith(s));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 判定一段文本是否包含硬编码凭证 */
|
|
45
|
+
export function containsHardcodedCredential(text: string): boolean {
|
|
46
|
+
for (const line of text.split('\n')) {
|
|
47
|
+
// 占位符/环境变量引用行整体豁免(your_xxx、process.env、示例值等)
|
|
48
|
+
if (PLACEHOLDER_PATTERN.test(line)) continue;
|
|
49
|
+
for (const pattern of FINGERPRINT_PATTERNS) {
|
|
50
|
+
if (pattern.test(line)) return true;
|
|
51
|
+
}
|
|
52
|
+
if (ASSIGNMENT_PATTERN.test(line)) return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const noHardcodedCredentials: ConstraintCheck = {
|
|
58
|
+
id: 'no_hardcoded_credentials',
|
|
59
|
+
evaluate: async (env) => {
|
|
60
|
+
const files = (env.context.changedFiles || []).filter(f => !isTemplateFile(f));
|
|
61
|
+
|
|
62
|
+
// 1) staged diff 新增行
|
|
63
|
+
let scanned = false;
|
|
64
|
+
try {
|
|
65
|
+
const diff = await env.stagedDiff();
|
|
66
|
+
const added = diff
|
|
67
|
+
.split('\n')
|
|
68
|
+
.filter(l => l.startsWith('+') && !l.startsWith('+++'))
|
|
69
|
+
.map(l => l.slice(1))
|
|
70
|
+
.join('\n');
|
|
71
|
+
if (added.trim().length > 0) {
|
|
72
|
+
scanned = true;
|
|
73
|
+
if (containsHardcodedCredential(added)) return false;
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// diff 不可用 → 退化为文件内容扫描
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 2) 变更文件内容(diff 为空或不可用时)
|
|
80
|
+
if (!scanned) {
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
try {
|
|
83
|
+
const content = readFileSync(join(env.projectPath, file), 'utf-8');
|
|
84
|
+
if (containsHardcodedCredential(content)) return false;
|
|
85
|
+
} catch {
|
|
86
|
+
// 文件不可读(已删除/二进制)→ 跳过
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return true;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -26,14 +26,22 @@ export interface CheckEnv {
|
|
|
26
26
|
srcScan(root: string): string[];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* 检查结果三态(ADR-0001 存在性探测 / flag 未接线):
|
|
31
|
+
* - true = 满足(pass)
|
|
32
|
+
* - false = 违反(fail)
|
|
33
|
+
* - 'skip' = 未评估(项目未采用对应约定,或证据 flag 未接线),不计 pass/fail
|
|
34
|
+
*/
|
|
35
|
+
export type CheckOutcome = boolean | 'skip';
|
|
36
|
+
|
|
29
37
|
/**
|
|
30
38
|
* 单条约束检查实现
|
|
31
39
|
*/
|
|
32
40
|
export interface ConstraintCheck {
|
|
33
41
|
/** 约束 ID(与 definitions 一致) */
|
|
34
42
|
id: string;
|
|
35
|
-
/** 检查主体:true =
|
|
36
|
-
evaluate(env: CheckEnv): Promise<
|
|
43
|
+
/** 检查主体:true = 满足;false = 违反;'skip' = 未评估 */
|
|
44
|
+
evaluate(env: CheckEnv): Promise<CheckOutcome> | CheckOutcome;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
/**
|
|
@@ -47,8 +55,22 @@ export function contextFlag(
|
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
/**
|
|
50
|
-
*
|
|
58
|
+
* 构造证据标志检查(ADR-0001:flag 未接线 = skip 而非 fail)
|
|
59
|
+
*
|
|
60
|
+
* - flag === undefined:调用方未接线该证据 → 'skip'(不评估,不误报违规)
|
|
61
|
+
* - flag === false:显式无证据 → fail
|
|
62
|
+
* - flag === true:有证据 → pass
|
|
51
63
|
*/
|
|
52
|
-
export function
|
|
53
|
-
|
|
64
|
+
export function contextEvidenceFlag(
|
|
65
|
+
id: string,
|
|
66
|
+
pick: (context: ConstraintContext) => boolean | undefined
|
|
67
|
+
): ConstraintCheck {
|
|
68
|
+
return {
|
|
69
|
+
id,
|
|
70
|
+
evaluate: (env) => {
|
|
71
|
+
const value = pick(env.context);
|
|
72
|
+
if (value === undefined) return 'skip';
|
|
73
|
+
return value;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
54
76
|
}
|
|
@@ -11,6 +11,7 @@ import * as path from 'path';
|
|
|
11
11
|
import { execSync } from 'child_process';
|
|
12
12
|
import { execAsync } from '../../utils/exec';
|
|
13
13
|
import { detectSourceRoots } from '../../utils/detect-source-roots';
|
|
14
|
+
import { DEFAULT_TRACE_FILE } from '../../types/trace';
|
|
14
15
|
import type { ConstraintContext, ConstraintTrigger } from '../../types/constraint';
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -58,21 +59,31 @@ export function isNewDirectory(headDirs: Set<string> | null, filePath: string):
|
|
|
58
59
|
return !headDirs.has(path.dirname(filePath));
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* 代码文件扩展名(ADR-0001:code_implementation 推断信号)
|
|
64
|
+
*
|
|
65
|
+
* 纯文档/配置(.md/.json/.yml 等)不算代码变更。
|
|
66
|
+
*/
|
|
67
|
+
const CODE_FILE_REGEX = /\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|rb|php|cs|cpp|cc|c|h|vue|svelte)$/i;
|
|
68
|
+
|
|
61
69
|
/**
|
|
62
70
|
* 检测触发条件
|
|
71
|
+
*
|
|
72
|
+
* ADR-0001:变更包含代码文件时,在主推断之外附加 code_implementation
|
|
73
|
+
* (返回数组 [主推断, 'code_implementation'];主推断逻辑保持不变)。
|
|
63
74
|
*/
|
|
64
75
|
export function detectTrigger(
|
|
65
76
|
changedFiles: string[],
|
|
66
77
|
options: { trigger?: ConstraintTrigger; projectPath?: string }
|
|
67
|
-
): ConstraintTrigger {
|
|
78
|
+
): ConstraintTrigger | ConstraintTrigger[] {
|
|
68
79
|
if (options.trigger) return options.trigger;
|
|
69
80
|
|
|
70
81
|
// 根据变更文件推断触发条件
|
|
71
|
-
// 注意:不自动推断 code_implementation——那是 caller 的语义判断。
|
|
72
|
-
// pre-commit 无法区分"模板编辑"和"逻辑实现",保守使用 file_modification。
|
|
73
82
|
const hasTestChange = changedFiles.some(f =>
|
|
74
83
|
f.includes('.test.') || f.includes('.spec.') || f.includes('__tests__')
|
|
75
84
|
);
|
|
85
|
+
// pre-commit 场景下变更含代码文件 → 附加 code_implementation 推断
|
|
86
|
+
const hasCodeChange = changedFiles.some(f => CODE_FILE_REGEX.test(f));
|
|
76
87
|
const projectPath = options.projectPath || process.cwd();
|
|
77
88
|
const sourceRoots = detectSourceRoots(projectPath);
|
|
78
89
|
const hasModuleChange = changedFiles.some(f =>
|
|
@@ -84,23 +95,22 @@ export function detectTrigger(
|
|
|
84
95
|
isNewDirectory(headDirs, f)
|
|
85
96
|
);
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
if (
|
|
98
|
+
let primary: ConstraintTrigger;
|
|
99
|
+
if (hasTestChange && !hasModuleChange) primary = 'test_creation';
|
|
100
|
+
else if (hasModuleCreation) primary = 'module_creation';
|
|
101
|
+
else if (hasModuleChange) primary = 'module_modification';
|
|
102
|
+
else primary = 'file_modification';
|
|
90
103
|
|
|
91
|
-
return '
|
|
104
|
+
return hasCodeChange ? [primary, 'code_implementation'] : primary;
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
/**
|
|
95
108
|
* 检测是否有失败的测试记录
|
|
96
|
-
* 扫描
|
|
109
|
+
* 扫描 trace 文件(DEFAULT_TRACE_FILE)中最近的记录
|
|
97
110
|
*/
|
|
98
111
|
export async function detectFailingTest(projectPath: string): Promise<boolean> {
|
|
99
112
|
try {
|
|
100
|
-
const
|
|
101
|
-
if (!fs.existsSync(tracesDir)) return false;
|
|
102
|
-
|
|
103
|
-
const traceFile = path.join(tracesDir, 'execution.log');
|
|
113
|
+
const traceFile = path.join(projectPath, DEFAULT_TRACE_FILE);
|
|
104
114
|
if (!fs.existsSync(traceFile)) return false;
|
|
105
115
|
|
|
106
116
|
const content = fs.readFileSync(traceFile, 'utf-8');
|
|
@@ -146,14 +156,11 @@ export function detectRootCauseInvestigation(projectPath: string): boolean {
|
|
|
146
156
|
|
|
147
157
|
/**
|
|
148
158
|
* 检测是否有验证证据
|
|
149
|
-
* 检查
|
|
159
|
+
* 检查 trace 文件(DEFAULT_TRACE_FILE)中最近的成功验证记录
|
|
150
160
|
*/
|
|
151
161
|
export async function detectVerificationEvidence(projectPath: string): Promise<boolean> {
|
|
152
162
|
try {
|
|
153
|
-
const
|
|
154
|
-
if (!fs.existsSync(tracesDir)) return false;
|
|
155
|
-
|
|
156
|
-
const traceFile = path.join(tracesDir, 'execution.log');
|
|
163
|
+
const traceFile = path.join(projectPath, DEFAULT_TRACE_FILE);
|
|
157
164
|
if (!fs.existsSync(traceFile)) return false;
|
|
158
165
|
|
|
159
166
|
const content = fs.readFileSync(traceFile, 'utf-8');
|
|
@@ -245,10 +252,12 @@ export async function buildConstraintContext(options: {
|
|
|
245
252
|
}): Promise<ConstraintContext> {
|
|
246
253
|
const projectPath = options.projectPath || process.cwd();
|
|
247
254
|
const changedFiles = await getChangedFiles(options.staged, options.projectPath);
|
|
248
|
-
const
|
|
255
|
+
const inferred = detectTrigger(changedFiles, { trigger: options.trigger, projectPath });
|
|
256
|
+
const triggers = Array.isArray(inferred) ? inferred : [inferred];
|
|
249
257
|
|
|
250
258
|
return {
|
|
251
|
-
operation:
|
|
259
|
+
operation: triggers[0],
|
|
260
|
+
extraTriggers: triggers.slice(1),
|
|
252
261
|
projectPath,
|
|
253
262
|
changedFiles,
|
|
254
263
|
hasTest: changedFiles.some(f => f.includes('.test.') || f.includes('.spec.')),
|