@dommaker/harness 1.2.0 → 1.2.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cli/commands/check.js +1 -1
  3. package/dist/cli/commands/check.js.map +1 -1
  4. package/dist/cli/commands/constraints-report.js +1 -1
  5. package/dist/cli/commands/constraints-report.js.map +1 -1
  6. package/dist/cli/commands/constraints-retire.d.ts +6 -3
  7. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  8. package/dist/cli/commands/constraints-retire.js +34 -25
  9. package/dist/cli/commands/constraints-retire.js.map +1 -1
  10. package/dist/cli/commands/init.d.ts +21 -1
  11. package/dist/cli/commands/init.d.ts.map +1 -1
  12. package/dist/cli/commands/init.js +112 -3
  13. package/dist/cli/commands/init.js.map +1 -1
  14. package/dist/core/constraints/checkers/governance-presence.d.ts +20 -0
  15. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -0
  16. package/dist/core/constraints/checkers/governance-presence.js +76 -0
  17. package/dist/core/constraints/checkers/governance-presence.js.map +1 -0
  18. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  19. package/dist/core/constraints/checkers/index.js +2 -0
  20. package/dist/core/constraints/checkers/index.js.map +1 -1
  21. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  22. package/dist/core/constraints/definitions/guidelines.js +21 -0
  23. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  24. package/dist/core/constraints/injection-drift.d.ts +29 -4
  25. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  26. package/dist/core/constraints/injection-drift.js +52 -20
  27. package/dist/core/constraints/injection-drift.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/__tests__/governance-presence.test.ts +129 -0
  30. package/src/__tests__/iron-laws.test.ts +7 -7
  31. package/src/cli/commands/CONTEXT.md +4 -1
  32. package/src/cli/commands/__tests__/constraints-report.test.ts +2 -2
  33. package/src/cli/commands/__tests__/constraints-retire.test.ts +45 -4
  34. package/src/cli/commands/__tests__/init-injection.test.ts +202 -0
  35. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +28 -0
  36. package/src/cli/commands/check.ts +1 -1
  37. package/src/cli/commands/constraints-report.ts +1 -1
  38. package/src/cli/commands/constraints-retire.ts +38 -27
  39. package/src/cli/commands/init.ts +123 -3
  40. package/src/core/CONTEXT.md +1 -1
  41. package/src/core/constraints/__tests__/injection-drift.test.ts +90 -0
  42. package/src/core/constraints/checkers/governance-presence.ts +74 -0
  43. package/src/core/constraints/checkers/index.ts +2 -0
  44. package/src/core/constraints/definitions/guidelines.ts +22 -0
  45. package/src/core/constraints/injection-drift.ts +60 -21
