@dommaker/harness 1.11.0 → 1.12.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.
Files changed (137) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cli/commands/constraints-pack-proposal.d.ts +48 -0
  3. package/dist/cli/commands/constraints-pack-proposal.d.ts.map +1 -0
  4. package/dist/cli/commands/constraints-pack-proposal.js +169 -0
  5. package/dist/cli/commands/constraints-pack-proposal.js.map +1 -0
  6. package/dist/cli/commands/constraints-reactivate.d.ts +55 -0
  7. package/dist/cli/commands/constraints-reactivate.d.ts.map +1 -0
  8. package/dist/cli/commands/constraints-reactivate.js +175 -0
  9. package/dist/cli/commands/constraints-reactivate.js.map +1 -0
  10. package/dist/cli/commands/constraints-report.d.ts +12 -1
  11. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  12. package/dist/cli/commands/constraints-report.js +35 -2
  13. package/dist/cli/commands/constraints-report.js.map +1 -1
  14. package/dist/cli/commands/constraints-retire.d.ts +49 -4
  15. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  16. package/dist/cli/commands/constraints-retire.js +116 -34
  17. package/dist/cli/commands/constraints-retire.js.map +1 -1
  18. package/dist/cli/commands/definitions.d.ts.map +1 -1
  19. package/dist/cli/commands/definitions.js +31 -1
  20. package/dist/cli/commands/definitions.js.map +1 -1
  21. package/dist/cli/commands/knowledge-view.d.ts +6 -3
  22. package/dist/cli/commands/knowledge-view.d.ts.map +1 -1
  23. package/dist/cli/commands/knowledge-view.js +8 -26
  24. package/dist/cli/commands/knowledge-view.js.map +1 -1
  25. package/dist/cli/commands/report.d.ts.map +1 -1
  26. package/dist/cli/commands/report.js +9 -5
  27. package/dist/cli/commands/report.js.map +1 -1
  28. package/dist/cli/state-io.d.ts +8 -1
  29. package/dist/cli/state-io.d.ts.map +1 -1
  30. package/dist/cli/state-io.js +2 -1
  31. package/dist/cli/state-io.js.map +1 -1
  32. package/dist/core/app-constraints-loader.d.ts +28 -0
  33. package/dist/core/app-constraints-loader.d.ts.map +1 -0
  34. package/dist/core/app-constraints-loader.js +187 -0
  35. package/dist/core/app-constraints-loader.js.map +1 -0
  36. package/dist/core/constraints/checker.js +1 -1
  37. package/dist/core/constraints/checker.js.map +1 -1
  38. package/dist/core/constraints/checkers/index.d.ts +20 -9
  39. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  40. package/dist/core/constraints/checkers/index.js +40 -11
  41. package/dist/core/constraints/checkers/index.js.map +1 -1
  42. package/dist/core/constraints/checkers/templated/file-exists.d.ts +16 -0
  43. package/dist/core/constraints/checkers/templated/file-exists.d.ts.map +1 -0
  44. package/dist/core/constraints/checkers/templated/file-exists.js +108 -0
  45. package/dist/core/constraints/checkers/templated/file-exists.js.map +1 -0
  46. package/dist/core/constraints/checkers/templated/glob.d.ts +10 -0
  47. package/dist/core/constraints/checkers/templated/glob.d.ts.map +1 -0
  48. package/dist/core/constraints/checkers/templated/glob.js +51 -0
  49. package/dist/core/constraints/checkers/templated/glob.js.map +1 -0
  50. package/dist/core/constraints/checkers/templated/index.d.ts +12 -0
  51. package/dist/core/constraints/checkers/templated/index.d.ts.map +1 -0
  52. package/dist/core/constraints/checkers/templated/index.js +17 -0
  53. package/dist/core/constraints/checkers/templated/index.js.map +1 -0
  54. package/dist/core/constraints/checkers/templated/regex-scan.d.ts +15 -0
  55. package/dist/core/constraints/checkers/templated/regex-scan.d.ts.map +1 -0
  56. package/dist/core/constraints/checkers/templated/regex-scan.js +129 -0
  57. package/dist/core/constraints/checkers/templated/regex-scan.js.map +1 -0
  58. package/dist/core/constraints/checkers/templates-registry.d.ts +17 -0
  59. package/dist/core/constraints/checkers/templates-registry.d.ts.map +1 -0
  60. package/dist/core/constraints/checkers/templates-registry.js +23 -0
  61. package/dist/core/constraints/checkers/templates-registry.js.map +1 -0
  62. package/dist/core/constraints/checkers/types.d.ts +14 -0
  63. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  64. package/dist/core/constraints/checkers/types.js.map +1 -1
  65. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  66. package/dist/core/constraints/definitions/guidelines.js +0 -14
  67. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  68. package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
  69. package/dist/core/constraints/definitions/iron-laws.js +15 -0
  70. package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
  71. package/dist/core/constraints/usage-report.d.ts +58 -6
  72. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  73. package/dist/core/constraints/usage-report.js +61 -12
  74. package/dist/core/constraints/usage-report.js.map +1 -1
  75. package/dist/core/project-config-loader.d.ts +9 -6
  76. package/dist/core/project-config-loader.d.ts.map +1 -1
  77. package/dist/core/project-config-loader.js +18 -8
  78. package/dist/core/project-config-loader.js.map +1 -1
  79. package/dist/gates/command.d.ts.map +1 -1
  80. package/dist/gates/command.js +50 -0
  81. package/dist/gates/command.js.map +1 -1
  82. package/dist/knowledge/lifecycle.d.ts +14 -0
  83. package/dist/knowledge/lifecycle.d.ts.map +1 -1
  84. package/dist/knowledge/lifecycle.js +50 -0
  85. package/dist/knowledge/lifecycle.js.map +1 -1
  86. package/dist/presets/standard.d.ts +1 -1
  87. package/dist/presets/standard.d.ts.map +1 -1
  88. package/dist/presets/standard.js +2 -3
  89. package/dist/presets/standard.js.map +1 -1
  90. package/dist/types/constraint.d.ts +24 -0
  91. package/dist/types/constraint.d.ts.map +1 -1
  92. package/dist/types/constraint.js.map +1 -1
  93. package/package.json +1 -1
  94. package/src/__tests__/app-constraints-loader.test.ts +220 -0
  95. package/src/__tests__/iron-laws.test.ts +5 -5
  96. package/src/cli/commands/CONTEXT.md +3 -2
  97. package/src/cli/commands/__tests__/check.test.ts +22 -4
  98. package/src/cli/commands/__tests__/constraints-pack-proposal.test.ts +142 -0
  99. package/src/cli/commands/__tests__/constraints-reactivate.test.ts +210 -0
  100. package/src/cli/commands/__tests__/constraints-report.test.ts +175 -6
  101. package/src/cli/commands/__tests__/constraints-retire.test.ts +135 -10
  102. package/src/cli/commands/__tests__/knowledge.test.ts +6 -16
  103. package/src/cli/commands/__tests__/report-app-constraints.test.ts +67 -0
  104. package/src/cli/commands/constraints-pack-proposal.ts +172 -0
  105. package/src/cli/commands/constraints-reactivate.ts +197 -0
  106. package/src/cli/commands/constraints-report.ts +44 -2
  107. package/src/cli/commands/constraints-retire.ts +129 -36
  108. package/src/cli/commands/definitions.ts +31 -1
  109. package/src/cli/commands/knowledge-view.ts +9 -28
  110. package/src/cli/commands/report.ts +9 -5
  111. package/src/cli/state-io.ts +8 -1
  112. package/src/core/CONTEXT.md +5 -4
  113. package/src/core/app-constraints-loader.ts +165 -0
  114. package/src/core/constraints/__tests__/collect-constraints.test.ts +3 -1
  115. package/src/core/constraints/__tests__/usage-report.test.ts +63 -0
  116. package/src/core/constraints/checker.ts +1 -1
  117. package/src/core/constraints/checkers/__tests__/templated-checkers.test.ts +180 -0
  118. package/src/core/constraints/checkers/__tests__/templates-registry.test.ts +81 -0
  119. package/src/core/constraints/checkers/index.ts +44 -10
  120. package/src/core/constraints/checkers/templated/file-exists.ts +75 -0
  121. package/src/core/constraints/checkers/templated/glob.ts +46 -0
  122. package/src/core/constraints/checkers/templated/index.ts +12 -0
  123. package/src/core/constraints/checkers/templated/regex-scan.ts +132 -0
  124. package/src/core/constraints/checkers/templates-registry.ts +22 -0
  125. package/src/core/constraints/checkers/types.ts +15 -0
  126. package/src/core/constraints/definitions/guidelines.ts +0 -15
  127. package/src/core/constraints/definitions/iron-laws.ts +16 -0
  128. package/src/core/constraints/usage-report.ts +128 -13
  129. package/src/core/project-config-loader.ts +19 -8
  130. package/src/gates/__tests__/command.test.ts +54 -0
  131. package/src/gates/command.ts +51 -0
  132. package/src/knowledge/CONTEXT.md +3 -1
  133. package/src/knowledge/__tests__/lifecycle.test.ts +81 -0
  134. package/src/knowledge/lifecycle.ts +49 -0
  135. package/src/presets/__tests__/standard.test.ts +5 -4
  136. package/src/presets/standard.ts +2 -3
  137. package/src/types/constraint.ts +28 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.12.0] - 2026-09-23
