@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
@@ -21,11 +21,10 @@ uncommitted pin, and the tree is then building a different version than the
21
21
  config claims.
22
22
 
23
23
  `fireforge source set`, `fireforge config <key> <value>` and `furnace init`
24
- edit `fireforge.json` in place (since 0.50.0). Only the values that change
24
+ edit `fireforge.json` in place. Only the values that change
25
25
  are rewritten. Indentation, key order, and objects or arrays kept on one
26
26
  line stay as they are, so a pin move is a diff of the `firefox` block alone.
27
- New keys follow the layout of the object they land in. Before 0.50.0 every
28
- write re-serialized the whole file and expanded every compact section.
27
+ New keys follow the layout of the object they land in.
29
28
 
30
29
  `fireforge doctor`'s "Source pin matches engine" check compares the pin with
31
30
  the engine's committed `browser/config/version.txt`. That file never carries
@@ -93,7 +92,7 @@ Most globals in it are `any`. `Cu` is typed as `CuShim`, the members of
93
92
  `nsIXPCComponents_Utils` (`js/xpconnect/idl/xpccomponents.idl`) with loose
94
93
  parameter and result types, so a call to a member `Cu` does not have (such
95
94
  as `Cu.now()`, which is `ChromeUtils.now()`) fails `typecheck` and per-patch
96
- lint instead of throwing at runtime (since 0.50.0). The same shim backs
95
+ lint instead of throwing at runtime. The same shim backs
97
96
  `fireforge typecheck`.
98
97
 
99
98
  - `patchLint.checkJsExtraShim` adds members to the structured shim globals
@@ -137,7 +136,7 @@ lint instead of throwing at runtime (since 0.50.0). The same shim backs
137
136
  structured `stagedDeclarations`, so a `lint --per-patch --report` file
138
137
  can be applied with one `patch staged-dependency <patch> --add --from
139
138
  <report>` per patch instead of one call per edge. The setting is opt-in because a queue
140
- that predates it can carry many such edges, and a new error would red a
139
+ that was never checked for these carriers can carry many such edges, and a new error would red a
141
140
  `--max-warnings 0` gate on install. The same census is on the public API as
142
141
  `findForwardRegistrations(patchesDir, { scope })`, which defaults to
143
142
  `'extended'`, for a consumer that keeps its own ratchet over these edges.
@@ -168,7 +167,7 @@ lint instead of throwing at runtime (since 0.50.0). The same shim backs
168
167
  what `file-too-large` counts. `'raw'` counts every line, the way `wc -l`
169
168
  does. `'code'` blanks comments with acorn's tokenizer (module first, then
170
169
  script, since chrome subscripts are scripts) and counts the lines left
171
- non-blank, so JSDoc and blank lines stop counting. A file acorn cannot
170
+ non-blank, so JSDoc and blank lines do not count. A file acorn cannot
172
171
  tokenize is counted in raw lines, the upper bound, and a
173
172
  `file-size-unparsed` notice names it. Every message says which unit it
174
173
  counted ("lines" or "code lines"). The counter is on the public API as
@@ -260,7 +259,7 @@ lint instead of throwing at runtime (since 0.50.0). The same shim backs
260
259
  `@font-feature-values`, `@font-palette-values`, `@page` and its margin
261
260
  boxes) are descriptors, not properties, and are not judged.
262
261
 
263
- Since 0.51.0 a scale also takes:
262
+ A scale also takes:
264
263
 
265
264
  - `prefix` as a list (`["--mybrowser-space-", "--mybrowser-gap-"]`) for a
266
265
  scale whose steps are spelled more than one way.
@@ -297,21 +296,37 @@ FireForge version. Diagnostics always use a fresh project
297
296
  program: prior incremental diagnostic state is never restored. Cached and
298
297
  `--no-cache` runs therefore check the same complete file set. `--no-cache`
299
298
  also disables build-info writes. A jsconfig's own `incremental` and
300
- `tsBuildInfoFile` remain ignored.
299
+ `tsBuildInfoFile` are ignored.
301
300
 
302
- `typecheck.litTypes: true` (since 0.51.0, default off) types Lit in the
301
+ `typecheck.litTypes: true` (default off) types Lit in the
303
302
  shim: `LitElement` and the helpers of
304
303
  `chrome://global/content/vendor/lit.all.mjs`, and `MozLitElement` of
305
304
  `chrome://global/content/lit-utils.mjs`. The element classes carry closed
306
305
  member lists, as `Cu` does, so a misspelt member of a widget host is an
307
306
  error rather than `any`. Template helpers and directives stay loose. It is
308
307
  off by default because a reactive property declared only in
309
- `static properties` or `static queries` then needs a constructor
310
- assignment or a JSDoc declaration to be known. A project shim can add
311
- members by merging `interface LitElementShimMembers`; a jsconfig `paths`
312
- entry that maps these URLs to real sources takes precedence.
308
+ `static properties` or `static queries` then needs a declaration to be
309
+ known. The shim declares the global type
310
+ `MozLitElementWith<Extra>`: a widget casts its base to it, and its
311
+ instances carry `Extra` beside MozLitElement's members. A query getter
312
+ cannot be declared any other way: a class field redefines the getter
313
+ MozLitElement installs and throws, and a constructor declaration reads it
314
+ before `renderRoot` exists.
315
+
316
+ ```js
317
+ const Base = /** @type {MozLitElementWith<{ input: HTMLInputElement }>} */ (MozLitElement);
318
+ export class MozSearch extends Base {
319
+ static queries = { input: 'input' };
320
+ }
321
+ ```
322
+
323
+ Without `litTypes` (and under per-patch checkJs, which does not type Lit)
324
+ `MozLitElementWith<Extra>` is `any`, as the base is, so the same source
325
+ checks in both. A project shim can add members by merging
326
+ `interface LitElementShimMembers`; a jsconfig `paths` entry that maps these
327
+ URLs to real sources takes precedence.
313
328
 
