@dommaker/harness 1.9.0 → 1.10.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 +6 -0
- package/README.md +9 -12
- package/dist/cli/commands/check.d.ts +3 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +41 -78
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -10
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +4 -57
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +9 -23
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +39 -121
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +3 -8
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +8 -23
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/definitions.js +2 -2
- package/dist/cli/commands/init.d.ts +0 -32
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -149
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +14 -17
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +0 -2
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
- package/dist/cli/commands/scaffold-templates.js +2 -37
- package/dist/cli/commands/scaffold-templates.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +0 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +0 -10
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/commands/status.js +10 -10
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts +0 -2
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +4 -23
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +16 -4
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +15 -24
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +33 -72
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +4 -3
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +1 -1
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +4 -8
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.js +4 -5
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +1 -1
- package/dist/core/constraints/checkers/types.js +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +9 -25
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.js +10 -81
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +7 -9
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +13 -16
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/index.d.ts +2 -5
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +3 -13
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +10 -38
- package/dist/core/constraints/injection-writer.d.ts.map +1 -1
- package/dist/core/constraints/injection-writer.js +17 -54
- package/dist/core/constraints/injection-writer.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +1 -10
- package/dist/core/constraints/run-env.d.ts.map +1 -1
- package/dist/core/constraints/run-env.js +1 -22
- package/dist/core/constraints/run-env.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +5 -7
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +5 -10
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/effective-constraints.d.ts +12 -22
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +13 -30
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -9
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +4 -42
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +16 -125
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +2 -2
- package/dist/core/validators/passes-gate.js +6 -6
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/index.d.ts +7 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -18
- package/dist/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +11 -12
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +4 -4
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +8 -8
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/standard.d.ts +8 -10
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +10 -16
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +24 -35
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +8 -7
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +5 -5
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +8 -66
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/project-config.js +3 -2
- package/dist/types/project-config.js.map +1 -1
- package/dist/types/trace.d.ts +8 -10
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/CONTEXT.md +2 -2
- package/src/__tests__/checker-extra.test.ts +37 -61
- package/src/__tests__/checker.test.ts +10 -115
- package/src/__tests__/effective-constraints.test.ts +18 -83
- package/src/__tests__/iron-laws.test.ts +71 -90
- package/src/__tests__/passes-gate-check.test.ts +2 -2
- package/src/__tests__/project-config-loader-extra.test.ts +22 -123
- package/src/__tests__/project-config-loader.test.ts +5 -33
- package/src/__tests__/public-exports.test.ts +1 -7
- package/src/__tests__/public-type-surface.test.ts +2 -5
- package/src/__tests__/public-value-surface.test.ts +1 -7
- package/src/__tests__/skip-semantics.test.ts +21 -37
- package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
- package/src/__tests__/trace-analyzer.test.ts +21 -21
- package/src/__tests__/trace-collector.test.ts +22 -22
- package/src/__tests__/traces.test.ts +27 -28
- package/src/cli/commands/CONTEXT.md +9 -9
- package/src/cli/commands/__tests__/check-read-count.test.ts +4 -15
- package/src/cli/commands/__tests__/check-skip-output.test.ts +14 -14
- package/src/cli/commands/__tests__/check.test.ts +14 -31
- package/src/cli/commands/__tests__/constraints-report.test.ts +12 -80
- package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
- package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
- package/src/cli/commands/__tests__/init-ondisk.test.ts +12 -20
- package/src/cli/commands/__tests__/init.test.ts +0 -11
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
- package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
- package/src/cli/commands/__tests__/report.test.ts +1 -1
- package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
- package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
- package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
- package/src/cli/commands/__tests__/status.test.ts +12 -12
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
- package/src/cli/commands/check.ts +43 -82
- package/src/cli/commands/constraints-report.ts +4 -59
- package/src/cli/commands/constraints-retire.ts +49 -154
- package/src/cli/commands/constraints.ts +11 -26
- package/src/cli/commands/definitions.ts +2 -2
- package/src/cli/commands/init.ts +8 -179
- package/src/cli/commands/report.ts +19 -23
- package/src/cli/commands/scaffold-templates.ts +1 -37
- package/src/cli/commands/scaffold.ts +0 -11
- package/src/cli/commands/status.ts +10 -10
- package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
- package/src/core/CONTEXT.md +9 -9
- package/src/core/constraints/__tests__/capability-counts.test.ts +5 -5
- package/src/core/constraints/__tests__/collect-constraints.test.ts +13 -8
- package/src/core/constraints/__tests__/git-evidence.test.ts +4 -7
- package/src/core/constraints/__tests__/injection-writer.test.ts +8 -61
- package/src/core/constraints/__tests__/run-env.test.ts +5 -34
- package/src/core/constraints/__tests__/trace-injection.test.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +17 -5
- package/src/core/constraints/checker.ts +39 -88
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +5 -8
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +1 -53
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +5 -25
- package/src/core/constraints/checkers/docs-freshness.ts +1 -1
- package/src/core/constraints/checkers/governance-presence.ts +4 -3
- package/src/core/constraints/checkers/index.ts +4 -10
- package/src/core/constraints/checkers/iron-flags.ts +3 -12
- package/src/core/constraints/checkers/no-test-simplification.ts +1 -1
- package/src/core/constraints/checkers/types.ts +1 -1
- package/src/core/constraints/definitions/guidelines.ts +8 -25
- package/src/core/constraints/definitions/iron-laws.ts +9 -82
- package/src/core/constraints/definitions.ts +14 -15
- package/src/core/constraints/index.ts +4 -19
- package/src/core/constraints/injection-writer.ts +16 -61
- package/src/core/constraints/run-env.ts +1 -33
- package/src/core/constraints/usage-report.ts +8 -15
- package/src/core/effective-constraints.ts +14 -45
- package/src/core/index.ts +3 -10
- package/src/core/project-config-loader.ts +20 -145
- package/src/core/validators/passes-gate.ts +6 -6
- package/src/failure/CONTEXT.md +1 -1
- package/src/hooks/__tests__/bootstrap.test.ts +3 -5
- package/src/index.ts +8 -18
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
- package/src/monitoring/__tests__/traces.test.ts +22 -22
- package/src/monitoring/trace-analyzer.ts +12 -13
- package/src/monitoring/traces.ts +8 -8
- package/src/presets/__tests__/standard.test.ts +12 -16
- package/src/presets/standard.ts +14 -23
- package/src/types/__tests__/types.test.ts +3 -3
- package/src/types/constraint.ts +26 -40
- package/src/types/passes-gate.ts +5 -5
- package/src/types/project-config.ts +8 -77
- package/src/types/trace.ts +8 -11
- package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
- package/dist/core/constraints/agent-prompt-renderer.js +0 -55
- package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
- package/dist/core/constraints/definitions/prompts.d.ts +0 -12
- package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
- package/dist/core/constraints/definitions/prompts.js +0 -400
- package/dist/core/constraints/definitions/prompts.js.map +0 -1
- package/dist/core/constraints/injection-drift.d.ts +0 -61
- package/dist/core/constraints/injection-drift.d.ts.map +0 -1
- package/dist/core/constraints/injection-drift.js +0 -131
- package/dist/core/constraints/injection-drift.js.map +0 -1
- package/dist/core/constraints/injection-renderer.d.ts +0 -23
- package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
- package/dist/core/constraints/injection-renderer.js +0 -44
- package/dist/core/constraints/injection-renderer.js.map +0 -1
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
- package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
- package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
- package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
- package/src/core/constraints/agent-prompt-renderer.ts +0 -67
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
- package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
- package/src/core/constraints/definitions/prompts.ts +0 -414
- package/src/core/constraints/injection-drift.ts +0 -142
- package/src/core/constraints/injection-renderer.ts +0 -44
package/src/cli/commands/init.ts
CHANGED
|
@@ -10,21 +10,9 @@ import * as path from 'path';
|
|
|
10
10
|
import * as yaml from 'js-yaml';
|
|
11
11
|
import { detectSourceRoots } from '../../utils/detect-source-roots';
|
|
12
12
|
import { getHarnessPackageVersion } from '../../utils/package-version';
|
|
13
|
-
import { getEffectiveConstraints } from '../../core/effective-constraints';
|
|
14
13
|
import { loadRawProjectConfig } from '../../core/project-config-loader';
|
|
15
14
|
import type { CiConfig, CiPlatform, GovernanceConfig } from '../../types/project-config';
|
|
16
|
-
import {
|
|
17
|
-
CONSTRAINTS_START_MARKER,
|
|
18
|
-
CONSTRAINTS_END_MARKER,
|
|
19
|
-
renderConstraintsSection,
|
|
20
|
-
} from '../../core/constraints/injection-renderer';
|
|
21
|
-
import {
|
|
22
|
-
replaceStandaloneRange,
|
|
23
|
-
replaceEnclosedRange,
|
|
24
|
-
cutMarkerBlock,
|
|
25
|
-
resolveGovernanceLanding,
|
|
26
|
-
GOVERNANCE_HEADING,
|
|
27
|
-
} from '../../core/constraints/injection-writer';
|
|
15
|
+
import { replaceStandaloneRange } from '../../core/constraints/injection-writer';
|
|
28
16
|
import { log, logError, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
29
17
|
import {
|
|
30
18
|
runPlan,
|
|
@@ -32,7 +20,6 @@ import {
|
|
|
32
20
|
preCommitHookFile,
|
|
33
21
|
prePushHookFile,
|
|
34
22
|
harnessCheckCiFile,
|
|
35
|
-
customConstraintsFile,
|
|
36
23
|
changelogFile,
|
|
37
24
|
contextDocFile,
|
|
38
25
|
governanceWorkflowFile,
|
|
@@ -294,9 +281,9 @@ export async function init(options: InitOptions, io: CommandIO = processIO): Pro
|
|
|
294
281
|
await fs.writeFile(configPath, configContent, 'utf-8');
|
|
295
282
|
log(io, chalk.green(`✅ 已创建配置文件: ${configPath} (v${pkgVersion})`));
|
|
296
283
|
|
|
297
|
-
// 受管示例文件:检查点 / Resolutions(RKB
|
|
284
|
+
// 受管示例文件:检查点 / Resolutions(RKB 狗粮)
|
|
298
285
|
await runPlan(
|
|
299
|
-
[checkpointsFile(projectPath), resolutionsFile(projectPath)
|
|
286
|
+
[checkpointsFile(projectPath), resolutionsFile(projectPath)],
|
|
300
287
|
io,
|
|
301
288
|
);
|
|
302
289
|
|
|
@@ -322,9 +309,8 @@ export async function init(options: InitOptions, io: CommandIO = processIO): Pro
|
|
|
322
309
|
log(io);
|
|
323
310
|
log(io, chalk.gray('下一步:'));
|
|
324
311
|
log(io, chalk.gray(' 1. 编辑 .harness/config.yml 自定义配置'));
|
|
325
|
-
log(io, chalk.gray(' 2.
|
|
326
|
-
log(io, chalk.gray(' 3.
|
|
327
|
-
log(io, chalk.gray(' 4. 运行 harness status 查看状态'));
|
|
312
|
+
log(io, chalk.gray(' 2. 正常开发:每次 git commit 查暂存的(增量快反馈),每次 git push 查整仓的(全量兜底)——重复是设计使然'));
|
|
313
|
+
log(io, chalk.gray(' 3. 运行 harness status 查看状态'));
|
|
328
314
|
log(io);
|
|
329
315
|
log(io, chalk.blue('💡 提示: 使用 harness init --print-snippets 查看配置代码片段'));
|
|
330
316
|
return { kind: 'ok' };
|
|
@@ -463,7 +449,7 @@ export async function setupClaudeMdOutputStyle(projectPath: string, io: CommandI
|
|
|
463
449
|
try {
|
|
464
450
|
content = await fs.readFile(claudeMdPath, 'utf-8');
|
|
465
451
|
} catch {
|
|
466
|
-
// CLAUDE.md
|
|
452
|
+
// CLAUDE.md 不存在——无人持有该文件,跳过 Output Style 段
|
|
467
453
|
return;
|
|
468
454
|
}
|
|
469
455
|
|
|
@@ -512,159 +498,6 @@ export async function setupClaudeMdOutputStyle(projectPath: string, io: CommandI
|
|
|
512
498
|
log(io, chalk.green('✅ 已在 CLAUDE.md 顶部写入 Output Style 段'));
|
|
513
499
|
}
|
|
514
500
|
|
|
515
|
-
/**
|
|
516
|
-
* 治理契约 PRESERVE 段标记(studio #302,ADR 2026-08-21 落点模型:
|
|
517
|
-
* 治理契约正本住 AGENTS.md 手写 PRESERVE 段,sync-docs 重新生成时原样保留)
|
|
518
|
-
*/
|
|
519
|
-
const GOVERNANCE_PRESERVE_BEGIN = '<!-- PRESERVE:governance -->';
|
|
520
|
-
const GOVERNANCE_PRESERVE_END = '<!-- /PRESERVE:governance -->';
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* 在 AGENTS.md 的 PRESERVE:governance 段写入/更新 Governance Rules 约束段(新落点模型)
|
|
524
|
-
*
|
|
525
|
-
* - AGENTS.md 不存在:创建最小骨架(标题 + 说明 + PRESERVE:governance 段),
|
|
526
|
-
* 完整导读由 `harness sync-docs --agents` 生成,PRESERVE 段在重新生成时原样保留
|
|
527
|
-
* - 已有 PRESERVE:governance 段:段内机器管理的只有 HARNESS_CONSTRAINTS 标记区间——
|
|
528
|
-
* 有标记则只替换标记区间,段内其余手写内容(治理契约引言/流程/纪律等)原样保留;
|
|
529
|
-
* 无标记(纯手写段)则在段尾追加注入段,不动手写内容
|
|
530
|
-
* - 无该段:在文件末尾追加
|
|
531
|
-
* - 段标记残缺(外层或段内 HARNESS_CONSTRAINTS 单边/乱序):不写入,告警交由人工修复(防二次损坏)
|
|
532
|
-
*/
|
|
533
|
-
export async function setupAgentsMdConstraints(projectPath: string, io: CommandIO): Promise<void> {
|
|
534
|
-
const agentsMdPath = path.join(projectPath, 'AGENTS.md');
|
|
535
|
-
const version = getHarnessPackageVersion();
|
|
536
|
-
|
|
537
|
-
const constraints = getEffectiveConstraints(projectPath);
|
|
538
|
-
const bodyOnly = renderConstraintsSection(constraints, version);
|
|
539
|
-
const block = `${GOVERNANCE_PRESERVE_BEGIN}\n${GOVERNANCE_HEADING}\n${bodyOnly}${GOVERNANCE_PRESERVE_END}\n`;
|
|
540
|
-
|
|
541
|
-
let existingContent: string | null = null;
|
|
542
|
-
try {
|
|
543
|
-
existingContent = await fs.readFile(agentsMdPath, 'utf-8');
|
|
544
|
-
} catch {
|
|
545
|
-
// AGENTS.md 不存在
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
if (existingContent === null) {
|
|
549
|
-
const skeleton = [
|
|
550
|
-
'# AGENTS.md',
|
|
551
|
-
'',
|
|
552
|
-
'> 机器生成部分由 `harness sync-docs --agents` 维护;`PRESERVE:governance` 段是治理契约正本(手写/治理变更流程管控),重新生成时原样保留。',
|
|
553
|
-
'',
|
|
554
|
-
block,
|
|
555
|
-
].join('\n');
|
|
556
|
-
await fs.writeFile(agentsMdPath, skeleton, 'utf-8');
|
|
557
|
-
log(io, chalk.green(`✅ 已创建 AGENTS.md 并写入治理契约 PRESERVE:governance 段 (v${version})`));
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
const preserve = cutMarkerBlock(existingContent, GOVERNANCE_PRESERVE_BEGIN, GOVERNANCE_PRESERVE_END);
|
|
562
|
-
|
|
563
|
-
if (preserve === 'half') {
|
|
564
|
-
log(io, chalk.yellow('⚠️ AGENTS.md 中 PRESERVE:governance 标记残缺(只有单边),跳过治理契约写入,请人工修复'));
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
if (preserve !== 'absent') {
|
|
569
|
-
// 段内机器管理的只有 HARNESS_CONSTRAINTS 标记区间;其余手写内容原样保留
|
|
570
|
-
const inner = replaceEnclosedRange(preserve.inner, CONSTRAINTS_START_MARKER, CONSTRAINTS_END_MARKER, bodyOnly);
|
|
571
|
-
if (inner.kind === 'half') {
|
|
572
|
-
log(io, chalk.yellow('⚠️ AGENTS.md PRESERVE:governance 段内 HARNESS_CONSTRAINTS 标记残缺(单边或乱序),跳过治理契约写入,请人工修复'));
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
const newInner =
|
|
576
|
-
inner.kind === 'updated'
|
|
577
|
-
? inner.content
|
|
578
|
-
: // 纯手写段(无约束标记):段尾追加注入段,手写内容不动
|
|
579
|
-
preserve.inner.trimEnd() + '\n\n' + GOVERNANCE_HEADING + '\n' + bodyOnly;
|
|
580
|
-
|
|
581
|
-
const write = replaceStandaloneRange(
|
|
582
|
-
existingContent,
|
|
583
|
-
GOVERNANCE_PRESERVE_BEGIN,
|
|
584
|
-
GOVERNANCE_PRESERVE_END,
|
|
585
|
-
GOVERNANCE_PRESERVE_BEGIN + newInner + GOVERNANCE_PRESERVE_END + '\n'
|
|
586
|
-
);
|
|
587
|
-
if (write.kind === 'updated' && write.content !== existingContent) {
|
|
588
|
-
await fs.writeFile(agentsMdPath, write.content, 'utf-8');
|
|
589
|
-
log(io, chalk.green(`✅ 已更新 AGENTS.md 治理契约 PRESERVE:governance 段 (v${version})`));
|
|
590
|
-
}
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// 无该段:文件末尾追加
|
|
595
|
-
const newContent = existingContent.trimEnd() + '\n\n' + block;
|
|
596
|
-
await fs.writeFile(agentsMdPath, newContent, 'utf-8');
|
|
597
|
-
log(io, chalk.green(`✅ 已追加治理契约 PRESERVE:governance 段到 AGENTS.md (v${version})`));
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/**
|
|
601
|
-
* 在 CLAUDE.md 中写入/更新 Governance Rules 约束段(旧落点模型,向后兼容保留)
|
|
602
|
-
*
|
|
603
|
-
* - 约束集来自 getEffectiveConstraints(ADR-0001):preset 裁剪、config.yml
|
|
604
|
-
* 禁用、custom 追加、scenes 过滤全部反映在注入文本里
|
|
605
|
-
* - 期望段文本由纯函数 renderConstraintsSection 渲染(P6 漂移校验复用)
|
|
606
|
-
* - 如果 CLAUDE.md 不存在,创建并写入完整约束段
|
|
607
|
-
* - 如果存在 HARNESS_CONSTRAINTS_START/END 标记,替换标记间内容
|
|
608
|
-
* - 如果不存在标记,在文件末尾追加约束段
|
|
609
|
-
*/
|
|
610
|
-
export async function setupClaudeMdConstraints(projectPath: string, io: CommandIO): Promise<void> {
|
|
611
|
-
const claudeMdPath = path.join(projectPath, 'CLAUDE.md');
|
|
612
|
-
|
|
613
|
-
const version = getHarnessPackageVersion();
|
|
614
|
-
|
|
615
|
-
// 生效约束集 → 渲染期望段(纯函数,与写文件分离)
|
|
616
|
-
const constraints = getEffectiveConstraints(projectPath);
|
|
617
|
-
const bodyOnly = renderConstraintsSection(constraints, version);
|
|
618
|
-
const fullSection = GOVERNANCE_HEADING + '\n' + bodyOnly;
|
|
619
|
-
|
|
620
|
-
// 检查 CLAUDE.md 是否存在
|
|
621
|
-
let existingContent: string;
|
|
622
|
-
let fileExists = false;
|
|
623
|
-
try {
|
|
624
|
-
existingContent = await fs.readFile(claudeMdPath, 'utf-8');
|
|
625
|
-
fileExists = true;
|
|
626
|
-
} catch {
|
|
627
|
-
existingContent = '';
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (!fileExists) {
|
|
631
|
-
// 创建新文件
|
|
632
|
-
await fs.writeFile(claudeMdPath, fullSection, 'utf-8');
|
|
633
|
-
log(io, chalk.green(`✅ 已创建 CLAUDE.md 并写入治理约束 (v${version})`));
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
const write = replaceStandaloneRange(existingContent, CONSTRAINTS_START_MARKER, CONSTRAINTS_END_MARKER, bodyOnly);
|
|
638
|
-
|
|
639
|
-
if (write.kind === 'updated') {
|
|
640
|
-
// 替换标记区间含标记本身(保持包括最新版本号),尾部换行规范化在 writer 内
|
|
641
|
-
await fs.writeFile(claudeMdPath, write.content, 'utf-8');
|
|
642
|
-
log(io, chalk.green(`✅ 已更新 CLAUDE.md 治理约束 (v${version})`));
|
|
643
|
-
} else if (write.kind === 'half') {
|
|
644
|
-
log(io, chalk.yellow('⚠️ CLAUDE.md 中 HARNESS_CONSTRAINTS 标记残缺(单边或乱序),跳过治理约束注入,请人工修复'));
|
|
645
|
-
} else {
|
|
646
|
-
// 在文件末尾追加完整段
|
|
647
|
-
const newContent = existingContent.trimEnd() + '\n\n' + fullSection;
|
|
648
|
-
await fs.writeFile(claudeMdPath, newContent, 'utf-8');
|
|
649
|
-
log(io, chalk.green(`✅ 已追加治理约束到 CLAUDE.md (v${version})`));
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* 治理约束段写入落点路由(studio #302,ADR 2026-08-21 落点模型)
|
|
655
|
-
*
|
|
656
|
-
* 判定收口在 core/constraints/injection-writer resolveGovernanceLanding:
|
|
657
|
-
* - 旧模型仓(CLAUDE.md 已有 HARNESS_CONSTRAINTS 标记或 `## Governance Rules` 块):
|
|
658
|
-
* 继续写 CLAUDE.md——init 幂等重跑不破坏既有仓,不制造双份约束正本
|
|
659
|
-
* - 其余(新仓初始化):写 AGENTS.md PRESERVE:governance 段(入库公共面正本)
|
|
660
|
-
*/
|
|
661
|
-
export async function setupGovernanceConstraints(projectPath: string, io: CommandIO): Promise<void> {
|
|
662
|
-
const { target } = resolveGovernanceLanding(projectPath);
|
|
663
|
-
return target === 'claude-md'
|
|
664
|
-
? setupClaudeMdConstraints(projectPath, io)
|
|
665
|
-
: setupAgentsMdConstraints(projectPath, io);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
501
|
/**
|
|
669
502
|
* 设置治理相关文件
|
|
670
503
|
*/
|
|
@@ -686,14 +519,10 @@ async function setupGovernance(
|
|
|
686
519
|
// 2. 在 CLAUDE.md 中写入 Output Style 段(仅在不存在时创建)
|
|
687
520
|
await setupClaudeMdOutputStyle(projectPath, io);
|
|
688
521
|
|
|
689
|
-
// 3.
|
|
690
|
-
// 旧模型仓 → CLAUDE.md,落点路由见 setupGovernanceConstraints)
|
|
691
|
-
await setupGovernanceConstraints(projectPath, io);
|
|
692
|
-
|
|
693
|
-
// 4. 生成 CONTEXT.md 文件(预设形状即 GovernanceConfig,无需再 cast)
|
|
522
|
+
// 3. 生成 CONTEXT.md 文件(预设形状即 GovernanceConfig,无需再 cast)
|
|
694
523
|
await runPlan(await contextDocPlan(projectPath, governance, io), io);
|
|
695
524
|
|
|
696
|
-
//
|
|
525
|
+
// 4. 生成治理 CI 面(仅 github 形;gitlab 已并入 .gitlab-ci.yml 正文,none 不建,harness#156)
|
|
697
526
|
await setupGovernanceWorkflow(projectPath, level, io, platform);
|
|
698
527
|
}
|
|
699
528
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import * as fs from 'fs/promises';
|
|
9
9
|
import { ConstraintChecker } from '../../core/constraints/checker';
|
|
10
|
-
import {
|
|
10
|
+
import { CONSTRAINTS } from '../../core/constraints/definitions';
|
|
11
11
|
import { TraceCollector } from '../../monitoring/traces';
|
|
12
12
|
import type { ConstraintContext } from '../../types/constraint';
|
|
13
13
|
import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
@@ -26,15 +26,14 @@ interface ReportData {
|
|
|
26
26
|
projectPath: string;
|
|
27
27
|
constraints: {
|
|
28
28
|
total: number;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
prompts: number;
|
|
29
|
+
errors: number;
|
|
30
|
+
warnings: number;
|
|
32
31
|
passed: number;
|
|
33
32
|
failed: number;
|
|
34
|
-
|
|
33
|
+
warningViolations: number;
|
|
35
34
|
violations: Array<{
|
|
36
35
|
id: string;
|
|
37
|
-
|
|
36
|
+
severity: string;
|
|
38
37
|
message: string;
|
|
39
38
|
}>;
|
|
40
39
|
};
|
|
@@ -51,23 +50,22 @@ export async function report(options: ReportOptions, io: CommandIO = processIO):
|
|
|
51
50
|
// #139:收集器锚根构造——不传 projectPath 时 trace 会落进调用方 cwd,B 侧读不到
|
|
52
51
|
const checker = new ConstraintChecker(new TraceCollector({ projectPath }));
|
|
53
52
|
|
|
54
|
-
const
|
|
55
|
-
const totalConstraints = Object.keys(allConstraints).length;
|
|
53
|
+
const totalConstraints = Object.keys(CONSTRAINTS).length;
|
|
56
54
|
|
|
57
55
|
const context: ConstraintContext = {
|
|
58
56
|
operation: 'file_modification',
|
|
59
57
|
projectPath,
|
|
60
58
|
};
|
|
61
59
|
|
|
62
|
-
// 收集模式(架构评审候选1
|
|
60
|
+
// 收集模式(架构评审候选1):不抛、全量收集,error 级违规如实进报告
|
|
63
61
|
const result = await checker.collectConstraints(context);
|
|
64
62
|
|
|
65
|
-
const
|
|
66
|
-
const
|
|
63
|
+
const failedErrors = result.errors.filter(r => !r.satisfied);
|
|
64
|
+
const failedWarnings = result.warnings.filter(r => !r.satisfied);
|
|
67
65
|
|
|
68
|
-
const violations = [...
|
|
66
|
+
const violations = [...failedErrors, ...failedWarnings].map(r => ({
|
|
69
67
|
id: r.id,
|
|
70
|
-
|
|
68
|
+
severity: r.severity,
|
|
71
69
|
message: r.message || '',
|
|
72
70
|
}));
|
|
73
71
|
|
|
@@ -76,12 +74,11 @@ export async function report(options: ReportOptions, io: CommandIO = processIO):
|
|
|
76
74
|
projectPath,
|
|
77
75
|
constraints: {
|
|
78
76
|
total: totalConstraints,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
prompts: Object.keys(PROMPTS).length,
|
|
77
|
+
errors: Object.values(CONSTRAINTS).filter(c => c.severity === 'error').length,
|
|
78
|
+
warnings: Object.values(CONSTRAINTS).filter(c => c.severity === 'warning').length,
|
|
82
79
|
passed: result.passed ? totalConstraints : totalConstraints - violations.length,
|
|
83
|
-
failed:
|
|
84
|
-
|
|
80
|
+
failed: failedErrors.length,
|
|
81
|
+
warningViolations: failedWarnings.length,
|
|
85
82
|
violations,
|
|
86
83
|
},
|
|
87
84
|
};
|
|
@@ -122,19 +119,18 @@ function generateMarkdownReport(data: ReportData): string {
|
|
|
122
119
|
`| 指标 | 数值 |`,
|
|
123
120
|
`|------|------|`,
|
|
124
121
|
`| 总约束 | ${data.constraints.total} |`,
|
|
125
|
-
`|
|
|
126
|
-
`|
|
|
127
|
-
`| Prompts | ${data.constraints.prompts} |`,
|
|
122
|
+
`| error 级 | ${data.constraints.errors} |`,
|
|
123
|
+
`| warning 级 | ${data.constraints.warnings} |`,
|
|
128
124
|
`| 通过 | ${data.constraints.passed} |`,
|
|
129
125
|
`| 失败 (error) | ${data.constraints.failed} |`,
|
|
130
|
-
`| 警告 (warning) | ${data.constraints.
|
|
126
|
+
`| 警告 (warning) | ${data.constraints.warningViolations} |`,
|
|
131
127
|
``,
|
|
132
128
|
];
|
|
133
129
|
|
|
134
130
|
if (data.constraints.violations.length > 0) {
|
|
135
131
|
lines.push(`### 违规项`, ``);
|
|
136
132
|
data.constraints.violations.forEach(v => {
|
|
137
|
-
lines.push(`- **${v.id}** (${v.
|
|
133
|
+
lines.push(`- **${v.id}** (${v.severity}): ${v.message}`);
|
|
138
134
|
});
|
|
139
135
|
lines.push(``);
|
|
140
136
|
}
|
|
@@ -249,42 +249,6 @@ ${docsCheckStep}
|
|
|
249
249
|
`;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
/** custom-constraints.yml 示例正文 */
|
|
253
|
-
export const CUSTOM_CONSTRAINTS_TEMPLATE = `# 自定义约束配置
|
|
254
|
-
#
|
|
255
|
-
# 此文件定义项目特定的约束,扩展或覆盖 harness 内置约束
|
|
256
|
-
|
|
257
|
-
# ========================================
|
|
258
|
-
# 自定义约束示例
|
|
259
|
-
# ========================================
|
|
260
|
-
|
|
261
|
-
custom_constraints:
|
|
262
|
-
# 示例 1:禁止 console.log
|
|
263
|
-
# my_project_no_console_log:
|
|
264
|
-
# id: my_project_no_console_log
|
|
265
|
-
# level: guideline
|
|
266
|
-
# rule: "NO CONSOLE.LOG IN PRODUCTION CODE"
|
|
267
|
-
# message: "生产代码禁止使用 console.log,请使用 logger 模块"
|
|
268
|
-
# trigger: ["code_implementation"]
|
|
269
|
-
# description: "使用项目统一的 logger 模块代替 console.log"
|
|
270
|
-
|
|
271
|
-
# 示例 2:禁止特定的导入
|
|
272
|
-
# my_project_no_moment_js:
|
|
273
|
-
# id: my_project_no_moment_js
|
|
274
|
-
# level: guideline
|
|
275
|
-
# rule: "NO MOMENT.JS IMPORTS"
|
|
276
|
-
# message: "禁止使用 moment.js,请使用 date-fns 或 dayjs"
|
|
277
|
-
# trigger: ["code_implementation"]
|
|
278
|
-
|
|
279
|
-
# 示例 3:要求特定的文件命名
|
|
280
|
-
# my_project_component_naming:
|
|
281
|
-
# id: my_project_component_naming
|
|
282
|
-
# level: tip
|
|
283
|
-
# rule: "REACT COMPONENTS SHOULD BE PASCAL CASE"
|
|
284
|
-
# message: "React 组件文件名应使用 PascalCase"
|
|
285
|
-
# trigger: ["file_creation"]
|
|
286
|
-
`;
|
|
287
|
-
|
|
288
252
|
/** CHANGELOG.md 正文(`format` 来自治理配置的 changelog.format) */
|
|
289
253
|
export function renderChangelog(format: string): string {
|
|
290
254
|
return format === 'keep-a-changelog'
|
|
@@ -386,7 +350,7 @@ export function renderCheckpoints(): string {
|
|
|
386
350
|
|
|
387
351
|
/** 默认 Resolutions(RKB — 约束 → 已知解法映射) */
|
|
388
352
|
const DEFAULT_RESOLUTIONS = {
|
|
389
|
-
|
|
353
|
+
no_completion_without_verification: {
|
|
390
354
|
title: 'commit message 缺少验证证据',
|
|
391
355
|
fix: '在 commit message body 中附上验证输出:\n`npx @dommaker/harness check --staged` | `npx @dommaker/harness validate` | `npm test -- --coverage`\n确认全部通过后重新 commit。',
|
|
392
356
|
},
|
|
@@ -20,7 +20,6 @@ import * as nodeFs from 'fs/promises';
|
|
|
20
20
|
import type { CiPlatform } from '../../types/project-config';
|
|
21
21
|
import { log, type CommandIO } from '../command-contract';
|
|
22
22
|
import {
|
|
23
|
-
CUSTOM_CONSTRAINTS_TEMPLATE,
|
|
24
23
|
DEFAULT_CHECKPOINT_FILE,
|
|
25
24
|
HARNESS_CHECK_WORKFLOW,
|
|
26
25
|
PRE_COMMIT_SNIPPET,
|
|
@@ -235,16 +234,6 @@ export function harnessCheckCiFile(
|
|
|
235
234
|
};
|
|
236
235
|
}
|
|
237
236
|
|
|
238
|
-
/** .harness/custom-constraints.yml 示例 */
|
|
239
|
-
export function customConstraintsFile(projectPath: string): ManagedFile {
|
|
240
|
-
return {
|
|
241
|
-
target: path.join(projectPath, '.harness', 'custom-constraints.yml'),
|
|
242
|
-
content: CUSTOM_CONSTRAINTS_TEMPLATE,
|
|
243
|
-
created: '✅ 已创建自定义约束示例: custom-constraints.yml',
|
|
244
|
-
onPresent: { outcome: 'skip', notice: 'custom-constraints.yml 已存在' },
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
237
|
/** .harness/checkpoints.yml 示例(原住 validate.ts,路径约定与 validate 共用) */
|
|
249
238
|
export function checkpointsFile(projectPath: string): ManagedFile {
|
|
250
239
|
const target = path.join(projectPath, DEFAULT_CHECKPOINT_FILE);
|
|
@@ -109,11 +109,11 @@ export async function status(options: StatusOptions, io: CommandIO = processIO):
|
|
|
109
109
|
log(io, chalk.blue('📈 约束统计:'));
|
|
110
110
|
log(io);
|
|
111
111
|
|
|
112
|
-
//
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
log(io, chalk.red('🔴
|
|
116
|
-
|
|
112
|
+
// severity='error'
|
|
113
|
+
const errorSummaries = summaries.filter((s: TraceSummary) => s.severity === 'error');
|
|
114
|
+
if (errorSummaries.length > 0) {
|
|
115
|
+
log(io, chalk.red('🔴 error 级约束:'));
|
|
116
|
+
errorSummaries.forEach((s: TraceSummary) => {
|
|
117
117
|
const status = s.passRate >= 1 ? '✅' : '❌';
|
|
118
118
|
log(io, ` ${status} ${s.constraintId}`);
|
|
119
119
|
if (options.detail) {
|
|
@@ -123,11 +123,11 @@ export async function status(options: StatusOptions, io: CommandIO = processIO):
|
|
|
123
123
|
log(io);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
//
|
|
127
|
-
const
|
|
128
|
-
if (
|
|
129
|
-
log(io, chalk.yellow('🟡
|
|
130
|
-
|
|
126
|
+
// severity='warning'
|
|
127
|
+
const warningSummaries = summaries.filter((s: TraceSummary) => s.severity === 'warning');
|
|
128
|
+
if (warningSummaries.length > 0) {
|
|
129
|
+
log(io, chalk.yellow('🟡 warning 级约束:'));
|
|
130
|
+
warningSummaries.forEach((s: TraceSummary) => {
|
|
131
131
|
const status = s.passRate >= 0.8 ? '✅' : s.passRate >= 0.5 ? '⚠️' : '❌';
|
|
132
132
|
log(io, ` ${status} ${s.constraintId}`);
|
|
133
133
|
if (options.detail) {
|
|
@@ -49,8 +49,6 @@ export interface GovernanceInfo {
|
|
|
49
49
|
hasConfig: boolean;
|
|
50
50
|
preset?: string;
|
|
51
51
|
hasClaudeGovernance: boolean;
|
|
52
|
-
ironLaws?: number;
|
|
53
|
-
guidelines?: number;
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
/**
|
|
@@ -114,11 +112,7 @@ export async function buildAgentsMd(projectPath: string, srcDirs: string[]): Pro
|
|
|
114
112
|
lines.push(`- 治理配置:\`.harness/config.yml\`${governance.preset ? `(preset: ${governance.preset})` : ''}`);
|
|
115
113
|
}
|
|
116
114
|
if (governance.hasClaudeGovernance) {
|
|
117
|
-
|
|
118
|
-
governance.ironLaws !== undefined ? `Iron Laws ${governance.ironLaws} 条` : null,
|
|
119
|
-
governance.guidelines !== undefined ? `Guidelines ${governance.guidelines} 条` : null,
|
|
120
|
-
].filter(Boolean).join('、');
|
|
121
|
-
lines.push(`- 约束清单:\`CLAUDE.md\` Governance Rules 块${counts ? `(${counts})` : ''}`);
|
|
115
|
+
lines.push('- 约束清单:`CLAUDE.md` Governance Rules 块');
|
|
122
116
|
}
|
|
123
117
|
if (!governance.hasConfig && !governance.hasClaudeGovernance) {
|
|
124
118
|
lines.push('- 未检测到 harness 治理配置,可运行 `harness init` 初始化');
|
|
@@ -208,7 +202,7 @@ async function listWorkspaceMembers(dir: string): Promise<string[]> {
|
|
|
208
202
|
}
|
|
209
203
|
}
|
|
210
204
|
|
|
211
|
-
/** 收集治理信息:.harness/config.yml 与 CLAUDE.md
|
|
205
|
+
/** 收集治理信息:.harness/config.yml 与 CLAUDE.md 治理块 */
|
|
212
206
|
async function getGovernanceInfo(projectPath: string): Promise<GovernanceInfo> {
|
|
213
207
|
const info: GovernanceInfo = { hasConfig: false, hasClaudeGovernance: false };
|
|
214
208
|
|
|
@@ -224,12 +218,8 @@ async function getGovernanceInfo(projectPath: string): Promise<GovernanceInfo> {
|
|
|
224
218
|
|
|
225
219
|
try {
|
|
226
220
|
const claude = await fs.readFile(path.join(projectPath, 'CLAUDE.md'), 'utf-8');
|
|
227
|
-
// 契约在场判定收口 injection-writer.hasGovernanceContract(#83,与 presence
|
|
228
|
-
|
|
229
|
-
info.hasClaudeGovernance = true;
|
|
230
|
-
info.ironLaws = countConstraintBullets(claude, 'Iron Laws');
|
|
231
|
-
info.guidelines = countConstraintBullets(claude, 'Guidelines');
|
|
232
|
-
}
|
|
221
|
+
// 契约在场判定收口 injection-writer.hasGovernanceContract(#83,与 presence 同谓词)
|
|
222
|
+
info.hasClaudeGovernance = hasGovernanceContract(claude);
|
|
233
223
|
} catch {
|
|
234
224
|
// CLAUDE.md 不存在
|
|
235
225
|
}
|
|
@@ -237,17 +227,6 @@ async function getGovernanceInfo(projectPath: string): Promise<GovernanceInfo> {
|
|
|
237
227
|
return info;
|
|
238
228
|
}
|
|
239
229
|
|
|
240
|
-
/** 统计 CLAUDE.md 指定约束分节下的条目数(`- **key**:` 行),分节不存在时返回 undefined */
|
|
241
|
-
function countConstraintBullets(content: string, section: string): number | undefined {
|
|
242
|
-
const heading = new RegExp(`^###\\s+${section}[^\\n]*$`, 'm').exec(content);
|
|
243
|
-
if (!heading) return undefined;
|
|
244
|
-
const rest = content.slice(heading.index + heading[0].length);
|
|
245
|
-
const end = rest.search(/^###\s|^\s*<!--\s*HARNESS_CONSTRAINTS_END/m);
|
|
246
|
-
const block = end === -1 ? rest : rest.slice(0, end);
|
|
247
|
-
const items = block.match(/^-\s+\*\*[A-Za-z0-9_]+\*\*/gm);
|
|
248
|
-
return items ? items.length : 0;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
230
|
/** 判断 .harness/knowledge 目录是否存在(只判存在性,不统计条数——条数易变,见 buildAgentsMd 注释) */
|
|
252
231
|
async function hasKnowledgeDir(projectPath: string): Promise<boolean> {
|
|
253
232
|
try {
|
package/src/core/CONTEXT.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# core/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
约束引擎核心:
|
|
4
|
+
约束引擎核心:severity 显式约束系统(ADR-0029,三层命名与 kind 二元已由 ADR-0001 迁至本模型)、生效集合并(effective-constraints)、检查点验证器(CSO/passes-gate)、Spec 验证器、项目配置加载。
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
|
-
- `constraints/` — 约束定义(
|
|
7
|
+
- `constraints/` — 约束定义(CONSTRAINTS 单桶,severity 在条目上,ADR-0029) + 检查引擎(ConstraintChecker,拦截统一由 checkBeforeExecution 承担,ADR-0004) + 缓存(CheckCache:TTL 缓存 + 计数采样,H6/G5 起公开导出) + 标记区间写入器(injection-writer:「声明→对照→幂等替换」中 marker-range 替换的唯一 writer——replaceStandaloneRange/replaceEnclosedRange/cutMarkerBlock + 半标记守护;治理正本探测 GOVERNANCE_HEADING/hasGovernanceContract/countGovernanceHeadings/hasPreserveBlock 同住此模块,ADR-0011/#83。ADR-0029:注入渲染(injection-renderer)、漂移校验(injection-drift)、Agent prompt 渲染(agent-prompt-renderer) 与注入段落点路由已随文本注入层关停整体删除)/trigger 谓词(triggers.ts——harness#105 定语义,约束域谓词不住 utils 层)/使用统计(usage-report:constraints report·retire 的只读数据层,读 trace 两个入口——`readProjectTracesReport()` 带 `skippedLines`、`readProjectTraces()` 兼容包装丢计数;报告模型 `ConstraintsUsageReport` 的降级位 = `traceFileExists` + `skippedLines`,harness#100)/清单对照(capabilities-reconcile:CAPABILITIES.md 与代码实况的覆盖/幽灵判定唯一实现,capability_sync·docs_freshness·sync-docs 共消费,ADR-0009;一次 check run 内该判定只算一次并由 `RunEnv.capabilities()` 供给两 checker,ADR-0023 决策 4——增量那一维由 capability_sync 用导出的唯一覆盖规则 `isCoveredByEntries` 自算,代码实况清单的唯一取法是 `collectPopulationFiles`);CheckEnv 生产侧唯一构造点 `buildCheckEnv(context, providers|'none', runEnv?)`(checkers/types.ts:CheckEnv `extends RunEnv`,runEnv 注入即与 context-builder 共用同一份上行数据读取、不传则自造一枚;providers = 证据接线,git 两项绑 GitEvidence 实例,'none' = 显式不接 → evidence flag checker 按契约 skip);判定接缝 `CheckOutcome = boolean | 'skip' | CheckDetail{pass, evidence}`(harness#119/ADR-0016)——编排层与 `gates/checker-gate` 一律经唯一归一点 `normalizeCheckOutcome()` 取三态 + 证据,不各自 `=== false`(对新形状会误判);证据行由 checker 自行措辞(自描述、不带缩进),组装与截断策略单点在 `formatEvidence()`/`MAX_EVIDENCE_ITEMS`(同住 checkers/types.ts);证据四出口 = `ConstraintResult.evidence` → CLI 结论块/提示块、`ExecutionTrace.evidence`、error 级 `ConstraintViolationError.message`(error 级违规直接 throw,异常文案是其证据唯一外溢面);检查引擎双出口——`checkConstraints`(block:首个 error 级违规即抛,throw 契约不动)与 `collectConstraints`(collect:共享检查体、不抛、全量收集,report 的不抛出口,架构评审候选1)
|
|
8
8
|
- `constraints/context-reconcile.ts` — CONTEXT.md「核心导出」节与目录导出面的**幽灵/未登记**判定唯一实现(harness#142 / ADR-0025,与 `capabilities-reconcile` 同一套覆盖/幽灵语义,不另造第二套对照逻辑):`reconcileContext({ contextMdContent, exportSurface, barrelExports? })` → `{ declaredSymbols, ghosts, unlistedBarrelExports, hasCoreExportsSection }`;配套两个口径正本也是纯函数——`parseDeclaredExportSymbols`(只认 bullet 行首反引号标识符,`/` 连接视为多声明,形参与 `(type)`注解剥掉,非标识符的模块名 bullet 整条跳过)与 `parseExportStatements`(声明形 / `export { a, b as c }` / 逐项与块级 `type` / `export default` / `export *` 的待解析 spec)。零 fs IO:文件遍历与 `export *` 目标解析在调用方(`cli/commands/sync-docs/context-syncer.ts` 的 `collectContextExportSurface`);覆盖方向只判 barrel 的**值**符号(类型面豁免),mtime 判定就此降级为本地提示。本层 checker 暂无消费方
|
|
9
9
|
- `git-evidence.ts` — check 链路的 git 取证唯一接缝(#87,范围按 ADR-0021 收窄;`spec/validator` 的 staged 列表同型复用):`createGitEvidence(projectPath, run?)` 返回 `GitEvidence{stagedDiff, changedFileNames(staged), headDirs}`,seam 上两个真 adapter(缺省 `realGitCommandRunner` 走真 git / 注入 runner 供计数与替身)。命令级 memo 落在此实例(含失败结果),实例生命周期 = 一次 check run,故 context-builder 与 ConstraintChecker 传同一实例即共用证据;判定语义与迁移前一致(diff 取证失败 → 空串,`git ls-tree` 失败 → null = 所有目录视为新);`parseHeadDirs`/`splitFileNames` 是配套纯函数
|
|
10
10
|
- `constraints/context-builder.ts` — 只装配不取证:`buildConstraintContext(options.evidence?, options.runEnv?)` 与 `detectTrigger(…, options.runEnv?)` 经 GitEvidence 取 git 事实(不传 = 自建真 adapter),本模块无 child_process import;上行数据(源码根、trace 记录)经 `RunEnv` 取(ADR-0023),其余证据标志(ROOT_CAUSE/reuse/需求文档标记)仍为本地探测;`detectFailingTest`/`detectVerificationEvidence` 是模块私有的布尔投影(除本模块外零消费者,收私依 ADR-0022 同判据)
|
|
11
|
-
- `constraints/run-env.ts` — 一次 check 的运行级观察面(ADR-0023):`createRunEnv(projectPath)` 返回 `RunEnv{projectPath, traceTail(limit), sourceRoots(), rawConfig(),
|
|
12
|
-
- `effective-constraints.ts` — `getEffectiveConstraints(target, {preset?})`:全仓唯一生效集来源(内置 → preset → config.yml
|
|
11
|
+
- `constraints/run-env.ts` — 一次 check 的运行级观察面(ADR-0023):`createRunEnv(projectPath)` 返回 `RunEnv{projectPath, traceTail(limit), sourceRoots(), rawConfig(), capabilities(populationFiles)}`(ADR-0029:customConstraints 观察口随 custom 约束面退役移除),懒建 + run 内 memo,同一份文件一次运行至多读一次。与 git-evidence 同一形状:一次 run 一份、沿调用链显式传递(CLI check 入口 → 生效集 / buildConstraintContext / checkConstraints),不做模块级全局。为何是两个类型而不是一个:`CheckEnv` 含 `context`,而 `context` 正是本对象读文件算出来的产物——同一对象不能既是构造者的输入又是其产物;故这里只放「不需要 context 就能造」的那半,`CheckEnv` 以 `extends RunEnv` 从它派生(`buildCheckEnv(context, evidence, runEnv?)` 第三参注入,不传 = 自造一枚一次性观察面)。配置口口径:`rawConfig()` 缺失 → undefined、空文件 → `{}`、YAML 解析失败**照抛**(兜与否属消费方判定)。`capabilities(population)` 一次读文档 + 一次 `reconcileCapabilities`(带存在性 oracle;不收 git 增量,`verdict.uncoveredChanges` 在此恒空),**memo 只记第一次传入的清单**——两消费方一律经 `collectPopulationFiles` 取数以由构造保证同形;源根 memo 与它共用。`traceTail(limit)` 底层是 `jsonl.readJsonlWindow`(一次分块读尾部行文本、按不同 limit 反复截窗),**先截行文本再 parse** 才与「各读一遍」逐字同口径(坏行占槽位不占 records 名额);请求超出窗口上限直接抛,不静默少给。`RunTarget = RunEnv | 项目根路径` 与 `resolveRunEnv()` 是这套入参形状的归一点:传路径 = 一次性读取语义(每次调用读当下内容)
|
|
12
|
+
- `effective-constraints.ts` — `getEffectiveConstraints(target, {preset?})`:全仓唯一生效集来源(内置 → preset → config.yml 禁用;ADR-0029:custom 追加与 scenes 过滤随文本注入层关停退役);`constraintsFromMerged(merged)`:从已算好的合并配置取清单(推导单点,`getEffectiveConstraints` 也走它);`getMergedConstraintsConfig(target, {preset?})`:同链路的完整 MergedConstraintsConfig 形状(含 constraints/disabled/unknownIds 单桶),内置工单 23 优先级规则(**只有显式传 preset 才覆盖 config.yml**,CLI 不给缺省值,ADR-0023 步骤 4.5),check 命令经此入口 → 一次运行只算一遍;三个入口的 `target` = `RunTarget`(传 RunEnv 即与 checker 共用同一份配置读取,ADR-0023 决策 2);`lintEffectiveConfig` 配置诊断(unknownIds)
|
|
13
13
|
- `effective-set.ts` — `filterEnabledEntries(knownIds, entries)`:config 条目筛选器唯一实现,语义单一——未知 id 静默忽略并收集进 `unknownIds` 供诊断(历史配置可能残留已移除条目的禁用键,故不抛错)。唯一生产调用方是约束侧 `mergeConstraints`;曾按语义分 `'collect'`/`'throw'` 两模式,throw 侧唯一调用方 `getEffectiveGates` 随 ADR-0002「后续变更」收缩后,该模式按 ADR-0022 判据删除(harness#141)
|
|
14
14
|
- `validators/` — checkpoints、passes-gate、CSO 验证器;`test-output.ts` —「测试产物解读」唯一实现:`extractFailures` 提取函数 + `judgeTestRun` 判定入口(退出码为主 + 行首锚定的文本失败信号交叉否决;文本救不回非零退出,`allowPartialPass` 只作用于文本否决那一维),passes-gate 与 `gates/acceptance` 的结论一律从它取、各自体内不写判定 `if`(解析收口 ADR-0012,判定依据 ADR-0014);**不读覆盖率**——harness 核心判定对覆盖率不取数不执法,判覆盖率的两处(CLI `passes-gate --coverage`、`gates/performance`)各自读 json-summary,`extractCoverage` 与 `TaskTestResult.coverage` 已随 ADR-0015 删除;`PassesGate.runTests(workDir)` 的执行与证据落盘根是**必传形参**,本层不再取 cwd(harness#95,约定正本见 `src/cli/commands/CONTEXT.md`);`detectTestCommand` 是测试命令探测唯一正本(模块级导出:test:ci → test 排 echo 占位 → test:e2e → test:coverage → Python 标记 pyproject.toml/pytest.ini → go.mod,探不到返回 undefined,兜底归调用方——CLI 映射 skip、PassesGate 内部 fail-closed,不再兜底 'npm test';CLI 本地版已删,架构评审 A2);PassesGate 扩展注册表(registerExtension/unregisterExtension/getExtensionNames/runAllTests/checkAllPasses + PassesGateExtension/ExtensionTestResult 两类型与包根导出)已整段删除——零 adapter 假想 seam 且扩展结果本就不参与 allowed 判定,需要扩展测试类型时按 ADR-0014 判例在 `judgeTestRun` 层加,不恢复旧 seam(harness#104,架构评审 B1);PassesGate 公开面 = `check()` + `runTests()` 两个真实消费点,`setPasses`/`getTestResult`/`checkTestFileChanges` 三方法与死数据 `PROTECTED_TEST_PATTERNS`、私有 `testResults` 缓存已随 ADR-0022(双仓零消费者)删除,历史 AC-007「保留 setPasses 向后兼容」同案推翻;`runTest` 的 `_task` 形参与其类型 `DynamicTask` 是同批漏收的第三处死面(二次复审 B1)——setPasses 删除后只剩这个从不读取的私有形参在供养它,而包根 `DynamicTask` 取自 `./types/passes-gate`,故它是**公开的**死类型;随 A1/A2 同判据删除,`runTest` 签名收为 `(workDir, command?)`,类型面钉子同 `PassesGateResult` 手法(`src/__tests__/public-exports.test.ts`);包内模块,不进 `src/index.ts` 导出面;`check-handlers/` 各族处理器统一签名 `(check, context)`,context 即既有注入车辆(`customHandlers` 同形)——`http_*` 族的重试退避等待经 `CheckpointContext.sleep` 注入(工单 #136),缺省 = 真定时器且退避公式与重试次数逐字不变,测试据此断言尝试次数与退避序列而不必真等;新增等待类副作用按同形收进 context,不再内联进处理器
|
|
15
15
|
- `spec/validator` — SpecValidator
|
|
16
|
-
- `project-config-loader` — 项目配置解读 + 约束合并(mergeConstraints 内含 preset 裁剪:presets/
|
|
17
|
-
- `index.ts` barrel 公开面(子路径 `@dommaker/harness/core`,ADR-0003 显式清单、禁 `export *`)——定义取数 `getAllConstraints()` / `getConstraint(id)` / `findConstraintsByTrigger(trigger)`(正本 `constraints/definitions.ts`);判定出口 `checkConstraints()` / `checkConstraint()` / `checkBeforeExecution()` 与未接线默认实例 `constraintChecker`(= `ConstraintChecker.getInstance()`,不写 trace,正本 `constraints/checker.ts
|
|
16
|
+
- `project-config-loader` — 项目配置解读 + 约束合并(mergeConstraints 内含 preset 裁剪:presets/ 纯数据(键按 severity 命名 errors/warnings,ADR-0029)+ 未知名回落 standard,禁用到未知 id 经 effective-set 共享筛选器收集(未知 id 不抛错、进 `unknownIds` 供诊断));ADR-0029:custom 约束装载(loadCustomConstraints/getCustomConstraints/toConstraint)与退役判定单点 `isConstraintRetired` 已随 custom 面退役移除,retire 的 already_retired 判定只剩 config.yml `enabled:false` 一处;**本模块不碰文件系统**:config.yml 的读取在 `constraints/run-env.ts`(ADR-0023 决策 2 撤销了工单 16 的进程级 `rawConfigCache` + mtime 指纹,改为一次运行一份快照);访问器族(`loadRawProjectConfig`/`getGovernanceConfig`/`resolveContextFiles`/`getCapabilitiesMode`)与 `ProjectConfigLoader` 构造器的入参形状 = `RunTarget`(传项目根路径 = 一次性读取,传 RunEnv = 与本 run 其余消费方共用同一份);`getGovernanceConfig` 是全仓唯一允许手写钻取 `config.governance` 的点(工单 84),消费方一律经它或经 `resolveContextFiles`(三态:unconfigured / enabled-empty / enabled,dirs 元素类型在此收口)取数据,不得再自己 cast `Record<string, unknown>`;checker 对前两态一律 skip,自动探测回落只属于 init/扫描类工具流调用方
|
|
17
|
+
- `index.ts` barrel 公开面(子路径 `@dommaker/harness/core`,ADR-0003 显式清单、禁 `export *`)——定义取数 `getAllConstraints()` / `getConstraint(id)` / `findConstraintsByTrigger(trigger)`(正本 `constraints/definitions.ts`);判定出口 `checkConstraints()` / `checkConstraint()` / `checkBeforeExecution()` 与未接线默认实例 `constraintChecker`(= `ConstraintChecker.getInstance()`,不写 trace,正本 `constraints/checker.ts`);约束数据 `CONSTRAINTS` 单桶(正本 `constraints/definitions.ts`;ADR-0029:注入渲染三件套导出已随文本注入层关停移除);验证器 `CheckpointValidator` / `CSOValidator`(两者私有构造 + `static getInstance()` 单例出口)与 `PassesGate` + 工厂 `createPassesGate(config?)`(`Partial<PassesGateConfig>`,正本 `validators/`)
|
|
18
18
|
|
|
19
19
|
## 依赖关系
|
|
20
20
|
- 被所有模块依赖(基础层,无上层依赖)
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
- 对 `cli/`、`gates/`、`monitoring/` **零值导入**(type-only 允许):方向由 eslint.config.mjs 的 `@typescript-eslint/no-restricted-imports`(error 级)锁死,守卫测试见 `src/__tests__/layering.test.ts`(harness#88;harness#137 起该守卫已参数化成「每个顶层单元声明允许的下行集合」,core 的这条边只是其中一个条目)
|
|
24
24
|
|
|
25
25
|
## 约定
|
|
26
|
-
- 约束定义在 `constraints/definitions/{iron-laws,guidelines
|
|
27
|
-
- check
|
|
28
|
-
-
|
|
26
|
+
- 约束定义在 `constraints/definitions/{iron-laws,guidelines}.ts`(按 severity 分组),不在运行时代码中定义
|
|
27
|
+
- 全部约束 kind='check',必须带真实 checker(注册表闭环,引用未注册 checker 构建报错)
|
|
28
|
+
- severity='error' 违规必须 throw ConstraintViolationError
|
|
29
29
|
- 上行数据一律注入,不在 core 内 require 上层(harness#88):trace 记录器经 `ConstraintChecker` 构造参数注入(缺省 no-op,`getInstance()`/`constraintChecker` 即未接线的默认实例,只评估不写 trace),真实收集器由组合根接线(CLI check/report、`bootstrapHarness`);CAPABILITIES.md 能力清单计数经 `capabilities-parser` 的 `CapabilityDefinitionSource` 由 cli 侧注入定义表
|
|
30
30
|
- git **取证**(读判定证据)在 check 链路内只能经 `constraints/git-evidence.ts` 的 adapter 取(#87,适用范围按 ADR-0021 收窄为「check 链路唯一」,不再声称全仓唯一):`context-builder` / `checker` / `CheckEnv` / `validators` 这条判定链内不得出现 `execSync`/`execAsync`/`execFileAsync` 跑 git;`spec/validator` 的 staged 文件列表与 `changedFileNames(staged:true)` 同型,已收口(就地 `createGitEvidence(cwd)`,测试注入 runner);一次 run 一份实例、沿调用链显式传递(CLI check → buildConstraintContext → checkConstraints),memo 既不做成模块级全局也不做成单例字段。**链外站点记名豁免**(语义异质:不要 memo、不要 once 语义、失败处理各异,硬并入会造出宽 interface)——`cli/commands/release.ts`(发布流程编排,非判定证据)、`gates/review.ts` 与 `cli/commands/review.ts`(PR 审查流,gh/日志语义)、`knowledge/import.ts`(导入启发式,失败记 ImportError 降级)。豁免清单外新增 git 站点先回答「在不在判定链内」;`validators/passes-gate.ts` 的 `git diff --name-only` 站点已随 ADR-0022 删 `checkTestFileChanges` 消失,判定链内当下无站外 git 调用点
|
|
31
31
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as os from 'os';
|
|
13
13
|
import * as path from 'path';
|
|
14
|
-
import {
|
|
14
|
+
import { CONSTRAINTS } from '../definitions';
|
|
15
15
|
import {
|
|
16
16
|
checkCapabilityCounts,
|
|
17
17
|
updateCapabilityCounts,
|
|
@@ -56,10 +56,10 @@ describe('能力清单计数(注入定义源,harness#88)', () => {
|
|
|
56
56
|
expect(out).toContain('Quality Gates (4)');
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
it('
|
|
60
|
-
const out = updateCapabilityCounts('
|
|
61
|
-
expect(out).toContain(`
|
|
62
|
-
expect(out).toContain(`
|
|
59
|
+
it('errors / warnings 计数仍来自 core 自身定义表(同层,不注入)', () => {
|
|
60
|
+
const out = updateCapabilityCounts('errors (999)\nwarnings (999)\n', SOURCE);
|
|
61
|
+
expect(out).toContain(`errors (${Object.values(CONSTRAINTS).filter(c => c.severity === 'error').length})`);
|
|
62
|
+
expect(out).toContain(`warnings (${Object.values(CONSTRAINTS).filter(c => c.severity === 'warning').length})`);
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
it('不触碰文档中不存在的计数行', () => {
|