@hominis/fireforge 0.43.1 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (470) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +10 -1
  3. package/dist/bin/fireforge.d.ts +4 -9
  4. package/dist/bin/fireforge.js +42 -40
  5. package/dist/build-info.json +4 -4
  6. package/dist/src/cli.js +74 -8
  7. package/dist/src/commands/bootstrap.js +17 -20
  8. package/dist/src/commands/build.js +25 -32
  9. package/dist/src/commands/config.js +34 -41
  10. package/dist/src/commands/discard.js +8 -9
  11. package/dist/src/commands/doctor-check-core.d.ts +4 -18
  12. package/dist/src/commands/doctor-check-core.js +8 -35
  13. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  14. package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
  15. package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
  16. package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
  17. package/dist/src/commands/doctor-furnace-jar.js +7 -9
  18. package/dist/src/commands/doctor-furnace.js +21 -24
  19. package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
  20. package/dist/src/commands/doctor-orphaned-harness.js +18 -44
  21. package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
  22. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  23. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  24. package/dist/src/commands/doctor-working-tree.js +11 -18
  25. package/dist/src/commands/doctor.js +35 -53
  26. package/dist/src/commands/download.js +32 -48
  27. package/dist/src/commands/export-all.js +29 -33
  28. package/dist/src/commands/export-flow.js +6 -6
  29. package/dist/src/commands/export-placement-conflicts.js +10 -9
  30. package/dist/src/commands/export-placement-gate.js +5 -5
  31. package/dist/src/commands/export-shared.d.ts +30 -33
  32. package/dist/src/commands/export-shared.js +45 -43
  33. package/dist/src/commands/export.js +21 -27
  34. package/dist/src/commands/furnace/apply.js +14 -23
  35. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  36. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  37. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  38. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  39. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  40. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  41. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  42. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  43. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  44. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  45. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  46. package/dist/src/commands/furnace/create-readback.js +12 -13
  47. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  48. package/dist/src/commands/furnace/create-templates.js +35 -30
  49. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  50. package/dist/src/commands/furnace/create.js +33 -23
  51. package/dist/src/commands/furnace/deploy.js +16 -24
  52. package/dist/src/commands/furnace/diff.js +9 -9
  53. package/dist/src/commands/furnace/init.js +33 -36
  54. package/dist/src/commands/furnace/override.js +23 -25
  55. package/dist/src/commands/furnace/preview.js +47 -67
  56. package/dist/src/commands/furnace/refresh.js +12 -16
  57. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  58. package/dist/src/commands/furnace/remove-mutations.js +9 -16
  59. package/dist/src/commands/furnace/remove-state.js +1 -1
  60. package/dist/src/commands/furnace/remove.js +33 -38
  61. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  62. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  63. package/dist/src/commands/furnace/rename.js +59 -69
  64. package/dist/src/commands/furnace/scan.js +19 -33
  65. package/dist/src/commands/furnace/status.js +12 -9
  66. package/dist/src/commands/furnace/sync.js +9 -16
  67. package/dist/src/commands/furnace/validate.js +20 -20
  68. package/dist/src/commands/import.js +42 -35
  69. package/dist/src/commands/lint-per-patch.js +15 -10
  70. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  71. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  72. package/dist/src/commands/lint-report.js +6 -7
  73. package/dist/src/commands/lint.d.ts +15 -16
  74. package/dist/src/commands/lint.js +86 -102
  75. package/dist/src/commands/package.js +6 -11
  76. package/dist/src/commands/patch/compact.js +1 -1
  77. package/dist/src/commands/patch/delete.js +24 -33
  78. package/dist/src/commands/patch/index.d.ts +5 -6
  79. package/dist/src/commands/patch/index.js +10 -12
  80. package/dist/src/commands/patch/lint-ignore.js +14 -13
  81. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  82. package/dist/src/commands/patch/move-files-create.js +8 -8
  83. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  84. package/dist/src/commands/patch/move-files-into.js +8 -8
  85. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  86. package/dist/src/commands/patch/patch-context.js +2 -3
  87. package/dist/src/commands/patch/rename.d.ts +10 -11
  88. package/dist/src/commands/patch/rename.js +11 -12
  89. package/dist/src/commands/patch/reorder.js +8 -9
  90. package/dist/src/commands/patch/split-plan.js +1 -1
  91. package/dist/src/commands/patch/split.d.ts +8 -10
  92. package/dist/src/commands/patch/split.js +9 -11
  93. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  94. package/dist/src/commands/patch/staged-dependency.js +7 -3
  95. package/dist/src/commands/patch/tier.d.ts +9 -9
  96. package/dist/src/commands/patch/tier.js +18 -15
  97. package/dist/src/commands/re-export-adjacent.js +9 -8
  98. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  99. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  100. package/dist/src/commands/re-export-drift.js +10 -10
  101. package/dist/src/commands/re-export-files.d.ts +6 -7
  102. package/dist/src/commands/re-export-files.js +34 -44
  103. package/dist/src/commands/re-export-lint.d.ts +10 -13
  104. package/dist/src/commands/re-export-lint.js +16 -14
  105. package/dist/src/commands/re-export-options.d.ts +6 -6
  106. package/dist/src/commands/re-export-options.js +6 -6
  107. package/dist/src/commands/re-export-register.d.ts +4 -10
  108. package/dist/src/commands/re-export-register.js +5 -11
  109. package/dist/src/commands/re-export-scan.js +1 -1
  110. package/dist/src/commands/re-export-single.d.ts +3 -3
  111. package/dist/src/commands/re-export-single.js +33 -25
  112. package/dist/src/commands/re-export.js +13 -21
  113. package/dist/src/commands/rebase/abort.js +3 -3
  114. package/dist/src/commands/rebase/confirm.js +2 -1
  115. package/dist/src/commands/rebase/continue.d.ts +1 -1
  116. package/dist/src/commands/rebase/continue.js +14 -9
  117. package/dist/src/commands/rebase/index.js +11 -11
  118. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  119. package/dist/src/commands/rebase/patch-loop.js +60 -39
  120. package/dist/src/commands/rebase/summary.js +1 -1
  121. package/dist/src/commands/register.js +5 -7
  122. package/dist/src/commands/reset.js +2 -1
  123. package/dist/src/commands/resolve.d.ts +16 -13
  124. package/dist/src/commands/resolve.js +30 -33
  125. package/dist/src/commands/run.js +33 -35
  126. package/dist/src/commands/setup-support.js +15 -19
  127. package/dist/src/commands/setup.js +13 -9
  128. package/dist/src/commands/status-check.d.ts +6 -6
  129. package/dist/src/commands/status-json.d.ts +6 -6
  130. package/dist/src/commands/status-json.js +5 -0
  131. package/dist/src/commands/status-output.d.ts +5 -5
  132. package/dist/src/commands/status-output.js +6 -6
  133. package/dist/src/commands/status-ownership.d.ts +16 -14
  134. package/dist/src/commands/status-ownership.js +16 -14
  135. package/dist/src/commands/status.js +42 -34
  136. package/dist/src/commands/test-build-phase.js +26 -13
  137. package/dist/src/commands/test-diagnose.js +44 -44
  138. package/dist/src/commands/test-register.js +1 -0
  139. package/dist/src/commands/test-run.d.ts +14 -16
  140. package/dist/src/commands/test-run.js +21 -13
  141. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  142. package/dist/src/commands/test-stale-gate.js +15 -16
  143. package/dist/src/commands/test-verdict.d.ts +20 -3
  144. package/dist/src/commands/test-verdict.js +30 -0
  145. package/dist/src/commands/test.js +83 -76
  146. package/dist/src/commands/token-coverage.js +3 -4
  147. package/dist/src/commands/token.js +16 -18
  148. package/dist/src/commands/tree.js +47 -31
  149. package/dist/src/commands/typecheck.d.ts +10 -12
  150. package/dist/src/commands/typecheck.js +24 -27
  151. package/dist/src/commands/verify.js +13 -15
  152. package/dist/src/commands/watch.js +21 -23
  153. package/dist/src/commands/wire.js +52 -62
  154. package/dist/src/core/ast-utils.d.ts +8 -7
  155. package/dist/src/core/ast-utils.js +8 -7
  156. package/dist/src/core/branding.d.ts +9 -14
  157. package/dist/src/core/branding.js +7 -11
  158. package/dist/src/core/browser-wire.d.ts +1 -1
  159. package/dist/src/core/browser-wire.js +14 -19
  160. package/dist/src/core/build-audit-platform.js +25 -33
  161. package/dist/src/core/build-audit-registration.js +17 -25
  162. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  163. package/dist/src/core/build-audit-resolve.js +27 -33
  164. package/dist/src/core/build-audit-transforms.js +18 -19
  165. package/dist/src/core/build-audit.js +28 -42
  166. package/dist/src/core/build-baseline-types.d.ts +29 -35
  167. package/dist/src/core/build-baseline.d.ts +5 -4
  168. package/dist/src/core/build-baseline.js +14 -14
  169. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  170. package/dist/src/core/build-overwrite-guard.js +17 -27
  171. package/dist/src/core/build-prepare.d.ts +2 -2
  172. package/dist/src/core/build-prepare.js +8 -9
  173. package/dist/src/core/config-state.js +12 -4
  174. package/dist/src/core/config-validate.js +8 -5
  175. package/dist/src/core/config.d.ts +5 -8
  176. package/dist/src/core/config.js +7 -16
  177. package/dist/src/core/coverage-extend.d.ts +15 -13
  178. package/dist/src/core/coverage-extend.js +15 -13
  179. package/dist/src/core/destructive.d.ts +31 -9
  180. package/dist/src/core/destructive.js +29 -26
  181. package/dist/src/core/diff-hunks.js +10 -12
  182. package/dist/src/core/discard-baseline.d.ts +8 -8
  183. package/dist/src/core/discard-baseline.js +16 -15
  184. package/dist/src/core/display-state.d.ts +12 -14
  185. package/dist/src/core/display-state.js +12 -14
  186. package/dist/src/core/engine-changes.d.ts +2 -4
  187. package/dist/src/core/engine-changes.js +2 -4
  188. package/dist/src/core/engine-precondition.d.ts +28 -13
  189. package/dist/src/core/engine-precondition.js +25 -10
  190. package/dist/src/core/engine-session-lock.d.ts +47 -21
  191. package/dist/src/core/engine-session-lock.js +168 -33
  192. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  193. package/dist/src/core/export-moved-code-shape.js +10 -10
  194. package/dist/src/core/file-lock.d.ts +62 -5
  195. package/dist/src/core/file-lock.js +196 -59
  196. package/dist/src/core/firefox-cache.js +1 -3
  197. package/dist/src/core/firefox-extract.js +10 -14
  198. package/dist/src/core/firefox.d.ts +7 -5
  199. package/dist/src/core/firefox.js +4 -5
  200. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  201. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  202. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  203. package/dist/src/core/furnace-apply-ftl.js +12 -12
  204. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  205. package/dist/src/core/furnace-apply-helpers.js +19 -16
  206. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  207. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  208. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  209. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  210. package/dist/src/core/furnace-apply.d.ts +5 -8
  211. package/dist/src/core/furnace-apply.js +19 -24
  212. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  213. package/dist/src/core/furnace-checksum-utils.js +16 -0
  214. package/dist/src/core/furnace-config.d.ts +21 -30
  215. package/dist/src/core/furnace-config.js +32 -54
  216. package/dist/src/core/furnace-constants.d.ts +8 -0
  217. package/dist/src/core/furnace-constants.js +10 -2
  218. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  219. package/dist/src/core/furnace-css-fragments.js +5 -5
  220. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  221. package/dist/src/core/furnace-jsconfig.js +11 -12
  222. package/dist/src/core/furnace-marker.d.ts +7 -8
  223. package/dist/src/core/furnace-marker.js +7 -8
  224. package/dist/src/core/furnace-operation.d.ts +31 -0
  225. package/dist/src/core/furnace-operation.js +54 -17
  226. package/dist/src/core/furnace-precondition.d.ts +61 -0
  227. package/dist/src/core/furnace-precondition.js +67 -0
  228. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  229. package/dist/src/core/furnace-registration-remove.js +6 -7
  230. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  231. package/dist/src/core/furnace-registration-validate.js +20 -1
  232. package/dist/src/core/furnace-registration.d.ts +5 -6
  233. package/dist/src/core/furnace-registration.js +10 -12
  234. package/dist/src/core/furnace-rollback.js +7 -7
  235. package/dist/src/core/furnace-scanner.d.ts +1 -0
  236. package/dist/src/core/furnace-scanner.js +1 -2
  237. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  238. package/dist/src/core/furnace-stale-export.js +10 -10
  239. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  240. package/dist/src/core/furnace-state-persist.js +6 -8
  241. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  242. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  243. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  244. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  245. package/dist/src/core/furnace-validate-helpers.js +0 -491
  246. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  247. package/dist/src/core/furnace-validate-registration.js +115 -41
  248. package/dist/src/core/furnace-validate.d.ts +16 -0
  249. package/dist/src/core/furnace-validate.js +188 -145
  250. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  251. package/dist/src/core/furnace-version-drift.js +10 -11
  252. package/dist/src/core/git-base.d.ts +7 -9
  253. package/dist/src/core/git-base.js +7 -9
  254. package/dist/src/core/git-diff.d.ts +20 -9
  255. package/dist/src/core/git-diff.js +148 -63
  256. package/dist/src/core/git-file-ops.d.ts +19 -19
  257. package/dist/src/core/git-file-ops.js +19 -19
  258. package/dist/src/core/git-readonly-index.d.ts +53 -19
  259. package/dist/src/core/git-readonly-index.js +75 -31
  260. package/dist/src/core/git-status.d.ts +5 -6
  261. package/dist/src/core/git-status.js +5 -6
  262. package/dist/src/core/git.d.ts +9 -10
  263. package/dist/src/core/git.js +54 -70
  264. package/dist/src/core/license-headers.d.ts +17 -23
  265. package/dist/src/core/license-headers.js +17 -23
  266. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  267. package/dist/src/core/mach-build-artifacts.js +57 -19
  268. package/dist/src/core/mach-error-hints.js +42 -29
  269. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  270. package/dist/src/core/mach-known-noise-filter.js +18 -20
  271. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  272. package/dist/src/core/mach-mozconfig.js +30 -0
  273. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  274. package/dist/src/core/mach-resource-shim.js +50 -67
  275. package/dist/src/core/mach.d.ts +37 -41
  276. package/dist/src/core/mach.js +50 -51
  277. package/dist/src/core/marionette-port.d.ts +36 -1
  278. package/dist/src/core/marionette-port.js +96 -31
  279. package/dist/src/core/marionette-preflight.d.ts +8 -12
  280. package/dist/src/core/marionette-preflight.js +86 -52
  281. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  282. package/dist/src/core/mochitest-server-port.js +119 -0
  283. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  284. package/dist/src/core/module-resolution-preflight.js +9 -11
  285. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  286. package/dist/src/core/moz-manifest-helpers.js +163 -0
  287. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  288. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  289. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  290. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  291. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  292. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  293. package/dist/src/core/ownership-table.d.ts +14 -16
  294. package/dist/src/core/ownership-table.js +14 -16
  295. package/dist/src/core/parser-fallback.d.ts +15 -1
  296. package/dist/src/core/parser-fallback.js +22 -3
  297. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  298. package/dist/src/core/patch-apply-fuzz.js +15 -18
  299. package/dist/src/core/patch-apply.d.ts +13 -34
  300. package/dist/src/core/patch-apply.js +16 -76
  301. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  302. package/dist/src/core/patch-artifact-normalize.js +13 -15
  303. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  304. package/dist/src/core/patch-export-metadata.js +13 -4
  305. package/dist/src/core/patch-export-update.d.ts +13 -1
  306. package/dist/src/core/patch-export-update.js +25 -8
  307. package/dist/src/core/patch-export.d.ts +15 -8
  308. package/dist/src/core/patch-export.js +10 -11
  309. package/dist/src/core/patch-files.d.ts +5 -6
  310. package/dist/src/core/patch-files.js +5 -6
  311. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  312. package/dist/src/core/patch-identifier-suggest.js +14 -19
  313. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  314. package/dist/src/core/patch-lint-binary.js +39 -0
  315. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  316. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  317. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  318. package/dist/src/core/patch-lint-checkjs.js +16 -16
  319. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  320. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  321. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  322. package/dist/src/core/patch-lint-creators.js +8 -8
  323. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  324. package/dist/src/core/patch-lint-cross.js +16 -16
  325. package/dist/src/core/patch-lint-css.d.ts +3 -5
  326. package/dist/src/core/patch-lint-css.js +17 -22
  327. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  328. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  329. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  330. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  331. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  332. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  333. package/dist/src/core/patch-lint-observer.js +7 -7
  334. package/dist/src/core/patch-lint.d.ts +38 -29
  335. package/dist/src/core/patch-lint.js +89 -121
  336. package/dist/src/core/patch-lock.d.ts +1 -1
  337. package/dist/src/core/patch-lock.js +45 -9
  338. package/dist/src/core/patch-manifest-consistency.d.ts +2 -1
  339. package/dist/src/core/patch-manifest-consistency.js +25 -27
  340. package/dist/src/core/patch-manifest-io.js +14 -18
  341. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  342. package/dist/src/core/patch-manifest-resolve.js +9 -12
  343. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  344. package/dist/src/core/patch-manifest-validate.js +25 -13
  345. package/dist/src/core/patch-parse.d.ts +24 -13
  346. package/dist/src/core/patch-parse.js +19 -14
  347. package/dist/src/core/patch-policy.d.ts +2 -2
  348. package/dist/src/core/patch-policy.js +2 -2
  349. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  350. package/dist/src/core/patch-registration-refs.js +9 -9
  351. package/dist/src/core/process-cpu-time.d.ts +12 -0
  352. package/dist/src/core/process-cpu-time.js +52 -0
  353. package/dist/src/core/rebase-session.d.ts +38 -26
  354. package/dist/src/core/rebase-session.js +55 -28
  355. package/dist/src/core/register-browser-content.js +9 -74
  356. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  357. package/dist/src/core/register-module.js +10 -43
  358. package/dist/src/core/register-scaffold.d.ts +8 -8
  359. package/dist/src/core/register-scaffold.js +10 -10
  360. package/dist/src/core/register-shared-css.d.ts +9 -12
  361. package/dist/src/core/register-shared-css.js +17 -87
  362. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  363. package/dist/src/core/register-xpcshell-test.js +7 -8
  364. package/dist/src/core/shared-ftl.js +12 -4
  365. package/dist/src/core/state-file.d.ts +45 -0
  366. package/dist/src/core/state-file.js +34 -0
  367. package/dist/src/core/status-binary.d.ts +18 -11
  368. package/dist/src/core/status-binary.js +19 -12
  369. package/dist/src/core/status-classify.d.ts +30 -36
  370. package/dist/src/core/status-classify.js +21 -26
  371. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  372. package/dist/src/core/test-changed-prefs.js +78 -0
  373. package/dist/src/core/test-harness-crash.d.ts +25 -63
  374. package/dist/src/core/test-harness-crash.js +121 -142
  375. package/dist/src/core/test-harness-signature.d.ts +18 -0
  376. package/dist/src/core/test-harness-signature.js +13 -0
  377. package/dist/src/core/test-path-scope.d.ts +12 -15
  378. package/dist/src/core/test-path-scope.js +15 -18
  379. package/dist/src/core/test-stale-check.d.ts +17 -17
  380. package/dist/src/core/test-stale-check.js +50 -43
  381. package/dist/src/core/test-stall-triage.d.ts +51 -0
  382. package/dist/src/core/test-stall-triage.js +126 -0
  383. package/dist/src/core/token-category.js +5 -7
  384. package/dist/src/core/token-coverage.js +9 -9
  385. package/dist/src/core/token-dark-mode.d.ts +9 -11
  386. package/dist/src/core/token-dark-mode.js +9 -11
  387. package/dist/src/core/token-docs.d.ts +2 -0
  388. package/dist/src/core/token-docs.js +2 -0
  389. package/dist/src/core/token-manager.d.ts +10 -2
  390. package/dist/src/core/token-manager.js +17 -8
  391. package/dist/src/core/token-scaffold.d.ts +7 -7
  392. package/dist/src/core/token-scaffold.js +7 -7
  393. package/dist/src/core/token-variant.d.ts +4 -4
  394. package/dist/src/core/token-variant.js +4 -4
  395. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  396. package/dist/src/core/toolchain-preflight.js +28 -31
  397. package/dist/src/core/tree-guard.js +12 -2
  398. package/dist/src/core/tree-store.d.ts +41 -27
  399. package/dist/src/core/tree-store.js +63 -42
  400. package/dist/src/core/typecheck-shim.d.ts +23 -24
  401. package/dist/src/core/typecheck-shim.js +23 -24
  402. package/dist/src/core/typecheck.js +8 -8
  403. package/dist/src/core/wire-destroy.d.ts +3 -3
  404. package/dist/src/core/wire-destroy.js +7 -15
  405. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  406. package/dist/src/core/wire-dom-fragment.js +11 -44
  407. package/dist/src/core/wire-init.d.ts +2 -3
  408. package/dist/src/core/wire-init.js +8 -19
  409. package/dist/src/core/wire-subscript.d.ts +2 -3
  410. package/dist/src/core/wire-subscript.js +4 -13
  411. package/dist/src/core/wire-utils.d.ts +7 -8
  412. package/dist/src/core/wire-utils.js +7 -8
  413. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  414. package/dist/src/core/xpcshell-appdir.js +38 -45
  415. package/dist/src/errors/base.d.ts +37 -16
  416. package/dist/src/errors/base.js +37 -16
  417. package/dist/src/errors/build.d.ts +13 -1
  418. package/dist/src/errors/build.js +14 -2
  419. package/dist/src/errors/codes.d.ts +16 -0
  420. package/dist/src/errors/codes.js +16 -0
  421. package/dist/src/errors/furnace.d.ts +4 -4
  422. package/dist/src/errors/git.d.ts +6 -10
  423. package/dist/src/errors/git.js +6 -10
  424. package/dist/src/errors/patch.d.ts +7 -7
  425. package/dist/src/errors/patch.js +7 -7
  426. package/dist/src/errors/rebase.d.ts +4 -4
  427. package/dist/src/errors/rebase.js +4 -4
  428. package/dist/src/index.d.ts +7 -1
  429. package/dist/src/index.js +5 -0
  430. package/dist/src/types/commands/index.d.ts +2 -2
  431. package/dist/src/types/commands/options.d.ts +88 -70
  432. package/dist/src/types/commands/patches.d.ts +24 -34
  433. package/dist/src/types/commands/project.d.ts +5 -34
  434. package/dist/src/types/config.d.ts +25 -23
  435. package/dist/src/types/config.js +6 -1
  436. package/dist/src/types/furnace.d.ts +37 -13
  437. package/dist/src/types/furnace.js +19 -1
  438. package/dist/src/types/index.d.ts +3 -3
  439. package/dist/src/utils/build-info.d.ts +7 -8
  440. package/dist/src/utils/build-info.js +12 -11
  441. package/dist/src/utils/errors.d.ts +6 -8
  442. package/dist/src/utils/errors.js +6 -8
  443. package/dist/src/utils/logger.d.ts +27 -10
  444. package/dist/src/utils/logger.js +28 -16
  445. package/dist/src/utils/machine-output.d.ts +12 -0
  446. package/dist/src/utils/machine-output.js +33 -0
  447. package/dist/src/utils/options.d.ts +31 -14
  448. package/dist/src/utils/options.js +86 -30
  449. package/dist/src/utils/paths.d.ts +10 -8
  450. package/dist/src/utils/paths.js +10 -8
  451. package/dist/src/utils/platform.d.ts +0 -25
  452. package/dist/src/utils/platform.js +1 -42
  453. package/dist/src/utils/process-group.d.ts +9 -4
  454. package/dist/src/utils/process-group.js +19 -5
  455. package/dist/src/utils/process.d.ts +42 -7
  456. package/dist/src/utils/process.js +44 -17
  457. package/dist/src/utils/ps-duration.d.ts +31 -0
  458. package/dist/src/utils/ps-duration.js +70 -0
  459. package/dist/src/utils/regex.d.ts +9 -10
  460. package/dist/src/utils/regex.js +9 -10
  461. package/dist/src/utils/validation.d.ts +58 -28
  462. package/dist/src/utils/validation.js +60 -33
  463. package/package.json +2 -2
  464. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  465. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  466. package/dist/src/core/manifest-helpers.d.ts +0 -38
  467. package/dist/src/core/manifest-helpers.js +0 -120
  468. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  469. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  470. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -2,20 +2,27 @@