10
+
11
+ ### Changes
12
+ - docs(capabilities): sync-docs 修正 severity 计数——no_hardcoded_credentials 升 error 级后 errors 4 / warnings 3
13
+ - feat(gates): 命令黑名单补 git 破坏类 7 条——强推/clean -f 阻断,reset --hard 等警告(ADR-0031,wayfinder 票08 P1-6)
14
+ - feat(knowledge): checkConstraintCandidate 知识→约束候选判定(ADR-0033,块3 子项6)
15
+ - feat(constraints): constraints pack-proposal 升级提案打包命令(ADR-0033,块3 子项5)
16
+ - feat(constraints): 零拦截观察名单中间态——列入挂 90 天,期满且样本足才转退役候选(ADR-0032/0033,块3 子项4)
17
+ - test(constraints): 修预存红——32c07ed 升 error 级后 code_implementation 域无 warning 约束,改 module_modification 域
18
+ - feat(constraints): 退休/报告执行面覆盖应用层约束(ADR-0033,块3 子项3)
19
+ - feat(constraints): 填空式 checker 模板 regex-scan / file-exists(ADR-0033,块3 子项2)
20
+ - feat(constraints): 两层约束模型——类型扩展 + .harness/constraints.yml 加载链 + 模板注册表闭环(ADR-0033,块3 子项1)
21
+ - refactor(knowledge): 知识库目录收编——旧 ~/.studio/knowledge 兼容沿用退役(ADR-0034,票 02 断点 1)
22
+ - feat(constraints): 退休幂等只认墓碑 + constraints reactivate 复活命令 + 落盘 commit 提示(ADR-0032 决策 6.4-6.6,票 02 断点 4/5/6)
23
+ - docs(adr-0031): 修订——撤销'高危不派 kimi'派单策略,改一视同仁(能力快照会烂 + 主流 CLI 信任模型)
24
+ - feat(constraints)!: no_hardcoded_credentials 升 error 级——安全底线 warning 不阻断等于没有(ADR-0032,wayfinder 票08 P0-1)
25
+ - docs(positioning): harness 重新定位——一页纸原则 + ADR-0031~0034(wayfinder harness-repositioning 地图 9 票定案)
26
+ - fix(constraints): constraints retire 知识沉淀写口走 openKnowledgeStore 统一解析点(#177)
27
+ - docs(knowledge): 沉淀 list() 过滤层=index.json 与 tag append 去重纪律(studio#614 审计连带)
28
+
9
29
  ## [1.11.0] - 2026-09-21
10
30
 
11
31
  ### Changes
@@ -0,0 +1,48 @@
1
+ /**
2
+ * harness constraints pack-proposal <id> —— 升级提案材料打包(ADR-0033 决策 4,块 3 子项 5)
3
+ *
4
+ * 升级通道链路:studio 审卡发起/初审 → approve 后由本命令打包脱敏材料 →
5
+ * 人拿材料去 harness 仓开 issue(github 操作留给人,本命令不触网)。
6
+ *
7
+ * 材料内容:条文 + severity + checker 模板与参数 + traces.log 使用统计(只有计数)
8
+ * + 升级理由留白段。
9
+ *
10
+ * 脱敏口径(工具做路径脱敏,内容脱敏归人):
11
+ * - 项目根绝对路径一律替换为 `<repoRoot>`;
12
+ * - 统计只有计数与首末次日期,不带样本内容;
13
+ * - 应用层约束的 params(正则/glob)**原文带出**并标注"请人工确认无应用内部信息"——
14
+ * 打包工具是质量前置,不是保密闸门。
15
+ */
16
+ import type { Constraint } from '../../types/constraint';
17
+ import { type CommandIO, type CommandResult } from '../command-contract';
18
+ export interface PackProposalOptions {
19
+ projectPath?: string;
20
+ /** 打印到 stdout 而不落盘 */
21
+ stdout?: boolean;
22
+ /** 日期注入(测试用;缺省真实时钟) */
23
+ now?: Date;
24
+ }
25
+ /** 提案材料的输入(查找结果 + 使用统计聚合) */
26
+ export interface ProposalMaterial {
27
+ constraint: Constraint;
28
+ stats: {
29
+ total: number;
30
+ pass: number;
31
+ fail: number;
32
+ skip: number;
33
+ evaluated: number;
34
+ firstAt?: number;
35
+ lastAt?: number;
36
+ };
37
+ }
38
+ /**
39
+ * 渲染脱敏提案材料 markdown(纯函数,导出供测试)
40
+ *
41
+ * @param projectRoot 用于路径脱敏(材料中出现的项目根绝对路径替换为 `<repoRoot>`)
42
+ */
43
+ export declare function renderProposalMarkdown(material: ProposalMaterial, projectRoot: string, now?: Date): string;
44
+ /**
45
+ * CLI handler: harness constraints pack-proposal <id>
46
+ */
47
+ export declare function constraintsPackProposal(id: string | undefined, options?: PackProposalOptions, io?: CommandIO): Promise<CommandResult>;
48
+ //# sourceMappingURL=constraints-pack-proposal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints-pack-proposal.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/constraints-pack-proposal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAA4B,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEnG,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uBAAuB;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,6BAA6B;AAC7B,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAgBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,gBAAgB,EAC1B,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,IAAiB,GACrB,MAAM,CAmDR;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,OAAO,GAAE,mBAAwB,EACjC,EAAE,GAAE,SAAqB,GACxB,OAAO,CAAC,aAAa,CAAC,CAwCxB"}
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ /**
3
+ * harness constraints pack-proposal <id> —— 升级提案材料打包(ADR-0033 决策 4,块 3 子项 5)
4
+ *
5
+ * 升级通道链路:studio 审卡发起/初审 → approve 后由本命令打包脱敏材料 →
6
+ * 人拿材料去 harness 仓开 issue(github 操作留给人,本命令不触网)。
7
+ *
8
+ * 材料内容:条文 + severity + checker 模板与参数 + traces.log 使用统计(只有计数)
9
+ * + 升级理由留白段。
10
+ *
11
+ * 脱敏口径(工具做路径脱敏,内容脱敏归人):
12
+ * - 项目根绝对路径一律替换为 `<repoRoot>`;
13
+ * - 统计只有计数与首末次日期,不带样本内容;
14
+ * - 应用层约束的 params(正则/glob)**原文带出**并标注"请人工确认无应用内部信息"——
15
+ * 打包工具是质量前置,不是保密闸门。
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ var __importDefault = (this && this.__importDefault) || function (mod) {
51
+ return (mod && mod.__esModule) ? mod : { "default": mod };
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.renderProposalMarkdown = renderProposalMarkdown;
55
+ exports.constraintsPackProposal = constraintsPackProposal;
56
+ const fs = __importStar(require("fs"));
57
+ const path = __importStar(require("path"));
58
+ const yaml = __importStar(require("js-yaml"));
59
+ const chalk_1 = __importDefault(require("chalk"));
60
+ const definitions_1 = require("../../core/constraints/definitions");
61
+ const app_constraints_loader_1 = require("../../core/app-constraints-loader");
62
+ const usage_report_1 = require("../../core/constraints/usage-report");
63
+ const command_contract_1 = require("../command-contract");
64
+ /**
65
+ * 查找约束定义(内置 + 应用层,与 findRetireTarget 同口径但带全量字段——
66
+ * checker/params 是提案材料的核心内容,RetireTargetInfo 不带)
67
+ */
68
+ function findProposalTarget(id, projectRoot) {
69
+ const builtIn = (0, definitions_1.getConstraint)(id);
70
+ if (builtIn)
71
+ return builtIn;
72
+ return (0, app_constraints_loader_1.loadAppConstraints)(projectRoot).find(c => c.id === id);
73
+ }
74
+ function formatDate(ts) {
75
+ return ts === undefined ? '(无记录)' : new Date(ts).toISOString().slice(0, 10);
76
+ }
77
+ /**
78
+ * 渲染脱敏提案材料 markdown(纯函数,导出供测试)
79
+ *
80
+ * @param projectRoot 用于路径脱敏(材料中出现的项目根绝对路径替换为 `<repoRoot>`)
81
+ */
82
+ function renderProposalMarkdown(material, projectRoot, now = new Date()) {
83
+ const c = material.constraint;
84
+ const s = material.stats;
85
+ const isApp = c.source === 'app';
86
+ const lines = [];
87
+ lines.push(`# 约束升级提案材料:${c.id}`);
88
+ lines.push('');
89
+ lines.push(`- 生成日期: ${now.toISOString().slice(0, 10)}`);
90
+ lines.push(`- 来源: ${isApp ? '应用层(.harness/constraints.yml)' : '内置'}`);
91
+ lines.push(`- severity: ${c.severity}`);
92
+ lines.push('');
93
+ lines.push('## 条文');
94
+ lines.push('');
95
+ lines.push(`rule: ${c.rule}`);
96
+ if (c.message && c.message !== c.rule)
97
+ lines.push(`message: ${c.message}`);
98
+ if (c.description)
99
+ lines.push(`description: ${c.description}`);
100
+ lines.push('');
101
+ lines.push('## checker');
102
+ lines.push('');
103
+ if (isApp) {
104
+ lines.push(`模板: \`${c.checker}\``);
105
+ lines.push('');
106
+ lines.push('参数(原文带出,**请人工确认无应用内部信息**——工具只做路径脱敏,内容脱敏归人):');
107
+ lines.push('');
108
+ lines.push('```yaml');
109
+ lines.push(yaml.dump(c.params ?? {}, { lineWidth: 120 }).trimEnd());
110
+ lines.push('```');
111
+ }
112
+ else {
113
+ lines.push('内置定制 checker(按约束 id 注册,非参数化模板)。升级到通用层前需评审:');
114
+ lines.push('改写为模板 + 参数,或作为通用规则保留定制实现。');
115
+ }
116
+ lines.push('');
117
+ lines.push('## 使用统计(traces.log 聚合,只有计数)');
118
+ lines.push('');
119
+ lines.push(`- total: ${s.total}`);
120
+ lines.push(`- evaluated: ${s.evaluated}(pass ${s.pass} / fail ${s.fail} / skip ${s.skip})`);
121
+ lines.push(`- 首次触发: ${formatDate(s.firstAt)}`);
122
+ lines.push(`- 最近触发: ${formatDate(s.lastAt)}`);
123
+ lines.push('');
124
+ lines.push('## 升级理由');
125
+ lines.push('');
126
+ lines.push('> (请在此填写:为什么这条约束该进通用层——适用面、证据、与其他仓的相关性)');
127
+ lines.push('');
128
+ // 路径脱敏:项目根绝对路径一律替换
129
+ return lines.join('\n').split(projectRoot).join('<repoRoot>');
130
+ }
131
+ /**
132
+ * CLI handler: harness constraints pack-proposal <id>
133
+ */
134
+ async function constraintsPackProposal(id, options = {}, io = command_contract_1.processIO) {
135
+ const projectRoot = options.projectPath || process.cwd();
136
+ if (!id) {
137
+ (0, command_contract_1.logError)(io, '❌ 缺少约束 id:harness constraints pack-proposal <id>');
138
+ return { kind: 'usage-error', reason: '缺少约束 id' };
139
+ }
140
+ const constraint = findProposalTarget(id, projectRoot);
141
+ if (!constraint) {
142
+ (0, command_contract_1.logError)(io, `❌ 未知约束 id: ${id}(内置与应用层 constraints.yml 均未找到),未做任何变更`);
143
+ return { kind: 'usage-error', reason: `未知约束 id: ${id}` };
144
+ }
145
+ const usage = (0, usage_report_1.collectUsageByConstraint)((0, usage_report_1.readProjectTraces)(projectRoot)).get(id);
146
+ const stats = {
147
+ total: usage?.total ?? 0,
148
+ pass: usage?.pass ?? 0,
149
+ fail: usage?.fail ?? 0,
150
+ skip: usage?.skip ?? 0,
151
+ evaluated: (usage?.total ?? 0) - (usage?.skip ?? 0),
152
+ firstAt: usage?.firstAt,
153
+ lastAt: usage?.lastAt,
154
+ };
155
+ const now = options.now ?? new Date();
156
+ const markdown = renderProposalMarkdown({ constraint, stats }, projectRoot, now);
157
+ if (options.stdout) {
158
+ (0, command_contract_1.log)(io, markdown);
159
+ return { kind: 'ok' };
160
+ }
161
+ const date = now.toISOString().slice(0, 10).replace(/-/g, '');
162
+ const outPath = path.join(projectRoot, '.harness', 'reports', `proposal-${id}-${date}.md`);
163
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
164
+ fs.writeFileSync(outPath, markdown, 'utf-8');
165
+ (0, command_contract_1.log)(io, chalk_1.default.green(`✅ 提案材料已生成: ${outPath}`));
166
+ (0, command_contract_1.log)(io, chalk_1.default.gray(' 下一步:人工确认 params 无应用内部信息后,拿材料去 harness 仓开 issue(不自动开)'));
167
+ return { kind: 'ok' };
168
+ }
169
+ //# sourceMappingURL=constraints-pack-proposal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints-pack-proposal.js","sourceRoot":"","sources":["../../../src/cli/commands/constraints-pack-proposal.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDH,wDAuDC;AAKD,0DA4CC;AA3JD,uCAAyB;AACzB,2CAA6B;AAC7B,8CAAgC;AAChC,kDAA0B;AAC1B,oEAAmE;AACnE,8EAAuE;AACvE,sEAAkG;AAElG,0DAAmG;AAwBnG;;;GAGG;AACH,SAAS,kBAAkB,CAAC,EAAU,EAAE,WAAmB;IACzD,MAAM,OAAO,GAAG,IAAA,2BAAa,EAAC,EAAE,CAAC,CAAC;IAClC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,OAAO,IAAA,2CAAkB,EAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,UAAU,CAAC,EAAsB;IACxC,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,SAAgB,sBAAsB,CACpC,QAA0B,EAC1B,WAAmB,EACnB,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC;IAC9B,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC;IAEjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACxD,KAAK,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACtE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9B,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3E,IAAI,CAAC,CAAC,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAC1C,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAClC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IAC5F,KAAK,CAAC,IAAI,CAAC,WAAW,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/C,KAAK,CAAC,IAAI,CAAC,WAAW,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC9C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,mBAAmB;IACnB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,uBAAuB,CAC3C,EAAsB,EACtB,UAA+B,EAAE,EACjC,KAAgB,4BAAS;IAEzB,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAEzD,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,IAAA,2BAAQ,EAAC,EAAE,EAAE,kDAAkD,CAAC,CAAC;QACjE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,IAAA,2BAAQ,EAAC,EAAE,EAAE,cAAc,EAAE,sCAAsC,CAAC,CAAC;QACrE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,KAAK,GAAG,IAAA,uCAAwB,EAAC,IAAA,gCAAiB,EAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG;QACZ,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;QACxB,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;QACtB,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;QACtB,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;QACtB,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,CAAC;QACnD,OAAO,EAAE,KAAK,EAAE,OAAO;QACvB,MAAM,EAAE,KAAK,EAAE,MAAM;KACtB,CAAC;IAEF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,sBAAsB,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;IAEjF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,IAAA,sBAAG,EAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,IAAI,KAAK,CAAC,CAAC;IAC3F,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAA,sBAAG,EAAC,EAAE,EAAE,eAAK,CAAC,KAAK,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC,CAAC;IAC9C,IAAA,sBAAG,EAAC,EAAE,EAAE,eAAK,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC,CAAC;IAC/E,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * harness constraints reactivate —— 约束复活(ADR-0032 决策 6.5,票 02 断点 5)
3
+ *
4
+ * 复活 = 撤销退休:删 config.yml `constraints.<id>` 墓碑段(与既有"恢复方法"语义一致),
5
+ * 同时写一条 KnowledgeStore 新条目 `constraint-reactivated-<id>`(consumptionMode: 'signal')
6
+ * ——不改历史:旧 `constraint-retired-<id>` 沉淀原样保留,复活原因单独成条。
7
+ *
8
+ * 幂等口径与 retire 对偶:只认 retired 墓碑。裸 enabled:false(禁用非退休)与
9
+ * 从未退休的约束一律 not_retired,不落盘任何文件。
10
+ *
11
+ * 人确认闸门与 retire 同形(ADR-0001 决策 2 语义):直达必须显式 --yes。
12
+ * 不提供交互批量模式——复活是低频点对点操作。
13
+ */
14
+ import { type CommandIO, type CommandResult } from '../command-contract';
15
+ export interface ReactivateExecuteOptions {
16
+ /** 复活原因(可空) */
17
+ reason?: string;
18
+ /** 注入当前时间(测试用) */
19
+ now?: Date;
20
+ /** 知识库路径解析的 io(缺省 processIO,与 retire 写口同形) */
21
+ io?: CommandIO;
22
+ }
23
+ export type ReactivateStatus = 'reactivated' | 'not_retired' | 'unknown_id';
24
+ export interface ReactivateResult {
25
+ id: string;
26
+ status: ReactivateStatus;
27
+ /** KnowledgeStore 条目 id(status='reactivated' 时存在) */
28
+ knowledgeEntryId?: string;
29
+ /** 复活记录实际落盘的知识库根(status='reactivated' 时存在) */
30
+ knowledgeBaseDir?: string;
31
+ }
32
+ export interface ConstraintsReactivateOptions {
33
+ projectPath?: string;
34
+ reason?: string;
35
+ /** 直达模式显式确认(--yes):与 retire 同一道人确认闸门,无此 flag 直达拒绝执行 */
36
+ yes?: boolean;
37
+ }
38
+ /**
39
+ * 执行单条约束复活(纯执行逻辑,无交互)
40
+ *
41
+ * 不存在的 id / 无 retired 墓碑的 id 通过 status 返回,由调用方提示。
42
+ */
43
+ export declare function reactivateConstraint(projectRoot: string, id: string, options?: ReactivateExecuteOptions): ReactivateResult;
44
+ /**
45
+ * 打印单条复活结果
46
+ */
47
+ export declare function printReactivateResult(result: ReactivateResult, io?: CommandIO, projectRoot?: string): CommandResult;
48
+ /**
49
+ * CLI handler: harness constraints reactivate <id>
50
+ *
51
+ * 人确认闸门与 retire 同形:直达必须显式 `--yes`,无 `--yes` 报错 + 非零退出码,
52
+ * 不落盘任何文件。
53
+ */
54
+ export declare function constraintsReactivate(id?: string, options?: ConstraintsReactivateOptions, io?: CommandIO): Promise<CommandResult>;
55
+ //# sourceMappingURL=constraints-reactivate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints-reactivate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/constraints-reactivate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAA4B,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOnG,MAAM,WAAW,wBAAwB;IACvC,eAAe;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB;IAClB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,8CAA8C;IAC9C,EAAE,CAAC,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,aAAa,GAAG,YAAY,CAAC;AAE5E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,gBAAgB,CAAC;IACzB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA0DD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,wBAA6B,GACrC,gBAAgB,CA8BlB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,GAAE,SAAqB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,aAAa,CAgB9H;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,EAAE,CAAC,EAAE,MAAM,EACX,OAAO,GAAE,4BAAiC,EAC1C,EAAE,GAAE,SAAqB,GACxB,OAAO,CAAC,aAAa,CAAC,CAmBxB"}
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ /**
3
+ * harness constraints reactivate —— 约束复活(ADR-0032 决策 6.5,票 02 断点 5)
4
+ *
5
+ * 复活 = 撤销退休:删 config.yml `constraints.<id>` 墓碑段(与既有"恢复方法"语义一致),
6
+ * 同时写一条 KnowledgeStore 新条目 `constraint-reactivated-<id>`(consumptionMode: 'signal')
7
+ * ——不改历史:旧 `constraint-retired-<id>` 沉淀原样保留,复活原因单独成条。
8
+ *
9
+ * 幂等口径与 retire 对偶:只认 retired 墓碑。裸 enabled:false(禁用非退休)与
10
+ * 从未退休的约束一律 not_retired,不落盘任何文件。
11
+ *
12
+ * 人确认闸门与 retire 同形(ADR-0001 决策 2 语义):直达必须显式 --yes。
13
+ * 不提供交互批量模式——复活是低频点对点操作。
14
+ */
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
48
+ var __importDefault = (this && this.__importDefault) || function (mod) {
49
+ return (mod && mod.__esModule) ? mod : { "default": mod };
50
+ };
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ exports.reactivateConstraint = reactivateConstraint;
53
+ exports.printReactivateResult = printReactivateResult;
54
+ exports.constraintsReactivate = constraintsReactivate;
55
+ const path = __importStar(require("path"));
56
+ const command_contract_1 = require("../command-contract");
57
+ const chalk_1 = __importDefault(require("chalk"));
58
+ const project_config_loader_1 = require("../../core/project-config-loader");
59
+ const knowledge_view_1 = require("./knowledge-view");
60
+ const constraints_retire_1 = require("./constraints-retire");
61
+ /**
62
+ * 写 KnowledgeStore 复活记录(consumptionMode: 'signal')
63
+ *
64
+ * baseDir 走 openKnowledgeStore 同一解析点,与 retire 沉淀写口同根。
65
+ */
66
+ function saveReactivateKnowledge(id, target, reason, iso, io) {
67
+ const entryId = `constraint-reactivated-${id}`;
68
+ const contentLines = [
69
+ `# 约束复活:${id}`,
70
+ '',
71
+ `原退役沉淀:constraint-retired-${id}(不改历史,原条目保留)`,
72
+ '',
73
+ '## 规则原文',
74
+ '',
75
+ target.description ? `description: ${target.description}` : undefined,
76
+ target.rule ? `rule: ${target.rule}` : undefined,
77
+ target.message ? `message: ${target.message}` : undefined,
78
+ '',
79
+ '## 复活原因',
80
+ '',
81
+ reason || '(未填写)',
82
+ '',
83
+ `复活日期: ${iso}`,
84
+ ].filter((l) => l !== undefined);
85
+ const entry = {
86
+ id: entryId,
87
+ type: 'decision',
88
+ title: `约束复活:${id}`,
89
+ content: contentLines.join('\n'),
90
+ maturity: 'verified',
91
+ layer: 'project',
92
+ created: iso,
93
+ lastReferenced: iso,
94
+ contributors: [],
95
+ projects: [],
96
+ tags: ['constraint-reactivated', `constraint:${id}`, `severity:${target.severity}`],
97
+ applicablePhases: [],
98
+ sourceReferences: [{ timestamp: iso }],
99
+ referencedBy: [],
100
+ executionResults: [],
101
+ consumptionMode: 'signal',
102
+ origin: 'human',
103
+ };
104
+ const store = (0, knowledge_view_1.openKnowledgeStore)({}, io);
105
+ store.save(entry);
106
+ return { entryId, baseDir: store.getBaseDir() };
107
+ }
108
+ /**
109
+ * 执行单条约束复活(纯执行逻辑,无交互)
110
+ *
111
+ * 不存在的 id / 无 retired 墓碑的 id 通过 status 返回,由调用方提示。
112
+ */
113
+ function reactivateConstraint(projectRoot, id, options = {}) {
114
+ const now = options.now ?? new Date();
115
+ const iso = now.toISOString();
116
+ const target = (0, constraints_retire_1.findRetireTarget)(id, projectRoot);
117
+ if (!target) {
118
+ return { id, status: 'unknown_id' };
119
+ }
120
+ // 只认 retired 墓碑:裸 disable 与未配置一律 not_retired(与 retire 的幂等口径对偶)
121
+ const loader = new project_config_loader_1.ProjectConfigLoader(projectRoot);
122
+ loader.load();
123
+ const existing = loader.getConfig().constraints?.[id];
124
+ if (!(existing?.enabled === false && existing.retired)) {
125
+ return { id, status: 'not_retired' };
126
+ }
127
+ // 1. 删 config.yml constraints.<id> 墓碑段(恢复 = 回生效集)
128
+ (0, constraints_retire_1.removeYamlEntry)(path.join(projectRoot, '.harness', 'config.yml'), 'constraints', id);
129
+ // 2. KnowledgeStore 复活条目(不改历史,新写一条)
130
+ const { entryId: knowledgeEntryId, baseDir: knowledgeBaseDir } = saveReactivateKnowledge(id, target, options.reason ?? '', iso, options.io ?? command_contract_1.processIO);
131
+ return { id, status: 'reactivated', knowledgeEntryId, knowledgeBaseDir };
132
+ }
133
+ /**
134
+ * 打印单条复活结果
135
+ */
136
+ function printReactivateResult(result, io = command_contract_1.processIO, projectRoot) {
137
+ switch (result.status) {
138
+ case 'unknown_id':
139
+ (0, command_contract_1.log)(io, chalk_1.default.red(`❌ ${result.id}: 约束不存在(内置与应用层约束中都未找到),未做任何变更`));
140
+ return { kind: 'skip', reason: `${result.id}: 约束不存在,未做任何变更` };
141
+ case 'not_retired':
142
+ (0, command_contract_1.log)(io, chalk_1.default.yellow(`⚠️ ${result.id}: 无 retired 墓碑(未退休或仅裸 disable),未做任何变更`));
143
+ return { kind: 'skip', reason: `${result.id}: 无 retired 墓碑,未做任何变更` };
144
+ case 'reactivated': {
145
+ (0, command_contract_1.log)(io, chalk_1.default.green(`✅ ${result.id}: 已复活(config.yml 墓碑段已删,约束回到生效集)`));
146
+ (0, command_contract_1.log)(io, ` 复活沉淀: ${result.knowledgeEntryId}(${result.knowledgeBaseDir})`);
147
+ if (projectRoot)
148
+ (0, constraints_retire_1.logCommitHint)(projectRoot, io);
149
+ break;
150
+ }
151
+ }
152
+ return { kind: 'ok' };
153
+ }
154
+ /**
155
+ * CLI handler: harness constraints reactivate <id>
156
+ *
157
+ * 人确认闸门与 retire 同形:直达必须显式 `--yes`,无 `--yes` 报错 + 非零退出码,
158
+ * 不落盘任何文件。
159
+ */
160
+ async function constraintsReactivate(id, options = {}, io = command_contract_1.processIO) {
161
+ const projectRoot = options.projectPath || process.cwd();
162
+ if (!id) {
163
+ (0, command_contract_1.logError)(io, chalk_1.default.red('❌ 用法:harness constraints reactivate <id> --yes [--reason "..."](复活是点对点操作,无交互模式)'));
164
+ return { kind: 'usage-error', reason: 'reactivate 缺少约束 id,未做任何变更' };
165
+ }
166
+ if (!options.yes) {
167
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`❌ 直达复活需要显式人确认(与 retire 同一闸门),未做任何变更\n`) +
168
+ ` 带 --yes 显式确认直达:harness constraints reactivate ${id} --yes` +
169
+ `${options.reason ? ` --reason "${options.reason}"` : ''}`);
170
+ return { kind: 'usage-error', reason: `直达复活 ${id} 缺少显式 --yes 人确认,未做任何变更` };
171
+ }
172
+ const result = reactivateConstraint(projectRoot, id, { reason: options.reason, io });
173
+ return printReactivateResult(result, io, projectRoot);
174
+ }
175
+ //# sourceMappingURL=constraints-reactivate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints-reactivate.js","sourceRoot":"","sources":["../../../src/cli/commands/constraints-reactivate.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * harness constraints report —— 约束使用报告(ADR-0001 决策 3/4,ADR-0029 收窄)
3
3
  *
