@hominis/fireforge 0.49.2 → 0.51.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 (400) hide show
  1. package/CHANGELOG.md +87 -1
  2. package/README.md +1 -1
  3. package/dist/bin/fireforge.js +6 -0
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/commands/build-prelock.d.ts +20 -0
  6. package/dist/src/commands/build-prelock.js +50 -0
  7. package/dist/src/commands/build.d.ts +3 -1
  8. package/dist/src/commands/build.js +13 -6
  9. package/dist/src/commands/check-report.d.ts +41 -0
  10. package/dist/src/commands/check-report.js +91 -0
  11. package/dist/src/commands/check-run.d.ts +33 -0
  12. package/dist/src/commands/check-run.js +112 -0
  13. package/dist/src/commands/check-steps.d.ts +75 -0
  14. package/dist/src/commands/check-steps.js +200 -0
  15. package/dist/src/commands/check.d.ts +32 -0
  16. package/dist/src/commands/check.js +160 -0
  17. package/dist/src/commands/config.js +5 -6
  18. package/dist/src/commands/declarations-report.d.ts +43 -0
  19. package/dist/src/commands/declarations-report.js +76 -0
  20. package/dist/src/commands/doctor-engine-eslint.js +6 -1
  21. package/dist/src/commands/doctor-source-pin.js +4 -1
  22. package/dist/src/commands/export-adopt-check.d.ts +38 -0
  23. package/dist/src/commands/export-adopt-check.js +98 -0
  24. package/dist/src/commands/export-flow.js +29 -14
  25. package/dist/src/commands/export-placement-gate.d.ts +6 -2
  26. package/dist/src/commands/export-placement-gate.js +8 -0
  27. package/dist/src/commands/export-shared.d.ts +2 -1
  28. package/dist/src/commands/export-staged-registration.d.ts +46 -0
  29. package/dist/src/commands/export-staged-registration.js +91 -0
  30. package/dist/src/commands/forward-import-remedy.d.ts +32 -0
  31. package/dist/src/commands/forward-import-remedy.js +63 -0
  32. package/dist/src/commands/furnace/apply.js +3 -3
  33. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
  34. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  35. package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
  36. package/dist/src/commands/furnace/chrome-doc.js +2 -2
  37. package/dist/src/commands/furnace/create.js +2 -2
  38. package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
  39. package/dist/src/commands/furnace/deploy-named.js +107 -0
  40. package/dist/src/commands/furnace/deploy.d.ts +3 -2
  41. package/dist/src/commands/furnace/deploy.js +50 -86
  42. package/dist/src/commands/furnace/diff.js +26 -3
  43. package/dist/src/commands/furnace/index.js +66 -41
  44. package/dist/src/commands/furnace/init.js +13 -6
  45. package/dist/src/commands/furnace/override.js +4 -2
  46. package/dist/src/commands/furnace/preview.js +4 -3
  47. package/dist/src/commands/furnace/refresh.js +2 -2
  48. package/dist/src/commands/furnace/remove.js +4 -3
  49. package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
  50. package/dist/src/commands/furnace/rename-dry-run.js +127 -0
  51. package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
  52. package/dist/src/commands/furnace/rename-helpers.js +15 -1
  53. package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
  54. package/dist/src/commands/furnace/rename-registrations.js +98 -0
  55. package/dist/src/commands/furnace/rename.d.ts +2 -1
  56. package/dist/src/commands/furnace/rename.js +28 -66
  57. package/dist/src/commands/furnace/scan.d.ts +9 -4
  58. package/dist/src/commands/furnace/scan.js +11 -9
  59. package/dist/src/commands/furnace/sync.js +3 -2
  60. package/dist/src/commands/furnace/validate-report.d.ts +45 -0
  61. package/dist/src/commands/furnace/validate-report.js +91 -0
  62. package/dist/src/commands/furnace/validate.d.ts +1 -1
  63. package/dist/src/commands/furnace/validate.js +151 -113
  64. package/dist/src/commands/furnace/validation-output.js +14 -3
  65. package/dist/src/commands/lint-per-patch.js +36 -61
  66. package/dist/src/commands/lint-per-run-checkjs.js +2 -2
  67. package/dist/src/commands/lint-report.d.ts +8 -4
  68. package/dist/src/commands/lint-report.js +20 -5
  69. package/dist/src/commands/lint.js +15 -34
  70. package/dist/src/commands/manifest.js +4 -0
  71. package/dist/src/commands/park.d.ts +22 -0
  72. package/dist/src/commands/park.js +232 -0
  73. package/dist/src/commands/patch/delete-gap-notice.js +3 -1
  74. package/dist/src/commands/patch/delete.d.ts +1 -1
  75. package/dist/src/commands/patch/delete.js +24 -104
  76. package/dist/src/commands/patch/index.d.ts +3 -3
  77. package/dist/src/commands/patch/index.js +10 -4
  78. package/dist/src/commands/patch/lint-ignore-from.d.ts +44 -0
  79. package/dist/src/commands/patch/lint-ignore-from.js +142 -0
  80. package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
  81. package/dist/src/commands/patch/lint-ignore.js +78 -31
  82. package/dist/src/commands/patch/move-files-create.js +34 -18
  83. package/dist/src/commands/patch/move-files-into.js +15 -7
  84. package/dist/src/commands/patch/move-files.js +6 -2
  85. package/dist/src/commands/patch/removal-created-files.d.ts +48 -0
  86. package/dist/src/commands/patch/removal-created-files.js +83 -0
  87. package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
  88. package/dist/src/commands/patch/removal-dependents.js +133 -0
  89. package/dist/src/commands/patch/rename.js +20 -1
  90. package/dist/src/commands/patch/reserve.d.ts +33 -0
  91. package/dist/src/commands/patch/reserve.js +152 -0
  92. package/dist/src/commands/patch/retire.d.ts +34 -0
  93. package/dist/src/commands/patch/retire.js +133 -0
  94. package/dist/src/commands/patch/split-plan.d.ts +17 -8
  95. package/dist/src/commands/patch/split-plan.js +60 -28
  96. package/dist/src/commands/patch/split-staged.d.ts +108 -0
  97. package/dist/src/commands/patch/split-staged.js +202 -0
  98. package/dist/src/commands/patch/split.d.ts +5 -0
  99. package/dist/src/commands/patch/split.js +62 -17
  100. package/dist/src/commands/patch/stage.d.ts +34 -0
  101. package/dist/src/commands/patch/stage.js +153 -0
  102. package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
  103. package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
  104. package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
  105. package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
  106. package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
  107. package/dist/src/commands/patch/staged-dependency-from.js +108 -0
  108. package/dist/src/commands/patch/staged-dependency-remove.d.ts +31 -0
  109. package/dist/src/commands/patch/staged-dependency-remove.js +80 -0
  110. package/dist/src/commands/patch/staged-dependency.js +54 -139
  111. package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
  112. package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
  113. package/dist/src/commands/re-export-adjacent.d.ts +50 -2
  114. package/dist/src/commands/re-export-adjacent.js +160 -40
  115. package/dist/src/commands/re-export-bulk-scan.d.ts +2 -1
  116. package/dist/src/commands/re-export-bulk-scan.js +22 -7
  117. package/dist/src/commands/re-export-drift.d.ts +36 -8
  118. package/dist/src/commands/re-export-drift.js +73 -49
  119. package/dist/src/commands/re-export-files.js +11 -8
  120. package/dist/src/commands/re-export-hunks.d.ts +35 -0
  121. package/dist/src/commands/re-export-hunks.js +220 -0
  122. package/dist/src/commands/re-export-lint.d.ts +2 -1
  123. package/dist/src/commands/re-export-lint.js +23 -10
  124. package/dist/src/commands/re-export-options.js +74 -6
  125. package/dist/src/commands/re-export-preflight.d.ts +23 -0
  126. package/dist/src/commands/re-export-preflight.js +160 -0
  127. package/dist/src/commands/re-export-purity-scope.d.ts +18 -0
  128. package/dist/src/commands/re-export-purity-scope.js +131 -0
  129. package/dist/src/commands/re-export-register.js +56 -17
  130. package/dist/src/commands/re-export-removed.d.ts +13 -2
  131. package/dist/src/commands/re-export-removed.js +57 -12
  132. package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
  133. package/dist/src/commands/re-export-scan-owner.js +116 -0
  134. package/dist/src/commands/re-export-scan.d.ts +19 -9
  135. package/dist/src/commands/re-export-scan.js +60 -24
  136. package/dist/src/commands/re-export-single.js +122 -46
  137. package/dist/src/commands/re-export-stale-registrations.d.ts +18 -0
  138. package/dist/src/commands/re-export-stale-registrations.js +62 -0
  139. package/dist/src/commands/re-export-summary.d.ts +28 -0
  140. package/dist/src/commands/re-export-summary.js +36 -0
  141. package/dist/src/commands/re-export.js +71 -57
  142. package/dist/src/commands/rebase/index.js +17 -17
  143. package/dist/src/commands/run.js +16 -2
  144. package/dist/src/commands/status-check.d.ts +55 -16
  145. package/dist/src/commands/status-check.js +92 -37
  146. package/dist/src/commands/status-json.d.ts +22 -9
  147. package/dist/src/commands/status-json.js +47 -32
  148. package/dist/src/commands/status-output.js +3 -1
  149. package/dist/src/commands/status-scope.d.ts +37 -0
  150. package/dist/src/commands/status-scope.js +54 -0
  151. package/dist/src/commands/status.js +66 -23
  152. package/dist/src/commands/test-browser-preflight.js +19 -3
  153. package/dist/src/commands/test-build-furnace.d.ts +22 -0
  154. package/dist/src/commands/test-build-furnace.js +54 -0
  155. package/dist/src/commands/test-build-phase.js +64 -41
  156. package/dist/src/commands/test-checkjs.js +10 -2
  157. package/dist/src/commands/test-diagnose.js +7 -0
  158. package/dist/src/commands/test-dispatch.d.ts +39 -0
  159. package/dist/src/commands/test-dispatch.js +211 -0
  160. package/dist/src/commands/test-furnace-reach.d.ts +37 -0
  161. package/dist/src/commands/test-furnace-reach.js +132 -0
  162. package/dist/src/commands/test-modes.d.ts +16 -1
  163. package/dist/src/commands/test-modes.js +24 -1
  164. package/dist/src/commands/test-multi-verdict.d.ts +61 -0
  165. package/dist/src/commands/test-multi-verdict.js +111 -0
  166. package/dist/src/commands/test-orchestrate.d.ts +52 -0
  167. package/dist/src/commands/test-orchestrate.js +240 -0
  168. package/dist/src/commands/test-perf-scope.d.ts +50 -0
  169. package/dist/src/commands/test-perf-scope.js +138 -0
  170. package/dist/src/commands/test-prelock.d.ts +51 -0
  171. package/dist/src/commands/test-prelock.js +273 -0
  172. package/dist/src/commands/test-register.js +69 -10
  173. package/dist/src/commands/test-results.d.ts +62 -0
  174. package/dist/src/commands/test-results.js +62 -0
  175. package/dist/src/commands/test-run.d.ts +13 -55
  176. package/dist/src/commands/test-run.js +74 -99
  177. package/dist/src/commands/test-shards.d.ts +110 -0
  178. package/dist/src/commands/test-shards.js +255 -0
  179. package/dist/src/commands/test-stale-gate.d.ts +5 -1
  180. package/dist/src/commands/test-stale-gate.js +60 -28
  181. package/dist/src/commands/test-verdict.d.ts +11 -1
  182. package/dist/src/commands/test-verdict.js +28 -1
  183. package/dist/src/commands/test.d.ts +6 -1
  184. package/dist/src/commands/test.js +120 -124
  185. package/dist/src/commands/token-lint.d.ts +14 -0
  186. package/dist/src/commands/token-lint.js +151 -0
  187. package/dist/src/commands/token.js +10 -0
  188. package/dist/src/commands/verify-scope.d.ts +28 -0
  189. package/dist/src/commands/verify-scope.js +67 -0
  190. package/dist/src/commands/verify.d.ts +10 -2
  191. package/dist/src/commands/verify.js +67 -36
  192. package/dist/src/core/build-baseline-types.d.ts +46 -0
  193. package/dist/src/core/build-baseline-validate.js +19 -0
  194. package/dist/src/core/build-baseline.d.ts +5 -1
  195. package/dist/src/core/build-baseline.js +3 -1
  196. package/dist/src/core/build-overwrite-guard.d.ts +50 -0
  197. package/dist/src/core/build-overwrite-guard.js +84 -4
  198. package/dist/src/core/build-prepare.d.ts +13 -0
  199. package/dist/src/core/build-prepare.js +42 -10
  200. package/dist/src/core/build-run-record.d.ts +74 -0
  201. package/dist/src/core/build-run-record.js +136 -0
  202. package/dist/src/core/checkjs-support-heads.d.ts +41 -0
  203. package/dist/src/core/checkjs-support-heads.js +104 -0
  204. package/dist/src/core/config-paths.d.ts +2 -2
  205. package/dist/src/core/config-paths.js +10 -0
  206. package/dist/src/core/config-validate-check.d.ts +8 -0
  207. package/dist/src/core/config-validate-check.js +219 -0
  208. package/dist/src/core/config-validate-file-size.d.ts +9 -6
  209. package/dist/src/core/config-validate-file-size.js +91 -2
  210. package/dist/src/core/config-validate-history.d.ts +11 -0
  211. package/dist/src/core/config-validate-history.js +38 -0
  212. package/dist/src/core/config-validate-patch-policy.js +100 -0
  213. package/dist/src/core/config-validate-test-toolchains.js +21 -0
  214. package/dist/src/core/config-validate-token-scales.d.ts +9 -0
  215. package/dist/src/core/config-validate-token-scales.js +92 -0
  216. package/dist/src/core/config-validate.js +20 -5
  217. package/dist/src/core/config.d.ts +5 -0
  218. package/dist/src/core/config.js +16 -0
  219. package/dist/src/core/coverage-dispatch.d.ts +24 -0
  220. package/dist/src/core/coverage-dispatch.js +44 -0
  221. package/dist/src/core/coverage-extend.d.ts +70 -45
  222. package/dist/src/core/coverage-extend.js +199 -144
  223. package/dist/src/core/coverage-records.d.ts +108 -0
  224. package/dist/src/core/coverage-records.js +384 -0
  225. package/dist/src/core/destructive.d.ts +7 -4
  226. package/dist/src/core/destructive.js +10 -10
  227. package/dist/src/core/engine-session-lock.d.ts +16 -1
  228. package/dist/src/core/engine-session-lock.js +38 -10
  229. package/dist/src/core/export-moved-code-shape.d.ts +49 -11
  230. package/dist/src/core/export-moved-code-shape.js +135 -44
  231. package/dist/src/core/file-lock-queue.d.ts +67 -0
  232. package/dist/src/core/file-lock-queue.js +221 -0
  233. package/dist/src/core/file-lock-shared.d.ts +57 -0
  234. package/dist/src/core/file-lock-shared.js +117 -0
  235. package/dist/src/core/file-lock.d.ts +13 -25
  236. package/dist/src/core/file-lock.js +163 -140
  237. package/dist/src/core/furnace-apply-output.d.ts +10 -0
  238. package/dist/src/core/furnace-apply-output.js +31 -1
  239. package/dist/src/core/furnace-apply.js +22 -12
  240. package/dist/src/core/furnace-composed-source.d.ts +76 -0
  241. package/dist/src/core/furnace-composed-source.js +167 -0
  242. package/dist/src/core/furnace-config-validate-exceptions.d.ts +16 -0
  243. package/dist/src/core/furnace-config-validate-exceptions.js +102 -0
  244. package/dist/src/core/furnace-config-validate.js +14 -0
  245. package/dist/src/core/furnace-css-fragments.d.ts +10 -0
  246. package/dist/src/core/furnace-css-fragments.js +26 -0
  247. package/dist/src/core/furnace-operation.d.ts +3 -3
  248. package/dist/src/core/furnace-registration-jar-inplace.d.ts +12 -0
  249. package/dist/src/core/furnace-registration-jar-inplace.js +80 -0
  250. package/dist/src/core/furnace-registration-remove.d.ts +22 -1
  251. package/dist/src/core/furnace-registration-remove.js +53 -17
  252. package/dist/src/core/furnace-registration-rename.d.ts +26 -0
  253. package/dist/src/core/furnace-registration-rename.js +77 -0
  254. package/dist/src/core/furnace-registration.d.ts +14 -4
  255. package/dist/src/core/furnace-registration.js +44 -10
  256. package/dist/src/core/furnace-staleness.d.ts +7 -0
  257. package/dist/src/core/furnace-staleness.js +34 -24
  258. package/dist/src/core/furnace-validate-accessibility.d.ts +8 -4
  259. package/dist/src/core/furnace-validate-accessibility.js +71 -28
  260. package/dist/src/core/furnace-validate-exceptions.d.ts +48 -0
  261. package/dist/src/core/furnace-validate-exceptions.js +80 -0
  262. package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
  263. package/dist/src/core/furnace-validate-icon-button.js +78 -0
  264. package/dist/src/core/furnace-validate.js +1 -1
  265. package/dist/src/core/harness-port-fallback.d.ts +60 -0
  266. package/dist/src/core/harness-port-fallback.js +96 -0
  267. package/dist/src/core/history-log-file.d.ts +61 -0
  268. package/dist/src/core/history-log-file.js +164 -0
  269. package/dist/src/core/history-log.d.ts +2 -1
  270. package/dist/src/core/history-log.js +2 -1
  271. package/dist/src/core/json-preserving-edit.d.ts +22 -0
  272. package/dist/src/core/json-preserving-edit.js +199 -0
  273. package/dist/src/core/line-prefixer.d.ts +18 -0
  274. package/dist/src/core/line-prefixer.js +34 -0
  275. package/dist/src/core/mach.d.ts +7 -0
  276. package/dist/src/core/mach.js +32 -10
  277. package/dist/src/core/module-resolution-preflight.d.ts +19 -3
  278. package/dist/src/core/module-resolution-preflight.js +24 -6
  279. package/dist/src/core/negative-control-compiled.d.ts +27 -0
  280. package/dist/src/core/negative-control-compiled.js +73 -0
  281. package/dist/src/core/negative-control-diff.d.ts +51 -0
  282. package/dist/src/core/negative-control-diff.js +179 -0
  283. package/dist/src/core/negative-control.d.ts +150 -0
  284. package/dist/src/core/negative-control.js +399 -0
  285. package/dist/src/core/objdir-install-manifests.d.ts +73 -0
  286. package/dist/src/core/objdir-install-manifests.js +226 -0
  287. package/dist/src/core/park-store.d.ts +63 -0
  288. package/dist/src/core/park-store.js +216 -0
  289. package/dist/src/core/patch-export-update.d.ts +6 -0
  290. package/dist/src/core/patch-export-update.js +13 -3
  291. package/dist/src/core/patch-export.d.ts +3 -2
  292. package/dist/src/core/patch-export.js +22 -16
  293. package/dist/src/core/patch-files.js +26 -8
  294. package/dist/src/core/patch-lint-checkjs-any-import.d.ts +39 -0
  295. package/dist/src/core/patch-lint-checkjs-any-import.js +63 -0
  296. package/dist/src/core/patch-lint-checkjs-diagnostics.d.ts +17 -0
  297. package/dist/src/core/patch-lint-checkjs-diagnostics.js +32 -0
  298. package/dist/src/core/patch-lint-checkjs.d.ts +10 -1
  299. package/dist/src/core/patch-lint-checkjs.js +20 -13
  300. package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
  301. package/dist/src/core/patch-lint-code-lines.js +105 -0
  302. package/dist/src/core/patch-lint-cross.d.ts +3 -1
  303. package/dist/src/core/patch-lint-cross.js +17 -4
  304. package/dist/src/core/patch-lint-css.d.ts +8 -2
  305. package/dist/src/core/patch-lint-css.js +24 -14
  306. package/dist/src/core/patch-lint-file-size.d.ts +12 -5
  307. package/dist/src/core/patch-lint-file-size.js +22 -11
  308. package/dist/src/core/patch-lint-forward-registration-extended.d.ts +23 -0
  309. package/dist/src/core/patch-lint-forward-registration-extended.js +36 -11
  310. package/dist/src/core/patch-lint-forward-registration.js +10 -0
  311. package/dist/src/core/patch-lint-ignore.d.ts +196 -0
  312. package/dist/src/core/patch-lint-ignore.js +429 -0
  313. package/dist/src/core/patch-lint-ownership.d.ts +10 -0
  314. package/dist/src/core/patch-lint-ownership.js +26 -0
  315. package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
  316. package/dist/src/core/patch-lint-size-thresholds.js +110 -0
  317. package/dist/src/core/patch-lint-size.d.ts +117 -0
  318. package/dist/src/core/patch-lint-size.js +331 -0
  319. package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
  320. package/dist/src/core/patch-lint-staged-registration.js +20 -0
  321. package/dist/src/core/patch-lint-token-scale.d.ts +57 -0
  322. package/dist/src/core/patch-lint-token-scale.js +286 -0
  323. package/dist/src/core/patch-lint.d.ts +11 -83
  324. package/dist/src/core/patch-lint.js +66 -288
  325. package/dist/src/core/patch-lock.js +7 -6
  326. package/dist/src/core/patch-manifest-consistency.js +7 -6
  327. package/dist/src/core/patch-manifest-io.d.ts +6 -3
  328. package/dist/src/core/patch-manifest-io.js +27 -9
  329. package/dist/src/core/patch-manifest-key-order.d.ts +24 -0
  330. package/dist/src/core/patch-manifest-key-order.js +61 -0
  331. package/dist/src/core/patch-manifest-stage.d.ts +46 -0
  332. package/dist/src/core/patch-manifest-stage.js +96 -0
  333. package/dist/src/core/patch-manifest-validate.js +61 -3
  334. package/dist/src/core/patch-order-legal.d.ts +26 -0
  335. package/dist/src/core/patch-order-legal.js +88 -0
  336. package/dist/src/core/patch-policy-content.d.ts +40 -0
  337. package/dist/src/core/patch-policy-content.js +216 -0
  338. package/dist/src/core/patch-policy.d.ts +22 -5
  339. package/dist/src/core/patch-policy.js +26 -12
  340. package/dist/src/core/patch-retired.d.ts +57 -0
  341. package/dist/src/core/patch-retired.js +92 -0
  342. package/dist/src/core/patch-subset.d.ts +16 -0
  343. package/dist/src/core/patch-subset.js +66 -0
  344. package/dist/src/core/preflight-wait.d.ts +20 -0
  345. package/dist/src/core/preflight-wait.js +38 -0
  346. package/dist/src/core/rebase-from-version.d.ts +46 -0
  347. package/dist/src/core/rebase-from-version.js +71 -0
  348. package/dist/src/core/run-log.d.ts +26 -0
  349. package/dist/src/core/run-log.js +106 -4
  350. package/dist/src/core/run-profile.d.ts +7 -0
  351. package/dist/src/core/run-profile.js +7 -1
  352. package/dist/src/core/smoke-patterns.d.ts +5 -0
  353. package/dist/src/core/smoke-patterns.js +13 -0
  354. package/dist/src/core/test-harness-crash.d.ts +19 -1
  355. package/dist/src/core/test-harness-crash.js +8 -9
  356. package/dist/src/core/test-harness-signature.d.ts +33 -0
  357. package/dist/src/core/test-harness-signature.js +23 -1
  358. package/dist/src/core/test-raw-log.d.ts +66 -0
  359. package/dist/src/core/test-raw-log.js +122 -0
  360. package/dist/src/core/test-run-completeness.d.ts +90 -0
  361. package/dist/src/core/test-run-completeness.js +184 -0
  362. package/dist/src/core/test-stale-check.d.ts +27 -1
  363. package/dist/src/core/test-stale-check.js +39 -49
  364. package/dist/src/core/test-stale-exempt.d.ts +40 -0
  365. package/dist/src/core/test-stale-exempt.js +70 -0
  366. package/dist/src/core/test-xpcshell-retry.d.ts +2 -0
  367. package/dist/src/core/test-xpcshell-retry.js +2 -1
  368. package/dist/src/core/token-lint.d.ts +53 -0
  369. package/dist/src/core/token-lint.js +131 -0
  370. package/dist/src/core/token-sibling-sheets.d.ts +46 -0
  371. package/dist/src/core/token-sibling-sheets.js +119 -0
  372. package/dist/src/core/tree-guard.js +5 -0
  373. package/dist/src/core/typecheck-shim.d.ts +6 -2
  374. package/dist/src/core/typecheck-shim.js +205 -4
  375. package/dist/src/core/typecheck.js +3 -1
  376. package/dist/src/index.d.ts +14 -3
  377. package/dist/src/index.js +6 -1
  378. package/dist/src/types/commands/index.d.ts +2 -2
  379. package/dist/src/types/commands/options.d.ts +212 -24
  380. package/dist/src/types/commands/patches.d.ts +118 -1
  381. package/dist/src/types/config.d.ts +264 -0
  382. package/dist/src/types/config.js +9 -0
  383. package/dist/src/types/furnace.d.ts +63 -1
  384. package/dist/src/types/furnace.js +7 -0
  385. package/dist/src/types/index.d.ts +1 -1
  386. package/dist/src/utils/options.d.ts +36 -7
  387. package/dist/src/utils/options.js +94 -8
  388. package/dist/src/utils/path-glob.d.ts +15 -0
  389. package/dist/src/utils/path-glob.js +47 -0
  390. package/dist/src/utils/regex.d.ts +15 -3
  391. package/dist/src/utils/regex.js +37 -16
  392. package/docs/configuration.md +369 -11
  393. package/docs/exit-codes.md +18 -1
  394. package/docs/furnace.md +139 -3
  395. package/docs/lifecycle-invariants.md +65 -30
  396. package/docs/machine-output.md +120 -12
  397. package/docs/patch-workflow.md +458 -22
  398. package/docs/run-logs.md +20 -1
  399. package/docs/testing.md +346 -21
  400. package/package.json +3 -3