314
- Per-patch checkJs gives each test script its own program. Since 0.51.0 a
329
+ Per-patch checkJs gives each test script its own program. A
315
330
  test's `resource:///` and `chrome://` imports (including a
316
331
  `typeof import("…")` cast) resolve to the modules the queue creates up to
317
332
  the test's own patch, so a test of an earlier patch's module is checked
@@ -425,11 +440,11 @@ carries `invocationId` (shared by every record one command writes), `pid`,
425
440
  `ppid`, and `sessionId` when the environment sets `FIREFORGE_SESSION_ID`, so
426
441
  a session sharing the checkout can select its own lines.
427
442
 
428
- Before 0.50.0 the log was `patches/.fireforge-history.jsonl`, inside the
429
- tracked queue. The first append after upgrading moves it to `history.path`;
430
- when both files exist, the old records are appended to the new file rather
431
- than either being overwritten. Setting `history.path` to
432
- `patches/.fireforge-history.jsonl` keeps the old location.
443
+ If a log exists at `patches/.fireforge-history.jsonl`, inside the tracked
444
+ queue, the next append moves it to `history.path`; when both files exist, its
445
+ records are appended to the `history.path` file rather than either being
446
+ overwritten. Setting `history.path` to `patches/.fireforge-history.jsonl`
447
+ keeps the log at that location.
433
448
 
434
449
  ## `buildAudit`
435
450
 
@@ -595,7 +610,7 @@ The dark and variant blocks mirror it and appear in `token show`.
595
610
  - `token-undeclared`: a `var(--<prefix>…)` in the tokens CSS, or in any CSS
596
611
  file a patch in the queue touches, names a token no block of the tokens
597
612
  CSS declares. A property declared in the same file, and the `furnace.json`
598
- `tokenAllowlist` and `runtimeVariables` names, are exempt. Since 0.51.0 a
613
+ `tokenAllowlist` and `runtimeVariables` names, are exempt. A
599
614
  reference with a fallback (`var(--x, 0)`) is optional and not reported,
600
615
  and a token a sibling sheet declares counts as declared. Sheets are
