@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
@@ -1,21 +1,20 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * Doctor check for PRE-EXISTING orphaned test-harness workers.
3
+ * Doctor check for pre-existing orphaned test-harness workers.
4
4
  *
5
5
  * A mach invocation that dies at harness startup can strand a Python
6
- * `multiprocessing` spawn worker plus its resource tracker; the worker
6
+ * `multiprocessing` spawn worker plus its resource tracker. The worker
7
7
  * reparents to launchd (PPID 1) and busy-spins at 100% CPU indefinitely.
8
- * The dispatch-side fix is the process-group reaping in `utils/process.ts`;
9
- * this check covers orphans that predate it (or leaked from non-FireForge
8
+ * The dispatch-side fix is the process-group reaping in `utils/process.ts`.
9
+ * This check covers orphans that predate it (or leaked from non-FireForge
10
10
  * invocations), detecting them by PPID 1 + large accumulated CPU TIME + a
11
11
  * `multiprocessing.spawn`/`resource_tracker` command line.
12
12
  *
13
- * Report-only by design: FireForge never kills pre-existing processes it did
14
- * not spawn — the check names each candidate and suggests the kill.
13
+ * Report-only: FireForge never kills pre-existing processes it did not
14
+ * spawn, so the check names each candidate and suggests the kill.
15
15
  *
16
- * Deliberately not wired into `test --canary`: a system-wide process scan on
17
- * the canary hot path buys no coverage the doctor check does not already
18
- * give.
16
+ * Not wired into `test --canary`: a system-wide process scan on the canary
17
+ * hot path buys no coverage the doctor check does not already give.
19
18
  */
20
19
  import { toError } from '../utils/errors.js';
21
20
  import { exec } from '../utils/process.js';
@@ -29,19 +28,11 @@ import { ok, warning } from './doctor-check-core.js';
29
28
  const HARNESS_WORKER_COMMAND_PATTERN = /multiprocessing\.(?:spawn|forkserver)\b|multiprocessing\.resource_tracker|from multiprocessing\.\w+ import/;
30
29
  /** Minimum accumulated CPU time before a match is reported (10 minutes). */
31
30
  const DEFAULT_MIN_CPU_SECONDS = 600;
32
- /**
33
- * Parses a `ps` TIME (accumulated CPU) value. Implemented in terms of
34
- * {@link parsePsDuration} — `time=` and `etime=` share one grammar, and a
35
- * second copy is how the two would drift.
36
- */
37
- export function parseCpuTime(time) {
38
- return parsePsDuration(time);
39
- }
40
31
  /**
41
32
  * Scans `ps -axo pid=,ppid=,time=,command=` output for orphaned harness
42
33
  * workers: PPID 1 (reparented to init/launchd), a multiprocessing
43
34
  * worker/tracker command line, and at least `minCpuSeconds` of accumulated
44
- * CPU time. Pure — fixture-testable without spawning anything.
35
+ * CPU time. Pure, so it is fixture-testable without spawning anything.
45
36
  */
46
37
  export function findOrphanedHarnessWorkers(psOutput, minCpuSeconds = DEFAULT_MIN_CPU_SECONDS) {
47
38
  const workers = [];
@@ -57,7 +48,7 @@ export function findOrphanedHarnessWorkers(psOutput, minCpuSeconds = DEFAULT_MIN
57
48
  continue;
58
49
  if (!HARNESS_WORKER_COMMAND_PATTERN.test(command))
59
50
  continue;
60
- const cpuSeconds = parseCpuTime(cpuTime);
51
+ const cpuSeconds = parsePsDuration(cpuTime);
61
52
  if (Number.isNaN(cpuSeconds) || cpuSeconds < minCpuSeconds)
62
53
  continue;
63
54
  workers.push({ pid, ppid, cpuTime, cpuSeconds, command });
@@ -68,7 +59,7 @@ export function findOrphanedHarnessWorkers(psOutput, minCpuSeconds = DEFAULT_MIN
68
59
  async function listSystemProcesses() {
69
60
  const result = await exec('ps', ['-axo', 'pid=,ppid=,time=,command='], { timeout: 10000 });
70
61
  if (result.exitCode !== 0) {
71
- throw new Error(`ps exited ${String(result.exitCode)}`);
62
+ throw new Error(`ps exited ${result.exitCode}`);
72
63
  }
73
64
  return result.stdout;
74
65
  }
@@ -86,17 +77,17 @@ async function runOrphanedHarnessCheck() {
86
77
  return ok(CHECK_NAME);
87
78
  }
88
79
  const rows = orphans
89
- .map((w) => `PID ${String(w.pid)} (CPU time ${w.cpuTime}): ${w.command.slice(0, 160)}`)
80
+ .map((w) => `PID ${w.pid} (CPU time ${w.cpuTime}): ${w.command.slice(0, 160)}`)
90
81
  .join('; ');
91
82
  const pids = orphans.map((w) => String(w.pid)).join(' ');
92
- return warning(CHECK_NAME, `Found ${String(orphans.length)} orphaned Python multiprocessing worker(s) — PPID 1 with ` +
83
+ return warning(CHECK_NAME, `Found ${orphans.length} orphaned Python multiprocessing worker(s) — PPID 1 with ` +
93
84
  `high accumulated CPU time, the shape a test harness that died at startup leaves behind ` +
94
85
  `(field incident: one such worker busy-spun for ~26 days). ${rows}`, `These look like workers orphaned by a crashed test harness. Verify each command line, ` +
95
86
  `then terminate with: kill ${pids} (or kill -9 if a process survives). ` +
96
87
  'FireForge never kills pre-existing processes automatically.');
97
88
  }
98
89
  /**
99
- * Doctor check reporting orphaned harness workers. Windows has no `ps`;
90
+ * Doctor check reporting orphaned harness workers. Windows has no `ps`, so
100
91
  * the check is skipped there (best-effort platform gap, matching the
101
92
  * process-group reaper's POSIX-only sweep).
102
93
  */
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The patch-manifest consistency check and its two repair paths, split out of
3
3
  * `doctor.ts` for the line budget. Exports a single `DoctorCheckDefinition`
4
- * consumed by the doctor registry; no registrar is exported and none is wanted.
4
+ * consumed by the doctor registry. No registrar is exported and none is
5
+ * wanted.
5
6
  */
6
7
  import type { DoctorCheckDefinition } from './doctor-check-core.js';
7
8
  /** Patch manifest consistency check, with its opt-in repair paths. */
@@ -2,7 +2,8 @@
2
2
  /**
3
3
  * The patch-manifest consistency check and its two repair paths, split out of
4
4
  * `doctor.ts` for the line budget. Exports a single `DoctorCheckDefinition`
5
- * consumed by the doctor registry; no registrar is exported and none is wanted.
5
+ * consumed by the doctor registry. No registrar is exported and none is
6
+ * wanted.
6
7
  */
7
8
  import { rebuildPatchesManifest, recommendManifestRepair, repairPatchesFilesAffected, validatePatchesManifestConsistency, } from '../core/patch-manifest.js';
8
9
  import { FireForgeError } from '../errors/base.js';
@@ -65,8 +66,8 @@ function describeCount(count) {
65
66
  async function runManifestRebuild(ctx) {
66
67
  // Repair stamps sourceEsrVersion into every recovered entry. If the
67
68
  // earlier "fireforge.json is valid" check failed, ctx.config is
68
- // undefined and we must refuse rather than fabricate a fallback —
69
- // persisting 'unknown' into manifest metadata is hard to reverse
69
+ // undefined and we must refuse rather than fabricate a fallback.
70
+ // Persisting 'unknown' into manifest metadata is hard to reverse
70
71
  // and would mislead every later command that reads it.
71
72
  if (!ctx.config) {
72
73
  return failure(CHECK_NAME, 'Cannot repair patches.json: fireforge.json could not be loaded, so the Firefox version to stamp into recovered manifest entries is unknown.', 'Fix the fireforge.json errors reported above and re-run "fireforge doctor --repair-patches-manifest". ' +
@@ -84,7 +85,7 @@ async function runManifestRebuild(ctx) {
84
85
  // trustworthy as an audit trail. The rebuilder returns the list of
85
86
  // filenames whose metadata was entirely invented, named explicitly
86
87
  // here so the operator knows which patches to review. Entries that
87
- // WERE preserved (only `filesAffected` / ordering drifted) are not
88
+ // were preserved (only `filesAffected` / ordering drifted) are not
88
89
  // flagged.
89
90
  for (const filename of repaired.recoveredFilenames) {
90
91
  // Telling the operator to hand-edit patches.json contradicts the
@@ -101,8 +102,8 @@ async function runManifestRebuild(ctx) {
101
102
  'Avoid hand-editing patches.json — FireForge owns that file and will regenerate it on the next manifest consistency pass.');
102
103
  }
103
104
  // A row whose patch file is gone is dropped by the rebuild. That is
104
- // correct — the patch file is the source of truth — but a manifest that
105
- // quietly loses rows is exactly what makes a repair hard to audit
105
+ // correct, since the patch file is the source of truth, but a manifest
106
+ // that quietly loses rows is what makes a repair hard to audit
106
107
  // afterwards, so each one is named.
107
108
  for (const filename of repaired.droppedFilenames) {
108
109
  warn(`Dropped the manifest entry for ${filename}: no such patch file on disk. ` +
@@ -16,7 +16,7 @@ async function readEngineText(engineDir, relativePath) {
16
16
  * Reports unreadable directories separately from "none found": the consumer
17
17
  * turns an empty result into the issue "no browser.toml files found", which is
18
18
  * the opposite diagnosis from "the tree could not be read". A swallowed
19
- * mid-walk EACCES also silently *shrank* the result set, so one unreadable
19
+ * mid-walk EACCES also silently shrank the result set, so one unreadable
20
20
  * subdirectory produced a false clean for that subtree.
21
21
  */
22
22
  async function collectBrowserTomlFiles(root) {
@@ -54,7 +54,7 @@ async function runPostRebaseAudit(ctx) {
54
54
  // The four probes below are upstream-Firefox shape markers: each names a
55
55
  // file and a token that a healthy post-rebase tree must still contain. A
56
56
  // rebase that dropped a fork patch, or an upstream reorganisation, shows up
57
- // as one of these going missing. The tokens are deliberately coarse — this
57
+ // as one of these going missing. The tokens are coarse on purpose: this
58
58
  // is a smoke check that runs warn-only behind an opt-in flag, not a
59
59
  // structural validator.
60
60
  const mozConfigure = await readEngineText(engineDir, 'browser/moz.configure');
@@ -88,7 +88,7 @@ async function runPostRebaseAudit(ctx) {
88
88
  }
89
89
  const browserTomls = await collectBrowserTomlFiles(engineDir);
90
90
  if (browserTomls.unreadable.length > 0) {
91
- issues.push(`could not read ${String(browserTomls.unreadable.length)} ` +
91
+ issues.push(`could not read ${browserTomls.unreadable.length} ` +
92
92
  `${browserTomls.unreadable.length === 1 ? 'directory' : 'directories'} under ` +
93
93
  `browser/base/content/test: ${browserTomls.unreadable.join(', ')}`);
94
94
  }
@@ -1,29 +1,29 @@
1
1
  /**
2
- * Doctor check comparing the CONFIGURED Firefox source pin against what the
2
+ * Doctor check comparing the configured Firefox source pin against what the
3
3
  * engine checkout actually is.
4
4
  *
5
5
  * `fireforge source set` lands `firefox.version` inside `fireforge.json`,
6
6
  * beside hand-maintained policy sections. That is convenient and it is also
7
- * the whole problem: a routine `git checkout -- fireforge.json` — undoing an
8
- * accidental reformat, say — silently reverts an uncommitted pin along with
7
+ * the whole problem: a routine `git checkout -- fireforge.json` (undoing an
8
+ * accidental reformat, say) silently reverts an uncommitted pin along with
9
9
  * it, and nothing afterwards reports that the pin and the checkout have
10
- * diverged. The tell in the field was a gate flipping green with no change
11
- * that should have made it green.
10
+ * diverged. In the field this showed up as a gate flipping green with no
11
+ * change that should have made it green.
12
12
  *
13
- * This is deliberately not a lock and not a refusal: a tree may legitimately
14
- * be mid-migration between two versions. It only makes a mismatched tree
15
- * VISIBLE, which is all that was missing.
13
+ * This is not a lock and not a refusal: a tree may legitimately be
14
+ * mid-migration between two versions. It only makes a mismatched tree
15
+ * visible, which is all that was missing.
16
16
  *
17
17
  * Scope is limited by what is actually recorded. `firefox.product`,
18
- * `firefox.sha256` and `firefox.candidate` have NO counterpart anywhere on
19
- * disk — `.fireforge/state.json` records only `downloadedVersion` and
20
- * `baseCommit` — so there is nothing to compare them against and the check
18
+ * `firefox.sha256` and `firefox.candidate` have no counterpart anywhere on
19
+ * disk (`.fireforge/state.json` records only `downloadedVersion` and
20
+ * `baseCommit`), so there is nothing to compare them against and the check
21
21
  * says nothing about them rather than implying it verified them.
22
22
  */
23
23
  import type { DoctorCheckDefinition } from './doctor-check-core.js';
24
24
  /**
25
25
  * Pin-vs-checkout reporting. Needs the loaded config, so it declares the
26
- * dependency on the config check that populates `ctx.config`; skipped
26
+ * dependency on the config check that populates `ctx.config`. It is skipped
27
27
  * entirely without an engine, where there is nothing to compare.
28
28
  */
29
29
  export declare const SOURCE_PIN_DOCTOR_CHECK: DoctorCheckDefinition;
@@ -1,24 +1,24 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * Doctor check comparing the CONFIGURED Firefox source pin against what the
3
+ * Doctor check comparing the configured Firefox source pin against what the
4
4
  * engine checkout actually is.
5
5
  *
6
6
  * `fireforge source set` lands `firefox.version` inside `fireforge.json`,
7
7
  * beside hand-maintained policy sections. That is convenient and it is also
8
- * the whole problem: a routine `git checkout -- fireforge.json` — undoing an
9
- * accidental reformat, say — silently reverts an uncommitted pin along with
8
+ * the whole problem: a routine `git checkout -- fireforge.json` (undoing an
9
+ * accidental reformat, say) silently reverts an uncommitted pin along with
10
10
  * it, and nothing afterwards reports that the pin and the checkout have
11
- * diverged. The tell in the field was a gate flipping green with no change
12
- * that should have made it green.
11
+ * diverged. In the field this showed up as a gate flipping green with no
12
+ * change that should have made it green.
13
13
  *
14
- * This is deliberately not a lock and not a refusal: a tree may legitimately
15
- * be mid-migration between two versions. It only makes a mismatched tree
16
- * VISIBLE, which is all that was missing.
14
+ * This is not a lock and not a refusal: a tree may legitimately be
15
+ * mid-migration between two versions. It only makes a mismatched tree
16
+ * visible, which is all that was missing.
17
17
  *
18
18
  * Scope is limited by what is actually recorded. `firefox.product`,
19
- * `firefox.sha256` and `firefox.candidate` have NO counterpart anywhere on
20
- * disk — `.fireforge/state.json` records only `downloadedVersion` and
21
- * `baseCommit` — so there is nothing to compare them against and the check
19
+ * `firefox.sha256` and `firefox.candidate` have no counterpart anywhere on
20
+ * disk (`.fireforge/state.json` records only `downloadedVersion` and
21
+ * `baseCommit`), so there is nothing to compare them against and the check
22
22
  * says nothing about them rather than implying it verified them.
23
23
  */
24
24
  import { getFirefoxVersion } from '../core/firefox.js';
@@ -40,8 +40,8 @@ async function runSourcePinCheck(ctx) {
40
40
  let actual;
41
41
  try {
42
42
  // A blank or whitespace-only version.txt (a truncated write, a partial
43
- // extraction) carries no version to compare — treat it as absent rather
44
- // than reporting the engine as being at version "".
43
+ // extraction) carries no version to compare, so treat it as absent
44
+ // rather than reporting the engine as being at version "".
45
45
  actual = (await getFirefoxVersion(ctx.paths.engine))?.trim() || undefined;
46
46
  }
47
47
  catch (error) {
@@ -70,7 +70,7 @@ async function runSourcePinCheck(ctx) {
70
70
  }
71
71
  /**
72
72
  * Pin-vs-checkout reporting. Needs the loaded config, so it declares the
73
- * dependency on the config check that populates `ctx.config`; skipped
73
+ * dependency on the config check that populates `ctx.config`. It is skipped
74
74
  * entirely without an engine, where there is nothing to compare.
75
75
  */
76
76
  export const SOURCE_PIN_DOCTOR_CHECK = {
@@ -3,18 +3,18 @@
3
3
  *
4
4
  * Partitions engine-tree dirtiness into `branding`, `patch-backed`,
5
5
  * `furnace`, `conflict`, and `unmanaged` buckets, and only warns on the
6
- * last two — everything else is tool-managed state that the operator
6
+ * last two. Everything else is tool-managed state that the operator
7
7
  * did not author directly.
8
8
  *
9
9
  * Split out of `doctor.ts` so that file stays under the per-file LOC
10
- * budget; see the call site in `runEngineGitChecks`.
10
+ * budget. See the call site in `runEngineGitChecks`.
11
11
  */
12
12
  import type { DoctorCheck } from '../types/commands/index.js';
13
13
  import type { DoctorCheckContext } from './doctor-check-core.js';
14
14
  /**
15
15
  * Inspects the engine working tree and returns a single `DoctorCheck`.
16
16
  * Ownership-aware: patch-backed / branding / furnace rows are reported as OK
17
- * with an ownership summary; unmanaged drift warns; cross-patch conflicts
17
+ * with an ownership summary. Unmanaged drift warns. Cross-patch conflicts
18
18
  * warn loudly with a pointer at `fireforge status --ownership` +
19
19
  * `fireforge verify`.
20
20
  *
@@ -4,11 +4,11 @@
4
4
  *
5
5
  * Partitions engine-tree dirtiness into `branding`, `patch-backed`,
6
6
  * `furnace`, `conflict`, and `unmanaged` buckets, and only warns on the
7
- * last two — everything else is tool-managed state that the operator
7
+ * last two. Everything else is tool-managed state that the operator
8
8
  * did not author directly.
9
9
  *
10
10
  * Split out of `doctor.ts` so that file stays under the per-file LOC
11
- * budget; see the call site in `runEngineGitChecks`.
11
+ * budget. See the call site in `runEngineGitChecks`.
12
12
  */
13
13
  import { collectFurnaceManagedPrefixes } from '../core/furnace-config.js';
14
14
  import { expandUntrackedDirectoryEntries, getWorkingTreeStatus } from '../core/git-status.js';
@@ -30,7 +30,7 @@ function formatManagedDetail(counts) {
30
30
  /**
31
31
  * Inspects the engine working tree and returns a single `DoctorCheck`.
32
32
  * Ownership-aware: patch-backed / branding / furnace rows are reported as OK
33
- * with an ownership summary; unmanaged drift warns; cross-patch conflicts
33
+ * with an ownership summary. Unmanaged drift warns. Cross-patch conflicts
34
34
  * warn loudly with a pointer at `fireforge status --ownership` +
35
35
  * `fireforge verify`.
36
36
  *
@@ -10,7 +10,7 @@ import type { DoctorCheckDefinition } from './doctor-check-core.js';
10
10
  * context-population bug at runtime.
11
11
  *
12
12
  * Exported so tests can exercise the forward-only invariant against
13
- * fixtures — the real DOCTOR_CHECKS list is also validated at import
13
+ * fixtures. The real DOCTOR_CHECKS list is also validated at import
14
14
  * time, but a targeted unit test makes the contract explicit and
15
15
  * prevents regressions if the validator is ever relaxed.
16
16
  */
@@ -21,6 +21,8 @@ export declare function validateCheckDependencies(checks: readonly DoctorCheckDe
21
21
  * context-population dependency chain (see {@link DOCTOR_CHECKS}) must
22
22
  * also update this list, which gives us a single place to notice and
23
23
  * think through the consequences.
24
+ *
25
+ * @internal Exported only so tests can reach it. Not part of the public surface.
24
26
  */
25
27
  export declare const DOCTOR_CHECK_ORDER: readonly string[];
26
28
  /**
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { Command } from 'commander';
3
- import { configExists, getProjectPaths, loadConfig, loadState, updateState, } from '../core/config.js';
3
+ import { configExists, getProjectPaths, loadConfig, loadState } from '../core/config.js';
4
4
  import { furnaceConfigExists as checkFurnaceConfigExists } from '../core/furnace-config.js';
5
5
  import { getCurrentBranch, getHead, isGitRepository, isMissingHeadError } from '../core/git.js';
6
6
  import { ensureGit } from '../core/git-base.js';
@@ -14,7 +14,7 @@ import { pathExists } from '../utils/fs.js';
14
14
  import { error, info, intro, outro, success, warn } from '../utils/logger.js';
15
15
  import { addWaitLockOption } from '../utils/options.js';
16
16
  import { findExecutable } from '../utils/process.js';
17
- import { failure, ok, resolveDoctorSeverity, warning } from './doctor-check-core.js';
17
+ import { failure, ok, warning } from './doctor-check-core.js';
18
18
  import { EXTERNAL_TOOLCHAIN_DOCTOR_CHECK } from './doctor-external-toolchains.js';
19
19
  import { FURNACE_DOCTOR_CHECKS } from './doctor-furnace.js';
20
20
  import { ORPHANED_HARNESS_DOCTOR_CHECK } from './doctor-orphaned-harness.js';
@@ -22,6 +22,7 @@ import { PATCH_MANIFEST_CONSISTENCY_CHECK } from './doctor-patch-manifest.js';
22
22
  import { POST_REBASE_AUDIT_CHECK } from './doctor-post-rebase-audit.js';
23
23
  import { SOURCE_PIN_DOCTOR_CHECK } from './doctor-source-pin.js';
24
24
  import { inspectEngineWorkingTree } from './doctor-working-tree.js';
25
+ import { clearPendingResolution } from './pending-resolution.js';
25
26
  import { collectPatchQueueHealth } from './verify.js';
26
27
  /**
27
28
  * Runs a single check definition, converting thrown errors into
@@ -89,8 +90,8 @@ async function runEngineGitChecks(ctx) {
89
90
  branch === undefined &&
90
91
  currentHead === undefined &&
91
92
  !state.baseCommit) {
92
- // Unborn repository with no recorded baseline — the earlier failure row
93
- // explains recovery; avoid adding a second near-identical row.
93
+ // Unborn repository with no recorded baseline. The earlier failure row
94
+ // explains recovery. Avoid adding a second near-identical row.
94
95
  }
95
96
  else if (!canValidateBranch) {
96
97
  rows.push(warning('Engine branch', 'Skipped branch validation because the baseline commit is missing.', 'Finish recreating the engine baseline with "fireforge download --force".'));
@@ -113,7 +114,7 @@ async function runEngineGitChecks(ctx) {
113
114
  * context-population bug at runtime.
114
115
  *
115
116
  * Exported so tests can exercise the forward-only invariant against
116
- * fixtures — the real DOCTOR_CHECKS list is also validated at import
117
+ * fixtures. The real DOCTOR_CHECKS list is also validated at import
117
118
  * time, but a targeted unit test makes the contract explicit and
118
119
  * prevents regressions if the validator is ever relaxed.
119
120
  */
@@ -135,7 +136,7 @@ export function validateCheckDependencies(checks) {
135
136
  /**
136
137
  * The declarative doctor check registry. The order of entries here is the
137
138
  * order checks appear in the report. Adding a new check is a one-entry
138
- * edit; each check only contains its own inspection logic.
139
+ * edit. Each check only contains its own inspection logic.
139
140
  *
140
141
  * ## Ordering dependency chain
141
142
  *
@@ -202,11 +203,7 @@ const DOCTOR_CHECKS = [
202
203
  if (health.errorCount > 0) {
203
204
  return failure('Pending Resolution', `Refusing to clear pending resolution for ${patchFilename}: patch queue health check found ${health.errorCount} error(s).`, 'Run "fireforge verify" for details, fix the queue, then retry "fireforge doctor --clear-resolution".');
204
205
  }
205
- await updateState(ctx.projectRoot, (current) => {
206
- const next = { ...current };
207
- delete next.pendingResolution;
208
- return next;
209
- });
206
+ await clearPendingResolution(ctx.projectRoot);
210
207
  return ok('Pending Resolution');
211
208
  }
212
209
  return failure('Pending Resolution', `You are currently resolving a conflict for patch ${patchFilename}.`, 'Build and Export commands may behave unexpectedly until "fireforge resolve" is completed. If the queue now verifies cleanly, run "fireforge doctor --clear-resolution" to discard the stale marker.');
@@ -217,7 +214,7 @@ const DOCTOR_CHECKS = [
217
214
  name: 'Engine is git repository',
218
215
  skipIf: (ctx) => !ctx.engineExists,
219
216
  // runEngineGitChecks consults ctx.config for ownership-aware
220
- // working-tree classification; declare the dependency so a future
217
+ // working-tree classification. Declare the dependency so a future
221
218
  // reorder doesn't silently regress the doctor back to the
222
219
  // count-only fallback.
223
220
  dependsOn: ['fireforge.json is valid'],
@@ -251,7 +248,7 @@ const DOCTOR_CHECKS = [
251
248
  // Resolve the absolute path so the OK row names what doctor actually
252
249
  // found. A PATH-export discrepancy between the operator's interactive
253
250
  // shell and the spawned subprocess otherwise reads as doctor printing
254
- // "OK" for a binary `which watchman` cannot find; surfacing the
251
+ // "OK" for a binary `which watchman` cannot find. Surfacing the
255
252
  // resolved path makes the discrepancy visible without a verbose flag.
256
253
  const path = await findExecutable('watchman');
257
254
  if (path) {
@@ -298,7 +295,7 @@ const DOCTOR_CHECKS = [
298
295
  POST_REBASE_AUDIT_CHECK,
299
296
  // Furnace checks live in a sibling module so this file stays under the
300
297
  // max-lines threshold. Splicing them in as an array preserves the
301
- // declarative registry contract — each entry remains a single
298
+ // declarative registry contract: each entry remains a single
302
299
  // `DoctorCheckDefinition` with its own skipIf/run/fix, and the order
303
300
  // here is the order they appear in the report.
304
301
  ...FURNACE_DOCTOR_CHECKS,
@@ -322,6 +319,8 @@ validateCheckDependencies(DOCTOR_CHECKS);
322
319
  * context-population dependency chain (see {@link DOCTOR_CHECKS}) must
323
320
  * also update this list, which gives us a single place to notice and
324
321
  * think through the consequences.
322
+ *
323
+ * @internal Exported only so tests can reach it. Not part of the public surface.
325
324
  */
326
325
  export const DOCTOR_CHECK_ORDER = DOCTOR_CHECKS.map((check) => check.name);
327
326
  /**
@@ -336,7 +335,7 @@ export function reportDoctorResults(checks, mutations = []) {
336
335
  let warningCount = 0;
337
336
  let failedCount = 0;
338
337
  for (const check of checks) {
339
- const severity = resolveDoctorSeverity(check);
338
+ const severity = check.severity;
340
339
  if (severity === 'warning') {
341
340
  warn(`! ${check.name}: ${check.message}`);
342
341
  if (check.fix) {
@@ -383,7 +382,7 @@ export function reportDoctorResults(checks, mutations = []) {
383
382
  * Rejects repair-flag combinations that cannot mean what they say.
384
383
  *
385
384
  * The two manifest repairs are different operations on the same file, and
386
- * `--dry-run` with nothing to project is a flag that silently does nothing —
385
+ * `--dry-run` with nothing to project is a flag that silently does nothing,
387
386
  * the shape most likely to be mistaken for a preview that reported "no
388
387
  * changes".
389
388
  */
@@ -78,7 +78,7 @@ async function cleanPatchTouchedFiles(engineDir, patchesDir, preExistingDirty) {
78
78
  * Prints a one-line nudge pointing at `fireforge import` when the project
79
79
  * carries a non-empty patch queue but the just-downloaded engine has not yet
80
80
  * had any patches applied. The post-download spinner closes with
81
- * "Patch-touched files already match baseline" because a fresh tree IS at
81
+ * "Patch-touched files already match baseline" because a fresh tree is at
82
82
  * baseline, which reads as "patches are restored" and invites skipping the
83
83
  * import step. Suppressed when patches/ is missing or the manifest is empty
84
84
  * so unconfigured projects stay quiet.
@@ -94,10 +94,11 @@ async function noteUnappliedPatches(patchesDir) {
94
94
  }
95
95
  /**
96
96
  * Stops `restoreSpinner` with a message that reflects what actually
97
- * happened. Three branches: empty queue → explicit no-op; queue present but
98
- * nothing dirty → "already clean"; queue with dirty files → the usual
99
- * "Patch-touched files restored" success line. Always closing with the third
100
- * claims restore work that did not happen on a project with zero patches.
97
+ * happened. Three branches. An empty queue gives an explicit no-op. A queue
98
+ * that is present but has nothing dirty gives "already clean". A queue with
99
+ * dirty files gives the usual "Patch-touched files restored" success line.
100
+ * Always closing with the third claims restore work that did not happen on a
101
+ * project with zero patches.
101
102
  */
102
103
  function closeRestoreSpinner(restoreSpinner, result) {
103
104
  if (!result.hadQueue) {
@@ -110,11 +111,6 @@ function closeRestoreSpinner(restoreSpinner, result) {
110
111
  }
111
112
  restoreSpinner.stop('Patch-touched files restored');
112
113
  }
113
- async function clearStaleFurnaceApplyState(projectRoot) {
114
- // --force installs a new baseCommit, which invalidates every applied
115
- // checksum in furnace-state.json.
116
- await clearAppliedFurnaceState(projectRoot);
117
- }
118
114
  async function activateReplacementEngine(args) {
119
115
  const { engineDir, replacementDir, backupDir } = args;
120
116
  await rename(engineDir, backupDir);
@@ -155,28 +151,39 @@ async function restorePreviousEngine(args) {
155
151
  }
156
152
  }
157
153
  async function downloadAndExtractFirefox(args) {
158
- const { version, product, engineDir, cacheDir, sha256, candidate } = args;
154
+ const { version, product, engineDir, cacheDir, sha256, candidate, allowUnverifiedDownload } = args;
159
155
  let s = spinner(`Downloading Firefox ${version}...`);
160
156
  let lastPercent = 0;
161
157
  const phaseState = { value: 'download' };
162
158
  try {
163
- await downloadFirefoxSource(version, product, engineDir, cacheDir, (downloaded, total) => {
164
- if (total <= 0)
165
- return;
166
- const percent = Math.floor((downloaded / total) * 100);
167
- if (percent !== lastPercent && percent % 5 === 0) {
168
- s.message(`Downloading Firefox ${version}... ${percent}% (${formatBytes(downloaded)} / ${formatBytes(total)})`);
169
- lastPercent = percent;
170
- }
171
- }, (phase) => {
172
- if (phase === 'extract' && phaseState.value === 'download') {
173
- s.stop(`Firefox ${version} downloaded`);
174
- phaseState.value = 'extract';
175
- s = spinner(`Extracting Firefox ${version}... (decompressing ~600 MB of source; typically 30–90s)`);
176
- }
177
- }, sha256, (message) => {
178
- s.message(message);
179
- }, candidate);
159
+ await downloadFirefoxSource({
160
+ version: version,
161
+ product: product,
162
+ destDir: engineDir,
163
+ cacheDir: cacheDir,
164
+ onProgress: (downloaded, total) => {
165
+ if (total <= 0)
166
+ return;
167
+ const percent = Math.floor((downloaded / total) * 100);
168
+ if (percent !== lastPercent && percent % 5 === 0) {
169
+ s.message(`Downloading Firefox ${version}... ${percent}% (${formatBytes(downloaded)} / ${formatBytes(total)})`);
170
+ lastPercent = percent;
171
+ }
172
+ },
173
+ onPhase: (phase) => {
174
+ if (phase === 'extract' && phaseState.value === 'download') {
175
+ s.stop(`Firefox ${version} downloaded`);
176
+ phaseState.value = 'extract';
177
+ s = spinner(`Extracting Firefox ${version}... (decompressing ~600 MB of source; typically 30–90s)`);
178
+ }
179
+ },
180
+ expectedSha256: sha256,
181
+ onPhaseProgress: (message) => {
182
+ s.message(message);
183
+ },
184
+ candidate: candidate,
185
+ integrity: allowUnverifiedDownload === undefined ? undefined : { allowUnverifiedDownload },
186
+ });
180
187
  s.stop(phaseState.value === 'extract'
181
188
  ? `Firefox ${version} extracted`
182
189
  : `Firefox ${version} downloaded`);
@@ -188,7 +195,7 @@ async function downloadAndExtractFirefox(args) {
188
195
  }
189
196
  /**
190
197
  * Prints the major-version-hop toolchain nudge when this download moved the
191
- * engine across a Firefox MAJOR version — the first post-hop build otherwise
198
+ * engine across a Firefox major version. The first post-hop build otherwise
192
199
  * dies in `mach configure` on a moved cbindgen minimum with nothing in the
193
200
  * download output suggesting `fireforge bootstrap`. Quiet on first downloads
194
201
  * and same-major re-downloads.
@@ -235,7 +242,9 @@ async function initializeDownloadedEngine(args) {
235
242
  throw error;
236
243
  }
237
244
  if (replacementActivated) {
238
- await clearStaleFurnaceApplyState(projectRoot);
245
+ // --force installs a new baseCommit, which invalidates every applied
246
+ // checksum in furnace-state.json.
247
+ await clearAppliedFurnaceState(projectRoot);
239
248
  }
240
249
  await updateState(projectRoot, {
241
250
  downloadedVersion: version,
@@ -268,7 +277,7 @@ export async function downloadCommand(projectRoot, options) {
268
277
  intro('FireForge Download');
269
278
  const config = await loadConfig(projectRoot), version = config.firefox.version;
270
279
  const paths = getProjectPaths(projectRoot);
271
- // Captured BEFORE any state update so the post-download major-hop
280
+ // Captured before any state update so the post-download major-hop
272
281
  // notice compares against what was actually on disk until now.
273
282
  const previousVersion = (await loadState(projectRoot)).downloadedVersion;
274
283
  info(`Firefox version: ${version}`);
@@ -276,7 +285,7 @@ export async function downloadCommand(projectRoot, options) {
276
285
  // A legitimate holder of this lock runs for 10+ minutes (download +
277
286
  // extract + git indexing). A 30 s timeout fails the second invocation with
278
287
  // "remove the lock directory if it is stale" advice while the lock is
279
- // actively held — dangerous guidance mid-extraction. Wait generously
288
+ // actively held, which is dangerous guidance mid-extraction. Wait generously
280
289
  // instead: a dead holder is reaped within seconds by the PID-based stale
281
290
  // probe, so a long timeout only ever waits on real work.
282
291
  const downloadLockOptions = {
@@ -289,7 +298,7 @@ export async function downloadCommand(projectRoot, options) {
289
298
  // Reclaim multi-GB partial trees left behind by interrupted runs.
290
299
  // Safe under the download lock: any `.tmp-*`/`.replacement-*` dir
291
300
  // present now is orphaned, since live ones only exist while a
292
- // download holds this lock. `.backup-*` dirs are NOT swept — a
301
+ // download holds this lock. `.backup-*` dirs are not swept: a
293
302
  // backup can hold the previous engine after a failed forced
294
303
  // replacement and is the operator's recovery copy.
295
304
  const sweptDirs = await sweepOrphanedEngineWorkDirs(paths.engine);
@@ -309,7 +318,8 @@ export async function downloadCommand(projectRoot, options) {
309
318
  }
310
319
  catch (error) {
311
320
  if (isMissingHeadError(error)) {
312
- // Partial init detected — attempt to resume instead of requiring --force
321
+ // Partial init detected. Attempt to resume instead of
322
+ // requiring --force.
313
323
  info('Detected partially initialized engine. Attempting to resume...');
314
324
  // Snapshot patch-touched files that are already dirty so we
315
325
  // can preserve them after the resume commit.
@@ -332,7 +342,7 @@ export async function downloadCommand(projectRoot, options) {
332
342
  });
333
343
  const baseCommit = await getHead(paths.engine);
334
344
  resumeSpinner.stop('Git repository resumed successfully');
335
- // Restore patch-touched files BEFORE stamping state. If this
345
+ // Restore patch-touched files before stamping state. If this
336
346
  // step fails (disk full, permission denied, git object issue),
337
347
  // state.json keeps the previous downloadedVersion so the
338
348
  // invariant "state.downloadedVersion matches a clean engine"
@@ -351,7 +361,7 @@ export async function downloadCommand(projectRoot, options) {
351
361
  }
352
362
  catch (error) {
353
363
  resumeSpinner.error('Resume failed');
354
- // Preserve the underlying cause so the operator sees *why*
364
+ // Preserve the underlying cause so the operator sees why
355
365
  // the resume failed (timeout, permission denied, corrupted
356
366
  // object, disk full) instead of only the generic "partial
357
367
  // engine exists" story. The cause reaches them through the
@@ -364,7 +374,7 @@ export async function downloadCommand(projectRoot, options) {
364
374
  // Re-throw unexpected git errors (corrupted objects, permission
365
375
  // denied, …) wrapped in PartialEngineExistsError so the user sees
366
376
  // both narratives: "we detected a partial engine and attempted
367
- // resume" AND the underlying git failure. Without the wrap the
377
+ // resume" and the underlying git failure. Without the wrap the
368
378
  // raw git error loses the context that resume was in flight.
369
379
  throw new PartialEngineExistsError(paths.engine, toError(error));
370
380
  }
@@ -389,6 +399,9 @@ export async function downloadCommand(projectRoot, options) {
389
399
  ...(config.firefox.candidate !== undefined
390
400
  ? { candidate: config.firefox.candidate }
391
401
  : {}),
402
+ ...(config.firefox.allowUnverifiedDownload !== undefined
403
+ ? { allowUnverifiedDownload: config.firefox.allowUnverifiedDownload }
404
+ : {}),
392
405
  });
393
406
  if (replacementEngineDir && backupEngineDir) {
394
407
  warn('Activating replacement engine directory...');