@@ -21,10 +21,11 @@ command, read the decision table at the bottom first.
21
21
  take the `.fireforge/engine-session.lock`, then subsystem locks such as
22
22
  build, furnace, or patch-directory locks. Furnace mutations serialize on a
23
23
  per-root lock, and patch directory mutations (filename allocation plus
24
- manifest writes) on a per-directory lock. Dry runs hold the engine-session
25
- lock too, because a `re-export --dry-run` over an untracked binary legally
26
- touches the git index, and holding the lock serializes even "read-only"
27
- previews against real writers.
24
+ manifest writes) on a per-directory lock. `re-export --dry-run` is the
25
+ exception: it writes nothing, runs its git plumbing on a private index
26
+ (so even the `--intent-to-add` staging an untracked binary needs never
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
29
 
29
30
  3. **A furnace mutation that does not finish restores the engine or says
30
31
  so.** Both a signal (SIGINT or SIGTERM) and a thrown error trigger a
@@ -73,6 +74,17 @@ command, read the decision table at the bottom first.
73
74
  prompting, so the flag only showed up in the history record. Enforced by
74
75
  `patch-tier-and-lint-ignore.integration.test.ts`.
75
76
 
77
+ The history log is write-only: FireForge appends to it and no command
78
+ reads it back, so nothing depends on its contents or location. It lives
79
+ 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
84
+ process), `pid`, `ppid`, and `sessionId` when `FIREFORGE_SESSION_ID` is
85
+ set, so a session sharing the checkout can select its own lines.
86
+ Enforced by `history-log-file.test.ts`.
87
+
76
88
  7. **A verification tree never mutates shared state.** Trees are full