2
2
  * Binary-aware drift classification for patch-owned files.
3
3
  *
4
4
  * The text classifier compares utf-8 decoded content against
5
- * `computePatchedContent`, which cannot apply `GIT binary patch` bodies —
6
- * so a binary file exported into a patch classified as `patch-owned-drift`
5
+ * `computePatchedContent`, which cannot apply `GIT binary patch` bodies — so
6
+ * a binary file exported into a patch classifies as `patch-owned-drift`
7
7
  * forever, keeping the engine-clean gate permanently red with nothing
8
- * actually un-durable. Binary sections DO carry the ground truth, though:
9
- * `git diff --binary` always records full blob hashes on the section's
8
+ * actually un-durable. A `GIT binary patch` section DOES carry the ground
9
+ * truth: `git diff --binary` always records full blob hashes on the section's
10
10
  * `index <old>..<new>` line. Comparing the live file's `git hash-object`
11
- * against the last owning section's new-side hash settles the
12
- * classification exactly. Bodies without a usable hash (hand-written
13
- * diffs, `Binary files … differ`) classify as `binary-unsupported` —
14
- * an honest "cannot compare" instead of a false "not durable".
11
+ * against the last owning section's new-side hash settles the classification
12
+ * exactly. Bodies without a usable hash (hand-written diffs) classify as
13
+ * `binary-unsupported` — an honest "cannot compare" instead of a false "not
14
+ * durable".
15
15
  *
