@hominis/fireforge 0.52.0 → 0.54.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 (248) hide show
  1. package/CHANGELOG.md +45 -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-remove.d.ts +26 -4
  54. package/dist/src/commands/patch/staged-dependency-remove.js +60 -23
  55. package/dist/src/commands/patch/staged-dependency.js +35 -24
  56. package/dist/src/commands/patch/tier.js +2 -2
  57. package/dist/src/commands/patch-rename-map.d.ts +14 -3
  58. package/dist/src/commands/patch-rename-map.js +21 -3
  59. package/dist/src/commands/re-export-adjacent.d.ts +4 -5
  60. package/dist/src/commands/re-export-lint.d.ts +4 -3
  61. package/dist/src/commands/re-export-lint.js +4 -3
  62. package/dist/src/commands/re-export-options.js +8 -2
  63. package/dist/src/commands/re-export-preflight.js +1 -2
  64. package/dist/src/commands/re-export-purity-scope.d.ts +1 -1
  65. package/dist/src/commands/re-export-purity-scope.js +13 -7
  66. package/dist/src/commands/re-export-retired-declarations.d.ts +22 -0
  67. package/dist/src/commands/re-export-retired-declarations.js +113 -0
  68. package/dist/src/commands/re-export-single.js +2 -2
  69. package/dist/src/commands/re-export-stale-registrations.d.ts +27 -6
  70. package/dist/src/commands/re-export-stale-registrations.js +65 -21
  71. package/dist/src/commands/re-export.js +4 -0
  72. package/dist/src/commands/rebase/continue.js +3 -3
  73. package/dist/src/commands/register.js +5 -4
  74. package/dist/src/commands/run.js +11 -4
  75. package/dist/src/commands/status.js +1 -1
  76. package/dist/src/commands/test-build-furnace.d.ts +1 -2
  77. package/dist/src/commands/test-build-furnace.js +1 -2
  78. package/dist/src/commands/test-build-phase.d.ts +8 -0
  79. package/dist/src/commands/test-build-phase.js +42 -7
  80. package/dist/src/commands/test-control-restore.d.ts +40 -0
  81. package/dist/src/commands/test-control-restore.js +86 -0
  82. package/dist/src/commands/test-dispatch.js +68 -7
  83. package/dist/src/commands/test-multi-verdict.js +1 -0
  84. package/dist/src/commands/test-orchestrate.d.ts +6 -0
  85. package/dist/src/commands/test-orchestrate.js +32 -1
  86. package/dist/src/commands/test-perf-scope.d.ts +1 -1
  87. package/dist/src/commands/test-perf-scope.js +1 -1
  88. package/dist/src/commands/test-prelock.d.ts +18 -8
  89. package/dist/src/commands/test-prelock.js +42 -14
  90. package/dist/src/commands/test-register.js +4 -3
  91. package/dist/src/commands/test-results.d.ts +10 -1
  92. package/dist/src/commands/test-results.js +20 -3
  93. package/dist/src/commands/test-run.d.ts +6 -0
  94. package/dist/src/commands/test-run.js +23 -4
  95. package/dist/src/commands/test-shards.d.ts +7 -3
  96. package/dist/src/commands/test-shards.js +24 -7
  97. package/dist/src/commands/test-stale-gate.d.ts +6 -1
  98. package/dist/src/commands/test-stale-gate.js +40 -2
  99. package/dist/src/commands/test-verdict.d.ts +5 -3
  100. package/dist/src/commands/test.js +14 -2
  101. package/dist/src/commands/token.js +2 -2
  102. package/dist/src/commands/verify.js +1 -1
  103. package/dist/src/commands/watch.js +1 -1
  104. package/dist/src/commands/wire.js +1 -1
  105. package/dist/src/core/build-audit-resolve.d.ts +4 -5
  106. package/dist/src/core/build-audit-resolve.js +4 -5
  107. package/dist/src/core/build-audit-skip.d.ts +2 -2
  108. package/dist/src/core/build-audit.d.ts +3 -4
  109. package/dist/src/core/build-baseline-types.d.ts +24 -3
  110. package/dist/src/core/build-baseline-validate.d.ts +30 -1
  111. package/dist/src/core/build-baseline-validate.js +127 -35
  112. package/dist/src/core/build-baseline.d.ts +8 -2
  113. package/dist/src/core/build-baseline.js +25 -16
  114. package/dist/src/core/build-jar-escalation.d.ts +4 -4
  115. package/dist/src/core/build-jar-escalation.js +4 -4
  116. package/dist/src/core/build-prepare.js +4 -4
  117. package/dist/src/core/config-validate.js +4 -4
  118. package/dist/src/core/coverage-extend.d.ts +5 -3
  119. package/dist/src/core/coverage-extend.js +8 -3
  120. package/dist/src/core/coverage-records.d.ts +4 -5
  121. package/dist/src/core/coverage-records.js +4 -5
  122. package/dist/src/core/destructive.d.ts +2 -2
  123. package/dist/src/core/destructive.js +2 -2
  124. package/dist/src/core/discard-baseline.d.ts +2 -2
  125. package/dist/src/core/discard-baseline.js +3 -3
  126. package/dist/src/core/engine-session-lock.d.ts +1 -1
  127. package/dist/src/core/engine-session-lock.js +1 -1
  128. package/dist/src/core/file-lock-owner.d.ts +5 -5
  129. package/dist/src/core/file-lock-owner.js +5 -5
  130. package/dist/src/core/file-lock-queue.js +6 -8
  131. package/dist/src/core/file-lock-shared.d.ts +4 -4
  132. package/dist/src/core/file-lock-shared.js +4 -4
  133. package/dist/src/core/file-lock.js +7 -10
  134. package/dist/src/core/firefox.js +2 -2
  135. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +1 -1
  136. package/dist/src/core/furnace-apply-overwrite-warn.js +1 -1
  137. package/dist/src/core/furnace-apply.js +3 -3
  138. package/dist/src/core/furnace-operation.js +5 -5
  139. package/dist/src/core/furnace-registration-jar-inplace.js +5 -6
  140. package/dist/src/core/furnace-registration-rename.d.ts +5 -5
  141. package/dist/src/core/furnace-registration-rename.js +5 -5
  142. package/dist/src/core/furnace-registration-validate.d.ts +2 -1
  143. package/dist/src/core/furnace-registration-validate.js +2 -1
  144. package/dist/src/core/furnace-registration.js +2 -2
  145. package/dist/src/core/git-base.js +4 -3
  146. package/dist/src/core/git-diff.js +1 -1
  147. package/dist/src/core/git.js +1 -1
  148. package/dist/src/core/harness-orphans.d.ts +6 -6
  149. package/dist/src/core/harness-orphans.js +6 -6
  150. package/dist/src/core/harness-port-reservation.d.ts +60 -0
  151. package/dist/src/core/harness-port-reservation.js +122 -0
  152. package/dist/src/core/history-log-file.d.ts +9 -9
  153. package/dist/src/core/history-log-file.js +15 -15
  154. package/dist/src/core/history-log.d.ts +3 -3
  155. package/dist/src/core/history-log.js +3 -3
  156. package/dist/src/core/launchable-build.d.ts +39 -0
  157. package/dist/src/core/launchable-build.js +85 -0
  158. package/dist/src/core/marionette-port.js +1 -3
  159. package/dist/src/core/moz-manifest-rules.js +3 -2
  160. package/dist/src/core/negative-control-compiled.d.ts +13 -3
  161. package/dist/src/core/negative-control-compiled.js +27 -5
  162. package/dist/src/core/negative-control-diff.js +31 -6
  163. package/dist/src/core/parser-fallback.d.ts +5 -5
  164. package/dist/src/core/parser-fallback.js +5 -5
  165. package/dist/src/core/patch-identifier-suggest.d.ts +6 -0
  166. package/dist/src/core/patch-identifier-suggest.js +11 -4
  167. package/dist/src/core/patch-lint-checkjs.d.ts +1 -1
  168. package/dist/src/core/patch-lint-checkjs.js +1 -1
  169. package/dist/src/core/patch-lint-css.js +2 -2
  170. package/dist/src/core/patch-lint-file-size.d.ts +5 -7
  171. package/dist/src/core/patch-lint-file-size.js +5 -7
  172. package/dist/src/core/patch-lint-ignore.d.ts +3 -3
  173. package/dist/src/core/patch-lint-ignore.js +3 -3
  174. package/dist/src/core/patch-lint-prettier.d.ts +4 -6
  175. package/dist/src/core/patch-lint-prettier.js +4 -6
  176. package/dist/src/core/patch-lint-projection.d.ts +1 -1
  177. package/dist/src/core/patch-lint-projection.js +1 -1
  178. package/dist/src/core/patch-lint-queue-types.d.ts +2 -2
  179. package/dist/src/core/patch-lint-queue-types.js +2 -2
  180. package/dist/src/core/patch-manifest-consistency.d.ts +2 -2
  181. package/dist/src/core/patch-manifest-consistency.js +2 -2
  182. package/dist/src/core/patch-manifest-io.d.ts +5 -1
  183. package/dist/src/core/patch-manifest-io.js +12 -10
  184. package/dist/src/core/patch-manifest-key-order.d.ts +3 -4
  185. package/dist/src/core/patch-manifest-key-order.js +3 -4
  186. package/dist/src/core/patch-parse.d.ts +3 -3
  187. package/dist/src/core/patch-parse.js +3 -3
  188. package/dist/src/core/patch-policy.d.ts +1 -1
  189. package/dist/src/core/patch-policy.js +1 -1
  190. package/dist/src/core/patch-source-metadata.d.ts +1 -1
  191. package/dist/src/core/patch-source-metadata.js +1 -1
  192. package/dist/src/core/patch-subset.d.ts +5 -3
  193. package/dist/src/core/patch-subset.js +8 -7
  194. package/dist/src/core/patch-transform.d.ts +2 -2
  195. package/dist/src/core/patch-transform.js +2 -2
  196. package/dist/src/core/rebase-session.js +4 -6
  197. package/dist/src/core/register-xpcshell-test.d.ts +28 -7
  198. package/dist/src/core/register-xpcshell-test.js +76 -27
  199. package/dist/src/core/run-log.d.ts +8 -8
  200. package/dist/src/core/run-log.js +8 -8
  201. package/dist/src/core/status-classify.d.ts +1 -2
  202. package/dist/src/core/status-classify.js +1 -2
  203. package/dist/src/core/test-harness-crash.d.ts +5 -3
  204. package/dist/src/core/test-harness-crash.js +13 -14
  205. package/dist/src/core/test-harness-infra.d.ts +32 -0
  206. package/dist/src/core/test-harness-infra.js +69 -0
  207. package/dist/src/core/test-harness-signature.d.ts +6 -0
  208. package/dist/src/core/test-stale-check.d.ts +38 -0
  209. package/dist/src/core/test-stale-check.js +82 -1
  210. package/dist/src/core/test-stall-triage.js +5 -7
  211. package/dist/src/core/token-category.js +2 -2
  212. package/dist/src/core/token-inventory.js +3 -3
  213. package/dist/src/core/token-manager.d.ts +2 -2
  214. package/dist/src/core/token-manager.js +5 -7
  215. package/dist/src/core/token-variant.d.ts +6 -6
  216. package/dist/src/core/token-variant.js +7 -7
  217. package/dist/src/core/typecheck-shim.js +1 -1
  218. package/dist/src/core/wire-destroy.d.ts +1 -1
  219. package/dist/src/core/wire-destroy.js +1 -1
  220. package/dist/src/core/wire-dom-fragment.js +1 -1
  221. package/dist/src/core/wire-init.d.ts +1 -1
  222. package/dist/src/core/wire-init.js +1 -1
  223. package/dist/src/core/wire-subscript.d.ts +1 -1
  224. package/dist/src/core/wire-subscript.js +1 -1
  225. package/dist/src/core/wire-utils.d.ts +2 -2
  226. package/dist/src/core/wire-utils.js +3 -3
  227. package/dist/src/errors/base.d.ts +1 -1
  228. package/dist/src/errors/base.js +1 -1
  229. package/dist/src/errors/rebase.d.ts +1 -1
  230. package/dist/src/errors/rebase.js +1 -1
  231. package/dist/src/types/commands/options.d.ts +38 -3
  232. package/dist/src/types/config.d.ts +5 -7
  233. package/dist/src/utils/options.js +2 -2
  234. package/dist/src/utils/process.js +2 -2
  235. package/dist/src/utils/regex.d.ts +2 -2
  236. package/dist/src/utils/regex.js +3 -3
  237. package/dist/src/utils/sleep.d.ts +5 -6
  238. package/dist/src/utils/sleep.js +5 -6
  239. package/dist/src/utils/validation.js +2 -2
  240. package/docs/configuration.md +28 -23
  241. package/docs/exit-codes.md +8 -11
  242. package/docs/furnace.md +3 -3
  243. package/docs/lifecycle-invariants.md +36 -45
  244. package/docs/machine-output.md +41 -24
  245. package/docs/patch-workflow.md +140 -29
  246. package/docs/run-logs.md +8 -9
  247. package/docs/testing.md +114 -48
  248. package/package.json +1 -1
