@hominis/fireforge 0.45.0 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (662) hide show
  1. package/CHANGELOG.md +409 -446
  2. package/dist/bin/fireforge.d.ts +1 -1
  3. package/dist/bin/fireforge.js +21 -15
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/cli-usage-error.d.ts +46 -0
  6. package/dist/src/cli-usage-error.js +81 -0
  7. package/dist/src/cli.d.ts +7 -2
  8. package/dist/src/cli.js +47 -47
  9. package/dist/src/commands/bootstrap-checks.js +2 -1
  10. package/dist/src/commands/bootstrap.js +3 -4
  11. package/dist/src/commands/build.js +27 -17
  12. package/dist/src/commands/config.js +9 -9
  13. package/dist/src/commands/destructive-decision.d.ts +19 -0
  14. package/dist/src/commands/destructive-decision.js +31 -0
  15. package/dist/src/commands/discard.js +5 -5
  16. package/dist/src/commands/doctor-check-core.d.ts +4 -10
  17. package/dist/src/commands/doctor-check-core.js +0 -8
  18. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  19. package/dist/src/commands/doctor-furnace-config-sync.d.ts +2 -2
  20. package/dist/src/commands/doctor-furnace-config-sync.js +20 -20
  21. package/dist/src/commands/doctor-furnace-jar.d.ts +1 -1
  22. package/dist/src/commands/doctor-furnace-jar.js +1 -1
  23. package/dist/src/commands/doctor-furnace.js +22 -22
  24. package/dist/src/commands/doctor-orphaned-harness.d.ts +10 -17
  25. package/dist/src/commands/doctor-orphaned-harness.js +14 -23
  26. package/dist/src/commands/doctor-patch-manifest.d.ts +2 -1
  27. package/dist/src/commands/doctor-patch-manifest.js +7 -6
  28. package/dist/src/commands/doctor-post-rebase-audit.js +3 -3
  29. package/dist/src/commands/doctor-source-pin.d.ts +12 -12
  30. package/dist/src/commands/doctor-source-pin.js +14 -14
  31. package/dist/src/commands/doctor-working-tree.d.ts +3 -3
  32. package/dist/src/commands/doctor-working-tree.js +3 -3
  33. package/dist/src/commands/doctor.d.ts +3 -1
  34. package/dist/src/commands/doctor.js +15 -16
  35. package/dist/src/commands/download.js +50 -37
  36. package/dist/src/commands/export-all.js +29 -12
  37. package/dist/src/commands/export-flow.d.ts +5 -5
  38. package/dist/src/commands/export-flow.js +29 -59
  39. package/dist/src/commands/export-placement-conflicts.d.ts +3 -3
  40. package/dist/src/commands/export-placement-conflicts.js +12 -12
  41. package/dist/src/commands/export-placement-gate.d.ts +2 -2
  42. package/dist/src/commands/export-placement-gate.js +7 -13
  43. package/dist/src/commands/export-placement-policy.d.ts +1 -1
  44. package/dist/src/commands/export-placement-policy.js +4 -6
  45. package/dist/src/commands/export-shared.d.ts +51 -32
  46. package/dist/src/commands/export-shared.js +17 -32
  47. package/dist/src/commands/export.js +14 -5
  48. package/dist/src/commands/furnace/apply.js +16 -16
  49. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  50. package/dist/src/commands/furnace/authoring-config.js +19 -0
  51. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +1 -1
  52. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  53. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +14 -14
  54. package/dist/src/commands/furnace/chrome-doc-templates.js +17 -17
  55. package/dist/src/commands/furnace/chrome-doc-tests.d.ts +2 -2
  56. package/dist/src/commands/furnace/chrome-doc-tests.js +4 -4
  57. package/dist/src/commands/furnace/chrome-doc.d.ts +8 -8
  58. package/dist/src/commands/furnace/chrome-doc.js +18 -18
  59. package/dist/src/commands/furnace/create-browser-test.d.ts +1 -1
  60. package/dist/src/commands/furnace/create-browser-test.js +7 -12
  61. package/dist/src/commands/furnace/create-dry-run.d.ts +4 -4
  62. package/dist/src/commands/furnace/create-dry-run.js +6 -9
  63. package/dist/src/commands/furnace/create-features.d.ts +2 -2
  64. package/dist/src/commands/furnace/create-features.js +2 -2
  65. package/dist/src/commands/furnace/create-mochikit.d.ts +4 -4
  66. package/dist/src/commands/furnace/create-mochikit.js +7 -7
  67. package/dist/src/commands/furnace/create-templates.d.ts +31 -15
  68. package/dist/src/commands/furnace/create-templates.js +20 -19
  69. package/dist/src/commands/furnace/create-xpcshell.d.ts +5 -5
  70. package/dist/src/commands/furnace/create-xpcshell.js +8 -8
  71. package/dist/src/commands/furnace/create.d.ts +1 -1
  72. package/dist/src/commands/furnace/create.js +35 -29
  73. package/dist/src/commands/furnace/deploy.js +15 -6
  74. package/dist/src/commands/furnace/diff.js +7 -7
  75. package/dist/src/commands/furnace/index.js +1 -1
  76. package/dist/src/commands/furnace/init.js +17 -16
  77. package/dist/src/commands/furnace/list.js +2 -2
  78. package/dist/src/commands/furnace/override.d.ts +1 -1
  79. package/dist/src/commands/furnace/override.js +43 -34
  80. package/dist/src/commands/furnace/preview.js +17 -17
  81. package/dist/src/commands/furnace/refresh.js +15 -7
  82. package/dist/src/commands/furnace/remove-mutations.d.ts +9 -10
  83. package/dist/src/commands/furnace/remove-mutations.js +19 -19
  84. package/dist/src/commands/furnace/remove-state.d.ts +1 -1
  85. package/dist/src/commands/furnace/remove-state.js +1 -1
  86. package/dist/src/commands/furnace/remove.js +4 -8
  87. package/dist/src/commands/furnace/rename-browser-test.js +10 -15
  88. package/dist/src/commands/furnace/rename-xpcshell.d.ts +2 -2
  89. package/dist/src/commands/furnace/rename-xpcshell.js +3 -3
  90. package/dist/src/commands/furnace/rename.js +36 -36
  91. package/dist/src/commands/furnace/scan.js +3 -3
  92. package/dist/src/commands/furnace/status.js +19 -16
  93. package/dist/src/commands/furnace/sync.js +6 -6
  94. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  95. package/dist/src/commands/furnace/test-file-name.js +39 -0
  96. package/dist/src/commands/furnace/validate.js +11 -12
  97. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  98. package/dist/src/commands/furnace/validation-output.js +3 -8
  99. package/dist/src/commands/import.js +22 -29
  100. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  101. package/dist/src/commands/lint-per-patch.js +27 -12
  102. package/dist/src/commands/lint-per-run-checkjs.d.ts +14 -15
  103. package/dist/src/commands/lint-per-run-checkjs.js +14 -15
  104. package/dist/src/commands/lint-report.d.ts +3 -3
  105. package/dist/src/commands/lint-report.js +4 -4
  106. package/dist/src/commands/lint.d.ts +5 -4
  107. package/dist/src/commands/lint.js +36 -36
  108. package/dist/src/commands/manifest.d.ts +4 -4
  109. package/dist/src/commands/manifest.js +1 -1
  110. package/dist/src/commands/package.js +4 -6
  111. package/dist/src/commands/patch/compact.d.ts +2 -2
  112. package/dist/src/commands/patch/compact.js +19 -20
  113. package/dist/src/commands/patch/delete.d.ts +1 -1
  114. package/dist/src/commands/patch/delete.js +23 -33
  115. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  116. package/dist/src/commands/patch/entry-projection.js +24 -0
  117. package/dist/src/commands/patch/lint-ignore.d.ts +3 -12
  118. package/dist/src/commands/patch/lint-ignore.js +33 -44
  119. package/dist/src/commands/patch/move-files-create.d.ts +2 -2
  120. package/dist/src/commands/patch/move-files-create.js +29 -15
  121. package/dist/src/commands/patch/move-files-into.d.ts +2 -2
  122. package/dist/src/commands/patch/move-files-into.js +41 -37
  123. package/dist/src/commands/patch/move-files.js +5 -14
  124. package/dist/src/commands/patch/patch-context.d.ts +1 -1
  125. package/dist/src/commands/patch/rename.d.ts +4 -4
  126. package/dist/src/commands/patch/rename.js +35 -49
  127. package/dist/src/commands/patch/reorder.js +35 -48
  128. package/dist/src/commands/patch/split-plan.d.ts +7 -8
  129. package/dist/src/commands/patch/split-plan.js +10 -13
  130. package/dist/src/commands/patch/split.d.ts +2 -2
  131. package/dist/src/commands/patch/split.js +50 -48
  132. package/dist/src/commands/patch/staged-dependency-validate.d.ts +1 -1
  133. package/dist/src/commands/patch/staged-dependency-validate.js +3 -3
  134. package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
  135. package/dist/src/commands/patch/staged-dependency.js +22 -28
  136. package/dist/src/commands/patch/tier.d.ts +2 -2
  137. package/dist/src/commands/patch/tier.js +20 -30
  138. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  139. package/dist/src/commands/patch-rename-map.js +36 -0
  140. package/dist/src/commands/pending-resolution.d.ts +15 -0
  141. package/dist/src/commands/pending-resolution.js +25 -0
  142. package/dist/src/commands/re-export-adjacent.d.ts +25 -3
  143. package/dist/src/commands/re-export-adjacent.js +40 -24
  144. package/dist/src/commands/re-export-bulk-scan.d.ts +3 -3
  145. package/dist/src/commands/re-export-bulk-scan.js +23 -27
  146. package/dist/src/commands/re-export-drift.d.ts +10 -10
  147. package/dist/src/commands/re-export-drift.js +33 -33
  148. package/dist/src/commands/re-export-files.d.ts +2 -3
  149. package/dist/src/commands/re-export-files.js +52 -44
  150. package/dist/src/commands/re-export-lint.d.ts +29 -30
  151. package/dist/src/commands/re-export-lint.js +36 -29
  152. package/dist/src/commands/re-export-options.d.ts +1 -1
  153. package/dist/src/commands/re-export-options.js +1 -1
  154. package/dist/src/commands/re-export-register.d.ts +1 -1
  155. package/dist/src/commands/re-export-register.js +1 -1
  156. package/dist/src/commands/re-export-scan.d.ts +1 -1
  157. package/dist/src/commands/re-export-scan.js +12 -9
  158. package/dist/src/commands/re-export-single.d.ts +23 -4
  159. package/dist/src/commands/re-export-single.js +39 -31
  160. package/dist/src/commands/re-export.js +35 -17
  161. package/dist/src/commands/rebase/abort.js +9 -14
  162. package/dist/src/commands/rebase/continue.js +20 -19
  163. package/dist/src/commands/rebase/index.d.ts +1 -1
  164. package/dist/src/commands/rebase/index.js +5 -5
  165. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  166. package/dist/src/commands/rebase/patch-loop.js +20 -24
  167. package/dist/src/commands/register.js +5 -5
  168. package/dist/src/commands/reset.js +1 -1
  169. package/dist/src/commands/resolve.d.ts +2 -2
  170. package/dist/src/commands/resolve.js +21 -13
  171. package/dist/src/commands/run.d.ts +0 -13
  172. package/dist/src/commands/run.js +38 -51
  173. package/dist/src/commands/setup-support.d.ts +16 -4
  174. package/dist/src/commands/setup-support.js +20 -20
  175. package/dist/src/commands/setup.js +4 -10
  176. package/dist/src/commands/source.js +9 -2
  177. package/dist/src/commands/status-check.d.ts +3 -3
  178. package/dist/src/commands/status-check.js +6 -6
  179. package/dist/src/commands/status-json.d.ts +14 -13
  180. package/dist/src/commands/status-json.js +9 -8
  181. package/dist/src/commands/status-output.d.ts +3 -3
  182. package/dist/src/commands/status-output.js +5 -5
  183. package/dist/src/commands/status-ownership.d.ts +3 -3
  184. package/dist/src/commands/status-ownership.js +3 -3
  185. package/dist/src/commands/status.js +25 -26
  186. package/dist/src/commands/test-build-phase.d.ts +3 -3
  187. package/dist/src/commands/test-build-phase.js +48 -24
  188. package/dist/src/commands/test-diagnose.d.ts +1 -1
  189. package/dist/src/commands/test-diagnose.js +20 -20
  190. package/dist/src/commands/test-modes.d.ts +4 -4
  191. package/dist/src/commands/test-modes.js +4 -5
  192. package/dist/src/commands/test-register.js +5 -3
  193. package/dist/src/commands/test-run.d.ts +11 -11
  194. package/dist/src/commands/test-run.js +40 -33
  195. package/dist/src/commands/test-stale-gate.d.ts +12 -12
  196. package/dist/src/commands/test-stale-gate.js +17 -17
  197. package/dist/src/commands/test-verdict.d.ts +19 -5
  198. package/dist/src/commands/test-verdict.js +26 -11
  199. package/dist/src/commands/test.d.ts +3 -3
  200. package/dist/src/commands/test.js +73 -36
  201. package/dist/src/commands/token-coverage.js +3 -3
  202. package/dist/src/commands/token-list.d.ts +26 -0
  203. package/dist/src/commands/token-list.js +153 -0
  204. package/dist/src/commands/token.js +26 -10
  205. package/dist/src/commands/tree.js +26 -21
  206. package/dist/src/commands/typecheck.d.ts +6 -6
  207. package/dist/src/commands/typecheck.js +16 -16
  208. package/dist/src/commands/verify.d.ts +3 -3
  209. package/dist/src/commands/verify.js +10 -10
  210. package/dist/src/commands/watch.js +11 -11
  211. package/dist/src/commands/wire.js +17 -16
  212. package/dist/src/core/ast-utils.d.ts +7 -7
  213. package/dist/src/core/ast-utils.js +8 -8
  214. package/dist/src/core/branding.d.ts +5 -12
  215. package/dist/src/core/branding.js +6 -5
  216. package/dist/src/core/browser-wire.d.ts +1 -1
  217. package/dist/src/core/browser-wire.js +10 -7
  218. package/dist/src/core/build-audit-platform.d.ts +32 -3
  219. package/dist/src/core/build-audit-platform.js +153 -13
  220. package/dist/src/core/build-audit-registration.d.ts +5 -5
  221. package/dist/src/core/build-audit-registration.js +10 -9
  222. package/dist/src/core/build-audit-resolve.d.ts +5 -5
  223. package/dist/src/core/build-audit-resolve.js +6 -6
  224. package/dist/src/core/build-audit-skip.d.ts +57 -0
  225. package/dist/src/core/build-audit-skip.js +112 -0
  226. package/dist/src/core/build-audit-transforms.d.ts +2 -2
  227. package/dist/src/core/build-audit-transforms.js +3 -3
  228. package/dist/src/core/build-audit.d.ts +21 -8
  229. package/dist/src/core/build-audit.js +142 -40
  230. package/dist/src/core/build-baseline-types.d.ts +19 -18
  231. package/dist/src/core/build-baseline-types.js +1 -1
  232. package/dist/src/core/build-baseline.d.ts +48 -31
  233. package/dist/src/core/build-baseline.js +20 -43
  234. package/dist/src/core/build-jar-escalation.d.ts +8 -8
  235. package/dist/src/core/build-jar-escalation.js +18 -17
  236. package/dist/src/core/build-overwrite-guard.d.ts +4 -4
  237. package/dist/src/core/build-overwrite-guard.js +14 -14
  238. package/dist/src/core/build-prepare.d.ts +4 -12
  239. package/dist/src/core/build-prepare.js +22 -26
  240. package/dist/src/core/config-mutate.js +4 -4
  241. package/dist/src/core/config-paths.d.ts +2 -6
  242. package/dist/src/core/config-paths.js +4 -2
  243. package/dist/src/core/config-state.d.ts +0 -6
  244. package/dist/src/core/config-state.js +3 -21
  245. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  246. package/dist/src/core/config-validate-build-audit.js +80 -0
  247. package/dist/src/core/config-validate-file-size.d.ts +1 -1
  248. package/dist/src/core/config-validate-file-size.js +3 -3
  249. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  250. package/dist/src/core/config-validate-patch-policy.js +3 -3
  251. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  252. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  253. package/dist/src/core/config-validate.js +22 -12
  254. package/dist/src/core/config.d.ts +12 -12
  255. package/dist/src/core/config.js +21 -25
  256. package/dist/src/core/coverage-extend.d.ts +16 -16
  257. package/dist/src/core/coverage-extend.js +20 -20
  258. package/dist/src/core/destructive.d.ts +14 -14
  259. package/dist/src/core/destructive.js +9 -9
  260. package/dist/src/core/diff-hunks.d.ts +2 -2
  261. package/dist/src/core/diff-hunks.js +7 -7
  262. package/dist/src/core/discard-baseline.d.ts +10 -10
  263. package/dist/src/core/discard-baseline.js +22 -23
  264. package/dist/src/core/display-state.d.ts +11 -11
  265. package/dist/src/core/display-state.js +12 -12
  266. package/dist/src/core/engine-changes.d.ts +2 -2
  267. package/dist/src/core/engine-changes.js +2 -2
  268. package/dist/src/core/engine-precondition.d.ts +5 -13
  269. package/dist/src/core/engine-precondition.js +5 -10
  270. package/dist/src/core/engine-session-lock.d.ts +18 -18
  271. package/dist/src/core/engine-session-lock.js +50 -54
  272. package/dist/src/core/export-moved-code-shape.d.ts +7 -7
  273. package/dist/src/core/export-moved-code-shape.js +10 -10
  274. package/dist/src/core/file-lock-owner.d.ts +103 -0
  275. package/dist/src/core/file-lock-owner.js +259 -0
  276. package/dist/src/core/file-lock.d.ts +14 -19
  277. package/dist/src/core/file-lock.js +90 -128
  278. package/dist/src/core/firefox-cache.d.ts +13 -1
  279. package/dist/src/core/firefox-cache.js +61 -36
  280. package/dist/src/core/firefox-download.d.ts +15 -0
  281. package/dist/src/core/firefox-download.js +38 -22
  282. package/dist/src/core/firefox-extract.d.ts +8 -7
  283. package/dist/src/core/firefox-extract.js +45 -27
  284. package/dist/src/core/firefox.d.ts +34 -36
  285. package/dist/src/core/firefox.js +13 -44
  286. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  287. package/dist/src/core/furnace-apply-dry-run.js +2 -8
  288. package/dist/src/core/furnace-apply-ftl.d.ts +5 -5
  289. package/dist/src/core/furnace-apply-ftl.js +8 -8
  290. package/dist/src/core/furnace-apply-helpers.d.ts +9 -20
  291. package/dist/src/core/furnace-apply-helpers.js +28 -39
  292. package/dist/src/core/furnace-apply-output.js +1 -1
  293. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +9 -9
  294. package/dist/src/core/furnace-apply-overwrite-warn.js +13 -11
  295. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +1 -1
  296. package/dist/src/core/furnace-apply-undeploy-actions.js +1 -1
  297. package/dist/src/core/furnace-apply.d.ts +4 -4
  298. package/dist/src/core/furnace-apply.js +50 -29
  299. package/dist/src/core/furnace-checksum-utils.d.ts +5 -5
  300. package/dist/src/core/furnace-checksum-utils.js +5 -5
  301. package/dist/src/core/furnace-config-custom.d.ts +1 -1
  302. package/dist/src/core/furnace-config-custom.js +2 -2
  303. package/dist/src/core/furnace-config-order.js +1 -1
  304. package/dist/src/core/furnace-config-tokens.d.ts +3 -3
  305. package/dist/src/core/furnace-config-tokens.js +3 -3
  306. package/dist/src/core/furnace-config-validate.js +2 -2
  307. package/dist/src/core/furnace-config.d.ts +5 -5
  308. package/dist/src/core/furnace-config.js +40 -53
  309. package/dist/src/core/furnace-constants.d.ts +6 -6
  310. package/dist/src/core/furnace-constants.js +9 -8
  311. package/dist/src/core/furnace-css-fragments.d.ts +6 -6
  312. package/dist/src/core/furnace-css-fragments.js +11 -11
  313. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  314. package/dist/src/core/furnace-dir-entry.js +21 -0
  315. package/dist/src/core/furnace-jsconfig.d.ts +7 -7
  316. package/dist/src/core/furnace-jsconfig.js +22 -20
  317. package/dist/src/core/furnace-operation.d.ts +22 -19
  318. package/dist/src/core/furnace-operation.js +38 -34
  319. package/dist/src/core/furnace-precondition.d.ts +10 -10
  320. package/dist/src/core/furnace-precondition.js +8 -8
  321. package/dist/src/core/furnace-refresh.d.ts +18 -6
  322. package/dist/src/core/furnace-refresh.js +5 -8
  323. package/dist/src/core/furnace-registration-ast.d.ts +2 -3
  324. package/dist/src/core/furnace-registration-ast.js +9 -12
  325. package/dist/src/core/furnace-registration-remove.d.ts +4 -4
  326. package/dist/src/core/furnace-registration-remove.js +6 -6
  327. package/dist/src/core/furnace-registration-validate.d.ts +2 -2
  328. package/dist/src/core/furnace-registration-validate.js +2 -2
  329. package/dist/src/core/furnace-registration.d.ts +7 -6
  330. package/dist/src/core/furnace-registration.js +29 -36
  331. package/dist/src/core/furnace-rollback.d.ts +1 -1
  332. package/dist/src/core/furnace-rollback.js +5 -5
  333. package/dist/src/core/furnace-scanner.js +3 -6
  334. package/dist/src/core/furnace-stale-export.d.ts +17 -16
  335. package/dist/src/core/furnace-stale-export.js +16 -15
  336. package/dist/src/core/furnace-staleness.d.ts +2 -2
  337. package/dist/src/core/furnace-staleness.js +2 -2
  338. package/dist/src/core/furnace-state-persist.d.ts +12 -12
  339. package/dist/src/core/furnace-state-persist.js +10 -10
  340. package/dist/src/core/furnace-step-errors.d.ts +6 -6
  341. package/dist/src/core/furnace-step-errors.js +6 -6
  342. package/dist/src/core/furnace-validate-accessibility.js +14 -14
  343. package/dist/src/core/furnace-validate-compatibility.js +9 -9
  344. package/dist/src/core/furnace-validate-registration.d.ts +2 -2
  345. package/dist/src/core/furnace-validate-registration.js +18 -20
  346. package/dist/src/core/furnace-validate-structure.d.ts +1 -1
  347. package/dist/src/core/furnace-validate-structure.js +4 -4
  348. package/dist/src/core/furnace-validate.js +9 -9
  349. package/dist/src/core/furnace-version-drift.d.ts +5 -5
  350. package/dist/src/core/furnace-version-drift.js +5 -5
  351. package/dist/src/core/git-base.d.ts +6 -4
  352. package/dist/src/core/git-base.js +29 -7
  353. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  354. package/dist/src/core/git-diff-new-file.js +119 -0
  355. package/dist/src/core/git-diff.d.ts +13 -13
  356. package/dist/src/core/git-diff.js +121 -189
  357. package/dist/src/core/git-file-ops.d.ts +6 -13
  358. package/dist/src/core/git-file-ops.js +11 -35
  359. package/dist/src/core/git-readonly-index.d.ts +12 -14
  360. package/dist/src/core/git-readonly-index.js +12 -16
  361. package/dist/src/core/git-status.d.ts +9 -2
  362. package/dist/src/core/git-status.js +23 -11
  363. package/dist/src/core/git.d.ts +3 -9
  364. package/dist/src/core/git.js +28 -57
  365. package/dist/src/core/harness-orphans.d.ts +17 -17
  366. package/dist/src/core/harness-orphans.js +28 -27
  367. package/dist/src/core/history-log.d.ts +19 -0
  368. package/dist/src/core/history-log.js +29 -0
  369. package/dist/src/core/license-headers.d.ts +19 -18
  370. package/dist/src/core/license-headers.js +29 -28
  371. package/dist/src/core/mach-build-artifacts.d.ts +21 -21
  372. package/dist/src/core/mach-build-artifacts.js +23 -23
  373. package/dist/src/core/mach-error-hints.d.ts +5 -10
  374. package/dist/src/core/mach-error-hints.js +14 -14
  375. package/dist/src/core/mach-known-noise-filter.d.ts +42 -38
  376. package/dist/src/core/mach-known-noise-filter.js +57 -56
  377. package/dist/src/core/mach-mozconfig.d.ts +6 -6
  378. package/dist/src/core/mach-mozconfig.js +17 -16
  379. package/dist/src/core/mach-python.d.ts +3 -0
  380. package/dist/src/core/mach-python.js +13 -7
  381. package/dist/src/core/mach-resource-shim.d.ts +24 -12
  382. package/dist/src/core/mach-resource-shim.js +124 -21
  383. package/dist/src/core/mach.d.ts +59 -73
  384. package/dist/src/core/mach.js +47 -115
  385. package/dist/src/core/marionette-port.d.ts +15 -30
  386. package/dist/src/core/marionette-port.js +46 -89
  387. package/dist/src/core/marionette-preflight.d.ts +6 -6
  388. package/dist/src/core/marionette-preflight.js +32 -50
  389. package/dist/src/core/markdown-table.d.ts +9 -9
  390. package/dist/src/core/markdown-table.js +14 -14
  391. package/dist/src/core/mochitest-server-port.d.ts +8 -8
  392. package/dist/src/core/mochitest-server-port.js +17 -17
  393. package/dist/src/core/module-resolution-preflight.d.ts +8 -8
  394. package/dist/src/core/module-resolution-preflight.js +11 -11
  395. package/dist/src/core/moz-manifest-helpers.d.ts +7 -7
  396. package/dist/src/core/moz-manifest-helpers.js +7 -7
  397. package/dist/src/core/moz-manifest-register.d.ts +1 -1
  398. package/dist/src/core/moz-manifest-register.js +2 -2
  399. package/dist/src/core/moz-manifest-rules.js +46 -42
  400. package/dist/src/core/moz-manifest-tokenizers.js +1 -1
  401. package/dist/src/core/ownership-table.d.ts +5 -5
  402. package/dist/src/core/ownership-table.js +7 -7
  403. package/dist/src/core/parser-fallback.d.ts +3 -3
  404. package/dist/src/core/parser-fallback.js +3 -3
  405. package/dist/src/core/patch-apply-fuzz.d.ts +5 -5
  406. package/dist/src/core/patch-apply-fuzz.js +13 -13
  407. package/dist/src/core/patch-apply.d.ts +13 -14
  408. package/dist/src/core/patch-apply.js +36 -42
  409. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  410. package/dist/src/core/patch-export-coverage.js +1 -30
  411. package/dist/src/core/patch-export-update.d.ts +21 -4
  412. package/dist/src/core/patch-export-update.js +7 -6
  413. package/dist/src/core/patch-export.d.ts +20 -30
  414. package/dist/src/core/patch-export.js +55 -77
  415. package/dist/src/core/patch-files.d.ts +0 -4
  416. package/dist/src/core/patch-files.js +0 -11
  417. package/dist/src/core/patch-identifier-suggest.d.ts +1 -1
  418. package/dist/src/core/patch-identifier-suggest.js +4 -4
  419. package/dist/src/core/patch-lint-binary.d.ts +12 -18
  420. package/dist/src/core/patch-lint-cache.d.ts +18 -2
  421. package/dist/src/core/patch-lint-cache.js +16 -19
  422. package/dist/src/core/patch-lint-checkjs.d.ts +55 -38
  423. package/dist/src/core/patch-lint-checkjs.js +78 -64
  424. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +8 -9
  425. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -11
  426. package/dist/src/core/patch-lint-creators.d.ts +5 -9
  427. package/dist/src/core/patch-lint-creators.js +3 -3
  428. package/dist/src/core/patch-lint-cross.d.ts +27 -19
  429. package/dist/src/core/patch-lint-cross.js +67 -110
  430. package/dist/src/core/patch-lint-css.d.ts +1 -1
  431. package/dist/src/core/patch-lint-css.js +15 -11
  432. package/dist/src/core/patch-lint-diff-tag.d.ts +5 -5
  433. package/dist/src/core/patch-lint-diff-tag.js +5 -5
  434. package/dist/src/core/patch-lint-diff.d.ts +4 -4
  435. package/dist/src/core/patch-lint-diff.js +4 -4
  436. package/dist/src/core/patch-lint-file-size.d.ts +3 -3
  437. package/dist/src/core/patch-lint-file-size.js +6 -6
  438. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  439. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  440. package/dist/src/core/patch-lint-jsdoc.d.ts +2 -2
  441. package/dist/src/core/patch-lint-jsdoc.js +46 -46
  442. package/dist/src/core/patch-lint-module-registration.d.ts +3 -9
  443. package/dist/src/core/patch-lint-module-registration.js +1 -1
  444. package/dist/src/core/patch-lint-mozbuild.d.ts +1 -1
  445. package/dist/src/core/patch-lint-mozbuild.js +10 -10
  446. package/dist/src/core/patch-lint-observer.d.ts +3 -3
  447. package/dist/src/core/patch-lint-observer.js +7 -7
  448. package/dist/src/core/patch-lint-ownership.d.ts +6 -6
  449. package/dist/src/core/patch-lint-ownership.js +6 -6
  450. package/dist/src/core/patch-lint-prettier.d.ts +6 -6
  451. package/dist/src/core/patch-lint-prettier.js +7 -7
  452. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  453. package/dist/src/core/patch-lint-queue-types.js +12 -0
  454. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  455. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  456. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +23 -23
  457. package/dist/src/core/patch-lint-unmanaged-companion.js +26 -26
  458. package/dist/src/core/patch-lint.d.ts +41 -35
  459. package/dist/src/core/patch-lint.js +52 -49
  460. package/dist/src/core/patch-lock.d.ts +1 -1
  461. package/dist/src/core/patch-lock.js +11 -11
  462. package/dist/src/core/patch-manifest-consistency.d.ts +7 -7
  463. package/dist/src/core/patch-manifest-consistency.js +16 -17
  464. package/dist/src/core/patch-manifest-files-affected.d.ts +1 -1
  465. package/dist/src/core/patch-manifest-files-affected.js +3 -3
  466. package/dist/src/core/patch-manifest-io.d.ts +14 -14
  467. package/dist/src/core/patch-manifest-io.js +35 -34
  468. package/dist/src/core/patch-manifest-query.d.ts +1 -1
  469. package/dist/src/core/patch-manifest-query.js +4 -4
  470. package/dist/src/core/patch-manifest-resolve.d.ts +5 -5
  471. package/dist/src/core/patch-manifest-resolve.js +7 -7
  472. package/dist/src/core/patch-manifest-validate.js +3 -3
  473. package/dist/src/core/patch-manifest.d.ts +2 -2
  474. package/dist/src/core/patch-manifest.js +2 -2
  475. package/dist/src/core/patch-parse.d.ts +32 -21
  476. package/dist/src/core/patch-parse.js +40 -15
  477. package/dist/src/core/patch-policy.js +15 -27
  478. package/dist/src/core/patch-registration-refs.d.ts +4 -4
  479. package/dist/src/core/patch-registration-refs.js +7 -7
  480. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  481. package/dist/src/core/patch-source-metadata.js +0 -4
  482. package/dist/src/core/patch-transform.d.ts +8 -16
  483. package/dist/src/core/patch-transform.js +13 -23
  484. package/dist/src/core/process-cpu-time.d.ts +1 -1
  485. package/dist/src/core/process-cpu-time.js +6 -6
  486. package/dist/src/core/rebase-session.d.ts +9 -15
  487. package/dist/src/core/rebase-session.js +13 -25
  488. package/dist/src/core/register-browser-content.js +3 -2
  489. package/dist/src/core/register-browser-test.js +2 -1
  490. package/dist/src/core/register-module.js +2 -1
  491. package/dist/src/core/register-result.d.ts +1 -1
  492. package/dist/src/core/register-result.js +1 -1
  493. package/dist/src/core/register-scaffold.d.ts +2 -2
  494. package/dist/src/core/register-scaffold.js +8 -7
  495. package/dist/src/core/register-shared-css.d.ts +3 -3
  496. package/dist/src/core/register-shared-css.js +6 -5
  497. package/dist/src/core/register-xpcshell-test.d.ts +2 -2
  498. package/dist/src/core/register-xpcshell-test.js +2 -2
  499. package/dist/src/core/run-log-redact.d.ts +38 -0
  500. package/dist/src/core/run-log-redact.js +69 -0
  501. package/dist/src/core/run-log.d.ts +21 -10
  502. package/dist/src/core/run-log.js +76 -27
  503. package/dist/src/core/shared-ftl.d.ts +2 -2
  504. package/dist/src/core/shared-ftl.js +6 -6
  505. package/dist/src/core/signal-critical.d.ts +2 -2
  506. package/dist/src/core/signal-critical.js +4 -6
  507. package/dist/src/core/smoke-patterns.d.ts +5 -6
  508. package/dist/src/core/smoke-patterns.js +15 -20
  509. package/dist/src/core/state-file.d.ts +15 -4
  510. package/dist/src/core/state-file.js +14 -1
  511. package/dist/src/core/status-binary.d.ts +8 -8
  512. package/dist/src/core/status-binary.js +12 -12
  513. package/dist/src/core/status-classify.d.ts +17 -17
  514. package/dist/src/core/status-classify.js +13 -12
  515. package/dist/src/core/test-changed-prefs.d.ts +12 -12
  516. package/dist/src/core/test-changed-prefs.js +40 -25
  517. package/dist/src/core/test-harness-crash.d.ts +39 -39
  518. package/dist/src/core/test-harness-crash.js +69 -69
  519. package/dist/src/core/test-harness-output.d.ts +7 -7
  520. package/dist/src/core/test-harness-output.js +7 -7
  521. package/dist/src/core/test-harness-verdict-notes.d.ts +13 -14
  522. package/dist/src/core/test-harness-verdict-notes.js +4 -4
  523. package/dist/src/core/test-path-scope.d.ts +8 -8
  524. package/dist/src/core/test-path-scope.js +11 -11
  525. package/dist/src/core/test-stale-check.d.ts +22 -23
  526. package/dist/src/core/test-stale-check.js +31 -30
  527. package/dist/src/core/test-stale-symlink.d.ts +1 -1
  528. package/dist/src/core/test-stale-symlink.js +1 -1
  529. package/dist/src/core/test-stall-triage.d.ts +9 -9
  530. package/dist/src/core/test-stall-triage.js +22 -22
  531. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  532. package/dist/src/core/test-xpcshell-retry.js +14 -11
  533. package/dist/src/core/token-category.d.ts +35 -2
  534. package/dist/src/core/token-category.js +67 -58
  535. package/dist/src/core/token-coverage.js +4 -5
  536. package/dist/src/core/token-dark-mode.d.ts +9 -9
  537. package/dist/src/core/token-dark-mode.js +13 -25
  538. package/dist/src/core/token-docs.d.ts +1 -1
  539. package/dist/src/core/token-inventory.d.ts +56 -0
  540. package/dist/src/core/token-inventory.js +109 -0
  541. package/dist/src/core/token-manager.d.ts +4 -4
  542. package/dist/src/core/token-manager.js +22 -22
  543. package/dist/src/core/token-scaffold.d.ts +4 -4
  544. package/dist/src/core/token-scaffold.js +4 -4
  545. package/dist/src/core/token-variant.d.ts +14 -14
  546. package/dist/src/core/token-variant.js +52 -53
  547. package/dist/src/core/toolchain-preflight.d.ts +15 -15
  548. package/dist/src/core/toolchain-preflight.js +23 -23
  549. package/dist/src/core/tree-cow.d.ts +3 -5
  550. package/dist/src/core/tree-cow.js +8 -8
  551. package/dist/src/core/tree-guard.d.ts +3 -3
  552. package/dist/src/core/tree-guard.js +23 -23
  553. package/dist/src/core/tree-store.d.ts +15 -15
  554. package/dist/src/core/tree-store.js +39 -38
  555. package/dist/src/core/typecheck-shim.d.ts +16 -15
  556. package/dist/src/core/typecheck-shim.js +26 -25
  557. package/dist/src/core/typecheck.d.ts +12 -12
  558. package/dist/src/core/typecheck.js +33 -33
  559. package/dist/src/core/wire-destroy.d.ts +2 -2
  560. package/dist/src/core/wire-destroy.js +5 -5
  561. package/dist/src/core/wire-dom-fragment.d.ts +3 -3
  562. package/dist/src/core/wire-dom-fragment.js +12 -23
  563. package/dist/src/core/wire-init.d.ts +2 -2
  564. package/dist/src/core/wire-init.js +7 -7
  565. package/dist/src/core/wire-subscript.d.ts +1 -1
  566. package/dist/src/core/wire-subscript.js +3 -3
  567. package/dist/src/core/wire-utils.d.ts +9 -9
  568. package/dist/src/core/wire-utils.js +16 -28
  569. package/dist/src/core/xpcshell-appdir.d.ts +20 -20
  570. package/dist/src/core/xpcshell-appdir.js +28 -30
  571. package/dist/src/core/xpcshell-profile-dir.d.ts +3 -3
  572. package/dist/src/core/xpcshell-profile-dir.js +7 -7
  573. package/dist/src/errors/base.d.ts +69 -16
  574. package/dist/src/errors/base.js +95 -26
  575. package/dist/src/errors/build.d.ts +6 -6
  576. package/dist/src/errors/build.js +26 -34
  577. package/dist/src/errors/codes.d.ts +10 -10
  578. package/dist/src/errors/codes.js +10 -10
  579. package/dist/src/errors/config.d.ts +0 -14
  580. package/dist/src/errors/config.js +13 -34
  581. package/dist/src/errors/download.d.ts +13 -0
  582. package/dist/src/errors/download.js +53 -24
  583. package/dist/src/errors/furnace.d.ts +2 -2
  584. package/dist/src/errors/furnace.js +8 -9
  585. package/dist/src/errors/git.d.ts +9 -9
  586. package/dist/src/errors/git.js +21 -31
  587. package/dist/src/errors/patch.d.ts +3 -3
  588. package/dist/src/errors/patch.js +11 -13
  589. package/dist/src/errors/rebase.d.ts +1 -1
  590. package/dist/src/errors/rebase.js +8 -8
  591. package/dist/src/errors/run.d.ts +1 -1
  592. package/dist/src/errors/run.js +1 -1
  593. package/dist/src/index.d.ts +4 -4
  594. package/dist/src/index.js +4 -4
  595. package/dist/src/types/commands/options.d.ts +155 -200
  596. package/dist/src/types/commands/patches.d.ts +19 -19
  597. package/dist/src/types/commands/project.d.ts +3 -3
  598. package/dist/src/types/config.d.ts +52 -19
  599. package/dist/src/types/furnace.d.ts +17 -17
  600. package/dist/src/types/index.d.ts +0 -1
  601. package/dist/src/types/json.d.ts +3 -3
  602. package/dist/src/types/json.js +2 -2
  603. package/dist/src/types/typecheck.d.ts +3 -3
  604. package/dist/src/utils/assert.d.ts +4 -16
  605. package/dist/src/utils/assert.js +7 -24
  606. package/dist/src/utils/build-info.d.ts +6 -6
  607. package/dist/src/utils/build-info.js +6 -6
  608. package/dist/src/utils/child-env.d.ts +7 -6
  609. package/dist/src/utils/child-env.js +5 -5
  610. package/dist/src/utils/concurrency.d.ts +2 -2
  611. package/dist/src/utils/concurrency.js +2 -2
  612. package/dist/src/utils/elapsed.js +10 -1
  613. package/dist/src/utils/errors.d.ts +8 -8
  614. package/dist/src/utils/errors.js +8 -8
  615. package/dist/src/utils/fs.d.ts +1 -1
  616. package/dist/src/utils/fs.js +15 -20
  617. package/dist/src/utils/hash.d.ts +12 -0
  618. package/dist/src/utils/hash.js +16 -0
  619. package/dist/src/utils/line-dispatch.d.ts +15 -0
  620. package/dist/src/utils/line-dispatch.js +48 -0
  621. package/dist/src/utils/logger.d.ts +4 -4
  622. package/dist/src/utils/logger.js +11 -11
  623. package/dist/src/utils/machine-output.js +2 -2
  624. package/dist/src/utils/options.d.ts +14 -20
  625. package/dist/src/utils/options.js +26 -27
  626. package/dist/src/utils/package-root.js +5 -14
  627. package/dist/src/utils/paths.d.ts +4 -4
  628. package/dist/src/utils/paths.js +4 -4
  629. package/dist/src/utils/platform.d.ts +0 -4
  630. package/dist/src/utils/process-group.d.ts +10 -9
  631. package/dist/src/utils/process-group.js +37 -36
  632. package/dist/src/utils/process.d.ts +70 -55
  633. package/dist/src/utils/process.js +129 -179
  634. package/dist/src/utils/ps-duration.d.ts +4 -4
  635. package/dist/src/utils/ps-duration.js +9 -9
  636. package/dist/src/utils/regex.d.ts +4 -4
  637. package/dist/src/utils/regex.js +7 -7
  638. package/dist/src/utils/sleep.d.ts +26 -0
  639. package/dist/src/utils/sleep.js +31 -0
  640. package/dist/src/utils/stdio-drain.d.ts +7 -7
  641. package/dist/src/utils/stdio-drain.js +8 -8
  642. package/dist/src/utils/validation.d.ts +12 -49
  643. package/dist/src/utils/validation.js +26 -36
  644. package/docs/README.md +2 -2
  645. package/docs/configuration.md +163 -69
  646. package/docs/exit-codes.md +66 -49
  647. package/docs/furnace.md +20 -19
  648. package/docs/lifecycle-invariants.md +414 -247
  649. package/docs/machine-output.md +58 -34
  650. package/docs/patch-workflow.md +48 -44
  651. package/docs/run-logs.md +72 -28
  652. package/docs/testing.md +105 -98
  653. package/docs/verification-trees.md +23 -22
  654. package/package.json +7 -6
  655. package/dist/src/commands/rebase.d.ts +0 -4
  656. package/dist/src/commands/rebase.js +0 -5
  657. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  658. package/dist/src/core/patch-artifact-normalize.js +0 -24
  659. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  660. package/dist/src/core/patch-lint-reexports.js +0 -8
  661. package/dist/src/core/wire-targets.d.ts +0 -7
  662. package/dist/src/core/wire-targets.js +0 -8