16
- * Lives in its own module (not `status-classify.ts`) so the classifier
17
- * stays within the per-file line budget and this module never has to
18
- * import it back (dpdm-clean: the dependency edge points one way).
16
+ * That hash is trusted ONLY when the section carries a replayable payload
17
+ * (`hasBinaryDelta`). A `Binary files … differ` stub carries a correct index
18
+ * line too, so hashing against it reports `patch-backed` for a body that
19
+ * cannot rebuild the file at all — precisely the dishonesty this module
20
+ * exists to avoid. Such a body is `binary-unsupported` here, and an error
21
+ * from the `binary-body-not-reconstructable` queue lint.
22
+ *
23
+ * Lives in its own module (not `status-classify.ts`) so the classifier stays
24
+ * within the per-file line budget and this module never has to import it
25
+ * back: the dependency edge points one way, which keeps dpdm clean.
19
26
  */
20
27
  import type { PatchedContentContext } from './patch-apply.js';
21
28
  /** Batched patch lookup, injected by callers that classify many files. */
@@ -3,20 +3,27 @@
3
3
  * Binary-aware drift classification for patch-owned files.
4
4
  *
5
5
  * The text classifier compares utf-8 decoded content against
6
- * `computePatchedContent`, which cannot apply `GIT binary patch` bodies —
7
- * so a binary file exported into a patch classified as `patch-owned-drift`
6
+ * `computePatchedContent`, which cannot apply `GIT binary patch` bodies — so
7
+ * a binary file exported into a patch classifies as `patch-owned-drift`
8
8
  * forever, keeping the engine-clean gate permanently red with nothing