@@ -74,10 +74,10 @@ function parseFirefoxBlock(rec) {
74
74
  throw new ConfigError('Config field "firefox.version" must be a valid Firefox version (e.g., "145.0")');
75
75
  }
76
76
  const firefoxProduct = requireConfigString(firefoxRec, 'product', 'firefox.product');
77
- // Uses the shared guard rather than a fourth copy of the member list. This
78
- // is the fireforge.json read path: a product added to the union but not to
79
- // the copy that used to live here would have been silently rejected from
80
- // every project config.
77
+ // Uses the shared guard rather than a separate copy of the member list.
78
+ // This is the fireforge.json read path: a product added to the union but
79
+ // not to a local copy would be silently rejected from every project
80
+ // config.
81
81
  if (!isValidFirefoxProduct(firefoxProduct)) {
82
82
  throw new ConfigError(`Config field "firefox.product" must be one of: ${FIREFOX_PRODUCTS.join(', ')}`);
83
83
  }
@@ -7,7 +7,7 @@
7
7
  * collapsing to this build's paths. `--extend-coverage` turns any drop into
8
8
  * a refusal before the build.
9
9
  *
10
- * Fingerprints must be complete: a legacy packageable-only record cannot
10
+ * Fingerprints must be complete: a record of packageable inputs only cannot
11
11
  * prove that a test fixture or manifest still matches its staged copy.