601
616
  siblings when one queue document links both (`<link rel="stylesheet">`,
@@ -617,7 +632,7 @@ non-CI launches print a warning saying so. Exit codes 12 and 13 separate a
617
632
  console regression from a launch failure. See
618
633
  [`exit-codes.md`](exit-codes.md).
619
634
 
620
- A smoke run starts on a fresh temporary profile by default (since 0.48.0).
635
+ A smoke run starts on a fresh temporary profile by default.
621
636
  The step exists to prove what the build does on a clean start. The
622
637
  developer's `obj-*/tmp/profile-default` carries whatever the last session
623
638
  left (a pre-reset database, a restored hand-made applet), and those reds
@@ -633,7 +648,7 @@ owning process is gone.
633
648
 
634
649
  - `--profile <path>` launches with a named profile directory and leaves it
635
650
  in place. Under `--smoke-exit` it replaces the fresh default. Firefox also
636
- gets `-purgecaches` (since 0.50.0), which discards the profile's startup
651
+ gets `-purgecaches`, which discards the profile's startup
637
652
  cache of compiled chrome scripts. That cache is keyed on the build id, and
638
653
  a `build --ui` does not change it, so without the purge a kept profile runs
639
654
  the chrome scripts an earlier build cached. `--no-purge-caches` keeps the
@@ -643,12 +658,11 @@ owning process is gone.
643
658
  profile.
644
659
 
645
660
  A smoke run whose browser exits with a clean status before the window
646
- elapses, without printing any console output, fails with exit 13 (since
647
- 0.50.0). Lines from mach's own log (the timestamped command echo) do not
661
+ elapses, without printing any console output, fails with exit 13.
662
+ Lines from mach's own log (the timestamped command echo) do not
648
663
  count as output. A launch that exits silently never reached a window: a
649
664
  stale `.parentlock`, or a profile whose run of SIGTERM-ended smoke runs
650
- counts as a startup-crash streak (`toolkit.startup.recent_crashes`). Before
651
- 0.50.0 such a run passed as a clean exit.
665
+ counts as a startup-crash streak (`toolkit.startup.recent_crashes`).
652
666
 
653
667
  A plain `run` without either flag keeps mach's `profile-default`. `mach run`'s
654
668
  desktop parser has no `--profile` option (its `--profile/-P` belongs to the
@@ -29,8 +29,7 @@ which error class produces each one.
29
29
 
30
30
  Usage errors that commander detects itself (an unknown command, an unknown
31
31
  or malformed option, a missing required argument) never reach a command
32
- action. Before 0.46.0 commander terminated the process with its own exit 1
33
- and printed no refusal envelope under `--json`. `createProgram` now installs
32
+ action. `createProgram` installs
34
33
  commander's `exitOverride()`, and the entry point maps the resulting
35
34
  `CommanderError` to `INVALID_ARGUMENT` (8) with the standard envelope on
36
35
  stdout. `--help` and `--version` take the same path and still exit 0, and so
@@ -46,10 +45,10 @@ action.
46
45
  change something, and a retry may then help. `INTERNAL_ERROR` means nothing
47
46
  they could change would have helped. Escalate it, do not retry.
48
47
 
49
- **Red, never ran, or thrown away.** These are three different facts, and
50
- before 0.44.0 two of them shared exit 1.
48
+ **Red, never ran, or thrown away.** These are three different facts, each
49
+ with its own code.
51
50
 
52
- - `BUILD_ERROR` (5) is a suite that failed. Since 0.45.0 it is thrown as
51
+ - `BUILD_ERROR` (5) is a suite that failed. It is thrown as
53
52
  `TestFailureError`, which keeps the code but carries test remedies rather
54
53
  than build ones (`--json` reports it as `test-failure`).
55
54
  - `LOCK_TIMEOUT` (15) is a run that never started, because another FireForge
@@ -59,18 +58,16 @@ before 0.44.0 two of them shared exit 1.
59
58
  `engine/` moved underneath it. The code may be perfectly green, so
60
59
  reporting it as red is wrong.
61
60
 
62
- A summary line reading `FAIL - exit 1` beside `FAIL - exit 5` invited
63
- treating opposite facts as the same kind of thing. The codes now separate
64
- them. The `FIREFORGE-VERDICT:` line's `reason=` key carries the same
61
+ Separate codes keep a summary line from showing opposite facts as the same
62
+ kind of failure, such as `FAIL - exit 1` beside `FAIL - exit 5`. The `FIREFORGE-VERDICT:` line's `reason=` key carries the same
65
63
  distinction for humans.
66
64
 
67
65
  `LockContentionError` backs every lock in FireForge (engine session, patch
68
66
  directory, build, furnace), so all four exit 15. That is intentional: the
69
67
  fact is the same whichever lock was contended.
70
68
 
71
- **Interrupted or declined.** Before 0.44.0 a Ctrl+C and a deliberate "no" at
72
- a confirmation both returned the same value and exited 0, so a script could
73
- not tell them apart. They are now distinct: an interrupt raises
69
+ **Interrupted or declined.** A Ctrl+C and a deliberate "no" at a
70
+ confirmation are distinct, so a script can tell them apart: an interrupt raises
74
71
  `CancellationError` and exits 130, and a declined confirmation returns
75
72
  normally and exits 0, because the run did what it was asked and the answer
76
73
  was no.
package/docs/furnace.md CHANGED
@@ -115,7 +115,7 @@ warning asking for the entry to be deleted. A run over one component judges
115
115
  only that component's entries. `deploy` honours the same entries when it
116
116
  validates, but leaves the staleness report to `validate`.
117
117
 
118
- An entry never suppresses an error (since 0.51.0): an error blocks apply,
118
+ An entry never suppresses an error: an error blocks apply,
119
119
  and a review record cannot make a broken component deployable. The error
120
120
  stays counted, and the entry is reported as a
121
121
  `validation-exception-on-error` error until the finding is fixed and the
@@ -136,12 +136,30 @@ entries cover all of them:
136
136
  }
137
137
  ```
138
138
 
139
+ An optional `file` narrows an entry the same way to findings
140
+ in one module of the component, by file name. `hardcoded-text` names the
141
+ modules it found text in, so a vendored bundle whose bytes are pinned (and
142
+ so cannot carry a `furnace-ignore` comment) can be accepted while the
143
+ authored modules beside it keep the check. An entry names a `tag` or a
144
+ `file`, not both:
145
+
146
+ ```json
147
+ {
148
+ "component": "moz-editor",
149
+ "check": "hardcoded-text",
150
+ "file": "moz-editor-codemirror.mjs",
151
+ "reason": "The vendored bundle's width probe is never rendered."
152
+ }
153
+ ```
154
+
139
155
  Accessibility checks judge the composed component: the host
140
156
  `<tag>.mjs` and its companion modules (`<tag>-*.mjs`, tests and stories
141
157
  left out) read as one source. Moving a template between the component's
142
- modules therefore no longer changes its findings: a keyboard handler in a
158
+ modules therefore does not change its findings: a keyboard handler in a
143
159
  companion covers an `@click` in the host, and an `@click` in a companion
144
- is judged too. Toolkit's `panel-item` counts as natively
160
+ is judged too. `hardcoded-text` judges each module on its own, so a
161
+ `// furnace-ignore: hardcoded-text` comment covers the file it is in, not
162
+ its siblings. Toolkit's `panel-item` counts as natively
145
163
  keyboard-activated, beside `button`, form controls and the `moz-*` buttons.
146
164
 
147
165
  `minComponents` sets the fewest components a full `furnace validate` must
@@ -25,7 +25,7 @@ command, read the decision table at the bottom first.
25
25
  exception: it writes nothing, runs its git plumbing on a private index
26
26
  (so even the `--intent-to-add` staging an untracked binary needs never
27
27
  reaches the shared `.git/index`), and serializes only against other dry
28
- runs, so a preview no longer queues behind a peer's build or test run.
28
+ runs, so a preview does not queue behind a peer's build or test run.
29
29
 
30
30
  3. **A furnace mutation that does not finish restores the engine or says
31
31
  so.** Both a signal (SIGINT or SIGTERM) and a thrown error trigger a
@@ -33,14 +33,11 @@ command, read the decision table at the bottom first.
33
33
  `pendingRepair` marker blocks all further furnace mutations until
34
34
  `fireforge doctor --repair-furnace` reconciles.
35
35
 
36
- The thrown-error half landed in 0.43.0. Before it, `runFurnaceMutation`'s
37
- `finally` only deregistered the operation, and because it also set
38
- `completed`, a body that threw removed itself from the signal handler's
39
- view as well. A throw outside a body's own catch therefore left the
40
- checkout torn with no marker. `applyAllComponents` was the live instance:
41
- it rolls back on collected `result.errors` and `stepErrors`, but a throw
42
- from its pre-loop or post-loop work escaped both. The wrapper now runs
43
- cleanups, restores the journal, and rethrows the original error. A
36
+ On a thrown error `runFurnaceMutation` runs cleanups, restores the
37
+ journal, and rethrows the original error, so a throw outside a body's own
38
+ catch does not leave the checkout torn with no marker. `applyAllComponents`
39
+ rolls back on collected `result.errors` and `stepErrors`, but a throw from
40
+ its pre-loop or post-loop work escapes both and relies on the wrapper. A
44
41
  rollback failure becomes a marker plus a warning, never a replacement
45
42
  error.
46
43
 
@@ -70,17 +67,15 @@ command, read the decision table at the bottom first.
70
67
  refusal on structural conflicts (`--force-unsafe` to override), and a
71
68
  JSONL history entry appended only after the mutation succeeded. This
72
69
  covers metadata mutations too: `patch tier` and `patch lint-ignore` route
73
- through `confirmDestructive`. They used to accept `--yes` without ever
74
- prompting, so the flag only showed up in the history record. Enforced by
70
+ through `confirmDestructive`. Enforced by
75
71
  `patch-tier-and-lint-ignore.integration.test.ts`.
76
72
 
77
73
  The history log is write-only: FireForge appends to it and no command
78
74
  reads it back, so nothing depends on its contents or location. It lives
79
75
  at `.fireforge/history.jsonl` (or `fireforge.json#history.path`), outside
80
- the tracked patch queue. Before 0.50.0 it was
81
- `patches/.fireforge-history.jsonl`; the first append after upgrading
82
- moves that file, and appends its records when both exist rather than
83
- overwriting either. Each record carries `invocationId` (one per FireForge
76
+ the tracked patch queue. If `patches/.fireforge-history.jsonl` exists,
77
+ the next append moves it there, and appends its records when both exist
78
+ rather than overwriting either. Each record carries `invocationId` (one per FireForge
84
79
  process), `pid`, `ppid`, and `sessionId` when `FIREFORGE_SESSION_ID` is
85
80
  set, so a session sharing the checkout can select its own lines.
86
81
  Enforced by `history-log-file.test.ts`.
@@ -117,14 +112,14 @@ command, read the decision table at the bottom first.
117
112
  and `tree remove` refuses while a live PID holds a tree lock and checks
118
113
  that its `rm -rf` target is contained in the tree path.
119
114
 
120
- In-tree `test` (0.41.0) is the one conditional carve-out, and it is
115
+ In-tree `test` is the one conditional carve-out, and it is
121
116
  marker-gated. mach objdirs embed absolute `topsrcdir` and `topobjdir`
122
117
  paths, so `tree create --with-objdir` rewrites the cloned objdir's
123
118
  `mozinfo.json` to the tree using the same safe-relocation rewriter that
124
119
  `build --rewrite-mozinfo` uses, scrubs the cloned `_virtualenvs` (they
125
120
  carry primary shebangs, and mach rebuilds them in-tree), and runs
126
121
  `mach configure` inside the tree so the configure-generated root files are
127
- regenerated against the tree's paths rather than keeping the primary's. It
122
+ regenerated against the tree's paths rather than keeping the primary's.
128
123
  Before configure, relocation rewrites old-source symlinks, `.pp`/`.d`,
129
124
  FasterMake and Cargo fingerprint metadata, and removes copied
130
125
  `config.statusd/*/config.track` inventories. Those inventories contain
@@ -151,14 +146,14 @@ command, read the decision table at the bottom first.
151
146
  **Both halves of that enforcement fail closed on an unreadable state,
152
147
  because "we cannot tell" is not "there is nothing there."** A marker that
153
148
  exists but does not parse reports `corrupt`, not `absent`, and refuses the
154
- command. Reading it as absent granted a snapshot the full mutating command
155
- set on the strength of a file that failed to parse. A lock directory whose
149
+ command. Reading it as absent would grant a snapshot the full mutating
150
+ command set on the strength of a file that failed to parse. A lock directory whose
156
151
  owner cannot be identified reports `unknown`, not free, and refuses
157
- removal. Since 0.45.1 `withFileLock` treats its owner-record write as
152
+ removal. `withFileLock` treats its owner-record write as
158
153
  fatal (`writeLockOwner` in `file-lock-owner.ts` removes the just-created
159
154
  directory and throws a `GeneralError` if the record cannot be written or
160
- does not read back as written), so an owner-less lock is only ever left by
161
- a pre-0.45.1 holder or by a crash inside that write. It is still refused,
155
+ does not read back as written), so an owner-less lock is left only by a
156
+ crash inside that write or by a holder that never wrote a record. It is refused,
162
157
  for the same reason. The one exception is a lock directory that vanished
163
158
  between the probe and the owner read: that is a clean release, and reads
164
159
  as free.
@@ -168,7 +163,7 @@ command, read the decision table at the bottom first.
168
163
  refusal spares the read-only commands that are unconditionally allowed
169
164
  (`status`, `lint`, `typecheck`, `verify`): their verdict never consults
170
165
  marker fields, so an unreadable marker cannot change their answer, and
171
- blocking them left the operator unable to diagnose the very tree that
166
+ blocking them would leave the operator unable to diagnose the very tree that
172
167
  needs inspecting. `conditional` verdicts can write, and stay refused.
173
168
  Enforced by `tree-guard.test.ts` (verdict drift gate and corrupt-marker
174
169
  refusal), `tree-store.integration.test.ts` (marker tri-state,
@@ -208,7 +203,7 @@ command, read the decision table at the bottom first.
208
203
  `GIT_INDEX_FILE` from the repository's own index so that every refresh
209
204
  lands there and is discarded. `typecheck` additionally ignores a
210
205
  jsconfig's own `incremental` and `tsBuildInfoFile`, so no sidecar is
211
- written under `engine/`. Its incremental build info (0.48.0) lives in the
206
+ written under `engine/`. Its incremental build info lives in the
212
207
  project's `.fireforge/typecheck/`, outside the engine. The scope fails open, meaning an unresolvable git dir
213
208
  simply does not install it, and it is not reentrant. This exists so that
214
209
  invariant 8 stays evidential: a concurrent `fireforge test` must not be
@@ -221,16 +216,14 @@ command, read the decision table at the bottom first.
221
216
  in `<lock>.waiters/` named `<startedAtMs>-<pid>-<uuid>` and deregisters in
222
217
  a `finally` that covers both outcomes: acquired the lock, or timed out.
223
218
  A newcomer that finds live tickets takes one before trying the lock at
224
- all. Since 0.50.0 the tickets decide the grant: only the waiter with no
219
+ all. The tickets decide the grant: only the waiter with no
225
220
  live ticket ahead of it (arrival time, then ticket name) attempts the
226
- lock, so "You are next" is a promise. Before, every waiter raced `mkdir`
227
- and a waiter whose poll had backed off lost each free moment to later
228
- arrivals. Because a ticket now blocks the queue, a dead one must not: a
221
+ lock, so "You are next" is a promise. Because a ticket blocks the queue, a dead one must not: a
229
222
  ticket whose PID is gone (or reused, where the start tick can tell) is
230
223
  skipped and removed, and one its owner has not refreshed for 30 s (every
231
224
  waiter rewrites its ticket every 5 s) is skipped, which covers a stopped
232
225
  waiter and PID reuse elsewhere. A registry that cannot be written falls
233
- back to the old unordered contention rather than failing an acquisition.
226
+ back to unordered contention rather than failing an acquisition.
234
227
  This applies to every `withFileLock` lock: the engine-session, patch,
235
228
  furnace and build locks among them. `fireforge status --lock` and the
236
229
  `--wait-lock` progress lines read it, and the lines name the ordering
@@ -258,7 +251,7 @@ command, read the decision table at the bottom first.
258
251
  Enforced by `patch-manifest-repair.test.ts` (real temp queues) and the
259
252
  repair cases in `doctor.test.ts`.
260
253
 
261
- ## Lock release (0.44.0)
254
+ ## Lock release
262
255
 
263
256
  `releaseLockIfOwned(lockPath)` in `src/core/file-lock.ts` is the
264
257
  ownership-checked removal for callers that hold no acquisition token.
@@ -266,13 +259,12 @@ Currently that is the signal-time furnace sweeper
266
259
  (`forceReleaseFurnaceLocksForActiveOperations`). It verifies the PID before
267
260
  removing, which is the half of `releaseLock`'s guard a sweeper can still
268
261
  perform: the sweeper knows which lock paths its own operations opened, but
269
- not the per-acquisition UUID they minted. The bare `rm` it replaced could
262
+ not the per-acquisition UUID they minted. A bare `rm` could
270
263
  delete a lock that a different process had acquired in the window between our
271
264
  operation dying and the sweep running.
272
265
 
273
266
  `completeJournalRollback` in `src/core/furnace-operation.ts` owns the
274
- mutation-body rollback sequence that eight furnace commands previously
275
- duplicated verbatim. The ordering is the invariant: `markRolledBack()` runs
267
+ mutation-body rollback sequence that eight furnace commands share. The ordering is the invariant: `markRolledBack()` runs
276
268
  before the restore, so a restore that itself fails does not leave the
277
269
  lifecycle wrapper replaying the same journal on the way out, and the original
278
270
  error is rethrown unless the rollback is what failed.
@@ -297,7 +289,7 @@ seconds while waiting, so a holder dying mid-wait cannot strand waiters until
297
289
  The owner-record write is fatal. A failed or unverifiable write releases the
298
290
  just-created lock and throws a `GeneralError` instead of running the
299
291
  operation. Without that, a lock with no readable owner is reaped by the age
300
- heuristic after 5 minutes, which for a multi-hour build meant a second holder
292
+ heuristic after 5 minutes, which for a multi-hour build would mean a second holder
301
293
  inside the critical section. Covered by `file-lock.test.ts`, "owner record
302
294
  write at acquisition": write failure, corrupt read-back, recorded acquisition
303
295
  time.
@@ -312,8 +304,7 @@ other layer.
312
304
  Wait ergonomics are opt-in: `pollMaxMs` (the poll interval doubles from
313
305
  `pollMs` up to the cap) and `waitProgressMs` with `onWaitProgress` (a periodic
314
306
  progress callback carrying the holder's PID, liveness, and owner-metadata
315
- lines from the pid file). All three are inert when unset, so existing callers
316
- keep byte-identical behavior. Within one poll iteration a due progress probe
307
+ lines from the pid file). All three are inert when unset. Within one poll iteration a due progress probe
317
308
  runs before the deadline check, so a wait that times out has always reported
318
309
  the holder at least once when its budget exceeds `waitProgressMs`, however
319
310
  slow the host (`file-lock.test.ts`, "budget shorter than one iteration").
@@ -321,8 +312,8 @@ slow the host (`file-lock.test.ts`, "budget shorter than one iteration").
321
312
  ### `src/core/file-lock-owner.ts`
322
313
 
323
314
  The owner record (the `pid` file) itself: its format (PID, token,
324
- `acquired-at-ms=` and `start-tick=` lines, plus free-form metadata, and older
325
- records without the mechanical lines still parse), the write-and-verify
315
+ `acquired-at-ms=` and `start-tick=` lines, plus free-form metadata; a record
316
+ without the mechanical lines parses too), the write-and-verify
326
317
  contract described above (a read-back that errors is retried before it counts
327
318
  as a failed write, while a missing or mismatching record fails immediately),