9
- * actually un-durable. Binary sections DO carry the ground truth, though:
10
- * `git diff --binary` always records full blob hashes on the section's
9
+ * actually un-durable. A `GIT binary patch` section DOES carry the ground
10
+ * truth: `git diff --binary` always records full blob hashes on the section's
11
11
  * `index <old>..<new>` line. Comparing the live file's `git hash-object`
12
- * against the last owning section's new-side hash settles the
13
- * classification exactly. Bodies without a usable hash (hand-written
14
- * diffs, `Binary files … differ`) classify as `binary-unsupported` —
15
- * an honest "cannot compare" instead of a false "not durable".
12
+ * against the last owning section's new-side hash settles the classification
13
+ * exactly. Bodies without a usable hash (hand-written diffs) classify as
14
+ * `binary-unsupported` — an honest "cannot compare" instead of a false "not
15
+ * durable".
16
16
  *
17
- * Lives in its own module (not `status-classify.ts`) so the classifier
18
- * stays within the per-file line budget and this module never has to
19
- * import it back (dpdm-clean: the dependency edge points one way).
17
+ * That hash is trusted ONLY when the section carries a replayable payload
18
+ * (`hasBinaryDelta`). A `Binary files … differ` stub carries a correct index
19
+ * line too, so hashing against it reports `patch-backed` for a body that
20
+ * cannot rebuild the file at all — precisely the dishonesty this module
21
+ * exists to avoid. Such a body is `binary-unsupported` here, and an error
22
+ * from the `binary-body-not-reconstructable` queue lint.
23
+ *
24
+ * Lives in its own module (not `status-classify.ts`) so the classifier stays
25
+ * within the per-file line budget and this module never has to import it
26
+ * back: the dependency edge points one way, which keeps dpdm clean.
20
27
  */
