@hominis/fireforge 0.51.0 → 0.53.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 (254) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/build-info.json +4 -4
  3. package/dist/src/commands/bootstrap.js +2 -3
  4. package/dist/src/commands/build-prelock.d.ts +3 -3
  5. package/dist/src/commands/build-prelock.js +3 -3
  6. package/dist/src/commands/build.js +4 -0
  7. package/dist/src/commands/declarations-report.d.ts +9 -3
  8. package/dist/src/commands/declarations-report.js +18 -11
  9. package/dist/src/commands/discard.js +3 -3
  10. package/dist/src/commands/doctor-furnace-config-sync.js +4 -4
  11. package/dist/src/commands/doctor-furnace.js +5 -5
  12. package/dist/src/commands/export-adopt-check.d.ts +5 -6
  13. package/dist/src/commands/export-adopt-check.js +5 -6
  14. package/dist/src/commands/export-flow.js +6 -9
  15. package/dist/src/commands/export-staged-registration.d.ts +4 -4
  16. package/dist/src/commands/export-staged-registration.js +4 -4
  17. package/dist/src/commands/forward-import-remedy.d.ts +2 -3
  18. package/dist/src/commands/forward-import-remedy.js +2 -3
  19. package/dist/src/commands/furnace/create-dry-run.d.ts +2 -2
  20. package/dist/src/commands/furnace/deploy-fragment-includers.d.ts +2 -2
  21. package/dist/src/commands/furnace/deploy-fragment-includers.js +2 -2
  22. package/dist/src/commands/furnace/index.js +2 -2
  23. package/dist/src/commands/lint.js +4 -4
  24. package/dist/src/commands/patch/compact.js +1 -1
  25. package/dist/src/commands/patch/lint-ignore-from.d.ts +3 -4
  26. package/dist/src/commands/patch/lint-ignore-from.js +3 -4
  27. package/dist/src/commands/patch/move-files-adopt.d.ts +33 -0
  28. package/dist/src/commands/patch/move-files-adopt.js +65 -0
  29. package/dist/src/commands/patch/move-files-create.js +41 -20
  30. package/dist/src/commands/patch/move-files-into.d.ts +18 -0
  31. package/dist/src/commands/patch/move-files-into.js +125 -60
  32. package/dist/src/commands/patch/move-files-retire.d.ts +39 -0
  33. package/dist/src/commands/patch/move-files-retire.js +91 -0
  34. package/dist/src/commands/patch/move-files.d.ts +3 -0
  35. package/dist/src/commands/patch/move-files.js +42 -12
  36. package/dist/src/commands/patch/move-size-tier.d.ts +27 -0
  37. package/dist/src/commands/patch/move-size-tier.js +66 -0
  38. package/dist/src/commands/patch/rename.js +1 -1
  39. package/dist/src/commands/patch/reorder-projection.d.ts +17 -0
  40. package/dist/src/commands/patch/reorder-projection.js +49 -0
  41. package/dist/src/commands/patch/reorder-shift.d.ts +30 -0
  42. package/dist/src/commands/patch/reorder-shift.js +307 -0
  43. package/dist/src/commands/patch/reorder.d.ts +9 -15
  44. package/dist/src/commands/patch/reorder.js +52 -50
  45. package/dist/src/commands/patch/split-plan.d.ts +31 -3
  46. package/dist/src/commands/patch/split-plan.js +36 -12
  47. package/dist/src/commands/patch/split-staged.d.ts +2 -2
  48. package/dist/src/commands/patch/split-staged.js +2 -2
  49. package/dist/src/commands/patch/split.js +18 -5
  50. package/dist/src/commands/patch/staged-dependency-entries.d.ts +9 -1
  51. package/dist/src/commands/patch/staged-dependency-entries.js +32 -4
  52. package/dist/src/commands/patch/staged-dependency-from.js +15 -9
  53. package/dist/src/commands/patch/staged-dependency.js +13 -9
  54. package/dist/src/commands/patch/tier.js +2 -2
  55. package/dist/src/commands/patch-rename-map.d.ts +14 -3
  56. package/dist/src/commands/patch-rename-map.js +21 -3
  57. package/dist/src/commands/re-export-adjacent.d.ts +4 -5
  58. package/dist/src/commands/re-export-lint.d.ts +4 -3
  59. package/dist/src/commands/re-export-lint.js +4 -3
  60. package/dist/src/commands/re-export-preflight.js +1 -2
  61. package/dist/src/commands/re-export-purity-scope.d.ts +1 -1
  62. package/dist/src/commands/re-export-purity-scope.js +13 -7
  63. package/dist/src/commands/re-export-retired-declarations.d.ts +22 -0
  64. package/dist/src/commands/re-export-retired-declarations.js +113 -0
  65. package/dist/src/commands/re-export-single.js +2 -2
  66. package/dist/src/commands/re-export-stale-registrations.d.ts +27 -6
  67. package/dist/src/commands/re-export-stale-registrations.js +65 -21
  68. package/dist/src/commands/re-export.js +4 -0
  69. package/dist/src/commands/rebase/continue.js +3 -3
  70. package/dist/src/commands/register.js +5 -4
  71. package/dist/src/commands/status-output.js +5 -1
  72. package/dist/src/commands/status.js +1 -1
  73. package/dist/src/commands/test-build-furnace.d.ts +1 -2
  74. package/dist/src/commands/test-build-furnace.js +1 -2
  75. package/dist/src/commands/test-build-phase.d.ts +10 -0
  76. package/dist/src/commands/test-build-phase.js +51 -16
  77. package/dist/src/commands/test-control-restore.d.ts +40 -0
  78. package/dist/src/commands/test-control-restore.js +86 -0
  79. package/dist/src/commands/test-dispatch.js +83 -13
  80. package/dist/src/commands/test-multi-verdict.js +1 -0
  81. package/dist/src/commands/test-orchestrate.d.ts +11 -0
  82. package/dist/src/commands/test-orchestrate.js +33 -2
  83. package/dist/src/commands/test-perf-scope.d.ts +1 -1
  84. package/dist/src/commands/test-perf-scope.js +1 -1
  85. package/dist/src/commands/test-prelock.d.ts +7 -8
  86. package/dist/src/commands/test-prelock.js +15 -12
  87. package/dist/src/commands/test-register.js +2 -2
  88. package/dist/src/commands/test-results.d.ts +10 -1
  89. package/dist/src/commands/test-results.js +20 -3
  90. package/dist/src/commands/test-run.d.ts +6 -0
  91. package/dist/src/commands/test-run.js +23 -4
  92. package/dist/src/commands/test-shards.d.ts +7 -3
  93. package/dist/src/commands/test-shards.js +24 -7
  94. package/dist/src/commands/test-stale-gate.d.ts +6 -1
  95. package/dist/src/commands/test-stale-gate.js +47 -2
  96. package/dist/src/commands/test-verdict.d.ts +5 -3
  97. package/dist/src/commands/test.js +14 -1
  98. package/dist/src/commands/token.js +2 -2
  99. package/dist/src/commands/verify.js +1 -1
  100. package/dist/src/commands/watch.js +1 -1
  101. package/dist/src/commands/wire.js +1 -1
  102. package/dist/src/core/build-audit-resolve.d.ts +4 -5
  103. package/dist/src/core/build-audit-resolve.js +4 -5
  104. package/dist/src/core/build-audit-skip.d.ts +2 -2
  105. package/dist/src/core/build-audit.d.ts +3 -4
  106. package/dist/src/core/build-baseline-types.d.ts +37 -3
  107. package/dist/src/core/build-baseline-validate.d.ts +30 -1
  108. package/dist/src/core/build-baseline-validate.js +134 -33
  109. package/dist/src/core/build-baseline.d.ts +8 -2
  110. package/dist/src/core/build-baseline.js +32 -16
  111. package/dist/src/core/build-jar-escalation.d.ts +4 -4
  112. package/dist/src/core/build-jar-escalation.js +4 -4
  113. package/dist/src/core/build-prepare.js +4 -4
  114. package/dist/src/core/config-validate.js +4 -4
  115. package/dist/src/core/coverage-extend.d.ts +5 -3
  116. package/dist/src/core/coverage-extend.js +8 -3
  117. package/dist/src/core/coverage-records.d.ts +4 -5
  118. package/dist/src/core/coverage-records.js +4 -5
  119. package/dist/src/core/destructive.d.ts +2 -2
  120. package/dist/src/core/destructive.js +2 -2
  121. package/dist/src/core/discard-baseline.d.ts +2 -2
  122. package/dist/src/core/discard-baseline.js +3 -3
  123. package/dist/src/core/engine-session-lock.d.ts +1 -1
  124. package/dist/src/core/engine-session-lock.js +1 -1
  125. package/dist/src/core/file-lock-owner.d.ts +5 -5
  126. package/dist/src/core/file-lock-owner.js +5 -5
  127. package/dist/src/core/file-lock-queue.js +6 -8
  128. package/dist/src/core/file-lock-shared.d.ts +4 -4
  129. package/dist/src/core/file-lock-shared.js +4 -4
  130. package/dist/src/core/file-lock.js +7 -10
  131. package/dist/src/core/firefox.js +2 -2
  132. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +1 -1
  133. package/dist/src/core/furnace-apply-overwrite-warn.js +1 -1
  134. package/dist/src/core/furnace-apply.js +3 -3
  135. package/dist/src/core/furnace-config-validate-exceptions.d.ts +4 -3
  136. package/dist/src/core/furnace-config-validate-exceptions.js +26 -6
  137. package/dist/src/core/furnace-operation.js +5 -5
  138. package/dist/src/core/furnace-registration-jar-inplace.js +5 -6
  139. package/dist/src/core/furnace-registration-rename.d.ts +5 -5
  140. package/dist/src/core/furnace-registration-rename.js +5 -5
  141. package/dist/src/core/furnace-registration-validate.d.ts +2 -1
  142. package/dist/src/core/furnace-registration-validate.js +2 -1
  143. package/dist/src/core/furnace-registration.js +2 -2
  144. package/dist/src/core/furnace-validate-accessibility.d.ts +1 -1
  145. package/dist/src/core/furnace-validate-accessibility.js +22 -11
  146. package/dist/src/core/furnace-validate-exceptions.d.ts +2 -1
  147. package/dist/src/core/furnace-validate-exceptions.js +31 -13
  148. package/dist/src/core/git-base.js +4 -3
  149. package/dist/src/core/git-diff.js +1 -1
  150. package/dist/src/core/git.js +1 -1
  151. package/dist/src/core/harness-orphans.d.ts +6 -6
  152. package/dist/src/core/harness-orphans.js +6 -6
  153. package/dist/src/core/harness-port-reservation.d.ts +60 -0
  154. package/dist/src/core/harness-port-reservation.js +122 -0
  155. package/dist/src/core/history-log-file.d.ts +9 -9
  156. package/dist/src/core/history-log-file.js +15 -15
  157. package/dist/src/core/history-log.d.ts +3 -3
  158. package/dist/src/core/history-log.js +3 -3
  159. package/dist/src/core/launchable-build.d.ts +39 -0
  160. package/dist/src/core/launchable-build.js +85 -0
  161. package/dist/src/core/marionette-port.js +1 -3
  162. package/dist/src/core/moz-manifest-rules.js +3 -2
  163. package/dist/src/core/negative-control-compiled.d.ts +22 -3
  164. package/dist/src/core/negative-control-compiled.js +41 -5
  165. package/dist/src/core/parser-fallback.d.ts +5 -5
  166. package/dist/src/core/parser-fallback.js +5 -5
  167. package/dist/src/core/patch-identifier-suggest.d.ts +6 -0
  168. package/dist/src/core/patch-identifier-suggest.js +11 -4
  169. package/dist/src/core/patch-lint-checkjs.d.ts +1 -1
  170. package/dist/src/core/patch-lint-checkjs.js +1 -1
  171. package/dist/src/core/patch-lint-css.js +2 -2
  172. package/dist/src/core/patch-lint-file-size.d.ts +5 -7
  173. package/dist/src/core/patch-lint-file-size.js +5 -7
  174. package/dist/src/core/patch-lint-ignore.d.ts +3 -3
  175. package/dist/src/core/patch-lint-ignore.js +3 -3
  176. package/dist/src/core/patch-lint-prettier.d.ts +4 -6
  177. package/dist/src/core/patch-lint-prettier.js +4 -6
  178. package/dist/src/core/patch-lint-projection.d.ts +1 -1
  179. package/dist/src/core/patch-lint-projection.js +1 -1
  180. package/dist/src/core/patch-lint-queue-types.d.ts +2 -2
  181. package/dist/src/core/patch-lint-queue-types.js +2 -2
  182. package/dist/src/core/patch-manifest-consistency.d.ts +2 -2
  183. package/dist/src/core/patch-manifest-consistency.js +2 -2
  184. package/dist/src/core/patch-manifest-io.d.ts +5 -1
  185. package/dist/src/core/patch-manifest-io.js +12 -10
  186. package/dist/src/core/patch-manifest-key-order.d.ts +3 -4
  187. package/dist/src/core/patch-manifest-key-order.js +3 -4
  188. package/dist/src/core/patch-parse.d.ts +3 -3
  189. package/dist/src/core/patch-parse.js +3 -3
  190. package/dist/src/core/patch-policy.d.ts +1 -1
  191. package/dist/src/core/patch-policy.js +1 -1
  192. package/dist/src/core/patch-source-metadata.d.ts +1 -1
  193. package/dist/src/core/patch-source-metadata.js +1 -1
  194. package/dist/src/core/patch-subset.d.ts +5 -3
  195. package/dist/src/core/patch-subset.js +8 -7
  196. package/dist/src/core/patch-transform.d.ts +2 -2
  197. package/dist/src/core/patch-transform.js +2 -2
  198. package/dist/src/core/rebase-session.js +4 -6
  199. package/dist/src/core/register-xpcshell-test.d.ts +28 -7
  200. package/dist/src/core/register-xpcshell-test.js +76 -27
  201. package/dist/src/core/run-log.d.ts +8 -8
  202. package/dist/src/core/run-log.js +8 -8
  203. package/dist/src/core/status-classify.d.ts +1 -2
  204. package/dist/src/core/status-classify.js +1 -2
  205. package/dist/src/core/test-harness-crash.d.ts +5 -3
  206. package/dist/src/core/test-harness-crash.js +13 -14
  207. package/dist/src/core/test-harness-infra.d.ts +32 -0
  208. package/dist/src/core/test-harness-infra.js +69 -0
  209. package/dist/src/core/test-harness-signature.d.ts +6 -0
  210. package/dist/src/core/test-run-completeness.d.ts +12 -5
  211. package/dist/src/core/test-run-completeness.js +15 -8
  212. package/dist/src/core/test-stale-check.d.ts +48 -1
  213. package/dist/src/core/test-stale-check.js +97 -2
  214. package/dist/src/core/test-stall-triage.js +5 -7
  215. package/dist/src/core/token-category.js +2 -2
  216. package/dist/src/core/token-inventory.js +3 -3
  217. package/dist/src/core/token-manager.d.ts +2 -2
  218. package/dist/src/core/token-manager.js +5 -7
  219. package/dist/src/core/token-variant.d.ts +6 -6
  220. package/dist/src/core/token-variant.js +7 -7
  221. package/dist/src/core/typecheck-shim.d.ts +2 -1
  222. package/dist/src/core/typecheck-shim.js +29 -5
  223. package/dist/src/core/wire-destroy.d.ts +1 -1
  224. package/dist/src/core/wire-destroy.js +1 -1
  225. package/dist/src/core/wire-dom-fragment.js +1 -1
  226. package/dist/src/core/wire-init.d.ts +1 -1
  227. package/dist/src/core/wire-init.js +1 -1
  228. package/dist/src/core/wire-subscript.d.ts +1 -1
  229. package/dist/src/core/wire-subscript.js +1 -1
  230. package/dist/src/core/wire-utils.d.ts +2 -2
  231. package/dist/src/core/wire-utils.js +3 -3
  232. package/dist/src/errors/base.d.ts +1 -1
  233. package/dist/src/errors/base.js +1 -1
  234. package/dist/src/errors/rebase.d.ts +1 -1
  235. package/dist/src/errors/rebase.js +1 -1
  236. package/dist/src/types/commands/options.d.ts +26 -3
  237. package/dist/src/types/config.d.ts +5 -7
  238. package/dist/src/types/furnace.d.ts +9 -0
  239. package/dist/src/utils/options.js +2 -2
  240. package/dist/src/utils/process.js +2 -2
  241. package/dist/src/utils/regex.d.ts +2 -2
  242. package/dist/src/utils/regex.js +3 -3
  243. package/dist/src/utils/sleep.d.ts +5 -6
  244. package/dist/src/utils/sleep.js +5 -6
  245. package/dist/src/utils/validation.js +2 -2
  246. package/docs/configuration.md +40 -26
  247. package/docs/exit-codes.md +8 -11
  248. package/docs/furnace.md +21 -3
  249. package/docs/lifecycle-invariants.md +36 -45
  250. package/docs/machine-output.md +43 -22
  251. package/docs/patch-workflow.md +129 -24
  252. package/docs/run-logs.md +8 -9
  253. package/docs/testing.md +118 -44
  254. package/package.json +1 -1
