@dommaker/harness 0.16.9 → 0.17.1
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 +39 -0
- package/README.md +33 -32
- package/bin/harness.js +33 -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 +5 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +2 -10
- package/dist/cli/commands/sync-docs/capabilities-syncer.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 +7 -0
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +13 -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 +9 -2
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +55 -22
- 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 +29 -572
- 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 +17 -2
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +55 -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 +26 -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 +228 -333
- 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 +41 -4
- 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/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 +5 -7
- 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 +13 -0
- package/src/core/constraints/checker.ts +73 -28
- package/src/core/constraints/checkers/capability-sync.ts +72 -29
- 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 +29 -597
- 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 +59 -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 +30 -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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDE.md 约束注入段渲染(ADR-0001)
|
|
3
|
+
*
|
|
4
|
+
* 纯函数:输入生效约束集 + harness 版本,输出标记区间的期望文本。
|
|
5
|
+
* 与写文件分离——init 注入与 P6 注入漂移校验(期望 vs 实际)共用此渲染。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Constraint } from '../../types/constraint';
|
|
9
|
+
|
|
10
|
+
/** 约束注入段开始标记 */
|
|
11
|
+
export const CONSTRAINTS_START_MARKER = '<!-- HARNESS_CONSTRAINTS_START -->';
|
|
12
|
+
/** 约束注入段结束标记 */
|
|
13
|
+
export const CONSTRAINTS_END_MARKER = '<!-- HARNESS_CONSTRAINTS_END -->';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 渲染 CLAUDE.md 约束注入段(含 START/END 标记,不含 `## Governance Rules` 标题)
|
|
17
|
+
*
|
|
18
|
+
* 结构:### Iron Laws / ### Guidelines / ### Prompts(行为约束),
|
|
19
|
+
* 条目为 `- **id**: promptInjection`;无 promptInjection 的 check 条目
|
|
20
|
+
* (如 docs_freshness)不出现在注入段。返回文本以换行结尾。
|
|
21
|
+
*
|
|
22
|
+
* @param constraints 生效约束集(getEffectiveConstraints 的输出)
|
|
23
|
+
* @param version harness 版本号(写入 `<!-- version: x -->` 供漂移校验)
|
|
24
|
+
*/
|
|
25
|
+
export function renderConstraintsSection(constraints: Constraint[], version: string): string {
|
|
26
|
+
const lines: string[] = [CONSTRAINTS_START_MARKER, `<!-- version: ${version} -->`];
|
|
27
|
+
|
|
28
|
+
const renderGroup = (level: 'iron_law' | 'guideline' | 'prompt', heading: string, first: boolean) => {
|
|
29
|
+
const items = constraints.filter(c => c.level === level && c.promptInjection);
|
|
30
|
+
if (items.length === 0) return;
|
|
31
|
+
if (!first) lines.push('');
|
|
32
|
+
lines.push(heading);
|
|
33
|
+
for (const c of items) {
|
|
34
|
+
lines.push(`- **${c.id}**: ${c.promptInjection}`);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
renderGroup('iron_law', '### Iron Laws (违反将阻断)', true);
|
|
39
|
+
renderGroup('guideline', '### Guidelines (应遵循)', lines.length === 2);
|
|
40
|
+
renderGroup('prompt', '### Prompts (行为约束)', lines.length === 2);
|
|
41
|
+
|
|
42
|
+
lines.push(CONSTRAINTS_END_MARKER);
|
|
43
|
+
return lines.join('\n') + '\n';
|
|
44
|
+
}
|
|
@@ -111,17 +111,18 @@ export class ConstraintInterceptor {
|
|
|
111
111
|
};
|
|
112
112
|
|
|
113
113
|
const constraints = constraintChecker.getConstraints();
|
|
114
|
+
// prompt 类约束不参与拦截执行(ADR-0001:仅参与注入)
|
|
114
115
|
const allConstraints = [
|
|
115
116
|
...Object.values(constraints.ironLaws),
|
|
116
117
|
...Object.values(constraints.guidelines),
|
|
117
118
|
...Object.values(constraints.tips),
|
|
118
|
-
];
|
|
119
|
+
].filter(c => c.kind !== 'prompt');
|
|
119
120
|
|
|
120
121
|
const applicableConstraints = allConstraints.filter(
|
|
121
122
|
(c) => normalizeTriggers<ConstraintTrigger>(c.trigger).includes(trigger)
|
|
122
123
|
);
|
|
123
124
|
|
|
124
|
-
const order: Record<ConstraintLevel, number> = { iron_law: 0, guideline: 1,
|
|
125
|
+
const order: Record<ConstraintLevel, number> = { iron_law: 0, guideline: 1, prompt: 2, tip: 3 };
|
|
125
126
|
const ordered = applicableConstraints.sort((a, b) => order[a.level] - order[b.level]);
|
|
126
127
|
|
|
127
128
|
for (const constraint of ordered) {
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 约束使用统计与退役候选诊断(ADR-0001 决策 2/3)
|
|
3
|
+
*
|
|
4
|
+
* `harness constraints report` 与 `harness constraints retire`(交互模式)共用的
|
|
5
|
+
* 数据层:读取项目 traces.log,与生效集(getEffectiveConstraints)对齐,
|
|
6
|
+
* 产出 check 层统计表与四类退役候选诊断。
|
|
7
|
+
*
|
|
8
|
+
* 只读:不创建目录、不写任何文件。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import type { Constraint } from '../../types/constraint';
|
|
14
|
+
import type { ExecutionTrace } from '../../types/trace';
|
|
15
|
+
import { DEFAULT_TRACE_FILE } from '../../types/trace';
|
|
16
|
+
import { getEffectiveConstraints, lintEffectiveConfig } from '../effective-constraints';
|
|
17
|
+
import type { EffectiveConfigLint } from '../effective-constraints';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* flag 型 check 约束(证据来自 ConstraintContext flag,见 checkers/iron-flags.ts)。
|
|
21
|
+
* 全部 skip 时标注"证据 flag 未接线"(区别于存在性探测的"约定未采用")。
|
|
22
|
+
*/
|
|
23
|
+
export const FLAG_EVIDENCE_CONSTRAINT_IDS: ReadonlySet<string> = new Set([
|
|
24
|
+
'no_completion_without_verification',
|
|
25
|
+
'incremental_progress',
|
|
26
|
+
'no_implementation_without_requirement',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 单条 check 约束的使用统计
|
|
31
|
+
*/
|
|
32
|
+
export interface ConstraintUsageStats {
|
|
33
|
+
id: string;
|
|
34
|
+
level: Constraint['level'];
|
|
35
|
+
|
|
36
|
+
/** trace 总行数(含 skip/bypassed) */
|
|
37
|
+
total: number;
|
|
38
|
+
pass: number;
|
|
39
|
+
fail: number;
|
|
40
|
+
skip: number;
|
|
41
|
+
bypassed: number;
|
|
42
|
+
|
|
43
|
+
/** 实际评估次数(total - skip;skip 未评估不计入,与 TraceAnalyzer 一致) */
|
|
44
|
+
evaluated: number;
|
|
45
|
+
|
|
46
|
+
/** 失败率(fail / evaluated;evaluated=0 时为 0) */
|
|
47
|
+
failRate: number;
|
|
48
|
+
|
|
49
|
+
/** 首次/最近触发时间(Unix ms);无 trace 时为 undefined */
|
|
50
|
+
firstAt?: number;
|
|
51
|
+
lastAt?: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 退役候选类别
|
|
56
|
+
*/
|
|
57
|
+
export type RetireCandidateKind =
|
|
58
|
+
| 'zero_trigger' // 零触发:生效集中从未出现在 trace
|
|
59
|
+
| 'unevaluable' // 不可评估:全部 skip
|
|
60
|
+
| 'high_noise' // 高噪:fail 率异常高,疑似误报源
|
|
61
|
+
| 'zero_intercept'; // 零拦截:样本充足但从未 fail
|
|
62
|
+
|
|
63
|
+
export interface RetireCandidate {
|
|
64
|
+
id: string;
|
|
65
|
+
kind: RetireCandidateKind;
|
|
66
|
+
stats: ConstraintUsageStats;
|
|
67
|
+
/** 人类可读的诊断说明(带证据数字) */
|
|
68
|
+
reason: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 候选诊断阈值(CLI 可覆盖)
|
|
73
|
+
*/
|
|
74
|
+
export interface DiagnoseThresholds {
|
|
75
|
+
/** 零拦截候选的最小评估样本数,默认 50 */
|
|
76
|
+
zeroInterceptMinEvaluated: number;
|
|
77
|
+
/** 高噪候选的 fail 率阈值(严格大于),默认 0.8 */
|
|
78
|
+
highNoiseFailRate: number;
|
|
79
|
+
/** 高噪候选的最小评估样本数,默认 20 */
|
|
80
|
+
highNoiseMinEvaluated: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const DEFAULT_DIAGNOSE_THRESHOLDS: DiagnoseThresholds = {
|
|
84
|
+
zeroInterceptMinEvaluated: 50,
|
|
85
|
+
highNoiseFailRate: 0.8,
|
|
86
|
+
highNoiseMinEvaluated: 20,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* report 数据模型
|
|
91
|
+
*/
|
|
92
|
+
export interface ConstraintsUsageReport {
|
|
93
|
+
/** check 层统计表(生效集内每条 check 约束一行,零出现 total=0) */
|
|
94
|
+
stats: ConstraintUsageStats[];
|
|
95
|
+
/** 退役候选诊断(按 zero_trigger → unevaluable → high_noise → zero_intercept 排序) */
|
|
96
|
+
candidates: RetireCandidate[];
|
|
97
|
+
/** 当前生效的 prompt 条目 id(注入清单) */
|
|
98
|
+
activePromptIds: string[];
|
|
99
|
+
/** 配置健康诊断(unknownIds 等) */
|
|
100
|
+
lint: EffectiveConfigLint;
|
|
101
|
+
/** trace 文件是否存在 */
|
|
102
|
+
traceFileExists: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 读取项目 traces.log(只读,容错:坏行跳过)
|
|
107
|
+
*/
|
|
108
|
+
export function readProjectTraces(projectRoot: string): ExecutionTrace[] {
|
|
109
|
+
const tracePath = path.join(projectRoot, DEFAULT_TRACE_FILE);
|
|
110
|
+
if (!fs.existsSync(tracePath)) return [];
|
|
111
|
+
|
|
112
|
+
const content = fs.readFileSync(tracePath, 'utf-8');
|
|
113
|
+
const traces: ExecutionTrace[] = [];
|
|
114
|
+
for (const line of content.split('\n')) {
|
|
115
|
+
const trimmed = line.trim();
|
|
116
|
+
if (!trimmed) continue;
|
|
117
|
+
try {
|
|
118
|
+
traces.push(JSON.parse(trimmed) as ExecutionTrace);
|
|
119
|
+
} catch {
|
|
120
|
+
// 坏行跳过(report 只读,不因单行损坏失败)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return traces;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 按约束聚合 trace 统计(Map: constraintId → 计数)
|
|
128
|
+
*/
|
|
129
|
+
export function collectUsageByConstraint(
|
|
130
|
+
traces: ExecutionTrace[]
|
|
131
|
+
): Map<string, Omit<ConstraintUsageStats, 'id' | 'level' | 'evaluated' | 'failRate'>> {
|
|
132
|
+
const map = new Map<string, { total: number; pass: number; fail: number; skip: number; bypassed: number; firstAt?: number; lastAt?: number }>();
|
|
133
|
+
for (const t of traces) {
|
|
134
|
+
let agg = map.get(t.constraintId);
|
|
135
|
+
if (!agg) {
|
|
136
|
+
agg = { total: 0, pass: 0, fail: 0, skip: 0, bypassed: 0 };
|
|
137
|
+
map.set(t.constraintId, agg);
|
|
138
|
+
}
|
|
139
|
+
agg.total++;
|
|
140
|
+
if (t.result === 'pass') agg.pass++;
|
|
141
|
+
else if (t.result === 'fail') agg.fail++;
|
|
142
|
+
else if (t.result === 'skip') agg.skip++;
|
|
143
|
+
else if (t.result === 'bypassed') agg.bypassed++;
|
|
144
|
+
if (agg.firstAt === undefined || t.timestamp < agg.firstAt) agg.firstAt = t.timestamp;
|
|
145
|
+
if (agg.lastAt === undefined || t.timestamp > agg.lastAt) agg.lastAt = t.timestamp;
|
|
146
|
+
}
|
|
147
|
+
return map;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function toStats(id: string, level: Constraint['level'], agg: { total: number; pass: number; fail: number; skip: number; bypassed: number; firstAt?: number; lastAt?: number } | undefined): ConstraintUsageStats {
|
|
151
|
+
const a = agg ?? { total: 0, pass: 0, fail: 0, skip: 0, bypassed: 0 };
|
|
152
|
+
const evaluated = a.total - a.skip;
|
|
153
|
+
return {
|
|
154
|
+
id,
|
|
155
|
+
level,
|
|
156
|
+
total: a.total,
|
|
157
|
+
pass: a.pass,
|
|
158
|
+
fail: a.fail,
|
|
159
|
+
skip: a.skip,
|
|
160
|
+
bypassed: a.bypassed,
|
|
161
|
+
evaluated,
|
|
162
|
+
failRate: evaluated > 0 ? a.fail / evaluated : 0,
|
|
163
|
+
firstAt: a.firstAt,
|
|
164
|
+
lastAt: a.lastAt,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 退役候选诊断(纯函数)
|
|
170
|
+
*
|
|
171
|
+
* 每条 check 约束最多归入一个类别,优先级:
|
|
172
|
+
* zero_trigger → unevaluable → high_noise → zero_intercept
|
|
173
|
+
*/
|
|
174
|
+
export function diagnoseRetireCandidates(
|
|
175
|
+
stats: ConstraintUsageStats[],
|
|
176
|
+
thresholds: Partial<DiagnoseThresholds> = {}
|
|
177
|
+
): RetireCandidate[] {
|
|
178
|
+
const t = { ...DEFAULT_DIAGNOSE_THRESHOLDS, ...thresholds };
|
|
179
|
+
const candidates: RetireCandidate[] = [];
|
|
180
|
+
|
|
181
|
+
for (const s of stats) {
|
|
182
|
+
if (s.total === 0) {
|
|
183
|
+
candidates.push({
|
|
184
|
+
id: s.id,
|
|
185
|
+
kind: 'zero_trigger',
|
|
186
|
+
stats: s,
|
|
187
|
+
reason: '零触发:生效集内从未出现在 trace',
|
|
188
|
+
});
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (s.skip === s.total) {
|
|
193
|
+
const wiring = FLAG_EVIDENCE_CONSTRAINT_IDS.has(s.id)
|
|
194
|
+
? '证据 flag 未接线'
|
|
195
|
+
: '约定未采用(存在性探测未命中)';
|
|
196
|
+
candidates.push({
|
|
197
|
+
id: s.id,
|
|
198
|
+
kind: 'unevaluable',
|
|
199
|
+
stats: s,
|
|
200
|
+
reason: `不可评估:全部 ${s.total} 次均为 skip(${wiring})`,
|
|
201
|
+
});
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (s.failRate > t.highNoiseFailRate && s.evaluated >= t.highNoiseMinEvaluated) {
|
|
206
|
+
candidates.push({
|
|
207
|
+
id: s.id,
|
|
208
|
+
kind: 'high_noise',
|
|
209
|
+
stats: s,
|
|
210
|
+
reason: `高噪:fail 率 ${Math.round(s.failRate * 100)}%(${s.fail}/${s.evaluated}),疑似误报源`,
|
|
211
|
+
});
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (s.fail === 0 && s.evaluated >= t.zeroInterceptMinEvaluated) {
|
|
216
|
+
candidates.push({
|
|
217
|
+
id: s.id,
|
|
218
|
+
kind: 'zero_intercept',
|
|
219
|
+
stats: s,
|
|
220
|
+
reason: `零拦截:${s.evaluated} 次评估从未 fail`,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return candidates;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 构建 report 数据模型(只读)
|
|
230
|
+
*/
|
|
231
|
+
export function buildConstraintsUsageReport(
|
|
232
|
+
projectRoot: string = process.cwd(),
|
|
233
|
+
thresholds: Partial<DiagnoseThresholds> = {}
|
|
234
|
+
): ConstraintsUsageReport {
|
|
235
|
+
const effective = getEffectiveConstraints(projectRoot);
|
|
236
|
+
const checkConstraints = effective.filter(c => c.kind === 'check');
|
|
237
|
+
const activePromptIds = effective.filter(c => c.kind === 'prompt').map(c => c.id);
|
|
238
|
+
|
|
239
|
+
const tracePath = path.join(projectRoot, DEFAULT_TRACE_FILE);
|
|
240
|
+
const traces = readProjectTraces(projectRoot);
|
|
241
|
+
const usage = collectUsageByConstraint(traces);
|
|
242
|
+
|
|
243
|
+
const stats = checkConstraints.map(c => toStats(c.id, c.level, usage.get(c.id)));
|
|
244
|
+
const candidates = diagnoseRetireCandidates(stats, thresholds);
|
|
245
|
+
const lint = lintEffectiveConfig(projectRoot);
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
stats,
|
|
249
|
+
candidates,
|
|
250
|
+
activePromptIds,
|
|
251
|
+
lint,
|
|
252
|
+
traceFileExists: fs.existsSync(tracePath),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生效约束集(ADR-0001)
|
|
3
|
+
*
|
|
4
|
+
* `getEffectiveConstraints(projectRoot)` 是全仓唯一的生效集来源:
|
|
5
|
+
* 内置 → preset → config.yml 禁用 → custom 追加 → scenes 过滤。
|
|
6
|
+
* init 注入、`harness check`、外部消费者(studio 等)全部消费它,
|
|
7
|
+
* 不再直接读 IRON_LAWS/GUIDELINES/PROMPTS 全集。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Constraint } from '../types/constraint';
|
|
11
|
+
import { PROMPTS } from './constraints/definitions';
|
|
12
|
+
import { ProjectConfigLoader } from './project-config-loader';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 获取项目当前生效的约束集(check + prompt,带 kind)
|
|
16
|
+
*
|
|
17
|
+
* 内部完成完整合并:内置 → preset 裁剪 → config.yml `constraints.<id>.enabled:false`
|
|
18
|
+
* 删除 → custom-constraints 追加/extend_exceptions → scenes 过滤
|
|
19
|
+
* (带 appliesTo 的 prompt 仅当 config.yml `scenes` 与其交集非空时保留,
|
|
20
|
+
* 缺省 scenes=[] 即场景专属 prompt 默认不进入生效集)。
|
|
21
|
+
*
|
|
22
|
+
* @param projectRoot 项目根路径(缺省 process.cwd())
|
|
23
|
+
*/
|
|
24
|
+
export function getEffectiveConstraints(projectRoot: string = process.cwd()): Constraint[] {
|
|
25
|
+
const loader = new ProjectConfigLoader(projectRoot);
|
|
26
|
+
loader.load();
|
|
27
|
+
const merged = loader.mergeConstraints();
|
|
28
|
+
return [
|
|
29
|
+
...Object.values(merged.ironLaws),
|
|
30
|
+
...Object.values(merged.guidelines),
|
|
31
|
+
...Object.values(merged.tips),
|
|
32
|
+
...Object.values(merged.prompts ?? {}),
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 生效集配置诊断结果
|
|
38
|
+
*/
|
|
39
|
+
export interface EffectiveConfigLint {
|
|
40
|
+
/**
|
|
41
|
+
* config.yml `constraints.<id>` 中既非内置也非自定义的未知 id
|
|
42
|
+
* (如禁用了已被本版移除的约束的残留配置)。生效集计算静默忽略,
|
|
43
|
+
* 在此列出供 report 提示。
|
|
44
|
+
*/
|
|
45
|
+
unknownIds: string[];
|
|
46
|
+
|
|
47
|
+
/** 项目配置的场景标签(config.yml `scenes`,缺省 []) */
|
|
48
|
+
scenes: string[];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 因 scenes 过滤而未进入生效集的场景专属 prompt id
|
|
52
|
+
* (已被 preset/config 禁用的不重复列出)
|
|
53
|
+
*/
|
|
54
|
+
sceneExcluded: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 诊断项目约束配置:未知 id 残留、scenes 生效情况
|
|
59
|
+
*
|
|
60
|
+
* 不抛错、不修改任何文件,供 report / check 的诊断输出使用。
|
|
61
|
+
*
|
|
62
|
+
* @param projectRoot 项目根路径(缺省 process.cwd())
|
|
63
|
+
*/
|
|
64
|
+
export function lintEffectiveConfig(projectRoot: string = process.cwd()): EffectiveConfigLint {
|
|
65
|
+
const loader = new ProjectConfigLoader(projectRoot);
|
|
66
|
+
const config = loader.load();
|
|
67
|
+
const merged = loader.mergeConstraints();
|
|
68
|
+
const scenes = config.scenes ?? [];
|
|
69
|
+
|
|
70
|
+
const sceneExcluded = Object.values(PROMPTS)
|
|
71
|
+
.filter(
|
|
72
|
+
c =>
|
|
73
|
+
c.appliesTo &&
|
|
74
|
+
c.appliesTo.length > 0 &&
|
|
75
|
+
!c.appliesTo.some(scene => scenes.includes(scene)) &&
|
|
76
|
+
!merged.disabled.includes(c.id)
|
|
77
|
+
)
|
|
78
|
+
.map(c => c.id);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
unknownIds: merged.unknownIds ?? [],
|
|
82
|
+
scenes,
|
|
83
|
+
sceneExcluded,
|
|
84
|
+
};
|
|
85
|
+
}
|
package/src/core/index.ts
CHANGED
|
@@ -15,7 +15,8 @@ import type {
|
|
|
15
15
|
MergedConstraintsConfig,
|
|
16
16
|
CapabilitiesConfig,
|
|
17
17
|
} from '../types/project-config';
|
|
18
|
-
import { IRON_LAWS, GUIDELINES,
|
|
18
|
+
import { IRON_LAWS, GUIDELINES, PROMPTS } from './constraints/definitions';
|
|
19
|
+
import { applyPreset } from '../presets';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* 默认配置
|
|
@@ -152,26 +153,50 @@ export class ProjectConfigLoader {
|
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
/**
|
|
155
|
-
*
|
|
156
|
+
* 合并内置约束和自定义约束(ADR-0001 生效集完整链路)
|
|
157
|
+
*
|
|
158
|
+
* 合并顺序:内置 → preset 裁剪 → config.yml `constraints.<id>.enabled:false`
|
|
159
|
+
* 删除 → custom-constraints 追加/extend_exceptions → scenes 过滤
|
|
160
|
+
* (带 appliesTo 的 prompt 仅当 scenes 交集非空时保留)。
|
|
161
|
+
*
|
|
162
|
+
* config.yml 中未知约束 id(如已移除约束的禁用残留)静默忽略,
|
|
163
|
+
* 记录在结果 unknownIds 中供诊断。
|
|
164
|
+
*
|
|
165
|
+
* @param options.preset 覆盖 config.yml 的 preset(CLI --preset 用)
|
|
156
166
|
*/
|
|
157
|
-
mergeConstraints(): MergedConstraintsConfig {
|
|
167
|
+
mergeConstraints(options?: { preset?: string }): MergedConstraintsConfig {
|
|
168
|
+
// 0. preset 裁剪(未知预设名由 applyPreset 回落 standard)
|
|
169
|
+
const presetMerged = applyPreset(options?.preset ?? this.config.preset ?? 'standard');
|
|
170
|
+
const unknownIds: string[] = [];
|
|
158
171
|
const result: MergedConstraintsConfig = {
|
|
159
|
-
ironLaws:
|
|
160
|
-
guidelines:
|
|
161
|
-
tips: {
|
|
162
|
-
|
|
172
|
+
ironLaws: presetMerged.ironLaws,
|
|
173
|
+
guidelines: presetMerged.guidelines,
|
|
174
|
+
tips: {},
|
|
175
|
+
prompts: presetMerged.prompts ?? {},
|
|
176
|
+
disabled: [...presetMerged.disabled],
|
|
163
177
|
custom: [],
|
|
178
|
+
unknownIds,
|
|
164
179
|
};
|
|
165
180
|
|
|
166
181
|
// 1. 处理启用/禁用配置
|
|
167
182
|
if (this.config.constraints) {
|
|
168
183
|
for (const [constraintId, config] of Object.entries(this.config.constraints)) {
|
|
184
|
+
const isKnown =
|
|
185
|
+
!!IRON_LAWS[constraintId] ||
|
|
186
|
+
!!GUIDELINES[constraintId] ||
|
|
187
|
+
!!PROMPTS[constraintId] ||
|
|
188
|
+
!!this.customConstraints[constraintId];
|
|
189
|
+
if (!isKnown) {
|
|
190
|
+
// 未知 id(如已移除约束的禁用残留):不报错,记录供诊断
|
|
191
|
+
unknownIds.push(constraintId);
|
|
192
|
+
}
|
|
169
193
|
if (config.enabled === false) {
|
|
170
194
|
result.disabled.push(constraintId);
|
|
171
195
|
// 从对应层级中移除
|
|
172
196
|
delete result.ironLaws[constraintId];
|
|
173
197
|
delete result.guidelines[constraintId];
|
|
174
198
|
delete result.tips[constraintId];
|
|
199
|
+
delete result.prompts![constraintId];
|
|
175
200
|
}
|
|
176
201
|
}
|
|
177
202
|
}
|
|
@@ -195,6 +220,7 @@ export class ProjectConfigLoader {
|
|
|
195
220
|
if (result.ironLaws[id]) result.ironLaws[id] = constraint;
|
|
196
221
|
if (result.guidelines[id]) result.guidelines[id] = constraint;
|
|
197
222
|
if (result.tips[id]) result.tips[id] = constraint;
|
|
223
|
+
if (result.prompts![id]) result.prompts![id] = constraint;
|
|
198
224
|
continue;
|
|
199
225
|
}
|
|
200
226
|
}
|
|
@@ -220,12 +246,29 @@ export class ProjectConfigLoader {
|
|
|
220
246
|
case 'guideline':
|
|
221
247
|
result.guidelines[id] = constraint;
|
|
222
248
|
break;
|
|
249
|
+
case 'prompt':
|
|
250
|
+
result.prompts![id] = constraint;
|
|
251
|
+
break;
|
|
223
252
|
case 'tip':
|
|
224
253
|
result.tips[id] = constraint;
|
|
225
254
|
break;
|
|
226
255
|
}
|
|
227
256
|
}
|
|
228
257
|
|
|
258
|
+
// 3. scenes 过滤(ADR-0001):带 appliesTo 标签的 prompt 仅当项目 scenes
|
|
259
|
+
// 与其交集非空时保留;无 appliesTo 的条目不受影响。缺省 scenes=[] 即
|
|
260
|
+
// 场景专属 prompt 默认不进入生效集。
|
|
261
|
+
const scenes = this.config.scenes ?? [];
|
|
262
|
+
for (const [id, constraint] of Object.entries(result.prompts!)) {
|
|
263
|
+
if (
|
|
264
|
+
constraint.appliesTo &&
|
|
265
|
+
constraint.appliesTo.length > 0 &&
|
|
266
|
+
!constraint.appliesTo.some(scene => scenes.includes(scene))
|
|
267
|
+
) {
|
|
268
|
+
delete result.prompts![id];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
229
272
|
return result;
|
|
230
273
|
}
|
|
231
274
|
|
|
@@ -236,21 +279,28 @@ export class ProjectConfigLoader {
|
|
|
236
279
|
id: string,
|
|
237
280
|
merged: MergedConstraintsConfig
|
|
238
281
|
): Constraint | undefined {
|
|
239
|
-
return merged.ironLaws[id] || merged.guidelines[id] || merged.tips[id];
|
|
282
|
+
return merged.ironLaws[id] || merged.guidelines[id] || merged.tips[id] || merged.prompts?.[id];
|
|
240
283
|
}
|
|
241
284
|
|
|
242
285
|
/**
|
|
243
286
|
* 将自定义约束定义转换为 Constraint
|
|
287
|
+
*
|
|
288
|
+
* kind 推导(ADR-0001):自定义约束没有注册表中的 checker,统一归为
|
|
289
|
+
* kind='prompt'(不执行 checker,check() 短路通过),level 仅决定其所在
|
|
290
|
+
* 桶与严重性行为,保持与历史"未注册默认通过"相同的执行语义。
|
|
244
291
|
*/
|
|
245
292
|
private toConstraint(def: CustomConstraintDefinition, defaultId: string): Constraint {
|
|
293
|
+
const level = def.level || 'guideline';
|
|
246
294
|
return {
|
|
247
295
|
id: def.id || defaultId,
|
|
248
|
-
|
|
296
|
+
kind: 'prompt',
|
|
297
|
+
level,
|
|
249
298
|
rule: def.rule || '',
|
|
250
299
|
message: def.message || '',
|
|
251
300
|
trigger: (def.trigger || 'manual') as ConstraintTrigger | ConstraintTrigger[],
|
|
252
301
|
exceptions: def.exceptions,
|
|
253
302
|
description: def.description,
|
|
303
|
+
promptInjection: def.promptInjection,
|
|
254
304
|
enabled: def.enabled !== false,
|
|
255
305
|
enforcement: 'custom',
|
|
256
306
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { DashboardDataProvider } from '../data';
|
|
6
6
|
import type { KnowledgeEntry } from '../../knowledge/types';
|
|
7
|
-
import type { ConstraintStats } from '
|
|
7
|
+
import type { ConstraintStats } from '../types';
|
|
8
8
|
|
|
9
9
|
function makeEntry(overrides: Partial<KnowledgeEntry> = {}): KnowledgeEntry {
|
|
10
10
|
return {
|
|
@@ -50,7 +50,7 @@ describe('DashboardDataProvider', () => {
|
|
|
50
50
|
|
|
51
51
|
it('应该接受约束统计', () => {
|
|
52
52
|
const stats: ConstraintStats[] = [{
|
|
53
|
-
constraintId: '
|
|
53
|
+
constraintId: 'no_bypass_checkpoint',
|
|
54
54
|
triggerCount: 100,
|
|
55
55
|
passCount: 78,
|
|
56
56
|
interceptCount: 22,
|
|
@@ -60,7 +60,7 @@ describe('DashboardDataProvider', () => {
|
|
|
60
60
|
|
|
61
61
|
const data = provider.generate([], stats);
|
|
62
62
|
const heatmap = data.constraintHeatmap;
|
|
63
|
-
const entry = heatmap.constraints.find(c => c.id === '
|
|
63
|
+
const entry = heatmap.constraints.find(c => c.id === 'no_bypass_checkpoint');
|
|
64
64
|
expect(entry?.trigger).toBe(100);
|
|
65
65
|
expect(entry?.intercept).toBe(22);
|
|
66
66
|
});
|
|
@@ -87,7 +87,7 @@ describe('DashboardDataProvider', () => {
|
|
|
87
87
|
|
|
88
88
|
it('应该检测拦截率下降的约束', () => {
|
|
89
89
|
const stats: ConstraintStats[] = [{
|
|
90
|
-
constraintId: '
|
|
90
|
+
constraintId: 'no_bypass_checkpoint',
|
|
91
91
|
triggerCount: 100,
|
|
92
92
|
passCount: 90,
|
|
93
93
|
interceptCount: 10,
|
|
@@ -95,7 +95,7 @@ describe('DashboardDataProvider', () => {
|
|
|
95
95
|
}];
|
|
96
96
|
|
|
97
97
|
const heatmap = provider.getConstraintHeatmap(stats);
|
|
98
|
-
expect(heatmap.decliningInterceptRate.some(c => c.id === '
|
|
98
|
+
expect(heatmap.decliningInterceptRate.some(c => c.id === 'no_bypass_checkpoint')).toBe(true);
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
101
|
|
|
@@ -108,11 +108,4 @@ describe('DashboardDataProvider', () => {
|
|
|
108
108
|
expect(loop.feedbackToKnowledge).toBeDefined();
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
|
-
|
|
112
|
-
describe('getRegistry', () => {
|
|
113
|
-
it('应该返回约束注册表', () => {
|
|
114
|
-
expect(provider.getRegistry()).toBeDefined();
|
|
115
|
-
expect(provider.getRegistry().getAll().length).toBeGreaterThan(0);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
111
|
});
|
package/src/dashboard/data.ts
CHANGED
|
@@ -11,19 +11,14 @@ import type {
|
|
|
11
11
|
ConstraintHeatmap,
|
|
12
12
|
KnowledgeFlow,
|
|
13
13
|
FeedbackLoopStatus,
|
|
14
|
+
ConstraintStats,
|
|
15
|
+
ConstraintLayer,
|
|
14
16
|
} from './types';
|
|
15
17
|
import type { KnowledgeEntry } from '../knowledge/types';
|
|
16
|
-
import type { ConstraintStats } from '../constraints/types';
|
|
17
18
|
import { computeKnowledgeOverview, computeKnowledgeFlow, computeInterceptRate } from './stats';
|
|
18
|
-
import {
|
|
19
|
+
import { getAllConstraints } from '../core/constraints/definitions';
|
|
19
20
|
|
|
20
21
|
export class DashboardDataProvider {
|
|
21
|
-
private registry: ConstraintRegistry;
|
|
22
|
-
|
|
23
|
-
constructor(registry?: ConstraintRegistry) {
|
|
24
|
-
this.registry = registry ?? new ConstraintRegistry();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
22
|
/**
|
|
28
23
|
* 生成完整 Dashboard 数据
|
|
29
24
|
*/
|
|
@@ -52,19 +47,21 @@ export class DashboardDataProvider {
|
|
|
52
47
|
* 约束执行热力图
|
|
53
48
|
*/
|
|
54
49
|
getConstraintHeatmap(stats: ConstraintStats[]): ConstraintHeatmap {
|
|
55
|
-
|
|
50
|
+
// P1:直接读 definitions;prompt 类不产生 trace 统计,热力图只含 check 层
|
|
51
|
+
const allConstraints = getAllConstraints().filter(c => c.kind === 'check');
|
|
56
52
|
|
|
57
53
|
const constraints = allConstraints.map(c => {
|
|
58
54
|
const stat = stats.find(s => s.constraintId === c.id);
|
|
55
|
+
const layer: ConstraintLayer = c.level === 'iron_law' ? 'safety' : 'quality';
|
|
59
56
|
return {
|
|
60
57
|
id: c.id,
|
|
61
|
-
layer
|
|
58
|
+
layer,
|
|
62
59
|
trigger: stat?.triggerCount ?? 0,
|
|
63
60
|
pass: stat?.passCount ?? 0,
|
|
64
61
|
intercept: stat?.interceptCount ?? 0,
|
|
65
62
|
interceptRate: stat ? computeInterceptRate(stat.triggerCount, stat.interceptCount) : 0,
|
|
66
|
-
deprecationStatus:
|
|
67
|
-
permanent:
|
|
63
|
+
deprecationStatus: 'active' as const,
|
|
64
|
+
permanent: false,
|
|
68
65
|
};
|
|
69
66
|
});
|
|
70
67
|
|
|
@@ -103,11 +100,4 @@ export class DashboardDataProvider {
|
|
|
103
100
|
feedbackToKnowledge: { total: 0, pending: 0 },
|
|
104
101
|
};
|
|
105
102
|
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 获取约束注册表
|
|
109
|
-
*/
|
|
110
|
-
getRegistry(): ConstraintRegistry {
|
|
111
|
-
return this.registry;
|
|
112
|
-
}
|
|
113
103
|
}
|