21
28
  import { join } from 'node:path';
22
29
  import { toError } from '../utils/errors.js';
@@ -102,7 +109,7 @@ export async function classifyBinaryOwnedFile(args) {
102
109
  // The last binary section expects the file to exist.
103
110
  return { ...entry, classification: 'patch-owned-drift', owner };
104
111
  }
105
- if (section.indexNewHash !== undefined) {
112
+ if (section.hasBinaryDelta && section.indexNewHash !== undefined) {
106
113
  const fullPath = join(engineDir, entry.file);
107
114
  const live = (await hashObjectBatch(engineDir, [fullPath])).get(fullPath);
108
115
  if (live !== undefined) {
@@ -7,38 +7,34 @@
7
7
  */
8
8
  /**
9
9
  * Classification buckets for engine file changes:
10
- * - `patch-backed`: content matches the expected post-patch state —
11
- * normal after `fireforge import`.
12
- * - `patch-owned-drift`: the file is claimed by exactly one patch, but
13
- * the live engine content no longer matches that patch's expected
14
- * post-apply content. This includes furnace-prefixed paths: a path
15
- * can be both Furnace-managed and patch-claimed (export a deployed
16
- * component, then edit the workspace source and `furnace deploy`
17
- * again — the deployed copy now has content the patch body lacks).
18
- * Before 0.38.0 the furnace prefix check ran first and silently
19
- * bucketed such files as `furnace`, so `status` reported the stale
20
- * patch as owned.
21
- * - `unmanaged`: edits not explained by any patch or tool — local
22
- * drift to export or discard.
10
+ * - `patch-backed`: content matches the expected post-patch state — normal
11
+ * after `fireforge import`.
12
+ * - `patch-owned-drift`: the file is claimed by exactly one patch, but the
13
+ * live engine content no longer matches that patch's expected post-apply
14
+ * content. This includes furnace-prefixed paths: a path can be both
15
+ * Furnace-managed and patch-claimed (export a deployed component, then
16
+ * edit the workspace source and `furnace deploy` again — the deployed copy
17
+ * now has content the patch body lacks). Checking the furnace prefix first
18
+ * would bucket such files as `furnace` and report the stale patch as
19
+ * owned.
20
+ * - `unmanaged`: edits not explained by any patch or tool — local drift to
21
+ * export or discard.
23
22
  * - `branding`: files under tool-managed branding paths, written by
24
23
  * FireForge's branding pipeline.
25
- * - `furnace`: files under Furnace-managed component prefixes. When
26
- * exactly one patch also claims the path, this bucket asserts the
27
- * live content matches that patch's expected post-apply content —
28
- * a mismatch is reported as `patch-owned-drift` instead (see above).
24
+ * - `furnace`: files under Furnace-managed component prefixes. When exactly
25
+ * one patch also claims the path, this bucket asserts the live content
26
+ * matches that patch's expected post-apply content — a mismatch is
27
+ * reported as `patch-owned-drift` instead (see above).
29
28
  * - `conflict`: the file is claimed by two or more patches in
30
- * `patches.json`. The human `--ownership` mode already surfaces
31
- * this bucket as `CONFLICT`; the classification is carried through
32
- * the JSON pipeline so machine consumers can detect the same
33
- * ownership breakage the human output shows. Before 0.16.0,
34
- * cross-patch conflicts silently rolled into the `unmanaged` bucket
35
- * in `--json`, which misled scripts built on top of the JSON view
36
- * into treating the file as routine local drift.
29
+ * `patches.json`. The human `--ownership` mode surfaces this bucket as
30
+ * `CONFLICT`; carrying the classification through the JSON pipeline lets
31
+ * machine consumers detect the same ownership breakage rather than seeing
32
+ * it rolled into `unmanaged` as routine local drift.
37
33
  * - `binary-unsupported`: the file's comparison is binary and the owning
38
- * patch body records no usable blob hash to compare against — an
39
- * honest "binary — comparison unsupported" instead of a permanent
40
- * false `patch-owned-drift`. Deliberately NOT in the default
41
- * `--check` fail set; opt in with `--fail-on binary-unsupported`.
34
+ * patch body records no usable blob hash to compare against — an honest
35
+ * "binary — comparison unsupported" instead of a permanent false
36
+ * `patch-owned-drift`. Deliberately NOT in the default `--check` fail set;
37
+ * opt in with `--fail-on binary-unsupported`.
42
38
  */
43
39
  export type FileClassification = 'patch-backed' | 'patch-owned-drift' | 'unmanaged' | 'branding' | 'furnace' | 'conflict' | 'binary-unsupported';
44
40
  export interface StatusFile {
@@ -91,13 +87,11 @@ export declare function isGeneratedBrandingPath(file: string, binaryName: string
91
87
  * Classifies files into patch-backed, unmanaged, branding, furnace, or
92
88
  * conflict buckets.
93
89
  *
94
- * Tracks patch ownership as a `Map<file, patchFilename[]>` rather than
95
- * a plain `Set<file>` so the classifier can surface cross-patch
96
- * ownership conflicts the same way the human `--ownership` mode does.
97
- * The 2026-04-21 eval's `status --json` run reported
98
- * `classification: "unmanaged"` on two files (`browser/base/jar.mn`,
99
- * `browser/themes/shared/jar.inc.mn`) that `--ownership` correctly
100
- * flagged as `CONFLICT`; the JSON output was effectively lying to
101
- * machine consumers about the nature of the drift.
90
+ * Tracks patch ownership as a `Map<file, patchFilename[]>` rather than a
91
+ * plain `Set<file>` so the classifier can surface cross-patch ownership
92
+ * conflicts the same way the human `--ownership` mode does. With only a Set,
93
+ * `status --json` reports `classification: "unmanaged"` on files that
94
+ * `--ownership` correctly flags as `CONFLICT`, which lies to machine
95
+ * consumers about the nature of the drift.
102
96
  */
103
97
  export declare function classifyFiles(files: StatusFile[], engineDir: string, patchesDir: string, binaryName: string, furnacePrefixes: Set<string>): Promise<ClassifiedFile[]>;
@@ -120,14 +120,12 @@ async function classifySingleOwnerFile(entry, engineDir, patchesDir, matchClassi
120
120
  * Classifies files into patch-backed, unmanaged, branding, furnace, or
121
121
  * conflict buckets.
122
122
  *
123
- * Tracks patch ownership as a `Map<file, patchFilename[]>` rather than
124
- * a plain `Set<file>` so the classifier can surface cross-patch
125
- * ownership conflicts the same way the human `--ownership` mode does.
126
- * The 2026-04-21 eval's `status --json` run reported
127
- * `classification: "unmanaged"` on two files (`browser/base/jar.mn`,
128
- * `browser/themes/shared/jar.inc.mn`) that `--ownership` correctly
129
- * flagged as `CONFLICT`; the JSON output was effectively lying to
130
- * machine consumers about the nature of the drift.
123
+ * Tracks patch ownership as a `Map<file, patchFilename[]>` rather than a
124
+ * plain `Set<file>` so the classifier can surface cross-patch ownership
125
+ * conflicts the same way the human `--ownership` mode does. With only a Set,
126
+ * `status --json` reports `classification: "unmanaged"` on files that
127
+ * `--ownership` correctly flags as `CONFLICT`, which lies to machine
128
+ * consumers about the nature of the drift.
131
129
  */
132
130
  export async function classifyFiles(files, engineDir, patchesDir, binaryName, furnacePrefixes) {
133
131
  // One manifest load + patch discovery + memoized body reads for the
@@ -135,13 +133,12 @@ export async function classifyFiles(files, engineDir, patchesDir, binaryName, fu
135
133
  // every dirty file (O(dirtyFiles × patches) redundant IO on a broad
136
134
  // engine edit session).
137
135
  const ctx = await createPatchedContentContext(patchesDir, engineDir);
138
- // Build a multimap from file path → list of claiming patch
139
- // filenames so we can detect cross-patch ownership conflicts. The
140
- // previous `Set<string>` captured only whether a path was claimed,
141
- // not by whom, and collapsed multi-owner files into the single-owner
142
- // branch where the expected-vs-actual content comparison then routed
143
- // them into `unmanaged` when the content didn't match either owner's
144
- // expectation.
136
+ // Build a multimap from file path → list of claiming patch filenames so
137
+ // cross-patch ownership conflicts are detectable. A plain `Set<string>`
138
+ // captures only whether a path was claimed, not by whom, and collapses
139
+ // multi-owner files into the single-owner branch where the
140
+ // expected-vs-actual content comparison then routes them into `unmanaged`
141
+ // when the content matches neither owner's expectation.
145
142
  const patchClaims = buildPatchClaims(ctx.manifestPatches);
146
143
  const deps = {
147
144
  engineDir,
@@ -176,11 +173,10 @@ async function classifyEntry(entry, deps) {
176
173
  };
177
174
  }
178
175
  // Branding paths are tool-managed for generated edits, but a brand-new
179
- // unowned branding asset must not disappear from `status --unmanaged`.
180
- // The Hominis Firefox 152 side-grade added Assets.car under the active
181
- // branding tree; classifying every branding path before checking
182
- // ownership hid that new patch candidate as "branding" even though no
183
- // patch claimed it yet.
176
+ // unowned branding asset must not disappear from `status --unmanaged`:
177
+ // classifying every branding path before checking ownership hides a new
178
+ // patch candidate (an Assets.car appearing under the active branding tree,
179
+ // for instance) as "branding" even though no patch claims it yet.
184
180
  const isUnownedNewFile = owners === undefined && (primaryCode === '?' || primaryCode === 'A');
185
181
  if (isBrandingManagedPath(entry.file, binaryName) &&
186
182
  (!isUnownedNewFile || isGeneratedBrandingPath(entry.file, binaryName))) {
@@ -197,12 +193,11 @@ async function classifyEntry(entry, deps) {
197
193
  }
198
194
  if (isFurnace) {
199
195
  // A furnace path claimed by exactly one patch gets the same
200
- // expected-vs-actual comparison as any other single-owner path:
201
- // after a `furnace deploy` of an edited component the deployed
202
- // copy has content the owning patch's body lacks, and the old
203
- // unconditional short-circuit silently bucketed that drift as
204
- // `furnace`. Multi-owner and unowned furnace paths keep the
205
- // short-circuit.
196
+ // expected-vs-actual comparison as any other single-owner path: after
197
+ // a `furnace deploy` of an edited component the deployed copy has
198
+ // content the owning patch's body lacks, and an unconditional
199
+ // short-circuit here would silently bucket that drift as `furnace`.
200
+ // Multi-owner and unowned furnace paths keep the short-circuit.
206
201
  if (owners && owners.length === 1 && owners[0] !== undefined) {
207
202
  return classifySingleOwnerFile(entry, engineDir, patchesDir, 'furnace', owners[0], ctx);
208
203
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Recognizer for the one mochitest failure shape that is a function of run
3
+ * LENGTH rather than of the code under test: the time-driven
4
+ * changed-preference checks.
5
+ *
6
+ * Separate from `test-harness-crash.ts` because it is not a crash
7
+ * classification — the run failed, and the classifier's verdict is correct.
8
+ * What this adds is which KIND of failure it was, which is a different
9
+ * question and one that only grows as more such shapes are recorded.
10
+ */
11
+ /**
12
+ * Describes a failure set that consists ENTIRELY of changed-preference
13
+ * checks on {@link TIME_DRIVEN_PREF_NAMES}.
14
+ *
15
+ * Deliberately keyed on the pref NAMES rather than on the harness's phrasing
16
+ * of its changed-preference report: the phrasing is upstream copy that has
17
+ * no stable contract with us, and the names are the part that identifies the
18
+ * shape. A single block that names anything else — a real assertion, a
19
+ * different pref — disqualifies the whole set, because the point is to
20
+ * describe a run whose every failure is this noise, never to explain away
21
+ * one real failure sitting beside it.
22
+ *
23
+ * Pure; returns undefined when the shape does not apply.
24
+ *
25
+ * @param output - Combined harness stdout+stderr
26
+ * @returns Operator-facing explanation, or undefined
27
+ */
28
+ export declare function describeChangedPrefNoise(output: string): string | undefined;
29
+ /** Short verdict-line note for the changed-preference-noise shape. */
30
+ export declare function changedPrefNoiseVerdictNote(output: string): string | undefined;
@@ -0,0 +1,78 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Recognizer for the one mochitest failure shape that is a function of run
4
+ * LENGTH rather than of the code under test: the time-driven
5
+ * changed-preference checks.
6
+ *
7
+ * Separate from `test-harness-crash.ts` because it is not a crash
8
+ * classification — the run failed, and the classifier's verdict is correct.
9
+ * What this adds is which KIND of failure it was, which is a different
10
+ * question and one that only grows as more such shapes are recorded.
11
+ */
12
+ import { collectUnexpectedFailureBlocks } from './test-harness-crash.js';
13
+ /**
14
+ * Preferences that move on their own as a function of how LONG a run takes,
15
+ * not of what it tested.
16
+ *
17
+ * Both are time-driven: a long run crosses the cold-startup check interval
18
+ * and the pref moves under the harness. The observable consequence is that
19
+ * chunk length, not the change under test, decides whether the suite is
20
+ * red — the same work at a smaller `--chunk` passes. FireForge cannot
21
+ * suppress the check (it is the harness's, not ours) but it can stop the
22
+ * pair being indistinguishable from two real assertion failures, which is
23
+ * the whole cost of the shape.
24
+ *
25
+ * Matched as substrings: the harness prints fully-qualified pref names and
26
+ * the leading namespace has moved upstream more than once.
27
+ */
28
+ const TIME_DRIVEN_PREF_NAMES = ['lastColdStartupCheck', 'globalprivacycontrol'];
29
+ /** A failure block that is a changed-preference report rather than an assertion. */
30
+ const CHANGED_PREF_BLOCK_PATTERN = /pref(?:erence)?s?\b/i;
31
+ /**
32
+ * Describes a failure set that consists ENTIRELY of changed-preference
33
+ * checks on {@link TIME_DRIVEN_PREF_NAMES}.
34
+ *
35
+ * Deliberately keyed on the pref NAMES rather than on the harness's phrasing
36
+ * of its changed-preference report: the phrasing is upstream copy that has
37
+ * no stable contract with us, and the names are the part that identifies the
38
+ * shape. A single block that names anything else — a real assertion, a
39
+ * different pref — disqualifies the whole set, because the point is to
40
+ * describe a run whose every failure is this noise, never to explain away
41
+ * one real failure sitting beside it.
42
+ *
43
+ * Pure; returns undefined when the shape does not apply.
44
+ *
45
+ * @param output - Combined harness stdout+stderr
46
+ * @returns Operator-facing explanation, or undefined
47
+ */
48
+ export function describeChangedPrefNoise(output) {
49
+ const blocks = collectUnexpectedFailureBlocks(output, CHANGED_PREF_BLOCK_LIMIT).filter((block) => !block.startsWith('…'));
50
+ if (blocks.length === 0)
51
+ return undefined;
52
+ const namesSeen = new Set();
53
+ for (const block of blocks) {
54
+ if (!CHANGED_PREF_BLOCK_PATTERN.test(block))
55
+ return undefined;
56
+ const matched = TIME_DRIVEN_PREF_NAMES.filter((name) => block.includes(name));
57
+ if (matched.length === 0)
58
+ return undefined;
59
+ for (const name of matched)
60
+ namesSeen.add(name);
61
+ }
62
+ return (`Every unexpected result in this run is a changed-preference check on ${[...namesSeen].join(' / ')}. ` +
63
+ 'Those preferences are TIME-DRIVEN: they move once a run is long enough to cross the ' +
64
+ 'cold-startup check interval, so they correlate with run LENGTH rather than with the ' +
65
+ 'change under test or with the build. The first probe is to halve the chunk size ' +
66
+ '(`--chunk 8` where a 40-row chunk tripped it) and see whether the same work passes; ' +
67
+ 'if it does, this was harness noise, not a regression. FireForge cannot forgive the ' +
68
+ 'check — it belongs to the harness — only tell you which kind of failure you are ' +
69
+ 'looking at.');
70
+ }
71
+ /** Failure blocks inspected before the changed-pref shape is abandoned. */
72
+ const CHANGED_PREF_BLOCK_LIMIT = 20;
73
+ /** Short verdict-line note for the changed-preference-noise shape. */
74
+ export function changedPrefNoiseVerdictNote(output) {
75
+ return describeChangedPrefNoise(output) === undefined
76
+ ? undefined
77
+ : 'all unexpected results are time-driven changed-pref checks';
78
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Harness-crash classification for `fireforge test` (field reports C1/C2).
2
+ * Harness-crash classification for `fireforge test`.
3
3
  *
4
4
  * The wrapped mach harness exhibits flaky non-test failures that an exit
5
5
  * code (or a "did it print a summary" grep) cannot distinguish from real
@@ -10,25 +10,22 @@
10
10
  * 'poll_interval'`, `host_statistics64(HOST_VM_INFO64) syscall failed`,
11
11
  * `(ipc/mig) array not large enough` — a psutil/macOS mismatch) which
12
12
  * abort the run before any test executes;
13
- * - hangs after browser startup that die at the no-output timeout yet
14
- * still emit a `Passed: 0` summary;
15
- * - post-green shutdown re-entry, where a fully green run stalls on
16
- * "must wait for focus" and records "Application shut down (without
17
- * crashing) in the middle of a test!" as the only unexpected failure.
13
+ * - hangs after browser startup that die at the no-output timeout yet still
14
+ * emit a `Passed: 0` summary;
15
+ * - post-green shutdown re-entry, where a fully green run stalls on "must
16
+ * wait for focus" and records "Application shut down (without crashing)
17
+ * in the middle of a test!" as the only unexpected failure.
18
18
  *
19
19
  * Classification therefore keys on `TEST-START` presence — summary lines
20
20
  * never count as proof that tests ran — and recognizes the crash shapes
21
- * above so the command layer can retry them with a bounded budget instead
22
- * of reporting phantom test failures (or phantom passes).
21
+ * above so the command layer can retry them with a bounded budget instead of
22
+ * reporting phantom test failures (or phantom passes).
23
23
  */
24
- import type { DisplaySleepState } from './display-state.js';
24
+ export type { HarnessCrashSignature } from './test-harness-signature.js';
25
+ import type { HarnessCrashSignature } from './test-harness-signature.js';
26
+ export { headedDisplayAsleepVerdictNote, headedNoOutputTimeoutHint } from './test-stall-triage.js';
25
27
  /** How a completed harness run should be interpreted. */
26
28
  export type HarnessRunClassification = 'tests-ran-ok' | 'test-failures' | 'harness-crash' | 'no-tests';
27
- /** A recognized harness-crash shape with its evidence line. */
28
- export interface HarnessCrashSignature {
29
- reason: string;
30
- line: string;
31
- }
32
29
  /** Numeric counts parsed from the harness's embedded result summary. */
33
30
  export interface HarnessSummaryCounts {
34
31
  /** Total checks the suite ran (`Ran N checks`), when the summary prints one. */
@@ -61,10 +58,10 @@ export interface HarnessRunVerdict extends HarnessSummaryCounts {
61
58
  /**
62
59
  * Set when the output embeds a green-LOOKING summary that was REJECTED:
63
60
  * crash or truncation evidence proves the suite never completed, so the
64
- * "green" counts only cover the files that ran before the run died
65
- * (field report: a SIGSEGV at the second of eight files left a
66
- * `Passed: 2 / Failed: 0` summary that 0.35.0 green-washed into a pass).
67
- * Callers must fail the run and surface the evidence.
61
+ * "green" counts only cover the files that ran before the run died — a
62
+ * SIGSEGV at the second of eight files leaves a `Passed: 2 / Failed: 0`
63
+ * summary that reads as a pass. Callers must fail the run and surface the
64
+ * evidence.
68
65
  */
69
66
  greenSummaryRejected?: GreenSummaryRejection;
70
67
  /**
@@ -99,16 +96,16 @@ export declare function stripNonSignalNoise(output: string): string;
99
96
  */
100
97
  export declare function findCrashMarkerLine(output: string): string | undefined;
101
98
  /**
102
- * True when the output embeds a COMPLETED, GREEN suite summary: an
103
- * execution signal, `Unexpected results: 0` (and no non-zero unexpected
104
- * count), a `SUITE_END` marker, no real `TEST-UNEXPECTED-*` lines — and
105
- * NO crash marker. Such a run finished its suite; any startup-traceback-
106
- * shaped noise in the same output is by definition non-fatal, so this
107
- * vetoes signature-based crash classification (field report: fully green
108
- * sharded runs reported `CRASH (N attempts)` because degradation warnings
109
- * matched the psutil signals). A summary printed after a crash marker is
110
- * NOT "completed" — it only covers the files that ran before the crash
111
- * (0.35.0 green-wash field report). Exported for direct unit testing.
99
+ * True when the output embeds a COMPLETED, GREEN suite summary: an execution
100
+ * signal, `Unexpected results: 0` (and no non-zero unexpected count), a
101
+ * `SUITE_END` marker, no real `TEST-UNEXPECTED-*` lines — and NO crash
102
+ * marker. Such a run finished its suite, so any startup-traceback-shaped
103
+ * noise in the same output is by definition non-fatal and this vetoes
104
+ * signature-based crash classification; without the veto, fully green
105
+ * sharded runs report `CRASH (N attempts)` because degradation warnings
106
+ * match the psutil signals. A summary printed after a crash marker is NOT
107
+ * "completed" — it only covers the files that ran before the crash.
108
+ * Exported for direct unit testing.
112
109
  */
113
110
  export declare function hasCompletedGreenSummary(output: string): boolean;
114
111
  /**
@@ -193,41 +190,6 @@ export declare function formatFireforgeVerdictLine(verdict: HarnessRunVerdict, s
193
190
  export declare function buildGreenSummaryRejectedMessage(rejection: GreenSummaryRejection, exitCode: number): string;
194
191
  /** Builds the operator-facing failure message after retries are exhausted. */
195
192
  export declare function buildHarnessCrashMessage(signature: HarnessCrashSignature, attempts: number, commandLabel?: string): string;
196
- /**
197
- * Optional hint appended to the harness-crash message when a HEADED run on
198
- * macOS died at the no-output timeout.
199
- *
200
- * When `displayState` names a MEASURED display state, the hint states it
201
- * as fact rather than as one of three possibilities — that is the whole
202
- * point of probing: an operator staring at a bare test failure should not
203
- * have to rediscover that their machine dimmed. `caffeinate` is described
204
- * accurately (it prevents sleep; it cannot wake an already-sleeping
205
- * display), because the previous wording sent operators to a command that
206
- * could not have helped them.
207
- *
208
- * Pure — the platform and the probed state are injected so unit tests need
209
- * no mocking.
210
- *
211
- * @param signature - The recognized crash shape
212
- * @param context - Run mode, platform, and the probed display state
213
- * @returns The hint text, or undefined when the shape does not apply
214
- */
215
- export declare function headedNoOutputTimeoutHint(signature: HarnessCrashSignature, context: {
216
- headless: boolean;
217
- platform: string;
218
- displayState?: DisplaySleepState;
219
- }): string | undefined;
220
- /**
221
- * Verdict-line note for a headed no-output stall whose display was
222
- * measured asleep. Returned as the parenthetical the verdict line carries,
223
- * so the one greppable line an automated consumer reads already names the
224
- * environmental cause instead of a bare `FAIL reason=crash`.
225
- */
226
- export declare function headedDisplayAsleepVerdictNote(signature: HarnessCrashSignature, context: {
227
- headless: boolean;
228
- platform: string;
229
- displayState: DisplaySleepState;
230
- }): string | undefined;
231
193
  /**
232
194
  * True when a harness run died on SIGSEGV having produced no test evidence
233
195
  * whatsoever.