@@ -109,7 +109,7 @@ adjacent to it, meaning the re-export would plausibly have to capture it:
109
109
  moz.build or test-manifest line, `*` globs included).
110
110
 
111
111
  A file in a sibling or nested directory is not adjacent, so a peer
112
- session's unexported test in `test/hominis-tiles/` no longer refuses a
112
+ session's unexported test in `test/hominis-tiles/` does not refuse a
113
113
  patch that owns `test/hominis/browser.toml`. The refusal names every file
114
114
  with its anchor (`beside engine/<dir>` or `registered by engine/<carrier>`).
115
115
 
@@ -139,8 +139,8 @@ fireforge re-export 102 200 --refuse-adjacent-unmanaged --refuse-foreign-drift \
139
139
 
140
140
  `--expect-from <file>` reads `--expect` paths from a file, one per line,
141
141
  with blank lines and `#` comments ignored. Under zsh a list held in one
142
- variable reaches FireForge as a single argument, and splitting it by hand
143
- left every path after the first as a positional patch name.
142
+ variable reaches FireForge as a single argument, and a file avoids splitting
143
+ it by hand.
144
144
 
145
145
  Arguments are checked before the run queues for the engine lock: an empty
146
146
  or unknown patch name, a positional that is an engine file (the refusal
@@ -192,6 +192,11 @@ exception. Removing every file would empty the patch and requires `patch
192
192
  delete` (or `patch retire`, which keeps the order number) instead.
193
193
  `--expect-removed` cannot combine with `--scan`, `--files` or `--all`.
194
194
 
195
+ The same run drops every staged declaration, registration or forward
196
+ import, in any patch of the queue, whose `creates` path it retired and no
197
+ other patch still owns. It prints each one (`would drop` in a dry run) and
198
+ records the drop in the history log (`re-export-staged-prune`).
199
+
195
200
  ### Forward imports and new modules
196
201
 
197
202
  A re-export (plain or `--scan`) refused because a file imports a module a
@@ -203,14 +208,35 @@ placeholder. A refused `--scan` adoption, `patch move-files` or `patch split`
203
208
  also writes every declaration it proposes into one lint-report-shaped JSON
204
209
  (`--declarations-out <path>`, by default under `.fireforge/declarations/`)
205
210
  and names the single `patch staged-dependency <holder> --add --from <file>`
206
- call per holding patch that discharges them all. Moving tests out of a patch
207
- that owns a shared manifest no longer needs one declaration typed per
208
- manifest line.
211
+ call per holding patch that discharges them all, so moving tests out of a
212
+ patch that owns a shared manifest needs no declaration typed per manifest
213
+ line.
214
+
215
+ For `patch move-files`, `patch split` and a placed `export`:
216
+
217
+ - The projection of `patch move-files`, `patch split` and a placed `export`
218
+ lints with the queue's whole lint configuration, as the committed `lint
219
+ --per-patch` gate does. With `patchLint.forwardRegistration: "extended"`,
220
+ a shared test manifest's suite entries (`["browser_*.js"]`) are projected
221
+ with the move and written to the `--declarations-out` report, not only its
222
+ `support-files` lines. A dry run that reads clean stays clean after the
223
+ move.
224
+ - A refused `move-files --create` or `split` report names the patch the
225
+ rerun creates as the `owner` (its planned `<order>-<category>-<name>.patch`).
226
+ - The declarations `move-files` and `split` write, and every entry of their
227
+ report, carry a `reason` naming the move, such as
228
+ `"201-ui-tests.patch → 236-ui-suites.patch by patch move-files"`. The
229
+ `--add --from` call the refusal prints then needs no `--reason`; an
230
+ entry's own reason wins over the flag.
209
231
 
210
232
  A re-export also drops a staged registration whose `creates` path no patch
211
233
  in the queue owns any more, for example after a merge deleted the file it
212
234
  registered. The dry run names the drop, and the write records it in the
213
- history log (`re-export-staged-prune`).
235
+ history log (`re-export-staged-prune`). A registration whose
236
+ `creates` file exists in engine/, or is an `--expect-new` path of the run,
237
+ is kept and named ("kept: file exists but no patch owns it yet"): the
238
+ manifest owner can re-export after the declaration and before the scan
239
+ that adopts the file, in either order.
214
240
 
215
241
  An `export --order` (or `--before`/`--after`) refused on a new system
216
242
  module that no patch registers in moz.build says "register first". When the
@@ -236,13 +262,61 @@ The re-export policy gate checks the `filesAffected` the export will write,
236
262
  and only for the patch being written, so one patch's stale row cannot fail
237
263
  the other patches in the same call.
238
264
 
265
+ ### Registering files
266
+
267
+ `fireforge register <path>` adds a new file's line to the build manifest
268
+ that packages or runs it: a jar.mn, a moz.build list, a test manifest. Most
269
+ manifests are kept in sorted order, and the entry is inserted
270
+ alphabetically. `--after <entry>` places it after the line containing that
271
+ substring instead, where the manifest's rule supports it. Elsewhere the
272
+ flag is ignored with a warning, and an `--after` that matches nothing falls
273
+ back to sorted order with a warning.
274
+
275
+ An xpcshell test (`test_*.js` outside `browser/base/content/test/`) goes
276
+ into its directory's `xpcshell.toml` as a `["test_*.js"]` section, placed
277
+ as follows:
278
+
279
+ - The new section is inserted ahead of the `#` comment lines directly above
280
+ the entry it precedes, never between a comment and the entry it describes.
281
+ - `--after <entry>` places the new section after the named entry's whole
282
+ block, with its keys and before the next entry's comment. An exact
283
+ section name matches first, then a substring.
284
+
285
+ ```sh
286
+ fireforge register browser/components/hominis/test/unit/test_HominisStoreQuota.js \
287
+ --after test_HominisStore.js
288
+ ```
289
+
290
+ `--create-manifest` scaffolds a missing manifest and wires it into the
291
+ nearest moz.build instead of refusing with "Manifest not found".
292
+
239
293
  ## Queue maintenance
240
294
 
241
295
  Under `fireforge patch`:
242
296
 
243
297
  - `patch compact` closes ordinal gaps, and is range-aware when
244
298
  `patchPolicy.ranges` is configured.
245
- - `patch reorder` moves a patch.
299
+ - `patch reorder` moves a patch. `patch reorder --shift <n>
300
+ --from <order> [--through <order>]` (no patch name) renumbers a run of
301
+ patches up by `n` in one transaction, to open room for inserts.
302
+ `--through` defaults to the end of the run of consecutive occupied orders
303
+ of `--from`'s category. Retired and reserved slots inside the run move
304
+ with it (a moved reservation is named, so its holder can be told). A shift
305
+ that would land on an order a patch or slot holds past the run is refused.
306
+ Under `patchPolicy.allowGaps: false` the opened orders are recorded as
307
+ reservations (owner `patch reorder --shift`), so the queue stays valid,
308
+ and the `export --order` or `split --order` that takes each one fills it.
309
+ The shift checks the patch policy, projects the cross-patch lint
310
+ (refusing only on errors the shift adds), and commits under the patch
311
+ lock, refusing if the plan changed while it waited for confirmation.
312
+ `--map-out <file>` on `reorder` (every mode) and on `split` writes the
313
+ committed old-to-new filename map as a JSON object, so references to the
314
+ renumbered patches can be rewritten from the file:
315
+
316
+ ```sh
317
+ fireforge patch reorder --shift 4 --from 104 --through 128 --map-out shift-map.json --yes
318
+ ```
319
+
246
320
  - `patch split <source> --files <paths...> --name <name>` carves files out of
247
321
  a patch into a new one as a single transaction, including staged-dependency
248
322
  owner rewrites, and supports `--dry-run`. `patch move-files <from> <to>
@@ -253,6 +327,23 @@ Under `fireforge patch`:
253
327
  declaration whose declaring file moved goes with it, and a declaration the
254
328
  move makes unused is dropped. The patch policy, including the gap rule, is
255
329
  checked before any lint runs.
330
+ - `patch move-files` covers a whole split in one call:
331
+ - `--scan-file <path>` (repeatable) adopts a new engine file that no patch
332
+ owns into the destination (`<to>`, or the patch `--create` makes) in
333
+ the same transaction. Both bodies are linted as the transaction leaves
334
+ them, so the source is never judged on a body grown by an adoption the
335
+ move would shrink again. An owned or missing path is refused.
336
+ - `--retire-empty` lets a move into an existing patch take every file of
337
+ the source: the declarations whose `owner` is the source re-point to the
338
+ destination, the source's own declarations go with its files (one that
339
+ becomes self-owned is dropped), and the source is retired as by `patch
340
+ retire`, its order kept as a retired slot. Without the flag, a move that
341
+ would empty the source is refused and names the flag.
342
+ - A per-patch size refusal on either side names a tier change behind it,
343
+ for example `Tier change: the source becomes test tier (it was general
344
+ tier): 9634 lines against 6000.` A source that keeps only test files is
345
+ held to the test tier's ceiling for every line, so move its test files
346
+ out before its last product file.
256
347
  - `patch rename` also supports `--category` and `--order`.
257
348
  - `patch retire <name>` removes a patch like `patch delete` (same dependency
258
349
  refusal, `--dry-run` and confirmation) but keeps its order number as a
@@ -268,7 +359,7 @@ compact` renumbers through retired slots like any other gap.
268
359
  nothing owns it, so `status` would report it unmanaged. Delete those files
269
360
  first, or pass `--delete-created` to remove them as part of the confirmed
270
361
  operation (under the engine lock as well). Files the patch only modifies
271
- keep their edits and become unmanaged, as before.
362
+ keep their edits and become unmanaged.
272
363
  - `patch reserve <order> --category <c> --owner <label> [--reason <text>]`
273
364
  holds an order for a patch a session has agreed to export but has not
274
365
  exported yet. The reservation lives in `patches.json#reserved`. Like a
@@ -289,22 +380,32 @@ hash-object` and `git update-index`. A collision (two rows at one order) or
289
380
  an invalid result is refused before the index is touched. The working copy
290
381
  is never changed and nothing is committed. Name a patch the working copy
291
382
  no longer has by its HEAD filename to stage its removal, and name both
292
- filenames of a renamed patch.
383
+ filenames of a renamed patch. Every manifest write, a
384
+ re-export's row update included, puts every row, changed or not, in the one
385
+ canonical key order that `patch stage` writes, so after a write the working
386
+ copy and the index do not differ on key order alone.
293
387
  - `patch staged-dependency <name> --remove` names the entries to remove by
294
388
  any of `--file`, `--specifier`/`--line`, `--creates` and `--owner`. It needs
295
389
  `--creates`, or `--file` with the matcher. `--creates` alone removes every
296
390
  declaration for that created path, which is the cleanup after a merge
297
391
  deletes it. Registration lines compare whitespace-insensitively, as the
298
- lint does. A selection that matches nothing is refused with exit 8 (it used
299
- to print "no staged registration matched" and exit 0), on a dry run too.
392
+ lint does. A selection that matches nothing is refused with exit 8, on a
393
+ dry run too.
300
394
  - `patch staged-dependency <name> --add --from <json-file>` adds many
301
395
  declarations in one call under one lock hold. The file is a `lint
302
396
  --per-patch --report` document (its findings' `stagedDeclarations` for
303
397
  that patch) or a JSON array of `{ file, specifier | line, creates, owner?,
304
398
  reason?, patch? }` entries; entries naming another patch are skipped, and
305
399
  `--reason` fills entries without one. Every `staged-dependency` call logs
306
- only its delta (`added` / `removed`) in the history; records written
307
- before 0.50.0 carry the whole `before` / `after` lists instead.
400
+ only its delta (`added` / `removed`) in the history. A history record may
401
+ carry the whole `before` / `after` lists instead.
402
+ - `--add` (single or `--from`) on a declaration already present
403
+ (same file, specifier or line, creates and owner) with a different
404
+ `reason` replaces the reason in place, keeping the entry's position in its
405
+ list. The summary counts these apart from additions, as in
406
+ `updated the reason of 87`, and the history logs each as the old entry
407
+ removed and the new one added. An `--add` without a reason never clears
408
+ one.
308
409
 
309
410
  ### Parking a session's edits
310
411
 
@@ -332,7 +433,10 @@ resolves.
332
433
 
333
434
  `lint --per-patch --patches` accepts repeated flags, comma lists, full
334
435
  filenames or stems, bare order numbers, manifest names, category-prefixed
335
- slugs, and bare slugs.
436
+ slugs, and bare slugs. A list is also split on whitespace, so
437
+ a list held in one shell variable (`--patches "$P"` under zsh) selects every
438
+ patch it names. A name that matches no patch is refused with that name, its
439
+ nearest matches and the queue size, not with every patch in the queue.
336
440
 
337
441
  `lint --per-patch --max-warnings 0` is the warning-clean form for a release
338
442
  gate.
@@ -341,8 +445,8 @@ gate.
341
445
  every NOTICE row, for example `NOTICE [large-patch-lines]: 180 across 172
342
446
  patch(es), 150 of them suppressed by lintIgnore (see --report)`. On a green
343
447
  queue those rows are reviewed waivers and under-threshold measurements, and
344
- there can be hundreds of them. The default, `--notices full`, prints them
345
- as before. Errors and warnings always print in full, and the `--report`
448
+ there can be hundreds of them. The default, `--notices full`, prints every
449
+ row. Errors and warnings always print in full, and the `--report`
346
450
  JSON is the same in both modes.
347
451
 
348
452
  `lint --per-patch --report <path>` writes a machine-readable JSON report with
@@ -361,7 +465,7 @@ programmatic API, with the file-size helpers `isTestFile`,
361
465
  `file-too-large` measures in a patch: the JS files it creates, and never a
362
466
  modified file or a new file of another kind. The rule selects its files
363
467
  through that same function, so an audit that uses it cannot drift from the
364
- rule. Since 0.51.0 the API also exports the measurement the size rules
468
+ rule. The API also exports the measurement the size rules
365
469
  themselves take, `measurePatchSize(engineDir, diff, filesAffected, config)`
366
470
  (lines, test-file lines, files and registration files under the configured
367
471
  metric), `isPatchSizeRegistrationFile(file, globs)`, the per-line
@@ -391,8 +495,8 @@ the real type. See
391
495
  [`configuration.md`](configuration.md#patchlint) for the project hooks
392
496
  (`checkJsExtraShim`, `checkJsTestFiles`, `checkJsTestShim`).
393
497
 
394
- For release whitespace checks, use `npm run whitespace:check`. It still
395
- checks source diffs while excluding the diff syntax of generated
498
+ For release whitespace checks, use `npm run whitespace:check`. It checks
499
+ source diffs while excluding the diff syntax of generated
396
500
  `patches/*.patch` files.
397
501
 
398
502
  ### Linting single files
@@ -515,13 +619,14 @@ On a shared engine, `--patches <list>` judges only the named patches:
515
619
  `status` keeps the files each one owns or claims (and, with `--ownership` or
516
620
  the ownership checks, the rows naming it), and `verify` keeps the findings
517
621
  that name it. Patches are named the way `lint --per-patch --patches` names
518
- them (filename, stem, order number, manifest name or slug). A peer's drifted
519
- patch then no longer turns this session's gate red. The limit is the
622
+ them (filename, stem, order number, manifest name or slug; repeated, or
623
+ separated by commas or whitespace). A peer's drifted
624
+ patch then does not turn this session's gate red. The limit is the
520
625
  point: an unmanaged file belongs to no patch, so a scoped run never judges
521
626
  unexported files, this session's included. Run the unscoped gate before a
522
627
  commit that should cover them. `lint --per-patch --patches` keeps every
523
628
  queue finding that names a scoped patch, even on a file the patch no longer
524
- touches (a stale `staged-dependency-unused` declaration, since 0.51.0). `verify --patches` keeps findings about the
629
+ touches (a stale `staged-dependency-unused` declaration). `verify --patches` keeps findings about the
525
630
  queue as a whole (a `patches.json` that does not parse) whatever the scope.
526
631
 
527
632
  `verify` resolves every `resource:///modules/…` and `resource://gre/modules/…`
@@ -586,7 +691,7 @@ as possibly cascading from it, since the replay continues without the
586
691
  rejected patch. Run it after the download, when HEAD is the new source. The
587
692
  header names the previous pin as "from": `firefox.version` in the
588
693
  `fireforge.json` committed at the project's HEAD, the tree the queue was
589
- last applied to (since 0.50.0). The patch stamps follow on their own line
694
+ last applied to. The patch stamps follow on their own line
590
695
  (`Patch stamps: 317 at 153.0esr, 5 at 153.3.0esr`), oldest first and
591
696
  compared as versions rather than strings, because patches untouched since an
592
697
  older hop keep their older stamp. When no committed pin precedes the target
package/docs/run-logs.md CHANGED
@@ -8,16 +8,15 @@
8
8
 
9
9
  ## Why the tool holds the log, not the operator
10
10
 
11
- A run's diagnosis used to exist in exactly one place: the terminal. The
12
- convenient thing to do when output is long is
11
+ Without a log, a run's diagnosis exists in exactly one place: the terminal.
12
+ The convenient thing to do when output is long is
13
13
  `fireforge test … 2>&1 | tail -40`, which keeps the summary, discards the
14
14
  `TEST-UNEXPECTED-FAIL` lines that say what broke, and launders the exit code
15
- as well. Nothing else held the output: `runMachCapture` keeps a 2 MB
15
+ as well. Nothing else holds the output: `runMachCapture` keeps a 2 MB
16
16
  in-memory tail that dies with the process, and the shard retry loop discards
17
17
  earlier attempts outright.
18
18
 
19
- The operator rule ("never pipe a run") was written down three times
20
- downstream and broken after each writing. That is a sign it is not an
19
+ An operator rule ("never pipe a run") gets broken, so this is not an
21
20
  operator problem. The tool is the only party that can make the log survive
22
21
  the mistake.
23
22
 
@@ -25,7 +24,7 @@ the mistake.
25
24
 
26
25
  - **Complete output, never the echo filter's collapsed form.** The artifact
27
26
  exists so that whoever is diagnosing the run can re-read it. Shortening it
28
- would bring back the loss it prevents. The one transformation applied is
27
+ would lose what it is kept for. The one transformation applied is
29
28
  the secret-masking pass below, which changes values but never line
30
29
  structure.
31
30
  - **Secret-shaped values are masked in the file, and only in the file.** The
@@ -94,11 +93,11 @@ away. The refusal's own explanatory text is written into the log, and to
94
93
  stdout, before the verdict line seals stdout.
95
94
 
96
95
  Both halves matter. The verdict seal routes everything the CLI error boundary
97
- renders afterwards to stderr, so a run captured with `> file` used to keep
96
+ renders afterwards to stderr, so a run captured with `> file` would keep
98
97
  `FIREFORGE-VERDICT: FAIL reason=preflight` and nothing else. That rendering
99
98
  also happens after the run log is closed, so the artifact the verdict's own
100
- `log=` key pointed at held only the pre-test build. Writing before the seal
101
- fixes both without giving up the contract: the reason lands on both channels,
99
+ `log=` key points at would hold only the pre-test build. Writing before the
100
+ seal avoids both without giving up the contract: the reason lands on both channels,
102
101
  and the verdict is still the run's last stdout line.
103
102
 
104
103
  ## Where the path is announced
package/docs/testing.md CHANGED
@@ -29,9 +29,7 @@ them:
29
29
  - **Mixed harnesses.** xpcshell and browser/mochitest paths in one request
30
30
  run as two consecutive mach invocations, the xpcshell half first. With
31
31
  `--build`, one pre-test build packages both halves (its coverage claim is
32
- the union), and the second half runs build-less against it. Before 0.50.0
33
- a mixed request was refused, and the documented split lost its coverage
34
- claim to peers' scoped builds between the steps.
32
+ the union), and the second half runs build-less against it.
35
33
  - **`--repeat <n>`** runs the same selection n times. A `--build` builds
36
34
  once, and `--doctor` probes once. Every run is reported, so a flaky suite's
37
35
  rate is visible from one invocation.
@@ -108,11 +106,30 @@ and reports `missed`: that result is the signal to target the file the
108
106
  browser actually loads.
109
107
 
110
108
  A control on compiled source (C/C++, Rust, IDL, WebIDL, IPDL or YAML that no
111
- install manifest ships) can only reach the binary through a build. Without
109
+ install manifest ships) can only reach the binary through a build. A
110
+ header (`.h`, `.hh`, `.hpp`, `.ipdlh`) counts as compiled even when an
111
+ `EXPORTS` install manifest copies it into `dist/include`: that copy only feeds
112
+ other compiles. Without
112
113
  `--build` it is refused before the lock. With `--build` it is applied before
113
114
  the pre-test build, so the binary under test carries it, stays applied for
114
115
  every `--repeat` run, and is restored with a fresh mtime so the next build
115
- recompiles the original. The verdict adds `control-build=compiled`. A
116
+ recompiles the original. That pre-test build is a full
117
+ `mach build`, with a notice naming the control's files: the default
118
+ `mach build faster` compiles nothing, so the control would still test
119
+ the previous binary and report `missed`. The verdict adds
120
+ `control-build=compiled`.
121
+
122
+ FireForge also rebuilds after such a control. Restoring the
123
+ source byte-exact does not unbuild the binary, which would keep the mutation
124
+ until the next full build, so every later run would test the control. After the last
125
+ run's restore, still under the same lock hold, it runs one more full
126
+ `mach build` and records a fresh baseline. The verdict adds
127
+ `control-restore-build=done` (or `=failed`). The baseline the control's build
128
+ writes carries a `controlBuild` stamp (diff file, targets, time applied) that
129
+ only a full build clears: if the run is killed before the restore build, or
130
+ that build fails, the next `fireforge test` refuses with `note=control-built`
131
+ until `fireforge build` succeeds, and a `test --build` escalates to a full
132
+ build. `fireforge build --ui` compiles nothing and keeps the stamp. A
116
133
  build-less control whose packaging is not current is refused before the
117
134
  lock with the advice to add `--build`, instead of ending `inconclusive`
118
135
  after a full queue.
@@ -151,15 +168,18 @@ copy outside `engine/` and `components/`, the control targets the old side.
151
168
  signal pipeline after the harness has shut down and before the held
152
169
  locks are released. A restore already in flight is awaited, not
153
170
  repeated.
171
+ - **The binary too.** A compiled control's binary is rebuilt
172
+ after the restore, under the same hold, and a binary still carrying a
173
+ control is refused (`note=control-built`) rather than tested. See above.
154
174
  - **Even after SIGKILL.** A SIGKILLed FireForge cannot restore, but the
155
175
  journal survives it. Every later `fireforge test` checks for it first,
156
176
  under the lock, before any gate or build: a file still holding the
157
177
  control's bytes is restored, with a notice; any other state refuses with
158
178
  `note=negative-control-pending` rather than guess.
159
179
 
160
- The shorthand `--mutate <file> <old> <new>` that was proposed alongside this
161
- was not added. Commander options take one value, so a three-value option
162
- would have to swallow the positional test paths. A one-hunk diff expresses
180
+ There is no `--mutate <file> <old> <new>` shorthand. Commander options take
181
+ one value, so a three-value option would have to swallow the positional test
182
+ paths. A one-hunk diff expresses
163
183
  the same edit and can be reviewed before it runs.
164
184
 
165
185
  ## Pathless runs must choose a mode
@@ -196,10 +216,30 @@ the run (and in the refusal, when other edits are stale). On a
196
216
  inputs of the sampled test and which lie elsewhere, so a sample whose fixture
197
217
  is unaffected can proceed knowingly under `--allow-stale-build`.
198
218
 
219
+ ### Compiled sources newer than the binary
220
+
221
+ The packaging check above judges packaged files only. Every run
222
+ also compares the compiled sources changed since the last build (C/C++,
223
+ Rust, IDL, WebIDL, IPDL, and YAML such as `StaticPrefList.yaml`) with the
224
+ time the binary under test was last linked: the newer of the launchable
225
+ binary and the `libxul` beside it. Sources under `test`, `tests`, `gtest`,
226
+ `testing` or `taskcluster` directories never reach that binary and are not
227
+ counted. A newer source means the run would test the binary from before the
228
+ edit, where a red reads as a failed fix. Without `--build` the run refuses
229
+ with `note=stale-compiled`, naming the files and the link time, and suggests
230
+ `fireforge build` or `test --build`; `--allow-stale-build` turns the refusal
231
+ into a warning. With `--build` the pre-test build escalates to a full
232
+ `mach build` with a notice, since `mach build faster` compiles nothing.
233
+
234
+ Every run that reaches dispatch also stamps the binary's `BuildID` (from the
235
+ `application.ini` beside it, the `moz:buildID` the browser reports) on its
236
+ verdict line as `build-id=`, so a result can be matched to the build it ran
237
+ against.
238
+
199
239
  ### Packaging coverage
200
240
 
201
241
  `--build-only` packages mixed-harness paths once, and then each harness half
202
- can run without `--build`. A plain mixed `test --build` now does both in one
242
+ can run without `--build`. A plain mixed `test --build` does both in one
203
243
  invocation (see [Several runs under one lock hold](#several-runs-under-one-lock-hold)). Coverage is kept per staged path. Each path of a
204
244
  scoped build records its own staging inputs: its manifest directory (the
205
245
  nearest directory at or above it with a test manifest) plus whatever a
@@ -210,7 +250,7 @@ moved elsewhere in the engine, including a peer's edits and an engine HEAD
210
250
  move that touches none of them. A path whose inputs changed is dropped and
211
251
  named, with the inputs that changed. Changed inputs this build restages are
212
252
  allowed, and a scoped build of one file restages that file's test manifest
213
- too: a manifest that gained an entry for the new file no longer drops the
253
+ too: a manifest that gained an entry for the new file does not drop the
214
254
  claims of its sibling files. A build-less run's packaging gates also run
215
255
  before the lock is requested, so a `coverage-replaced` or `stale-build`
216
256
  refusal arrives before the run queues.
@@ -219,15 +259,23 @@ A scoped build after a full build keeps the claim full while nothing outside
219
259
  its scope moved. Otherwise it carries the full build's anchor forward, and a
220
260
  later build-less run over any path stays covered while that path's own
221
261
  staging inputs are unchanged since the full build. A refusal names the inputs
222
- that did change.
223
-
224
- `--extend-coverage` remains accepted. Retention is now the default, so the
225
- flag only turns a drop into a refusal before the build. Before 0.50.0 a
226
- scoped build without the flag replaced the claim whenever anything outside
227
- its scope had changed, and the flag refused unless engine HEAD, the
228
- mozconfig and every staging input recorded by the previous build were
229
- unchanged. Baselines without complete staging-input fingerprints (older than
230
- 0.49.0) cannot prove anything unchanged and are dropped once.
262
+ that did change, and who wrote the coverage record: its
263
+ invocation, when, and the writing process's pid (marked `still running`
264
+ while it is), `FIREFORGE_SESSION_ID` and invocation id, the id its history
265
+ records carry. `status --test-coverage` shows the pid and session too. A
266
+ peer whose run a scoped build or a test-file edit refused can tell whose
267
+ build to wait for.
268
+
269
+ Retention is the default, so `--extend-coverage` only turns a drop into a
270
+ refusal before the build. A baseline without complete staging-input
271
+ fingerprints cannot prove anything unchanged, so its claims are dropped once.
272
+
273
+ A scoped build records its request paths normalized (no trailing `/`, no
274
+ `./`), as the baseline validator requires. An invalid baseline's refusal
275
+ names the first failing field and its value, and when that field is part of
276
+ the coverage claim it offers rerunning with `--build` as well as
277
+ `fireforge build`. Dirty paths are fingerprinted unquoted, so a non-ASCII
278
+ file name does not make the baseline invalid.
231
279
 
232
280
  ### Furnace components in a pre-test build
233
281
 
@@ -299,22 +347,21 @@ do something better instead: `testing/mochitest/runtests.py` bind-probes
299
347
  the HTTP server, ssltunnel and WebSocket ports before starting its servers,
300
348
  and the Marionette port before launching the browser, moves each busy one to
301
349
  a free port, and routes `mochi.test:8888` to wherever the server landed.
302
- FireForge's own refusal used to fire before that fallback could. In auto
303
- mode the mochitest-server and Marionette gates step aside, with a notice
350
+ In the default mode FireForge's own refusal fires before that fallback can.
351
+ In auto mode the mochitest-server and Marionette gates step aside, with a notice
304
352
  naming the holder, when the holder is not this checkout's own (a sibling
305
353
  checkout's live suite or an unrelated listener). This checkout's own
306
- leftovers are refused exactly as before, and `--kill-stale-marionette`
354
+ leftovers are refused exactly as in the default mode, and `--kill-stale-marionette`
307
355
  still applies to them. Auto mode takes effect only when the engine's
308
356
  `runtests.py` carries the fallback; otherwise a notice says so and the
309
357
  default refusal stays. It never applies to `--doctor`'s Marionette probe,
310
- which launches its own browser on that port. The default (`"fixed"`) is
311
- unchanged.
358
+ which launches its own browser on that port. The default is `"fixed"`.
312
359
 
313
360
  Every suite samples host load and the highest CPU process before and after
314
361
  dispatch. Load at least 4 or a process at least 90% CPU produces a warning;
315
362
  verdicts include `host-load` and, when needed, `host-cpu-warning=true`.
316
363
  Perf runs include `power-source=ac|battery|unknown`; on macOS this comes from
317
- `pmset -g ps`, including charging on AC. Sample JSON gains a `fireforgeHost`
364
+ `pmset -g ps`, including charging on AC. Sample JSON carries a `fireforgeHost`
318
365
  object with start/end states and `powerSourceChanged`. A known power-source
319
366
  transition produces `FAIL reason=inconclusive power-changed=true`.
320
367
  Unknown power state is reported without inventing a transition.
@@ -349,7 +396,7 @@ Furnace-applied components:
349
396
  (untracked in the engine repo, so no install manifest exists for it yet),
350
397
  or when its jar declaration carries a bracketed base-directory prefix,
351
398
  which redirects the install destination away from the default chrome root.
352
- An entry added to an existing `dist/bin` manifest no longer escalates. Any
399
+ An entry added to an existing `dist/bin` manifest does not escalate. Any
353
400
  probe failure (an unreadable manifest, no git) escalates, and the notice
354
401
  names the manifest and the reason.
355
402
 
@@ -361,16 +408,13 @@ carried forward from the previous full build, so a registration made between
361
408
  a `build --ui` and the next `test --build` still triggers the full build
362
409
  once.
363
410
 
364
- The 0.44.0 changelog left an open question here: whether a full build is
365
- really required for an entry added to an existing `dist/bin` `jar.mn`. It was
366
- settled downstream by exactly the experiment that entry asked for. In two
367
- clean runs, a jar-only registration of a new content file was installed by a
368
- plain `fireforge build --ui` (`faster/install_dist_bin_browser` named the
369
- destination, and the file reached `dist/bin` and the `.app` bundle) and then
370
- fetched over `chrome://` by a plain `fireforge test`. That case no longer
371
- escalates. The two halves the experiment did not cover, a new `jar.mn` file
372
- and a non-default install destination, still escalate, for the reason the
373
- original entry gives: relaxing a stale-artifact guard without evidence trades
411
+ An entry added to an existing `dist/bin` `jar.mn` needs no full build: a
412
+ jar-only registration of a new content file is installed by a plain
413
+ `fireforge build --ui` (`faster/install_dist_bin_browser` names the
414
+ destination, and the file reaches `dist/bin` and the `.app` bundle) and is
415
+ then fetched over `chrome://` by a plain `fireforge test`. A new `jar.mn`
416
+ file and a non-default install destination have no such evidence and
417
+ escalate: relaxing a stale-artifact guard without evidence trades
374
418
  a slow build for a silently wrong test.
375
419
 
376
420
  ## Resilience
@@ -465,9 +509,9 @@ FIREFORGE-VERDICT: PASS|FAIL reason=… [note=<class>] [started=<n>/<m> first-un
465
509
  ```
466
510
 
467
511
  The start count reads every line the harness printed, not the last 2 MB of
468
- captured output: a suite printing more than that (a large browser directory,
469
- or any run under `--full-output`) used to report files that ran and passed
470
- as never started. `started-source=` names the evidence. A sharded run also
512
+ captured output, so a suite printing more than that (a large browser
513
+ directory, or any run under `--full-output`) does not report files that ran
514
+ and passed as never started. `started-source=` names the evidence. A sharded run also
471
515
  prints a `FIREFORGE-SHARD:` line per shard and names the shards that did not
472
516
  pass as `failed-shards=`; see [`machine-output.md`](machine-output.md).
473
517
 
@@ -582,10 +626,38 @@ helpers.
582
626
  `--jobs <n>` runs a request's shards on n harness instances inside one lock
583
627
  hold. Each instance has its own profile and its own Marionette port, and
584
628
  auto ports (required) move each instance's mochitest server ports; output
585
- lines carry a `[job <i>]` prefix in the terminal and the run log. With
586
- `--no-shard`, or a single directory argument, the files are split in order
587
- into n contiguous chunks. Parallel browsers raise host load, so measure
588
- before relying on it.
629
+ lines carry a `[job <i>]` prefix in the terminal and the run log. Each
630
+ instance also writes its own mochitest test list
631
+ (`--testrun-manifest-file`): on the harness's shared default
632
+ `_tests/testing/mochitest/tests.json`, the second instance would run the
633
+ first shard's file and pass, or find none. A `--jobs` run always reads each
634
+ dispatch's mozlog raw log, and a dispatch whose raw log starts none of its
635
+ requested files fails with `started=0/<m>` instead of passing on another
636
+ shard's results. With `--no-shard`, or a single directory argument, the
637
+ files are split in order into n contiguous chunks. Parallel browsers raise
638
+ host load, so measure before relying on it.
639
+
640
+ When the caller sets `MOZ_UPLOAD_DIR`, each `--jobs` instance gets its own
641
+ `<MOZ_UPLOAD_DIR>/job-<i>` (created up front) instead of the shared
642
+ directory, where instances would replace each other's fixed-name artifacts
643
+ such as the `modifiedPrefs.json` pref census. The verdict names them as
644
+ `upload-dirs=<dir>,<dir>,...` so a caller can merge per-instance artifacts.
645
+ Without `MOZ_UPLOAD_DIR` nothing changes.
646
+
647
+ A browser `--jobs` run on more than one instance also holds
648
+ placeholder listeners on `127.0.0.1` on the mochitest HTTP, ssltunnel and
649
+ WebSocket ports (8888, 4443, 9988) for the whole parallel hold, after the
650
+ server-port preflight. Desktop mochitest has no flags for these ports, and
651
+ its busy-port probe passes for instances that start together, which then
652
+ race for 8888 and die with `could not start server on port 8888`. With the
653
+ ports held, every instance's probe fails and each falls back to its own
654
+ port; a port that is already busy is left alone. Each `FIREFORGE-SHARD:`
655
+ line carries the ports its instance took as `ports=http:<n>,ssl:<n>,ws:<n>`.
656
+ A harness whose server still fails to start (`could not start server on
657
+ port` or `Timed out while waiting for server startup`, before any test) is
658
+ an infrastructure failure, not `no-tests`: it is retried within the
659
+ `--harness-retries` budget like a harness crash, and reported as
660
+ `reason=server-startup` once the budget is spent.
589
661
 
590
662
  `--shard-size <n>` dispatches n files per mach invocation (mochitest restarts
591
663
  the browser between files), paying the harness start once per chunk instead
@@ -597,7 +669,9 @@ of once per file, and judges each file from mozlog's structured log:
597
669
  run-log line where the dispatch began) from mozlog's structured log, which
598
670
  each dispatch then writes with `--log-raw`, and names it on the verdict as
599
671
  `results=<path>`. A `--log-raw <file>` already forwarded with `--mach-arg` is
600
- read instead. The checks that need no lock run before it is
672
+ read instead. A harness-level record (`test` is `automation.py`,
673
+ such as a hung browser's no-output TIMEOUT) also carries `shardFiles`, the
674
+ shard's dispatched files, and `file` when the shard has exactly one. The checks that need no lock run before it is
601
675
  requested: the launchable binary, the named-file checkJs pass, and the
602
676
  `--wait-browser`/`--wait-port` budgets. The run log is open from the start,
603
677
  so a `reason=lock-timeout` line carries `log=` too.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hominis/fireforge",
3
- "version": "0.51.0",
3
+ "version": "0.53.0",
4
4
  "description": "FireForge — a build tool for customizing Firefox",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",