4
- * 只读。面向"人审约束"场景:
4
+ * 面向"人审约束"场景:
5
5
  * - check 约束统计表(total/pass/fail/skip、fail 率、首次/最近触发)
6
6
  * - 退役候选诊断(零触发/零拦截/不可评估/高噪),retire 交互模式的数据源
7
+ * - 零拦截观察名单(ADR-0032,块 3 子项 4):零拦截命中先进观察名单挂一个季度,
8
+ * 进 report 但不进退役候选,期满且样本足才转候选;名单状态经 StateIO 读-改-写
9
+ * `.harness/.state.json`(本命令因此不再是纯只读——唯一写点是观察名单列入)
7
10
  * - 配置健康(unknownIds 残留提示)
8
11
  * - --export [file]:脱敏 markdown 摘要(不含项目路径)
9
12
  */
10
13
  import { type ConstraintsUsageReport } from '../../core/constraints/usage-report';
14
+ import { type StateIO } from '../state-io';
11
15
  import { type CommandIO, type CommandResult } from '../command-contract';
12
16
  export interface ConstraintsReportOptions {
13
17
  projectPath?: string;
@@ -20,6 +24,13 @@ export interface ConstraintsReportOptions {
20
24
  noiseFailRate?: number;
21
25
  /** 高噪候选最小评估样本数 */
22
26
  noiseMinTotal?: number;
27
+ /**
28
+ * 观察名单状态接缝(缺省 = fileStateIO(projectPath))。
29
+ * 注入则 `.harness/.state.json` 的读写全程走注入面(ADR-0026 同口径)。
30
+ */
31
+ stateIO?: StateIO;
32
+ /** 观察期判定时钟(Unix ms),测试注入用——别在测试里等 90 天 */
33
+ now?: number;
23
34
  }
24
35
  /**
25
36
  * 渲染脱敏 markdown 摘要(ADR-0001 决策 4)
@@ -1 +1 @@
1
- {"version":3,"file":"constraints-report.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/constraints-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAIL,KAAK,sBAAsB,EAG5B,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAkB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzF,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,oBAAoB;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAiBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,IAAiB,GACrB,MAAM,CA4CR;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,EAAE,EAAE,GAAE,SAAqB,GAAG,OAAO,CAAC,aAAa,CAAC,CAsEjI"}
1
+ {"version":3,"file":"constraints-report.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/constraints-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAKL,KAAK,sBAAsB,EAG5B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAkB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzF,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,oBAAoB;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAiBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,IAAiB,GACrB,MAAM,CAwDR;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,EAAE,EAAE,GAAE,SAAqB,GAAG,OAAO,CAAC,aAAa,CAAC,CAwFjI"}
@@ -2,9 +2,12 @@
2
2
  /**
3
3
  * harness constraints report —— 约束使用报告(ADR-0001 决策 3/4,ADR-0029 收窄)
4
4
  *
5
- * 只读。面向"人审约束"场景:
5
+ * 面向"人审约束"场景:
6
6
  * - check 约束统计表(total/pass/fail/skip、fail 率、首次/最近触发)
7
7
  * - 退役候选诊断(零触发/零拦截/不可评估/高噪),retire 交互模式的数据源
8
+ * - 零拦截观察名单(ADR-0032,块 3 子项 4):零拦截命中先进观察名单挂一个季度,
9
+ * 进 report 但不进退役候选,期满且样本足才转候选;名单状态经 StateIO 读-改-写
10
+ * `.harness/.state.json`(本命令因此不再是纯只读——唯一写点是观察名单列入)
8
11
  * - 配置健康(unknownIds 残留提示)
9
12
  * - --export [file]:脱敏 markdown 摘要(不含项目路径)
10
13
  */
@@ -51,6 +54,7 @@ const fs = __importStar(require("fs"));
51
54
  const path = __importStar(require("path"));
52
55
  const chalk_1 = __importDefault(require("chalk"));
53
56
  const usage_report_1 = require("../../core/constraints/usage-report");
57
+ const state_io_1 = require("../state-io");
54
58
  const constraints_1 = require("./constraints");
55
59
  const command_contract_1 = require("../command-contract");
56
60
  function formatTime(ts) {
@@ -101,6 +105,18 @@ function renderExportMarkdown(report, version, now = new Date()) {
101
105
  }
102
106
  }
103
107
  lines.push('');
108
+ // 观察名单(块 3 子项 4):进报告不进候选,列入时间与剩余观察期随条目带出
109
+ lines.push('## 观察名单');
110
+ lines.push('');
111
+ if (report.watchlist.length === 0) {
112
+ lines.push('(空)');
113
+ }
114
+ else {
115
+ for (const w of report.watchlist) {
116
+ lines.push(`- \`${w.id}\` — ${w.reason}(列入 ${w.listedAt.slice(0, 10)},剩余观察 ${w.remainingDays} 天;满 ${usage_report_1.WATCHLIST_PERIOD_DAYS} 天且样本足转退役候选)`);
117
+ }
118
+ }
119
+ lines.push('');
104
120
  if (report.lint.unknownIds.length > 0) {
105
121
  lines.push('## 配置健康');
106
122
  lines.push('');
@@ -123,7 +139,16 @@ async function constraintsReport(options = {}, io = command_contract_1.processIO
123
139
  thresholds.highNoiseFailRate = options.noiseFailRate;
124
140
  if (options.noiseMinTotal !== undefined)
125
141
  thresholds.highNoiseMinEvaluated = options.noiseMinTotal;
126
- const report = (0, usage_report_1.buildConstraintsUsageReport)(projectRoot, thresholds);
142
+ const stateIO = options.stateIO ?? (0, state_io_1.fileStateIO)(projectRoot);
143
+ const state = stateIO.read();
144
+ const report = (0, usage_report_1.buildConstraintsUsageReport)(projectRoot, thresholds, {
145
+ watchlist: state.constraintWatchlist,
146
+ now: options.now,
147
+ });
148
+ // 观察名单只增不删:键数变多 = 本次有新列入,经 StateIO 写回(读-改-写,不动其他字段)
149
+ if (Object.keys(report.nextWatchlist).length > Object.keys(state.constraintWatchlist ?? {}).length) {
150
+ stateIO.write({ ...state, constraintWatchlist: report.nextWatchlist });
151
+ }
127
152
  if (options.json) {
128
153
  (0, command_contract_1.log)(io, JSON.stringify(report, null, 2));
129
154
  return { kind: 'ok' };
@@ -154,6 +179,14 @@ async function constraintsReport(options = {}, io = command_contract_1.processIO
154
179
  (0, command_contract_1.log)(io, chalk_1.default.gray(' 可运行 `harness constraints retire` 交互式处理候选'));
155
180
  }
156
181
  (0, command_contract_1.log)(io);
182
+ // 观察名单分组(零拦截中间态,块 3 子项 4):无条目零噪声
183
+ if (report.watchlist.length > 0) {
184
+ (0, command_contract_1.log)(io, chalk_1.default.bold(`观察名单(${report.watchlist.length} 条,零拦截观察期 ${usage_report_1.WATCHLIST_PERIOD_DAYS} 天,期满且样本足才转退役候选):`));
185
+ for (const w of report.watchlist) {
186
+ (0, command_contract_1.log)(io, ` [观察中] ${w.id} — ${w.reason}(列入 ${w.listedAt.slice(0, 10)},剩余观察 ${w.remainingDays} 天)`);
187
+ }
188
+ (0, command_contract_1.log)(io);
189
+ }
157
190
  // 配置健康
158
191
  if (report.lint.unknownIds.length > 0) {
159
192
  (0, command_contract_1.log)(io, chalk_1.default.yellow('⚠️ 配置健康:'));