77
89
  project-root CoW snapshots under `.fireforge/trees/<name>`, with no
78
90
  merge-back model.
@@ -204,16 +216,28 @@ command, read the decision table at the bottom first.
204
216
  `git-readonly-index.test.ts`, against a real repository and a real index
205
217
  mtime.
206
218
 
207
- 10. **Lock queues are observable, and leaving one is as reliable as joining
208
- it.** A waiter that contends registers a file in `<lock>.waiters/` named
209
- `<startedAtMs>-<pid>-<uuid>` and deregisters in a `finally` that covers
210
- both outcomes: acquired the lock, or timed out. Depth counts only entries
211
- whose PID is still alive, so a killed waiter cannot inflate it forever,
212
- and the registry is advisory: failures to register are logged verbosely
213
- and never fail an acquisition. `fireforge status --lock` and the
214
- `--wait-lock` progress line read it. Enforced by `lock-visibility.test.ts`
215
- (a real concurrent waiter) and `engine-session-lock.test.ts` (queue
216
- position in the wait line).
219
+ 10. **Lock queues are observable, first come first served, and leaving one
220
+ is as reliable as joining it.** A waiter that contends registers a ticket
221
+ in `<lock>.waiters/` named `<startedAtMs>-<pid>-<uuid>` and deregisters in
222
+ a `finally` that covers both outcomes: acquired the lock, or timed out.
223
+ 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
225
+ 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
229
+ ticket whose PID is gone (or reused, where the start tick can tell) is
230
+ skipped and removed, and one its owner has not refreshed for 30 s (every
231
+ waiter rewrites its ticket every 5 s) is skipped, which covers a stopped
232
+ waiter and PID reuse elsewhere. A registry that cannot be written falls
233
+ back to the old unordered contention rather than failing an acquisition.
234
+ This applies to every `withFileLock` lock: the engine-session, patch,
235
+ furnace and build locks among them. `fireforge status --lock` and the
236
+ `--wait-lock` progress lines read it, and the lines name the ordering
237
+ rule. Enforced by `file-lock-queue.test.ts` (arrival order against faster
238
+ later pollers, crashed, killed and stopped waiters, timeouts),
239
+ `lock-visibility.test.ts` (a real concurrent waiter) and
240
+ `engine-session-lock.test.ts` (queue position in the wait line).
217
241
 
