@hominis/fireforge 0.49.2 → 0.51.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 (400) hide show
  1. package/CHANGELOG.md +87 -1
  2. package/README.md +1 -1
  3. package/dist/bin/fireforge.js +6 -0
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/commands/build-prelock.d.ts +20 -0
  6. package/dist/src/commands/build-prelock.js +50 -0
  7. package/dist/src/commands/build.d.ts +3 -1
  8. package/dist/src/commands/build.js +13 -6
  9. package/dist/src/commands/check-report.d.ts +41 -0
  10. package/dist/src/commands/check-report.js +91 -0
  11. package/dist/src/commands/check-run.d.ts +33 -0
  12. package/dist/src/commands/check-run.js +112 -0
  13. package/dist/src/commands/check-steps.d.ts +75 -0
  14. package/dist/src/commands/check-steps.js +200 -0
  15. package/dist/src/commands/check.d.ts +32 -0
  16. package/dist/src/commands/check.js +160 -0
  17. package/dist/src/commands/config.js +5 -6
  18. package/dist/src/commands/declarations-report.d.ts +43 -0
  19. package/dist/src/commands/declarations-report.js +76 -0
  20. package/dist/src/commands/doctor-engine-eslint.js +6 -1
  21. package/dist/src/commands/doctor-source-pin.js +4 -1
  22. package/dist/src/commands/export-adopt-check.d.ts +38 -0
  23. package/dist/src/commands/export-adopt-check.js +98 -0
  24. package/dist/src/commands/export-flow.js +29 -14
  25. package/dist/src/commands/export-placement-gate.d.ts +6 -2
  26. package/dist/src/commands/export-placement-gate.js +8 -0
  27. package/dist/src/commands/export-shared.d.ts +2 -1
  28. package/dist/src/commands/export-staged-registration.d.ts +46 -0
  29. package/dist/src/commands/export-staged-registration.js +91 -0
  30. package/dist/src/commands/forward-import-remedy.d.ts +32 -0
  31. package/dist/src/commands/forward-import-remedy.js +63 -0
  32. package/dist/src/commands/furnace/apply.js +3 -3
  33. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
  34. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  35. package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
  36. package/dist/src/commands/furnace/chrome-doc.js +2 -2
  37. package/dist/src/commands/furnace/create.js +2 -2
  38. package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
  39. package/dist/src/commands/furnace/deploy-named.js +107 -0
  40. package/dist/src/commands/furnace/deploy.d.ts +3 -2
  41. package/dist/src/commands/furnace/deploy.js +50 -86
  42. package/dist/src/commands/furnace/diff.js +26 -3
  43. package/dist/src/commands/furnace/index.js +66 -41
  44. package/dist/src/commands/furnace/init.js +13 -6
  45. package/dist/src/commands/furnace/override.js +4 -2
  46. package/dist/src/commands/furnace/preview.js +4 -3
  47. package/dist/src/commands/furnace/refresh.js +2 -2
  48. package/dist/src/commands/furnace/remove.js +4 -3
  49. package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
  50. package/dist/src/commands/furnace/rename-dry-run.js +127 -0
  51. package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
  52. package/dist/src/commands/furnace/rename-helpers.js +15 -1
  53. package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
  54. package/dist/src/commands/furnace/rename-registrations.js +98 -0
  55. package/dist/src/commands/furnace/rename.d.ts +2 -1
  56. package/dist/src/commands/furnace/rename.js +28 -66
  57. package/dist/src/commands/furnace/scan.d.ts +9 -4
  58. package/dist/src/commands/furnace/scan.js +11 -9
  59. package/dist/src/commands/furnace/sync.js +3 -2
  60. package/dist/src/commands/furnace/validate-report.d.ts +45 -0
  61. package/dist/src/commands/furnace/validate-report.js +91 -0
  62. package/dist/src/commands/furnace/validate.d.ts +1 -1
  63. package/dist/src/commands/furnace/validate.js +151 -113
  64. package/dist/src/commands/furnace/validation-output.js +14 -3
  65. package/dist/src/commands/lint-per-patch.js +36 -61
  66. package/dist/src/commands/lint-per-run-checkjs.js +2 -2
  67. package/dist/src/commands/lint-report.d.ts +8 -4
  68. package/dist/src/commands/lint-report.js +20 -5
  69. package/dist/src/commands/lint.js +15 -34
  70. package/dist/src/commands/manifest.js +4 -0
  71. package/dist/src/commands/park.d.ts +22 -0
  72. package/dist/src/commands/park.js +232 -0
  73. package/dist/src/commands/patch/delete-gap-notice.js +3 -1
  74. package/dist/src/commands/patch/delete.d.ts +1 -1
  75. package/dist/src/commands/patch/delete.js +24 -104
  76. package/dist/src/commands/patch/index.d.ts +3 -3
  77. package/dist/src/commands/patch/index.js +10 -4
  78. package/dist/src/commands/patch/lint-ignore-from.d.ts +44 -0
  79. package/dist/src/commands/patch/lint-ignore-from.js +142 -0
  80. package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
  81. package/dist/src/commands/patch/lint-ignore.js +78 -31
  82. package/dist/src/commands/patch/move-files-create.js +34 -18
  83. package/dist/src/commands/patch/move-files-into.js +15 -7
  84. package/dist/src/commands/patch/move-files.js +6 -2
  85. package/dist/src/commands/patch/removal-created-files.d.ts +48 -0
  86. package/dist/src/commands/patch/removal-created-files.js +83 -0
  87. package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
  88. package/dist/src/commands/patch/removal-dependents.js +133 -0
  89. package/dist/src/commands/patch/rename.js +20 -1
  90. package/dist/src/commands/patch/reserve.d.ts +33 -0
  91. package/dist/src/commands/patch/reserve.js +152 -0
  92. package/dist/src/commands/patch/retire.d.ts +34 -0
  93. package/dist/src/commands/patch/retire.js +133 -0
  94. package/dist/src/commands/patch/split-plan.d.ts +17 -8
  95. package/dist/src/commands/patch/split-plan.js +60 -28
  96. package/dist/src/commands/patch/split-staged.d.ts +108 -0
  97. package/dist/src/commands/patch/split-staged.js +202 -0
  98. package/dist/src/commands/patch/split.d.ts +5 -0
  99. package/dist/src/commands/patch/split.js +62 -17
  100. package/dist/src/commands/patch/stage.d.ts +34 -0
  101. package/dist/src/commands/patch/stage.js +153 -0
  102. package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
  103. package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
  104. package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
  105. package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
  106. package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
  107. package/dist/src/commands/patch/staged-dependency-from.js +108 -0
  108. package/dist/src/commands/patch/staged-dependency-remove.d.ts +31 -0
  109. package/dist/src/commands/patch/staged-dependency-remove.js +80 -0
  110. package/dist/src/commands/patch/staged-dependency.js +54 -139
  111. package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
  112. package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
  113. package/dist/src/commands/re-export-adjacent.d.ts +50 -2
  114. package/dist/src/commands/re-export-adjacent.js +160 -40
  115. package/dist/src/commands/re-export-bulk-scan.d.ts +2 -1
  116. package/dist/src/commands/re-export-bulk-scan.js +22 -7
  117. package/dist/src/commands/re-export-drift.d.ts +36 -8
  118. package/dist/src/commands/re-export-drift.js +73 -49
  119. package/dist/src/commands/re-export-files.js +11 -8
  120. package/dist/src/commands/re-export-hunks.d.ts +35 -0
  121. package/dist/src/commands/re-export-hunks.js +220 -0
  122. package/dist/src/commands/re-export-lint.d.ts +2 -1
  123. package/dist/src/commands/re-export-lint.js +23 -10
  124. package/dist/src/commands/re-export-options.js +74 -6
  125. package/dist/src/commands/re-export-preflight.d.ts +23 -0
  126. package/dist/src/commands/re-export-preflight.js +160 -0
  127. package/dist/src/commands/re-export-purity-scope.d.ts +18 -0
  128. package/dist/src/commands/re-export-purity-scope.js +131 -0
  129. package/dist/src/commands/re-export-register.js +56 -17
  130. package/dist/src/commands/re-export-removed.d.ts +13 -2
  131. package/dist/src/commands/re-export-removed.js +57 -12
  132. package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
  133. package/dist/src/commands/re-export-scan-owner.js +116 -0
  134. package/dist/src/commands/re-export-scan.d.ts +19 -9
  135. package/dist/src/commands/re-export-scan.js +60 -24
  136. package/dist/src/commands/re-export-single.js +122 -46
  137. package/dist/src/commands/re-export-stale-registrations.d.ts +18 -0
  138. package/dist/src/commands/re-export-stale-registrations.js +62 -0
  139. package/dist/src/commands/re-export-summary.d.ts +28 -0
  140. package/dist/src/commands/re-export-summary.js +36 -0
  141. package/dist/src/commands/re-export.js +71 -57
  142. package/dist/src/commands/rebase/index.js +17 -17
  143. package/dist/src/commands/run.js +16 -2
  144. package/dist/src/commands/status-check.d.ts +55 -16
  145. package/dist/src/commands/status-check.js +92 -37
  146. package/dist/src/commands/status-json.d.ts +22 -9
  147. package/dist/src/commands/status-json.js +47 -32
  148. package/dist/src/commands/status-output.js +3 -1
  149. package/dist/src/commands/status-scope.d.ts +37 -0
  150. package/dist/src/commands/status-scope.js +54 -0
  151. package/dist/src/commands/status.js +66 -23
  152. package/dist/src/commands/test-browser-preflight.js +19 -3
  153. package/dist/src/commands/test-build-furnace.d.ts +22 -0
  154. package/dist/src/commands/test-build-furnace.js +54 -0
  155. package/dist/src/commands/test-build-phase.js +64 -41
  156. package/dist/src/commands/test-checkjs.js +10 -2
  157. package/dist/src/commands/test-diagnose.js +7 -0
  158. package/dist/src/commands/test-dispatch.d.ts +39 -0
  159. package/dist/src/commands/test-dispatch.js +211 -0
  160. package/dist/src/commands/test-furnace-reach.d.ts +37 -0
  161. package/dist/src/commands/test-furnace-reach.js +132 -0
  162. package/dist/src/commands/test-modes.d.ts +16 -1
  163. package/dist/src/commands/test-modes.js +24 -1
  164. package/dist/src/commands/test-multi-verdict.d.ts +61 -0
  165. package/dist/src/commands/test-multi-verdict.js +111 -0
  166. package/dist/src/commands/test-orchestrate.d.ts +52 -0
  167. package/dist/src/commands/test-orchestrate.js +240 -0
  168. package/dist/src/commands/test-perf-scope.d.ts +50 -0
  169. package/dist/src/commands/test-perf-scope.js +138 -0
  170. package/dist/src/commands/test-prelock.d.ts +51 -0
  171. package/dist/src/commands/test-prelock.js +273 -0
  172. package/dist/src/commands/test-register.js +69 -10
  173. package/dist/src/commands/test-results.d.ts +62 -0
  174. package/dist/src/commands/test-results.js +62 -0
  175. package/dist/src/commands/test-run.d.ts +13 -55
  176. package/dist/src/commands/test-run.js +74 -99
  177. package/dist/src/commands/test-shards.d.ts +110 -0
  178. package/dist/src/commands/test-shards.js +255 -0
  179. package/dist/src/commands/test-stale-gate.d.ts +5 -1
  180. package/dist/src/commands/test-stale-gate.js +60 -28
  181. package/dist/src/commands/test-verdict.d.ts +11 -1
  182. package/dist/src/commands/test-verdict.js +28 -1
  183. package/dist/src/commands/test.d.ts +6 -1
  184. package/dist/src/commands/test.js +120 -124
  185. package/dist/src/commands/token-lint.d.ts +14 -0
  186. package/dist/src/commands/token-lint.js +151 -0
  187. package/dist/src/commands/token.js +10 -0
  188. package/dist/src/commands/verify-scope.d.ts +28 -0
  189. package/dist/src/commands/verify-scope.js +67 -0
  190. package/dist/src/commands/verify.d.ts +10 -2
  191. package/dist/src/commands/verify.js +67 -36
  192. package/dist/src/core/build-baseline-types.d.ts +46 -0
  193. package/dist/src/core/build-baseline-validate.js +19 -0
  194. package/dist/src/core/build-baseline.d.ts +5 -1
  195. package/dist/src/core/build-baseline.js +3 -1
  196. package/dist/src/core/build-overwrite-guard.d.ts +50 -0
  197. package/dist/src/core/build-overwrite-guard.js +84 -4
  198. package/dist/src/core/build-prepare.d.ts +13 -0
  199. package/dist/src/core/build-prepare.js +42 -10
  200. package/dist/src/core/build-run-record.d.ts +74 -0
  201. package/dist/src/core/build-run-record.js +136 -0
  202. package/dist/src/core/checkjs-support-heads.d.ts +41 -0
  203. package/dist/src/core/checkjs-support-heads.js +104 -0
  204. package/dist/src/core/config-paths.d.ts +2 -2
  205. package/dist/src/core/config-paths.js +10 -0
  206. package/dist/src/core/config-validate-check.d.ts +8 -0
  207. package/dist/src/core/config-validate-check.js +219 -0
  208. package/dist/src/core/config-validate-file-size.d.ts +9 -6
  209. package/dist/src/core/config-validate-file-size.js +91 -2
  210. package/dist/src/core/config-validate-history.d.ts +11 -0
  211. package/dist/src/core/config-validate-history.js +38 -0
  212. package/dist/src/core/config-validate-patch-policy.js +100 -0
  213. package/dist/src/core/config-validate-test-toolchains.js +21 -0
  214. package/dist/src/core/config-validate-token-scales.d.ts +9 -0
  215. package/dist/src/core/config-validate-token-scales.js +92 -0
  216. package/dist/src/core/config-validate.js +20 -5
  217. package/dist/src/core/config.d.ts +5 -0
  218. package/dist/src/core/config.js +16 -0
  219. package/dist/src/core/coverage-dispatch.d.ts +24 -0
  220. package/dist/src/core/coverage-dispatch.js +44 -0
  221. package/dist/src/core/coverage-extend.d.ts +70 -45
  222. package/dist/src/core/coverage-extend.js +199 -144
  223. package/dist/src/core/coverage-records.d.ts +108 -0
  224. package/dist/src/core/coverage-records.js +384 -0
  225. package/dist/src/core/destructive.d.ts +7 -4
  226. package/dist/src/core/destructive.js +10 -10
  227. package/dist/src/core/engine-session-lock.d.ts +16 -1
  228. package/dist/src/core/engine-session-lock.js +38 -10
  229. package/dist/src/core/export-moved-code-shape.d.ts +49 -11
  230. package/dist/src/core/export-moved-code-shape.js +135 -44
  231. package/dist/src/core/file-lock-queue.d.ts +67 -0
  232. package/dist/src/core/file-lock-queue.js +221 -0
  233. package/dist/src/core/file-lock-shared.d.ts +57 -0
  234. package/dist/src/core/file-lock-shared.js +117 -0
  235. package/dist/src/core/file-lock.d.ts +13 -25
  236. package/dist/src/core/file-lock.js +163 -140
  237. package/dist/src/core/furnace-apply-output.d.ts +10 -0
  238. package/dist/src/core/furnace-apply-output.js +31 -1
  239. package/dist/src/core/furnace-apply.js +22 -12
  240. package/dist/src/core/furnace-composed-source.d.ts +76 -0
  241. package/dist/src/core/furnace-composed-source.js +167 -0
  242. package/dist/src/core/furnace-config-validate-exceptions.d.ts +16 -0
  243. package/dist/src/core/furnace-config-validate-exceptions.js +102 -0
  244. package/dist/src/core/furnace-config-validate.js +14 -0
  245. package/dist/src/core/furnace-css-fragments.d.ts +10 -0
  246. package/dist/src/core/furnace-css-fragments.js +26 -0
  247. package/dist/src/core/furnace-operation.d.ts +3 -3
  248. package/dist/src/core/furnace-registration-jar-inplace.d.ts +12 -0
  249. package/dist/src/core/furnace-registration-jar-inplace.js +80 -0
  250. package/dist/src/core/furnace-registration-remove.d.ts +22 -1
  251. package/dist/src/core/furnace-registration-remove.js +53 -17
  252. package/dist/src/core/furnace-registration-rename.d.ts +26 -0
  253. package/dist/src/core/furnace-registration-rename.js +77 -0
  254. package/dist/src/core/furnace-registration.d.ts +14 -4
  255. package/dist/src/core/furnace-registration.js +44 -10
  256. package/dist/src/core/furnace-staleness.d.ts +7 -0
  257. package/dist/src/core/furnace-staleness.js +34 -24
  258. package/dist/src/core/furnace-validate-accessibility.d.ts +8 -4
  259. package/dist/src/core/furnace-validate-accessibility.js +71 -28
  260. package/dist/src/core/furnace-validate-exceptions.d.ts +48 -0
  261. package/dist/src/core/furnace-validate-exceptions.js +80 -0
  262. package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
  263. package/dist/src/core/furnace-validate-icon-button.js +78 -0
  264. package/dist/src/core/furnace-validate.js +1 -1
  265. package/dist/src/core/harness-port-fallback.d.ts +60 -0
  266. package/dist/src/core/harness-port-fallback.js +96 -0
  267. package/dist/src/core/history-log-file.d.ts +61 -0
  268. package/dist/src/core/history-log-file.js +164 -0
  269. package/dist/src/core/history-log.d.ts +2 -1
  270. package/dist/src/core/history-log.js +2 -1
  271. package/dist/src/core/json-preserving-edit.d.ts +22 -0
  272. package/dist/src/core/json-preserving-edit.js +199 -0
  273. package/dist/src/core/line-prefixer.d.ts +18 -0
  274. package/dist/src/core/line-prefixer.js +34 -0
  275. package/dist/src/core/mach.d.ts +7 -0
  276. package/dist/src/core/mach.js +32 -10
  277. package/dist/src/core/module-resolution-preflight.d.ts +19 -3
  278. package/dist/src/core/module-resolution-preflight.js +24 -6
  279. package/dist/src/core/negative-control-compiled.d.ts +27 -0
  280. package/dist/src/core/negative-control-compiled.js +73 -0
  281. package/dist/src/core/negative-control-diff.d.ts +51 -0
  282. package/dist/src/core/negative-control-diff.js +179 -0
  283. package/dist/src/core/negative-control.d.ts +150 -0
  284. package/dist/src/core/negative-control.js +399 -0
  285. package/dist/src/core/objdir-install-manifests.d.ts +73 -0
  286. package/dist/src/core/objdir-install-manifests.js +226 -0
  287. package/dist/src/core/park-store.d.ts +63 -0
  288. package/dist/src/core/park-store.js +216 -0
  289. package/dist/src/core/patch-export-update.d.ts +6 -0
  290. package/dist/src/core/patch-export-update.js +13 -3
  291. package/dist/src/core/patch-export.d.ts +3 -2
  292. package/dist/src/core/patch-export.js +22 -16
  293. package/dist/src/core/patch-files.js +26 -8
  294. package/dist/src/core/patch-lint-checkjs-any-import.d.ts +39 -0
  295. package/dist/src/core/patch-lint-checkjs-any-import.js +63 -0
  296. package/dist/src/core/patch-lint-checkjs-diagnostics.d.ts +17 -0
  297. package/dist/src/core/patch-lint-checkjs-diagnostics.js +32 -0
  298. package/dist/src/core/patch-lint-checkjs.d.ts +10 -1
  299. package/dist/src/core/patch-lint-checkjs.js +20 -13
  300. package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
  301. package/dist/src/core/patch-lint-code-lines.js +105 -0
  302. package/dist/src/core/patch-lint-cross.d.ts +3 -1
  303. package/dist/src/core/patch-lint-cross.js +17 -4
  304. package/dist/src/core/patch-lint-css.d.ts +8 -2
  305. package/dist/src/core/patch-lint-css.js +24 -14
  306. package/dist/src/core/patch-lint-file-size.d.ts +12 -5
  307. package/dist/src/core/patch-lint-file-size.js +22 -11
  308. package/dist/src/core/patch-lint-forward-registration-extended.d.ts +23 -0
  309. package/dist/src/core/patch-lint-forward-registration-extended.js +36 -11
  310. package/dist/src/core/patch-lint-forward-registration.js +10 -0
  311. package/dist/src/core/patch-lint-ignore.d.ts +196 -0
  312. package/dist/src/core/patch-lint-ignore.js +429 -0
  313. package/dist/src/core/patch-lint-ownership.d.ts +10 -0
  314. package/dist/src/core/patch-lint-ownership.js +26 -0
  315. package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
  316. package/dist/src/core/patch-lint-size-thresholds.js +110 -0
  317. package/dist/src/core/patch-lint-size.d.ts +117 -0
  318. package/dist/src/core/patch-lint-size.js +331 -0
  319. package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
  320. package/dist/src/core/patch-lint-staged-registration.js +20 -0
  321. package/dist/src/core/patch-lint-token-scale.d.ts +57 -0
  322. package/dist/src/core/patch-lint-token-scale.js +286 -0
  323. package/dist/src/core/patch-lint.d.ts +11 -83
  324. package/dist/src/core/patch-lint.js +66 -288
  325. package/dist/src/core/patch-lock.js +7 -6
  326. package/dist/src/core/patch-manifest-consistency.js +7 -6
  327. package/dist/src/core/patch-manifest-io.d.ts +6 -3
  328. package/dist/src/core/patch-manifest-io.js +27 -9
  329. package/dist/src/core/patch-manifest-key-order.d.ts +24 -0
  330. package/dist/src/core/patch-manifest-key-order.js +61 -0
  331. package/dist/src/core/patch-manifest-stage.d.ts +46 -0
  332. package/dist/src/core/patch-manifest-stage.js +96 -0
  333. package/dist/src/core/patch-manifest-validate.js +61 -3
  334. package/dist/src/core/patch-order-legal.d.ts +26 -0
  335. package/dist/src/core/patch-order-legal.js +88 -0
  336. package/dist/src/core/patch-policy-content.d.ts +40 -0
  337. package/dist/src/core/patch-policy-content.js +216 -0
  338. package/dist/src/core/patch-policy.d.ts +22 -5
  339. package/dist/src/core/patch-policy.js +26 -12
  340. package/dist/src/core/patch-retired.d.ts +57 -0
  341. package/dist/src/core/patch-retired.js +92 -0
  342. package/dist/src/core/patch-subset.d.ts +16 -0
  343. package/dist/src/core/patch-subset.js +66 -0
  344. package/dist/src/core/preflight-wait.d.ts +20 -0
  345. package/dist/src/core/preflight-wait.js +38 -0
  346. package/dist/src/core/rebase-from-version.d.ts +46 -0
  347. package/dist/src/core/rebase-from-version.js +71 -0
  348. package/dist/src/core/run-log.d.ts +26 -0
  349. package/dist/src/core/run-log.js +106 -4
  350. package/dist/src/core/run-profile.d.ts +7 -0
  351. package/dist/src/core/run-profile.js +7 -1
  352. package/dist/src/core/smoke-patterns.d.ts +5 -0
  353. package/dist/src/core/smoke-patterns.js +13 -0
  354. package/dist/src/core/test-harness-crash.d.ts +19 -1
  355. package/dist/src/core/test-harness-crash.js +8 -9
  356. package/dist/src/core/test-harness-signature.d.ts +33 -0
  357. package/dist/src/core/test-harness-signature.js +23 -1
  358. package/dist/src/core/test-raw-log.d.ts +66 -0
  359. package/dist/src/core/test-raw-log.js +122 -0
  360. package/dist/src/core/test-run-completeness.d.ts +90 -0
  361. package/dist/src/core/test-run-completeness.js +184 -0
  362. package/dist/src/core/test-stale-check.d.ts +27 -1
  363. package/dist/src/core/test-stale-check.js +39 -49
  364. package/dist/src/core/test-stale-exempt.d.ts +40 -0
  365. package/dist/src/core/test-stale-exempt.js +70 -0
  366. package/dist/src/core/test-xpcshell-retry.d.ts +2 -0
  367. package/dist/src/core/test-xpcshell-retry.js +2 -1
  368. package/dist/src/core/token-lint.d.ts +53 -0
  369. package/dist/src/core/token-lint.js +131 -0
  370. package/dist/src/core/token-sibling-sheets.d.ts +46 -0
  371. package/dist/src/core/token-sibling-sheets.js +119 -0
  372. package/dist/src/core/tree-guard.js +5 -0
  373. package/dist/src/core/typecheck-shim.d.ts +6 -2
  374. package/dist/src/core/typecheck-shim.js +205 -4
  375. package/dist/src/core/typecheck.js +3 -1
  376. package/dist/src/index.d.ts +14 -3
  377. package/dist/src/index.js +6 -1
  378. package/dist/src/types/commands/index.d.ts +2 -2
  379. package/dist/src/types/commands/options.d.ts +212 -24
  380. package/dist/src/types/commands/patches.d.ts +118 -1
  381. package/dist/src/types/config.d.ts +264 -0
  382. package/dist/src/types/config.js +9 -0
  383. package/dist/src/types/furnace.d.ts +63 -1
  384. package/dist/src/types/furnace.js +7 -0
  385. package/dist/src/types/index.d.ts +1 -1
  386. package/dist/src/utils/options.d.ts +36 -7
  387. package/dist/src/utils/options.js +94 -8
  388. package/dist/src/utils/path-glob.d.ts +15 -0
  389. package/dist/src/utils/path-glob.js +47 -0
  390. package/dist/src/utils/regex.d.ts +15 -3
  391. package/dist/src/utils/regex.js +37 -16
  392. package/docs/configuration.md +369 -11
  393. package/docs/exit-codes.md +18 -1
  394. package/docs/furnace.md +139 -3
  395. package/docs/lifecycle-invariants.md +65 -30
  396. package/docs/machine-output.md +120 -12
  397. package/docs/patch-workflow.md +458 -22
  398. package/docs/run-logs.md +20 -1
  399. package/docs/testing.md +346 -21
  400. package/package.json +3 -3
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * `fireforge patch <verb>` parent command. Groups single-patch mutations and
4
4
  * planners (`compact`, `delete`, `lint-ignore`, `move-files`, `rename`,
5
- * `reorder`, `staged-dependency`, `tier`) so they do not clutter the
6
- * top-level command list. Queue-level verbs like `lint`, `export`, `verify`,
7
- * and `status` stay flat.
5
+ * `reorder`, `reserve`, `retire`, `split`, `stage`, `staged-dependency`,
6
+ * `tier`) so they do not clutter the top-level command list. Queue-level
7
+ * verbs like `lint`, `export`, `verify`, and `status` stay flat.
8
8
  */
