@dommaker/harness 0.18.0 → 1.0.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 +23 -0
- package/bin/harness.js +136 -552
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +2 -13
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +90 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -0
- package/dist/cli/commands/definitions.js +519 -0
- package/dist/cli/commands/definitions.js.map +1 -0
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +3 -25
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +3 -93
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +4 -4
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/update-user-model.d.ts +1 -0
- package/dist/cli/commands/update-user-model.d.ts.map +1 -1
- package/dist/cli/commands/update-user-model.js +12 -5
- package/dist/cli/commands/update-user-model.js.map +1 -1
- package/dist/context/index.d.ts +1 -5
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +1 -5
- package/dist/context/index.js.map +1 -1
- package/dist/context/knowledge-injector.d.ts +3 -3
- package/dist/context/knowledge-injector.d.ts.map +1 -1
- package/dist/context/knowledge-injector.js +1 -1
- package/dist/context/session-manager.d.ts +1 -12
- package/dist/context/session-manager.d.ts.map +1 -1
- package/dist/context/session-manager.js +0 -63
- package/dist/context/session-manager.js.map +1 -1
- package/dist/context/types.d.ts +1 -39
- package/dist/context/types.d.ts.map +1 -1
- package/dist/context/types.js +2 -15
- package/dist/context/types.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.d.ts +32 -0
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -0
- package/dist/core/constraints/agent-prompt-renderer.js +58 -0
- package/dist/core/constraints/agent-prompt-renderer.js.map +1 -0
- package/dist/core/constraints/capabilities-parser.d.ts +17 -2
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +84 -2
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/check-cache.d.ts +48 -3
- package/dist/core/constraints/check-cache.d.ts.map +1 -1
- package/dist/core/constraints/check-cache.js +53 -5
- package/dist/core/constraints/check-cache.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +3 -17
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +7 -47
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +0 -7
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +1 -8
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/index.d.ts +5 -1
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +8 -3
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/interceptor.d.ts.map +1 -1
- package/dist/core/constraints/interceptor.js +1 -5
- package/dist/core/constraints/interceptor.js.map +1 -1
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +0 -1
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +1 -8
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/dashboard/stats.d.ts.map +1 -1
- package/dist/dashboard/stats.js.map +1 -1
- package/dist/dashboard/types.d.ts +2 -2
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/failure/constraint-handler.d.ts.map +1 -1
- package/dist/failure/constraint-handler.js +0 -2
- package/dist/failure/constraint-handler.js.map +1 -1
- package/dist/gates/acceptance.d.ts +9 -1
- package/dist/gates/acceptance.d.ts.map +1 -1
- package/dist/gates/acceptance.js +21 -0
- package/dist/gates/acceptance.js.map +1 -1
- package/dist/gates/checker-gate.d.ts +23 -0
- package/dist/gates/checker-gate.d.ts.map +1 -0
- package/dist/gates/checker-gate.js +53 -0
- package/dist/gates/checker-gate.js.map +1 -0
- package/dist/gates/command.d.ts +9 -2
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +10 -0
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts +8 -2
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +9 -0
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/decision.d.ts +17 -0
- package/dist/gates/decision.d.ts.map +1 -0
- package/dist/gates/decision.js +25 -0
- package/dist/gates/decision.js.map +1 -0
- package/dist/gates/definitions.d.ts +63 -0
- package/dist/gates/definitions.d.ts.map +1 -0
- package/dist/gates/definitions.js +160 -0
- package/dist/gates/definitions.js.map +1 -0
- package/dist/gates/effective-gates.d.ts +30 -0
- package/dist/gates/effective-gates.d.ts.map +1 -0
- package/dist/gates/effective-gates.js +85 -0
- package/dist/gates/effective-gates.js.map +1 -0
- package/dist/gates/index.d.ts +25 -1
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js +37 -1
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/performance.d.ts +8 -2
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +9 -0
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/registry.d.ts +32 -0
- package/dist/gates/registry.d.ts.map +1 -0
- package/dist/gates/registry.js +90 -0
- package/dist/gates/registry.js.map +1 -0
- package/dist/gates/review.d.ts +8 -2
- package/dist/gates/review.d.ts.map +1 -1
- package/dist/gates/review.js +9 -0
- package/dist/gates/review.js.map +1 -1
- package/dist/gates/runner.d.ts +30 -0
- package/dist/gates/runner.d.ts.map +1 -0
- package/dist/gates/runner.js +38 -0
- package/dist/gates/runner.js.map +1 -0
- package/dist/gates/security.d.ts +8 -2
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +9 -0
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +41 -1
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +0 -2
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/config.d.ts +26 -0
- package/dist/hooks/config.d.ts.map +1 -0
- package/dist/hooks/config.js +30 -0
- package/dist/hooks/config.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/registry.d.ts +18 -1
- package/dist/hooks/registry.d.ts.map +1 -1
- package/dist/hooks/registry.js +45 -0
- package/dist/hooks/registry.js.map +1 -1
- package/dist/hooks/types.d.ts +20 -0
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -21
- package/dist/index.js.map +1 -1
- package/dist/knowledge/import.d.ts +2 -2
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
- package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
- package/dist/knowledge/types.d.ts +5 -7
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js +1 -1
- package/dist/knowledge/types.js.map +1 -1
- package/dist/monitoring/constraint-doctor.d.ts +4 -22
- package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
- package/dist/monitoring/constraint-doctor.js +7 -94
- package/dist/monitoring/constraint-doctor.js.map +1 -1
- package/dist/presets/standard.d.ts +1 -1
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +1 -2
- package/dist/presets/standard.js.map +1 -1
- package/dist/spec/annotation-checker.d.ts.map +1 -1
- package/dist/spec/annotation-checker.js +6 -26
- package/dist/spec/annotation-checker.js.map +1 -1
- package/dist/tools/index.d.ts +0 -4
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/types/constraint.d.ts +1 -12
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +0 -3
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/project-config.d.ts +0 -2
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/utils/file-walk.d.ts +2 -2
- package/dist/utils/file-walk.js +2 -2
- package/package.json +9 -4
- package/src/CONTEXT.md +14 -0
- package/src/__tests__/checker-extra.test.ts +2 -59
- package/src/__tests__/constraint-doctor.test.ts +1 -140
- package/src/__tests__/constraint-handler.test.ts +0 -4
- package/src/__tests__/project-config-loader-extra.test.ts +0 -20
- package/src/__tests__/project-config-loader.test.ts +0 -1
- package/src/cli/commands/CONTEXT.md +15 -7
- package/src/cli/commands/__tests__/analyze-sessions.test.ts +178 -0
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
- package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
- package/src/cli/commands/__tests__/check.test.ts +7 -75
- package/src/cli/commands/__tests__/registry.test.ts +201 -0
- package/src/cli/commands/__tests__/release.test.ts +162 -0
- package/src/cli/commands/__tests__/update-user-model.test.ts +205 -0
- package/src/cli/commands/check.ts +2 -14
- package/src/cli/commands/definitions.ts +593 -0
- package/src/cli/commands/knowledge.ts +3 -3
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
- package/src/cli/commands/sync-docs/index.ts +8 -5
- package/src/cli/commands/update-user-model.ts +15 -5
- package/src/context/CONTEXT.md +2 -7
- package/src/context/__tests__/session-manager.test.ts +0 -91
- package/src/context/index.ts +1 -5
- package/src/context/knowledge-injector.ts +3 -3
- package/src/context/session-manager.ts +0 -74
- package/src/context/types.ts +2 -63
- package/src/core/CONTEXT.md +1 -1
- package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +129 -0
- package/src/core/constraints/__tests__/check-cache.test.ts +227 -0
- package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
- package/src/core/constraints/agent-prompt-renderer.ts +71 -0
- package/src/core/constraints/capabilities-parser.ts +95 -2
- package/src/core/constraints/check-cache.ts +75 -3
- package/src/core/constraints/checker.ts +8 -53
- package/src/core/constraints/definitions.ts +0 -8
- package/src/core/constraints/doc-freshness/runner.ts +1 -2
- package/src/core/constraints/index.ts +11 -2
- package/src/core/constraints/interceptor.ts +1 -6
- package/src/core/effective-constraints.ts +0 -1
- package/src/core/project-config-loader.ts +1 -7
- package/src/dashboard/stats.ts +2 -2
- package/src/dashboard/types.ts +2 -2
- package/src/failure/constraint-handler.ts +0 -2
- package/src/gates/CONTEXT.md +16 -13
- package/src/gates/__tests__/checker-gate.test.ts +45 -0
- package/src/gates/__tests__/contract.test.ts +73 -0
- package/src/gates/__tests__/effective-gates.test.ts +82 -0
- package/src/gates/__tests__/gate-interface.test.ts +202 -0
- package/src/gates/__tests__/registry.test.ts +87 -0
- package/src/gates/__tests__/runner.test.ts +81 -0
- package/src/gates/acceptance.ts +24 -1
- package/src/gates/checker-gate.ts +54 -0
- package/src/gates/command.ts +13 -2
- package/src/gates/contract.ts +12 -2
- package/src/gates/decision.ts +27 -0
- package/src/gates/definitions.ts +207 -0
- package/src/gates/effective-gates.ts +106 -0
- package/src/gates/index.ts +38 -0
- package/src/gates/performance.ts +12 -2
- package/src/gates/registry.ts +107 -0
- package/src/gates/review.ts +12 -2
- package/src/gates/runner.ts +57 -0
- package/src/gates/security.ts +12 -2
- package/src/gates/types.ts +48 -1
- package/src/hooks/CONTEXT.md +12 -2
- package/src/hooks/__tests__/bootstrap.test.ts +0 -1
- package/src/hooks/__tests__/config.test.ts +19 -0
- package/src/hooks/__tests__/pipeline.test.ts +79 -0
- package/src/hooks/__tests__/registry.test.ts +67 -0
- package/src/hooks/bootstrap.ts +0 -2
- package/src/hooks/config.ts +29 -0
- package/src/hooks/index.ts +2 -1
- package/src/hooks/registry.ts +60 -1
- package/src/hooks/types.ts +21 -0
- package/src/index.ts +13 -22
- package/src/knowledge/import.ts +3 -3
- package/src/knowledge/ingest.ts +4 -4
- package/src/knowledge/lifecycle-hooks.ts +3 -3
- package/src/knowledge/types.ts +5 -8
- package/src/monitoring/constraint-doctor.ts +7 -106
- package/src/presets/__tests__/standard.test.ts +0 -2
- package/src/presets/standard.ts +1 -2
- package/src/spec/__tests__/annotation-checker.test.ts +9 -3
- package/src/spec/annotation-checker.ts +7 -29
- package/src/tools/CONTEXT.md +7 -14
- package/src/tools/index.ts +0 -4
- package/src/types/constraint.ts +1 -14
- package/src/types/project-config.ts +0 -3
- package/src/utils/file-walk.ts +2 -2
- package/dist/architecture/constraint-engine.d.ts +0 -67
- package/dist/architecture/constraint-engine.d.ts.map +0 -1
- package/dist/architecture/constraint-engine.js +0 -295
- package/dist/architecture/constraint-engine.js.map +0 -1
- package/dist/architecture/cross-project-checker.d.ts +0 -54
- package/dist/architecture/cross-project-checker.d.ts.map +0 -1
- package/dist/architecture/cross-project-checker.js +0 -467
- package/dist/architecture/cross-project-checker.js.map +0 -1
- package/dist/cli/commands/index.d.ts +0 -30
- package/dist/cli/commands/index.d.ts.map +0 -1
- package/dist/cli/commands/index.js +0 -91
- package/dist/cli/commands/index.js.map +0 -1
- package/dist/context/file-budget.d.ts +0 -40
- package/dist/context/file-budget.d.ts.map +0 -1
- package/dist/context/file-budget.js +0 -147
- package/dist/context/file-budget.js.map +0 -1
- package/dist/context/progressive-loader.d.ts +0 -109
- package/dist/context/progressive-loader.d.ts.map +0 -1
- package/dist/context/progressive-loader.js +0 -166
- package/dist/context/progressive-loader.js.map +0 -1
- package/dist/context/token-pipeline.d.ts +0 -55
- package/dist/context/token-pipeline.d.ts.map +0 -1
- package/dist/context/token-pipeline.js +0 -220
- package/dist/context/token-pipeline.js.map +0 -1
- package/dist/context/tool-output-budget.d.ts +0 -59
- package/dist/context/tool-output-budget.d.ts.map +0 -1
- package/dist/context/tool-output-budget.js +0 -177
- package/dist/context/tool-output-budget.js.map +0 -1
- package/dist/core/constraints/default-executors.d.ts +0 -10
- package/dist/core/constraints/default-executors.d.ts.map +0 -1
- package/dist/core/constraints/default-executors.js +0 -120
- package/dist/core/constraints/default-executors.js.map +0 -1
- package/dist/llm/adapter.d.ts +0 -36
- package/dist/llm/adapter.d.ts.map +0 -1
- package/dist/llm/adapter.js +0 -118
- package/dist/llm/adapter.js.map +0 -1
- package/dist/llm/index.d.ts +0 -3
- package/dist/llm/index.d.ts.map +0 -1
- package/dist/llm/index.js +0 -19
- package/dist/llm/index.js.map +0 -1
- package/dist/llm/types.d.ts +0 -62
- package/dist/llm/types.d.ts.map +0 -1
- package/dist/llm/types.js +0 -8
- package/dist/llm/types.js.map +0 -1
- package/dist/tools/core/index.d.ts +0 -13
- package/dist/tools/core/index.d.ts.map +0 -1
- package/dist/tools/core/index.js +0 -198
- package/dist/tools/core/index.js.map +0 -1
- package/dist/tools/loader.d.ts +0 -21
- package/dist/tools/loader.d.ts.map +0 -1
- package/dist/tools/loader.js +0 -199
- package/dist/tools/loader.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -59
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -115
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/types.d.ts +0 -38
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js +0 -6
- package/dist/tools/types.js.map +0 -1
- package/src/__tests__/architecture-engine.test.ts +0 -619
- package/src/__tests__/context/progressive-loader.test.ts +0 -131
- package/src/__tests__/cross-project-checker.test.ts +0 -318
- package/src/architecture/CONTEXT.md +0 -22
- package/src/architecture/constraint-engine.ts +0 -351
- package/src/architecture/cross-project-checker.ts +0 -545
- package/src/cli/commands/index.ts +0 -30
- package/src/context/__tests__/file-budget.test.ts +0 -142
- package/src/context/__tests__/progressive-loader.test.ts +0 -264
- package/src/context/__tests__/token-pipeline.test.ts +0 -178
- package/src/context/__tests__/tool-output-budget.test.ts +0 -95
- package/src/context/file-budget.ts +0 -142
- package/src/context/progressive-loader.ts +0 -260
- package/src/context/token-pipeline.ts +0 -265
- package/src/context/tool-output-budget.ts +0 -178
- package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
- package/src/core/constraints/default-executors.ts +0 -92
- package/src/llm/CONTEXT.md +0 -21
- package/src/llm/__tests__/adapter.test.ts +0 -172
- package/src/llm/adapter.ts +0 -149
- package/src/llm/index.ts +0 -2
- package/src/llm/types.ts +0 -81
- package/src/tools/__tests__/loader.test.ts +0 -252
- package/src/tools/__tests__/registry.test.ts +0 -157
- package/src/tools/core/__tests__/core.test.ts +0 -122
- package/src/tools/core/index.ts +0 -201
- package/src/tools/loader.ts +0 -216
- package/src/tools/registry.ts +0 -128
- package/src/tools/types.ts +0 -46
|
@@ -4,10 +4,19 @@
|
|
|
4
4
|
* 缓存 git diff 和 src/ 递归扫描结果,减少重复 I/O。
|
|
5
5
|
* 同一请求内多次 checkConstraints 调用共享缓存。
|
|
6
6
|
*
|
|
7
|
+
* 计数采样(H6/G5,收编 studio runtime/cache.ts):
|
|
8
|
+
* get/getSync 可传声明式采样配置,对每个 (namespace, key) 独立计数,
|
|
9
|
+
* 第 1 次调用必执行,此后每 N 次执行 1 次完整检查,其余 N-1 次复用缓存;
|
|
10
|
+
* 非采样轮缓存未命中(无缓存或已过期)时返回 defaultValueOnMiss,不执行 fn。
|
|
11
|
+
* invalidate 同时重置对应计数,保证失效后下一次调用恢复为完整检查。
|
|
12
|
+
*
|
|
7
13
|
* 用法:
|
|
8
14
|
* ```typescript
|
|
9
15
|
* const cache = new CheckCache({ ttlMs: 5000 });
|
|
16
|
+
* // 普通 TTL 缓存
|
|
10
17
|
* const diff = await cache.get('git_diff', projectPath, () => runCommand('git diff --cached', projectPath));
|
|
18
|
+
* // 计数采样:每 3 次调用执行 1 次完整检查
|
|
19
|
+
* const ok = await cache.get('goal_check', key, check, { sampleRate: 3, defaultValueOnMiss: true });
|
|
11
20
|
* ```
|
|
12
21
|
*/
|
|
13
22
|
|
|
@@ -16,13 +25,41 @@ interface CacheEntry {
|
|
|
16
25
|
expiresAt: number;
|
|
17
26
|
}
|
|
18
27
|
|
|
28
|
+
/**
|
|
29
|
+
* CheckCache 构造配置
|
|
30
|
+
*/
|
|
19
31
|
export interface CheckCacheConfig {
|
|
20
32
|
/** 缓存 TTL(毫秒),默认 5000 */
|
|
21
33
|
ttlMs: number;
|
|
22
34
|
}
|
|
23
35
|
|
|
36
|
+
/**
|
|
37
|
+
* 计数采样配置(声明式)
|
|
38
|
+
*
|
|
39
|
+
* 传此配置后,对应 (namespace, key) 启用计数采样:
|
|
40
|
+
* - 第 1 次调用必执行 fn(采样轮),结果以正常 TTL 写入缓存;
|
|
41
|
+
* - 其后每 N 次调用执行 1 次(计数独立于普通缓存读写);
|
|
42
|
+
* - 非采样轮:缓存命中(未过期)返回缓存值;未命中或已过期
|
|
43
|
+
* 返回 {@link defaultValueOnMiss},不执行 fn、不刷新 TTL。
|
|
44
|
+
*/
|
|
45
|
+
export interface CheckSamplingConfig<T> {
|
|
46
|
+
/** 采样率:每 N 次调用执行 1 次完整检查;N <= 1 视为不采样(回退为普通 TTL 缓存语义) */
|
|
47
|
+
sampleRate: number;
|
|
48
|
+
/** 非采样轮且缓存未命中(无缓存或已过期)时的默认返回值 */
|
|
49
|
+
defaultValueOnMiss: T;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 约束检查缓存:TTL 缓存 + 可选计数采样
|
|
54
|
+
*
|
|
55
|
+
* 普通 get/getSync 为 TTL 缓存;传入 sampling 配置后对应 (namespace, key)
|
|
56
|
+
* 启用计数采样(每 N 次执行 1 次完整检查,其余轮次复用缓存或返回
|
|
57
|
+
* defaultValueOnMiss)。语义详见 {@link CheckSamplingConfig}。
|
|
58
|
+
*/
|
|
24
59
|
export class CheckCache {
|
|
25
60
|
private store = new Map<string, CacheEntry>();
|
|
61
|
+
/** 采样计数器(仅启用采样的 key 有计数) */
|
|
62
|
+
private sampleCounters = new Map<string, number>();
|
|
26
63
|
private ttlMs: number;
|
|
27
64
|
|
|
28
65
|
constructor(config?: CheckCacheConfig) {
|
|
@@ -35,19 +72,28 @@ export class CheckCache {
|
|
|
35
72
|
* @param namespace 命名空间(如 'git_diff', 'src_scan')
|
|
36
73
|
* @param key 缓存键(如 projectPath)
|
|
37
74
|
* @param fn miss 时的计算函数
|
|
75
|
+
* @param sampling 计数采样配置(可选;不传时每次 miss 都执行 fn)
|
|
38
76
|
*/
|
|
39
77
|
async get<T>(
|
|
40
78
|
namespace: string,
|
|
41
79
|
key: string,
|
|
42
80
|
fn: () => Promise<T>,
|
|
81
|
+
sampling?: CheckSamplingConfig<T>,
|
|
43
82
|
): Promise<T> {
|
|
44
83
|
const cacheKey = `${namespace}:${key}`;
|
|
45
84
|
const entry = this.store.get(cacheKey);
|
|
85
|
+
const rate = sampling ? Math.max(1, Math.floor(sampling.sampleRate)) : 1;
|
|
86
|
+
// 采样轮优先于缓存命中:每 N 次调用必执行一次完整检查(第 1 次必执行)
|
|
87
|
+
const sampleTurn = rate > 1 && this.isSampleTurn(cacheKey, rate);
|
|
46
88
|
|
|
47
|
-
if (entry && Date.now() < entry.expiresAt) {
|
|
89
|
+
if (!sampleTurn && entry && Date.now() < entry.expiresAt) {
|
|
48
90
|
return entry.value as T;
|
|
49
91
|
}
|
|
50
92
|
|
|
93
|
+
if (sampling && rate > 1 && !sampleTurn) {
|
|
94
|
+
return sampling.defaultValueOnMiss;
|
|
95
|
+
}
|
|
96
|
+
|
|
51
97
|
const value = await fn();
|
|
52
98
|
this.store.set(cacheKey, {
|
|
53
99
|
value,
|
|
@@ -58,19 +104,28 @@ export class CheckCache {
|
|
|
58
104
|
|
|
59
105
|
/**
|
|
60
106
|
* 同步版本(用于 readdirSync 等同步操作)
|
|
107
|
+
*
|
|
108
|
+
* @param sampling 计数采样配置(语义同 {@link get})
|
|
61
109
|
*/
|
|
62
110
|
getSync<T>(
|
|
63
111
|
namespace: string,
|
|
64
112
|
key: string,
|
|
65
113
|
fn: () => T,
|
|
114
|
+
sampling?: CheckSamplingConfig<T>,
|
|
66
115
|
): T {
|
|
67
116
|
const cacheKey = `${namespace}:${key}`;
|
|
68
117
|
const entry = this.store.get(cacheKey);
|
|
118
|
+
const rate = sampling ? Math.max(1, Math.floor(sampling.sampleRate)) : 1;
|
|
119
|
+
const sampleTurn = rate > 1 && this.isSampleTurn(cacheKey, rate);
|
|
69
120
|
|
|
70
|
-
if (entry && Date.now() < entry.expiresAt) {
|
|
121
|
+
if (!sampleTurn && entry && Date.now() < entry.expiresAt) {
|
|
71
122
|
return entry.value as T;
|
|
72
123
|
}
|
|
73
124
|
|
|
125
|
+
if (sampling && rate > 1 && !sampleTurn) {
|
|
126
|
+
return sampling.defaultValueOnMiss;
|
|
127
|
+
}
|
|
128
|
+
|
|
74
129
|
const value = fn();
|
|
75
130
|
this.store.set(cacheKey, {
|
|
76
131
|
value,
|
|
@@ -80,7 +135,8 @@ export class CheckCache {
|
|
|
80
135
|
}
|
|
81
136
|
|
|
82
137
|
/**
|
|
83
|
-
*
|
|
138
|
+
* 使指定命名空间缓存失效(同时重置该命名空间的采样计数器,
|
|
139
|
+
* 保证失效后的下一次调用恢复为采样轮/完整检查)
|
|
84
140
|
*/
|
|
85
141
|
invalidate(namespace?: string): void {
|
|
86
142
|
if (namespace) {
|
|
@@ -89,8 +145,24 @@ export class CheckCache {
|
|
|
89
145
|
this.store.delete(key);
|
|
90
146
|
}
|
|
91
147
|
}
|
|
148
|
+
for (const key of this.sampleCounters.keys()) {
|
|
149
|
+
if (key.startsWith(`${namespace}:`)) {
|
|
150
|
+
this.sampleCounters.delete(key);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
92
153
|
} else {
|
|
93
154
|
this.store.clear();
|
|
155
|
+
this.sampleCounters.clear();
|
|
94
156
|
}
|
|
95
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 计数采样判定:计数 +1 后返回是否采样轮(第 1 次必采样,每 N 次 1 次)。
|
|
161
|
+
* 仅在采样启用(rate > 1)时调用。
|
|
162
|
+
*/
|
|
163
|
+
private isSampleTurn(cacheKey: string, rate: number): boolean {
|
|
164
|
+
const count = (this.sampleCounters.get(cacheKey) ?? 0) + 1;
|
|
165
|
+
this.sampleCounters.set(cacheKey, count);
|
|
166
|
+
return count % rate === 1;
|
|
167
|
+
}
|
|
96
168
|
}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
ConstraintViolationError,
|
|
18
18
|
} from '../../types/constraint';
|
|
19
19
|
import type { ExecutionTrace } from '../../types/trace';
|
|
20
|
-
import { IRON_LAWS, GUIDELINES,
|
|
20
|
+
import { IRON_LAWS, GUIDELINES, PROMPTS } from './definitions';
|
|
21
21
|
import type { MergedConstraintsConfig } from '../../types/project-config';
|
|
22
22
|
import { normalizeTriggers } from '../../utils/exec';
|
|
23
23
|
import { runCommand } from '../../utils/exec';
|
|
@@ -74,9 +74,6 @@ const NOOP_TRACE_RECORDER: TraceRecorder = { record: () => undefined };
|
|
|
74
74
|
export class ConstraintChecker {
|
|
75
75
|
private static instance: ConstraintChecker;
|
|
76
76
|
|
|
77
|
-
/** 自定义约束配置(项目级) */
|
|
78
|
-
private customConfig: MergedConstraintsConfig | null = null;
|
|
79
|
-
|
|
80
77
|
/** 检查结果缓存(S7:src 扫描等重复 I/O,TTL=1s 防跨测试污染) */
|
|
81
78
|
private cache: CheckCache = new CheckCache({ ttlMs: 1000 });
|
|
82
79
|
|
|
@@ -139,31 +136,20 @@ export class ConstraintChecker {
|
|
|
139
136
|
this.traceRecorder = recorder;
|
|
140
137
|
}
|
|
141
138
|
|
|
142
|
-
/**
|
|
143
|
-
* 设置项目级约束配置
|
|
144
|
-
*
|
|
145
|
-
* @deprecated 修改单例状态,多项目并发时存在污染风险。
|
|
146
|
-
* 请改用 checkConstraints() / beforeExecution() 的 customConfig 参数实现 per-request 隔离。
|
|
147
|
-
*/
|
|
148
|
-
setCustomConfig(config: MergedConstraintsConfig): void {
|
|
149
|
-
this.customConfig = config;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
139
|
/**
|
|
153
140
|
* 获取当前的约束集合(内置 + 自定义)
|
|
154
141
|
*
|
|
155
|
-
* @param customConfig 可选,per-request
|
|
142
|
+
* @param customConfig 可选,per-request 自定义配置;不传则返回内置约束集
|
|
156
143
|
*/
|
|
157
144
|
getConstraints(customConfig?: MergedConstraintsConfig | null): {
|
|
158
145
|
ironLaws: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
159
146
|
guidelines: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
160
|
-
tips: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
161
147
|
prompts: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
162
148
|
} {
|
|
163
|
-
const config = customConfig
|
|
149
|
+
const config = customConfig;
|
|
164
150
|
const source = config
|
|
165
|
-
? { ironLaws: config.ironLaws, guidelines: config.guidelines,
|
|
166
|
-
: { ironLaws: IRON_LAWS, guidelines: GUIDELINES,
|
|
151
|
+
? { ironLaws: config.ironLaws, guidelines: config.guidelines, prompts: config.prompts ?? PROMPTS }
|
|
152
|
+
: { ironLaws: IRON_LAWS, guidelines: GUIDELINES, prompts: PROMPTS };
|
|
167
153
|
|
|
168
154
|
// Wire unified check() method on every constraint
|
|
169
155
|
const wire = (constraints: Record<string, Constraint>) => {
|
|
@@ -175,7 +161,6 @@ export class ConstraintChecker {
|
|
|
175
161
|
return {
|
|
176
162
|
ironLaws: wire(source.ironLaws),
|
|
177
163
|
guidelines: wire(source.guidelines),
|
|
178
|
-
tips: wire(source.tips),
|
|
179
164
|
prompts: wire(source.prompts),
|
|
180
165
|
};
|
|
181
166
|
}
|
|
@@ -260,7 +245,6 @@ export class ConstraintChecker {
|
|
|
260
245
|
case 'guideline':
|
|
261
246
|
return 'warning';
|
|
262
247
|
case 'prompt':
|
|
263
|
-
case 'tip':
|
|
264
248
|
return 'info';
|
|
265
249
|
default:
|
|
266
250
|
return 'warning';
|
|
@@ -350,7 +334,6 @@ export class ConstraintChecker {
|
|
|
350
334
|
): {
|
|
351
335
|
ironLaws: Constraint[];
|
|
352
336
|
guidelines: Constraint[];
|
|
353
|
-
tips: Constraint[];
|
|
354
337
|
prompts: Constraint[];
|
|
355
338
|
} {
|
|
356
339
|
const operations = [context.operation, ...(context.extraTriggers ?? [])];
|
|
@@ -368,17 +351,15 @@ export class ConstraintChecker {
|
|
|
368
351
|
return {
|
|
369
352
|
ironLaws: filterByTrigger(constraints.ironLaws),
|
|
370
353
|
guidelines: filterByTrigger(constraints.guidelines),
|
|
371
|
-
tips: filterByTrigger(constraints.tips),
|
|
372
354
|
prompts: filterByTrigger(constraints.prompts),
|
|
373
355
|
};
|
|
374
356
|
}
|
|
375
357
|
|
|
376
358
|
/**
|
|
377
|
-
*
|
|
359
|
+
* 执行约束检查
|
|
378
360
|
*
|
|
379
361
|
* - Iron Laws:检查失败立即抛出异常
|
|
380
362
|
* - Guidelines:检查失败记录警告
|
|
381
|
-
* - Tips:检查失败记录提示
|
|
382
363
|
*
|
|
383
364
|
* @param context 约束上下文
|
|
384
365
|
* @param customConfig 可选,per-request 自定义配置(避免多请求间的单例状态污染)
|
|
@@ -395,10 +376,8 @@ export class ConstraintChecker {
|
|
|
395
376
|
const result: ConstraintCheckResult = {
|
|
396
377
|
ironLaws: [],
|
|
397
378
|
guidelines: [],
|
|
398
|
-
tips: [],
|
|
399
379
|
passed: true,
|
|
400
380
|
warningCount: 0,
|
|
401
|
-
tipCount: 0,
|
|
402
381
|
};
|
|
403
382
|
|
|
404
383
|
const traceCollector = this.traceRecorder;
|
|
@@ -431,19 +410,6 @@ export class ConstraintChecker {
|
|
|
431
410
|
}
|
|
432
411
|
}
|
|
433
412
|
|
|
434
|
-
// 3. Tips: 仅记录
|
|
435
|
-
for (const constraint of Object.values(constraints.tips)) {
|
|
436
|
-
if (!this.matchesTrigger(constraint, context)) continue;
|
|
437
|
-
|
|
438
|
-
const checkResult = await this.check(constraint, context);
|
|
439
|
-
result.tips.push(checkResult);
|
|
440
|
-
this.recordTrace(traceCollector, constraint, checkResult, context);
|
|
441
|
-
|
|
442
|
-
if (!checkResult.satisfied) {
|
|
443
|
-
result.tipCount++;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
413
|
return result;
|
|
448
414
|
} finally {
|
|
449
415
|
this.runCache = null;
|
|
@@ -451,7 +417,7 @@ export class ConstraintChecker {
|
|
|
451
417
|
}
|
|
452
418
|
|
|
453
419
|
/**
|
|
454
|
-
* S11: 安全全量检查 —
|
|
420
|
+
* S11: 安全全量检查 — 全部约束检查,不抛异常
|
|
455
421
|
*
|
|
456
422
|
* 区别 checkConstraints(): Iron Law 违规收集在结果中而不是抛异常。
|
|
457
423
|
* 调用方通过 result.passed + result.warningCount 判断状态。
|
|
@@ -468,10 +434,8 @@ export class ConstraintChecker {
|
|
|
468
434
|
const result: ConstraintCheckResult = {
|
|
469
435
|
ironLaws: [],
|
|
470
436
|
guidelines: [],
|
|
471
|
-
tips: [],
|
|
472
437
|
passed: true,
|
|
473
438
|
warningCount: 0,
|
|
474
|
-
tipCount: 0,
|
|
475
439
|
};
|
|
476
440
|
|
|
477
441
|
const traceCollector = this.traceRecorder;
|
|
@@ -493,14 +457,6 @@ export class ConstraintChecker {
|
|
|
493
457
|
if (!checkResult.satisfied) result.warningCount++;
|
|
494
458
|
}
|
|
495
459
|
|
|
496
|
-
for (const constraint of Object.values(constraints.tips)) {
|
|
497
|
-
if (!this.matchesTrigger(constraint, context)) continue;
|
|
498
|
-
const checkResult = await this.check(constraint, context);
|
|
499
|
-
result.tips.push(checkResult);
|
|
500
|
-
this.recordTrace(traceCollector, constraint, checkResult, context);
|
|
501
|
-
if (!checkResult.satisfied) result.tipCount++;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
460
|
return result;
|
|
505
461
|
} finally {
|
|
506
462
|
this.runCache = null;
|
|
@@ -549,13 +505,12 @@ export async function checkConstraint(
|
|
|
549
505
|
const constraint =
|
|
550
506
|
constraints.ironLaws[constraintId] ||
|
|
551
507
|
constraints.guidelines[constraintId] ||
|
|
552
|
-
constraints.tips[constraintId] ||
|
|
553
508
|
constraints.prompts[constraintId];
|
|
554
509
|
|
|
555
510
|
if (!constraint) {
|
|
556
511
|
return {
|
|
557
512
|
id: constraintId,
|
|
558
|
-
level: '
|
|
513
|
+
level: 'guideline',
|
|
559
514
|
satisfied: false,
|
|
560
515
|
message: `未知的约束: ${constraintId}`,
|
|
561
516
|
checkedAt: new Date(),
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
* kind 二元模型(ADR-0001):
|
|
5
5
|
* - IRON_LAWS / GUIDELINES:check 层,必须带真实 checker(注册表闭环)
|
|
6
6
|
* - PROMPTS:prompt 层,纯文本注入,不占检查位
|
|
7
|
-
* - TIPS:已退役,导出空表仅为在途消费者(capabilities-syncer)编译兼容,
|
|
8
|
-
* 后续阶段随其改造一并删除
|
|
9
7
|
*
|
|
10
8
|
* 工单 20:字面量定义按层拆至 ./definitions/{iron-laws,guidelines,prompts}.ts;
|
|
11
9
|
* 本文件保持原路径做薄聚合(studio rule-scanner 按此路径解析,P0 #8)。
|
|
@@ -16,12 +14,6 @@ import { IRON_LAWS } from './definitions/iron-laws';
|
|
|
16
14
|
import { GUIDELINES } from './definitions/guidelines';
|
|
17
15
|
import { PROMPTS } from './definitions/prompts';
|
|
18
16
|
|
|
19
|
-
/**
|
|
20
|
-
* @deprecated TIPS 已退役(ADR-0001),恒为空表。
|
|
21
|
-
* 仅为在途 capabilities-syncer 编译兼容保留,后续阶段删除。
|
|
22
|
-
*/
|
|
23
|
-
export const TIPS: Record<string, Constraint> = {};
|
|
24
|
-
|
|
25
17
|
export { IRON_LAWS, GUIDELINES, PROMPTS };
|
|
26
18
|
|
|
27
19
|
// ========================================
|
|
@@ -44,7 +44,6 @@ interface RunContext {
|
|
|
44
44
|
constraintCounts?: {
|
|
45
45
|
ironLaws?: number;
|
|
46
46
|
guidelines?: number;
|
|
47
|
-
tips?: number;
|
|
48
47
|
};
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -58,7 +57,7 @@ export class FreshnessRunner {
|
|
|
58
57
|
runAll(
|
|
59
58
|
config: DocFreshnessConfig,
|
|
60
59
|
projectPath: string,
|
|
61
|
-
ctx?: { requiredDirs?: string[]; constraintCounts?: { ironLaws?: number; guidelines?: number
|
|
60
|
+
ctx?: { requiredDirs?: string[]; constraintCounts?: { ironLaws?: number; guidelines?: number } }
|
|
62
61
|
): FreshnessCheckResult[] {
|
|
63
62
|
if (config.enabled === false) return [];
|
|
64
63
|
if (!config.checks || config.checks.length === 0) return [];
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* 约束模块入口
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
// 约束定义(kind 二元:check 层 + prompt
|
|
5
|
+
// 约束定义(kind 二元:check 层 + prompt 层)
|
|
6
6
|
export {
|
|
7
7
|
IRON_LAWS,
|
|
8
8
|
GUIDELINES,
|
|
9
9
|
PROMPTS,
|
|
10
|
-
TIPS,
|
|
11
10
|
getAllConstraints,
|
|
12
11
|
findConstraintsByTrigger,
|
|
13
12
|
getConstraint,
|
|
@@ -29,6 +28,16 @@ export {
|
|
|
29
28
|
renderConstraintsSection,
|
|
30
29
|
} from './injection-renderer';
|
|
31
30
|
|
|
31
|
+
// 约束检查缓存(H6/G5:TTL 缓存 + 计数采样,公开导出)
|
|
32
|
+
export { CheckCache } from './check-cache';
|
|
33
|
+
export type { CheckCacheConfig, CheckSamplingConfig } from './check-cache';
|
|
34
|
+
|
|
35
|
+
// Agent prompt 约束段渲染(H6/G6:trigger 参数化分组渲染,role 路由留 studio)
|
|
36
|
+
export {
|
|
37
|
+
renderConstraintsByTrigger,
|
|
38
|
+
} from './agent-prompt-renderer';
|
|
39
|
+
export type { RenderConstraintsByTriggerOptions } from './agent-prompt-renderer';
|
|
40
|
+
|
|
32
41
|
// 类型导出
|
|
33
42
|
export type {
|
|
34
43
|
ConstraintId,
|
|
@@ -115,14 +115,13 @@ export class ConstraintInterceptor {
|
|
|
115
115
|
const allConstraints = [
|
|
116
116
|
...Object.values(constraints.ironLaws),
|
|
117
117
|
...Object.values(constraints.guidelines),
|
|
118
|
-
...Object.values(constraints.tips),
|
|
119
118
|
].filter(c => c.kind !== 'prompt');
|
|
120
119
|
|
|
121
120
|
const applicableConstraints = allConstraints.filter(
|
|
122
121
|
(c) => normalizeTriggers<ConstraintTrigger>(c.trigger).includes(trigger)
|
|
123
122
|
);
|
|
124
123
|
|
|
125
|
-
const order: Record<ConstraintLevel, number> = { iron_law: 0, guideline: 1, prompt: 2
|
|
124
|
+
const order: Record<ConstraintLevel, number> = { iron_law: 0, guideline: 1, prompt: 2 };
|
|
126
125
|
const ordered = applicableConstraints.sort((a, b) => order[a.level] - order[b.level]);
|
|
127
126
|
|
|
128
127
|
for (const constraint of ordered) {
|
|
@@ -200,10 +199,6 @@ export class ConstraintInterceptor {
|
|
|
200
199
|
|
|
201
200
|
export const constraintInterceptor = ConstraintInterceptor.getInstance();
|
|
202
201
|
|
|
203
|
-
// 注册内置 executors
|
|
204
|
-
import { registerDefaultExecutors } from './default-executors';
|
|
205
|
-
registerDefaultExecutors();
|
|
206
|
-
|
|
207
202
|
export async function interceptOperation(
|
|
208
203
|
trigger: ConstraintTrigger,
|
|
209
204
|
context: ConstraintContext
|
|
@@ -172,7 +172,6 @@ export class ProjectConfigLoader {
|
|
|
172
172
|
const result: MergedConstraintsConfig = {
|
|
173
173
|
ironLaws: presetMerged.ironLaws,
|
|
174
174
|
guidelines: presetMerged.guidelines,
|
|
175
|
-
tips: {},
|
|
176
175
|
prompts: presetMerged.prompts ?? {},
|
|
177
176
|
disabled: [...presetMerged.disabled],
|
|
178
177
|
custom: [],
|
|
@@ -196,7 +195,6 @@ export class ProjectConfigLoader {
|
|
|
196
195
|
// 从对应层级中移除
|
|
197
196
|
delete result.ironLaws[constraintId];
|
|
198
197
|
delete result.guidelines[constraintId];
|
|
199
|
-
delete result.tips[constraintId];
|
|
200
198
|
delete result.prompts![constraintId];
|
|
201
199
|
}
|
|
202
200
|
}
|
|
@@ -225,7 +223,6 @@ export class ProjectConfigLoader {
|
|
|
225
223
|
];
|
|
226
224
|
if (result.ironLaws[id]) result.ironLaws[id] = constraint;
|
|
227
225
|
if (result.guidelines[id]) result.guidelines[id] = constraint;
|
|
228
|
-
if (result.tips[id]) result.tips[id] = constraint;
|
|
229
226
|
if (result.prompts![id]) result.prompts![id] = constraint;
|
|
230
227
|
continue;
|
|
231
228
|
}
|
|
@@ -255,9 +252,6 @@ export class ProjectConfigLoader {
|
|
|
255
252
|
case 'prompt':
|
|
256
253
|
result.prompts![id] = constraint;
|
|
257
254
|
break;
|
|
258
|
-
case 'tip':
|
|
259
|
-
result.tips[id] = constraint;
|
|
260
|
-
break;
|
|
261
255
|
}
|
|
262
256
|
}
|
|
263
257
|
|
|
@@ -285,7 +279,7 @@ export class ProjectConfigLoader {
|
|
|
285
279
|
id: string,
|
|
286
280
|
merged: MergedConstraintsConfig
|
|
287
281
|
): Constraint | undefined {
|
|
288
|
-
return merged.ironLaws[id] || merged.guidelines[id] || merged.
|
|
282
|
+
return merged.ironLaws[id] || merged.guidelines[id] || merged.prompts?.[id];
|
|
289
283
|
}
|
|
290
284
|
|
|
291
285
|
/**
|
package/src/dashboard/stats.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 纯函数,从原始数据计算统计指标
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { KnowledgeEntry,
|
|
7
|
+
import type { KnowledgeEntry, KnowledgeSubsystem } from '../knowledge/types';
|
|
8
8
|
import type { KnowledgeOverview, KnowledgeFlow } from './types';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -36,7 +36,7 @@ export function computeKnowledgeOverview(entries: KnowledgeEntry[]): KnowledgeOv
|
|
|
36
36
|
* 按类型分组统计
|
|
37
37
|
*/
|
|
38
38
|
function computeByType(entries: KnowledgeEntry[]): KnowledgeOverview['byType'] {
|
|
39
|
-
const typeMap = new Map<
|
|
39
|
+
const typeMap = new Map<KnowledgeSubsystem, { total: number; proven: number; verified: number; draft: number }>();
|
|
40
40
|
|
|
41
41
|
for (const entry of entries) {
|
|
42
42
|
const existing = typeMap.get(entry.type) || { total: 0, proven: 0, verified: 0, draft: 0 };
|
package/src/dashboard/types.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* harness 提供数据,studio 负责渲染
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type {
|
|
7
|
+
import type { KnowledgeSubsystem } from '../knowledge/types';
|
|
8
8
|
|
|
9
9
|
// ── 约束分层(原 src/constraints/types,约束分层子系统删除后归位 dashboard)──
|
|
10
10
|
|
|
@@ -28,7 +28,7 @@ export interface ConstraintStats {
|
|
|
28
28
|
|
|
29
29
|
export interface KnowledgeOverview {
|
|
30
30
|
byType: Array<{
|
|
31
|
-
type:
|
|
31
|
+
type: KnowledgeSubsystem;
|
|
32
32
|
total: number;
|
|
33
33
|
proven: number;
|
|
34
34
|
verified: number;
|
package/src/gates/CONTEXT.md
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
# gates/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
质量门禁系统:6
|
|
4
|
+
质量门禁系统:6 种门禁检查(验收/命令/契约/性能/审查/安全)。统一 Gate 三态协议 + 注册表闭环 + 声明式生效集(G1)。
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
|
-
- `
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- `
|
|
12
|
-
-
|
|
13
|
-
- `
|
|
14
|
-
-
|
|
7
|
+
- 统一协议:`Gate{id, order, evaluate(ctx)}` → `GateDecision{status: deny|abstain|ask, result: GateResult}`(`types.ts`;`decisionFromResult` 报告→决策映射,浅冻结)
|
|
8
|
+
- 注册表:`gateRegistry`(`registry.ts`)——定义即注册 + 构建期双向闭环(定义无实现/实现无定义/重复 id → 加载期抛错);`getGate`(未注册抛错)/`listRegisteredGates`/`registeredGateCount`
|
|
9
|
+
- 定义表:`GATE_DEFINITIONS`(`definitions.ts`)——id/description/默认 order/CLI 元数据;bin/harness.js 注册表驱动生成 6 门禁命令(纯数据模块,禁 import 实现,保 --help 懒加载;CLI 实现引用为 module+export,per-command 懒加载,H5)
|
|
10
|
+
- 执行器:`runGates`(`runner.ts`)——按 order 升序;deny 单调(下游 abstain 不得改回 allow)+ ask 枚举预留 fail-closed = deny;决策浅冻结
|
|
11
|
+
- 生效集:`getEffectiveGates(projectRoot)`(`effective-gates.ts`)——对齐 getEffectiveConstraints 裁剪模式:config.yml `gates.order` 重排 + `gates.<id>.enabled:false` 移除;引用未注册 id 抛错
|
|
12
|
+
- checker-as-guard 接线点:`createCheckerGate(check)`(`checker-gate.ts`)——ConstraintCheck → Gate(studio #129 随动);false→deny,true/'skip'→abstain
|
|
13
|
+
- 门禁类:`ReviewGate` / `SecurityGate` / `PerformanceGate` / `ContractGate` / `SpecAcceptanceGate` / `CommandGate`(各自执行细节私有,保留 `check()`/`scan()` 报告方法)
|
|
14
|
+
- `types.ts` — GateResult(报告结构,保留)/ GateContext / GateDecision 等公共类型
|
|
15
|
+
- 便捷工厂函数:createReviewGate / createSecurityGate / createPerformanceGate / createContractGate / createSpecAcceptanceGate / createCommandGate
|
|
15
16
|
|
|
16
17
|
## 依赖关系
|
|
17
|
-
- 依赖 `src/core
|
|
18
|
+
- 依赖 `src/core/`(effective-gates 读 project-config-loader;checker-gate 适配 checkers 注册表类型)
|
|
18
19
|
- 依赖 `src/types/` 公共类型
|
|
19
20
|
|
|
20
21
|
## 约定
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
22
|
+
- 新门禁必须:① 在 `definitions.ts` 补 GateDefinition(含 CLI 元数据)② 在 `registry.ts` IMPLEMENTATIONS 注册实现(缺一 → 加载期抛错)③ 实现统一 Gate 接口(evaluate 产三态决策,报告走 GateResult)④ 配 CLI 命令(命令实现文件 + CLI 元数据的 module+export 引用,bin 由定义表驱动生成,不再手写块)+ 测试文件
|
|
23
|
+
- deny 单调是接口契约:决策浅冻结,下游不得改写上游决策
|
|
24
|
+
- ask 枚举预留:暂无实现,runGates fail-closed 按 deny 计
|
|
24
25
|
|
|
25
26
|
## 注意事项
|
|
26
27
|
- 门禁系统不包含业务逻辑,只提供检查能力
|
|
28
|
+
- 统一的是决策协议(id/order/三态),执行细节(gh pr view/正则黑名单/OpenAPI diff/benchmark)私有——不要把执行细节塞进 Gate 接口
|
|
29
|
+
- config.yml gates 段为新增面:引用未注册 id 直接抛错(无历史残留配置需兼容)
|
|
27
30
|
- CommandGate 为命令黑名单检查(SEC-006)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* checker-as-guard 接线点测试(G1 / studio #129 随动)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createCheckerGate } from '../checker-gate';
|
|
6
|
+
import { contextFlag, contextEvidenceFlag } from '../../core/constraints/checkers/types';
|
|
7
|
+
|
|
8
|
+
describe('createCheckerGate', () => {
|
|
9
|
+
const ctx = { projectId: 'p', projectPath: '/tmp/project' };
|
|
10
|
+
|
|
11
|
+
it('checker 通过(true)→ abstain', async () => {
|
|
12
|
+
const gate = createCheckerGate(contextFlag('flag-pass', () => true), 7);
|
|
13
|
+
expect(gate.id).toBe('flag-pass');
|
|
14
|
+
expect(gate.order).toBe(7);
|
|
15
|
+
|
|
16
|
+
const decision = await gate.evaluate(ctx);
|
|
17
|
+
expect(decision.status).toBe('abstain');
|
|
18
|
+
expect(decision.result.gate).toBe('flag-pass');
|
|
19
|
+
expect(decision.result.passed).toBe(true);
|
|
20
|
+
expect(decision.result.message).toContain('通过');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('checker 违规(false)→ deny', async () => {
|
|
24
|
+
const gate = createCheckerGate(contextFlag('flag-fail', () => false));
|
|
25
|
+
const decision = await gate.evaluate(ctx);
|
|
26
|
+
expect(decision.status).toBe('deny');
|
|
27
|
+
expect(decision.result.passed).toBe(false);
|
|
28
|
+
expect(decision.result.message).toContain('判定违规');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('证据未接线(skip)→ abstain(不阻断)', async () => {
|
|
32
|
+
const gate = createCheckerGate(
|
|
33
|
+
contextEvidenceFlag('flag-skip', () => undefined)
|
|
34
|
+
);
|
|
35
|
+
const decision = await gate.evaluate(ctx);
|
|
36
|
+
expect(decision.status).toBe('abstain');
|
|
37
|
+
expect(decision.result.message).toContain('跳过');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('决策浅冻结(单调语义契约)', async () => {
|
|
41
|
+
const gate = createCheckerGate(contextFlag('flag-freeze', () => true));
|
|
42
|
+
const decision = await gate.evaluate(ctx);
|
|
43
|
+
expect(Object.isFrozen(decision)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|