218
242
  11. **A manifest repair preserves what it cannot recompute, or refuses.**
219
243
  `doctor --repair-patches-manifest` is a merge: only `filesAffected` and
@@ -325,13 +349,14 @@ real-procfs `withFileLock` reap of a live-PID stale record.
325
349
  The project-wide engine-session lock under `.fireforge/`, stamped with PID,
326
350
  command, and start time metadata. CLI `build`, `test`, `export`, `re-export`,
327
351
  `furnace apply`, `furnace deploy` and `furnace sync` take it before subsystem
328
- locks, unconditionally, including dry runs: a `re-export --dry-run` over an
329
- untracked binary legally touches the git index (temporary `--intent-to-add`
330
- staging), so holding the lock serializes even "read-only" previews against
331
- real writers. Dry-run purity is separately enforced at runtime by
332
- `withDryRunPurityGuard` (`re-export-bulk-scan.ts`), which fingerprints the
333
- engine tree and every patch artifact around the dry run and fails hard on
334
- divergence.
352
+ locks. `re-export --dry-run` does not: it runs inside `withPrivateGitIndex`,
353
+ so its temporary `--intent-to-add` staging lands on a throwaway index, and it
354
+ serializes only against other dry runs. Dry-run purity is enforced at runtime
355
+ by `withDryRunPurityGuard` (`re-export-bulk-scan.ts`). For `re-export` it
356
+ fingerprints what the dry run could touch (the engine HEAD and index, the
357
+ selected patches' bodies, their `patches.json` rows and the engine files they
358
+ own) and fails hard on divergence, naming a foreign writer when the history
359
+ log or the engine-session lock shows one in the window (`re-export-purity-scope.ts`).
335
360
 