328
319
  and PID-reuse liveness.
@@ -333,13 +324,13 @@ re-verification, status snapshot and timeout message uses. Beyond
333
324
  tick (`/proc/self/stat` field 22), and the probe reads the candidate's tick
334
325
  from `/proc/<pid>/stat`. A different tick means a different process, so the
335
326
  lock is stale. A crashed build's PID handed to a long-lived daemon therefore
336
- no longer keeps the dead lock honoured until the waiter's timeout. It uses
327
+ does not keep the dead lock honoured until the waiter's timeout. It uses
337
328
  ticks rather than wall-clock times because `btime` moves with clock steps, and
338
329
  a wall-clock comparison would reap a live holder after NTP steps the clock
339
330
  forward. macOS and Windows have no cheap start-time source (a `ps` spawn per
340
331
  poll is not worth it) and keep the PID-only answer, and so do records without
341
- a start tick. Enforced by `file-lock-owner.test.ts`: backward-compatible
342
- parsing, forged mechanical lines dropped, transient read-back error retried
332
+ a start tick. Enforced by `file-lock-owner.test.ts`: parsing of records without the
333
+ mechanical lines, forged mechanical lines dropped, transient read-back error retried
343
334
  versus persistent error versus mismatch, fake-procfs tick parsing, reused-PID