9
9
  import { Command } from 'commander';
10
10
  import { registerPatchCompact } from './compact.js';
@@ -13,7 +13,10 @@ import { registerPatchLintIgnore } from './lint-ignore.js';
13
13
  import { registerPatchMoveFiles } from './move-files.js';
14
14
  import { registerPatchRename } from './rename.js';
15
15
  import { registerPatchReorder } from './reorder.js';
16
+ import { registerPatchReserve } from './reserve.js';
17
+ import { registerPatchRetire } from './retire.js';
16
18
  import { registerPatchSplit } from './split.js';
19
+ import { registerPatchStage } from './stage.js';
17
20
  import { registerPatchStagedDependency } from './staged-dependency.js';
18
21
  import { registerPatchTier } from './tier.js';
19
22
  /**
@@ -25,7 +28,7 @@ import { registerPatchTier } from './tier.js';
25
28
  export function registerPatch(program, context) {
26
29
  const patch = program
27
30
  .command('patch')
28
- .description('Manage individual patches in the queue (compact, delete, lint-ignore, move-files, rename, reorder, staged-dependency, tier)')
31
+ .description('Manage individual patches in the queue (compact, delete, lint-ignore, move-files, rename, reorder, reserve, retire, split, stage, staged-dependency, tier)')
29
32
  // Match `fireforge furnace`'s no-args contract: print the group's help
30
33
  // and exit 0. Without this default action, commander routes
31
34
  // `fireforge patch` (no subcommand) through its own help-then-exit-1
@@ -40,7 +43,10 @@ export function registerPatch(program, context) {
40
43
  registerPatchMoveFiles(patch, context);
41
44
  registerPatchRename(patch, context);
42
45
  registerPatchReorder(patch, context);
46
+ registerPatchReserve(patch, context);
47
+ registerPatchRetire(patch, context);
43
48
  registerPatchSplit(patch, context);
49
+ registerPatchStage(patch, context);
44
50
  registerPatchStagedDependency(patch, context);
45
51
  registerPatchTier(patch, context);
46
52
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Batch and per-entry input for `patch lint-ignore --add`. Split out of
3
+ * `lint-ignore.ts` for the per-file line budget.
4
+ *
5
+ * A rebaseline that sets a ceiling per rule used to take one call per rule,
6
+ * each taking the patch lock and rewriting `patches.json` (138 calls on one
7
+ * consumer's queue), because `--max` applied to every `--add` of a call.
8
+ * Two forms lift that:
9
+ *
10
+ * - `--add <rule>=<max>` gives each entry its own ceiling in one call.
11
+ * - `--from <json-file>` reads every entry from a file, under one lock hold
12
+ * and one write. The file is a JSON array of entries (a check ID string,
13
+ * or `{ rule, max, reason?, share?, meta?, patch? }`), or a
14
+ * `lint --per-patch --report` document, whose `lintIgnoreCeilings` rows
15
+ * for the target patch are rebaselined to their current measurement.
16
+ */
17
+ import type { LintIgnoreEntry, PatchLintIgnoreOptions, PatchSizeShare } from '../../types/commands/index.js';
18
+ /**
19
+ * Turns `--add` values into entries: `<rule>=<max>` is a ceiling at `max`,
20
+ * a bare ID is a ceiling at `--max` when that is given and a bare waiver
21
+ * otherwise. Every ceiling takes `--reason` (required) and `--share`.
22
+ *
23
+ * @param ids - The `--add` values
24
+ * @param options - Command options (`max`, `reason`, `share`)
25
+ * @returns The entries to upsert
26
+ */
27
+ export declare function resolveAddEntries(ids: readonly string[], options: Pick<PatchLintIgnoreOptions, 'max' | 'reason' | 'share'>): LintIgnoreEntry[];
28
+ /** Entries read from a `--from` file for one target patch. */
29
+ export interface LintIgnoreBatch {
30
+ entries: LintIgnoreEntry[];
31
+ /** Entries naming a different patch, left out. */
32
+ skippedForOtherPatches: number;
33
+ }
34
+ /**
35
+ * Reads a `--from` file into entries for `targetFilename`.
36
+ *
37
+ * @param path - JSON file path
38
+ * @param targetFilename - The patch the command edits
39
+ * @param defaultReason - `--reason`, used for object entries without one
40
+ * @returns The validated entries and the count left out for other patches
41
+ */
42
+ export declare function readLintIgnoreBatch(path: string, targetFilename: string, defaultReason: string | undefined): Promise<LintIgnoreBatch>;
43
+ /** Parses `--share`. */
44
+ export declare function parseShare(value: string): PatchSizeShare;
@@ -0,0 +1,142 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Batch and per-entry input for `patch lint-ignore --add`. Split out of
4
+ * `lint-ignore.ts` for the per-file line budget.
5
+ *
6
+ * A rebaseline that sets a ceiling per rule used to take one call per rule,
7
+ * each taking the patch lock and rewriting `patches.json` (138 calls on one
8
+ * consumer's queue), because `--max` applied to every `--add` of a call.
9
+ * Two forms lift that:
10
+ *
11
+ * - `--add <rule>=<max>` gives each entry its own ceiling in one call.
12
+ * - `--from <json-file>` reads every entry from a file, under one lock hold
13
+ * and one write. The file is a JSON array of entries (a check ID string,
14
+ * or `{ rule, max, reason?, share?, meta?, patch? }`), or a
15
+ * `lint --per-patch --report` document, whose `lintIgnoreCeilings` rows
16
+ * for the target patch are rebaselined to their current measurement.
17
+ */
18
+ import { LINT_IGNORE_CEILING_RULES, parseLintIgnoreEntries } from '../../core/patch-lint-ignore.js';
19
+ import { InvalidArgumentError } from '../../errors/base.js';
20
+ import { toError } from '../../utils/errors.js';
21
+ import { readJson } from '../../utils/fs.js';
22
+ import { isArray, isObject } from '../../utils/validation.js';
23
+ const RULE_MAX_PATTERN = /^(?<rule>[^=\s]+)=(?<max>[1-9]\d*)$/;
24
+ /**
25
+ * Turns `--add` values into entries: `<rule>=<max>` is a ceiling at `max`,
26
+ * a bare ID is a ceiling at `--max` when that is given and a bare waiver
27
+ * otherwise. Every ceiling takes `--reason` (required) and `--share`.
28
+ *
29
+ * @param ids - The `--add` values
30
+ * @param options - Command options (`max`, `reason`, `share`)
31
+ * @returns The entries to upsert
32
+ */
33
+ export function resolveAddEntries(ids, options) {
34
+ const parsed = ids.map((id) => {
35
+ if (id.includes('=')) {
36
+ const match = RULE_MAX_PATTERN.exec(id);
37
+ if (match?.groups === undefined) {
38
+ throw new InvalidArgumentError(`--add ${id}: expected <rule>=<max> with a positive integer max.`, '--add');
39
+ }
40
+ return { rule: match.groups['rule'], max: Number(match.groups['max']) };
41
+ }
42
+ return { rule: id, max: options.max };
43
+ });
44
+ const ceilings = parsed.filter((entry) => entry.max !== undefined);
45
+ if (ceilings.length === 0) {
46
+ if (options.reason !== undefined || options.share !== undefined) {
47
+ throw new InvalidArgumentError('--reason and --share apply to ceilings: pass --max <n> or --add <rule>=<max>.', options.reason !== undefined ? '--reason' : '--share');
48
+ }
49
+ return parsed.map((entry) => entry.rule);
50
+ }
51
+ if (options.reason === undefined || options.reason.trim() === '') {
52
+ throw new InvalidArgumentError('A lintIgnore ceiling needs --reason <text> (with --max <n> or --add <rule>=<max>).', '--reason');
53
+ }
54
+ const unmeasured = ceilings.filter((entry) => !LINT_IGNORE_CEILING_RULES.includes(entry.rule));
55
+ if (unmeasured.length > 0) {
56
+ throw new InvalidArgumentError(`A ceiling only applies to the measured size rules (${LINT_IGNORE_CEILING_RULES.join(', ')}); ` +
57
+ `got ${unmeasured.map((entry) => entry.rule).join(', ')}.`, '--max');
58
+ }
59
+ const { reason, share } = options;
60
+ return validated(parsed.map((entry) => entry.max === undefined
61
+ ? entry.rule
62
+ : { rule: entry.rule, max: entry.max, reason, ...(share !== undefined ? { share } : {}) }), '--add');
63
+ }
64
+ /** Runs the manifest validator over CLI-built entries, as an argument error. */
65
+ function validated(entries, flag) {
66
+ try {
67
+ return parseLintIgnoreEntries(entries, flag);
68
+ }
69
+ catch (error) {
70
+ throw new InvalidArgumentError(toError(error).message, flag);
71
+ }
72
+ }
73
+ /** The target patch's ceiling rows of a lint report, rebaselined to their measurement. */
74
+ function rebaselineFromReport(data, targetFilename) {
75
+ const patches = isArray(data['patches']) ? data['patches'] : [];
76
+ const row = patches.find((patch) => isObject(patch) && patch['filename'] === targetFilename);
77
+ const ceilings = isObject(row) && isArray(row['lintIgnoreCeilings']) ? row['lintIgnoreCeilings'] : [];
78
+ const raw = [];
79
+ for (const ceiling of ceilings) {
80
+ if (!isObject(ceiling) || typeof ceiling['measurement'] !== 'number')
81
+ continue;
82
+ if (ceiling['measurement'] <= 0)
83
+ continue;
84
+ const { measurement, rule, reason, share, meta } = ceiling;
85
+ raw.push({
86
+ rule,
87
+ max: measurement,
88
+ reason,
89
+ ...(share !== undefined ? { share } : {}),
90
+ ...(meta !== undefined ? { meta } : {}),
91
+ });
92
+ }
93
+ return { raw, skipped: patches.length - (row === undefined ? 0 : 1) };
94
+ }
95
+ /**
96
+ * Reads a `--from` file into entries for `targetFilename`.
97
+ *
98
+ * @param path - JSON file path
99
+ * @param targetFilename - The patch the command edits
100
+ * @param defaultReason - `--reason`, used for object entries without one
101
+ * @returns The validated entries and the count left out for other patches
102
+ */
103
+ export async function readLintIgnoreBatch(path, targetFilename, defaultReason) {
104
+ let data;
105
+ try {
106
+ data = await readJson(path);
107
+ }
108
+ catch (error) {
109
+ throw new InvalidArgumentError(`Cannot read --from ${path}: ${toError(error).message}`, '--from');
110
+ }
111
+ if (isObject(data)) {
112
+ const { raw, skipped } = rebaselineFromReport(data, targetFilename);
113
+ return { entries: validated(raw, '--from'), skippedForOtherPatches: skipped };
114
+ }
115
+ if (!isArray(data)) {
116
+ throw new InvalidArgumentError('--from must hold a JSON array of lintIgnore entries or a "lint --per-patch --report" document.', '--from');
117
+ }
118
+ const raw = [];
119
+ let skipped = 0;
120
+ for (const entry of data) {
121
+ if (!isObject(entry)) {
122
+ raw.push(entry);
123
+ continue;
124
+ }
125
+ const { patch, ...rest } = entry;
126
+ if (patch !== undefined && patch !== targetFilename) {
127
+ skipped++;
128
+ continue;
129
+ }
130
+ raw.push(rest['reason'] === undefined && defaultReason !== undefined
131
+ ? { ...rest, reason: defaultReason }
132
+ : rest);
133
+ }
134
+ return { entries: validated(raw, '--from'), skippedForOtherPatches: skipped };
135
+ }
136
+ /** Parses `--share`. */
137
+ export function parseShare(value) {
138
+ if (value !== 'test' && value !== 'product') {
139
+ throw new InvalidArgumentError(`--share must be "test" or "product" (got "${value}").`, '--share');
140
+ }
141
+ return value;
142
+ }
@@ -11,7 +11,11 @@
11
11
  * re-export's engine read + diff regeneration roundtrip is wasted.
12
12
  *
13
13
  * Modes are mutually exclusive: exactly one of `--add`, `--remove`, or
14
- * `--clear` must be supplied per invocation. The read-modify-write
14
+ * `--clear` must be supplied per invocation. `--add <rule> --max <n>
15
+ * --reason <text>` writes a size waiver with a ceiling (see
16
+ * `core/patch-lint-ignore.ts`) instead of a bare check ID; an `--add` for a
17
+ * rule that already has an entry replaces it, so the same command moves a
18
+ * ceiling or widens it back to a bare waiver. The read-modify-write
15
19
  * happens inside the patch directory lock via {@link mutatePatchMetadata}
16
20
  * so a concurrent writer cannot interleave between the read and the
17
21
  * write, which matters when an operator scripts repeated invocations or
@@ -12,20 +12,27 @@
12
12
  * re-export's engine read + diff regeneration roundtrip is wasted.
13
13
  *
14
14
  * Modes are mutually exclusive: exactly one of `--add`, `--remove`, or
15
- * `--clear` must be supplied per invocation. The read-modify-write
15
+ * `--clear` must be supplied per invocation. `--add <rule> --max <n>
16
+ * --reason <text>` writes a size waiver with a ceiling (see
17
+ * `core/patch-lint-ignore.ts`) instead of a bare check ID; an `--add` for a
18
+ * rule that already has an entry replaces it, so the same command moves a
19
+ * ceiling or widens it back to a bare waiver. The read-modify-write
16
20
  * happens inside the patch directory lock via {@link mutatePatchMetadata}
17
21
  * so a concurrent writer cannot interleave between the read and the
18
22
  * write, which matters when an operator scripts repeated invocations or
19
23
  * runs `--add` and `--remove` back-to-back.
20
24
  */
21
- import { Command } from 'commander';
25
+ import { Command, Option } from 'commander';
22
26
  import { confirmDestructive } from '../../core/destructive.js';
23
27
  import { appendHistoryBestEffort } from '../../core/history-log.js';
24
28
  import { mutatePatchMetadata } from '../../core/patch-export.js';
29
+ import { addedLintIgnoreEntries, formatLintIgnoreEntry, formatLintIgnoreList, lintIgnoreRule, removeLintIgnoreRules, upsertLintIgnoreEntries, } from '../../core/patch-lint-ignore.js';
25
30
  import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
26
31
  import { info, intro, outro, warn } from '../../utils/logger.js';
27
- import { addWaitLockOption, resolveWaitLockSeconds, stringListOption, } from '../../utils/options.js';
32
+ import { addWaitLockOption, commanderArgParser, resolveWaitLockSeconds, stringListOption, } from '../../utils/options.js';
33
+ import { parsePositiveIntegerFlag } from '../../utils/validation.js';
28
34
  import { proceedAfterDecision } from '../destructive-decision.js';
35
+ import { parseShare, readLintIgnoreBatch, resolveAddEntries } from './lint-ignore-from.js';
29
36
  import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
30
37
  /**
31
38
  * Printed whenever `--add` lands a new check id. The waiver this command
@@ -44,19 +51,10 @@ const LINT_IGNORE_REVIEW_WARNING = "This lint waiver is subject to the project's
44
51
  * present" contract).
45
52
  */
46
53
  function applyMode(existing, mode, values) {
47
- const existingSet = new Set(existing);
48
- if (mode === 'add') {
49
- for (const v of values)
50
- existingSet.add(v);
51
- const merged = [...existingSet];
52
- return merged.length > 0 ? merged : undefined;
53
- }
54
- if (mode === 'remove') {
55
- for (const v of values)
56
- existingSet.delete(v);
57
- const remaining = [...existingSet];
58
- return remaining.length > 0 ? remaining : undefined;
59
- }
54
+ if (mode === 'add')
55
+ return upsertLintIgnoreEntries(existing, values);
56
+ if (mode === 'remove')
57
+ return removeLintIgnoreRules(existing, values.map(lintIgnoreRule));
60
58
  // mode === 'clear'
61
59
  return undefined;
62
60
  }
@@ -66,29 +64,53 @@ function applyMode(existing, mode, values) {
66
64
  * message format directly without mocking the logger transport.
67
65
  */
68
66
  function describeChange(before, after, mode, values) {
69
- const beforeSet = new Set(before);
70
- const afterSet = new Set(after);
71
67
  if (mode === 'clear') {
72
68
  return before.length === 0
73
69
  ? 'lintIgnore was already empty — no change'
74
- : `lintIgnore cleared (was ${before.join(', ')})`;
70
+ : `lintIgnore cleared (was ${formatLintIgnoreList(before)})`;
75
71
  }
76
- const currentLabel = before.length > 0 ? `[${before.join(', ')}]` : '(empty)';
72
+ const currentLabel = before.length > 0 ? `[${formatLintIgnoreList(before)}]` : '(empty)';
77
73
  if (mode === 'add') {
78
- const added = values.filter((v) => !beforeSet.has(v));
74
+ const added = addedLintIgnoreEntries(before, after);
79
75
  if (added.length === 0) {
80
76
  // Surface the existing list so a no-op `--add` does not require a
81
77
  // follow-up `patches.json` read to confirm what was already present.
82
78
  return `lintIgnore unchanged (current: ${currentLabel}; all requested IDs already present)`;
83
79
  }
84
- return `lintIgnore += ${added.join(', ')} → ${[...afterSet].join(', ') || '(empty)'}`;
80
+ return `lintIgnore += ${formatLintIgnoreList(added)} → ${formatLintIgnoreList(after)}`;
85
81
  }
86
82
  // mode === 'remove'
87
- const removed = values.filter((v) => beforeSet.has(v));
83
+ const requested = new Set(values.map(lintIgnoreRule));
84
+ const removed = before.filter((entry) => requested.has(lintIgnoreRule(entry)));
88
85
  if (removed.length === 0) {
89
86
  return `lintIgnore unchanged (current: ${currentLabel}; none of the requested IDs were present)`;
90
87
  }
91
- return `lintIgnore −= ${removed.join(', ')} → ${[...afterSet].join(', ') || '(empty)'}`;
88
+ return `lintIgnore −= ${formatLintIgnoreList(removed)} → ${formatLintIgnoreList(after)}`;
89
+ }
90
+ /**
91
+ * Turns the mode's IDs into manifest entries, applying `--max`/`--reason`
92
+ * and `<rule>=<max>` to `--add` (see `lint-ignore-from.ts`). Refuses a
93
+ * ceiling flag without `--add`, a ceiling without a `--reason`, and a
94
+ * ceiling on a rule that carries no measurement, so a waiver that could
95
+ * never suppress anything is not written. A `--from` add resolves later,
96
+ * once the target patch is known.
97
+ */
98
+ function resolveRequestedEntries(mode, options) {
99
+ const ceilingFlag = options.max !== undefined ? '--max' : options.share !== undefined ? '--share' : undefined;
100
+ if ((ceilingFlag !== undefined || options.reason !== undefined) && mode !== 'add') {
101
+ throw new InvalidArgumentError('--max, --reason and --share only apply to --add.', ceilingFlag ?? '--reason');
102
+ }
103
+ if (mode === 'remove')
104
+ return [...(options.remove ?? [])];
105
+ if (mode !== 'add')
106
+ return [];
107
+ if (options.from !== undefined) {
108
+ if ((options.add?.length ?? 0) > 0 || ceilingFlag !== undefined) {
109
+ throw new InvalidArgumentError('--from takes its entries from the file; it combines only with --reason (the default reason).', (options.add?.length ?? 0) > 0 ? '--add' : ceilingFlag);
110
+ }
111
+ return [];
112
+ }
113
+ return resolveAddEntries(options.add ?? [], options);
92
114
  }
93
115
  /**
94
116
  * Runs the `patch lint-ignore` command: reads the patch's existing
@@ -105,7 +127,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
105
127
  // `--add foo --remove bar` are rejected: an operator who needs both
106
128
  // runs the command twice (clearer audit trail), and `--clear` plus a
107
129
  // mode is contradictory.
108
- const adding = (options.add?.length ?? 0) > 0;
130
+ const adding = (options.add?.length ?? 0) > 0 || options.from !== undefined;
109
131
  const removing = (options.remove?.length ?? 0) > 0;
110
132
  const clearing = options.clear === true;
111
133
  const modeCount = [adding, removing, clearing].filter(Boolean).length;
@@ -113,12 +135,22 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
113
135
  throw new InvalidArgumentError('--add, --remove, and --clear are mutually exclusive. Pick one mode per invocation.', 'patch lint-ignore');
114
136
  }
115
137
  if (modeCount === 0) {
116
- throw new InvalidArgumentError('Specify --add <id>, --remove <id>, or --clear.', 'patch lint-ignore');
138
+ throw new InvalidArgumentError('Specify --add <id>, --from <json-file>, --remove <id>, or --clear.', 'patch lint-ignore');
117
139
  }
118
140
  const mode = adding ? 'add' : removing ? 'remove' : 'clear';
119
- const values = mode === 'add' ? (options.add ?? []) : mode === 'remove' ? (options.remove ?? []) : [];
141
+ let values = resolveRequestedEntries(mode, options);
120
142
  const { paths, manifest } = await requirePatchQueue(projectRoot);
121
143
  const target = requirePatchTarget(identifier, manifest.patches);
144
+ if (options.from !== undefined) {
145
+ const batch = await readLintIgnoreBatch(options.from, target.filename, options.reason);
146
+ if (batch.skippedForOtherPatches > 0) {
147
+ info(`--from: ${batch.skippedForOtherPatches} entry/entries for other patches left out.`);
148
+ }
149
+ if (batch.entries.length === 0) {
150
+ throw new InvalidArgumentError(`--from ${options.from} holds no lintIgnore entry for ${target.filename}.`, '--from');
151
+ }
152
+ values = batch.entries;
153
+ }
122
154
  // Destructive-operation contract (docs/lifecycle-invariants.md): manifest
123
155
  // metadata mutations get summary + dry-run + confirmation/--yes + history
124
156
  // uniformly. Accepting --yes without ever prompting makes the flag appear
@@ -126,7 +158,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
126
158
  // of mutation an operator should approve consciously.
127
159
  const currentList = target.lintIgnore ?? [];
128
160
  const projectedList = applyMode(currentList, mode, values) ?? [];
129
- const addsNewIds = mode === 'add' && projectedList.length > currentList.length;
161
+ const addsNewIds = mode === 'add' && addedLintIgnoreEntries(currentList, projectedList).length > 0;
130
162
  const projectedSummary = describeChange(currentList, projectedList, mode, values);
131
163
  const decision = await confirmDestructive({
132
164
  operation: 'patch-lint-ignore',
@@ -158,7 +190,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
158
190
  const existing = result.before.lintIgnore ?? [];
159
191
  const projected = result.after.lintIgnore ?? [];
160
192
  info(`${target.filename}: ${describeChange(existing, projected, mode, values)}.`);
161
- if (mode === 'add' && projected.length > existing.length) {
193
+ if (mode === 'add' && addedLintIgnoreEntries(existing, projected).length > 0) {
162
194
  warn(LINT_IGNORE_REVIEW_WARNING);
163
195
  }
164
196
  await appendHistoryBestEffort(paths.patches, {
@@ -166,7 +198,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
166
198
  args: {
167
199
  filename: target.filename,
168
200
  mode,
169
- values: [...values],
201
+ values: values.map(formatLintIgnoreEntry),
170
202
  before: existing,
171
203
  after: projected,
172
204
  },
@@ -186,9 +218,16 @@ export function registerPatchLintIgnore(parent, context) {
186
218
  const command = parent
187
219
  .command('lint-ignore <name>')
188
220
  .description('Edit PatchMetadata.lintIgnore on a single patch (no .patch body rewrite). One mode per invocation.')
189
- .option('--add <check-id>', 'Lint check ID to add to the patch lintIgnore list (repeatable)', ...stringListOption())
221
+ .option('--add <check-id>', 'Lint check ID to add to the patch lintIgnore list (repeatable); <rule>=<max> adds a ceiling at <max>', ...stringListOption())
190
222
  .option('--remove <check-id>', 'Lint check ID to remove from the patch lintIgnore list (repeatable)', ...stringListOption())
191
223
  .option('--clear', 'Drop the lintIgnore field entirely')
224
+ .addOption(new Option('--max <n>', 'With --add: write a size waiver with a ceiling, suppressing the rule only while its ' +
225
+ 'measurement is at most <n> (file-too-large, large-patch-lines, large-patch-files)').argParser(commanderArgParser((v) => parsePositiveIntegerFlag('--max', v))))
226
+ .option('--reason <text>', 'Why the ceiling exists (required with --max or <rule>=<max>; with --from: the default for entries without one)')
227
+ .addOption(new Option('--share <share>', 'With a large-patch-lines ceiling: bound only the "product" or "test" share of the measurement').argParser(commanderArgParser(parseShare)))
228
+ .option('--from <json-file>', 'Add every entry in a JSON file under one lock hold: an array of check IDs or ' +
229
+ '{ rule, max, reason?, share?, meta?, patch? } objects, or a "lint --per-patch --report" ' +
230
+ 'document, whose ceilings for this patch are rebaselined to their current measurement')
192
231
  .option('--dry-run', 'Show what would change without writing')
193
232
  .option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)');
194
233
  addWaitLockOption(command).action(withErrorHandling(async (name, options) => {
@@ -203,6 +242,14 @@ export function registerPatchLintIgnore(parent, context) {
203
242
  normalized.remove = options.remove;
204
243
  if (options.clear === true)
205
244
  normalized.clear = true;
245
+ if (options.max !== undefined)
246
+ normalized.max = options.max;
247
+ if (options.reason !== undefined)
248
+ normalized.reason = options.reason;
249
+ if (options.share !== undefined)
250
+ normalized.share = options.share;
251
+ if (options.from !== undefined)
252
+ normalized.from = options.from;
206
253
  if (options.dryRun === true)
207
254
  normalized.dryRun = true;
208
255
  if (options.yes === true)
@@ -14,17 +14,20 @@ import { getProjectPaths, loadConfig } from '../../core/config.js';
14
14
  import { assertConfirmationAvailable, confirmDestructive } from '../../core/destructive.js';
15
15
  import { formatPatchNotFoundError } from '../../core/patch-identifier-suggest.js';
16
16
  import { buildPatchQueueContext } from '../../core/patch-lint.js';
17
+ import { buildLintIgnoreChecks } from '../../core/patch-lint-ignore.js';
17
18
  import { loadPatchesManifest, resolvePatchIdentifier } from '../../core/patch-manifest.js';
18
19
  import { formatPatchOrder } from '../../core/patch-parse.js';
19
20
  import { enforcePatchPolicy } from '../../core/patch-policy.js';
20
21
  import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
21
22
  import { info, intro, outro, success } from '../../utils/logger.js';
22
23
  import { normalizePatchDisplayName } from '../../utils/validation.js';
24
+ import { withDeclarationsReport } from '../declarations-report.js';
23
25
  import { proceedAfterDecision } from '../destructive-decision.js';
24
26
  import { resolvePlacementPlan } from '../export-flow.js';
25
27
  import { runPatchLint } from '../export-shared.js';
26
- import { commitPatchSplit } from './split.js';
27
- import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, findOwnerRewriteHolders, projectSplitManifest, runProjectedSplitLint, } from './split-plan.js';
28
+ import { commitPatchSplit, reportSplitStagedChanges } from './split.js';
29
+ import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, projectSplitManifest, runProjectedSplitLint, splitDeclarationRenamer, } from './split-plan.js';
30
+ import { emptyStagedMaintenance, findOwnerRewriteHolders } from './split-staged.js';
28
31
  /**
29
32
  * Runs `patch move-files --create --order <n>`: plans the create+move as a
30
33
  * split, lints the projection, confirms, and commits transactionally.
@@ -85,9 +88,26 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
85
88
  name: displayName,
86
89
  description: options.description ?? '',
87
90
  ownerRewrites: findOwnerRewriteHolders(manifest.patches, source.filename, movedSet),
88
- // Populated by runProjectedSplitLint below (forward edges into the new patch).
91
+ // Populated by runProjectedSplitLint below (forward edges into the new
92
+ // patch, and the declarations the move re-homes or makes unused).
89
93
  stagedDependencyAdditions: new Map(),
94
+ stagedMaintenance: emptyStagedMaintenance(),
90
95
  };
96
+ // Policy first: the projected manifest (orders, ranges, the gap rule)
97
+ // does not depend on the lint below, so a refused --order fails before
98
+ // the per-patch and projected lint passes instead of after them.
99
+ enforcePatchPolicy({
100
+ config,
101
+ manifest: projectSplitManifest(manifest, plan, buildNewPatchMetadata(plan, config)),
102
+ command: 'patch move-files --create',
103
+ forceUnsafe: options.forceUnsafe === true,
104
+ hints: {
105
+ 'description-required': 'Pass --description "<text>" (or -d) on this command to set the created patch\'s description.',
106
+ 'numeric-gap': 'With patchPolicy.allowGaps: false, orders are contiguous within a range: --order must ' +
107
+ "fill an existing gap or a retired slot, or take the number right after the range's last " +
108
+ 'patch. A number reserved past a gap cannot be used until the numbers before it exist.',
109
+ },
110
+ });
91
111
  // Per-patch lint both projected bodies, threading the source patch's
92
112
  // tier/lintIgnore so an intentional-advisory patch can still move files.
93
113
  // The whole-queue context (built once, with the config so it carries the
@@ -96,7 +116,7 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
96
116
  // resolve exactly as they will after the move lands. Without it the
97
117
  // projection lint is blind.
98
118
  const patchQueueCtx = await buildPatchQueueContext(paths.patches, config);
99
- const ignoreChecks = source.lintIgnore ? new Set(source.lintIgnore) : undefined;
119
+ const ignoreChecks = buildLintIgnoreChecks(source.lintIgnore);
100
120
  await runPatchLint({
101
121
  engineDir: paths.engine,
102
122
  filesAffected: remainingFiles,
@@ -117,18 +137,16 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
117
137
  ignoreChecks,
118
138
  patchTier: source.tier,
119
139
  });
120
- const { conflicts, stagedDependencyAdditions } = runProjectedSplitLint(plan, patchQueueCtx);
121
- plan.stagedDependencyAdditions = stagedDependencyAdditions;
122
- const newMetadata = buildNewPatchMetadata(plan, config);
123
- enforcePatchPolicy({
124
- config,
125
- manifest: projectSplitManifest(manifest, plan, newMetadata),
126
- command: 'patch move-files --create',
127
- forceUnsafe: options.forceUnsafe === true,
128
- hints: {
129
- 'description-required': 'Pass --description "<text>" (or -d) on this command to set the created patch\'s description.',
130
- },
140
+ const projected = runProjectedSplitLint(plan, patchQueueCtx);
141
+ plan.stagedDependencyAdditions = projected.stagedDependencyAdditions;
142
+ plan.stagedMaintenance = projected.stagedMaintenance;
143
+ const conflicts = await withDeclarationsReport(projected.conflicts, {
144
+ projectRoot,
145
+ regressions: projected.regressions,
146
+ out: options.declarationsOut,
147
+ rename: splitDeclarationRenamer(plan),
131
148
  });
149
+ const newMetadata = buildNewPatchMetadata(plan, config);
132
150
  const decision = await confirmDestructive({
133
151
  operation: 'patch-move-files-create',
134
152
  title: `Create ${placement.newFilename} (order ${placement.insertionOrder}) and move ${movedFiles.length} file(s) out of ${source.filename}`,
@@ -143,8 +161,6 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
143
161
  await commitPatchSplit(paths.patches, plan, newMetadata, options, config);
144
162
  success(`Created ${placement.newFilename} (order ${formatPatchOrder(placement.insertionOrder)}) ` +
145
163
  `and moved ${plan.movedFiles.length} file(s) out of ${source.filename}`);
146
- if (plan.ownerRewrites.length > 0) {
147
- info(`Re-pointed staged-dependency owners in: ${plan.ownerRewrites.join(', ')}`);
148
- }
164
+ reportSplitStagedChanges(plan);
149
165
  outro('Move complete');
150
166
  }
@@ -14,10 +14,11 @@
14
14
  * reflect both patches' content, because both bodies are regenerated from
15
15
  * the worktree.
16
16
  */
17
- import { join } from 'node:path';
17
+ import { dirname, join, resolve } from 'node:path';
18
18
  import { assertConfirmationAvailable, confirmDestructive, } from '../../core/destructive.js';
19
19
  import { appendHistoryBestEffort } from '../../core/history-log.js';
20
20
  import { buildPatchQueueContext, collectForwardImportEdges, formatPatchLintIssue, lintPatchQueue, } from '../../core/patch-lint.js';
21
+ import { buildLintIgnoreChecks } from '../../core/patch-lint-ignore.js';
21
22
  import { computeProjectedLintRegressions } from '../../core/patch-lint-projection.js';
22
23
  import { withPatchDirectoryLock } from '../../core/patch-lock.js';
23
24
  import { loadPatchesManifest, savePatchesManifest, validatePatchesManifest, } from '../../core/patch-manifest.js';
@@ -26,10 +27,12 @@ import { toError } from '../../utils/errors.js';
26
27
  import { readText, writeText } from '../../utils/fs.js';
27
28
  import { info, outro, success, warn } from '../../utils/logger.js';
28
29
  import { resolveWaitLockSeconds } from '../../utils/options.js';
30
+ import { withDeclarationsReport } from '../declarations-report.js';
29
31
  import { proceedAfterDecision } from '../destructive-decision.js';
30
32
  import { runPatchLint } from '../export-shared.js';
31
33
  import { projectEntryBody } from './entry-projection.js';
32
- import { buildSplitDiff, findOwnerRewriteHolders, mergeStagedForwardImports, rewriteSplitOwners, } from './split-plan.js';
34
+ import { buildSplitDiff, mergeStagedForwardImports } from './split-plan.js';
35
+ import { findOwnerRewriteHolders, rewriteSplitOwners } from './split-staged.js';
33
36
  /**
34
37
  * Projects the post-move queue through cross-patch lint, auto-declaring the
35
38
  * forward edges the move introduces into the target (an importer that used
@@ -89,7 +92,7 @@ function runProjectedMoveLint(plan, baseCtx) {
89
92
  reason: `move would introduce ${regressions.length} cross-patch lint error(s)`,
90
93
  details: regressions.map(formatPatchLintIssue),
91
94
  };
92
- return { conflicts, stagedDependencyAdditions };
95
+ return { conflicts, regressions, stagedDependencyAdditions };
93
96
  }
94
97
  /**
95
98
  * Commits a confirmed move under the patch-directory lock: write both
@@ -198,7 +201,7 @@ export async function runMoveFilesInto(args) {
198
201
  config: args.config,
199
202
  skipLint: options.skipLint,
200
203
  patchQueueCtx,
201
- ignoreChecks: source.lintIgnore ? new Set(source.lintIgnore) : undefined,
204
+ ignoreChecks: buildLintIgnoreChecks(source.lintIgnore),
202
205
  patchTier: source.tier,
203
206
  });
204
207
  await runPatchLint({
@@ -208,7 +211,7 @@ export async function runMoveFilesInto(args) {
208
211
  config: args.config,
209
212
  skipLint: options.skipLint,
210
213
  patchQueueCtx,
211
- ignoreChecks: target.lintIgnore ? new Set(target.lintIgnore) : undefined,
214
+ ignoreChecks: buildLintIgnoreChecks(target.lintIgnore),
212
215
  patchTier: target.tier,
213
216
  });
214
217
  const manifest = await loadPatchesManifest(patchesDir);
@@ -223,8 +226,13 @@ export async function runMoveFilesInto(args) {
223
226
  ownerRewrites: findOwnerRewriteHolders(manifest?.patches ?? [], source.filename, new Set(files)),
224
227
  stagedDependencyAdditions: new Map(),
225
228
  };
226
- const { conflicts, stagedDependencyAdditions } = runProjectedMoveLint(plan, patchQueueCtx);
227
- plan.stagedDependencyAdditions = stagedDependencyAdditions;
229
+ const projected = runProjectedMoveLint(plan, patchQueueCtx);
230
+ plan.stagedDependencyAdditions = projected.stagedDependencyAdditions;
231
+ const conflicts = await withDeclarationsReport(projected.conflicts, {
232
+ projectRoot: dirname(resolve(patchesDir)),
233
+ regressions: projected.regressions,
234
+ out: options.declarationsOut,
235
+ });
228
236
  const decision = await confirmDestructive({
229
237
  operation: 'patch-move-files',
230
238
  title: `Move ${files.length} file(s) from ${source.filename} to ${target.filename}`,
@@ -164,13 +164,16 @@ export function registerPatchMoveFiles(parent, context) {
164
164
  'or create the target patch and move the files in one transaction with --create --order <n>.')
165
165
  .option('--file <path>', 'File path relative to engine/ to move (repeatable)', ...stringListOption())
166
166
  .option('--create', 'Create <to> as a new patch and move the files into it (requires --order)')
167
- .option('--order <n>', 'Exact sparse order for the created patch (only valid with --create)', commanderArgParser((raw) => parsePositiveIntegerFlag('--order', raw)))
167
+ .option('--order <n>', 'Exact sparse order for the created patch (only valid with --create). With ' +
168
+ 'patchPolicy.allowGaps: false, orders are contiguous within a range: pick a gap, a ' +
169
+ "retired slot, or the number after the range's last patch; checked before any lint runs", commanderArgParser((raw) => parsePositiveIntegerFlag('--order', raw)))
168
170
  .option('--category <category>', "Category for the created patch (default: the source patch's)")
169
171
  .option('-d, --description <desc>', 'Description for the created patch')
170
172
  .option('--dry-run', 'Show what would happen without writing')
171
173
  .option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)')
172
174
  .option('--force-unsafe', 'Bypass projected-lint refusals')
173
- .option('--skip-lint', 'Skip per-patch lint of the projected bodies');
175
+ .option('--skip-lint', 'Skip per-patch lint of the projected bodies')
176
+ .option('--declarations-out <path>', 'Where a refusal writes its one --from-ready staged-declaration report (default: .fireforge/declarations/<timestamp>.json)');
174
177
  addWaitLockOption(command).action(withErrorHandling(async (from, to, options) => {
175
178
  await patchMoveFilesCommand(getProjectRoot(), from, to, {
176
179
  ...pickDefined({
@@ -183,6 +186,7 @@ export function registerPatchMoveFiles(parent, context) {
183
186
  yes: options.yes,
184
187
  forceUnsafe: options.forceUnsafe,
185
188
  skipLint: options.skipLint,
189
+ declarationsOut: options.declarationsOut,
186
190
  waitLock: options.waitLock,
187
191
  }),
188
192
  });