336
361
  Contention fails fast after about a second by default, with the fixed message
337
362
  "Another FireForge engine-mutating command is already running. Wait for it to
@@ -346,7 +371,10 @@ run and how large the budget is.
346
371
 
347
372
  The budget is not the engine lock's alone. It covers every lock the command
348
373
  takes, which for the three furnace commands means the per-root furnace lock as
349
- well (see the `furnace-operation.ts` section). `FIREFORGE_WAIT_LOCK=<seconds>`
374
+ well (see the `furnace-operation.ts` section). The furnace leaves that take
375
+ only the furnace lock (`create`, `remove`, `rename`, `refresh`, `scan`,
376
+ `override`, `preview`, `chrome-doc create`/`remove`) honour the flag too, for
377
+ that one lock. `FIREFORGE_WAIT_LOCK=<seconds>`
350
378
  sets the same budget for a whole session, parsed with the same bounds as the
351
379
  flag so the two cannot drift, and an explicit budget at a call site always
352
380
  wins. Commands that take no lock accept `--wait-lock` and ignore it, so a
@@ -364,11 +392,10 @@ cleanups and a `completed` flag), signal rollback with bounded timeouts,
364
392
 
365
393
  The lock's wait budget is `options.lockTimeoutMs`, threaded from `--wait-lock`