344
335
  versus genuine-holder verdicts, stepped-clock immunity, and on Linux a
345
336
  real-procfs `withFileLock` reap of a live-PID stale record.
@@ -360,9 +351,9 @@ log or the engine-session lock shows one in the window (`re-export-purity-scope.
360
351
 
361
352
  Contention fails fast after about a second by default, with the fixed message
362
353
  "Another FireForge engine-mutating command is already running. Wait for it to
363
- finish, then retry `<command>`." That message contract is unchanged, and it
364
- also closes the bounded wait. Each of those commands accepts
365
- `--wait-lock [seconds]` (bare flag means 60, valid 1 to 3600) for a bounded
354
+ finish, then retry `<command>`." The same message also closes the bounded
355
+ wait. Each of those commands accepts
356
+ `--wait-lock [seconds]` (bare flag means 60, valid 1 to 86400) for a bounded
366
357
  operator wait instead: polling backs off exponentially from 100 ms to 2 s, and
367
358
  roughly every 5 s a progress line identifies the holder from the lock's
368
359
  owner-metadata lines, in the form `Waiting for the FireForge engine lock held
@@ -432,7 +423,7 @@ hold it.
432
423
  The destructive-operation contract: `confirmDestructive` (summary, prompt,
433
424
  `--yes`, `--dry-run`, conflict refusal, `--force-unsafe`) and the
434
425
  append-only history log (`src/core/history-log-file.ts` resolves its path,
435
- migrates a legacy `patches/.fireforge-history.jsonl`, and stamps each
426
+ moves a `patches/.fireforge-history.jsonl` it finds to that path, and stamps each
436
427
  record's invocation and session).
437
428
 
438
429
  ### `src/core/tree-store.ts`
@@ -2,12 +2,9 @@
2
2
 
3
3
  # Machine-readable output contract (internal)
4
4
 
5
- The `--json` surface is a contract with scripted consumers. Before 0.44.0 it
6
- was four unrelated shapes: `status --json` and `tree list --json` shared a
7
- `schemaVersion: 1` envelope but disagreed on failure, `test` emitted a
8
- `FIREFORGE-VERDICT:` line, and `lint --per-patch --report` wrote a versioned
9
- file that nothing ever reads back. This file states what a `--json` command
10
- has to do, so the next one added does not invent a fifth shape.
5
+ The `--json` surface is a contract with scripted consumers. This file states
6
+ what a `--json` command has to do, so the next one added does not invent a
7
+ shape of its own.
11
8
 
12
9
  ## The rules
13
10
 
@@ -56,11 +53,11 @@ if (options.json === true) {
56
53
  | Surface | Shape | Notes |
57
54
  | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58
55
  | `status --json` | `{ schemaVersion: 1, … }` | Error envelope on three engine preconditions |
59
- | `status --json` under `--check` | adds `check: { enabled, failOn, allow?, failOnUnknown?, failed, offenders }` | Since 0.50.0 on the full payload too, not only `--summary`. `allow` and `failOnUnknown` appear only when set. An offender failing only through `--fail-on-unknown` carries `unnamed: true`. `--patches` adds `scope: { patches }` |
56
+ | `status --json` under `--check` | adds `check: { enabled, failOn, allow?, failOnUnknown?, failed, offenders }` | On the full payload too, not only `--summary`. `allow` and `failOnUnknown` appear only when set. An offender failing only through `--fail-on-unknown` carries `unnamed: true`. `--patches` adds `scope: { patches }` |
60
57
  | `furnace validate --json` | `{ schemaVersion: 1, passed, summary, maxWarnings?, components, issues, excepted, staleExceptions }` | `issues` counts after `validationExceptions`, stale-exception warnings included. `excepted` carries each suppressed finding with its `reason` |
61
58
  | `check --json` | `{ schemaVersion: 1, verdict, reason?, summary, steps }` | One entry per step: `name`, `kind`, `command`, `status` (`pass`/`fail`/`skipped`), `exitCode`, `durationMs`, `detail`, and on failure `outputTail` (`timedOut` when killed at its budget). No verdict line under `--json` |
62
59
  | `check` verdict line | `FIREFORGE-VERDICT: PASS\|FAIL [reason=step-failures\|lock-timeout\|skipped-steps failed=<steps>] steps=<n> skipped=<n> [skipped-steps=<steps>]` | Not JSON. The last stdout line, after the step table, sealed like `test`'s. Its `reason=` set is its own, not `test`'s |
63
- | `tree list --json` | `{ schemaVersion: 1, trees }` | Error envelope added in 0.44.0 |
60
+ | `tree list --json` | `{ schemaVersion: 1, trees }` | Error envelope on failure |
64
61
  | `token list --json` | `{ schemaVersion: 1, tokensCssPath, categories }` | Categories in file order, each with its tokens (`name`, `line`, `value`) |
65
62
  | `token show --json` | `{ schemaVersion: 1, name, category, declarations }` | One entry per declaring block, with its selector trail and line |
66
63
  | `token lint --json` | `{ schemaVersion: 1, tokensCssPath, tokenPrefix, filesScanned, findings }` | Each finding has `check`, `file`, `line`, `token`, `message`. The document is written even when findings exist; the exit is then non-zero, as with `status --check --json` |
@@ -80,7 +77,7 @@ it. Follow the rules above rather than inventing a shape.
80
77
 
81
78
  The run logs behind `log=` are specified in [`run-logs.md`](run-logs.md).
82
79
 
83
- Additive keys since 0.51.0 outside `test`:
80
+ Additive keys outside `test`:
84
81
 
85
82
  - `check --json`: a skipped step carries `skipKind`, `config` when
86
83
  `check.skip` names it and `unavailable` when the project has nothing for
@@ -92,6 +89,8 @@ Additive keys since 0.51.0 outside `test`:
92
89
  `@click`). An exception that matched an error adds a
93
90
  `validation-exception-on-error` error, and a full run under
94
91
  `furnace.json#minComponents` a `component-floor` error on `furnace.json`.
92
+ - `furnace validate --json`: a `hardcoded-text` finding
93
+ carries `files`, the component's module file names it was found in.
95
94
 
96
95
  ## Versioning
97
96
 
@@ -108,6 +107,7 @@ moved.
108
107
  | Reason | Meaning |
109
108
  | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
110
109
  | `crash` | The harness or the browser died, so there is no trustworthy suite result. |
110
+ | `server-startup` | A mochitest server never came up (`could not start server on port`, `Timed out while waiting for server startup`) before any test ran. An infrastructure failure, retried like `crash` within `--harness-retries`; this reason is what is left once the budget is spent. |
111
111
  | `harness-arguments` | Profile or other harness argument setup failed before any tests could run; a rebuild is not the repair. |
112
112
  | `no-tests` | The run dispatched but nothing ran. |
113
113
  | `test-failures` | The suite ran and reported unexpected results. |
@@ -124,15 +124,19 @@ developer browser holding the objdir. An unattended reader whose pipe kept
124
124
  only the verdict line could not tell those apart, so a preflight refusal that
125
125
  can classify itself also emits `note=<class>`.
126
126
 
127
- `note=` is additive, like `log=`, `signal=` and `shards=`. `reason=` is
128
- unchanged and still comes from the closed set above, so a consumer that
127
+ `note=` is additive, like `log=`, `signal=` and `shards=`. `reason=` always
128
+ comes from the closed set above, so a consumer that
129
129
  tokenises `key=value` pairs is unaffected, and a gate that names no class
130
130
  emits no `note=` at all. The classes are not a closed set and may grow, so
131
131
  consumers should treat an unrecognised one as opaque. Current values:
132
132
  `browser-busy`, `stale-browser`, `stale-browser-kill-failed`, `marionette-port-busy`, `mochitest-port-busy`,
133
133
  `coverage-replaced`, `stale-build`, `stale-components`, `no-launchable-binary`, `negative-control` (the
134
134
  control diff was refused), `negative-control-pending` (an interrupted run's
135
- control could not be undone safely). `note=negative-control-restore` rides a
135
+ control could not be undone safely). Also `stale-compiled`
136
+ (compiled sources changed after the binary under test was linked) and
137
+ `control-built` (the binary still carries a compiled negative control whose
138
+ restore build never ran or failed). An `invalid-build-baseline` refusal
139
+ names the first failing field and its value. `note=negative-control-restore` rides a
136
140
  `reason=inconclusive` line whose control could not be restored byte-exact.
137
141
 
138
142
  ### Multi-run invocations: `FIREFORGE-RUN:`, `runs=` and `control=`
@@ -151,7 +155,7 @@ A `FIREFORGE-RUN:` line is never a verdict: the invocation still ends with
151
155
  exactly one `FIREFORGE-VERDICT:` line. With more than one planned run it
152
156
  carries `runs=<passed>/<planned>` and no count keys; a failed aggregate takes
153
157
  the most structural reason among its runs (`inconclusive`, `preflight`,
154
- `crash`, `harness-arguments`, `no-tests`, `test-failures`) and that run's
158
+ `crash`, `server-startup`, `harness-arguments`, `no-tests`, `test-failures`) and that run's
155
159
  `note=`. A run that never started (the invocation stopped at a refusal)