@@ -5,7 +5,6 @@
5
5
  * loop calls {@link reExportSinglePatchWithIndexLockRetry} per selected
6
6
  * patch.
7
7
  */
8
- import { getProjectPaths } from '../core/config.js';
9
8
  import { stdioIsInteractive } from '../core/destructive.js';
10
9
  import { enforceFreshFurnaceSources } from '../core/furnace-stale-export.js';
11
10
  import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
@@ -14,6 +13,7 @@ import { updatePatchAndMetadata } from '../core/patch-export.js';
14
13
  import { buildProjectedManifest, enforcePatchPolicy } from '../core/patch-policy.js';
15
14
  import { isGitIndexLockConflict } from '../errors/git.js';
16
15
  import { info, success, warn } from '../utils/logger.js';
16
+ import { sleep } from '../utils/sleep.js';
17
17
  import { findMissingFiles, reportAdjacentUnmanagedFiles } from './re-export-adjacent.js';
18
18
  import { reportForeignDrift } from './re-export-drift.js';
19
19
  import { lintReExportedPatch, refreshQueueCtxEntry, storeReExportLintResult, } from './re-export-lint.js';
@@ -23,23 +23,24 @@ const GIT_INDEX_LOCK_RETRY_MS = 300;
23
23
  * Retries a single patch re-export exactly once when the failure is