366
394
  by `waitLockMutationOptions`, which does the seconds-to-milliseconds
367
- conversion once. It falls back to `sessionWaitLockMs()`
368
- (`FIREFORGE_WAIT_LOCK`) and then to `DEFAULT_LOCK_TIMEOUT_MS`. That fallback
369
- is what covers the twelve furnace mutators that declare no flag (`create`,
370
- `remove`, `rename`, `refresh`, `scan`, `override`, `chrome-doc`, and the
371
- rest). A contended refusal here names the furnace lock, says it is the second
395
+ conversion once. Every furnace leaf that takes the lock threads its own
396
+ flag. It falls back to `sessionWaitLockMs()` (`FIREFORGE_WAIT_LOCK`) and then
397
+ to `DEFAULT_LOCK_TIMEOUT_MS`; that fallback is the only budget for callers
398
+ that thread none (the pre-build apply, `doctor --repair-furnace`). A contended refusal here names the furnace lock, says it is the second
372
399
  of two, and points at `--wait-lock` and `FIREFORGE_WAIT_LOCK`. A budget that
373
400
  is paid in full and then refuses anyway is the worst shape a budget can have,
374
401
  so the refusal must not read as the engine lock's.
@@ -404,7 +431,9 @@ hold it.
404
431
 
405
432
  The destructive-operation contract: `confirmDestructive` (summary, prompt,
406
433
  `--yes`, `--dry-run`, conflict refusal, `--force-unsafe`) and the
407
- `.fireforge-history.jsonl` audit log.
434
+ append-only history log (`src/core/history-log-file.ts` resolves its path,
435
+ migrates a legacy `patches/.fireforge-history.jsonl`, and stamps each
436
+ record's invocation and session).
408
437
 
409
438
  ### `src/core/tree-store.ts`
410
439
 
@@ -444,9 +473,15 @@ On SIGINT or SIGTERM, `bin/fireforge.ts`:
444
473
  work: the group sweep and the caller's `postCloseSweep` (the harness
445
474
  helper reap in `fireforge test`). Both run before the child's closure
446
475
  settles, so a forwarded SIGTERM cannot race `process.exit` past them.