156
160
  counts toward `<planned>` but not `<passed>`.
157
161
 
@@ -183,7 +187,7 @@ A sharded run prints one `FIREFORGE-SHARD:` line per finished shard, before
183
187
  the verdict line:
184
188
 
185
189
  ```
186
- FIREFORGE-SHARD: PASS|FAIL [reason=…] [checks=<n>] [unexpected=<n>] shard=<i>/<n> path=<label> [failed-files=<path>,…]
190
+ FIREFORGE-SHARD: PASS|FAIL [reason=…] [checks=<n>] [unexpected=<n>] shard=<i>/<n> path=<label> [failed-files=<path>,…] [ports=http:<n>,ssl:<n>,ws:<n>]
187
191
  ```
188
192
 
189
193
  `path=` is the shard's path argument (whitespace replaced by `_`), or a
@@ -193,13 +197,31 @@ aggregate verdict appends `failed-shards=<label>,…` after `shards=` when any
193
197
  shard did not pass; under `--shard-size` `shards=` counts files and
194
198
  `failed-shards=` names files. Both are absent on a green run.
195
199
 
200
+ `ports=` names the mochitest HTTP server, ssltunnel and
201
+ WebSocket ports the shard's harness reported (its "falling back to port"
202
+ lines), in that order and only those it reported; it is absent when the
203
+ harness reported none (an xpcshell shard, or a harness that died before its
204
+ servers). A shard whose server never started says `reason=server-startup`,
205
+ and the aggregate verdict does too when every crashed shard failed that way.
206
+
196
207
  ### Additive keys of parallel and per-file runs
