@dommaker/harness 1.6.0 → 1.8.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 (560) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/bin/harness.js +17 -1
  3. package/dist/agents/index.d.ts +6 -2
  4. package/dist/agents/index.d.ts.map +1 -1
  5. package/dist/agents/index.js +6 -16
  6. package/dist/agents/index.js.map +1 -1
  7. package/dist/cli/commands/__tests__/gate-decision.d.ts +18 -0
  8. package/dist/cli/commands/__tests__/gate-decision.d.ts.map +1 -0
  9. package/dist/cli/commands/__tests__/gate-decision.js +26 -0
  10. package/dist/cli/commands/__tests__/gate-decision.js.map +1 -0
  11. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  12. package/dist/cli/commands/acceptance.js +17 -28
  13. package/dist/cli/commands/acceptance.js.map +1 -1
  14. package/dist/cli/commands/check.d.ts +11 -3
  15. package/dist/cli/commands/check.d.ts.map +1 -1
  16. package/dist/cli/commands/check.js +26 -11
  17. package/dist/cli/commands/check.js.map +1 -1
  18. package/dist/cli/commands/command.d.ts +0 -1
  19. package/dist/cli/commands/command.d.ts.map +1 -1
  20. package/dist/cli/commands/command.js +8 -6
  21. package/dist/cli/commands/command.js.map +1 -1
  22. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  23. package/dist/cli/commands/constraints-retire.js +36 -57
  24. package/dist/cli/commands/constraints-retire.js.map +1 -1
  25. package/dist/cli/commands/contract.d.ts.map +1 -1
  26. package/dist/cli/commands/contract.js +36 -42
  27. package/dist/cli/commands/contract.js.map +1 -1
  28. package/dist/cli/commands/definitions.d.ts +2 -0
  29. package/dist/cli/commands/definitions.d.ts.map +1 -1
  30. package/dist/cli/commands/definitions.js +11 -6
  31. package/dist/cli/commands/definitions.js.map +1 -1
  32. package/dist/cli/commands/failure.d.ts.map +1 -1
  33. package/dist/cli/commands/failure.js +16 -2
  34. package/dist/cli/commands/failure.js.map +1 -1
  35. package/dist/cli/commands/init.d.ts +14 -8
  36. package/dist/cli/commands/init.d.ts.map +1 -1
  37. package/dist/cli/commands/init.js +172 -364
  38. package/dist/cli/commands/init.js.map +1 -1
  39. package/dist/cli/commands/knowledge-view.d.ts +90 -0
  40. package/dist/cli/commands/knowledge-view.d.ts.map +1 -0
  41. package/dist/cli/commands/knowledge-view.js +198 -0
  42. package/dist/cli/commands/knowledge-view.js.map +1 -0
  43. package/dist/cli/commands/knowledge.d.ts +137 -52
  44. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  45. package/dist/cli/commands/knowledge.js +534 -400
  46. package/dist/cli/commands/knowledge.js.map +1 -1
  47. package/dist/cli/commands/passes-gate.d.ts +4 -1
  48. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  49. package/dist/cli/commands/passes-gate.js +11 -2
  50. package/dist/cli/commands/passes-gate.js.map +1 -1
  51. package/dist/cli/commands/performance.d.ts.map +1 -1
  52. package/dist/cli/commands/performance.js +19 -31
  53. package/dist/cli/commands/performance.js.map +1 -1
  54. package/dist/cli/commands/report.d.ts.map +1 -1
  55. package/dist/cli/commands/report.js +2 -1
  56. package/dist/cli/commands/report.js.map +1 -1
  57. package/dist/cli/commands/review.d.ts.map +1 -1
  58. package/dist/cli/commands/review.js +13 -25
  59. package/dist/cli/commands/review.js.map +1 -1
  60. package/dist/cli/commands/scaffold-templates.d.ts +73 -0
  61. package/dist/cli/commands/scaffold-templates.d.ts.map +1 -0
  62. package/dist/cli/commands/scaffold-templates.js +424 -0
  63. package/dist/cli/commands/scaffold-templates.js.map +1 -0
  64. package/dist/cli/commands/scaffold.d.ts +105 -0
  65. package/dist/cli/commands/scaffold.d.ts.map +1 -0
  66. package/dist/cli/commands/scaffold.js +263 -0
  67. package/dist/cli/commands/scaffold.js.map +1 -0
  68. package/dist/cli/commands/security.d.ts.map +1 -1
  69. package/dist/cli/commands/security.js +22 -24
  70. package/dist/cli/commands/security.js.map +1 -1
  71. package/dist/cli/commands/spec-baseline-check.d.ts +43 -0
  72. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  73. package/dist/cli/commands/spec-baseline-check.js +109 -34
  74. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  75. package/dist/cli/commands/sync-docs/context-syncer.d.ts +21 -2
  76. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  77. package/dist/cli/commands/sync-docs/context-syncer.js +112 -28
  78. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  79. package/dist/cli/commands/sync-docs/index.d.ts +1 -1
  80. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  81. package/dist/cli/commands/sync-docs/index.js +86 -27
  82. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  83. package/dist/cli/commands/sync-docs/project-reader.d.ts +24 -1
  84. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  85. package/dist/cli/commands/sync-docs/project-reader.js +33 -7
  86. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  87. package/dist/cli/commands/validate.d.ts +0 -16
  88. package/dist/cli/commands/validate.d.ts.map +1 -1
  89. package/dist/cli/commands/validate.js +2 -97
  90. package/dist/cli/commands/validate.js.map +1 -1
  91. package/dist/cli/gate-command.d.ts +37 -0
  92. package/dist/cli/gate-command.d.ts.map +1 -0
  93. package/dist/cli/gate-command.js +64 -0
  94. package/dist/cli/gate-command.js.map +1 -0
  95. package/dist/completion-checkers/index.d.ts +1 -1
  96. package/dist/completion-checkers/index.d.ts.map +1 -1
  97. package/dist/completion-checkers/index.js +0 -15
  98. package/dist/completion-checkers/index.js.map +1 -1
  99. package/dist/context/knowledge-injector.d.ts +4 -2
  100. package/dist/context/knowledge-injector.d.ts.map +1 -1
  101. package/dist/context/knowledge-injector.js +13 -6
  102. package/dist/context/knowledge-injector.js.map +1 -1
  103. package/dist/core/constraints/agent-prompt-renderer.js +2 -2
  104. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  105. package/dist/core/constraints/capabilities-reconcile.d.ts +20 -0
  106. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -1
  107. package/dist/core/constraints/capabilities-reconcile.js +26 -4
  108. package/dist/core/constraints/capabilities-reconcile.js.map +1 -1
  109. package/dist/core/constraints/check-cache.d.ts +1 -1
  110. package/dist/core/constraints/check-cache.js +1 -1
  111. package/dist/core/constraints/checker.d.ts +8 -3
  112. package/dist/core/constraints/checker.d.ts.map +1 -1
  113. package/dist/core/constraints/checker.js +30 -19
  114. package/dist/core/constraints/checker.js.map +1 -1
  115. package/dist/core/constraints/checkers/capability-sync.d.ts +3 -1
  116. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  117. package/dist/core/constraints/checkers/capability-sync.js +17 -24
  118. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  119. package/dist/core/constraints/checkers/context-doc-sync.d.ts +1 -1
  120. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  121. package/dist/core/constraints/checkers/context-doc-sync.js +9 -6
  122. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  123. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  124. package/dist/core/constraints/checkers/docs-freshness.js +24 -30
  125. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  126. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  127. package/dist/core/constraints/checkers/governance-presence.js +10 -4
  128. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  129. package/dist/core/constraints/checkers/types.d.ts +10 -6
  130. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  131. package/dist/core/constraints/checkers/types.js +7 -3
  132. package/dist/core/constraints/checkers/types.js.map +1 -1
  133. package/dist/core/constraints/context-builder.d.ts +11 -10
  134. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  135. package/dist/core/constraints/context-builder.js +31 -27
  136. package/dist/core/constraints/context-builder.js.map +1 -1
  137. package/dist/core/constraints/context-reconcile.d.ts +75 -0
  138. package/dist/core/constraints/context-reconcile.d.ts.map +1 -0
  139. package/dist/core/constraints/context-reconcile.js +161 -0
  140. package/dist/core/constraints/context-reconcile.js.map +1 -0
  141. package/dist/core/constraints/doc-freshness/runner.d.ts +7 -0
  142. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  143. package/dist/core/constraints/doc-freshness/runner.js +8 -3
  144. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  145. package/dist/core/constraints/git-evidence.js +1 -1
  146. package/dist/core/constraints/git-evidence.js.map +1 -1
  147. package/dist/core/constraints/injection-drift.d.ts +7 -2
  148. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  149. package/dist/core/constraints/injection-drift.js +13 -7
  150. package/dist/core/constraints/injection-drift.js.map +1 -1
  151. package/dist/core/constraints/run-env.d.ts +92 -0
  152. package/dist/core/constraints/run-env.d.ts.map +1 -0
  153. package/dist/core/constraints/run-env.js +175 -0
  154. package/dist/core/constraints/run-env.js.map +1 -0
  155. package/dist/core/constraints/triggers.d.ts +22 -0
  156. package/dist/core/constraints/triggers.d.ts.map +1 -0
  157. package/dist/core/constraints/triggers.js +31 -0
  158. package/dist/core/constraints/triggers.js.map +1 -0
  159. package/dist/core/effective-constraints.d.ts +13 -4
  160. package/dist/core/effective-constraints.d.ts.map +1 -1
  161. package/dist/core/effective-constraints.js +16 -6
  162. package/dist/core/effective-constraints.js.map +1 -1
  163. package/dist/core/effective-set.d.ts +8 -15
  164. package/dist/core/effective-set.d.ts.map +1 -1
  165. package/dist/core/effective-set.js +7 -12
  166. package/dist/core/effective-set.js.map +1 -1
  167. package/dist/core/index.d.ts +2 -1
  168. package/dist/core/index.d.ts.map +1 -1
  169. package/dist/core/index.js.map +1 -1
  170. package/dist/core/project-config-loader.d.ts +39 -17
  171. package/dist/core/project-config-loader.d.ts.map +1 -1
  172. package/dist/core/project-config-loader.js +51 -122
  173. package/dist/core/project-config-loader.js.map +1 -1
  174. package/dist/core/validators/check-handlers/http.d.ts +2 -2
  175. package/dist/core/validators/check-handlers/http.d.ts.map +1 -1
  176. package/dist/core/validators/check-handlers/http.js +12 -7
  177. package/dist/core/validators/check-handlers/http.js.map +1 -1
  178. package/dist/core/validators/index.d.ts +1 -1
  179. package/dist/core/validators/index.d.ts.map +1 -1
  180. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  181. package/dist/core/validators/passes-gate.js +2 -2
  182. package/dist/core/validators/passes-gate.js.map +1 -1
  183. package/dist/failure/classifier.d.ts +1 -1
  184. package/dist/failure/classifier.d.ts.map +1 -1
  185. package/dist/failure/classifier.js +8 -8
  186. package/dist/failure/classifier.js.map +1 -1
  187. package/dist/failure/index.d.ts +2 -1
  188. package/dist/failure/index.d.ts.map +1 -1
  189. package/dist/failure/index.js +9 -17
  190. package/dist/failure/index.js.map +1 -1
  191. package/dist/failure/recorder.d.ts +1 -1
  192. package/dist/failure/recorder.d.ts.map +1 -1
  193. package/dist/gates/checker-gate.d.ts +2 -1
  194. package/dist/gates/checker-gate.d.ts.map +1 -1
  195. package/dist/gates/checker-gate.js +3 -2
  196. package/dist/gates/checker-gate.js.map +1 -1
  197. package/dist/gates/command.d.ts +10 -4
  198. package/dist/gates/command.d.ts.map +1 -1
  199. package/dist/gates/command.js +36 -62
  200. package/dist/gates/command.js.map +1 -1
  201. package/dist/gates/contract.d.ts.map +1 -1
  202. package/dist/gates/contract.js +0 -4
  203. package/dist/gates/contract.js.map +1 -1
  204. package/dist/gates/definitions.d.ts +2 -1
  205. package/dist/gates/definitions.d.ts.map +1 -1
  206. package/dist/gates/definitions.js +6 -5
  207. package/dist/gates/definitions.js.map +1 -1
  208. package/dist/gates/index.d.ts +0 -4
  209. package/dist/gates/index.d.ts.map +1 -1
  210. package/dist/gates/index.js +1 -6
  211. package/dist/gates/index.js.map +1 -1
  212. package/dist/gates/performance.d.ts.map +1 -1
  213. package/dist/gates/performance.js +0 -4
  214. package/dist/gates/performance.js.map +1 -1
  215. package/dist/gates/runner.d.ts +1 -1
  216. package/dist/gates/runner.js +1 -1
  217. package/dist/gates/security.d.ts +1 -1
  218. package/dist/gates/security.d.ts.map +1 -1
  219. package/dist/gates/security.js +5 -6
  220. package/dist/gates/security.js.map +1 -1
  221. package/dist/gates/types.d.ts +14 -17
  222. package/dist/gates/types.d.ts.map +1 -1
  223. package/dist/gates/types.js.map +1 -1
  224. package/dist/hooks/bootstrap.d.ts +6 -4
  225. package/dist/hooks/bootstrap.d.ts.map +1 -1
  226. package/dist/hooks/bootstrap.js +25 -13
  227. package/dist/hooks/bootstrap.js.map +1 -1
  228. package/dist/hooks/config.d.ts +6 -2
  229. package/dist/hooks/config.d.ts.map +1 -1
  230. package/dist/hooks/config.js +5 -1
  231. package/dist/hooks/config.js.map +1 -1
  232. package/dist/hooks/index.d.ts +1 -1
  233. package/dist/hooks/index.d.ts.map +1 -1
  234. package/dist/hooks/index.js +0 -15
  235. package/dist/hooks/index.js.map +1 -1
  236. package/dist/hooks/pipeline.d.ts.map +1 -1
  237. package/dist/hooks/pipeline.js +2 -4
  238. package/dist/hooks/pipeline.js.map +1 -1
  239. package/dist/hooks/registry.d.ts +11 -8
  240. package/dist/hooks/registry.d.ts.map +1 -1
  241. package/dist/hooks/registry.js +22 -7
  242. package/dist/hooks/registry.js.map +1 -1
  243. package/dist/hooks/types.d.ts +23 -7
  244. package/dist/hooks/types.d.ts.map +1 -1
  245. package/dist/index.d.ts +4 -4
  246. package/dist/index.d.ts.map +1 -1
  247. package/dist/index.js +3 -4
  248. package/dist/index.js.map +1 -1
  249. package/dist/knowledge/audit-scoring.d.ts +87 -0
  250. package/dist/knowledge/audit-scoring.d.ts.map +1 -0
  251. package/dist/knowledge/audit-scoring.js +470 -0
  252. package/dist/knowledge/audit-scoring.js.map +1 -0
  253. package/dist/knowledge/audit.d.ts +11 -59
  254. package/dist/knowledge/audit.d.ts.map +1 -1
  255. package/dist/knowledge/audit.js +47 -522
  256. package/dist/knowledge/audit.js.map +1 -1
  257. package/dist/knowledge/import.d.ts.map +1 -1
  258. package/dist/knowledge/import.js +2 -0
  259. package/dist/knowledge/import.js.map +1 -1
  260. package/dist/knowledge/index-generator.d.ts.map +1 -1
  261. package/dist/knowledge/index-generator.js +4 -5
  262. package/dist/knowledge/index-generator.js.map +1 -1
  263. package/dist/knowledge/index.d.ts +3 -2
  264. package/dist/knowledge/index.d.ts.map +1 -1
  265. package/dist/knowledge/index.js +3 -16
  266. package/dist/knowledge/index.js.map +1 -1
  267. package/dist/knowledge/ingest.js +1 -1
  268. package/dist/knowledge/ingest.js.map +1 -1
  269. package/dist/knowledge/lifecycle.d.ts.map +1 -1
  270. package/dist/knowledge/lifecycle.js +4 -2
  271. package/dist/knowledge/lifecycle.js.map +1 -1
  272. package/dist/knowledge/migration.d.ts +3 -0
  273. package/dist/knowledge/migration.d.ts.map +1 -1
  274. package/dist/knowledge/migration.js +8 -2
  275. package/dist/knowledge/migration.js.map +1 -1
  276. package/dist/knowledge/query.d.ts +6 -0
  277. package/dist/knowledge/query.d.ts.map +1 -1
  278. package/dist/knowledge/query.js +8 -2
  279. package/dist/knowledge/query.js.map +1 -1
  280. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  281. package/dist/knowledge/reference-tracker.js +4 -6
  282. package/dist/knowledge/reference-tracker.js.map +1 -1
  283. package/dist/knowledge/store.d.ts +22 -16
  284. package/dist/knowledge/store.d.ts.map +1 -1
  285. package/dist/knowledge/store.js +53 -12
  286. package/dist/knowledge/store.js.map +1 -1
  287. package/dist/knowledge/tree-walker.d.ts +19 -0
  288. package/dist/knowledge/tree-walker.d.ts.map +1 -0
  289. package/dist/knowledge/tree-walker.js +30 -0
  290. package/dist/knowledge/tree-walker.js.map +1 -0
  291. package/dist/knowledge/types.d.ts +16 -0
  292. package/dist/knowledge/types.d.ts.map +1 -1
  293. package/dist/monitoring/traces.d.ts +5 -3
  294. package/dist/monitoring/traces.d.ts.map +1 -1
  295. package/dist/monitoring/traces.js +19 -5
  296. package/dist/monitoring/traces.js.map +1 -1
  297. package/dist/test-setup/jsonl-fake-fs.d.ts +30 -0
  298. package/dist/test-setup/jsonl-fake-fs.d.ts.map +1 -0
  299. package/dist/test-setup/jsonl-fake-fs.js +49 -0
  300. package/dist/test-setup/jsonl-fake-fs.js.map +1 -0
  301. package/dist/tools/index.d.ts +4 -1
  302. package/dist/tools/index.d.ts.map +1 -1
  303. package/dist/tools/index.js +7 -15
  304. package/dist/tools/index.js.map +1 -1
  305. package/dist/types/checkpoint.d.ts +5 -0
  306. package/dist/types/checkpoint.d.ts.map +1 -1
  307. package/dist/types/passes-gate.d.ts +0 -13
  308. package/dist/types/passes-gate.d.ts.map +1 -1
  309. package/dist/types/project-config.d.ts +18 -0
  310. package/dist/types/project-config.d.ts.map +1 -1
  311. package/dist/types/trace.d.ts +13 -4
  312. package/dist/types/trace.d.ts.map +1 -1
  313. package/dist/types/trace.js +4 -3
  314. package/dist/types/trace.js.map +1 -1
  315. package/dist/utils/exec.d.ts +4 -27
  316. package/dist/utils/exec.d.ts.map +1 -1
  317. package/dist/utils/exec.js +4 -94
  318. package/dist/utils/exec.js.map +1 -1
  319. package/dist/utils/jsonl.d.ts +17 -7
  320. package/dist/utils/jsonl.d.ts.map +1 -1
  321. package/dist/utils/jsonl.js +144 -17
  322. package/dist/utils/jsonl.js.map +1 -1
  323. package/dist/utils/numeric-flag.d.ts +44 -0
  324. package/dist/utils/numeric-flag.d.ts.map +1 -0
  325. package/dist/utils/numeric-flag.js +62 -0
  326. package/dist/utils/numeric-flag.js.map +1 -0
  327. package/package.json +1 -1
  328. package/src/CONTEXT.md +5 -2
  329. package/src/__tests__/acceptance-gate.test.ts +7 -4
  330. package/src/__tests__/checker-extra.test.ts +3 -2
  331. package/src/__tests__/checker.test.ts +4 -3
  332. package/src/__tests__/checkpoint.test.ts +26 -9
  333. package/src/__tests__/context-files-resolution.test.ts +29 -15
  334. package/src/__tests__/contract-gate.test.ts +19 -40
  335. package/src/__tests__/error-classifier.test.ts +1 -1
  336. package/src/__tests__/exec.test.ts +45 -107
  337. package/src/__tests__/failure-recorder.test.ts +7 -5
  338. package/src/__tests__/hooks-pipeline.test.ts +34 -47
  339. package/src/__tests__/layering.test.ts +217 -25
  340. package/src/__tests__/passes-gate.test.ts +9 -4
  341. package/src/__tests__/performance-gate.test.ts +3 -15
  342. package/src/__tests__/project-config-loader-extra.test.ts +31 -57
  343. package/src/__tests__/project-config-loader.test.ts +6 -3
  344. package/src/__tests__/public-exports.test.ts +27 -6
  345. package/src/__tests__/public-type-surface.test.ts +360 -0
  346. package/src/__tests__/public-value-surface.test.ts +182 -0
  347. package/src/__tests__/review-gate.test.ts +4 -6
  348. package/src/__tests__/security-gate.test.ts +5 -20
  349. package/src/__tests__/skip-semantics.test.ts +5 -3
  350. package/src/__tests__/spec-validator.test.ts +3 -2
  351. package/src/__tests__/sub-barrels-explicit.test.ts +87 -0
  352. package/src/__tests__/test-fixture-hygiene.test.ts +53 -0
  353. package/src/__tests__/trace-analyzer-extra.test.ts +5 -3
  354. package/src/__tests__/trace-analyzer.test.ts +5 -3
  355. package/src/__tests__/trace-collector.test.ts +5 -3
  356. package/src/agents/index.ts +13 -2
  357. package/src/cli/__tests__/gate-command.test.ts +162 -0
  358. package/src/cli/commands/CONTEXT.md +29 -5
  359. package/src/cli/commands/__tests__/acceptance.test.ts +24 -25
  360. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +15 -0
  361. package/src/cli/commands/__tests__/check-drift.test.ts +42 -1
  362. package/src/cli/commands/__tests__/check-read-count.test.ts +167 -0
  363. package/src/cli/commands/__tests__/check-skip-output.test.ts +4 -1
  364. package/src/cli/commands/__tests__/check.test.ts +43 -36
  365. package/src/cli/commands/__tests__/command-gate-dead-options.test.ts +60 -0
  366. package/src/cli/commands/__tests__/command.test.ts +33 -17
  367. package/src/cli/commands/__tests__/constraints-retire.test.ts +52 -0
  368. package/src/cli/commands/__tests__/context-syncer.test.ts +141 -0
  369. package/src/cli/commands/__tests__/contract.test.ts +20 -20
  370. package/src/cli/commands/__tests__/failure.test.ts +5 -4
  371. package/src/cli/commands/__tests__/gate-decision.ts +30 -0
  372. package/src/cli/commands/__tests__/init-dead-options.test.ts +50 -0
  373. package/src/cli/commands/__tests__/init-governance-io.test.ts +139 -0
  374. package/src/cli/commands/__tests__/init-injection.test.ts +3 -3
  375. package/src/cli/commands/__tests__/init-ondisk.test.ts +867 -0
  376. package/src/cli/commands/__tests__/init.test.ts +82 -6
  377. package/src/cli/commands/__tests__/knowledge-audit-threshold.test.ts +146 -0
  378. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +1 -1
  379. package/src/cli/commands/__tests__/knowledge-tone-mapping.test.ts +244 -0
  380. package/src/cli/commands/__tests__/knowledge-view.test.ts +839 -0
  381. package/src/cli/commands/__tests__/knowledge.test.ts +33 -15
  382. package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +368 -0
  383. package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +89 -1
  384. package/src/cli/commands/__tests__/performance.test.ts +19 -21
  385. package/src/cli/commands/__tests__/posteval-plan.test.ts +5 -3
  386. package/src/cli/commands/__tests__/pre-push-hook.test.ts +181 -0
  387. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +95 -2
  388. package/src/cli/commands/__tests__/project-path-convention.test.ts +103 -25
  389. package/src/cli/commands/__tests__/registry.test.ts +2 -0
  390. package/src/cli/commands/__tests__/report.test.ts +23 -1
  391. package/src/cli/commands/__tests__/review.test.ts +10 -17
  392. package/src/cli/commands/__tests__/scaffold.test.ts +505 -0
  393. package/src/cli/commands/__tests__/security-default-scan.test.ts +75 -0
  394. package/src/cli/commands/__tests__/security.test.ts +21 -24
  395. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +3 -2
  396. package/src/cli/commands/__tests__/spec-baseline-index-cap.test.ts +131 -0
  397. package/src/cli/commands/__tests__/spec-baseline-read-count.test.ts +325 -0
  398. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +19 -4
  399. package/src/cli/commands/__tests__/sync-docs-context-drift.test.ts +232 -0
  400. package/src/cli/commands/__tests__/sync-docs-module-order.test.ts +111 -0
  401. package/src/cli/commands/__tests__/sync-docs-read-count.test.ts +192 -0
  402. package/src/cli/commands/__tests__/sync-docs.test.ts +3 -2
  403. package/src/cli/commands/__tests__/validate.test.ts +1 -48
  404. package/src/cli/commands/acceptance.ts +24 -29
  405. package/src/cli/commands/check.ts +40 -17
  406. package/src/cli/commands/command.ts +9 -8
  407. package/src/cli/commands/constraints-retire.ts +57 -70
  408. package/src/cli/commands/contract.ts +44 -47
  409. package/src/cli/commands/definitions.ts +12 -6
  410. package/src/cli/commands/failure.ts +16 -3
  411. package/src/cli/commands/init.ts +242 -389
  412. package/src/cli/commands/knowledge-view.ts +224 -0
  413. package/src/cli/commands/knowledge.ts +643 -448
  414. package/src/cli/commands/passes-gate.ts +12 -3
  415. package/src/cli/commands/performance.ts +31 -38
  416. package/src/cli/commands/report.ts +3 -2
  417. package/src/cli/commands/review.ts +22 -28
  418. package/src/cli/commands/scaffold-templates.ts +406 -0
  419. package/src/cli/commands/scaffold.ts +302 -0
  420. package/src/cli/commands/security.ts +27 -23
  421. package/src/cli/commands/spec-baseline-check.ts +158 -33
  422. package/src/cli/commands/sync-docs/__tests__/context-export-surface.test.ts +102 -0
  423. package/src/cli/commands/sync-docs/__tests__/module-descriptions.test.ts +196 -0
  424. package/src/cli/commands/sync-docs/context-syncer.ts +137 -31
  425. package/src/cli/commands/sync-docs/index.ts +103 -28
  426. package/src/cli/commands/sync-docs/project-reader.ts +51 -7
  427. package/src/cli/commands/validate.ts +1 -104
  428. package/src/cli/gate-command.ts +87 -0
  429. package/src/completion-checkers/index.ts +10 -1
  430. package/src/context/CONTEXT.md +2 -1
  431. package/src/context/__tests__/knowledge-injector.test.ts +27 -0
  432. package/src/context/__tests__/session-manager.test.ts +19 -12
  433. package/src/context/knowledge-injector.ts +14 -7
  434. package/src/core/CONTEXT.md +10 -7
  435. package/src/core/__tests__/effective-set.test.ts +36 -60
  436. package/src/core/constraints/__tests__/context-reconcile.test.ts +246 -0
  437. package/src/core/constraints/__tests__/run-env.test.ts +377 -0
  438. package/src/core/constraints/__tests__/triggers.test.ts +70 -0
  439. package/src/core/constraints/agent-prompt-renderer.ts +1 -1
  440. package/src/core/constraints/capabilities-reconcile.ts +28 -4
  441. package/src/core/constraints/check-cache.ts +1 -1
  442. package/src/core/constraints/checker.ts +30 -14
  443. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +8 -3
  444. package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +16 -7
  445. package/src/core/constraints/checkers/__tests__/no-hardcoded-credentials.test.ts +2 -1
  446. package/src/core/constraints/checkers/capability-sync.ts +27 -28
  447. package/src/core/constraints/checkers/context-doc-sync.ts +9 -7
  448. package/src/core/constraints/checkers/docs-freshness.ts +28 -33
  449. package/src/core/constraints/checkers/governance-presence.ts +10 -6
  450. package/src/core/constraints/checkers/types.ts +14 -8
  451. package/src/core/constraints/context-builder.ts +35 -26
  452. package/src/core/constraints/context-reconcile.ts +200 -0
  453. package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +25 -0
  454. package/src/core/constraints/doc-freshness/runner.ts +18 -4
  455. package/src/core/constraints/git-evidence.ts +1 -1
  456. package/src/core/constraints/injection-drift.ts +19 -8
  457. package/src/core/constraints/run-env.ts +207 -0
  458. package/src/core/constraints/triggers.ts +33 -0
  459. package/src/core/effective-constraints.ts +17 -6
  460. package/src/core/effective-set.ts +8 -24
  461. package/src/core/index.ts +2 -1
  462. package/src/core/project-config-loader.ts +54 -102
  463. package/src/core/validators/__tests__/http-retry-sleep.test.ts +231 -0
  464. package/src/core/validators/check-handlers/http.ts +18 -7
  465. package/src/core/validators/index.ts +0 -1
  466. package/src/core/validators/passes-gate.ts +2 -3
  467. package/src/failure/CONTEXT.md +2 -1
  468. package/src/failure/classifier.ts +2 -2
  469. package/src/failure/index.ts +14 -2
  470. package/src/failure/recorder.ts +1 -1
  471. package/src/gates/CONTEXT.md +13 -7
  472. package/src/gates/__tests__/checker-gate.test.ts +1 -1
  473. package/src/gates/__tests__/command-single-predicate.test.ts +135 -0
  474. package/src/gates/__tests__/command.test.ts +8 -15
  475. package/src/gates/__tests__/contract.test.ts +5 -5
  476. package/src/gates/__tests__/gate-interface.test.ts +0 -17
  477. package/src/gates/__tests__/performance.test.ts +0 -13
  478. package/src/gates/__tests__/review.test.ts +0 -1
  479. package/src/gates/__tests__/runner.test.ts +1 -1
  480. package/src/gates/__tests__/security-default-scan.test.ts +97 -0
  481. package/src/gates/__tests__/security.test.ts +0 -13
  482. package/src/gates/checker-gate.ts +3 -2
  483. package/src/gates/command.ts +51 -74
  484. package/src/gates/contract.ts +0 -5
  485. package/src/gates/definitions.ts +6 -5
  486. package/src/gates/index.ts +0 -5
  487. package/src/gates/performance.ts +1 -6
  488. package/src/gates/runner.ts +1 -1
  489. package/src/gates/security.ts +9 -11
  490. package/src/gates/types.ts +20 -23
  491. package/src/hooks/CONTEXT.md +10 -9
  492. package/src/hooks/__tests__/bootstrap.test.ts +29 -6
  493. package/src/hooks/__tests__/pipeline.test.ts +28 -17
  494. package/src/hooks/__tests__/registry.test.ts +51 -3
  495. package/src/hooks/bootstrap.ts +35 -15
  496. package/src/hooks/config.ts +6 -2
  497. package/src/hooks/index.ts +10 -1
  498. package/src/hooks/pipeline.ts +4 -6
  499. package/src/hooks/registry.ts +31 -12
  500. package/src/hooks/types.ts +24 -7
  501. package/src/index.ts +1 -3
  502. package/src/knowledge/CONTEXT.md +14 -7
  503. package/src/knowledge/__tests__/audit-labels.test.ts +8 -5
  504. package/src/knowledge/__tests__/audit-scoring.test.ts +230 -0
  505. package/src/knowledge/__tests__/audit-write-count.test.ts +131 -0
  506. package/src/knowledge/__tests__/audit.test.ts +291 -208
  507. package/src/knowledge/__tests__/doctor.test.ts +3 -2
  508. package/src/knowledge/__tests__/ingest.test.ts +3 -2
  509. package/src/knowledge/__tests__/lifecycle.test.ts +57 -2
  510. package/src/knowledge/__tests__/migration.test.ts +35 -6
  511. package/src/knowledge/__tests__/query.test.ts +3 -2
  512. package/src/knowledge/__tests__/reference-tracker.test.ts +23 -2
  513. package/src/knowledge/__tests__/store.test.ts +149 -4
  514. package/src/knowledge/__tests__/tree-walker.test.ts +132 -0
  515. package/src/knowledge/audit-scoring.ts +589 -0
  516. package/src/knowledge/audit.ts +55 -599
  517. package/src/knowledge/import.ts +2 -0
  518. package/src/knowledge/index-generator.ts +4 -6
  519. package/src/knowledge/index.ts +27 -2
  520. package/src/knowledge/ingest.ts +1 -1
  521. package/src/knowledge/lifecycle.ts +5 -2
  522. package/src/knowledge/migration.ts +8 -2
  523. package/src/knowledge/query.ts +8 -1
  524. package/src/knowledge/reference-tracker.ts +5 -7
  525. package/src/knowledge/store.ts +75 -16
  526. package/src/knowledge/tree-walker.ts +29 -0
  527. package/src/knowledge/types.ts +19 -0
  528. package/src/monitoring/CONTEXT.md +5 -2
  529. package/src/monitoring/__tests__/context-tracker.test.ts +16 -8
  530. package/src/monitoring/__tests__/trace-file-anchoring.test.ts +80 -0
  531. package/src/monitoring/traces.ts +19 -5
  532. package/src/release/CONTEXT.md +1 -1
  533. package/src/release/__tests__/integrity.test.ts +21 -12
  534. package/src/test-setup/__tests__/jsonl-fake-fs.test.ts +98 -0
  535. package/src/test-setup/jsonl-fake-fs.ts +59 -0
  536. package/src/tools/index.ts +5 -1
  537. package/src/types/checkpoint.ts +6 -0
  538. package/src/types/passes-gate.ts +0 -17
  539. package/src/types/project-config.ts +21 -0
  540. package/src/types/trace.ts +14 -4
  541. package/src/utils/__tests__/file-walk.test.ts +3 -2
  542. package/src/utils/__tests__/jsonl-bounded-read.test.ts +106 -0
  543. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +10 -6
  544. package/src/utils/__tests__/jsonl.test.ts +61 -14
  545. package/src/utils/__tests__/numeric-flag.test.ts +80 -0
  546. package/src/utils/__tests__/package-version.test.ts +2 -2
  547. package/src/utils/exec.ts +4 -64
  548. package/src/utils/jsonl.ts +159 -20
  549. package/src/utils/numeric-flag.ts +64 -0
  550. package/dist/failure/types.d.ts +0 -9
  551. package/dist/failure/types.d.ts.map +0 -1
  552. package/dist/failure/types.js +0 -25
  553. package/dist/failure/types.js.map +0 -1
  554. package/dist/gates/effective-gates.d.ts +0 -30
  555. package/dist/gates/effective-gates.d.ts.map +0 -1
  556. package/dist/gates/effective-gates.js +0 -81
  557. package/dist/gates/effective-gates.js.map +0 -1
  558. package/src/failure/types.ts +0 -9
  559. package/src/gates/__tests__/effective-gates.test.ts +0 -77
  560. package/src/gates/effective-gates.ts +0 -99