@@ -0,0 +1,20 @@
1
+ /**
2
+ * governance_presence:治理契约在场守护(studio #302,ADR 2026-08-21 落点模型)
3
+ *
4
+ * 治理契约正本 = AGENTS.md 手写 `PRESERVE:governance` 段(旧模型仓 = CLAUDE.md
5
+ * Governance Rules / HARNESS_CONSTRAINTS 注入段)。PRESERVE 只保「存在」不保「在场」:
6
+ * 段被删除/掏空后 sync-docs 重新生成会静默丢失,此处补「在场」校验。
7
+ *
8
+ * ADR-0001 存在性探测:项目未采用 harness 治理(无 .harness/config.yml)→ skip。
9
+ * 向后兼容:旧模型仓(CLAUDE.md 有治理块、AGENTS.md 无 PRESERVE:governance)→ pass,
10
+ * 不强制迁移;两处都没有才报违规。
11
+ */
12
+ import type { ConstraintCheck } from './types';
13
+ /** 治理契约 PRESERVE 段名(ADR 落点模型约定) */
14
+ export declare const GOVERNANCE_PRESERVE_NAME = "governance";
15
+ /** AGENTS.md 是否存在非空 PRESERVE:governance 块(标记须独占一行,与 preserve-block.ts 同语义) */
16
+ export declare function hasGovernancePreserveBlock(agentsMdPath: string): boolean;
17
+ /** CLAUDE.md 是否存在治理块(与 agents-syncer getGovernanceInfo 同判定,旧模型仓豁免) */
18
+ export declare function hasClaudeGovernance(claudeMdPath: string): boolean;
19
+ export declare const governancePresence: ConstraintCheck;
20
+ //# sourceMappingURL=governance-presence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"governance-presence.d.ts","sourceRoot":"","sources":["../../../../src/core/constraints/checkers/governance-presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,mCAAmC;AACnC,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAIrD,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAcxE;AAED,sEAAsE;AACtE,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAQjE;AAED,eAAO,MAAM,kBAAkB,EAAE,eAuBhC,CAAC"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * governance_presence:治理契约在场守护(studio #302,ADR 2026-08-21 落点模型)
4
+ *
5
+ * 治理契约正本 = AGENTS.md 手写 `PRESERVE:governance` 段(旧模型仓 = CLAUDE.md
6
+ * Governance Rules / HARNESS_CONSTRAINTS 注入段)。PRESERVE 只保「存在」不保「在场」:
7
+ * 段被删除/掏空后 sync-docs 重新生成会静默丢失,此处补「在场」校验。
8
+ *
9
+ * ADR-0001 存在性探测:项目未采用 harness 治理(无 .harness/config.yml)→ skip。
10
+ * 向后兼容:旧模型仓(CLAUDE.md 有治理块、AGENTS.md 无 PRESERVE:governance)→ pass,
11
+ * 不强制迁移;两处都没有才报违规。
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.governancePresence = exports.GOVERNANCE_PRESERVE_NAME = void 0;
15
+ exports.hasGovernancePreserveBlock = hasGovernancePreserveBlock;
16
+ exports.hasClaudeGovernance = hasClaudeGovernance;
17
+ const fs_1 = require("fs");
18
+ const path_1 = require("path");
19
+ const project_config_loader_1 = require("../../project-config-loader");
20
+ /** 治理契约 PRESERVE 段名(ADR 落点模型约定) */
21
+ exports.GOVERNANCE_PRESERVE_NAME = 'governance';
22
+ const GOVERNANCE_BEGIN_RE = /^<!-- PRESERVE:governance -->\s*$/m;
23
+ /** AGENTS.md 是否存在非空 PRESERVE:governance 块(标记须独占一行,与 preserve-block.ts 同语义) */
24
+ function hasGovernancePreserveBlock(agentsMdPath) {
25
+ let content;
26
+ try {
27
+ content = (0, fs_1.readFileSync)(agentsMdPath, 'utf-8');
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ const beginMatch = GOVERNANCE_BEGIN_RE.exec(content);
33
+ if (!beginMatch)
34
+ return false;
35
+ const rest = content.slice(beginMatch.index + beginMatch[0].length);
36
+ const endIdx = rest.indexOf(`<!-- /PRESERVE:${exports.GOVERNANCE_PRESERVE_NAME} -->`);
37
+ if (endIdx === -1)
38
+ return false;
39
+ // 块体(首尾标记之间)去空白后非空才算「在场」
40
+ return rest.slice(0, endIdx).trim().length > 0;
41
+ }
42
+ /** CLAUDE.md 是否存在治理块(与 agents-syncer getGovernanceInfo 同判定,旧模型仓豁免) */
43
+ function hasClaudeGovernance(claudeMdPath) {
44
+ let content;
45
+ try {
46
+ content = (0, fs_1.readFileSync)(claudeMdPath, 'utf-8');
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ return /^##\s+Governance Rules/m.test(content) || content.includes('HARNESS_CONSTRAINTS_START');
52
+ }
53
+ exports.governancePresence = {
54
+ id: 'governance_presence',
55
+ async evaluate(env) {
56
+ const projectPath = env.projectPath;
57
+ // 存在性探测:无 harness 配置 = 未采用治理约定 → skip(不计 pass/fail)
58
+ let adopted = false;
59
+ try {
60
+ adopted = (0, project_config_loader_1.loadRawProjectConfig)(projectPath) !== undefined;
61
+ }
62
+ catch {
63
+ adopted = false;
64
+ }
65
+ if (!adopted)
66
+ return 'skip';
67
+ if (hasGovernancePreserveBlock((0, path_1.join)(projectPath, 'AGENTS.md')))
68
+ return true;
69
+ if (hasClaudeGovernance((0, path_1.join)(projectPath, 'CLAUDE.md')))
70
+ return true;
71
+ console.error('[governance_presence] 治理契约缺失:AGENTS.md 无非空 PRESERVE:governance 段,' +
72
+ '且 CLAUDE.md 无 Governance Rules 块——约束正本静默丢失,请恢复其一');
73
+ return false;
74
+ },
75
+ };
76
+ //# sourceMappingURL=governance-presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"governance-presence.js","sourceRoot":"","sources":["../../../../src/core/constraints/checkers/governance-presence.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAaH,gEAcC;AAGD,kDAQC;AApCD,2BAAkC;AAClC,+BAA4B;AAC5B,uEAAmE;AAGnE,mCAAmC;AACtB,QAAA,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AAEjE,8EAA8E;AAC9E,SAAgB,0BAA0B,CAAC,YAAoB;IAC7D,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,IAAA,iBAAY,EAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,gCAAwB,MAAM,CAAC,CAAC;IAC9E,IAAI,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAChC,yBAAyB;IACzB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,sEAAsE;AACtE,SAAgB,mBAAmB,CAAC,YAAoB;IACtD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,IAAA,iBAAY,EAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC;AAClG,CAAC;AAEY,QAAA,kBAAkB,GAAoB;IACjD,EAAE,EAAE,qBAAqB;IACzB,KAAK,CAAC,QAAQ,CAAC,GAAG;QAChB,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;QAEpC,oDAAoD;QACpD,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,IAAA,4CAAoB,EAAC,WAAW,CAAC,KAAK,SAAS,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,OAAO,MAAM,CAAC;QAE5B,IAAI,0BAA0B,CAAC,IAAA,WAAI,EAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5E,IAAI,mBAAmB,CAAC,IAAA,WAAI,EAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAErE,OAAO,CAAC,KAAK,CACX,mEAAmE;YACnE,kDAAkD,CACnD,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/constraints/checkers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAuD/C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE1E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/constraints/checkers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAyD/C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE1E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC"}
@@ -20,6 +20,7 @@ const capability_sync_1 = require("./capability-sync");
20
20
  const context_doc_sync_1 = require("./context-doc-sync");
21
21
  const docs_freshness_1 = require("./docs-freshness");
22
22
  const no_hardcoded_credentials_1 = require("./no-hardcoded-credentials");
23
+ const governance_presence_1 = require("./governance-presence");
23
24
  const CHECKS = [
24
25
  // Iron Laws
25
26
  iron_flags_1.noCompletionWithoutVerification,
@@ -32,6 +33,7 @@ const CHECKS = [
32
33
  no_bypass_checkpoint_1.noBypassCheckpoint,
33
34
  capability_sync_1.capabilitySync,
34
35
  context_doc_sync_1.contextDocSync,
36
+ governance_presence_1.governancePresence,
35
37
  ];
36
38
  const registry = new Map(CHECKS.map(c => [c.id, c]));
37
39
  // ========================================
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/constraints/checkers/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AA6DH,gDAEC;AAKD,oDAEC;AAnED,gDAAmD;AAEnD,6CAIsB;AACtB,iEAA4D;AAC5D,qEAAgE;AAChE,uDAAmD;AACnD,yDAAoD;AACpD,qDAAiD;AACjD,yEAAoE;AAEpE,MAAM,MAAM,GAAsB;IAChC,YAAY;IACZ,4CAA+B;IAC/B,gCAAmB;IACnB,+CAAkC;IAClC,6CAAoB;IACpB,8BAAa;IACb,aAAa;IACb,iDAAsB;IACtB,yCAAkB;IAClB,gCAAc;IACd,iCAAc;CACf,CAAC;AAEF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAA0B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9E,2CAA2C;AAC3C,eAAe;AACf,2CAA2C;AAE3C,MAAM,gBAAgB,GAAG,IAAA,+BAAiB,GAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;AAE7E,KAAK,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;IACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,2CAA2C,CAAC,CAAC,EAAE,gBAAgB;YAC/D,6CAA6C,CAC9C,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;IACjC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACb,kCAAkC,EAAE,4BAA4B;YAChE,gCAAgC,CACjC,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,EAAU;IAC3C,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB;IAClC,OAAO,QAAQ,CAAC,IAAI,CAAC;AACvB,CAAC;AAED,iCAAwC;AAA/B,sGAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/constraints/checkers/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AA+DH,gDAEC;AAKD,oDAEC;AArED,gDAAmD;AAEnD,6CAIsB;AACtB,iEAA4D;AAC5D,qEAAgE;AAChE,uDAAmD;AACnD,yDAAoD;AACpD,qDAAiD;AACjD,yEAAoE;AACpE,+DAA2D;AAE3D,MAAM,MAAM,GAAsB;IAChC,YAAY;IACZ,4CAA+B;IAC/B,gCAAmB;IACnB,+CAAkC;IAClC,6CAAoB;IACpB,8BAAa;IACb,aAAa;IACb,iDAAsB;IACtB,yCAAkB;IAClB,gCAAc;IACd,iCAAc;IACd,wCAAkB;CACnB,CAAC;AAEF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAA0B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9E,2CAA2C;AAC3C,eAAe;AACf,2CAA2C;AAE3C,MAAM,gBAAgB,GAAG,IAAA,+BAAiB,GAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;AAE7E,KAAK,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;IACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,2CAA2C,CAAC,CAAC,EAAE,gBAAgB;YAC/D,6CAA6C,CAC9C,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;IACjC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACb,kCAAkC,EAAE,4BAA4B;YAChE,gCAAgC,CACjC,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,EAAU;IAC3C,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB;IAClC,OAAO,QAAQ,CAAC,IAAI,CAAC;AACvB,CAAC;AAED,iCAAwC;AAA/B,sGAAA,aAAa,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"guidelines.d.ts","sourceRoot":"","sources":["../../../../src/core/constraints/definitions/guidelines.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAwFjD,CAAC"}
1
+ {"version":3,"file":"guidelines.d.ts","sourceRoot":"","sources":["../../../../src/core/constraints/definitions/guidelines.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CA8GjD,CAAC"}
@@ -94,5 +94,26 @@ exports.GUIDELINES = {
94
94
  - 测试目录(__tests__、test)
95
95
  - 生成代码目录(dist、build、generated)`,
96
96
  },
97
+ /**
98
+ * 治理契约在场守护(studio #302,ADR 2026-08-21 落点模型)
99
+ * 原因:PRESERVE 只保「存在」不保「在场」——AGENTS.md 治理段被删除/掏空后
100
+ * sync-docs 重新生成会静默丢失约束正本
101
+ */
102
+ governance_presence: {
103
+ id: 'governance_presence',
104
+ kind: 'check',
105
+ rule: 'GOVERNANCE CONTRACT MUST BE PRESENT (AGENTS.MD PRESERVE:GOVERNANCE BLOCK OR CLAUDE.MD GOVERNANCE RULES)',
106
+ message: '治理契约缺失:AGENTS.md 缺少非空 PRESERVE:governance 段,且 CLAUDE.md 无 Governance Rules 块',
107
+ level: 'guideline',
108
+ trigger: ['file_modification', 'module_creation', 'module_modification', 'doc_update', 'config_change', 'commit'],
109
+ enforcement: 'governance-presence-check',
110
+ description: `治理契约(约束清单正本)必须在场,两处居其一:
111
+ - 新模型:AGENTS.md 手写 \`<!-- PRESERVE:governance -->\` 段(块体非空)
112
+ - 旧模型:CLAUDE.md 的 \`## Governance Rules\` 块 / HARNESS_CONSTRAINTS 注入段
113
+
114
+ PRESERVE 机制只保证「存在的段重新生成时保留」,不保证「段在场」——段被删除或掏空后,
115
+ sync-docs 重新生成会静默丢弃治理契约。本检查在 harness check 时校验在场性,防静默丢失。
116
+ 未采用 harness 治理(无 .harness/config.yml)的项目跳过评估(skip)。`,
117
+ },
97
118
  };
98
119
  //# sourceMappingURL=guidelines.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"guidelines.js","sourceRoot":"","sources":["../../../../src/core/constraints/definitions/guidelines.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAIU,QAAA,UAAU,GAA+B;IACpD;;;OAGG;IACH,wBAAwB,EAAE;QACxB,EAAE,EAAE,0BAA0B;QAC9B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,4EAA4E;QAClF,OAAO,EAAE,sCAAsC;QAC/C,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,CAAC,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,CAAC;QACzE,WAAW,EAAE,iBAAiB;QAC9B,WAAW,EAAE,4KAA4K;QACzL,eAAe,EAAE,sDAAsD;KACxE;IAED;;;OAGG;IACH,oBAAoB,EAAE;QACpB,EAAE,EAAE,sBAAsB;QAC1B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,WAAW;QACpB,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,qBAAqB;QAC9B,WAAW,EAAE,qBAAqB;QAClC,WAAW,EAAE,mCAAmC;QAChD,eAAe,EAAE,8EAA8E;KAChG;IAED;;;OAGG;IACH,eAAe,EAAE;QACf,EAAE,EAAE,iBAAiB;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,0CAA0C;QAChD,OAAO,EAAE,6BAA6B;QACtC,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,CAAC,iBAAiB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,kBAAkB,CAAC;QAC1F,WAAW,EAAE,qBAAqB;QAClC,WAAW,EAAE;;;;;;;;;;;;;yFAawE;KACtF;IAED;;;OAGG;IACH,gBAAgB,EAAE;QAChB,EAAE,EAAE,kBAAkB;QACtB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,wCAAwC;QAC9C,OAAO,EAAE,oDAAoD;QAC7D,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,qBAAqB;QAC9B,WAAW,EAAE,eAAe;QAC5B,WAAW,EAAE;;;;;;;;;;;;;+BAac;KAC5B;CACF,CAAC"}
1
+ {"version":3,"file":"guidelines.js","sourceRoot":"","sources":["../../../../src/core/constraints/definitions/guidelines.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAIU,QAAA,UAAU,GAA+B;IACpD;;;OAGG;IACH,wBAAwB,EAAE;QACxB,EAAE,EAAE,0BAA0B;QAC9B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,4EAA4E;QAClF,OAAO,EAAE,sCAAsC;QAC/C,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,CAAC,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,CAAC;QACzE,WAAW,EAAE,iBAAiB;QAC9B,WAAW,EAAE,4KAA4K;QACzL,eAAe,EAAE,sDAAsD;KACxE;IAED;;;OAGG;IACH,oBAAoB,EAAE;QACpB,EAAE,EAAE,sBAAsB;QAC1B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,WAAW;QACpB,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,qBAAqB;QAC9B,WAAW,EAAE,qBAAqB;QAClC,WAAW,EAAE,mCAAmC;QAChD,eAAe,EAAE,8EAA8E;KAChG;IAED;;;OAGG;IACH,eAAe,EAAE;QACf,EAAE,EAAE,iBAAiB;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,0CAA0C;QAChD,OAAO,EAAE,6BAA6B;QACtC,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,CAAC,iBAAiB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,kBAAkB,CAAC;QAC1F,WAAW,EAAE,qBAAqB;QAClC,WAAW,EAAE;;;;;;;;;;;;;yFAawE;KACtF;IAED;;;OAGG;IACH,gBAAgB,EAAE;QAChB,EAAE,EAAE,kBAAkB;QACtB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,wCAAwC;QAC9C,OAAO,EAAE,oDAAoD;QAC7D,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,qBAAqB;QAC9B,WAAW,EAAE,eAAe;QAC5B,WAAW,EAAE;;;;;;;;;;;;;+BAac;KAC5B;IAED;;;;OAIG;IACH,mBAAmB,EAAE;QACnB,EAAE,EAAE,qBAAqB;QACzB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,yGAAyG;QAC/G,OAAO,EAAE,8EAA8E;QACvF,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,CAAC,mBAAmB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,CAAC;QACjH,WAAW,EAAE,2BAA2B;QACxC,WAAW,EAAE;;;;;;oDAMmC;KACjD;CACF,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CLAUDE.md 约束注入段漂移校验(ADR-0001 决策 7)
2
+ * 治理约束注入段漂移校验(ADR-0001 决策 7)
3
3
  *
4
4
  * 检测三类漂移:
5
5
  * 1. 版本漂移:标记段内 `<!-- version: x -->` ≠ 当前 harness 包版本
@@ -9,15 +9,24 @@
9
9
  * 3. 重复章节:标记段之外还存在另一个 `## Governance Rules` 标题
10
10
  * (init 旧版本在无标记时追加第二个同名章节的历史问题)
11
11
  *
12
+ * 落点路由(studio #307,ADR 2026-08-21 落点模型):注入段可能在
13
+ * CLAUDE.md(旧模型仓)或 AGENTS.md `PRESERVE:governance` 段内(新模型仓),
14
+ * 与 init 的 setupGovernanceConstraints 路由一致——CLAUDE.md 有标记优先。
15
+ *
12
16
  * 纯检测、只读:不改任何文件。check 仅警告不阻断,详细差异进 report。
13
- * 文件无标记段 = 未注入,不算漂移(report 一句话提示,check 不警告)。
17
+ * 两处均无标记段 = 未注入,不算漂移(report 一句话提示,check 不警告)。
14
18
  */
19
+ /** 注入段落点文件名(检测顺序即路由优先级:旧模型仓 CLAUDE.md 豁免优先) */
20
+ declare const INJECTION_FILES: readonly ["CLAUDE.md", "AGENTS.md"];
21
+ export type InjectionFile = (typeof INJECTION_FILES)[number];
15
22
  /** 注入漂移检测结果 */
16
23
  export interface InjectionDrift {
17
24
  /** 是否存在任一漂移(版本/内容/重复章节) */
18
25
  hasDrift: boolean;
19
- /** CLAUDE.md 不存在或无约束标记段(未注入,不算漂移) */
26
+ /** CLAUDE.md / AGENTS.md 均无约束标记段(未注入,不算漂移) */
20
27
  notInjected: boolean;
28
+ /** 注入段落点文件(未注入时 undefined) */
29
+ injectionFile?: InjectionFile;
21
30
  /** 版本漂移:注入段版本 ≠ 当前 harness 版本 */
22
31
  versionDrift?: {
23
32
  expected: string;
@@ -39,10 +48,26 @@ export interface InjectionDrift {
39
48
  /** 修复指引(重跑 init 幂等同步注入段) */
40
49
  export declare const INJECTION_DRIFT_FIX_HINT = "\u91CD\u8DD1 `npx @dommaker/harness init` \u540C\u6B65\u6CE8\u5165\u6BB5\uFF08\u6F02\u79FB\u4EC5\u8B66\u544A\uFF0C\u4E0D\u963B\u65AD\uFF09";
41
50
  /**
42
- * 检测 CLAUDE.md 约束注入段漂移
51
+ * 解析治理约束注入段落点(studio #307,ADR 2026-08-21 落点模型)
52
+ *
53
+ * 与 init 的 setupGovernanceConstraints 路由一致:CLAUDE.md 含标记段优先
54
+ * (旧模型仓豁免),否则看 AGENTS.md(新模型仓注入段在 PRESERVE:governance 内);
55
+ * 两处均无完整标记段 → null(未注入)。
56
+ *
57
+ * detectInjectionDrift 与 constraints retire 的注入段同步共用本路由。
58
+ */
59
+ export declare function resolveInjectionTarget(projectRoot: string): {
60
+ file: InjectionFile;
61
+ content: string;
62
+ startIdx: number;
63
+ endIdx: number;
64
+ } | null;
65
+ /**
66
+ * 检测约束注入段漂移
43
67
  *
44
68
  * @param projectRoot 项目根路径
45
69
  * @param currentVersion 当前 harness 版本(缺省读 package.json;测试可显式传入)
46
70
  */
47
71
  export declare function detectInjectionDrift(projectRoot: string, currentVersion?: string): InjectionDrift;
72
+ export {};
48
73
  //# sourceMappingURL=injection-drift.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"injection-drift.d.ts","sourceRoot":"","sources":["../../../src/core/constraints/injection-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,eAAe;AACf,MAAM,WAAW,cAAc;IAC7B,2BAA2B;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,WAAW,EAAE,OAAO,CAAC;IACrB,iCAAiC;IACjC,YAAY,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD;;;OAGG;IACH,YAAY,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACtD,2CAA2C;IAC3C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,4BAA4B;AAC5B,eAAO,MAAM,wBAAwB,+IAAqD,CAAC;AA+B3F;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,cAAc,GAAE,MAA4B,GAC3C,cAAc,CAsDhB"}
1
+ {"version":3,"file":"injection-drift.d.ts","sourceRoot":"","sources":["../../../src/core/constraints/injection-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAWH,+CAA+C;AAC/C,QAAA,MAAM,eAAe,qCAAsC,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAe;AACf,MAAM,WAAW,cAAc;IAC7B,2BAA2B;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,8CAA8C;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iCAAiC;IACjC,YAAY,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD;;;OAGG;IACH,YAAY,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACtD,2CAA2C;IAC3C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,4BAA4B;AAC5B,eAAO,MAAM,wBAAwB,+IAAqD,CAAC;AAwC3F;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,GAClB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAWnF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,cAAc,GAAE,MAA4B,GAC3C,cAAc,CAkDhB"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * CLAUDE.md 约束注入段漂移校验(ADR-0001 决策 7)
3
+ * 治理约束注入段漂移校验(ADR-0001 决策 7)
4
4
  *
5
5
  * 检测三类漂移:
6
6
  * 1. 版本漂移:标记段内 `<!-- version: x -->` ≠ 当前 harness 包版本
@@ -10,8 +10,12 @@
10
10
  * 3. 重复章节:标记段之外还存在另一个 `## Governance Rules` 标题
11
11
  * (init 旧版本在无标记时追加第二个同名章节的历史问题)
12
12
  *
13
+ * 落点路由(studio #307,ADR 2026-08-21 落点模型):注入段可能在
14
+ * CLAUDE.md(旧模型仓)或 AGENTS.md `PRESERVE:governance` 段内(新模型仓),
15
+ * 与 init 的 setupGovernanceConstraints 路由一致——CLAUDE.md 有标记优先。
16
+ *
13
17
  * 纯检测、只读:不改任何文件。check 仅警告不阻断,详细差异进 report。
14
- * 文件无标记段 = 未注入,不算漂移(report 一句话提示,check 不警告)。
18
+ * 两处均无标记段 = 未注入,不算漂移(report 一句话提示,check 不警告)。
15
19
  */
16
20
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
21
  if (k2 === undefined) k2 = k;
@@ -48,11 +52,14 @@ var __importStar = (this && this.__importStar) || (function () {
48
52
  })();
49
53
  Object.defineProperty(exports, "__esModule", { value: true });
50
54
  exports.INJECTION_DRIFT_FIX_HINT = void 0;
55
+ exports.resolveInjectionTarget = resolveInjectionTarget;
51
56
  exports.detectInjectionDrift = detectInjectionDrift;
52
57
  const fs = __importStar(require("fs"));
53
58
  const path = __importStar(require("path"));
54
59
  const effective_constraints_1 = require("../effective-constraints");
55
60
  const injection_renderer_1 = require("./injection-renderer");
61
+ /** 注入段落点文件名(检测顺序即路由优先级:旧模型仓 CLAUDE.md 豁免优先) */
62
+ const INJECTION_FILES = ['CLAUDE.md', 'AGENTS.md'];
56
63
  /** 修复指引(重跑 init 幂等同步注入段) */
57
64
  exports.INJECTION_DRIFT_FIX_HINT = '重跑 `npx @dommaker/harness init` 同步注入段(漂移仅警告,不阻断)';
58
65
  const VERSION_LINE_RE = /<!-- version: ([^ ]+) -->/;
@@ -82,8 +89,39 @@ function significantLines(section) {
82
89
  .map(l => l.trim())
83
90
  .filter(l => ENTRY_LINE_RE.test(l) || GROUP_HEADING_RE.test(l));
84
91
  }
92
+ function readIfExists(filePath) {
93
+ try {
94
+ const content = fs.readFileSync(filePath, 'utf-8');
95
+ return typeof content === 'string' ? content : null;
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
85
101
  /**
86
- * 检测 CLAUDE.md 约束注入段漂移
102
+ * 解析治理约束注入段落点(studio #307,ADR 2026-08-21 落点模型)
103
+ *
104
+ * 与 init 的 setupGovernanceConstraints 路由一致:CLAUDE.md 含标记段优先
105
+ * (旧模型仓豁免),否则看 AGENTS.md(新模型仓注入段在 PRESERVE:governance 内);
106
+ * 两处均无完整标记段 → null(未注入)。
107
+ *
108
+ * detectInjectionDrift 与 constraints retire 的注入段同步共用本路由。
109
+ */
110
+ function resolveInjectionTarget(projectRoot) {
111
+ for (const file of INJECTION_FILES) {
112
+ const content = readIfExists(path.join(projectRoot, file));
113
+ if (content === null)
114
+ continue;
115
+ const startIdx = content.indexOf(injection_renderer_1.CONSTRAINTS_START_MARKER);
116
+ const endIdx = content.indexOf(injection_renderer_1.CONSTRAINTS_END_MARKER);
117
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
118
+ return { file, content, startIdx, endIdx };
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+ /**
124
+ * 检测约束注入段漂移
87
125
  *
88
126
  * @param projectRoot 项目根路径
89
127
  * @param currentVersion 当前 harness 版本(缺省读 package.json;测试可显式传入)
@@ -95,29 +133,23 @@ function detectInjectionDrift(projectRoot, currentVersion = getPackageVersion())
95
133
  duplicateHeading: false,
96
134
  fixHint: exports.INJECTION_DRIFT_FIX_HINT,
97
135
  };
98
- const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
99
- let content;
100
- try {
101
- content = fs.readFileSync(claudeMdPath, 'utf-8');
102
- }
103
- catch {
104
- result.notInjected = true;
105
- return result;
106
- }
107
- if (typeof content !== 'string') {
136
+ const target = resolveInjectionTarget(projectRoot);
137
+ if (!target) {
138
+ // 两处均无标记段 = 未注入,不算漂移(check 不警告)。
139
+ // 重复章节仍如实记录供 report 提示:旧落点 CLAUDE.md 优先,其次 AGENTS.md。
140
+ const legacy = readIfExists(path.join(projectRoot, 'CLAUDE.md')) ??
141
+ readIfExists(path.join(projectRoot, 'AGENTS.md'));
142
+ if (legacy !== null) {
143
+ result.duplicateHeading = (legacy.match(GOVERNANCE_HEADING_RE) ?? []).length > 1;
144
+ }
108
145
  result.notInjected = true;
109
146
  return result;
110
147
  }
148
+ result.injectionFile = target.file;
149
+ const { content, startIdx, endIdx } = target;
111
150
  // 重复章节:全文统计 `## Governance Rules` 标题数(含标记段所属的合法标题)
112
151
  const headingCount = (content.match(GOVERNANCE_HEADING_RE) ?? []).length;
113
152
  result.duplicateHeading = headingCount > 1;
114
- const startIdx = content.indexOf(injection_renderer_1.CONSTRAINTS_START_MARKER);
115
- const endIdx = content.indexOf(injection_renderer_1.CONSTRAINTS_END_MARKER);
116
- if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
117
- // 无标记段 = 未注入,不算漂移(check 不警告;duplicateHeading 仍如实记录供 report 提示)
118
- result.notInjected = true;
119
- return result;
120
- }
121
153
  const actualSection = content.slice(startIdx, endIdx + injection_renderer_1.CONSTRAINTS_END_MARKER.length);
122
154
  // 1. 版本漂移
123
155
  const versionMatch = VERSION_LINE_RE.exec(actualSection);
@@ -1 +1 @@
1
- {"version":3,"file":"injection-drift.js","sourceRoot":"","sources":["../../../src/core/constraints/injection-drift.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEH,oDAyDC;AA3HD,uCAAyB;AACzB,2CAA6B;AAC7B,oEAAmE;AACnE,6DAI8B;AAqB9B,4BAA4B;AACf,QAAA,wBAAwB,GAAG,kDAAkD,CAAC;AAE3F,MAAM,eAAe,GAAG,2BAA2B,CAAC;AACpD,MAAM,aAAa,GAAG,qBAAqB,CAAC;AAC5C,MAAM,gBAAgB,GAAG,UAAU,CAAC;AACpC,MAAM,qBAAqB,GAAG,+BAA+B,CAAC;AAE9D;;GAEG;AACH,SAAS,iBAAiB;IACxB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QACvE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC,OAAO,IAAI,SAAS,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,OAAe;IACvC,OAAO,OAAO;SACX,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAClC,WAAmB,EACnB,iBAAyB,iBAAiB,EAAE;IAE5C,MAAM,MAAM,GAAmB;QAC7B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,KAAK;QAClB,gBAAgB,EAAE,KAAK;QACvB,OAAO,EAAE,gCAAwB;KAClC,CAAC;IAEF,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACzD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,mDAAmD;IACnD,MAAM,YAAY,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,CAAC,gBAAgB,GAAG,YAAY,GAAG,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,6CAAwB,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,2CAAsB,CAAC,CAAC;IACvD,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,IAAI,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC3D,+DAA+D;QAC/D,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,2CAAsB,CAAC,MAAM,CAAC,CAAC;IAEtF,UAAU;IACV,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC9D,IAAI,aAAa,KAAK,cAAc,EAAE,CAAC;QACrC,MAAM,CAAC,YAAY,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAC5E,CAAC;IAED,2BAA2B;IAC3B,MAAM,eAAe,GAAG,IAAA,6CAAwB,EAAC,IAAA,+CAAuB,EAAC,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC;IACvG,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,CAAC,YAAY,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,MAAM,CAAC,gBAAgB,CAAC;IAC1G,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"injection-drift.js","sourceRoot":"","sources":["../../../src/core/constraints/injection-drift.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dommaker/harness",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "通用工程约束框架 - 铁律系统、检查点验证、测试门控",
5
5
  "keywords": [
6
6
  "harness",
@@ -0,0 +1,129 @@
1
+ /**
2
+ * governance_presence checker 测试(studio #302,ADR 2026-08-21 落点模型)
3
+ *
4
+ * 在场守护:PRESERVE 只保「存在」不保「在场」——治理契约段被删除/掏空后
5
+ * sync-docs 重新生成会静默丢失,本 checker 在 harness check 时校验在场性。
6
+ */
7
+
8
+ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+ import {
12
+ governancePresence,
13
+ hasGovernancePreserveBlock,
14
+ hasClaudeGovernance,
15
+ } from '../core/constraints/checkers/governance-presence';
16
+ import type { CheckEnv } from '../core/constraints/checkers';
17
+
18
+ function makeEnv(projectPath: string): CheckEnv {
19
+ return {
20
+ context: { operation: 'commit', projectPath },
21
+ projectPath,
22
+ stagedDiff: async () => '',
23
+ stagedDiffNames: async () => '',
24
+ srcScan: () => [],
25
+ };
26
+ }
27
+
28
+ const GOVERNANCE_BLOCK = [
29
+ '<!-- PRESERVE:governance -->',
30
+ '## Governance Rules',
31
+ '',
32
+ '<!-- HARNESS_CONSTRAINTS_START -->',
33
+ '- **law_a**: 描述 a',
34
+ '<!-- HARNESS_CONSTRAINTS_END -->',
35
+ '<!-- /PRESERVE:governance -->',
36
+ ].join('\n');
37
+
38
+ describe('governance_presence checker', () => {
39
+ let tempDir: string;
40
+ let errorSpy: jest.SpiedFunction<typeof console.error>;
41
+
42
+ const writeConfig = () => {
43
+ fs.mkdirSync(path.join(tempDir, '.harness'), { recursive: true });
44
+ fs.writeFileSync(path.join(tempDir, '.harness', 'config.yml'), 'preset: standard\n');
45
+ };
46
+
47
+ beforeEach(() => {
48
+ tempDir = fs.mkdtempSync(path.join(process.cwd(), 'temp-test-gov-presence-'));
49
+ errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
50
+ });
51
+
52
+ afterEach(() => {
53
+ jest.restoreAllMocks();
54
+ fs.rmSync(tempDir, { recursive: true, force: true });
55
+ });
56
+
57
+ it('未采用 harness 治理(无 .harness/config.yml)→ skip', async () => {
58
+ expect(await governancePresence.evaluate(makeEnv(tempDir))).toBe('skip');
59
+ });
60
+
61
+ it('新模型:AGENTS.md 有非空 PRESERVE:governance 段 → pass', async () => {
62
+ writeConfig();
63
+ fs.writeFileSync(path.join(tempDir, 'AGENTS.md'), `# AGENTS.md\n\n${GOVERNANCE_BLOCK}\n`);
64
+ expect(await governancePresence.evaluate(makeEnv(tempDir))).toBe(true);
65
+ });
66
+
67
+ it('旧模型豁免:CLAUDE.md 有 Governance Rules 块(无 AGENTS.md)→ pass', async () => {
68
+ writeConfig();
69
+ fs.writeFileSync(path.join(tempDir, 'CLAUDE.md'), '# CLAUDE.md\n\n## Governance Rules\n\n条款\n');
70
+ expect(await governancePresence.evaluate(makeEnv(tempDir))).toBe(true);
71
+ });
72
+
73
+ it('旧模型豁免:CLAUDE.md 有 HARNESS_CONSTRAINTS 标记 → pass', async () => {
74
+ writeConfig();
75
+ fs.writeFileSync(path.join(tempDir, 'CLAUDE.md'), '<!-- HARNESS_CONSTRAINTS_START -->\nx\n');
76
+ expect(await governancePresence.evaluate(makeEnv(tempDir))).toBe(true);
77
+ });
78
+
79
+ it('两处都没有治理契约 → fail 并报警(防静默丢失)', async () => {
80
+ writeConfig();
81
+ fs.writeFileSync(path.join(tempDir, 'AGENTS.md'), '# AGENTS.md\n\n只有机器生成内容\n');
82
+ fs.writeFileSync(path.join(tempDir, 'CLAUDE.md'), '# CLAUDE.md\n\n@AGENTS.md\n');
83
+ expect(await governancePresence.evaluate(makeEnv(tempDir))).toBe(false);
84
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('治理契约缺失'));
85
+ });
86
+
87
+ it('PRESERVE:governance 段为空(被掏空)且 CLAUDE.md 无治理块 → fail', async () => {
88
+ writeConfig();
89
+ fs.writeFileSync(
90
+ path.join(tempDir, 'AGENTS.md'),
91
+ '# AGENTS.md\n\n<!-- PRESERVE:governance -->\n\n<!-- /PRESERVE:governance -->\n'
92
+ );
93
+ expect(await governancePresence.evaluate(makeEnv(tempDir))).toBe(false);
94
+ });
95
+
96
+ it('PRESERVE:governance 标记未闭合视为不在场 → fail', async () => {
97
+ writeConfig();
98
+ fs.writeFileSync(
99
+ path.join(tempDir, 'AGENTS.md'),
100
+ '# AGENTS.md\n\n<!-- PRESERVE:governance -->\n## Governance Rules\n内容\n'
101
+ );
102
+ expect(await governancePresence.evaluate(makeEnv(tempDir))).toBe(false);
103
+ });
104
+ });
105
+
106
+ describe('hasGovernancePreserveBlock / hasClaudeGovernance 判定函数', () => {
107
+ let tempDir: string;
108
+
109
+ beforeEach(() => {
110
+ tempDir = fs.mkdtempSync(path.join(process.cwd(), 'temp-test-gov-helpers-'));
111
+ });
112
+
113
+ afterEach(() => {
114
+ fs.rmSync(tempDir, { recursive: true, force: true });
115
+ });
116
+
117
+ it('文件不存在 → false', () => {
118
+ expect(hasGovernancePreserveBlock(path.join(tempDir, 'AGENTS.md'))).toBe(false);
119
+ expect(hasClaudeGovernance(path.join(tempDir, 'CLAUDE.md'))).toBe(false);
120
+ });
121
+
122
+ it('块体仅空白/注释外无内容 → false;有内容 → true', () => {
123
+ const p = path.join(tempDir, 'AGENTS.md');
124
+ fs.writeFileSync(p, '<!-- PRESERVE:governance -->\n \n<!-- /PRESERVE:governance -->\n');
125
+ expect(hasGovernancePreserveBlock(p)).toBe(false);
126
+ fs.writeFileSync(p, `<!-- PRESERVE:governance -->\n条款一\n<!-- /PRESERVE:governance -->\n`);
127
+ expect(hasGovernancePreserveBlock(p)).toBe(true);
128
+ });
129
+ });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 约束系统测试(ADR-0001:kind 二元模型,25 条清单构成 + 注册表闭环)
2
+ * 约束系统测试(ADR-0001:kind 二元模型,26 条清单构成 + 注册表闭环)
3
3
  */
4
4
 
5
5
  import { describe, it, expect } from '@jest/globals';
@@ -16,17 +16,17 @@ import { getConstraintCheck, registeredCheckCount } from '../core/constraints/ch
16
16
  import type { ConstraintContext } from '../types/constraint';
17
17
 
18
18
  describe('Constraint System', () => {
19
- describe('清单构成(ADR-0001:42 → 25)', () => {
20
- it('getAllConstraints 返回 25 条:9 check + 16 prompt', () => {
19
+ describe('清单构成(ADR-0001:42 → 26)', () => {
20
+ it('getAllConstraints 返回 26 条:10 check + 16 prompt', () => {
21
21
  const all = getAllConstraints();
22
- expect(all).toHaveLength(25);
23
- expect(all.filter(c => c.kind === 'check')).toHaveLength(9);
22
+ expect(all).toHaveLength(26);
23
+ expect(all.filter(c => c.kind === 'check')).toHaveLength(10);
24
24
  expect(all.filter(c => c.kind === 'prompt')).toHaveLength(16);
25
25
  });
26
26
 
27
- it('check 层 = 5 iron + 4 guideline', () => {
27
+ it('check 层 = 5 iron + 5 guideline', () => {
28
28
  expect(Object.keys(IRON_LAWS)).toHaveLength(5);
29
- expect(Object.keys(GUIDELINES)).toHaveLength(4);
29
+ expect(Object.keys(GUIDELINES)).toHaveLength(5);
30
30
  Object.values(IRON_LAWS).forEach(c => {
31
31
  expect(c.kind).toBe('check');
32
32
  expect(c.level).toBe('iron_law');
@@ -12,7 +12,7 @@ H5(#44)起:
12
12
  - 各命令文件:check / validate / passes-gate / init / report / status / spec / sync-docs / knowledge / sdd / failure / posteval-plan / release / analyze-sessions / update-user-model / constraints / doc-freshness-check / spec-baseline-check
13
13
  - 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`,形状同为 `CommandDefinition`,ADR-0007)
14
14
 
15
- `constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom 落 custom-constraints.yml 条目 retired 段(#82 D6 一处真相)+ KnowledgeStore 沉淀 + CLAUDE.md 注入段同步)。
15
+ `constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom 落 custom-constraints.yml 条目 retired 段(#82 D6 一处真相)+ KnowledgeStore 沉淀 + 治理注入段同步)。
16
16
 
17
17
  ## 依赖关系
18
18
  - 依赖 `src/core/constraints/` 约束引擎
@@ -30,5 +30,8 @@ H5(#44)起:
30
30
 
31
31
  ## 注意事项
32
32
  - 新增命令需同步更新 CLAUDE.md / CAPABILITIES.md / src/CONTEXT.md
33
+ - init 治理约束段写入落点(studio #302,ADR 2026-08-21 落点模型):新仓 → AGENTS.md `PRESERVE:governance` 段(sync-docs 重新生成时保留);旧模型仓(CLAUDE.md 已有 HARNESS_CONSTRAINTS 标记或 `## Governance Rules` 块)→ 续写 CLAUDE.md,幂等重跑不制造双份正本。治理段「在场守护」由 core 侧 governance_presence checker 承担(段缺失/为空时 check 报警)
34
+ - PRESERVE:governance 段内写入纪律:机器管理的只有 HARNESS_CONSTRAINTS 标记区间——有标记只换标记区间,无标记(纯手写段)在段尾追加注入段;段内其余手写内容(治理契约引言/流程/纪律等)必须原样保留(曾整段替换清空手写内容的回归,init-injection.test 有防回归用例)
35
+ - 注入段读取侧同一路由(studio #307):`resolveInjectionTarget`(core/constraints/injection-drift)按「CLAUDE.md 有标记优先,否则 AGENTS.md」定位注入段,`detectInjectionDrift` 与 `constraints retire` 的注入段同步共用;未注入 = 两处均无完整标记段
33
36
  - knowledge 命令包含 13 个子操作(list/search/import/decay/stats/upsert/sync-status/sync-rag/audit/snapshot/migrate/index/health)
34
37
  - 特殊路由(选项条件、子命令兜底、退出码处理、裸跑语义)表达在定义表的 optionRoutes / subcommands / subcommandStrict / bareRunsAction / afterRun 字段,bin 是纯通用引擎、不含单命令知识
@@ -57,14 +57,14 @@ describe('buildConstraintsUsageReport', () => {
57
57
  const report = buildConstraintsUsageReport(root);
58
58
 
59
59
  expect(report.traceFileExists).toBe(false);
60
- expect(report.stats.length).toBe(9); // 5 iron + 4 guideline
60
+ expect(report.stats.length).toBe(10); // 5 iron + 5 guideline
61
61
  for (const s of report.stats) {
62
62
  expect(s.total).toBe(0);
63
63
  expect(s.evaluated).toBe(0);
64
64
  expect(s.failRate).toBe(0);
65
65
  expect(s.firstAt).toBeUndefined();
66
66
  }
67
- expect(report.candidates.length).toBe(9);
67
+ expect(report.candidates.length).toBe(10);
68
68
  expect(report.candidates.every(c => c.kind === 'zero_trigger')).toBe(true);
69
69
  // prompt 注入清单(standard preset、无 scenes → 14 条通用 prompt)
70
70
  expect(report.activePromptIds.length).toBe(14);