12
12
  */
13
13
  import type { BuildBaseline, CoverageAnchor, CoverageRecord, TestPackagingCoverage } from './build-baseline-types.js';
@@ -70,9 +70,11 @@ export interface RecordedCoverage {
70
70
  *
71
71
  * @param engineDir - Path to the engine directory
72
72
  * @param plan - Plan after {@link applyMozconfigAnchor}
73
- * @param requested - Normalized request paths this build packaged
73
+ * @param requestedPaths - Request paths this build packaged. Normalized
74
+ * here (no trailing slash, no `./`): the baseline validator rejects the
75
+ * raw CLI shapes, and a baseline it rejects refuses every later run.
74
76
  */
75
- export declare function recordScopedCoverage(engineDir: string, plan: CoverageRetentionPlan, requested: string[]): Promise<RecordedCoverage>;
77
+ export declare function recordScopedCoverage(engineDir: string, plan: CoverageRetentionPlan, requestedPaths: string[]): Promise<RecordedCoverage>;
76
78
  /** Renders one dropped claim with its cause. */
77
79
  export declare function describeDroppedClaim(claim: DroppedClaim): string;
78
80
  /**
@@ -8,7 +8,7 @@
8
8
  * collapsing to this build's paths. `--extend-coverage` turns any drop into
9
9
  * a refusal before the build.
10
10
  *
11
- * Fingerprints must be complete: a legacy packageable-only record cannot
11
+ * Fingerprints must be complete: a record of packageable inputs only cannot
12
12
  * prove that a test fixture or manifest still matches its staged copy.
13
13
  */
14
14
  import { toError } from '../utils/errors.js';
@@ -163,11 +163,16 @@ export async function applyMozconfigAnchor(engineDir, plan) {
163
163
  *
164
164
  * @param engineDir - Path to the engine directory
165
165
  * @param plan - Plan after {@link applyMozconfigAnchor}
166
- * @param requested - Normalized request paths this build packaged
166
+ * @param requestedPaths - Request paths this build packaged. Normalized
167
+ * here (no trailing slash, no `./`): the baseline validator rejects the
168
+ * raw CLI shapes, and a baseline it rejects refuses every later run.
167
169
  */
168
- export async function recordScopedCoverage(engineDir, plan, requested) {
170
+ export async function recordScopedCoverage(engineDir, plan, requestedPaths) {
169
171
  if (plan.keepFull)
170
172
  return { testPackagingCoverage: 'full' };
173
+ const requested = [
174
+ ...new Set(requestedPaths.map(normalizeRequestPath).filter((path) => path.length > 0)),
175
+ ];
171
176
  const records = { ...plan.retained };
172
177
  try {
173
178
  const state = await readEngineContentState(engineDir);
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * Per-path packaging-coverage anchors for scoped `fireforge test --build`.
3
3
  *
4
- * A scoped build used to carry one claim anchored to the whole engine, so
5
- * any edit outside the new scope (a peer's, on a shared engine, nearly
6
- * always) replaced it. Each staged request path now carries its own anchor
7
- * restricted to its own staging inputs, and a claim is dropped only when
8
- * one of those inputs changed.
4
+ * Each staged request path carries its own anchor restricted to its own
5
+ * staging inputs, and a claim is dropped only when one of those inputs
6
+ * changed. One claim anchored to the whole engine would be replaced by any
7
+ * edit outside the new scope (a peer's, on a shared engine, nearly always).
9
8
  *
10
9
  * The staging inputs of a request path are its manifest directory (the
11
10
  * nearest directory at or above it that holds a test manifest) plus every
@@ -2,11 +2,10 @@
2
2
  /**
3
3
  * Per-path packaging-coverage anchors for scoped `fireforge test --build`.
4
4
  *
5
- * A scoped build used to carry one claim anchored to the whole engine, so
6
- * any edit outside the new scope (a peer's, on a shared engine, nearly
7
- * always) replaced it. Each staged request path now carries its own anchor
8
- * restricted to its own staging inputs, and a claim is dropped only when
9
- * one of those inputs changed.
5
+ * Each staged request path carries its own anchor restricted to its own
6
+ * staging inputs, and a claim is dropped only when one of those inputs
7
+ * changed. One claim anchored to the whole engine would be replaced by any
8
+ * edit outside the new scope (a peer's, on a shared engine, nearly always).
10
9
  *
11
10
  * The staging inputs of a request path are its manifest directory (the
12
11
  * nearest directory at or above it that holds a test manifest) plus every
@@ -128,8 +128,8 @@ export declare function confirmDestructive(input: DestructiveOpInput): Promise<D
128
128
  /**
129
129
  * Appends a single JSONL record to the project's patch history log
130
130
  * (`.fireforge/history.jsonl` unless `fireforge.json#history.path` says
131
- * otherwise; a log at the pre-0.50 `patches/.fireforge-history.jsonl` is
132
- * moved there first).
131
+ * otherwise; a log found at `patches/.fireforge-history.jsonl` is moved
132
+ * there first).
133
133
  *
134
134
  * Call order matters: append only after the mutation succeeds, never
135
135
  * pre-emptively, so rolled-back failures do not leave ghost entries. The log
@@ -159,8 +159,8 @@ export async function confirmDestructive(input) {
159
159
  /**
160
160
  * Appends a single JSONL record to the project's patch history log
161
161
  * (`.fireforge/history.jsonl` unless `fireforge.json#history.path` says
162
- * otherwise; a log at the pre-0.50 `patches/.fireforge-history.jsonl` is
163
- * moved there first).
162
+ * otherwise; a log found at `patches/.fireforge-history.jsonl` is moved
163
+ * there first).
164
164
  *
165
165
  * Call order matters: append only after the mutation succeeds, never
166
166
  * pre-emptively, so rolled-back failures do not leave ghost entries. The log
@@ -30,7 +30,7 @@ export interface DiscardBaselinePlan {
30
30
  /** Rename/copy only: baseline content for `originalPath` when that side is claimed. */
31
31
  expectedOriginalContent?: string | null;
32
32
  }
33
- /** Builds an all-unmanaged plan set, the `--to-upstream` (legacy) shape. */
33
+ /** Builds an all-unmanaged plan set, the `--to-upstream` shape. */
34
34
  export declare function planUpstreamDiscards(entries: readonly GitStatusEntry[]): DiscardBaselinePlan[];
35
35
  /**
36
36
  * Plans the restore target for each status entry. Entries claimed by the
@@ -47,7 +47,7 @@ export declare function planDiscardBaselines(patchesDir: string, engineDir: stri
47
47
  * Applies one restore plan. Patch-claimed paths end up exactly as `import`
48
48
  * leaves them: patched content as an unstaged worktree state (tracked files
49
49
  * read ` M`, created files `??`), nothing staged. Unmanaged plans delegate
50
- * to the legacy {@link discardStatusEntry} mechanics.
50
+ * to the plain git {@link discardStatusEntry} mechanics.
51
51
  */
52
52
  export declare function applyDiscardBaseline(engineDir: string, plan: DiscardBaselinePlan): Promise<void>;
53
53
  /** One-line annotation for dry-run listings and confirmation summaries. */
@@ -24,7 +24,7 @@ import { createPatchedContentContext } from './patch-apply.js';
24
24
  import { loadPatchesManifestState } from './patch-manifest-io.js';
25
25
  import { parseDiffSections } from './patch-parse.js';
26
26
  import { buildPatchClaims } from './status-classify.js';
27
- /** Builds an all-unmanaged plan set, the `--to-upstream` (legacy) shape. */
27
+ /** Builds an all-unmanaged plan set, the `--to-upstream` shape. */
28
28
  export function planUpstreamDiscards(entries) {
29
29
  return entries.map((entry) => ({ entry, kind: 'unmanaged', owners: [], conflicted: false }));
30
30
  }
@@ -151,7 +151,7 @@ export async function planDiscardBaselines(patchesDir, engineDir, entries) {
151
151
  * Applies one restore plan. Patch-claimed paths end up exactly as `import`
152
152
  * leaves them: patched content as an unstaged worktree state (tracked files
153
153
  * read ` M`, created files `??`), nothing staged. Unmanaged plans delegate
154
- * to the legacy {@link discardStatusEntry} mechanics.
154
+ * to the plain git {@link discardStatusEntry} mechanics.
155
155
  */
156
156
  export async function applyDiscardBaseline(engineDir, plan) {
157
157
  if (plan.kind === 'unmanaged') {
@@ -173,7 +173,7 @@ export async function applyDiscardBaseline(engineDir, plan) {
173
173
  await writeText(join(engineDir, entry.file), plan.expectedContent);
174
174
  }
175
175
  // A rename/copy's original side restores to its baseline too: the patch
176
- // baseline when claimed, else the legacy HEAD restore (matching what
176
+ // baseline when claimed, else the HEAD restore (matching what
177
177
  // discardStatusEntry does for that side).
178
178
  if (entry.originalPath !== undefined) {
179
179
  if (plan.expectedOriginalContent !== undefined) {
@@ -77,7 +77,7 @@ export declare function generationDeltaPaths(before: string, after: string): {
77
77
  /**
78
78
  * Describes what moved between two generation tokens, in operator terms.
79
79
  *
80
- * The refusal used to say only that the engine changed, which left the
80
+ * A refusal that said only that the engine changed would leave the
81
81
  * operator to reconstruct the writer by hand: `find -newermt` against the
82
82
  * run window, minus the objdir, minus git's own bookkeeping. Everything
83
83
  * needed to answer it is already in the two tokens, so the refusal answers
@@ -281,7 +281,7 @@ export function generationDeltaPaths(before, after) {
281
281
  /**
282
282
  * Describes what moved between two generation tokens, in operator terms.
283
283
  *
284
- * The refusal used to say only that the engine changed, which left the
284
+ * A refusal that said only that the engine changed would leave the
285
285
  * operator to reconstruct the writer by hand: `find -newermt` against the
286
286
  * run window, minus the objdir, minus git's own bookkeeping. Everything
287
287
  * needed to answer it is already in the two tokens, so the refusal answers
@@ -46,11 +46,11 @@ export declare function readLockOwner(lockPath: string): Promise<LockOwner>;
46
46
  * time, own start tick, diagnostic metadata) into a lock directory it has
47
47
  * just created, and reads the record back to prove it landed.
48
48
  *
49
- * The write is fatal. It used to be best-effort, on the theory that stale
50
- * recovery would fall back to the age heuristic, but that heuristic reaps
51
- * any owner-less lock after five minutes, and the build lock legitimately
52
- * holds for hours. A holder whose record failed to write was therefore
53
- * reaped mid-build and a second process entered the critical section. On
49
+ * The write is fatal rather than best-effort. Stale recovery could fall
50
+ * back to the age heuristic, but that heuristic reaps any owner-less lock
51
+ * after five minutes, and the build lock legitimately holds for hours. A
52
+ * holder whose record failed to write would be reaped mid-build and a
53
+ * second process would enter the critical section. On
54
54
  * failure the just-created lock directory is removed (nothing else can own
55
55
  * it yet) and a {@link GeneralError} names the cause.
56
56
  *
@@ -141,11 +141,11 @@ async function readBackLockOwner(lockPath) {
141
141
  * time, own start tick, diagnostic metadata) into a lock directory it has
142
142
  * just created, and reads the record back to prove it landed.
143
143
  *
144
- * The write is fatal. It used to be best-effort, on the theory that stale
145
- * recovery would fall back to the age heuristic, but that heuristic reaps
146
- * any owner-less lock after five minutes, and the build lock legitimately
147
- * holds for hours. A holder whose record failed to write was therefore
148
- * reaped mid-build and a second process entered the critical section. On
144
+ * The write is fatal rather than best-effort. Stale recovery could fall
145
+ * back to the age heuristic, but that heuristic reaps any owner-less lock
146
+ * after five minutes, and the build lock legitimately holds for hours. A
147
+ * holder whose record failed to write would be reaped mid-build and a
148
+ * second process would enter the critical section. On
149
149
  * failure the just-created lock directory is removed (nothing else can own
150
150
  * it yet) and a {@link GeneralError} names the cause.
151
151
  *
@@ -3,18 +3,16 @@
3
3
  * Waiter tickets for `withFileLock`: who is queued for a contended lock, in
4
4
  * what order, and whose turn it is.
5
5
  *
6
- * The queue started as a display: waiters advertised themselves so the wait
7
- * line could say "You are next", but the grant itself was a free-for-all of
8
- * `mkdir` polls. A waiter whose poll interval had backed off to 2 s lost
9
- * every free moment to later arrivals polling at 100 ms, so a run told it
10
- * was next for twenty minutes watched three later runs take the lock in
11
- * turn. The tickets now decide the grant: only the head of the queue may
12
- * attempt the lock, so the position printed is the position honoured.
6
+ * The tickets decide the grant: only the head of the queue may attempt the
7
+ * lock, so the position printed is the position honoured. A free-for-all of
8
+ * `mkdir` polls would let later arrivals polling at 100 ms take every free
9
+ * moment from a waiter whose poll interval had backed off to 2 s, so a run
10
+ * told it was next could watch later runs take the lock in turn.
13
11
  *
14
12
  * A ticket is a file in `<lock>.waiters/` named
15
13
  * `<startedAtMs>-<pid>-<uuid>`. Arrival order is the wait start, with the
16
14
  * whole name as a tie-break so every reader sorts the queue identically.
17
- * Because a ticket now blocks everyone behind it, a ticket whose owner is
15
+ * Because a ticket blocks everyone behind it, a ticket whose owner is
18
16
  * gone must not hold the queue: a dead PID (or a reused one, where the
19
17
  * start-tick check can tell) drops out and its ticket is removed, and a
20
18
  * ticket its owner has stopped refreshing drops out without being removed,
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Shared holds on a FireForge directory lock.
3
3
  *
4
- * Every lock used to be exclusive, so two build-less `fireforge test` runs
5
- * on one objdir, each with its own profile and (under auto ports) its own
6
- * ports, still waited for each other: the gate's browser tiers ran strictly
7
- * serially on a 14-core host. A shared hold lets such runs overlap while
4
+ * Two build-less `fireforge test` runs on one objdir, each with its own
5
+ * profile and (under auto ports) its own ports, have no reason to wait for
6
+ * each other; with only exclusive holds the gate's browser tiers would run
7
+ * strictly serially on a many-core host. A shared hold lets such runs overlap while
8
8
  * anything that writes (a build, a re-export, a test run that builds)
9
9
  * still holds the lock alone.
10
10
  *
@@ -2,10 +2,10 @@
2
2
  /**
3
3
  * Shared holds on a FireForge directory lock.
4
4
  *
5
- * Every lock used to be exclusive, so two build-less `fireforge test` runs
6
- * on one objdir, each with its own profile and (under auto ports) its own
7
- * ports, still waited for each other: the gate's browser tiers ran strictly
8
- * serially on a 14-core host. A shared hold lets such runs overlap while
5
+ * Two build-less `fireforge test` runs on one objdir, each with its own
6
+ * profile and (under auto ports) its own ports, have no reason to wait for
7
+ * each other; with only exclusive holds the gate's browser tiers would run
8
+ * strictly serially on a many-core host. A shared hold lets such runs overlap while
9
9
  * anything that writes (a build, a re-export, a test run that builds)
10
10
  * still holds the lock alone.
11
11
  *
@@ -269,10 +269,9 @@ async function buildLockTimeoutError(lockPath, onTimeoutMessage, cause, queue) {
269
269
  *
270
270
  * The queue is read at the moment the budget expires rather than taken from
271
271
  * the last progress probe. A probe snapshot is up to one `waitProgressMs`
272
- * stale, and, the reason this changed, it may not exist at all: a short
273
- * budget can expire before the first probe lands (a slow runner's
274
- * `registerWaiter` alone can outlast it), and a caller that configured no
275
- * probe at all never had one. Both used to lose the position silently.
272
+ * stale, and it may not exist at all: a short budget can expire before the
273
+ * first probe lands (a slow runner's `registerWaiter` alone can outlast it),
274
+ * and a caller that configured no probe at all never has one.
276
275
  */
277
276
  function formatQueuePositionReached(queue) {
278
277
  if (queue.depth === 0)
@@ -423,12 +422,10 @@ export async function withFileLock(lockPath, operation, options = {}) {
423
422
  // buys a fresh budget, because the wait is working. Only a queue
424
423
  // that stops moving starves, which is the case the timeout is for.
425
424
  //
426
- // The first observation is a seed, never an advance. `bestAhead`
427
- // used to start `undefined`, so the opening probe always satisfied
428
- // this test and handed out a free full budget: a `--wait-lock 300`
429
- // run outlived its 300 s even against a queue that never moved
430
- // once, which is the "it does not reliably give up after N
431
- // seconds" behaviour a downstream fork reported.
425
+ // The first observation is a seed, never an advance. Counted as an
426
+ // advance, the opening probe would hand out a free full budget: a
427
+ // `--wait-lock 300` run would outlive its 300 s even against a queue
428
+ // that never moved once.
432
429
  if (hardDeadline !== undefined) {
433
430
  if (bestAhead === undefined) {
434
431
  bestAhead = probe.queue.ahead;
@@ -96,8 +96,8 @@ export async function downloadFirefoxSource(options) {
96
96
  await removeDir(tempDir);
97
97
  // Only throw away the ~600 MB cached archive when the failure indicts
98
98
  // the archive, not the environment. ENOSPC, a permission problem in
99
- // destDir, or tar disappearing mid-run used to invalidate a perfectly
100
- // good download and force a full re-fetch on retry.
99
+ // destDir, or tar disappearing mid-run leaves a perfectly good download
100
+ // that a retry can reuse without a full re-fetch.
101
101
  if (isLikelyArchiveCorruptionError(error)) {
102
102
  await invalidateArchiveCache(archive, cacheDir);
103
103
  }
@@ -13,7 +13,7 @@
13
13
  * The per-file byte comparison already exists:
14
14
  * `checkRegistrationConsistency` for custom components (fragment- and
15
15
  * ftl-aware) and the copy-candidate walk for overrides. This module only
16
- * surfaces what was previously computed and thrown away.
16
+ * surfaces what those comparisons compute.
17
17
  */
18
18
  import type { CustomComponentConfig, OverrideComponentConfig } from '../types/furnace.js';
19
19
  /** One deployed, patch-owned file about to be overwritten with different bytes. */
@@ -14,7 +14,7 @@
14
14
  * The per-file byte comparison already exists:
15
15
  * `checkRegistrationConsistency` for custom components (fragment- and
16
16
  * ftl-aware) and the copy-candidate walk for overrides. This module only
17
- * surfaces what was previously computed and thrown away.
17
+ * surfaces what those comparisons compute.
18
18
  */
19
19
  import { readdir } from 'node:fs/promises';
20
20
  import { join, relative } from 'node:path';
@@ -258,9 +258,9 @@ async function applyCustomBatch(ctx, root, markerComment) {
258
258
  filesAffectedTotal.push(...removed);
259
259
  }
260
260
  if (!dryRun) {
261
- // Runs unconditionally. The `changed === true` path used to skip
262
- // drift detection entirely, which is when a deployed engine-only
263
- // fix gets silently replaced (J6).
261
+ // Runs unconditionally, including on the `changed === true` path:
262
+ // that is when a deployed engine-only fix gets silently replaced
263
+ // (J6).
264
264
  recordOverwriteWarnings(result, await findPatchOwnedOverwrites({
265
265
  type: 'custom',
266
266
  root,
@@ -305,11 +305,11 @@ export async function completeJournalRollback(ctx, journal, error, options) {
305
305
  export async function forceReleaseFurnaceLocksForActiveOperations() {
306
306
  const paths = new Set([...activeOperations.values()].map((op) => getFurnaceLockPath(op.root)));
307
307
  for (const lockPath of paths) {
308
- // Ownership-checked, like every other release path. The bare `rm` this
309
- // replaced would delete a lock a different process had acquired in the
310
- // window between our operation dying and this sweep running. That is the
311
- // exact race `releaseLock`'s PID verification was added to close, reopened
312
- // here because the sweeper has no acquisition token to check.
308
+ // Ownership-checked, like every other release path. A bare `rm` would
309
+ // delete a lock a different process had acquired in the window between
310
+ // our operation dying and this sweep running. That is the exact race
311
+ // `releaseLock`'s PID verification closes, and the sweeper has no
312
+ // acquisition token to check.
313
313
  const removed = await releaseLockIfOwned(lockPath);
314
314
  verbose(removed
315
315
  ? `Removed furnace lock at ${lockPath} during signal teardown`
@@ -2,10 +2,9 @@
2
2
  /**
3
3
  * In-place edits to a widget's `toolkit/content/jar.mn` block.
4
4
  *
5
- * Undeploying one file and renaming a component used to drop every line of
6
- * the widget and re-add the live ones, which re-sorted the whole block and
7
- * left the owning patch carrying reorder hunks. These helpers touch only
8
- * the lines that change: a deleted file's line goes, a renamed component's
5
+ * Dropping every line of the widget and re-adding the live ones would
6
+ * re-sort the whole block and leave the owning patch carrying reorder
7
+ * hunks. These helpers touch only the lines that change: a deleted file's line goes, a renamed component's
9
8
  * lines are rewritten where they stand, and anything still missing is left
10
9
  * for `addJarMnEntries` to insert.
11
10
  */
@@ -15,8 +14,8 @@ import { escapeRegex } from '../utils/regex.js';
15
14
  import { JAR_MN } from './furnace-constants.js';
16
15
  /**
17
16
  * The file a widget jar.mn line registers: the `(widgets/<tag>/<file>)`
18
- * source mapping, or the legacy `content/global/elements/<tag>.<ext>`
19
- * target written before the mapping existed. Undefined when the line is
17
+ * source mapping, or the `content/global/elements/<tag>.<ext>` target of a
18
+ * line that carries no source mapping. Undefined when the line is
20
19
  * not one of this tag's.
21
20
  */
22
21
  function registeredFile(line, tagName) {
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * In-place rename of a custom element registration in customElements.js.
3
3
  *
4
- * `furnace rename` used to remove the old entry and add a new one. On a
5
- * registration list the remove path could not see, that left the old entry
6
- * beside the new one, and the new entry was written without the project
7
- * marker every sibling line carries (`// <MARKER>:`), so the next lint
8
- * flagged it as an unmarked engine edit. Rewriting the tag and module
4
+ * Removing the old entry and adding a new one would, on a registration
5
+ * list the remove path cannot see, leave the old entry beside the new one,
6
+ * and the new entry would lack the project marker every sibling line
7
+ * carries (`// <MARKER>:`), so the next lint would flag it as an unmarked
8
+ * engine edit. Rewriting the tag and module
9
9
  * literals inside the existing entry keeps the entry's position, layout and
10
10
  * trailing marker comments exactly as they were.
11
11
  */
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * In-place rename of a custom element registration in customElements.js.
4
4
  *
5
- * `furnace rename` used to remove the old entry and add a new one. On a
6
- * registration list the remove path could not see, that left the old entry
7
- * beside the new one, and the new entry was written without the project
8
- * marker every sibling line carries (`// <MARKER>:`), so the next lint
9
- * flagged it as an unmarked engine edit. Rewriting the tag and module
5
+ * Removing the old entry and adding a new one would, on a registration
6
+ * list the remove path cannot see, leave the old entry beside the new one,
7
+ * and the new entry would lack the project marker every sibling line
8
+ * carries (`// <MARKER>:`), so the next lint would flag it as an unmarked
9
+ * engine edit. Rewriting the tag and module
10
10
  * literals inside the existing entry keeps the entry's position, layout and
11
11
  * trailing marker comments exactly as they were.
12
12
  */
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Shared validation for furnace custom element registration placement.
3
- * Used after both AST and legacy code paths to avoid duplicating logic.
3
+ * Shared by the AST registration editor and the registration validator to
4
+ * avoid duplicating logic.
4
5
  */
5
6
  /**
6
7
  * Regex for valid custom element tag names. A valid name is lowercase, starts
@@ -1,7 +1,8 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
3
  * Shared validation for furnace custom element registration placement.
4
- * Used after both AST and legacy code paths to avoid duplicating logic.
4
+ * Shared by the AST registration editor and the registration validator to
5
+ * avoid duplicating logic.
5
6
  */
6
7
  import { FurnaceError } from '../errors/furnace.js';
7
8
  import { escapeRegex, stripJsComments } from '../utils/regex.js';
@@ -252,8 +252,8 @@ export async function removeJarMnEntries(engineDir, tagName) {
252
252
  // line the component registered is removed regardless of the target
253
253
  // basename. A helper `.mjs` whose name does not start with the tag (e.g.
254
254
  // a renamed `foo-utils.mjs`) otherwise survives the remove pass and leaves
255
- // a stale registration that breaks packaging. The legacy target-path match
256
- // is kept as an OR for lines written before the source mapping existed.
255
+ // a stale registration that breaks packaging. The target-path match is
256
+ // kept as an OR for lines that carry no source mapping.
257
257
  // Word boundaries keep "moz-card" from matching "moz-card-group".
258
258
  const sourcePattern = new RegExp(`\\(widgets/${escapeRegex(tagName)}/`);
259
259
  const legacyTargetPattern = new RegExp(`content/global/elements/${escapeRegex(tagName)}\\.`);
@@ -45,9 +45,10 @@ export const GIT_ADD_CHUNK_TIMEOUT_MS = resolveTimeoutFromEnv(GIT_ADD_CHUNK_TIME
45
45
  /**
46
46
  * In-flight or settled successful `git` availability probe.
47
47
  *
48
- * The probe spawns `which git` (or `where` on Windows), and every git wrapper
49
- * in this layer used to run it before each command, which meant dozens of
50
- * extra spawns per `fireforge export`. Only success is cached: a rejection
48
+ * The probe spawns `which git` (or `where` on Windows); running it before
49
+ * every command would mean dozens of extra spawns per `fireforge export`, so
50
+ * the result is shared by every git wrapper in this layer. Only success is
51
+ * cached: a rejection
51
52
  * clears the slot so a run that installs git mid-flight (or a test that flips
52
53
  * the probe) sees the new answer instead of a sticky refusal.
53
54
  */
@@ -521,7 +521,7 @@ async function generateBinaryFilePatchWithConfig(repoDir, filePath, gitConfigArg
521
521
  * "absent", and this branch only runs for files untracked in HEAD). A prior
522
522
  * stage-0 entry is re-pointed at its recorded mode and blob. Unmerged entries
523
523
  * (stages 1 to 3) cannot be rebuilt through `--cacheinfo`. That degenerate
524
- * state keeps the legacy reset-to-HEAD behavior rather than corrupting the
524
+ * state falls back to the reset-to-HEAD restore rather than corrupting the
525
525
  * conflict.
526
526
  * @param repoDir - Repository directory
527
527
  * @param filePath - File path (relative to repo root)
@@ -46,7 +46,7 @@ const GIT_ADD_ENV = { GIT_INDEX_THREADS: '0' };
46
46
  * AbortError when the signal fires, so both shapes have to be recognised.
47
47
  */
48
48
  function isTimeoutError(error) {
49
- // `exec` now rejects with a typed ExecTimeoutError when its `timeout`
49
+ // `exec` rejects with a typed ExecTimeoutError when its `timeout`
50
50
  // option fires (the raw AbortError check below is kept for defence in
51
51
  // depth against other abort paths).
52
52
  if (error instanceof ExecTimeoutError)
@@ -45,12 +45,12 @@ export interface OrphanedHarnessProcess {
45
45
  * True when `command` IS a harness helper: its executable is a helper
46
46
  * binary, or an interpreter whose first argument is a helper script.
47
47
  *
48
- * Structural on purpose. An earlier version matched the helper names
49
- * anywhere in the command line, which is right for the httpd (`xpcshell -g
50
- * … -f …/server.js`) and wrong for everything that merely mentions such a
51
- * path: an editor opened on `server.js`, a shell whose command text quotes
52
- * it, a `grep` over the objdir. Report-only, that printed a misleading
53
- * `kill`; under the reap posture it terminated the operator's shell.
48
+ * Structural on purpose. Matching the helper names anywhere in the command
49
+ * line is right for the httpd (`xpcshell -g … -f …/server.js`) and wrong for
50
+ * everything that merely mentions such a path: an editor opened on
51
+ * `server.js`, a shell whose command text quotes it, a `grep` over the
52
+ * objdir. Report-only, that would print a misleading `kill`; under the reap
53
+ * posture it would terminate the operator's shell.
54
54
  */
55
55
  export declare function matchesHarnessHelper(command: string): boolean;
56
56
  /** Objdir provenance: an explicit objdir path, a `obj-…` path segment, or `_tests/`. */
@@ -55,12 +55,12 @@ function basename(token) {
55
55
  * True when `command` IS a harness helper: its executable is a helper
56
56
  * binary, or an interpreter whose first argument is a helper script.
57
57
  *
58
- * Structural on purpose. An earlier version matched the helper names
59
- * anywhere in the command line, which is right for the httpd (`xpcshell -g
60
- * … -f …/server.js`) and wrong for everything that merely mentions such a
61
- * path: an editor opened on `server.js`, a shell whose command text quotes
62
- * it, a `grep` over the objdir. Report-only, that printed a misleading
63
- * `kill`; under the reap posture it terminated the operator's shell.
58
+ * Structural on purpose. Matching the helper names anywhere in the command
59
+ * line is right for the httpd (`xpcshell -g … -f …/server.js`) and wrong for
60
+ * everything that merely mentions such a path: an editor opened on
61
+ * `server.js`, a shell whose command text quotes it, a `grep` over the
62
+ * objdir. Report-only, that would print a misleading `kill`; under the reap
63
+ * posture it would terminate the operator's shell.
64
64
  */
65
65
  export function matchesHarnessHelper(command) {
66
66
  const [exe, first] = command.trim().split(/\s+/);