@@ -18,6 +18,7 @@ import type { Constraint, ConstraintContext } from '../types/constraint';
18
18
  import type { ExecutionTrace } from '../types/trace';
19
19
  import * as fs from 'fs';
20
20
  import * as path from 'path';
21
+ import * as os from 'os';
21
22
  import { execSync } from 'child_process';
22
23
 
23
24
  /** 套件共用 checker:显式 no-op 记录器(trace 断言的用例自带记录器实例) */
@@ -32,10 +33,10 @@ function makeConstraint(
32
33
  }
33
34
 
34
35
  describe('skip 三态语义(ADR-0001)', () => {
35
- const tempDir = path.join(process.cwd(), 'temp-test-skip-semantics');
36
+ let tempDir: string;
36
37
 
37
38
  beforeAll(() => {
38
- fs.mkdirSync(tempDir, { recursive: true });
39
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'temp-test-skip-semantics-'));
39
40
  execSync('git init', { cwd: tempDir });
40
41
  execSync('git config user.email "test@test.com"', { cwd: tempDir });
41
42
  execSync('git config user.name "Test"', { cwd: tempDir });
@@ -227,9 +228,10 @@ describe('skip 三态语义(ADR-0001)', () => {
227
228
  });
228
229
 
229
230
  describe('detectTrigger 补 code_implementation 推断', () => {
230
- const triggerDir = path.join(tempDir, 'trigger-probe');
231
+ let triggerDir: string;
231
232
 
232
233
  beforeAll(() => {
234
+ triggerDir = path.join(tempDir, 'trigger-probe');
233
235
  // src/ 下有已提交 .ts 文件 → detectSourceRoots 命中 'src'
234
236
  fs.mkdirSync(path.join(triggerDir, 'src'), { recursive: true });
235
237
  fs.writeFileSync(path.join(triggerDir, 'src', 'existing.ts'), 'export const x = 1;');
@@ -6,13 +6,14 @@ import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
6
6
  import { SpecValidator } from '../core/spec/validator';
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
+ import * as os from 'os';
9
10
 
10
11
  describe('SpecValidator', () => {
11
- const tempDir = path.join(process.cwd(), 'temp-test-spec');
12
+ let tempDir: string;
12
13
  let validator: SpecValidator;
13
14
 
14
15
  beforeAll(() => {
15
- fs.mkdirSync(tempDir, { recursive: true });
16
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'temp-test-spec-'));
16
17
  validator = SpecValidator.getInstance();
17
18
  });
18
19
 
@@ -0,0 +1,87 @@
1
+ /**
2
+ * 子 barrel 显式清单闸(harness#137,判据同 ADR-0003 / ADR-0022)
3
+ *
4
+ * ADR-0003 的「禁 export *」此前只钉到五个 `exports` 入口(`public-type-surface.test.ts`
5
+ * 的写法白名单闸),子目录 `index.ts` 的星号再导出不受管——星号让「这个 barrel 到底
6
+ * 对外给了什么」无法按名字回答,同名遮蔽(两个 `types.ts` 撞名)也在此处静默发生。
7
+ * 本闸把它扩到 src 下全部 barrel:**逐文件、逐条**登记豁免,未登记的 `export *` 即红。
8
+ *
9
+ * 与两道公共面闸的分工:入口清单(`src/index.ts` 等五个)由 `public-type-surface.test.ts`
10
+ * 与 `public-value-surface.test.ts` 逐字冻结,本闸不重复钉入口,只管**入口之外**的目录级
11
+ * barrel;新增子 barrel 星号由本闸管住。
12
+ */
13
+
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+
17
+ const SRC_DIR = path.join(__dirname, '..');
18
+
19
+ /**
20
+ * 已核无星号的 barrel 若日后引入 `export *`,需要在这里登记理由。
21
+ * 当前为空:harness#137 已把 8 处目录级星号全部改成显式清单(`failure/types.ts` 那处
22
+ * 是兼容再导出 shim,随票删除而非改写清单)。
23
+ */
24
+ const STAR_EXEMPTIONS: Record<string, string> = {};
25
+
26
+ /** 收集 src 下所有目录级 barrel(`<dir>/index.ts`),不含 `__tests__` 内的测试辅助文件 */
27
+ function listBarrels(dir: string, out: string[] = []): string[] {
28
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
29
+ const full = path.join(dir, entry.name);
30
+ if (!entry.isDirectory() || entry.name === '__tests__') continue;
31
+ const indexTs = path.join(full, 'index.ts');
32
+ if (fs.existsSync(indexTs)) out.push(indexTs);
33
+ listBarrels(full, out);
34
+ }
35
+ return out;
36
+ }
37
+
38
+ /** 行首 `export *` / `export type *` / `export * as ns`(去注释后判定) */
39
+ const STAR_EXPORT_RE = /^export\s+(?:type\s+\*)\s*|^export\s+\*/;
40
+
41
+ function stripComments(code: string): string {
42
+ return code.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
43
+ }
44
+
45
+ function starExports(barrel: string): string[] {
46
+ const text = stripComments(fs.readFileSync(barrel, 'utf-8'));
47
+ return text
48
+ .split('\n')
49
+ .map(line => line.trim())
50
+ .filter(line => STAR_EXPORT_RE.test(line));
51
+ }
52
+
53
+ describe('子 barrel 显式清单(harness#137)', () => {
54
+ const barrels = [path.join(SRC_DIR, 'index.ts'), ...listBarrels(SRC_DIR)];
55
+
56
+ it('扫到了 barrel(扫空即说明遍历失效,不得假绿)', () => {
57
+ expect(barrels.length).toBeGreaterThan(5);
58
+ });
59
+
60
+ it('src 下每个目录 barrel 都不含 `export *`(豁免须逐条登记理由)', () => {
61
+ const offenders = barrels
62
+ .map(barrel => ({
63
+ barrel: path.relative(SRC_DIR, barrel),
64
+ stars: starExports(barrel),
65
+ }))
66
+ .filter(entry => entry.stars.length > 0 && !STAR_EXEMPTIONS[entry.barrel]);
67
+ expect(offenders).toEqual([]);
68
+ });
69
+
70
+ it('豁免登记是死账:登记过却已无星号的条目须撤销(防豁免面只增不减)', () => {
71
+ const stale = Object.keys(STAR_EXEMPTIONS).filter(
72
+ rel => !starExports(path.join(SRC_DIR, rel)).length
73
+ );
74
+ expect(stale).toEqual([]);
75
+ });
76
+
77
+ it('failure/types.ts 兼容再导出 shim 已删除(转发链第三跳收口)', () => {
78
+ // 正本在 src/types/failure.ts(工单 14 归位),原模块路径经双仓核实无引用方
79
+ expect(fs.existsSync(path.join(SRC_DIR, 'failure', 'types.ts'))).toBe(false);
80
+ });
81
+
82
+ it('failure barrel 直连类型正本,不再经 shim 中转', () => {
83
+ const text = stripComments(fs.readFileSync(path.join(SRC_DIR, 'failure', 'index.ts'), 'utf-8'));
84
+ expect(text).toContain("from '../types/failure'");
85
+ expect(text).not.toMatch(/from '\.\/types'/);
86
+ });
87
+ });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 测试夹具卫生闸(harness#145)
3
+ *
4
+ * 钉住一件事:`__tests__` 里的文件系统夹具根一律经 `fs.mkdtempSync` 取每轮唯一目录,
5
+ * 不得再出现「cwd 下固定名 temp-test 目录」这种夹具形状。
6
+ *
7
+ * 固定名的危害:同一 checkout 的两个并发 jest 进程共享该路径,一方的 afterAll rmSync
8
+ * 会删掉另一方在用的夹具(表现为像产品 bug 的 flaky);被 kill 的运行还会留下目录,
9
+ * 下一轮从脏夹具起步。唯一后缀(mkdtemp 前缀以 `-` 结尾)从根上消掉这两条。
10
+ *
11
+ * 只判「名字是否唯一」,不判「落在哪个父目录」:cwd 锚定行为本身是被测语义的套件
12
+ * (injection-writer / init-injection / parentDir opt-out)留在 cwd 是有意为之。
13
+ */
14
+
15
+ import { describe, it, expect } from '@jest/globals';
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+
19
+ /** 固定名夹具形状:cwd 拼一个字面量 temp-test 目录名,且该名字不以 `-` 结尾(非 mkdtemp 前缀) */
20
+ const FIXED_FIXTURE_RE = /(?:path\.)?join\(\s*process\.cwd\(\)\s*,\s*(['"])temp-test-([^'"]*)\1\s*\)/g;
21
+
22
+ function collectTestFiles(dir: string, out: string[] = []): string[] {
23
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
24
+ if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'coverage') continue;
25
+ const full = path.join(dir, entry.name);
26
+ if (entry.isDirectory()) collectTestFiles(full, out);
27
+ else if (entry.name.endsWith('.test.ts')) out.push(full);
28
+ }
29
+ return out;
30
+ }
31
+
32
+ describe('测试夹具卫生(#145)', () => {
33
+ const repoRoot = path.join(__dirname, '..', '..');
34
+ const files = collectTestFiles(repoRoot);
35
+
36
+ it('扫描面非空(防止 glob 失效把闸扫成空集)', () => {
37
+ expect(files.length).toBeGreaterThan(100);
38
+ });
39
+
40
+ it('__tests__ 内不存在固定名 temp-test-* 夹具目录', () => {
41
+ const offenders: string[] = [];
42
+ for (const file of files) {
43
+ fs.readFileSync(file, 'utf-8').split('\n').forEach((line, idx) => {
44
+ FIXED_FIXTURE_RE.lastIndex = 0;
45
+ let m: RegExpExecArray | null;
46
+ while ((m = FIXED_FIXTURE_RE.exec(line)) !== null) {
47
+ if (!m[2].endsWith('-')) offenders.push(`${path.relative(repoRoot, file)}:${idx + 1}: ${line.trim()}`);
48
+ }
49
+ });
50
+ }
51
+ expect(offenders).toEqual([]);
52
+ });
53
+ });
@@ -7,16 +7,18 @@ import { TraceAnalyzer, createAnalyzer } from '../monitoring/trace-analyzer';
7
7
  import { TraceCollector } from '../monitoring/traces';
8
8
  import * as fs from 'fs';
9
9
  import * as path from 'path';
10
+ import * as os from 'os';
10
11
  import type { ExecutionTrace } from '../types/trace';
11
12
 
12
13
  describe('TraceAnalyzer - 补充覆盖', () => {
13
- const tempDir = path.join(process.cwd(), 'temp-test-analyzer-extra');
14
- const logFile = path.join(tempDir, 'traces.log');
14
+ let tempDir: string;
15
+ let logFile: string;
15
16
  let collector: TraceCollector;
16
17
  let analyzer: TraceAnalyzer;
17
18
 
18
19
  beforeAll(() => {
19
- fs.mkdirSync(tempDir, { recursive: true });
20
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'temp-test-analyzer-extra-'));
21
+ logFile = path.join(tempDir, 'traces.log');
20
22
  fs.writeFileSync(logFile, '');
21
23
  });
22
24
 
@@ -11,16 +11,18 @@ import {
11
11
  import { TraceCollector } from '../monitoring/traces';
12
12
  import * as fs from 'fs';
13
13
  import * as path from 'path';
14
+ import * as os from 'os';
14
15
  import type { ExecutionTrace, TraceSummary } from '../types/trace';
15
16
 
16
17
  describe('TraceAnalyzer', () => {
17
- const tempDir = path.join(process.cwd(), 'temp-test-analyzer');
18
- const logFile = path.join(tempDir, 'traces.log');
18
+ let tempDir: string;
19
+ let logFile: string;
19
20
  let collector: TraceCollector;
20
21
  let analyzer: TraceAnalyzer;
21
22
 
22
23
  beforeAll(() => {
23
- fs.mkdirSync(tempDir, { recursive: true });
24
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'temp-test-analyzer-'));
25
+ logFile = path.join(tempDir, 'traces.log');
24
26
  fs.writeFileSync(logFile, '');
25
27
  collector = new TraceCollector({ traceFile: logFile, enabled: true });
26
28
  analyzer = new TraceAnalyzer(collector);
@@ -6,15 +6,17 @@ import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/glo
6
6
  import { TraceCollector } from '../monitoring/traces';
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
+ import * as os from 'os';
9
10
  import type { ExecutionTrace } from '../types/trace';
10
11
 
11
12
  describe('TraceCollector', () => {
12
- const tempDir = path.join(process.cwd(), 'temp-test-traces');
13
- const traceFile = path.join(tempDir, 'traces.log');
13
+ let tempDir: string;
14
+ let traceFile: string;
14
15
  let collector: TraceCollector;
15
16
 
16
17
  beforeAll(() => {
17
- fs.mkdirSync(tempDir, { recursive: true });
18
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'temp-test-traces-'));
19
+ traceFile = path.join(tempDir, 'traces.log');
18
20
  });
19
21
 
20
22
  afterAll(() => {
@@ -1,2 +1,13 @@
1
- export * from './types';
2
- export * from './lifecycle';
1
+ /**
2
+ * Agents 模块导出(ADR-0003:显式清单,禁 export *,harness#137)
3
+ */
4
+
5
+ export type {
6
+ AgentStatus,
7
+ AgentConfig,
8
+ AgentState,
9
+ AgentEvent,
10
+ FallbackStrategy,
11
+ } from './types';
12
+ export { AgentLifecycle } from './lifecycle';
13
+ export type { EventHandler } from './lifecycle';
@@ -0,0 +1,162 @@
1
+ /**
2
+ * 门禁命令共享面测试(架构评审候选1)
3
+ *
4
+ * 替身是「假门禁产决策」:不碰 git、不装 gh、不跑真扫描。
5
+ * 断言「六条门禁的失败措辞形状一致」——这份断言在映射散在 6 个 handler 时写不出来。
6
+ */
7
+
8
+ import {
9
+ gateCommandResult,
10
+ reportGateDecision,
11
+ reportGateError,
12
+ type GateCommandView,
13
+ } from '../gate-command';
14
+ import { decisionFromResult } from '../../gates/decision';
15
+ import { gateResult } from '../../gates/types';
16
+ import type { GateDecisionStatus } from '../../gates/types';
17
+ import { captureIO, type CapturingIO } from '../command-contract';
18
+
19
+ jest.mock('chalk', () => ({
20
+ blue: jest.fn((s: string) => s),
21
+ green: jest.fn((s: string) => s),
22
+ red: jest.fn((s: string) => s),
23
+ gray: jest.fn((s: string) => s),
24
+ }));
25
+
26
+ function decide(
27
+ gateId: string,
28
+ passed: boolean,
29
+ message: string,
30
+ details?: Record<string, any>,
31
+ status?: GateDecisionStatus
32
+ ) {
33
+ return decisionFromResult(gateResult(gateId, passed, message, Date.now(), details), status);
34
+ }
35
+
36
+ function view(over: Partial<GateCommandView> = {}): GateCommandView {
37
+ return { gateId: 'review', label: '代码审查门控', ...over };
38
+ }
39
+
40
+ describe('gateCommandResult — 决策到命令结果的唯一映射', () => {
41
+ it('放行 → ok 且不带 reason', () => {
42
+ expect(gateCommandResult('review', decide('review', true, 'ok'))).toEqual({ kind: 'ok' });
43
+ });
44
+
45
+ it('拦下 → fail,reason 为 `<gateId> gate denied: <message>`', () => {
46
+ expect(gateCommandResult('review', decide('review', false, 'needs approval'))).toEqual({
47
+ kind: 'fail',
48
+ reason: 'review gate denied: needs approval',
49
+ });
50
+ });
51
+
52
+ it('六条门禁的 denied reason 形状一致', () => {
53
+ for (const id of ['review', 'security', 'performance', 'contract', 'acceptance', 'command']) {
54
+ expect(gateCommandResult(id, decide(id, false, 'boom'))).toEqual({
55
+ kind: 'fail',
56
+ reason: `${id} gate denied: boom`,
57
+ });
58
+ }
59
+ });
60
+
61
+ it('ask 无实现者 → fail-closed 按拦下计(与 runGates 聚合同规则)', () => {
62
+ expect(gateCommandResult('review', decide('review', true, 'need human', undefined, 'ask'))).toEqual({
63
+ kind: 'fail',
64
+ reason: 'review gate denied: need human',
65
+ });
66
+ });
67
+ });
68
+
69
+ describe('reportGateDecision — ✓/✗ 输出骨架', () => {
70
+ let io: CapturingIO;
71
+
72
+ beforeEach(() => {
73
+ io = captureIO();
74
+ });
75
+
76
+ it('通过:空行 + 通过横幅 + onPass 行', () => {
77
+ const result = reportGateDecision(
78
+ io,
79
+ view({ onPass: (r) => [` 审批: ${r.details!.approvals}`] }),
80
+ decide('review', true, 'ok', { approvals: 2 })
81
+ );
82
+
83
+ expect(io.outLines()).toEqual(['', '✅ 代码审查门控检查通过', ' 审批: 2']);
84
+ expect(result).toEqual({ kind: 'ok' });
85
+ });
86
+
87
+ it('未通过:横幅 + message 行 + onFail 行(空串 = 空行)', () => {
88
+ const result = reportGateDecision(
89
+ io,
90
+ view({ onFail: () => ['', 'see the audit'] }),
91
+ decide('review', false, 'needs approval')
92
+ );
93
+
94
+ expect(io.outLines()).toEqual([
95
+ '',
96
+ '❌ 代码审查门控检查失败',
97
+ ' needs approval',
98
+ '',
99
+ 'see the audit',
100
+ ]);
101
+ expect(result).toEqual({ kind: 'fail', reason: 'review gate denied: needs approval' });
102
+ });
103
+
104
+ it('不传闭包时只出空行与横幅(未通过仍出 message 行)', () => {
105
+ reportGateDecision(io, view(), decide('review', true, 'ok'));
106
+ reportGateDecision(io, view(), decide('review', false, 'nope'));
107
+
108
+ expect(io.outLines()).toEqual([
109
+ '',
110
+ '✅ 代码审查门控检查通过',
111
+ '',
112
+ '❌ 代码审查门控检查失败',
113
+ ' nope',
114
+ ]);
115
+ });
116
+
117
+ it('闭包返回空数组不产生多余行', () => {
118
+ reportGateDecision(io, view({ onPass: () => [] }), decide('review', true, 'ok'));
119
+ expect(io.outLines()).toEqual(['', '✅ 代码审查门控检查通过']);
120
+ });
121
+
122
+ it('label 决定横幅用词(验收标准无「门控」二字)', () => {
123
+ reportGateDecision(io, view({ gateId: 'acceptance', label: '验收标准' }), decide('acceptance', true, 'ok'));
124
+ expect(io.outLines()).toEqual(['', '✅ 验收标准检查通过']);
125
+ });
126
+ });
127
+
128
+ describe('reportGateError — 门禁之外的抛出物', () => {
129
+ let io: CapturingIO;
130
+
131
+ beforeEach(() => {
132
+ io = captureIO();
133
+ });
134
+
135
+ it('出错横幅 + `<gateId> gate error: <message>`', () => {
136
+ const result = reportGateError(io, 'review', '代码审查门控', new Error('boom'));
137
+
138
+ expect(io.outLines()).toEqual(['', '❌ 代码审查门控检查出错', ' boom']);
139
+ expect(result).toEqual({ kind: 'fail', reason: 'review gate error: boom' });
140
+ });
141
+
142
+ it('extra 按错误文本追加提示行', () => {
143
+ reportGateError(io, 'review', '代码审查门控', new Error('not a git repository'), (msg) =>
144
+ msg.includes('not a git repository') ? ['', '提示: 此命令需要在 Git 仓库中运行'] : []
145
+ );
146
+
147
+ expect(io.outLines()).toEqual([
148
+ '',
149
+ '❌ 代码审查门控检查出错',
150
+ ' not a git repository',
151
+ '',
152
+ '提示: 此命令需要在 Git 仓库中运行',
153
+ ]);
154
+ });
155
+
156
+ it('非 Error 抛出物按 String() 取原因', () => {
157
+ expect(reportGateError(io, 'contract', '契约门控', 'plain string')).toEqual({
158
+ kind: 'fail',
159
+ reason: 'contract gate error: plain string',
160
+ });
161
+ });
162
+ });
@@ -10,8 +10,12 @@ H5(#44)起:
10
10
  ## 核心导出
11
11
  - `COMMAND_DEFINITIONS`(definitions.ts)— 全部非门禁命令定义(纯数据模块、零闭包,禁止 import 命令实现;ADR-0010)
12
12
  - 命令契约(`src/cli/command-contract.ts`,上层目录)— `CommandResult` / `CommandKind` / `CommandIO` / `processIO` / `captureIO` / `lastJsonOutput` / `log` / `logError`
13
+ - 门禁命令共享面(`src/cli/gate-command.ts`,上层目录)— `GateDecision → CommandResult` 的唯一映射 `gateCommandResult`,加 ✓/✗ 输出骨架 `reportGateDecision` 与出错横幅 `reportGateError`。同一句失败措辞此前抄在 6 个 handler 里(「`<id>` gate denied」×6 / 「`<id>` gate error」×4),门禁特有的指标行经 `onPass`/`onFail` 闭包传入(架构评审候选1)
14
+ - 脚手架落盘面(`src/cli/commands/scaffold.ts`)— 受管文件(managed file)三态判定 `writeManagedFile` / 一组落盘 `runPlan` + 9 个站点工厂(含对外文案);模板正文住 `scaffold-templates.ts`,落盘注入面 `ScaffoldFileSystem` 可换内存替身(harness#132);CI 站点工厂 `harnessCheckCiFile` 带平台维度(github / gitlab,harness#143);两道 Git hook 工厂 `preCommitHookFile` / `prePushHookFile`(后者 harness#144,带可执行位)
15
+ - knowledge 投影面(`src/cli/commands/knowledge-view.ts`)— 11 个 knowledge 子操作的**display model** 渲染与出口:`emitKnowledgeView`(json/人读唯一分派 + 退出码)、`announce`(取数期进度行,`--json` 下静默)、`TONE_STYLES` 角色→样式单表、维度/规则 label 与 `toneForMaturity`/`toneForScore`/`toneForSeverity` 三张映射、`resolveKnowledgeBaseDir` + `openKnowledgeStore`(路径兜底与 store 构造单点)(harness#133)
13
16
  - 各命令文件:check / validate / passes-gate / init / report / status / spec / sync-docs / knowledge / sdd / failure / posteval-plan / release / constraints / spec-baseline-check
14
- - 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`,形状同为 `CommandDefinition`,ADR-0007
17
+ - 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`,形状同为 `CommandDefinition`,ADR-0007);**判定一律穿过统一接口 `evaluate()`**,成败与措辞由共享面映射。`command` 只复用映射、保留自己的单行 ✓/✗ 输出(已对外的机器友好形状);`security audit` / `acceptance list` / `contract validate-schema` / `review status` / `command --list/--level` 是只展示不判断的子命令,直读报告面(`scan()` 等),不套骨架
18
+ - CLI 子命令 `command` 的两个分支共用同一台 gate 实例(#135/ADR-0024):默认分支经 `evaluate()` 取裁决、`--level` 经同一实例的 `getRiskLevel()` 取等级,命令侧不再引用模块级单例出口——「配置传进去却到不了判定」的那条路已封。`--strict` 旗帜随 `CommandGateConfig.strict`(零消费者、从不生效)一并删除
15
19
 
16
20
  `constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom 落 custom-constraints.yml 条目 retired 段(studio#82 D6 一处真相)+ KnowledgeStore 沉淀 + 治理注入段同步)。
17
21
 
@@ -25,29 +29,49 @@ H5(#44)起:
25
29
 
26
30
  ## 约定
27
31
  - 每个命令一个实现文件,命名与命令名一致(sync-docs 为模块族目录)
32
+ - **CONTEXT.md 内容判定的采集侧住本层(harness#142 / ADR-0025)**:`sync-docs/context-syncer.ts` 的 `collectContextExportSurface(dirPath, projectPath)` 负责目录遍历(`findTsSourceFiles`,排 `__tests__/`、`*.d.ts`)、`export *` 与具名再导出的仓内目标解析(越出 projectPath、命中 `node_modules`/跳过目录即不并入)并按正本声明剔除类型-only;判定本体是 `core/constraints/context-reconcile.ts` 的纯函数,本层不写判定规则。`sync-docs` 的 4b 段对每个含 CONTEXT.md 的目录跑一次对撞:**内容漂移 → `--check` 判 fail 且 reason 点名文件与符号**;mtime 差异自此只是本地提示(`process.env.CI` 置位时连提示都不给,因全新 checkout 的时序无意义)。写入模式**不改写 CONTEXT.md**(散文不可机械生成),退出码面不变;`--json` 侧新增 `contentDrift` 与 `summary.contextContentDrift`
28
33
  - **新增命令 = 命令实现文件 + definitions.ts 一条定义(含 CLI 元数据与实现引用)+ 测试**,不再改 bin/harness.js
29
34
  - **definitions.ts 是纯数据模块(ADR-0010 后零闭包)**:禁止 import 任何命令实现/运行时依赖(保 --help/--version 懒加载);子命令别名是数据(条目 `aliases: string[]`,不复印整块),实参编组(缺参闸门/强转/兜底)归各命令模块的具名导出(如 `knowledgeSearchCommand`/`coverageCheck`);实现引用可解析性由 `__tests__/registry.test.ts` 构建/测试期断言
30
35
  - 命令选项类型命名规范:XxxOptions
31
- - **本层是上行数据的注入方(harness#88)**:core 对 cli/gates/monitoring 零值导入,故 `check`/`report` 自己 `new ConstraintChecker(getTraceCollector())`(组合根接线 trace 记录器;用 `constraintChecker`/`getInstance()` 拿到的默认实例不写 trace),`sync-docs` 把 `COMMAND_DEFINITIONS` + `GATE_DEFINITIONS` 组装成 `CapabilityDefinitionSource` 注入 `capabilities-parser` 供能力清单计数
36
+ - **本层是上行数据的注入方(harness#88)**:core 对 cli/gates/monitoring 零值导入,故 `check`/`report` 自己 `new ConstraintChecker(new TraceCollector({ projectPath }))`(组合根接线 trace 记录器,#139 起收集器锚根构造;用 `constraintChecker`/`getInstance()` 拿到的默认实例不写 trace),`sync-docs` 把 `COMMAND_DEFINITIONS` + `GATE_DEFINITIONS` 组装成 `CapabilityDefinitionSource` 注入 `capabilities-parser` 供能力清单计数
32
37
  - **数据已在手就不再构造带 IO 副作用的实例(ADR-0020)**:`status` 按 projectPath 直读出 traces 后,直调 `monitoring/trace-analyzer` 的模块级纯函数 `summarizeTraces()`/`detectTraceAnomalies()`,不再 `new TraceCollector({traceFile})` + `new TraceAnalyzer(c)`——collector 构造函数带 mkdir 副作用,而这条路径上它的读方法从未被调用,实例纯属构造仪式。机器可检:`__tests__/status-analysis-wiring.test.ts`(源形状闸:命令体不得出现 `new TraceCollector`/`new TraceAnalyzer`,也不 import `monitoring/traces`)
33
38
  - **projectPath 只在入口兜底一次,并传到每个 IO/执行点(harness#95)**:`-p/--project-path` 的 cwd 兜底唯一落点是命令模块入口(`const projectPath = options.projectPath || process.cwd()`);往下每层只能收参数,**禁止再取一次 `process.cwd()`、也禁止用相对路径默认值**——两者都让 `-p` 半失效:执行/读写位置悄悄回到调用方 cwd,而 acceptance 的「无 tasks.yml 即跳过」本身就是 `passed:true`,于是失效表现为假绿。落地形状:执行/IO 根用**必传形参**表达(`PassesGate.runTests(workDir)`),门禁内的相对子路径锚到 projectPath:`ContractGate` 一直是 `path.join(context.projectPath, contractPath)`,`SpecAcceptanceGate` 的 tasksPath 随 #95 对齐成 `path.resolve(projectPath, 相对值)`(绝对值原样,缺省 `<projectPath>/tasks.yml`)
34
- - 机器可检:`__tests__/project-path-convention.test.ts` 三道闸——CLI 层 cwd 必须是 `xxx || process.cwd()` 兜底形状、下游层(= src 减 cli)cwd 站点**逐行**冻结(#98,豁免文件内新增行/行变形同样失败)、相对路径默认值冻结(#98 收紧:`xxxPath: '相对值'` 对象字面量之外,参数默认值形/模板字面量/双引号形同罪,扫描域 = 整个下游层);豁免逐条带理由,站点消失却不删条目同样失败
35
- - 行为可检:`__tests__/project-path-anchoring.test.ts` 前提统一 **cwd ≠ projectPath**、不 mock 任何 IO,断言测试真在 B 执行、证据落 `B/.harness/evidence/`、acceptance 读 `B/tasks.yml`。「根已传入却又取 cwd」这类静态看不出的漂移由它守,冻结表守的是不再新增站点
39
+ - 机器可检:`__tests__/project-path-convention.test.ts` 四道闸——CLI 层 cwd 必须是 `xxx || process.cwd()` 兜底形状、下游层(= src 减 cli)cwd 站点**逐行**冻结(#98,豁免文件内新增行/行变形同样失败)、相对路径默认值冻结(#98 收紧:`xxxPath: '相对值'` 对象字面量之外,参数默认值形/模板字面量/双引号形同罪;#139 再扩两形:键名后缀 `Path` → `Path|File|Log`,加 `xxxFile: SOME_CONSTANT` 常量引用臂——traceFile 病灶正是「键名不带 Path + 值引用常量」两头都不认的形状。只扫使用点,常量定义本体是正本、不入闸)、组合根对 cwd 锚定的 `getTraceCollector()` 单例零消费(#139);豁免逐条带理由,站点消失却不删条目同样失败
40
+ - 行为可检:`__tests__/project-path-anchoring.test.ts` 前提统一 **cwd ≠ projectPath**、不 mock 任何 IO,断言测试真在 B 执行、证据落 `B/.harness/evidence/`、acceptance 读 `B/tasks.yml`、`check --project-path` 的 trace 落 `B/.harness/logs/traces.log` 且 `status` 读得到它(站点 3,#139)。「根已传入却又取 cwd」这类静态看不出的漂移由它守,冻结表守的是不再新增站点
36
41
  - **读到部分结果必须告知(harness#100)**:命令面以 `skip` 策略读 JSONL(traces.log / failures.log)时,坏行计数要到用户可见输出,**不允许默默丢弃**。共同约束:无损坏零噪声(计数为 0 不出行)、不改任何既有计数口径(`status` 的 `记录数` 仍是原始非空行数,逐字节不变)、计数一律取自 `src/utils/jsonl` 读链返回的 `skippedLines`(经报告入口透传或在读点就地取用),**禁止**用「原始行数 − 统计条数」反推(窗过滤掉的合法行与坏行分不开)。三处落点各自对得上一行代码:
37
42
  - `status`:告知**走 stderr**(`logError(io, …)`),因为 stdout 是报告体、其字节已被 `记录数` 那一行定死,挤进去等于改输出
38
43
  - `constraints report`:提示属于**报告体内的降级位**,与既有的 `traceFileExists` 说明同在 stdout;结构化面另两份——`--json` 的 `skippedLines` 字段、`--export` markdown 的警示行(只报条数,不报路径与坏行内容)
39
44
  - `constraints retire`:两条路径都**走 stderr**——交互模式在候选列表前一行(沿用本函数既有的 console 例外,`console.error`),`--yes` 直达分支在落盘前 `logError(io, …)`(`retireConstraint` 本身按契约「纯执行无交互」不打印)
40
45
  - 原则正本与逐点去向见 `src/monitoring/CONTEXT.md`「约定」段;机器可检面是 `src/utils/__tests__/jsonl-skip-disposition.test.ts`(冻结全仓 skip 读点集合 + 要求每个读点记名声明 `计数去向:`)
46
+ - **一次命令运行内,同一份数据至多取一次(harness#146,#140 A 票;与 ADR-0023 同族,但不扩 `RunEnv` 公共面)**:`spec-baseline-check` 的三条验证路径(文件存在性 / 依赖 / 代码模式)一律从命令级共享索引 `createBaselineIndex(projectPath)` 取数,不再各自裸调 `fs`——全仓 `.ts`/`.js` 内容扫描一遍建成 `路径 → 内容` 表、`package.json` 读解析一次(**读取失败态同样入库**,不逐条重试)、同一落点的存在性探测只 stat 一次;逐条前置与每个关键词只在索引上重放判定。索引**懒建**(没有前置需要某类数据时该类读取一次都不发生)、**运行结束即弃**(不跨命令复用:本命令是独立命令而非 `harness check` 的子步骤,「同一口径取一次」命令内自足即够,#140 triage 裁决 5)。改前代价 = 前置条件数 × 关键词数 × 源文件数,改后与条数无关,判定/证据文案/stdout/退出码逐字不变。**驻留上界(harness#162)**:索引容量有双上限 `SOURCE_INDEX_MAX_ENTRIES = 5000` / `SOURCE_INDEX_MAX_BYTES = 64MB`——超任一上限即放弃驻留、**回落逐文件流式读**(每个关键词重扫一遍,读完即弃),峰值内存由 O(全仓源码) 回到 O(单文件),正常规模仓保留「一次遍历」收益;取数面相应由 `sourceContents(): Map` 改为 `countFilesContaining(keyword)`,容量策略全部收在索引内部。机器可检:`__tests__/spec-baseline-read-count.test.ts`(形状照 `check-read-count.test.ts`:`readFileSync`·只读 `openSync`·`existsSync`·`readdirSync` 记件 + 整张读取表与目录扫描表逐条冻结 + 「同一份数据的同一口径至多取一次」+ N×K 缩放不变式 + 懒建不扫 + 失败态入库)+ `__tests__/spec-baseline-index-cap.test.ts`(驻留上界闸:上限内每文件至多读一遍、超条目/字节上限回落流式且判定不变)
47
+ - **先判定「本次要什么」再决定「读什么」,且清单顺序是对外面(harness#147,#140 B 票)**:`sync-docs/index.ts` 把 CAPABILITIES.md 的**格式判定放在源码扫描之前**——capability-listing 格式的判定面是计数、不做条目级比对,模块清单在本次运行里无人消费,于是整棵源码树**一份 `.ts` 内容都不读**(改造前是无条件全树逐文件 `await readFile`,只为首行注释)。要描述的分支(file-table)照旧取描述,但 `project-reader.ts` 分两步:先按目录遍历序定清单,再经 `extractFileDescriptions()` **并发**取数(上限固定 16,按票下裁决不做成配置面)并**按原索引回填**。清单顺序对外可见(`--check` 逐行比对生成的表格、人看 diff),票下裁决是「顺序逐字保持现状、不改字典序」,故「按并发完成顺序 append」属行为漂移而非优化。读盘次数之外一切不变:判定、漂移检测、写回内容逐字保持。机器可检:`__tests__/sync-docs-read-count.test.ts`(形状照 `check-read-count.test.ts`——listing 模式 `.ts` 内容读 = 0 + file-table 反证每份文件恰读一次 + 整张读取表冻结)、`sync-docs/__tests__/module-descriptions.test.ts`(替身把「起始序」与「完成序」刻意错开:串位或重排即红,另钉上限)、`__tests__/sync-docs-module-order.test.ts`(生成表格行序 = 目录遍历序、两次运行逐字复现)
41
48
 
42
49
  ## 注意事项
43
50
  - 新增命令需同步更新 CLAUDE.md / CAPABILITIES.md / src/CONTEXT.md
51
+ - **scaffold 三态模型(harness#132 已落地)**:init/validate 的「目标在不在场→写或告知」判定(9 站点 = init 7 + validate 2,#132 收口时 8 处)收口为 scaffold 模块。术语:**managed file**(受管文件)= harness 模板拥有正本、用户可持有的落盘文件;三态 = `created`(不在场→落盘)/ `exists`(在场→告知跳过,**不细分内容是否等于模板**,内容比较是独立特性)/ `manual`(在场→打印片段请用户手工合并)。纪律:scaffold 不持有覆盖/跳过策略(`--no-git-hooks`/`--no-github-actions` 由命令层翻成 plan 列表,scaffold 只见 plan);模板留代码内住 `scaffold-templates.ts`,不落 `templates/`(其无运行时消费者、integrity 清单不覆盖);**打印给用户的片段必须是落盘正文的一部分**(GH Actions 站点冲突分支打印完整 workflow 全文而非 job 片段,#103 判据);`--print-snippets` 的片段视图同判据且**由正本裁剪派生**(harness#153:`GITHUB_ACTIONS_SNIPPET` = `HARNESS_CHECK_WORKFLOW` 的 `jobs:` 段之后,形状与 GitLab 侧「打印的是落盘正文的尾巴」对仗。它曾是手抄的第二份文本,比落盘正本少 `validate` / `passes-gate` 两道门禁——照抄的用户拿到比同版本 `harness init` 弱一半的 CI;引导语随之改准为「以下正文取自 harness-check.yml 的 job 段」,`--print-snippets` 旗帜本身保留);gitlab 形同判据由 harness#157 补齐——`printSnippets` 收 `governanceLevel`、打 `renderGitLabCiJobs(governanceLevel)`,与落盘 / 冲突分支同一个正本函数(此前打无 level 的 `GITLAB_CI_SNIPPET`,`-g` 档照抄的用户静默少拿治理与 docs 新鲜度两个任务);CLAUDE.md/AGENTS.md 标记化幂等写(读-改-写形状)不纳入 scaffold;死选项 `-t/--type` 已随本票删除。GitLab CI 接线决议见下条;pre-push 站点已随 harness#144 落地,见「本地防线两道」条
52
+ - 9 站点的落点(#132 收口时 8 处,pre-push 是 harness#144 加的第 9 处):`scaffold.ts` 存 9 个 `ManagedFile` 工厂(目标路径 + 三态文案 + 冲突片段),`init.ts` 只留「前置告知(无 .git / 目录不在场 / 已有 workflow 覆盖治理命令)+ runPlan」;原住 `validate.ts` 的 `createExampleCheckpoint`/`createExampleResolutions` 随工厂迁入 scaffold,`init.ts` 对 `validate` 的反向 import 因此消失,`validate.ts` 反向消费 `scaffold-templates` 的检查点文件路径约定。新增受管文件 = 一个工厂 + 一条 plan。**#132 口径外的第 9 处同形写手**(`sync-docs/context-syncer.ts:createContextMd` 自带一份 CONTEXT.md 正文、与正本已漂移)已于 harness#150 归位——现走 `contextDocFile` + `runPlan`,与 init 共用同一模板正本(`scaffold-templates.ts:renderContextDoc`)
53
+ - 可检面:`__tests__/scaffold.test.ts`(注入内存 fs 替身,零真实文件系统测全三态 + 9 站点文案逐字冻结 + 同源闸及其反证探针(两道 hook 各一条)+ 片段视图的「是正本裁出来的一段」包含闸)、`__tests__/init-ondisk.test.ts`(临时目录真跑 init:第一遍逐字节断言落盘内容并对齐改造前的整屏输出(#149 起这份整屏多出治理段 writer 的一行落盘提示——那行本就一直在真机上,只是此前 io 被丢、不进捕获面),第二遍断言 9 站点第三态与「打印即磁盘正文」;另有「`--print-snippets` 的 GitHub Actions 片段视图」两道闸——同源闸对 `harness-check.yml` 的字节级冻结基线、steps 集合闸按 YAML 解析枚举 job 的 `run:` 命令;harness#157 起「`--print-snippets` 的 GitLab CI 片段视图」四道闸——`-g standard` 同源闸(打印正文逐字等于 `GITLAB_GOVERNED` 基线的任务段 + 与真落盘 `.gitlab-ci.yml` 按 YAML 解析比对 harness-* 任务集合,正本删一个任务即红)、script 集合闸、不传 `-g` 单任务回归、`-g` 非法值 usage-error 零打印)
54
+ - 同族不同面:#150(已落地)是同一份 CONTEXT.md 模板被 `init` 与 `sync-docs` 两个命令各抄一份(`sync-docs/context-syncer.ts:createContextMd` 自带一份、与正本 `renderContextDoc` 已漂移),本条 #153 是同一命令的两个出口(打印 / 落盘)各抄一份——判据同一个(#103),落点不同,两票不并。#150 收口后 `createContextMd` 走 `contextDocFile` + `runPlan`(含 io / 注入 fs,正常只落 `created`),可检面 `__tests__/context-syncer.test.ts`(对撞闸:sync-docs 落点与 init 站点的骨架逐字相同;正本并入两行引导 + 注释形占位钉住;present→exists 不覆盖;#158 补骨架正文逐字冻结字面量——对撞闸只钉「两侧同源」,正本被改由冻结字面量撞红)与 `__tests__/init-ondisk.test.ts`(#158 起 `src/CONTEXT.md` 落盘从 `toContain` 升为 `CONTEXT_MD_BYTES` 字节级基线,同 `PRE_PUSH_HOOK_BYTES` 手法)
55
+ - **CI 平台维度(harness#143 已落地)**:init 的服务端 CI 接线带平台维度。术语:**CI 平台(ci platform)** = 服务端门禁的接线目标(`github` / `gitlab` / `none`,`none` = 不接线,即旧 `--no-github-actions`),一次平台选择贯通三个面(harness-check 站点、治理 CI 面、`--print-snippets` 的 CI 片段)。决议 ①–⑥ 的落地形状:① 显式 `--ci <github|gitlab|none>`、缺省 `github`、**不做 remote URL 自动检测**(init 常跑在首次 push 前,检测错是静默写错文件);② 最小参数化——CI 站点工厂 `harnessCheckCiFile(projectPath, platform, existingCiWorkflows, governanceLevel?)` 收平台参数,GitLab 正文住 `scaffold-templates.ts`(`GITLAB_CI_SNIPPET` / `renderGitLabCiJobs` / `renderGitLabCiFile`),`renderGovernanceWorkflow(level, platform)` 同步参数化,不建注册表抽象;③ 解析链 = flag > `.harness/config.yml` 的 `ci.platform` > `github`(读点 `readConfiguredCiPlatform`,缺失/脏值按未配置),**只有非默认平台才写 `ci.platform`**——github 不写键,而 init 每次都重写 config.yml,故「不写」即等于清除,旧配置零迁移、validate 不报错;④ `--no-github-actions` 保留为 `--ci none` 别名并打 deprecation 警告,与显式 `--ci <非 none>` 同时出现判 `usage-error` 且完全不落盘(不猜意图);⑤ `.gitlab-ci.yml` 三态:不在场 → `created`、在场 → `manual`(打印 harness 任务正文,它是落盘全文的尾巴 → #103 判据成立),GH 侧冲突判定逐字不变;⑥ GitLab 模板与 GH 对仗(同三条命令 + `image: node:20` + `stage: test` + `rules:` 触发 MR 与 main/master push + 无缓存),治理档 `sync-docs --check` 的非阻断对应 `allow_failure: true`
56
+ - 平台维度带来的两处形状差(GH 侧行为逐字节不变):**一个平台一份 CI 文件**——GitLab 没有「第二个 CI 文件位」,`-g` 档的治理任务并入同一份 `.gitlab-ci.yml` 正文,gitlab 形下 `governanceWorkflowFile` 无站点;**`none` = 不接线任何服务端 CI、不创建任何 CI 文件,治理 CI 面随之不建**(harness#156 裁决 F1,推翻 #143「`none` 只摘 harness-check 站点、`-g` 档照建 GH 治理 workflow」的有意保留——旧边界让 `config.yml` 写 `ci.platform: none` 而磁盘上躺着一份 GitHub Actions 文件,状态自相矛盾);治理文档面(CHANGELOG、约束段、CONTEXT.md)由 `-g` 独立决定,`none` 下照常生成。`--print-snippets` 照出 GH 片段(非落盘面,#156 未动)。切回 `github` 时 init 不删既有的 `.gitlab-ci.yml`——scaffold 只有「落盘 / 告知」两种动作,删除不是它的能力
57
+ - `-g` 值域校验在命令入口(harness#156 裁决 F2,与 #152 脏输入 fail-loud 同判据):合法值域 = `GOVERNANCE_PRESETS` 键集合(入口校验与装配共用同一份表),非法值判 `usage-error` 退 1、先于任何落盘,github / gitlab / none 三平台同判(旧行为:gitlab 侧非法值透传模板落三个任务、github 侧查表 miss 静默不建——同一输入两种后果)。隶属判定用 `Object.hasOwn` 而非 `in`(harness#164):`in` 走原型链,`-g constructor`/`toString` 曾穿透校验、把 `Object` 构造函数写进 `configData` 并在 `yaml.dump` 抛 `YAMLException`
58
+ - 可检面:`__tests__/scaffold.test.ts`「CI 站点的平台维度」+ 同源闸两形覆盖、`__tests__/init-ondisk.test.ts`「init --ci 平台维度」(`.gitlab-ci.yml` 逐字节 / 第二趟 manual / 治理并入 / 持久化回读 / 别名与冲突与非法值)与「init --ci × -g 组合边界(harness#156)」(`--ci none -g standard` 落盘文件集合 + config.yml 两投影逐字冻结、`-g` 非法值三平台一致性钉、合法值回归)
59
+ - **本地防线两道(harness#144 已落地)**:术语:**本地防线** = init 落的两个 Git hook 站点,分工是设计而非冗余——**pre-commit** 查暂存的(`check --staged` + plan 覆盖,增量、快反馈),**pre-push** 查整仓的(第 9 站点 `prePushHookFile` → `.git/hooks/pre-push`,`mode 0o755`,全量 `harness check` 不带 `--staged` + `harness validate`,任一失败 `exit 1` 挡住 push),兜的是 `git commit --no-verify` 绕出去的内容。三道决定按 #132 grilling 定论钉死:① **不做增量**——不解析 pre-push 从 stdin 收到的 `<local_ref> <local_oid> <remote_ref> <remote_oid>` 清单,无论推什么(含删远端分支这种 stdin 空清单的场景)整仓过一遍,钩子内子进程一律 `< /dev/null`、不让它吃掉 ref 清单;② **不内建逃生机制**——无环境变量开关、无超时、无「慢则降级」,唯一口子是 git 原生 `git push --no-verify`;③ **定位不变**——本地 hook = 自检与提醒,服务端 CI(见「CI 平台维度」条)才是门禁,pre-push 不替代它。`--no-git-hooks` 一并覆盖本站点(策略仍在命令层翻成 plan,scaffold 不持策略、不新增跳过旗帜);`--print-snippets` 与冲突分支打印的片段 = 落盘正文去 shebang 头(#103 判据);init 收尾提示与本站点引导语写明「查暂存的 / 查整仓的,重复是设计使然」。仓库自身的 `bin/install-precommit-hook.sh`(`npm run hooks:install`)仍是 pre-commit 单道,本票不动它
60
+ - **knowledge view 收口模型(harness#133 已落地)**:knowledge 11 个子操作的「取数 + json 投影 + 人读排版 + 退出码」四件事拆开——命令模块只产 `{ data, human() }`(`data` 是 `--json` 正文的唯一正本,11 个 `knowledgeXxxView` 具名导出即投影本体),`knowledge-view.ts` 收口排版渲染、上色、label 映射、json/人读分派与退出码、路径解析与 store 构造。术语:**display model** = `human()` 返回的结构化人读视图 `{ sections: [{ title, rows: [{ cells }] }] }`,一格(cell)三选一:`label`(纯文案)/ `field`(点分路径声明它投影自 json 面哪个字段,数组下标与 `.length` 合法)/ `derived`(json 面没有的派生量,须在测试豁免表登记理由)。空行是显式的一行(`blankLine()`),不再靠 `\n` 前后缀藏排版。颜色只经 `DisplayTone` 角色查 `TONE_STYLES` 一张表,命令侧零 `chalk` 调用。可检面:`__tests__/knowledge-view.test.ts`(四道闸:源形状 / 11·11 json 字段清单冻结 / 两投影一致性 + 派生量双向对撞 / 满态与空态人读逐行冻结,基线取自改造前实现实测捕获)+ `__tests__/knowledge-tone-mapping.test.ts`(#151 加:tone 映射的纯函数级冻结,判据见下两条)。
61
+ - **冻结类测试的两条判据(#133 收尾须知自报第一条,#151 补完其推论)**:
62
+ - **fixture 走不到的分支须逐个建形,不共用一个空根**。闸 4b 的 `emptyRoot()` 是裸 `mkdtemp` 目录,天然没有 `.harness/knowledge-docs/`,于是 `syncRagRows` 的两个空态分支只有第一个(`!docsPresent`)进过基线,第二个(目录在场、无 `.md`)在测试里走不到——文案改字全绿。而 json 面两分支恒同形(`data = {directory, files}`),`--json` 断言钉不住人读第二行。落点:闸 4c 的平行表 `EXTRA_EMPTY_HUMAN_CASES`(每 case 自带 `makeRoot` + 人读逐行基线 + json 形状;不改 `EMPTY_HUMAN_BASELINE: Record<SubName, string[]>` 的形状,一个子命令只装一种形状的约束留在原表)。
63
+ - **渲染类映射(tone / label / 颜色)必须有纯函数级断言,identity-mock 的文本基线不算覆盖**。`knowledge-view.test.ts` 把 chalk mock 成 identity(其注释「人读断言看的是文本,不看 ANSI」即本条病灶的自供),于是 `TONE_STYLES` 与 `toneForMaturity`/`toneForSeverity`/`toneForScore` 全仓零命中,#133 有意偏离③(`stats` 的 `verified` 由黄变青)改回去不会有任何测试红。落点:`knowledge-tone-mapping.test.ts` 逐格冻两张表——「输入值 → tone」(6 个成熟度 / 7 个 severity / 分数边界各一格)与「tone → chalk 样式键」(7 个档位;chalk 替身按样式键给文本加标签,断言走 `renderDisplayModel` / `announce` 的真实渲染链而非只读表),另加「表键集合穷尽」双钉(编译期 `Record<DisplayTone, …>` 缺键即 tsc 红 + 运行期与源码 `DisplayTone` 联合、`TONE_STYLES` 字面量逐字对撞)。**生产侧零改动**:`TONE_STYLES` 仍不导出——本闸钉的是它的行为与声明形状,不是它的可访问性(导出会动 `knowledge-view.ts` 的公开面,ADR-0022 口径下得另走定级)。#158 补最后一跳:`knowledge-view.test.ts` 另有一条 display model 结构断言「stats 成熟度格必须携带 `tone` 且 = `toneForMaturity(maturity)`」——命令侧把 `tone:` 从格上摘掉(复审 M7)由它撞红,看结构不看 ANSI,不违 identity-mock 立场。
64
+ - **json 面现状冻结**:11 个顶层形状逐字不动(双仓核实 `knowledge --json` 零程序化消费者,studio 走 library 直调;统一包壳是纯 churn,删除 `--json` 死面按 ADR-0022 口径另票评估)。三处有意偏离,均不改字段集合:① `snapshot`/`index` 的 `--json` 由单行紧凑改为两空格缩进(分派单点化的直接结果,与其余 9 个子操作同形);② `sync-rag` 的两个空态此前把人类文案直接打到 `--json` 的 stdout(吐出的不是 JSON),现由 display model 承担,json 面恒为 `{directory, files}`;③ `stats` 人读面 `verified` 成熟度的颜色随 `toneForMaturity` 单表变为青色(此前 list 青 / stats 黄两处手抄不一致,文字不变)。
65
+ - 与 #134(KnowledgeAudit 收 store)独立落地,唯一交点 `knowledgeAudit` 命令函数——#134 是后到方并已适配:`knowledgeAuditView` 经 `openKnowledgeStore` 取 store 作引擎第一形参、`shortContentThreshold` 作第二形参(构造期不再自建 store)。审计后重建索引「只在人读路径发生」是本票保留的既有策略(`--json` 不写盘),也是 `knowledge.ts` 内被逐行冻结的唯一 `options.json` 分支。
44
66
  - 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 报警)
45
67
  - PRESERVE:governance 段内写入纪律:机器管理的只有 HARNESS_CONSTRAINTS 标记区间——有标记只换标记区间,无标记(纯手写段)在段尾追加注入段;段内其余手写内容(治理契约引言/流程/纪律等)必须原样保留(曾整段替换清空手写内容的回归,init-injection.test 有防回归用例)
46
68
  - 注入段落点路由与 marker-range 替换收口在 `core/constraints/injection-writer`(ADR-0011):读侧 `resolveInjectionTarget`(漂移检测/retire 同步共用「CLAUDE.md 有标记优先,否则 AGENTS.md」)+ 写侧 `resolveGovernanceLanding`(init 落点选择);init 三个治理段 writer 与 retire 注入同步均为「渲染 body + 调 writer」,半标记(单边/乱序)一律拒写告警;未注入 = 两处均无完整标记段
47
69
  - knowledge 命令包含 11 个子操作(list/search/import/decay/stats/sync-rag/audit/snapshot/migrate/index/health);upsert/sync-status 已迁至 studio CLI(harness#110,二者硬编码 localhost Studio 内部端点,不属 harness「通用框架、文件驱动」定位)
48
70
  - `stats` / `health` 的飞轮数字不在 CLI 内计算:分子口径唯一实现是 `knowledge/flywheel-metrics.ts`(`evaluateFlywheel`,ADR-0013),本层只做百分比取整/一位小数与字段名映射;`.consumption-stats.json` 的读取留在各命令(module 零 IO)
49
71
  - 特殊路由(选项条件、子命令兜底、裸跑语义)表达在定义表的 optionRoutes / subcommands / subcommandStrict / bareRunsAction 字段,bin 是纯通用引擎、不含单命令知识
72
+ - **数值旗帜装配口径(harness#152)**:`<n>` / `<rate>` 这类数值旗帜经 commander 到命令侧**恒是字符串**(`defaultValue: '50'` 也是),所以入参声明必须跟着运行时形状走——装配点之前声明 `string`(或双形 `string | number`,如 `failure --limit`),数值只在装配点转一次。装配点有两处:**有 `mapActionArgs` 的命令在定义表装配**(`status --hours`、`constraints report` 三阈值、门禁 `--coverage-threshold`/`--bundle-threshold`/`--min-reviewers`),**knowledge 族是「纯投影条目」故编组归命令模块**(`knowledgeSearchCommand` 的 `--limit`、`knowledgeAudit` 的 `--threshold`);两形都对,判据是「声明与运行时一致」,因此 `as any` 不该出现在这条链上(它替编译器记住了旗帜的实际形状,去掉 `parseInt` 就是往数值槽塞字符串)。**转不出来必须 fail-loud**(`logError` + `usage-error`,非零退出):下游兜底写的多是 `?? 缺省值`,而 `??` 只兜 `null`/`undefined`、NaN 照单全收,于是一句 `len < NaN` 恒 false 就把一道质量判定静默关掉、退出码仍是 0(#152 病灶正本:`knowledge audit --threshold abc`)。上游按口径挡住的,引擎侧不再重复兜(库面向的守卫已落 harness#163:`KnowledgeAudit` 构造器对 `shortContentThreshold`/`staleDays`/`promotionBlockDays` 三槽的显式脏值(NaN/±Infinity/负数)构造期抛 `TypeError`,未传/显式 `undefined` 走缺省、显式 `0` 合法)。可检面:`__tests__/knowledge-audit-threshold.test.ts`(脏值 fail-loud / 缺省 '50' 与未传同果 / 显式 '0' 不被当未传 / `knowledge.ts` 零 `as any`)+ `__tests__/bin-exit-mapping.test.ts`(对外退出码 1 的端到端证据);knowledge 的 `kind:` 逐行冻结豁免表在 `__tests__/knowledge-view.test.ts` 闸 1(#152 起两行、#154 起三行,理由随表注)。其余数值旗帜的脏输入后果(fail-closed 与静默按缺省两类)逐项判定见 #152 票面,#152 只收 `knowledge audit --threshold` 一处;**harness#154 已把口径推广到全部数值旗帜**(已推广清单如下),**唯一解析器 = `src/utils/numeric-flag.ts`**(本条目指名的正本:`parseNumericFlag` 判别 + `numericFlagMessage` 文案模板 + `requireNumericFlag` 抛出 + `NumericFlagError`;`parseInt`/`parseFloat` 的前缀截断 `'30abc'→30`、`'1e2'→1` 同属静默误读,禁回)。两消费形同一条判定规则:定义表 `mapActionArgs` 站点(`constraints report` 三阈值、`performance --coverage-threshold`/`--bundle-threshold`、`review --min-reviewers`)走 `requireNumericFlag` 抛出、bin 在 `runDefinition` 捕获映射 usage-error——定义表是纯数据模块返回不了 CommandResult,装配失败的通道只此一条,bin 不因此新增单命令知识;命令模块站点(`knowledge search --limit`、`failure list --limit`、`passes-gate --coverage-threshold` 的 `coverageCheck`)走 `parseNumericFlag` 就地 `logError` + `usage-error`,且都先于 store/recorder 构造与任何执行(脏值零落盘)。#152 的 `assembleShortContentThreshold` 改为委派 `parseNumericFlag`(函数面与 #152 报错文案保留、冻结测试钉死,未新建第二套解析器)。**#152 判为 fail-closed 的两处不保留、一并收敛 fail-loud**(#154 裁定):`passes-gate --coverage-threshold` 的「覆盖率不足 (x% < NaN%)」自证异常文案与 `review --min-reviewers` 的恒不通过——脏输入属用法错误、自证异常的文案即缺陷;合法输入路径逐字不变,退出码面 review 1→1、passes-gate coverage 脏值 0→1 属缺陷修正(定级与 CHANGELOG 入账见该票)。边界不动:门禁/引擎构造器侧的 falsy/`??` 约定是库面向语义(显式 `0` 在 performance 门禁内仍 = 不检查该项),与 #163 同例不重复兜;`status --hours` 是死旗帜,另族另票。可检面新增:`src/utils/__tests__/numeric-flag.test.ts`(解析规则矩阵)+ `src/cli/commands/__tests__/numeric-flag-assembly.test.ts`(定义表逐旗帜装配 + 命令入口 + 端到端 spawn:脏输入 / 合法路径对拍 / 显式 `0`)
50
73
  - **命令 interface = `CommandResult` + 注入 io(架构评审候选7)**:命令实现一律声明 `Promise<CommandResult>`(判别联合 `ok|skip|fail|usage-error`,`fail`/`usage-error` 必附可定位的 `reason`,多闸门命令 reason 含 `gate <id>`),末位可选形参 `io: CommandIO = processIO`;类型与写入面在 `src/cli/command-contract.ts`
74
+ - **writer 层 `io` 必传,缺省只留在对外命令入口(harness#149)**:`= processIO` 的缺省是给 bin 不注入时的对外入口用的(每个命令模块一处,如 `init()`)。命令内部的落盘 writer 与它们的路由一律收**必传** `io`——缺省留在链路上时,调用点漏传不报错,只是静默把提示兜到真实 stdout:`captureIO()` 的测试面因此天然缺那批行,`⚠️ …标记残缺…请人工修复` 这类唯一提示写坏了没有测试会红。init 治理段的三个 writer(Output Style / AGENTS.md / CLAUDE.md,共 14 条 `log(io, …)`)与落点路由 `setupGovernanceConstraints` 已按此收口;机器可检:`__tests__/init-governance-io.test.ts`(源形状闸:`init.ts` 内该缺省只剩 `init()` 一处 + 四个 writer 签名必传 + `setupGovernance` 调用点逐个传 io)与行为面(临时目录真跑 init,断言这批提示落进注入面;#158 把 `⚠️ …标记残缺…请人工修复` 四处分支补齐——CLAUDE.md 的 Output Style 段 / AGENTS.md 的 PRESERVE 外层单边 / 段内 HARNESS_CONSTRAINTS 单边 / CLAUDE.md 的 HARNESS_CONSTRAINTS 单边各一条捕获面断言,fixture 各自单建)
51
75
  - **本目录零 `process.exit` / `process.exitCode`**:kind → 退出码的唯一映射在 `bin/harness.js`(`ok`/`skip` → 0,`fail`/`usage-error` → 1,未知 kind fail-closed)。历史上两处条件式(`command --level` 按严重级、门禁按 passed)已由命令侧译成 kind;`sync-docs --check` 的漂移改由实现返回 `fail`(定义表的 `afterRun` 逃生门随之废除)
52
76
  - **输出不得直接用 console**:流式打印走 `log(io, …)` / `logError(io, …)`(`util.format` + 换行,与 console 逐字节等价,见 `src/cli/__tests__/command-contract.test.ts`);测试断言输出用 `captureIO()`,不再 `spyOn(process, 'exit')`;`--json` 输出断言用 `lastJsonOutput(io)`(JSON.parse 正本,harness#108)。例外:`constraints retire` 交互正文仍走 console(其 `RetireIO` 只注入 readline 流),退役结果打印已接注入流
53
- - **#95 同型扫荡结论**:两门禁站点已修(`passes-gate` 执行/证据落 projectPath、`acceptance` 去掉 `tasksPath: './tasks.yml'` 相对默认值)。逐点判定后的豁免三处,理由与站点原文一起冻结在 `__tests__/project-path-convention.test.ts` 的豁免表:`release` 的 `pkgPath`(定义表本无 `-p`,pkgPath 就是该命令唯一的根且已逐个传给每条 `run(cmd, pkgPath)`,不构成半失效;给发布流水线新增 `-p` 属新能力)、`constraints` 的 `join(process.cwd(), 'package.json')`(报的是 harness 自身包版本,主锚 `__dirname`,cwd 仅兜底)、`core/spec/validator.ts` 的 `schemaPath: './specs/schemas'`(**确是同型病灶**:`validateAll(projectPath)` 用 projectPath 找 spec 文件、却用 cwd 找 schema;但 `validateFile`/`loadSchema` 签名里没有根,补齐要穿透整个 spec 域,留待 spec 单票收口)
77
+ - **#95 同型扫荡结论**:两门禁站点已修(`passes-gate` 执行/证据落 projectPath、`acceptance` 去掉 `tasksPath: './tasks.yml'` 相对默认值)。逐点判定后的豁免四处,理由与站点原文一起冻结在 `__tests__/project-path-convention.test.ts` 的豁免表:`release` 的 `pkgPath`(定义表本无 `-p`,pkgPath 就是该命令唯一的根且已逐个传给每条 `run(cmd, pkgPath)`,不构成半失效;给发布流水线新增 `-p` 属新能力)、`command` 的 `evaluate({ projectPath: process.cwd(), … })`(`CommandGate` 只做命令串正则判定、零文件读写,且该命令定义表里没有 `-p`,取 cwd 仅为满足 `GateContext` 形状,架构评审候选1)、`constraints` 的 `join(process.cwd(), 'package.json')`(报的是 harness 自身包版本,主锚 `__dirname`,cwd 仅兜底)、`core/spec/validator.ts` 的 `schemaPath: './specs/schemas'`(**确是同型病灶**:`validateAll(projectPath)` 用 projectPath 找 spec 文件、却用 cwd 找 schema;但 `validateFile`/`loadSchema` 签名里没有根,补齐要穿透整个 spec 域,留待 spec 单票收口)