197
208
 
198
209
  `jobs=<n>` marks a `--jobs` run, `lock=shared` a run that held the engine
199
- session lock shared, and `results=<path>` names the `--results` document. A
210
+ session lock shared, and `results=<path>` names the `--results` document.
211
+ A `--jobs` run whose caller set `MOZ_UPLOAD_DIR` appends
212
+ `upload-dirs=<dir>,…`, the per-instance upload directories
213
+ (`<MOZ_UPLOAD_DIR>/job-<i>`, one per instance that ran), and a `--results`
214
+ record whose `test` is `automation.py` (a harness-level result) carries
215
+ `shardFiles`, plus `file` when its shard has exactly one file. A
200
216
  run whose engine changed mid-run only in files the running browser cannot
201
217
  see keeps its verdict and appends `drift-tolerated=<n>`. A negative control
202
- on compiled source appends `control-build=compiled`.
218
+ on compiled source appends `control-build=compiled`, and
219
+ `control-restore-build=done|failed` for the full build that recompiles the
220
+ restored source after the last run. Every run that reaches
221
+ dispatch appends `build-id=<BuildID>`, read from the launchable build's
222
+ `application.ini` (absent when that file has no `BuildID`). A raw log
223
+ that starts none of a dispatch's requested files counts as
224
+ `started=0/<m> ... started-source=raw`.
203
225
 