447
- 3. Force-releases the furnace lock directories of active operations, since
476
+ 3. Restores any `test --negative-control` mutation still applied
477
+ (`restoreActiveNegativeControlsForSignal`), after child shutdown and
478
+ before any lock is released, so the next lock holder never builds or
479
+ tests the neuter. A restore the run itself already started is awaited.
480
+ A control a SIGKILL left behind is undone by the next `fireforge test`
481
+ from its on-disk journal (`src/core/negative-control.ts`).
482
+ 4. Force-releases the furnace lock directories of active operations, since
448
483
  `withFileLock`'s `finally` will never run past `process.exit`.
449
- 4. Calls `process.exit(130 | 143)`.
484
+ 5. Calls `process.exit(130 | 143)`.
450
485
 
451
486
  A parent that dies without signalling FireForge (SIGKILL on the `npm` above
452
487
  it) enters no handler here. `fireforge test` covers that case itself with
@@ -53,27 +53,46 @@ if (options.json === true) {
53
53
 
54
54
  ## Current surfaces
55
55
 
56
- | Surface | Shape | Notes |
57
- | ---------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58
- | `status --json` | `{ schemaVersion: 1, … }` | Error envelope on three engine preconditions |
59
- | `tree list --json` | `{ schemaVersion: 1, trees }` | Error envelope added in 0.44.0 |
60
- | `token list --json` | `{ schemaVersion: 1, tokensCssPath, categories }` | Categories in file order, each with its tokens (`name`, `line`, `value`) |
61
- | `token show --json` | `{ schemaVersion: 1, name, category, declarations }` | One entry per declaring block, with its selector trail and line |
62
- | `test` verdict line | `FIREFORGE-VERDICT: PASS\|FAIL reason=… [note=<class>] [shuffle=<seed>] [log=<path>]` | Not JSON. A single final stdout line, sealed by `setStdoutSealed` so nothing can displace it. `log=` names the run's own complete output under `.fireforge/logs/`, so a piped or truncated run still leaves a re-readable artifact |
63
- | `lint --per-patch --report <path>` | `{ schemaVersion: 1, … }` | Written to a file, never read back by FireForge |
64
- | `status --raw` | `git status --porcelain` lines | Not JSON, but still a machine surface: it engages the same stdout discipline, so rules 3 and 4 apply and rules 1 and 2 do not. A clean tree prints nothing |
65
- | `build` log announcement | `Full build output: <path>` | Not JSON. `build` prints no verdict line, so it names its run log directly on the way out |
56
+ | Surface | Shape | Notes |
57
+ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58
+ | `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 }` |
60
+ | `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
+ | `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
+ | `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 |
64
+ | `token list --json` | `{ schemaVersion: 1, tokensCssPath, categories }` | Categories in file order, each with its tokens (`name`, `line`, `value`) |
65
+ | `token show --json` | `{ schemaVersion: 1, name, category, declarations }` | One entry per declaring block, with its selector trail and line |
66
+ | `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` |
67
+ | `test` verdict line | `FIREFORGE-VERDICT: PASS\|FAIL reason=… [note=<class>] [started=<n>/<m> first-unstarted=<path> started-source=<src>] [shards=<p>/<t> [failed-shards=<labels>]] [runs=<p>/<n>] [control=<outcome>] [shuffle=<seed>] [furnace-applied=<names>\|furnace-apply=skipped] [log=<path>]` | Not JSON. A single final stdout line, sealed by `setStdoutSealed` so nothing can displace it. `log=` names the run's own complete output under `.fireforge/logs/`, so a piped or truncated run still leaves a re-readable artifact |
68
+ | `lint --per-patch --report <path>` | `{ schemaVersion: 1, patches, queueIssues, totals, … }` | Written to a file. `queueIssues` holds the cross-patch findings; `patch staged-dependency --add --from` reads back their `stagedDeclarations` |
69
+ | `status --raw` | `git status --porcelain` lines | Not JSON, but still a machine surface: it engages the same stdout discipline, so rules 3 and 4 apply and rules 1 and 2 do not. A clean tree prints nothing |
70
+ | `build` log announcement | `Full build output: <path>` | Not JSON. `build` prints no verdict line, so it names its run log directly on the way out |
66
71
 
67
72
  `--raw` is why `cli.ts` reads `process.argv` for `--json` or `--raw` before
68
73
  dispatching. Rule 3 is about who owns stdout, and that question is settled
69
74
  the same way for a porcelain stream as for a JSON document.
70
75
 
71
- `doctor` and `verify` have no `--json` mode. `doctor` already computes a
76
+ `doctor` and `verify` have no `--json` mode (`fireforge check` reports
77
+ `verify` as a step with its exit code and output tail). `doctor` already computes a
72
78
  serializable `DoctorCheck[]`, so adding one is cheap if a consumer asks for
73
79
  it. Follow the rules above rather than inventing a shape.
74
80
 
75
81
  The run logs behind `log=` are specified in [`run-logs.md`](run-logs.md).
76
82
 
83
+ Additive keys since 0.51.0 outside `test`:
84
+
85
+ - `check --json`: a skipped step carries `skipKind`, `config` when
86
+ `check.skip` names it and `unavailable` when the project has nothing for
87
+ it to run. The verdict line adds `skipped-steps=<steps>` whenever a step
88
+ is skipped, and `reason=skipped-steps` when `check.failOnSkipped` fails a
89
+ run on an `unavailable` skip alone.
90
+ - `furnace validate --json`: a finding about particular elements carries
91
+ `tags` (`no-keyboard-handler` names each synthetic element with an
92
+ `@click`). An exception that matched an error adds a
93
+ `validation-exception-on-error` error, and a full run under
94
+ `furnace.json#minComponents` a `component-floor` error on `furnace.json`.
95
+
77
96
  ## Versioning
78
97
 
79
98
  `schemaVersion` is `MACHINE_OUTPUT_SCHEMA_VERSION` in
@@ -111,7 +130,76 @@ tokenises `key=value` pairs is unaffected, and a gate that names no class
111
130
  emits no `note=` at all. The classes are not a closed set and may grow, so
112
131
  consumers should treat an unrecognised one as opaque. Current values:
113
132
  `browser-busy`, `stale-browser`, `stale-browser-kill-failed`, `marionette-port-busy`, `mochitest-port-busy`,
114
- `coverage-replaced`, `stale-build`, `stale-components`.
133
+ `coverage-replaced`, `stale-build`, `stale-components`, `no-launchable-binary`, `negative-control` (the
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
136
+ `reason=inconclusive` line whose control could not be restored byte-exact.
137
+
138
+ ### Multi-run invocations: `FIREFORGE-RUN:`, `runs=` and `control=`
139
+
140
+ A mixed-harness request, `--repeat <n>` and `--negative-control <diff>` run
141
+ several mach invocations under one lock hold (see
142
+ [`testing.md`](testing.md#several-runs-under-one-lock-hold)). Each sub-run
143
+ writes one raw stdout line before the verdict:
144
+
145
+ ```
146
+ FIREFORGE-RUN: PASS|FAIL [reason=…] [note=…] [checks=<n>] [unexpected=<n>] [shards=<p>/<t>] run=<i>/<n> [harness=xpcshell|mochitest]
147
+ ```
148
+
149
+ Its keys are the ones that sub-run's verdict line would have carried alone.
150
+ A `FIREFORGE-RUN:` line is never a verdict: the invocation still ends with
151
+ exactly one `FIREFORGE-VERDICT:` line. With more than one planned run it
152
+ carries `runs=<passed>/<planned>` and no count keys; a failed aggregate takes
153
+ the most structural reason among its runs (`inconclusive`, `preflight`,
154
+ `crash`, `harness-arguments`, `no-tests`, `test-failures`) and that run's
155
+ `note=`. A run that never started (the invocation stopped at a refusal)
156
+ counts toward `<planned>` but not `<passed>`.
157
+
158
+ `control=caught|missed|inconclusive` is present only for a negative control.
159
+ `caught` means every repetition went red with `reason=test-failures`, and
160
+ the process exits 0 even though the status is `FAIL`; `missed` means a
161
+ repetition stayed green (exit 5); `inconclusive` covers every other outcome.
162
+ A one-run control keeps its run's full line, counts included, and appends
163
+ the key.
164
+
165
+ ### The additive `started=` and `first-unstarted=` keys
166
+
167
+ Written together on a `FAIL reason=test-failures` line when a run that mach
168
+ exited 0 on was refused its pass because requested test files never started:
169
+ `started=<n>/<m>` counts the requested test files with a start (or skip)
170
+ marker, and `first-unstarted=<path>` names the first, in dispatch order,
171
+ that has none. A run refused only for a non-zero `unexpected=` count or a
172
+ mid-test shutdown carries neither. Absent on every other line. See
173
+ [`testing.md`](testing.md#the-verdict-line).
174
+
175
+ `started-source=raw|stream|capture` follows them and names the evidence the
176
+ count came from: the mozlog structured log (a run with `--results` or
177
+ `--shard-size`), the complete streamed output (every other run), or, only
178
+ when nothing was streamed, the last 2 MB of the capture.
179
+
180
+ ### Shards: `FIREFORGE-SHARD:` and `failed-shards=`
181
+
182
+ A sharded run prints one `FIREFORGE-SHARD:` line per finished shard, before
183
+ the verdict line:
184
+
185
+ ```
186
+ FIREFORGE-SHARD: PASS|FAIL [reason=…] [checks=<n>] [unexpected=<n>] shard=<i>/<n> path=<label> [failed-files=<path>,…]
187
+ ```
188
+
189
+ `path=` is the shard's path argument (whitespace replaced by `_`), or a
190
+ `--shard-size`/`--jobs` chunk's first file plus `+<k>`. `failed-files=`
191
+ appears under `--shard-size` and names the chunk's files that failed. The
192
+ aggregate verdict appends `failed-shards=<label>,…` after `shards=` when any
193
+ shard did not pass; under `--shard-size` `shards=` counts files and
194
+ `failed-shards=` names files. Both are absent on a green run.
195
+
196
+ ### Additive keys of parallel and per-file runs
197
+
198
+ `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
200
+ run whose engine changed mid-run only in files the running browser cannot
201
+ see keeps its verdict and appends `drift-tolerated=<n>`. A negative control
202
+ on compiled source appends `control-build=compiled`.
115
203
 
116
204
  ### The additive `shuffle=` key
117
205
 
@@ -158,3 +246,23 @@ gains `fireforgeHost: { start, end, powerSourceChanged }`, where each host
158
246
  state includes `load`, `power` and an optional `topProcess` with CPU and
159
247
  command. Consumers should tolerate this additive object. Unknown power
160
248
  state is visible but never treated as a proven transition.
249
+
250
+ On a perf run (`--perf-samples`), an `engine/` change during the run makes
251
+ the verdict `reason=inconclusive` only when HEAD moved or a changed path is
252
+ packaged by the running build: a source an objdir install manifest names, a
253
+ directory one installs from, or a test file. Edits that no manifest installs
254
+ (compiled sources, build files, docs) leave the sample standing with a
255
+ warning naming them. Other runs are inconclusive only when a changed path is
256
+ one the running browser can see: a file the objdir links to its source, one
257
+ staged at dispatch, or a test file or staging input of the run; anything
258
+ else leaves the verdict standing with `drift-tolerated=<n>`.
259
+
260
+ ### Additive Furnace keys
261
+
262
+ A `fireforge test --build` or `--build-only` run whose pre-test Furnace apply
263
+ deployed components appends `furnace-applied=<name>,<name>…`: the components
264
+ whose `components/` source changed since their last deploy, which the
265
+ runtime under test therefore includes. `--no-furnace-apply` skips that apply
266
+ and appends `furnace-apply=skipped`. `--furnace-apply reached` appends
267
+ `furnace-skipped=<names>` for components with undeployed edits that no
268
+ requested test reaches. All are absent otherwise.