24
24
  * transient git `index.lock` contention (an external git process holding
25
25
  * the engine repo's index). The retry re-enters `reExportSinglePatch`
26
- * from the top, so it re-reads clean state; a second lock failure
26
+ * from the top, so it re-reads clean state. A second lock failure
27
27
  * propagates to the loop's honest "N of M" accounting.
28
28
  */
29
- export async function reExportSinglePatchWithIndexLockRetry(patch, paths, manifest, options, isDryRun, config, adjacentCtx, driftCtx, lintCtx) {
29
+ export async function reExportSinglePatchWithIndexLockRetry(args) {
30
30
  try {
31
- return await reExportSinglePatch(patch, paths, manifest, options, isDryRun, config, adjacentCtx, driftCtx, lintCtx);
31
+ return await reExportSinglePatch(args);
32
32
  }
33
33
  catch (error) {
34
34
  if (!isGitIndexLockConflict(error)) {
35
35
  throw error;
36
36
  }
37
- warn(`${patch.filename}: git index.lock contention detected — retrying once after ${String(GIT_INDEX_LOCK_RETRY_MS)} ms...`);
38
- await new Promise((resolve) => setTimeout(resolve, GIT_INDEX_LOCK_RETRY_MS));
39
- return await reExportSinglePatch(patch, paths, manifest, options, isDryRun, config, adjacentCtx, driftCtx, lintCtx);
37
+ warn(`${args.patch.filename}: git index.lock contention detected — retrying once after ${GIT_INDEX_LOCK_RETRY_MS} ms...`);
38
+ await sleep(GIT_INDEX_LOCK_RETRY_MS);
39
+ return await reExportSinglePatch(args);
40
40
  }
41
41
  }
42
- async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, config, adjacentCtx, driftCtx, lintCtx) {
42
+ async function reExportSinglePatch(args) {
43
+ const { patch, paths, manifest, options, isDryRun, config, adjacentCtx, driftCtx, lintCtx } = args;
43
44
  let currentFilesAffected = [...patch.filesAffected];
44
45
  // --- Scan for new/removed files ---
45
46
  if (options.scan) {
@@ -52,10 +53,10 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
52
53
  ...(options.scanFiles !== undefined ? { scanFiles: options.scanFiles } : {}),
53
54
  });
54
55
  // Forward-import gate at adoption time (runs in dry-run too): refuse
55
- // to adopt unmanaged files that import modules created by LATER
56
- // patches — the after-the-fact lint failure this leaves behind is the
57
- // field-reported footgun. Applies to broad --scan, --scan-file, and
58
- // the --scan-files bulk assignments alike.
56
+ // to adopt unmanaged files that import modules created by later
57
+ // patches, because the after-the-fact lint failure this leaves behind
58
+ // is the field-reported footgun. Applies to broad --scan, --scan-file,
59
+ // and the --scan-files bulk assignments alike.
59
60
  await assertScanAdoptionsHaveNoForwardImports({
60
61
  patchesDir: paths.patches,
61
62
  engineDir: paths.engine,
@@ -81,7 +82,7 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
81
82
  // When neither `--scan` nor `--files` is set and some of the manifest's
82
83
  // claimed files no longer exist on disk, the re-export silently writes a
83
84
  // refreshed body whose filesAffected still names the vanished paths.
84
- // That is the documented contract, but it is also a footgun — a later
85
+ // That is the documented contract, but it is also a footgun: a later
85
86
  // `verify` then fails on manifest-consistency with no obvious trigger.
86
87
  // Emit one advisory warning up-front when the drift is cheap to detect,
87
88
  // so the operator can re-run with `--scan` or `--files` before the stale
@@ -91,9 +92,9 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
91
92
  return false;
92
93
  }
93
94
  // --- Explicit file-subset path ---
94
- // When --files is given, the target filesAffected is authoritative — drop
95
+ // When --files is given, the target filesAffected is authoritative: drop
95
96
  // anything not in the list, add anything new. This is the surgical repair
96
- // primitive that replaces hand-editing patches.json; the user has already
97
+ // primitive that replaces hand-editing patches.json. The user has already
97
98
  // acknowledged via confirmDestructive (done in the caller) that any drop
98
99
  // is intentional.
99
100
  if (options.files !== undefined) {
@@ -108,16 +109,16 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
108
109
  }
109
110
  // Stale-furnace-source gate: re-export captures deployed engine copies, so
110
111
  // a component source edited after the last furnace apply would land in the
111
- // patch as its OLD deployed content. Refuse (or warn under
112
+ // patch as its old deployed content. Refuse (or warn under
112
113
  // --allow-stale-furnace) before diffing. Runs in dry-run too so the failure
113
114
  // surfaces early.
114
115
  await enforceFreshFurnaceSources(paths.root, currentFilesAffected, options.allowStaleFurnace === true, 're-export');
115
116
  const absentFiles = await findMissingFiles(paths.engine, currentFilesAffected);
116
- // A path absent from disk but tracked in engine HEAD is a DELETION, and
117
+ // A path absent from disk but tracked in engine HEAD is a deletion, and
117
118
  // `git diff HEAD` renders it as a real `deleted file mode` section. Only
118
119
  // a never-tracked path has nothing to diff. Skipping both alike meant a
119
120
  // retired file quietly left the patch body while staying in its file
120
- // list — the patch then claimed a file it could not restore.
121
+ // list, so the patch then claimed a file it could not restore.
121
122
  const trackedAbsent = await listTrackedInHead(paths.engine, absentFiles);
122
123
  const deletedFiles = absentFiles.filter((f) => trackedAbsent.has(f));
123
124
  const untrackedAbsent = absentFiles.filter((f) => !trackedAbsent.has(f));
@@ -147,9 +148,9 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
147
148
  }
148
149
  // Foreign-drift preview + optional hard stop: show which
149
150
  // payload lines are about to enter the body that the old body did not
150
- // carry — the case where a concurrent session's uncommitted edits in
151
- // OWNED files would be silently absorbed. Always previews (including
152
- // dry-run and --scan); refuses only under --refuse-foreign-drift.
151
+ // carry, the case where a concurrent session's uncommitted edits in
152
+ // owned files would be silently absorbed. Always previews (including
153
+ // dry-run and --scan). Refuses only under --refuse-foreign-drift.
153
154
  if (await reportForeignDrift({
154
155
  patch,
155
156
  patchesDir: paths.patches,
@@ -166,7 +167,7 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
166
167
  command: 're-export',
167
168
  forceUnsafe: options.forceUnsafe === true,
168
169
  });
169
- // Lint against the ONCE-per-invocation hoisted context (queue context +
170
+ // Lint against the once-per-invocation hoisted context (queue context +
170
171
  // queue-wide checkJs program + per-patch result cache) instead of
171
172
  // rebuilding ~37 s of setup for every patch in the loop.
172
173
  const projectedMetadata = { ...patch, ...updates };
@@ -212,17 +213,24 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
212
213
  * directory lock (a split write would let a concurrent queue mutation
213
214
  * leave body and `filesAffected` disagreeing), then keeps the run's
214
215
  * in-memory state honest: the queue context entry is refreshed and the
215
- * fresh lint result is stored keyed against the just-written body — the
216
- * state a repeat invocation will observe — and the in-memory
216
+ * fresh lint result is stored keyed against the just-written body (the
217
+ * state a repeat invocation will observe), and the in-memory
217
218
  * manifest row mirrors the on-disk write for later loop iterations
218
219
  * (notably `--all --scan`, where `getClaimedFiles` reads this manifest).
219
220
  */
220
221
  async function commitRefreshedPatch(args) {
221
222
  const { patch, paths, manifest, options, config, lintCtx, diffContent, updates } = args;
222
- const bodyChanged = await updatePatchAndMetadata(paths.patches, patch.filename, diffContent, updates, undefined, {
223
- config,
224
- command: 're-export',
225
- forceUnsafe: options.forceUnsafe === true,
223
+ const bodyChanged = await updatePatchAndMetadata({
224
+ patchesDir: paths.patches,
225
+ filename: patch.filename,
226
+ newContent: diffContent,
227
+ updates,
228
+ onCommitted: undefined,
229
+ policyGate: {
230
+ config,
231
+ command: 're-export',
232
+ forceUnsafe: options.forceUnsafe === true,
233
+ },
226
234
  });
227
235
  refreshQueueCtxEntry(lintCtx, patch.filename, diffContent);
228
236
  if (args.lintResult !== null) {
@@ -238,8 +246,8 @@ async function commitRefreshedPatch(args) {
238
246
  };
239
247
  }
240
248
  }
241
- // A bulk run refreshes every patch; saying "Re-exported" for one whose body
242
- // did not move buries the patches that DID move. The run still counts this
249
+ // A bulk run refreshes every patch. Saying "Re-exported" for one whose body
250
+ // did not move buries the patches that did move. The run still counts this
243
251
  // patch as successfully refreshed, so `--stamp` completeness is unaffected.
244
252
  if (bodyChanged) {
245
253
  success(`Re-exported ${patch.filename}`);
@@ -250,7 +258,7 @@ async function commitRefreshedPatch(args) {
250
258
  }
251
259
  /**
252
260
  * Threads the patch's own `lintIgnore` list through so the per-patch
253
- * suppression honoured by export/export-all is also honoured here — a
261
+ * suppression honoured by export/export-all is also honoured here. A
254
262
  * re-export could otherwise not refresh an advisory-noisy but intentional
255
263
  * patch without the blunt `--skip-lint`. The paired `patch.tier` threads the
256
264
  * explicit branding-threshold opt-in the same way. The CLI flags `--tier`
@@ -9,7 +9,7 @@ import { loadPatchesManifest, resolvePatchIdentifier, stampPatchVersions, } from
9
9
  import { GeneralError, InvalidArgumentError } from '../errors/base.js';
10
10
  import { elapsedSince } from '../utils/elapsed.js';
11
11
  import { toError } from '../utils/errors.js';
12
- import { cancel, info, intro, isCancel, outro, spinner, success, warn } from '../utils/logger.js';
12
+ import { cancel, info, intro, isCancel, outro, spinner, success, warn, } from '../utils/logger.js';
13
13
  import { loadScanFilesAssignments, withDryRunPurityGuard, withDryRunReExportLock, } from './re-export-bulk-scan.js';
14
14
  import { warnUnseenExpectedDrift } from './re-export-drift.js';
15
15
  import { reExportFilesInPlace } from './re-export-files.js';
@@ -26,15 +26,16 @@ async function resolveSelectedPatches(patches, options, manifest) {
26
26
  for (const identifier of patches) {
27
27
  const match = resolvePatchIdentifier(identifier, manifest.patches);
28
28
  if (!match) {
29
- // Suggest, never dump: a wrong guess would otherwise print the
30
- // whole ~300-entry manifest, burying the error it was reporting.
29
+ // Suggest close matches instead of dumping the queue: a wrong guess
30
+ // would otherwise print the whole ~300-entry manifest, burying the
31
+ // error it was reporting.
31
32
  throw new InvalidArgumentError(formatPatchNotFoundError(identifier, manifest.patches), identifier);
32
33
  }
33
34
  selectedPatches.push(match);
34
35
  }
35
36
  return selectedPatches;
36
37
  }
37
- // No patches specified — prompt or error
38
+ // No patches specified: prompt or error
38
39
  const isInteractive = stdioIsInteractive();
39
40
  if (!isInteractive) {
40
41
  throw new InvalidArgumentError('Specify patch identifiers or use --all in non-interactive mode.\n\n' +
@@ -143,7 +144,17 @@ export async function reExportCommand(projectRoot, patches, options) {
143
144
  const patchOptions = assignedScanFiles !== undefined ? { ...options, scanFiles: assignedScanFiles } : options;
144
145
  progress.message(`Re-exporting ${index + 1}/${selectedPatches.length}: ${patch.filename} (${patch.filesAffected.length} file(s), ${elapsedSince(startedAt)} elapsed)...`);
145
146
  try {
146
- const exported = await reExportSinglePatchWithIndexLockRetry(patch, paths, manifest, patchOptions, isDryRun, config, adjacentCtx, driftCtx, lintCtx);
147
+ const exported = await reExportSinglePatchWithIndexLockRetry({
148
+ patch,
149
+ paths,
150
+ manifest,
151
+ options: patchOptions,
152
+ isDryRun,
153
+ config,
154
+ adjacentCtx,
155
+ driftCtx,
156
+ lintCtx,
157
+ });
147
158
  if (exported) {
148
159
  reExported++;
149
160
  reExportedFilenames.push(patch.filename);
@@ -166,7 +177,7 @@ export async function reExportCommand(projectRoot, patches, options) {
166
177
  }
167
178
  // `--stamp` only fires on a run where every selected patch refreshed
168
179
  // cleanly. A partial success would leave some patches with a stale body
169
- // but a new version — the opposite of the "what I tested, what the
180
+ // but a new version, the opposite of the "what I tested, what the
170
181
  // manifest says" invariant `sourceEsrVersion` exists to record. A
171
182
  // non-empty `reExportedFilenames` with fewer entries than `selectedPatches`
172
183
  // means a lint failure or missing-file skip landed somewhere in the loop,
@@ -204,7 +215,7 @@ function buildForeignDriftContext(options) {
204
215
  /**
205
216
  * Names `--expect-unmanaged` paths that were never met this run. A typo'd
206
217
  * carve-out silently leaves the refusal armed for the path it was meant to
207
- * admit — and, worse, reads as an approval that is in force when it is not.
218
+ * admit, and reads as an approval that is in force when it is not.
208
219
  * A warning rather than a refusal: an approved path legitimately goes unmet
209
220
  * when the reviewed file has since been adopted into a patch, which is the
210
221
  * outcome the carve-out exists to be retired by.
@@ -227,19 +238,26 @@ function warnUnseenApprovedUnmanaged(adjacentCtx) {
227
238
  function throwRunLevelRefusals(adjacentCtx, driftCtx, progress) {
228
239
  if (adjacentCtx.refusals.length > 0) {
229
240
  progress.error('Re-export refused');
230
- const names = adjacentCtx.refusals.map((r) => r.patchFilename).join(', ');
231
- throw new GeneralError(`Refused ${String(adjacentCtx.refusals.length)} patch(es) with adjacent unmanaged files ` +
232
- `(--refuse-adjacent-unmanaged): ${names}. Adopt reviewed files with --scan --scan-file, ` +
233
- `or export them as a new patch.`);
241
+ // Each offender is named with the owned directory that made it
242
+ // adjacent: an unattended run reads only this line, and without the
243
+ // anchor it cannot tell which of a multi-directory patch's locations
244
+ // the file sits beside.
245
+ const offenders = adjacentCtx.refusals
246
+ .map((r) => `${r.patchFilename} (${r.anchored.join(', ')})`)
247
+ .join('; ');
248
+ throw new GeneralError(`Refused ${adjacentCtx.refusals.length} patch(es) with adjacent unmanaged files ` +
249
+ `(--refuse-adjacent-unmanaged): ${offenders}. Adjacent means the file sits in a ` +
250
+ `directory the patch already owns a file in. Adopt reviewed files with ` +
251
+ `--scan --scan-file, or export them as a new patch.`);
234
252
  }
235
253
  if (driftCtx.refusals.length > 0) {
236
254
  progress.error('Re-export refused');
237
255
  const names = driftCtx.refusals.map((r) => r.patchFilename).join(', ');
238
256
  const unreadable = driftCtx.refusals.filter((r) => r.reason === 'baseline-unreadable');
239
257
  const unreadablePart = unreadable.length > 0
240
- ? ` ${String(unreadable.length)} of these refused because the old patch body was missing or unreadable, so the drift comparison could not run (fail-closed).`
258
+ ? ` ${unreadable.length} of these refused because the old patch body was missing or unreadable, so the drift comparison could not run (fail-closed).`
241
259
  : '';
242
- throw new GeneralError(`Refused ${String(driftCtx.refusals.length)} patch(es) whose refreshed body would absorb ` +
260
+ throw new GeneralError(`Refused ${driftCtx.refusals.length} patch(es) whose refreshed body would absorb ` +
243
261
  `engine edits not present in the old patch body (--refuse-foreign-drift): ${names}.${unreadablePart}\n\n` +
244
262
  'The flag cannot tell WHO wrote those lines, and the two populations have opposite ' +
245
263
  'remedies:\n' +
@@ -254,7 +272,7 @@ function throwRunLevelRefusals(adjacentCtx, driftCtx, progress) {
254
272
  * Prints the end-of-run summary and enforces the exit contract: a partial run
255
273
  * exits non-zero by default. "Re-exported 2 of 3" printing with exit 0 lets a
256
274
  * partial refresh ride an `&&` chain as success. The summary still prints
257
- * first so the honest "N of M" accounting stays visible; the throw then
275
+ * first so the honest "N of M" accounting stays visible. The throw then
258
276
  * carries the failed/skipped breakdown. Dry-run included: a preview
259
277
  * reporting that the real run would partially fail must not read as a passing
260
278
  * preview in scripts.
@@ -283,9 +301,9 @@ function reportReExportOutcome(args) {
283
301
  if (reExported < selectedCount) {
284
302
  const skippedCount = selectedCount - reExported - args.failedFilenames.length;
285
303
  const failedPart = args.failedFilenames.length > 0 ? ` Failed: ${args.failedFilenames.join(', ')}.` : '';
286
- const skippedPart = skippedCount > 0 ? ` Skipped: ${String(skippedCount)} patch(es) (see warnings above).` : '';
287
- throw new GeneralError(`${isDryRun ? '[dry-run] Would re-export' : 'Re-exported'} only ${String(reExported)} of ` +
288
- `${String(selectedCount)} selected patch(es).${failedPart}${skippedPart} ` +
304
+ const skippedPart = skippedCount > 0 ? ` Skipped: ${skippedCount} patch(es) (see warnings above).` : '';
305
+ throw new GeneralError(`${isDryRun ? '[dry-run] Would re-export' : 'Re-exported'} only ${reExported} of ` +
306
+ `${selectedCount} selected patch(es).${failedPart}${skippedPart} ` +
289
307
  'Partial re-export exits non-zero so it cannot pass as success in scripts; ' +
290
308
  'the per-patch errors are listed above.');
291
309
  }
@@ -2,21 +2,22 @@
2
2
  /**
3
3
  * Rebase abort flow.
4
4
  */
5
- import { getProjectPaths, updateState } from '../../core/config.js';
5
+ import { getProjectPaths } from '../../core/config.js';
6
6
  import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
7
7
  import { resetChanges } from '../../core/git.js';
8
8
  import { clearRebaseSession, getRebaseSessionPath, readRebaseSession, } from '../../core/rebase-session.js';
9
9
  import { NoRebaseSessionError } from '../../errors/rebase.js';
10
10
  import { intro, outro, spinner, success, warn } from '../../utils/logger.js';
11
+ import { clearPendingResolution } from '../pending-resolution.js';
11
12
  import { confirmDirtyEngineReset } from './confirm.js';
12
13
  /**
13
14
  * Handles `fireforge rebase --abort`.
14
15
  */
15
16
  export async function handleAbort(projectRoot, yes) {
16
17
  intro('FireForge Rebase — Abort');
17
- // Abort is the escape hatch, so it deliberately does NOT require a *valid*
18
- // session — only that one is present. Nothing below reads the session
19
- // object (the restore works off `paths.engine` and `resetChanges`), and an
18
+ // Abort is the escape hatch, so it does not require a valid session, only
19
+ // that one is present. Nothing below reads the session object (the restore
20
+ // works off `paths.engine` and `resetChanges`), and an
20
21
  // `if (!session) throw` would refuse to run against a corrupt file, which
21
22
  // is the only thing that could have cleared it.
22
23
  const read = await readRebaseSession(projectRoot);
@@ -39,8 +40,8 @@ export async function handleAbort(projectRoot, yes) {
39
40
  return;
40
41
  }
41
42
  const s = spinner('Restoring engine to pre-rebase state...');
42
- // Step 1: git reset. If this fails, the rebase session MUST stay on disk
43
- // so the user can retry the abort — resetChanges is the only irreversible
43
+ // Step 1: git reset. If this fails, the rebase session must stay on disk
44
+ // so the user can retry the abort: resetChanges is the only irreversible
44
45
  // operation in this handler and everything downstream assumes it ran.
45
46
  try {
46
47
  await resetChanges(paths.engine);
@@ -57,14 +58,8 @@ export async function handleAbort(projectRoot, yes) {
57
58
  // idempotently re-clear furnace state.
58
59
  await clearAppliedFurnaceState(projectRoot);
59
60
  // Step 3: clear pending resolution transactionally.
60
- await updateState(projectRoot, (current) => {
61
- if (!current.pendingResolution)
62
- return current;
63
- const next = { ...current };
64
- delete next.pendingResolution;
65
- return next;
66
- });
67
- // Step 4: clear the rebase session LAST so a failure in any prior step
61
+ await clearPendingResolution(projectRoot);
62
+ // Step 4: clear the rebase session last so a failure in any prior step
68
63
  // preserves the session on disk and a retry of --abort can succeed.
69
64
  await clearRebaseSession(projectRoot);
70
65
  success('Rebase aborted and session cleared.');
@@ -3,7 +3,7 @@
3
3
  * Rebase --continue flow.
4
4
  */
5
5
  import { join } from 'node:path';
6
- import { getProjectPaths, updateState } from '../../core/config.js';
6
+ import { getProjectPaths } from '../../core/config.js';
7
7
  import { getStagedDiffForFiles } from '../../core/git-diff.js';
8
8
  import { stageFiles, unstageFiles } from '../../core/git-file-ops.js';
9
9
  import { updatePatchAndMetadata } from '../../core/patch-export.js';
@@ -14,13 +14,14 @@ import { GeneralError } from '../../errors/base.js';
14
14
  import { CorruptRebaseSessionError, NoRebaseSessionError, RebaseError, } from '../../errors/rebase.js';
15
15
  import { pathExists } from '../../utils/fs.js';
16
16
  import { info, intro, success, warn } from '../../utils/logger.js';
17
+ import { clearPendingResolution } from '../pending-resolution.js';
17
18
  import { runPatchLoop } from './patch-loop.js';
18
19
  /**
19
20
  * Handles `fireforge rebase --continue`.
20
21
  */
21
22
  export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
22
23
  intro('FireForge Rebase — Continue');
23
- // A present-but-unreadable session is NOT "no session in progress" — that
24
+ // A present-but-unreadable session is not "no session in progress". That
24
25
  // conflation is what made a corrupt file unrecoverable. Name the file and
25
26
  // point at --abort, which can now clear it.
26
27
  const read = await readRebaseSession(projectRoot);
@@ -33,10 +34,10 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
33
34
  const paths = getProjectPaths(projectRoot);
34
35
  // Special case: every patch has already applied but a previous run failed
35
36
  // somewhere in the post-apply work (re-export, version stamping). In that
36
- // state currentIndex is past the end of the queue; jumping straight back
37
+ // state currentIndex is past the end of the queue. Jumping straight back
37
38
  // into runPatchLoop replays the no-op apply loop and re-attempts the
38
- // post-apply pipeline. Without this branch the user would be stuck —
39
- // there is no failed patch to resolve, but the session is still active.
39
+ // post-apply pipeline. Without this branch the user would be stuck: there
40
+ // is no failed patch to resolve, but the session is still active.
40
41
  if (session.currentIndex >= session.patches.length) {
41
42
  info('All patches already applied; retrying post-apply re-export and version stamping.');
42
43
  await runPatchLoop(projectRoot, session, paths, maxFuzz, waitLockSeconds);
@@ -77,10 +78,15 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
77
78
  // updatePatchMetadata sequence can interleave with a concurrent export /
78
79
  // re-export / patch reorder / patch compact and leave the manifest
79
80
  // disagreeing with the freshly-written patch body. Mirrors resolve.ts.
80
- await updatePatchAndMetadata(paths.patches, currentPatch.filename, diffContent, {
81
- sourceEsrVersion: session.toVersion,
82
- sourceVersion: session.toVersion,
83
- ...(session.toProduct !== undefined ? { sourceProduct: session.toProduct } : {}),
81
+ await updatePatchAndMetadata({
82
+ patchesDir: paths.patches,
83
+ filename: currentPatch.filename,
84
+ newContent: diffContent,
85
+ updates: {
86
+ sourceEsrVersion: session.toVersion,
87
+ sourceVersion: session.toVersion,
88
+ ...(session.toProduct !== undefined ? { sourceProduct: session.toProduct } : {}),
89
+ },
84
90
  });
85
91
  }
86
92
  finally {
@@ -93,9 +99,10 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
93
99
  // clear is held until both writes land, matching the guarantee the apply
94
100
  // loop in patch-loop.ts provides.
95
101
  await runInSignalCriticalSection(`rebase-continue:${currentPatch.filename}`, async () => {
96
- // Replaced, not mutated: flipping `status` in place leaves the
97
- // failure's `error` and `conflictingFiles` on a now-resolved entry, and
98
- // persist that to the session file. The union makes it impossible.
102
+ // The entry is replaced rather than mutated. Flipping `status` in place
103
+ // would leave the failure's `error` and `conflictingFiles` on a
104
+ // now-resolved entry and persist that to the session file. The union
105
+ // type rules that out.
99
106
  session.patches[session.currentIndex] = {
100
107
  filename: currentPatch.filename,
101
108
  status: 'resolved',
@@ -104,13 +111,7 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
104
111
  await saveRebaseSession(projectRoot, session);
105
112
  // Clear pending resolution transactionally so concurrent state-file
106
113
  // writes to unrelated keys are not clobbered by a stale reload.
107
- await updateState(projectRoot, (current) => {
108
- if (!current.pendingResolution)
109
- return current;
110
- const next = { ...current };
111
- delete next.pendingResolution;
112
- return next;
113
- });
114
+ await clearPendingResolution(projectRoot);
114
115
  });
115
116
  success(`Resolved ${currentPatch.filename}`);
116
117
  // Continue applying remaining patches
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `fireforge rebase` — semi-automated Firefox source version upgrade.
2
+ * `fireforge rebase`: semi-automated Firefox source version upgrade.
3
3
  *
4
4
  * Orchestrates the full patch-rebase workflow:
5
5
  * 1. Reset engine to baseline
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * `fireforge rebase` — semi-automated Firefox source version upgrade.
3
+ * `fireforge rebase`: semi-automated Firefox source version upgrade.
4
4
  *
5
5
  * Orchestrates the full patch-rebase workflow:
6
6
  * 1. Reset engine to baseline
@@ -17,7 +17,7 @@ import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
17
17
  import { getHead, resetChanges } from '../../core/git.js';
18
18
  import { discoverPatches } from '../../core/patch-files.js';
19
19
  import { loadPatchesManifest } from '../../core/patch-manifest.js';
20
- import { getPatchSourceProduct, getPatchSourceVersion } from '../../core/patch-source-metadata.js';
20
+ import { getPatchSourceVersion } from '../../core/patch-source-metadata.js';
21
21
  import { getRebaseSessionPath, readRebaseSession, saveRebaseSession, } from '../../core/rebase-session.js';
22
22
  import { GeneralError } from '../../errors/base.js';
23
23
  import { CorruptRebaseSessionError, RebaseSessionExistsError } from '../../errors/rebase.js';
@@ -33,7 +33,7 @@ async function handleFreshStart(projectRoot, options) {
33
33
  const isDryRun = options.dryRun === true;
34
34
  const maxFuzz = options.maxFuzz ?? 3;
35
35
  intro(isDryRun ? 'FireForge Rebase (dry run)' : 'FireForge Rebase');
36
- // One read decides liveness AND validity: a pathExists pre-probe here left
36
+ // One read decides liveness and validity: a pathExists pre-probe here left
37
37
  // a window where the file vanished between probe and read, reporting
38
38
  // "already in progress" with no session on disk.
39
39
  const existing = await readRebaseSession(projectRoot);
@@ -59,7 +59,7 @@ async function handleFreshStart(projectRoot, options) {
59
59
  }
60
60
  // Determine the "from" version from the patches
61
61
  const patchVersions = new Set(manifest.patches.map((p) => getPatchSourceVersion(p)));
62
- const patchProducts = new Set(manifest.patches.map((p) => getPatchSourceProduct(p)).filter(Boolean));
62
+ const patchProducts = new Set(manifest.patches.map((p) => p.sourceProduct).filter(Boolean));
63
63
  const sortedVersions = [...patchVersions].sort();
64
64
  const fromVersion = sortedVersions[0] ?? currentVersion;
65
65
  const fromProduct = [...patchProducts].sort()[0] ?? config.firefox.product;
@@ -93,7 +93,7 @@ async function handleFreshStart(projectRoot, options) {
93
93
  const resetSpinner = spinner('Resetting engine to baseline...');
94
94
  await resetChanges(paths.engine);
95
95
  resetSpinner.stop('Engine reset to baseline');
96
- // Clear Furnace state — the engine no longer contains deployed components.
96
+ // Clear Furnace state: the engine no longer contains deployed components.
97
97
  await clearAppliedFurnaceState(projectRoot);
98
98
  // Create rebase session
99
99
  const allPatches = await discoverPatches(paths.patches);
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Patch application loop and re-export flow.
3
3
  */
4
- import type { getProjectPaths } from '../../core/config.js';
5
4
  import type { RebaseSession } from '../../core/rebase-session.js';
5
+ import type { ProjectPaths } from '../../types/config.js';
6
6
  /**
7
7
  * Runs the patch application loop, re-exports applied patches, and stamps versions.
8
8
  *
@@ -12,4 +12,4 @@ import type { RebaseSession } from '../../core/rebase-session.js';
12
12
  * @param maxFuzz - Maximum context-reduction steps for `git apply -C<n>`
13
13
  * @param waitLockSeconds - `--wait-lock` budget for the patch-directory lock
14
14
  */
15
- export declare function runPatchLoop(projectRoot: string, session: RebaseSession, paths: ReturnType<typeof getProjectPaths>, maxFuzz: number, waitLockSeconds?: number): Promise<void>;
15
+ export declare function runPatchLoop(projectRoot: string, session: RebaseSession, paths: ProjectPaths, maxFuzz: number, waitLockSeconds?: number): Promise<void>;
@@ -20,13 +20,14 @@ import { toError } from '../../utils/errors.js';
20
20
  import { pathExists } from '../../utils/fs.js';
21
21
  import { error, info, outro, spinner, success, warn } from '../../utils/logger.js';
22
22
  import { isValidFirefoxVersion } from '../../utils/validation.js';
23
+ import { clearPendingResolution } from '../pending-resolution.js';
23
24
  import { buildRebaseConflictSummary } from './conflict-summary.js';
24
25
  import { printSummary } from './summary.js';
25
26
  /**
26
27
  * Builds the session entry for a patch-apply outcome.
27
28
  *
28
- * Constructing a whole entry rather than assigning fields is what the
29
- * discriminated union buys: a status can never be flipped while the previous
29
+ * The entry is constructed whole rather than assigned field by field, so the
30
+ * discriminated union stops a status being flipped while the previous
30
31
  * status's payload stays behind, which is how
31
32
  * `{ status: 'resolved', error, conflictingFiles }` reaches the session file.
32
33
  *
@@ -67,9 +68,10 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
67
68
  const patchFile = allPatches.find((p) => p.filename === entry.filename);
68
69
  if (!patchFile) {
69
70
  warn(`Patch file not found for ${entry.filename}, skipping.`);
70
- // Replaced, not mutated: the entry is a discriminated union, so a status
71
- // change is a new value rather than a field assignment. That is what
72
- // stops a status flip stranding the previous status's payload.
71
+ // The entry is replaced rather than mutated: it is a discriminated
72
+ // union, so a status change is a new value rather than a field
73
+ // assignment, and a status flip cannot strand the previous status's
74
+ // payload.
73
75
  session.patches[i] = { filename: entry.filename, status: 'skipped' };
74
76
  session.currentIndex = i + 1;
75
77
  await saveRebaseSession(projectRoot, session);
@@ -100,9 +102,9 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
100
102
  }
101
103
  else {
102
104
  // Set pendingResolution in state for visibility. Kept outside the
103
- // critical section — it is advisory UX, not a correctness invariant,
104
- // and its absence would at most cause `fireforge status` to omit the
105
- // pending-conflict hint until the next state write.
105
+ // critical section, since it is advisory UX rather than a correctness
106
+ // invariant, and its absence would at most cause `fireforge status`
107
+ // to omit the pending-conflict hint until the next state write.
106
108
  await updateState(projectRoot, (current) => ({
107
109
  ...current,
108
110
  pendingResolution: {
@@ -144,7 +146,7 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
144
146
  s.stop('All patches applied');
145
147
  // Re-export all successfully applied patches. Failures here mean the
146
148
  // engine has been rebased onto the new Firefox version but some .patch
147
- // files were not refreshed — the queue would lie about what version each
149
+ // files were not refreshed, so the queue would lie about what version each
148
150
  // patch was tested against if we proceeded to stamp. Refuse to claim
149
151
  // success and leave the session in place so the user can recover via
150
152
  // `fireforge rebase --continue` after fixing the underlying cause.
@@ -163,7 +165,7 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
163
165
  `still describe the pre-rebase source. Re-export them manually (e.g. ` +
164
166
  `"fireforge re-export") and verify the overlapping hunks before the next import.`);
165
167
  }
166
- // Stamp versions — only for patches whose .patch file now actually
168
+ // Stamp versions, only for patches whose .patch file now actually
167
169
  // reflects the new source (overlap-skipped ones keep their old stamp so
168
170
  // the queue does not lie about what each patch was tested against).
169
171
  const overlapSkippedSet = new Set(overlapSkipped);
@@ -176,19 +178,19 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
176
178
  }
177
179
  // Stamp every Furnace override's `baseVersion` to match the rebased
178
180
  // Firefox source version. Without it, a successful source bump leaves
179
- // overrides in a doctor-failing drift state — each still claiming the
180
- // pre-rebase source as its baseline — and every subsequent
181
+ // overrides in a doctor-failing drift state, each still claiming the
182
+ // pre-rebase source as its baseline, and every subsequent
181
183
  // `fireforge doctor` fails `Furnace component validation`. The stamp is
182
184
  // unconditional per the helper's contract: rebase already succeeded on
183
185
  // the patch side, so the operator is committing to the new source
184
- // baseline; per-component health checking stays with
186
+ // baseline. Per-component health checking stays with
185
187
  // `fireforge furnace validate` / `doctor --repair-furnace`.
186
188
  //
187
189
  // "Unconditional" means unconditional on component health, not on the
188
190
  // value: `stampFurnaceOverrideBaseVersions` assigns and persists whatever
189
191
  // string it is handed, so a session whose `toVersion` is not a real
190
192
  // Firefox version would rewrite every override's baseline to that value.
191
- // `isValidSession` rejects such a session at read time; this refuses to
193
+ // `isValidSession` rejects such a session at read time. This refuses to
192
194
  // persist it even if a future caller reaches here another way.
193
195
  if (!isValidFirefoxVersion(session.toVersion)) {
194
196
  throw new RebaseError(`Refusing to stamp Furnace override baseVersion(s): the rebase session's target version ` +
@@ -208,13 +210,7 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
208
210
  await clearRebaseSession(projectRoot);
209
211
  // Clear pending resolution if any (transactionally, so a concurrent
210
212
  // state write to an unrelated key is not clobbered by a stale reload).
211
- await updateState(projectRoot, (current) => {
212
- if (!current.pendingResolution)
213
- return current;
214
- const next = { ...current };
215
- delete next.pendingResolution;
216
- return next;
217
- });
213
+ await clearPendingResolution(projectRoot);
218
214
  info('');
219
215
  success(`All patches re-exported with sourceVersion=${session.toVersion}`);
220
216
  outro('Rebase complete!');
@@ -225,13 +221,13 @@ async function reExportAppliedPatches(session, paths, waitLockSeconds) {
225
221
  const manifest = await loadPatchesManifest(paths.patches);
226
222
  if (!manifest)
227
223
  return { failures, overlapSkipped };
228
- // Re-export writes `getDiffForFilesAgainstHead(files)` — the CUMULATIVE
224
+ // Re-export writes `getDiffForFilesAgainstHead(files)`, the cumulative
229
225
  // diff of those files. For a file claimed by two patches (a supported
230
- // `--allow-overlap` queue), that diff contains BOTH patches' hunks, so
226
+ // `--allow-overlap` queue), that diff contains both patches' hunks, so
231
227
  // rewriting each patch with it would duplicate the shared file's hunks
232
228
  // into every owner: the very next import then fails or silently
233
229
  // double-materialises. Refuse to auto-re-export any patch whose files
234
- // overlap another patch; the operator re-exports those manually where
230
+ // overlap another patch. The operator re-exports those manually where
235
231
  // per-patch attribution is possible.
236
232
  const fileOwners = new Map();
237
233
  for (const patch of manifest.patches) {