204
226
  ### The additive `shuffle=` key
205
227
 
@@ -219,9 +241,8 @@ preflight, survivors of an earlier run in this objdir under `--reap-orphans`
219
241
  or `test.reapOrphans: "reap"`; at teardown, helpers this run's harness
220
242
  launched (httpd, websocket server, ssltunnel, process bridge, the browser,
221
243
  `moz-http2`) that were still alive after mach exited. Summed across the census, every
222
- retry attempt and every shard. Absent when nothing was reaped, so a green on
223
- a quiet machine prints the line it always did, and a green after a reap is
224
- distinguishable from it. A run ended by SIGTERM or by the parent-exit
244
+ retry attempt and every shard. Absent when nothing was reaped, so a green after a reap is
245
+ distinguishable from a green on a quiet machine. A run ended by SIGTERM or by the parent-exit
225
246
  watchdog writes its verdict before the teardown reap runs, so that line never
226
247
  carries the key; the reap is reported on stderr instead.
227
248
 
@@ -239,10 +260,10 @@ and measured CPU. These diagnostics annotate a test failure without changing
239
260
  its classifier reason.
240
261
 
241
262
  Perf runs append `power-source=ac|battery|unknown`. On macOS the source is
242
- read from `pmset -g ps`, so charging on AC is correctly `ac`. A known source
263
+ read from `pmset -g ps`, so charging on AC reads `ac`. A known source
243
264
  transition appends `power-changed=true` and refuses the run as
244
- `reason=inconclusive`. The producer's sample JSON retains its fields and
245
- gains `fireforgeHost: { start, end, powerSourceChanged }`, where each host
265
+ `reason=inconclusive`. The producer's sample JSON carries an additional
266
+ `fireforgeHost: { start, end, powerSourceChanged }` object, where each host
246
267
  state includes `load`, `power` and an optional `topProcess` with CPU and
247
268
  command. Consumers should tolerate this additive object. Unknown power
248
269
  state is visible but never treated as a proven transition.