@hominis/fireforge 0.46.0 → 0.46.2
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.
- package/CHANGELOG.md +381 -498
- package/dist/bin/fireforge.d.ts +1 -1
- package/dist/bin/fireforge.js +17 -17
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +9 -9
- package/dist/src/cli-usage-error.js +12 -12
- package/dist/src/cli.d.ts +3 -2
- package/dist/src/cli.js +29 -28
- package/dist/src/commands/bootstrap-checks.js +2 -1
- package/dist/src/commands/bootstrap.js +2 -2
- package/dist/src/commands/build.js +12 -11
- package/dist/src/commands/config.js +9 -9
- package/dist/src/commands/destructive-decision.d.ts +4 -4
- package/dist/src/commands/destructive-decision.js +4 -4
- package/dist/src/commands/discard.js +5 -5
- package/dist/src/commands/doctor-check-core.d.ts +4 -4
- package/dist/src/commands/doctor-external-toolchains.js +2 -2
- package/dist/src/commands/doctor-furnace-config-sync.d.ts +2 -2
- package/dist/src/commands/doctor-furnace-config-sync.js +20 -20
- package/dist/src/commands/doctor-furnace-jar.d.ts +1 -1
- package/dist/src/commands/doctor-furnace-jar.js +1 -1
- package/dist/src/commands/doctor-furnace.js +22 -22
- package/dist/src/commands/doctor-orphaned-harness.d.ts +10 -11
- package/dist/src/commands/doctor-orphaned-harness.js +10 -11
- package/dist/src/commands/doctor-patch-manifest.d.ts +2 -1
- package/dist/src/commands/doctor-patch-manifest.js +7 -6
- package/dist/src/commands/doctor-post-rebase-audit.js +2 -2
- package/dist/src/commands/doctor-source-pin.d.ts +12 -12
- package/dist/src/commands/doctor-source-pin.js +14 -14
- package/dist/src/commands/doctor-working-tree.d.ts +3 -3
- package/dist/src/commands/doctor-working-tree.js +3 -3
- package/dist/src/commands/doctor.d.ts +2 -2
- package/dist/src/commands/doctor.js +9 -9
- package/dist/src/commands/download.js +15 -13
- package/dist/src/commands/export-all.js +8 -8
- package/dist/src/commands/export-flow.d.ts +5 -5
- package/dist/src/commands/export-flow.js +14 -14
- package/dist/src/commands/export-placement-conflicts.d.ts +3 -3
- package/dist/src/commands/export-placement-conflicts.js +9 -9
- package/dist/src/commands/export-placement-gate.d.ts +2 -2
- package/dist/src/commands/export-placement-gate.js +5 -5
- package/dist/src/commands/export-placement-policy.d.ts +1 -1
- package/dist/src/commands/export-placement-policy.js +1 -1
- package/dist/src/commands/export-shared.d.ts +10 -10
- package/dist/src/commands/export-shared.js +13 -13
- package/dist/src/commands/export.js +4 -4
- package/dist/src/commands/furnace/apply.js +16 -16
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +1 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +14 -14
- package/dist/src/commands/furnace/chrome-doc-templates.js +17 -17
- package/dist/src/commands/furnace/chrome-doc-tests.d.ts +2 -2
- package/dist/src/commands/furnace/chrome-doc-tests.js +4 -4
- package/dist/src/commands/furnace/chrome-doc.d.ts +8 -8
- package/dist/src/commands/furnace/chrome-doc.js +18 -18
- package/dist/src/commands/furnace/create-browser-test.d.ts +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +4 -4
- package/dist/src/commands/furnace/create-dry-run.d.ts +4 -4
- package/dist/src/commands/furnace/create-dry-run.js +4 -4
- package/dist/src/commands/furnace/create-features.d.ts +2 -2
- package/dist/src/commands/furnace/create-features.js +2 -2
- package/dist/src/commands/furnace/create-mochikit.d.ts +4 -4
- package/dist/src/commands/furnace/create-mochikit.js +7 -7
- package/dist/src/commands/furnace/create-templates.d.ts +14 -14
- package/dist/src/commands/furnace/create-templates.js +18 -18
- package/dist/src/commands/furnace/create-xpcshell.d.ts +5 -5
- package/dist/src/commands/furnace/create-xpcshell.js +8 -8
- package/dist/src/commands/furnace/create.d.ts +1 -1
- package/dist/src/commands/furnace/create.js +10 -10
- package/dist/src/commands/furnace/deploy.js +4 -4
- package/dist/src/commands/furnace/diff.js +7 -7
- package/dist/src/commands/furnace/index.js +1 -1
- package/dist/src/commands/furnace/init.js +15 -15
- package/dist/src/commands/furnace/list.js +2 -2
- package/dist/src/commands/furnace/override.d.ts +1 -1
- package/dist/src/commands/furnace/override.js +15 -15
- package/dist/src/commands/furnace/preview.js +17 -17
- package/dist/src/commands/furnace/refresh.js +6 -6
- package/dist/src/commands/furnace/remove-mutations.d.ts +3 -3
- package/dist/src/commands/furnace/remove-mutations.js +19 -18
- package/dist/src/commands/furnace/remove-state.d.ts +1 -1
- package/dist/src/commands/furnace/remove-state.js +1 -1
- package/dist/src/commands/furnace/remove.js +2 -2
- package/dist/src/commands/furnace/rename-browser-test.js +9 -8
- package/dist/src/commands/furnace/rename-xpcshell.d.ts +2 -2
- package/dist/src/commands/furnace/rename-xpcshell.js +3 -3
- package/dist/src/commands/furnace/rename.js +9 -9
- package/dist/src/commands/furnace/scan.js +3 -3
- package/dist/src/commands/furnace/status.js +7 -7
- package/dist/src/commands/furnace/sync.js +6 -6
- package/dist/src/commands/furnace/test-file-name.d.ts +2 -2
- package/dist/src/commands/furnace/test-file-name.js +2 -2
- package/dist/src/commands/furnace/validate.js +11 -12
- package/dist/src/commands/import.js +20 -20
- package/dist/src/commands/lint-per-patch.js +8 -8
- package/dist/src/commands/lint-per-run-checkjs.d.ts +12 -13
- package/dist/src/commands/lint-per-run-checkjs.js +14 -15
- package/dist/src/commands/lint-report.d.ts +3 -3
- package/dist/src/commands/lint-report.js +4 -4
- package/dist/src/commands/lint.d.ts +3 -3
- package/dist/src/commands/lint.js +35 -35
- package/dist/src/commands/manifest.d.ts +4 -4
- package/dist/src/commands/package.js +2 -2
- package/dist/src/commands/patch/compact.d.ts +2 -2
- package/dist/src/commands/patch/compact.js +8 -8
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +8 -8
- package/dist/src/commands/patch/lint-ignore.d.ts +3 -4
- package/dist/src/commands/patch/lint-ignore.js +14 -15
- package/dist/src/commands/patch/move-files-create.d.ts +2 -2
- package/dist/src/commands/patch/move-files-create.js +5 -5
- package/dist/src/commands/patch/move-files-into.d.ts +2 -2
- package/dist/src/commands/patch/move-files-into.js +3 -3
- package/dist/src/commands/patch/move-files.js +1 -1
- package/dist/src/commands/patch/patch-context.d.ts +1 -1
- package/dist/src/commands/patch/rename.d.ts +4 -4
- package/dist/src/commands/patch/rename.js +8 -8
- package/dist/src/commands/patch/reorder.js +10 -10
- package/dist/src/commands/patch/split-plan.d.ts +3 -3
- package/dist/src/commands/patch/split-plan.js +6 -5
- package/dist/src/commands/patch/split.d.ts +2 -2
- package/dist/src/commands/patch/split.js +5 -5
- package/dist/src/commands/patch/staged-dependency-validate.d.ts +1 -1
- package/dist/src/commands/patch/staged-dependency-validate.js +3 -3
- package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
- package/dist/src/commands/patch/staged-dependency.js +6 -6
- package/dist/src/commands/patch/tier.d.ts +2 -2
- package/dist/src/commands/patch/tier.js +5 -5
- package/dist/src/commands/patch-rename-map.d.ts +1 -1
- package/dist/src/commands/patch-rename-map.js +1 -1
- package/dist/src/commands/pending-resolution.d.ts +5 -5
- package/dist/src/commands/pending-resolution.js +5 -5
- package/dist/src/commands/re-export-adjacent.d.ts +3 -3
- package/dist/src/commands/re-export-adjacent.js +8 -8
- package/dist/src/commands/re-export-bulk-scan.d.ts +3 -3
- package/dist/src/commands/re-export-bulk-scan.js +17 -17
- package/dist/src/commands/re-export-drift.d.ts +10 -10
- package/dist/src/commands/re-export-drift.js +29 -29
- package/dist/src/commands/re-export-files.js +14 -15
- package/dist/src/commands/re-export-lint.d.ts +26 -26
- package/dist/src/commands/re-export-lint.js +27 -27
- package/dist/src/commands/re-export-options.d.ts +1 -1
- package/dist/src/commands/re-export-options.js +1 -1
- package/dist/src/commands/re-export-register.d.ts +1 -1
- package/dist/src/commands/re-export-register.js +1 -1
- package/dist/src/commands/re-export-scan.d.ts +1 -1
- package/dist/src/commands/re-export-scan.js +7 -7
- package/dist/src/commands/re-export-single.d.ts +1 -1
- package/dist/src/commands/re-export-single.js +20 -20
- package/dist/src/commands/re-export.js +7 -6
- package/dist/src/commands/rebase/abort.js +6 -6
- package/dist/src/commands/rebase/continue.js +8 -7
- package/dist/src/commands/rebase/index.d.ts +1 -1
- package/dist/src/commands/rebase/index.js +3 -3
- package/dist/src/commands/rebase/patch-loop.js +18 -17
- package/dist/src/commands/register.js +5 -5
- package/dist/src/commands/reset.js +1 -1
- package/dist/src/commands/resolve.d.ts +2 -2
- package/dist/src/commands/resolve.js +3 -3
- package/dist/src/commands/run.js +26 -26
- package/dist/src/commands/setup-support.js +4 -4
- package/dist/src/commands/setup.js +1 -1
- package/dist/src/commands/source.js +1 -1
- package/dist/src/commands/status-check.d.ts +3 -3
- package/dist/src/commands/status-check.js +4 -4
- package/dist/src/commands/status-json.d.ts +14 -13
- package/dist/src/commands/status-json.js +9 -8
- package/dist/src/commands/status-output.d.ts +3 -3
- package/dist/src/commands/status-output.js +3 -3
- package/dist/src/commands/status-ownership.d.ts +3 -3
- package/dist/src/commands/status-ownership.js +3 -3
- package/dist/src/commands/status.js +22 -22
- package/dist/src/commands/test-build-phase.d.ts +1 -1
- package/dist/src/commands/test-build-phase.js +17 -17
- package/dist/src/commands/test-diagnose.d.ts +1 -1
- package/dist/src/commands/test-diagnose.js +20 -20
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +4 -4
- package/dist/src/commands/test-run.d.ts +11 -11
- package/dist/src/commands/test-run.js +15 -15
- package/dist/src/commands/test-stale-gate.d.ts +12 -12
- package/dist/src/commands/test-stale-gate.js +13 -13
- package/dist/src/commands/test-verdict.d.ts +6 -6
- package/dist/src/commands/test-verdict.js +11 -11
- package/dist/src/commands/test.d.ts +3 -3
- package/dist/src/commands/test.js +36 -36
- package/dist/src/commands/token-coverage.js +3 -3
- package/dist/src/commands/token-list.js +5 -5
- package/dist/src/commands/token.js +9 -9
- package/dist/src/commands/tree.js +19 -19
- package/dist/src/commands/typecheck.d.ts +6 -6
- package/dist/src/commands/typecheck.js +7 -7
- package/dist/src/commands/verify.d.ts +3 -3
- package/dist/src/commands/verify.js +10 -10
- package/dist/src/commands/watch.js +11 -11
- package/dist/src/commands/wire.js +13 -13
- package/dist/src/core/ast-utils.d.ts +7 -7
- package/dist/src/core/ast-utils.js +8 -8
- package/dist/src/core/branding.d.ts +5 -5
- package/dist/src/core/branding.js +3 -3
- package/dist/src/core/browser-wire.d.ts +1 -1
- package/dist/src/core/browser-wire.js +4 -4
- package/dist/src/core/build-audit-platform.d.ts +6 -6
- package/dist/src/core/build-audit-platform.js +26 -26
- package/dist/src/core/build-audit-registration.d.ts +5 -5
- package/dist/src/core/build-audit-registration.js +7 -7
- package/dist/src/core/build-audit-resolve.d.ts +5 -5
- package/dist/src/core/build-audit-resolve.js +6 -6
- package/dist/src/core/build-audit-skip.d.ts +8 -8
- package/dist/src/core/build-audit-skip.js +12 -12
- package/dist/src/core/build-audit-transforms.d.ts +2 -2
- package/dist/src/core/build-audit-transforms.js +3 -3
- package/dist/src/core/build-audit.d.ts +8 -8
- package/dist/src/core/build-audit.js +39 -37
- package/dist/src/core/build-baseline-types.d.ts +19 -18
- package/dist/src/core/build-baseline-types.js +1 -1
- package/dist/src/core/build-baseline.d.ts +8 -8
- package/dist/src/core/build-baseline.js +14 -14
- package/dist/src/core/build-jar-escalation.d.ts +8 -8
- package/dist/src/core/build-jar-escalation.js +15 -15
- package/dist/src/core/build-overwrite-guard.d.ts +4 -4
- package/dist/src/core/build-overwrite-guard.js +13 -13
- package/dist/src/core/build-prepare.d.ts +4 -4
- package/dist/src/core/build-prepare.js +16 -15
- package/dist/src/core/config-mutate.js +4 -4
- package/dist/src/core/config-state.js +2 -2
- package/dist/src/core/config-validate-build-audit.js +3 -3
- package/dist/src/core/config-validate-file-size.d.ts +1 -1
- package/dist/src/core/config-validate-file-size.js +2 -2
- package/dist/src/core/config-validate.js +8 -9
- package/dist/src/core/config.d.ts +10 -10
- package/dist/src/core/config.js +10 -10
- package/dist/src/core/coverage-extend.d.ts +16 -16
- package/dist/src/core/coverage-extend.js +16 -16
- package/dist/src/core/destructive.d.ts +14 -14
- package/dist/src/core/destructive.js +9 -9
- package/dist/src/core/diff-hunks.d.ts +2 -2
- package/dist/src/core/diff-hunks.js +7 -7
- package/dist/src/core/discard-baseline.d.ts +10 -10
- package/dist/src/core/discard-baseline.js +15 -16
- package/dist/src/core/display-state.d.ts +11 -11
- package/dist/src/core/display-state.js +12 -12
- package/dist/src/core/engine-changes.d.ts +2 -2
- package/dist/src/core/engine-changes.js +2 -2
- package/dist/src/core/engine-precondition.d.ts +4 -4
- package/dist/src/core/engine-precondition.js +2 -2
- package/dist/src/core/engine-session-lock.d.ts +18 -18
- package/dist/src/core/engine-session-lock.js +37 -37
- package/dist/src/core/export-moved-code-shape.d.ts +7 -7
- package/dist/src/core/export-moved-code-shape.js +8 -8
- package/dist/src/core/file-lock-owner.d.ts +11 -11
- package/dist/src/core/file-lock-owner.js +18 -18
- package/dist/src/core/file-lock.d.ts +12 -12
- package/dist/src/core/file-lock.js +52 -39
- package/dist/src/core/firefox-cache.d.ts +1 -1
- package/dist/src/core/firefox-cache.js +10 -10
- package/dist/src/core/firefox-download.d.ts +3 -3
- package/dist/src/core/firefox-download.js +6 -6
- package/dist/src/core/firefox-extract.d.ts +2 -2
- package/dist/src/core/firefox-extract.js +13 -13
- package/dist/src/core/firefox.d.ts +5 -5
- package/dist/src/core/firefox.js +8 -8
- package/dist/src/core/furnace-apply-dry-run.js +1 -1
- package/dist/src/core/furnace-apply-ftl.d.ts +5 -5
- package/dist/src/core/furnace-apply-ftl.js +8 -8
- package/dist/src/core/furnace-apply-helpers.d.ts +9 -9
- package/dist/src/core/furnace-apply-helpers.js +21 -21
- package/dist/src/core/furnace-apply-output.js +1 -1
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +9 -9
- package/dist/src/core/furnace-apply-overwrite-warn.js +9 -9
- package/dist/src/core/furnace-apply-undeploy-actions.d.ts +1 -1
- package/dist/src/core/furnace-apply-undeploy-actions.js +1 -1
- package/dist/src/core/furnace-apply.d.ts +3 -3
- package/dist/src/core/furnace-apply.js +22 -21
- package/dist/src/core/furnace-checksum-utils.d.ts +5 -5
- package/dist/src/core/furnace-checksum-utils.js +5 -5
- package/dist/src/core/furnace-config-custom.d.ts +1 -1
- package/dist/src/core/furnace-config-custom.js +2 -2
- package/dist/src/core/furnace-config-order.js +1 -1
- package/dist/src/core/furnace-config-tokens.d.ts +3 -3
- package/dist/src/core/furnace-config-tokens.js +3 -3
- package/dist/src/core/furnace-config-validate.js +2 -2
- package/dist/src/core/furnace-config.d.ts +4 -4
- package/dist/src/core/furnace-config.js +18 -18
- package/dist/src/core/furnace-constants.d.ts +6 -6
- package/dist/src/core/furnace-constants.js +6 -6
- package/dist/src/core/furnace-css-fragments.d.ts +6 -6
- package/dist/src/core/furnace-css-fragments.js +11 -11
- package/dist/src/core/furnace-dir-entry.d.ts +1 -1
- package/dist/src/core/furnace-dir-entry.js +1 -1
- package/dist/src/core/furnace-jsconfig.d.ts +7 -7
- package/dist/src/core/furnace-jsconfig.js +22 -20
- package/dist/src/core/furnace-operation.d.ts +21 -20
- package/dist/src/core/furnace-operation.js +36 -34
- package/dist/src/core/furnace-precondition.d.ts +10 -10
- package/dist/src/core/furnace-precondition.js +8 -8
- package/dist/src/core/furnace-refresh.js +2 -2
- package/dist/src/core/furnace-registration-ast.d.ts +2 -2
- package/dist/src/core/furnace-registration-ast.js +9 -9
- package/dist/src/core/furnace-registration-remove.d.ts +4 -4
- package/dist/src/core/furnace-registration-remove.js +6 -6
- package/dist/src/core/furnace-registration-validate.d.ts +2 -2
- package/dist/src/core/furnace-registration-validate.js +2 -2
- package/dist/src/core/furnace-registration.d.ts +5 -5
- package/dist/src/core/furnace-registration.js +7 -7
- package/dist/src/core/furnace-rollback.d.ts +1 -1
- package/dist/src/core/furnace-rollback.js +5 -5
- package/dist/src/core/furnace-stale-export.d.ts +17 -16
- package/dist/src/core/furnace-stale-export.js +16 -15
- package/dist/src/core/furnace-staleness.d.ts +2 -2
- package/dist/src/core/furnace-staleness.js +2 -2
- package/dist/src/core/furnace-state-persist.d.ts +10 -10
- package/dist/src/core/furnace-state-persist.js +10 -10
- package/dist/src/core/furnace-step-errors.d.ts +6 -6
- package/dist/src/core/furnace-step-errors.js +6 -6
- package/dist/src/core/furnace-validate-accessibility.js +14 -14
- package/dist/src/core/furnace-validate-compatibility.js +9 -9
- package/dist/src/core/furnace-validate-registration.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +13 -13
- package/dist/src/core/furnace-validate-structure.d.ts +1 -1
- package/dist/src/core/furnace-validate-structure.js +4 -4
- package/dist/src/core/furnace-validate.js +9 -9
- package/dist/src/core/furnace-version-drift.d.ts +5 -5
- package/dist/src/core/furnace-version-drift.js +5 -5
- package/dist/src/core/git-base.d.ts +4 -4
- package/dist/src/core/git-base.js +9 -9
- package/dist/src/core/git-diff-new-file.d.ts +4 -4
- package/dist/src/core/git-diff-new-file.js +8 -8
- package/dist/src/core/git-diff.d.ts +4 -4
- package/dist/src/core/git-diff.js +35 -34
- package/dist/src/core/git-file-ops.d.ts +6 -6
- package/dist/src/core/git-file-ops.js +7 -7
- package/dist/src/core/git-readonly-index.d.ts +12 -12
- package/dist/src/core/git-readonly-index.js +12 -12
- package/dist/src/core/git-status.d.ts +4 -4
- package/dist/src/core/git-status.js +4 -4
- package/dist/src/core/git.d.ts +3 -3
- package/dist/src/core/git.js +14 -14
- package/dist/src/core/harness-orphans.d.ts +17 -17
- package/dist/src/core/harness-orphans.js +20 -20
- package/dist/src/core/history-log.d.ts +3 -3
- package/dist/src/core/history-log.js +3 -3
- package/dist/src/core/license-headers.d.ts +19 -18
- package/dist/src/core/license-headers.js +29 -28
- package/dist/src/core/mach-build-artifacts.d.ts +21 -21
- package/dist/src/core/mach-build-artifacts.js +23 -23
- package/dist/src/core/mach-error-hints.d.ts +5 -5
- package/dist/src/core/mach-error-hints.js +13 -13
- package/dist/src/core/mach-known-noise-filter.d.ts +41 -38
- package/dist/src/core/mach-known-noise-filter.js +53 -50
- package/dist/src/core/mach-mozconfig.d.ts +6 -6
- package/dist/src/core/mach-mozconfig.js +15 -15
- package/dist/src/core/mach-python.d.ts +2 -1
- package/dist/src/core/mach-python.js +4 -3
- package/dist/src/core/mach-resource-shim.d.ts +12 -12
- package/dist/src/core/mach-resource-shim.js +20 -20
- package/dist/src/core/mach.d.ts +23 -23
- package/dist/src/core/mach.js +36 -36
- package/dist/src/core/marionette-port.d.ts +15 -13
- package/dist/src/core/marionette-port.js +34 -31
- package/dist/src/core/marionette-preflight.d.ts +6 -6
- package/dist/src/core/marionette-preflight.js +23 -23
- package/dist/src/core/markdown-table.d.ts +9 -9
- package/dist/src/core/markdown-table.js +14 -14
- package/dist/src/core/mochitest-server-port.d.ts +8 -8
- package/dist/src/core/mochitest-server-port.js +14 -14
- package/dist/src/core/module-resolution-preflight.d.ts +8 -8
- package/dist/src/core/module-resolution-preflight.js +11 -11
- package/dist/src/core/moz-manifest-helpers.d.ts +7 -7
- package/dist/src/core/moz-manifest-helpers.js +7 -7
- package/dist/src/core/moz-manifest-register.d.ts +1 -1
- package/dist/src/core/moz-manifest-register.js +2 -2
- package/dist/src/core/moz-manifest-rules.js +11 -11
- package/dist/src/core/moz-manifest-tokenizers.js +1 -1
- package/dist/src/core/ownership-table.d.ts +5 -5
- package/dist/src/core/ownership-table.js +7 -7
- package/dist/src/core/parser-fallback.d.ts +3 -3
- package/dist/src/core/parser-fallback.js +3 -3
- package/dist/src/core/patch-apply-fuzz.d.ts +5 -5
- package/dist/src/core/patch-apply-fuzz.js +9 -9
- package/dist/src/core/patch-apply.d.ts +9 -9
- package/dist/src/core/patch-apply.js +30 -30
- package/dist/src/core/patch-export-update.d.ts +4 -4
- package/dist/src/core/patch-export-update.js +3 -3
- package/dist/src/core/patch-export.d.ts +10 -10
- package/dist/src/core/patch-export.js +13 -13
- package/dist/src/core/patch-identifier-suggest.d.ts +1 -1
- package/dist/src/core/patch-identifier-suggest.js +4 -4
- package/dist/src/core/patch-lint-binary.d.ts +7 -7
- package/dist/src/core/patch-lint-cache.d.ts +2 -2
- package/dist/src/core/patch-lint-cache.js +13 -13
- package/dist/src/core/patch-lint-checkjs.d.ts +26 -26
- package/dist/src/core/patch-lint-checkjs.js +42 -41
- package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +8 -9
- package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -11
- package/dist/src/core/patch-lint-creators.d.ts +3 -3
- package/dist/src/core/patch-lint-creators.js +3 -3
- package/dist/src/core/patch-lint-cross.d.ts +13 -13
- package/dist/src/core/patch-lint-cross.js +21 -21
- package/dist/src/core/patch-lint-css.d.ts +1 -1
- package/dist/src/core/patch-lint-css.js +5 -5
- package/dist/src/core/patch-lint-diff-tag.d.ts +5 -5
- package/dist/src/core/patch-lint-diff-tag.js +5 -5
- package/dist/src/core/patch-lint-diff.d.ts +4 -4
- package/dist/src/core/patch-lint-diff.js +4 -4
- package/dist/src/core/patch-lint-file-size.d.ts +3 -3
- package/dist/src/core/patch-lint-file-size.js +3 -3
- package/dist/src/core/patch-lint-forward-registration.d.ts +4 -4
- package/dist/src/core/patch-lint-forward-registration.js +12 -12
- package/dist/src/core/patch-lint-jsdoc.d.ts +2 -2
- package/dist/src/core/patch-lint-jsdoc.js +11 -11
- package/dist/src/core/patch-lint-module-registration.d.ts +1 -1
- package/dist/src/core/patch-lint-module-registration.js +1 -1
- package/dist/src/core/patch-lint-mozbuild.d.ts +1 -1
- package/dist/src/core/patch-lint-mozbuild.js +10 -10
- package/dist/src/core/patch-lint-observer.d.ts +3 -3
- package/dist/src/core/patch-lint-observer.js +7 -7
- package/dist/src/core/patch-lint-ownership.d.ts +6 -6
- package/dist/src/core/patch-lint-ownership.js +6 -6
- package/dist/src/core/patch-lint-prettier.d.ts +6 -6
- package/dist/src/core/patch-lint-prettier.js +6 -6
- package/dist/src/core/patch-lint-queue-types.d.ts +1 -1
- package/dist/src/core/patch-lint-queue-types.js +1 -1
- package/dist/src/core/patch-lint-staged-registration.d.ts +2 -2
- package/dist/src/core/patch-lint-staged-registration.js +6 -6
- package/dist/src/core/patch-lint-unmanaged-companion.d.ts +23 -23
- package/dist/src/core/patch-lint-unmanaged-companion.js +26 -26
- package/dist/src/core/patch-lint.d.ts +27 -26
- package/dist/src/core/patch-lint.js +48 -46
- package/dist/src/core/patch-lock.d.ts +1 -1
- package/dist/src/core/patch-lock.js +8 -8
- package/dist/src/core/patch-manifest-consistency.d.ts +7 -7
- package/dist/src/core/patch-manifest-consistency.js +16 -17
- package/dist/src/core/patch-manifest-files-affected.d.ts +1 -1
- package/dist/src/core/patch-manifest-files-affected.js +3 -3
- package/dist/src/core/patch-manifest-io.d.ts +14 -14
- package/dist/src/core/patch-manifest-io.js +27 -27
- package/dist/src/core/patch-manifest-query.d.ts +1 -1
- package/dist/src/core/patch-manifest-query.js +4 -4
- package/dist/src/core/patch-manifest-resolve.d.ts +5 -5
- package/dist/src/core/patch-manifest-resolve.js +7 -7
- package/dist/src/core/patch-manifest-validate.js +3 -3
- package/dist/src/core/patch-manifest.d.ts +2 -2
- package/dist/src/core/patch-manifest.js +2 -2
- package/dist/src/core/patch-parse.d.ts +14 -14
- package/dist/src/core/patch-parse.js +16 -16
- package/dist/src/core/patch-registration-refs.d.ts +4 -4
- package/dist/src/core/patch-registration-refs.js +7 -7
- package/dist/src/core/patch-transform.d.ts +8 -8
- package/dist/src/core/patch-transform.js +13 -13
- package/dist/src/core/process-cpu-time.d.ts +1 -1
- package/dist/src/core/process-cpu-time.js +5 -5
- package/dist/src/core/rebase-session.d.ts +7 -7
- package/dist/src/core/rebase-session.js +8 -8
- package/dist/src/core/register-result.d.ts +1 -1
- package/dist/src/core/register-result.js +1 -1
- package/dist/src/core/register-scaffold.d.ts +2 -2
- package/dist/src/core/register-scaffold.js +3 -3
- package/dist/src/core/register-shared-css.d.ts +3 -3
- package/dist/src/core/register-shared-css.js +4 -4
- package/dist/src/core/register-xpcshell-test.d.ts +2 -2
- package/dist/src/core/register-xpcshell-test.js +2 -2
- package/dist/src/core/run-log-redact.d.ts +12 -12
- package/dist/src/core/run-log-redact.js +16 -16
- package/dist/src/core/run-log.d.ts +9 -9
- package/dist/src/core/run-log.js +14 -14
- package/dist/src/core/shared-ftl.d.ts +2 -2
- package/dist/src/core/shared-ftl.js +6 -6
- package/dist/src/core/signal-critical.d.ts +2 -2
- package/dist/src/core/signal-critical.js +2 -2
- package/dist/src/core/smoke-patterns.d.ts +5 -5
- package/dist/src/core/smoke-patterns.js +9 -9
- package/dist/src/core/state-file.d.ts +3 -3
- package/dist/src/core/status-binary.d.ts +8 -8
- package/dist/src/core/status-binary.js +12 -12
- package/dist/src/core/status-classify.d.ts +17 -17
- package/dist/src/core/status-classify.js +11 -11
- package/dist/src/core/test-changed-prefs.d.ts +12 -12
- package/dist/src/core/test-changed-prefs.js +15 -15
- package/dist/src/core/test-harness-crash.d.ts +39 -39
- package/dist/src/core/test-harness-crash.js +67 -67
- package/dist/src/core/test-harness-output.d.ts +7 -7
- package/dist/src/core/test-harness-output.js +7 -7
- package/dist/src/core/test-harness-verdict-notes.d.ts +13 -14
- package/dist/src/core/test-harness-verdict-notes.js +4 -4
- package/dist/src/core/test-path-scope.d.ts +8 -8
- package/dist/src/core/test-path-scope.js +11 -11
- package/dist/src/core/test-stale-check.d.ts +22 -21
- package/dist/src/core/test-stale-check.js +29 -28
- package/dist/src/core/test-stale-symlink.d.ts +1 -1
- package/dist/src/core/test-stale-symlink.js +1 -1
- package/dist/src/core/test-stall-triage.d.ts +9 -9
- package/dist/src/core/test-stall-triage.js +22 -22
- package/dist/src/core/token-category.d.ts +4 -4
- package/dist/src/core/token-category.js +13 -12
- package/dist/src/core/token-coverage.js +4 -5
- package/dist/src/core/token-dark-mode.d.ts +17 -9
- package/dist/src/core/token-dark-mode.js +41 -28
- package/dist/src/core/token-docs.d.ts +1 -1
- package/dist/src/core/token-inventory.d.ts +6 -6
- package/dist/src/core/token-inventory.js +7 -7
- package/dist/src/core/token-manager.d.ts +3 -3
- package/dist/src/core/token-manager.js +21 -21
- package/dist/src/core/token-scaffold.d.ts +4 -4
- package/dist/src/core/token-scaffold.js +4 -4
- package/dist/src/core/token-variant.d.ts +14 -14
- package/dist/src/core/token-variant.js +32 -32
- package/dist/src/core/toolchain-preflight.d.ts +15 -15
- package/dist/src/core/toolchain-preflight.js +22 -22
- package/dist/src/core/tree-cow.d.ts +3 -3
- package/dist/src/core/tree-cow.js +6 -6
- package/dist/src/core/tree-guard.d.ts +3 -3
- package/dist/src/core/tree-guard.js +23 -23
- package/dist/src/core/tree-store.d.ts +15 -15
- package/dist/src/core/tree-store.js +35 -33
- package/dist/src/core/typecheck-shim.d.ts +16 -15
- package/dist/src/core/typecheck-shim.js +26 -25
- package/dist/src/core/typecheck.d.ts +12 -12
- package/dist/src/core/typecheck.js +31 -31
- package/dist/src/core/wire-destroy.d.ts +2 -2
- package/dist/src/core/wire-destroy.js +5 -5
- package/dist/src/core/wire-dom-fragment.d.ts +3 -3
- package/dist/src/core/wire-dom-fragment.js +6 -6
- package/dist/src/core/wire-init.d.ts +2 -2
- package/dist/src/core/wire-init.js +7 -7
- package/dist/src/core/wire-subscript.d.ts +1 -1
- package/dist/src/core/wire-subscript.js +3 -3
- package/dist/src/core/wire-utils.d.ts +9 -9
- package/dist/src/core/wire-utils.js +11 -11
- package/dist/src/core/xpcshell-appdir.d.ts +20 -20
- package/dist/src/core/xpcshell-appdir.js +26 -26
- package/dist/src/core/xpcshell-profile-dir.d.ts +3 -3
- package/dist/src/core/xpcshell-profile-dir.js +7 -7
- package/dist/src/errors/base.d.ts +22 -21
- package/dist/src/errors/base.js +22 -21
- package/dist/src/errors/build.d.ts +6 -6
- package/dist/src/errors/build.js +6 -6
- package/dist/src/errors/codes.d.ts +10 -10
- package/dist/src/errors/codes.js +10 -10
- package/dist/src/errors/furnace.d.ts +2 -2
- package/dist/src/errors/git.d.ts +1 -1
- package/dist/src/errors/git.js +1 -1
- package/dist/src/errors/patch.d.ts +3 -3
- package/dist/src/errors/patch.js +3 -3
- package/dist/src/errors/rebase.d.ts +1 -1
- package/dist/src/errors/rebase.js +1 -1
- package/dist/src/errors/run.d.ts +1 -1
- package/dist/src/errors/run.js +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.js +4 -4
- package/dist/src/types/commands/options.d.ts +86 -85
- package/dist/src/types/commands/patches.d.ts +19 -19
- package/dist/src/types/commands/project.d.ts +3 -3
- package/dist/src/types/config.d.ts +24 -23
- package/dist/src/types/furnace.d.ts +17 -17
- package/dist/src/types/json.d.ts +3 -3
- package/dist/src/types/json.js +2 -2
- package/dist/src/types/typecheck.d.ts +3 -3
- package/dist/src/utils/assert.d.ts +1 -1
- package/dist/src/utils/assert.js +6 -6
- package/dist/src/utils/build-info.d.ts +5 -5
- package/dist/src/utils/build-info.js +6 -6
- package/dist/src/utils/child-env.d.ts +5 -5
- package/dist/src/utils/child-env.js +5 -5
- package/dist/src/utils/concurrency.d.ts +2 -2
- package/dist/src/utils/concurrency.js +2 -2
- package/dist/src/utils/elapsed.js +1 -1
- package/dist/src/utils/errors.d.ts +8 -8
- package/dist/src/utils/errors.js +8 -8
- package/dist/src/utils/fs.d.ts +1 -1
- package/dist/src/utils/fs.js +8 -8
- package/dist/src/utils/line-dispatch.d.ts +3 -3
- package/dist/src/utils/line-dispatch.js +5 -5
- package/dist/src/utils/logger.d.ts +4 -4
- package/dist/src/utils/logger.js +11 -11
- package/dist/src/utils/machine-output.js +2 -2
- package/dist/src/utils/options.d.ts +13 -13
- package/dist/src/utils/options.js +20 -20
- package/dist/src/utils/package-root.js +2 -2
- package/dist/src/utils/paths.d.ts +4 -4
- package/dist/src/utils/paths.js +4 -4
- package/dist/src/utils/process-group.d.ts +8 -8
- package/dist/src/utils/process-group.js +19 -19
- package/dist/src/utils/process.d.ts +34 -33
- package/dist/src/utils/process.js +20 -19
- package/dist/src/utils/ps-duration.d.ts +4 -4
- package/dist/src/utils/ps-duration.js +6 -6
- package/dist/src/utils/regex.d.ts +4 -4
- package/dist/src/utils/regex.js +7 -7
- package/dist/src/utils/sleep.d.ts +7 -7
- package/dist/src/utils/sleep.js +7 -7
- package/dist/src/utils/stdio-drain.d.ts +7 -7
- package/dist/src/utils/stdio-drain.js +8 -8
- package/dist/src/utils/validation.d.ts +11 -12
- package/dist/src/utils/validation.js +20 -21
- package/docs/README.md +2 -2
- package/docs/configuration.md +120 -114
- package/docs/exit-codes.md +63 -58
- package/docs/furnace.md +20 -19
- package/docs/lifecycle-invariants.md +412 -251
- package/docs/machine-output.md +33 -32
- package/docs/patch-workflow.md +48 -45
- package/docs/run-logs.md +62 -58
- package/docs/testing.md +101 -101
- package/docs/verification-trees.md +23 -22
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,702 +2,588 @@
|
|
|
2
2
|
|
|
3
3
|
## 0.46.0
|
|
4
4
|
|
|
5
|
-
###
|
|
6
|
-
|
|
7
|
-
- **A successful build printed seven `Audit:` warnings, none of them real, and summarised `4 missing` on a run with zero real misses.** The post-build packaging audit is warn-only, which makes a false positive cheap alone and ruinous in aggregate: an operator who learns these lines are usually wrong stops reading the one that is right. Four distinct causes, each now its own classification with its own counted skip reason. (1) `--with-branding` selects ONE branding tree; files under the others are not inputs to the objdir at all, so "no packaged artifact" is their expected state — and worse, the basename walker matched them against the SELECTED branding's same-named artifact and reported them "newer than its packaged artifact", a stale claim about a comparison with no meaning. The selected branding is read from the `engine/mozconfig` FireForge generates, through the `extractWithBrandingPath` that already existed for the branding-agreement assertion; when the mozconfig cannot be read or names no branding, the old behaviour is kept deliberately — a skip that cannot name its evidence is a masked warning. (2) `detectPlatformGate` reads the file's OWN nearest `moz.build`, but Firefox gates whole subtrees one or more levels up: nothing in `toolkit/mozapps/defaultagent/moz.build` says "Windows only", yet `toolkit/moz.build` reaches it through a `DIRS +=` nested inside `OS_ARCH == "WINNT"`, a `CC_TYPE` test and `MOZ_DEFAULT_BROWSER_AGENT`. A new ancestor walk collects the FULL enclosing `if` stack for the `DIRS` entry that reaches the file's directory — not the innermost, which `findEnclosingGate` returns, because it is the OUTERMOST one that excludes the directory off-Windows — and only quoted values inside a `DIRS` assignment are considered, so an unrelated string literal cannot gate a subtree. The walk is bounded (12 ancestors, 256 KiB per `moz.build`) and an unreadable or oversized ancestor falls back to warning. (3) `*.stories.mjs` / `*.stories.js` are rendered by the in-tree Storybook's own Vite config and carry no packaging entry; the suffix list is closed and documented rather than generalised to "anything with a dotted infix", which would swallow the `*.worker.js` and `*.sys.mjs` that ARE packaged. (4) A deliberately unpackaged source — a type-only mirror whose header says it is never loaded — is the one case FireForge cannot derive from the tree, so it takes a declaration: `buildAudit.unpackaged: [{ path, reason }]` in `fireforge.json`, `reason` required and non-empty because the declaration IS the evidence. It follows the 0.45.0 `--expect-unmanaged` model: admitted paths are LISTED, never silenced, and an admitted path that DOES resolve to a packaged artifact is reported as a STALE carve-out rather than suppressed — the declaration asserts a fact about the tree that has stopped being true. One deliberate divergence from that model: a declaration matching nothing that changed is NOT reported. `--expect-unmanaged` reports its unmet entries because it is a per-invocation flag list where an unmet entry is probably a typo in the command just typed; this is a standing list checked only against the files that happened to change, so "not met" is the normal case and reporting it would put a warning on every build. `**` in a carve-out path is refused outright — a subtree exception is how a reviewed one quietly becomes a blanket one. The `Packaged:` line now names every non-zero skip class with its count, so the four false `missing` cannot recur without a counterpart in the numbers an operator can check. A downstream-shaped fixture (selected branding, unselected branding, ancestor-gated directory, story file, declared mirror) asserts zero warnings and the per-class counts, with a negative control per class — a classifier that skipped unconditionally would also pass a zero-warnings assertion.
|
|
8
|
-
- **Considered and rejected: a header marker instead of the config list.** A first-comment `fireforge: unpackaged` line would keep the declaration next to the file it describes, which is the strongest argument for it, and the 0.45.0 reasoning against standing config lists ("a standing exception nobody re-reads is exactly what this refusal exists to prevent") appears to apply. It does not, and the difference is what decided it: `--expect-unmanaged` guards a REFUSAL, where a forgotten carve-out silently disarms a gate on the next run; this guards a WARNING, and the failure mode is the opposite — nobody can review a suppression they cannot enumerate. A marker is invisible to anyone auditing what a project has admitted, cannot be diffed as a set, and puts the rationale in the one place a reader of `fireforge.json` will not look. The config list is reviewable in one place, is what `git blame` answers questions about, and — because admitted paths are listed on every run and a stale one is reported — cannot rot unnoticed the way the 0.45.0 entry feared.
|
|
9
|
-
- **`fireforge test` preflight refusals were invisible in redirected output.** With stdout redirected, both `fireforge test …` and `fireforge -v test …` printed only `FIREFORGE-VERDICT: FAIL reason=preflight`, and the run log the line named held only the pre-test build. The gates were right; the text was lost twice over, for two independent reasons. `emitFailVerdict` calls `setStdoutSealed(true)` — the seal that keeps the verdict the run's LAST stdout write — and `routeToStderr()` then sends everything the CLI error boundary renders to stderr, so a `> file` capture kept the verdict and dropped the reason. And that rendering happens after `testCommand`'s `finally` has already closed and flushed the run log, so the artifact the verdict's own `log=` key points at never contained the refusal either. The obvious fix — unsealing, or rendering to stdout from the boundary — trades away the contract that made the verdict readable in the first place. Instead the refusal's own text is written to stdout AND into the run log inside `testCommand`'s catch, BEFORE the verdict is emitted: both channels get the reason and the verdict is still last. Preflight gates that classify themselves now throw `PreflightRefusalError`, which carries a stable kebab-case class and reports it as an additive `note=` on the verdict line (`stale-browser`, `coverage-replaced`, `marionette-port-busy`, …). `reason=` is unchanged and still comes from the closed set, `note=` joins the existing additive `log=`/`signal=`/`shards=` keys, and a gate that names no class emits no `note=` — so a consumer tokenising `key=value` pairs is unaffected. `PreflightRefusalError` extends `GeneralError` and keeps exit 1: no exit code moves.
|
|
10
|
-
- **The staged-dependency registration rule was blind to binary-created files, and its other half did not exist.** Patch 102's `xpcshell.toml` registers `support-files = ["fixtures/*.sqlite"]` for a fixture patch 103 creates as `new file mode` + `GIT binary patch`. With the declaration present, lint reported `staged-dependency-unused`; with it removed, lint was equally silent. Two independent defects, and the symmetry is what made them invisible rather than merely absent. First, `detectNewFilesInDiff` does see the binary creation, but `buildPatchQueueContext` then drops it: `extractNewFileContent` correctly refuses to decode a binary body, and the `catch` left the path out of `newFiles` — the map the registration `creates` resolution indexed. So a declared dependency on a binary could never resolve. A new `createdFiles: Set<string>` beside `newFiles` carries every creation regardless of body kind, and the registration index is built from it; `newFiles` stays text-only, because 0.45.0's `buildNewFileTextProjection` reasoning still holds — a binary authors no imports, so the import rules gain nothing from it. A payload-less `Binary files … differ` section counts as a creation too whenever it carries `new file mode`: the file exists after apply either way, and only the body differs. Making the field required forced every projection site (`export --order`, `re-export --files`, `re-export --scan`, `patch split`, `patch move-files`) to supply it, which also collapsed three open-coded copies of the projection onto the existing `projectEntryBody`. Second, there was no "declaration needed" arm for registrations at all: `forward-import` is import-only, and registration metadata was checked for STALENESS once declared and never for absence. A new `forward-registration` rule flags a test manifest that registers a file only a later-ordered patch creates, resolving each `support-files` entry against the manifest's own directory and expanding a `*` within one path segment. Scope is deliberately narrow and the alternative is rejected outright: jar.mn packaging lines and actor/customElements registrations name their targets through indirection the linter cannot resolve without the build system, and a rule that guessed there would refuse correct queues on a Firefox-sized tree. The manifest basename list is closed (`xpcshell`, `mochitest`, `browser`, `chrome`, `a11y`, `.toml` and `.ini`) rather than "anything `.toml`", which would sweep in Cargo and taskcluster manifests whose `support-files`-shaped keys mean something else. Both rules share one apply-order predicate, so the arm that FLAGS an undeclared registration and the arm that VALIDATES a declared one cannot disagree about the same pair of patches. The new rule is an **error**, matching `forward-import` for the identical defect class, and consumers should expect it to find pre-existing instances: measured against the reporting fork's own 338-patch queue it raises 48 findings where there were previously zero cross-patch findings, from exactly three shared test manifests (102's `xpcshell.toml`, 111's and 201's `browser.toml`) whose directories later patches add fixtures to. Those are genuine — applying the queue up to 201 leaves `browser.toml` naming support files that do not exist yet, which is a bisect-time failure — so they are reported rather than tuned away; a rule narrowed until one fork's queue passed would be shaped to that fork's data, and an exact filename is a MORE certain forward reference than the glob the report was about, not less. Each message carries the paste-and-run `patch staged-dependency --add --kind registration` that discharges it, and reordering is the other option.
|
|
11
|
-
- **The `forward-registration` remedy did not discharge the finding it printed.** All 44 unique paste-and-run commands the new rule emitted on the reporting fork's 338-patch queue were run verbatim; the next `lint --per-patch --max-warnings 0` came back with zero `forward-registration` errors and **36 `staged-dependency-unused` warnings** — "the patch does not add that line" — on the declarations the remedy had just told the operator to write. The 0.46.0 claim that both arms "share one apply-order predicate, so the arm that FLAGS and the arm that VALIDATES cannot disagree about the same pair of patches" was true and insufficient: they agreed about ORDER and disagreed about the LINE, and the line is what the operator pastes. The rule resolved a `support-files` ENTRY and synthesised `--line` as `support-files = ["<entry>"]`, while `isRegistrationLinePresent` compares `--line`, whitespace-trimmed, against the lines the patch ADDS — a match only for a manifest whose array is single-line and single-entry. A Firefox tree writes the other two shapes constantly: 201's `browser.toml` lists its support files one per line inside a multi-line array, and 111's and 201's per-test sections use two-entry one-liners, so 36 of 44 synthesised lines matched nothing. `extractSupportFileEntries` now matches quoted tokens per PHYSICAL line instead of over the accumulated array body, so every entry carries the line it is written on, and the printed `--line` is that line — which is what `--line`'s help text has always promised ("as the patch adds it") and what `--remove` matches on. Entries are deduplicated across the manifest, and an entry spelled out on several added lines prints the first and says so: one declaration covers the file either way, because the declared check keys on `--file` and `--creates`. Registration lines carry their own quotes, so both messages that print a command now escape them for the shell. **Considered and rejected: relaxing the used-arm** to treat a declaration as used when the patch adds a line CONTAINING the declared entry as a quoted token. It would also have cleared the 36, and it is the smaller diff, but it makes `--line` mean two different things in the two directions — the add path would accept a line the patch never adds, and `--remove --line` would then have to carry the same relaxed match or a declaration would be unremovable by the command that created it. Printing the truth keeps one meaning. The fork's 44 hand-corrected declarations are exactly the shapes the fixed rule now emits, and a fixture pins each shape so a later change cannot strand them.
|
|
12
|
-
- **The `staged-dependency-unused` remedy named a command that refuses without its arguments, and `--patches` refused the ordinal it advertised.** The warning said to remove the stale declaration with `fireforge patch staged-dependency --remove --kind registration`, but that form refuses without `--file` AND `--line` (`--creates` only disambiguates) — and the `--line` it needs is the stale one the declaration holds, which the warning already had in hand. It now prints the whole command, shell-quoted, so the remedy is paste-and-run in both directions. Separately, `lint --per-patch --patches 102` was refused with "No patch in the queue matches" by a message that offers stems in the same breath; the bare order number is now an alias, both as the manifest records it and as the filename pads it (`2` and `002`).
|
|
13
|
-
- **`fireforge token list` and `token show` are new.** `token add` requires `--category` and refuses one the tokens CSS does not declare, but nothing reported the categories a project has — so naming one meant hand-parsing a neighbouring `= Category =` banner out of the file. `token list [--category <name>] [--json]` reports each banner with its tokens in file order; `token show <name> [--json]` reports the owning category and the value the token takes in every declaring block (base `:root`, the dark `@media` mirror, each `:root[variant]`) with the line of each. Both read through the parser `token add` and `token coverage` already use — a second tokens-CSS reader would let the report and the writer disagree about which section a token lives in, which is exactly the mistake `token list` exists to prevent. Two shapes are reported rather than dropped, because dropping them would make a real file look like a different one: a declaration above the first banner (listed under `(no category)`) and a banner with no tokens under it (still listed — `token add --category` accepts it). An unknown `--category` is refused by naming the ones that exist, since a filter that printed nothing would read as "this category is empty", which is a different fact.
|
|
14
|
-
- **`--refuse-adjacent-unmanaged` named no anchor — and the report's other half is refuted.** The notice named the offending file and the patch but not WHICH of the patch's owned locations the file sits beside, so an unattended run could not triage a refusal on a patch owning files in several directories. Both the per-patch notice and the run-level refusal now name the anchoring directory and state the rule in one clause. The rest of the report does not survive checking: adjacency is NOT scoped to a shared ancestor. `findAdjacentUnmanagedFiles` has always scanned exactly the immediate directories of the patch's own `filesAffected` — there is nothing to narrow. Verified against the fork that reported it: `905-branding-trademark-artwork.patch` owns files only under `…/test/about/` and `…/test/contextMenu/`, so a file under `…/test/hominis-tiles/` cannot be adjacent to it under the current rule; the notice must have named one of the ~150 patches that DO own files in `hominis-tiles`, and the anchor is precisely what would have made that legible. The belt still catches a peer's file in the SAME directory as an owned file, which is the case it exists for, and the 0.45.0 residual blind spot (a cousin directory no owned file lives in) is unchanged.
|
|
15
|
-
- **The mach resource-monitor shim degraded on every 153.x build, and the reported diagnosis was wrong.** Every build printed `FireForge: host resource monitor degraded (SystemResourceMonitor.record_marker() takes 4 positional arguments but 5 were given)`, which reads as a stale arity in the shim's wrapper. It is not: the wrapper has been `(self, *args, **kwargs)` passthrough all along, so making it "signature-agnostic" would have changed nothing. The real cause is a DESCRIPTOR mismatch. In 153, `SystemResourceMonitor.record_marker` and `record_event` are `@staticmethod`s taking no receiver. `getattr(cls, name)` hands back the underlying plain function; setting that back on the class re-binds it as an ordinary instance method, so an instance-qualified call threads the receiver through as a fifth positional and the original raises — which tripped the shim's own degrade path and disabled resource monitoring for the whole build. The install loop now reads the raw class attribute with `inspect.getattr_static`, wraps a `staticmethod`/`classmethod` without injecting a receiver, and re-applies the descriptor it actually had; those wrappers consult the module-level host-degraded flag, since a static method has no instance to carry one. The degrade path is kept for a genuinely incompatible host. Pinned by a python3 harness that declares a 4-argument `@staticmethod record_marker` exactly as 153.0esr does and asserts both call shapes return the real value with no warning; the pre-fix source reproduces the reported message byte for byte. Deliberately NOT folded together with the recognised `stop_time`/`AttributeError` teardown noise `docs/testing.md` annotates — that is cosmetic upstream shutdown noise after `SUITE_END`, this silently disabled a subsystem for the whole run. Also confirmed rather than assumed: the shim directory that ships is the per-user, uid-suffixed `<tmpdir>/fireforge-mach-resource-shim-<uid>`, created `0700` and `lstat`-verified for ownership and mode before it joins `PYTHONPATH` — not the per-dispatch `mkdtemp` the 0.45.0 entry described and did not ship.
|
|
5
|
+
### Packaging audit
|
|
16
6
|
|
|
17
|
-
|
|
7
|
+
- The post-build audit no longer warns about files it was never going to package. It now skips unselected branding trees, directories gated off-platform by an ancestor `moz.build`, Storybook `*.stories.mjs`/`*.stories.js` files, and paths declared in `buildAudit.unpackaged` in `fireforge.json`. The `Packaged:` line counts every skip reason so the numbers can be checked.
|
|
8
|
+
- A declared carve-out that does resolve to a packaged artifact is reported as stale rather than hidden. `**` in a carve-out path is refused.
|
|
18
9
|
|
|
19
|
-
|
|
20
|
-
- **Archive preflight let `.git` members through.** `isUnsafeArchivePath` rejected only traversal. An archive carrying `firefox-x/.git/config` or `.git/hooks/*` extracted cleanly, and the subsequent `git init`/commit honoured whatever it configured (`core.hooksPath`, `core.fsmonitor`, hooks). Any member with a `.git` path segment at any depth is now rejected before extraction; `.gitmodules` and `.gitignore` remain legitimate upstream files and pass.
|
|
21
|
-
- **The mach PYTHONPATH shim lived at a fixed shared temp path.** The 0.45.0 entry described the shim as "minted per dispatch with `mkdtemp`"; that design did not ship. What shipped wrote `$TMPDIR/fireforge-mach-resource-shim/sitecustomize.py` with the default mode and no ownership check, so on a shared `/tmp` another local account could pre-create the directory and have its Python run inside every build and test dispatch. The directory is now per-user (uid-suffixed via `process.getuid`, not `os.userInfo()`, which throws for a uid with no passwd entry — containers run as an arbitrary uid would otherwise lose every build and test dispatch; username on Windows), created `0700`, and `lstat`-verified as a real directory owned by the current uid and not group/world-writable before it is placed on `PYTHONPATH`; the ownership and mode checks are skipped on Windows, where the per-user name still applies.
|
|
22
|
-
- **Run logs were written unredacted.** The `.fireforge/logs` tee copied every byte, including the environment dumps mach prints. A conservative per-line pass now masks `KEY=value` where `KEY` matches `TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|AUTH` (case-insensitive), including the `--password=…` and `-DAUTH_TOKEN=…` spellings mach and configure echo from their argv, and the credential in `Authorization:` headers — file sink only, terminal untouched, lines split across chunks handled. Bare tokens in URLs, space-separated CLI flag values, or JSON fields still pass through; `docs/run-logs.md` now states exactly what is and is not masked instead of "raw bytes". The tee treats a lone `\r` as a line end (progress-bar repaints never send a newline), caps the held partial line at 1 MiB so a newline-free stream cannot grow memory without bound, and is flushed from the SIGINT/SIGTERM path so a killed run's last line survives.
|
|
10
|
+
### Patch queue and lint
|
|
23
11
|
|
|
24
|
-
|
|
12
|
+
- New `forward-registration` lint error: a test manifest that registers a file only a later patch creates. On a 338-patch queue this finds 48 real cases in three shared manifests. Each message prints the `patch staged-dependency --add --kind registration` command that fixes it.
|
|
13
|
+
- The command that rule prints now quotes the manifest line the way the patch actually writes it, including multi-line and multi-entry arrays. Before this it produced declarations that immediately warned as unused.
|
|
14
|
+
- The `staged-dependency-unused` warning prints the full removal command instead of one that refuses without its arguments.
|
|
15
|
+
- `lint --per-patch --patches 102` accepts a bare order number, padded or not.
|
|
16
|
+
- Staged-dependency registration checks now see binary files. A patch creating a binary through `GIT binary patch` used to be invisible to the resolver.
|
|
17
|
+
- `patch compact` refuses if the queue changed between the confirmation prompt and the lock, the same gate `patch reorder` already had.
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
- **A recycled PID kept a dead lock honoured until the waiter's timeout.** Liveness was `process.kill(pid, 0)`, so a crashed holder's PID reused by a long-lived daemon looked alive. On Linux the owner record now carries the holder's own boot-relative start tick (`start-tick=`, field 22 of `/proc/self/stat`), placed among the metadata lines so pre-existing records and readers stay compatible; a stale probe reads the candidate's tick from `/proc/<pid>/stat` and a different tick is a different process, so the lock is stale. Ticks rather than wall-clock times, deliberately: `btime` moves with every clock step on current kernels, so comparing a recorded acquisition time against a `btime`-derived start would have reaped a LIVE build lock after NTP stepped the clock forward. The record also carries `acquired-at-ms=` for diagnostics only. macOS and Windows keep PID-only liveness deliberately: their only start-time source is a `ps`/`wmic` spawn per poll, not worth the cost. `docs/lifecycle-invariants.md` records both contracts and the suites that pin them.
|
|
19
|
+
### Patch bodies
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
- Fixed corruption of new non-UTF-8 text files on export. The body was built from a UTF-8 decode while the blob hash came from the real bytes, so the patch wrote different bytes than the tree held and drift detection flagged the file forever. Text vs binary is now decided by a NUL byte or invalid UTF-8, and the binary arm forces a byte-faithful `GIT binary patch`.
|
|
22
|
+
- Worktree-side renames no longer produce a phantom status entry. Git emits an unstaged rename as ` R new\0old\0`, and the old path was parsed as its own entry.
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
24
|
+
### Integrity
|
|
25
|
+
|
|
26
|
+
- Checksum verification fails closed. An unreachable `SHA256SUMS` used to accept the tarball on TLS alone. Ways out: fix connectivity, pin `firefox.sha256`, or set `firefox.allowUnverifiedDownload: true`. The checksum request now uses the same timeout and retry as the tarball fetch.
|
|
27
|
+
- Archive extraction rejects any member with a `.git` path segment. `.gitmodules` and `.gitignore` still pass.
|
|
28
|
+
- The mach `PYTHONPATH` shim directory is now per-user, created `0700`, and checked for ownership and mode before it joins `PYTHONPATH`. It used to be a fixed shared temp path any local account could pre-create.
|
|
29
|
+
- Run logs are redacted. `KEY=value` pairs matching `TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|AUTH`, `--password=` style flags, and `Authorization:` headers are masked in the log file only. `docs/run-logs.md` states what is and is not masked.
|
|
34
30
|
|
|
35
|
-
###
|
|
31
|
+
### Locks
|
|
36
32
|
|
|
37
|
-
-
|
|
33
|
+
- A failed owner-record write no longer lets two builds into the critical section. The write is verified, and on failure the lock directory is removed and the run refuses.
|
|
34
|
+
- A recycled PID no longer keeps a dead lock honoured. On Linux the owner record carries the holder's start tick from `/proc`, so a different process with the same PID reads as stale. macOS and Windows keep PID-only liveness.
|
|
38
35
|
|
|
39
|
-
###
|
|
36
|
+
### Exit codes
|
|
40
37
|
|
|
41
|
-
-
|
|
42
|
-
-
|
|
38
|
+
- Commander usage errors exit 8 as documented, with a JSON envelope under `--json`. `--help`, `--version`, and a command group with no subcommand still exit 0.
|
|
39
|
+
- `GitNotFoundError` exits 7 like its siblings. A contract test now checks every error class against the table in `docs/exit-codes.md` in both directions.
|
|
40
|
+
- Manifest invariant failures raise `InternalInvariantError` and render as a bug report instead of a bare stack.
|
|
43
41
|
|
|
44
42
|
### Diagnostics
|
|
45
43
|
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
44
|
+
- A timed-out command keeps what it captured. `ExecTimeoutError` carries stdout, stderr, and a truncation flag, and shows the last 20 lines.
|
|
45
|
+
- `exec()` used to accept `processGroup` and `mirror` and ignore both. The options type is split so it cannot admit them.
|
|
46
|
+
- `test --help` lists all seven verdict reasons.
|
|
49
47
|
|
|
50
48
|
### Tooling
|
|
51
49
|
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
50
|
+
- `@types/node` moves to `^22` to match the supported Node floor, and a `node-floor` CI job runs the suite on Node 22, 24, and `.nvmrc`.
|
|
51
|
+
- Three copy-pasted mach test wrappers became one `runMachTestSuite(kind, options)`. Several other duplicated helpers collapsed the same way.
|
|
52
|
+
- `ReturnType<typeof X>` on local imports is now a lint error; every site uses the named exported type.
|
|
53
|
+
- Two new lint rules: `no-unnecessary-type-conversion` and `prefer-object-has-own`. Both were already satisfied when they landed.
|
|
54
|
+
- The spawned-CLI test helper settles on `'close'` and rejects on `'error'`.
|
|
55
|
+
- Wall-clock test budgets replaced with ratio checks and fake timers.
|
|
58
56
|
|
|
59
57
|
### Dead code
|
|
60
58
|
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
- **`fireforge import --stop-at` is removed.** It was registered as an alias for `--until` in the same commit that introduced `--until`, so it was never a migration path from an older spelling, and no documentation mentioned it.
|
|
59
|
+
- Twenty-two exports that only tests referenced are gone, along with those tests and the barrel lines. `deadcode:check:production` resolves from the two real entry points and runs in `release:check`, so it cannot come back.
|
|
60
|
+
- Three impossible-state fallbacks removed rather than documented.
|
|
61
|
+
- `fireforge import --stop-at` is removed. It was an alias added in the same commit as `--until`.
|
|
65
62
|
|
|
66
63
|
### Duplication
|
|
67
64
|
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
65
|
+
- One `sleep(ms, { unref })` replaces nine copies that disagreed on whether the timer keeps the event loop alive.
|
|
66
|
+
- Eight `--order` parsers now share `parsePositiveIntegerFlag`, so `--order 12abc` is refused instead of becoming 12.
|
|
67
|
+
- `normalizePatchArtifact` was an identity function on every patch write path; it is gone and its warning moved to the export write site.
|
|
68
|
+
- `ensureGit()` is memoised and called inside `git()` instead of before 32 of the 38 call sites.
|
|
69
|
+
- Single helpers now cover repeated shapes: `sha256Hex`, `normalizePathSlashes`, `formatPatchOrder`, `appendHistoryBestEffort`, `proceedAfterDecision`, `deriveTestStem`, and others.
|
|
70
|
+
- `--dry-run`, `--yes`, and `--force-unsafe` are documented once each through five option mixins.
|
|
74
71
|
|
|
75
72
|
### Test suite
|
|
76
73
|
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
- **The type-level guards are type-level now.** A 40-line hand-written exhaustive `switch` in `validation.test.ts` conceded in its own comment that its runtime assertions named nothing; it is a `satisfies` operand beside `FIREFOX_PRODUCTS` that fails compilation in both directions, verified by adding a member and watching `tsc` reject it.
|
|
74
|
+
- Eleven test files and about 5,600 lines are gone with no assertion lost. They tested barrels, layers, and coverage thresholds rather than behaviour.
|
|
75
|
+
- Twenty-one full-text help snapshots became one inventory snapshot of commands and sorted flag names.
|
|
76
|
+
- `process-boundary.test.ts` is replaced by the lint rule that already enforced it.
|
|
77
|
+
- Four tests that asserted less than their titles claimed now assert what they say.
|
|
82
78
|
|
|
83
79
|
## 0.45.0
|
|
84
80
|
|
|
85
81
|
### Patch bodies
|
|
86
82
|
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
Worth recording that the reported symptom was narrower than stated in one direction and wider in another. Plain `fireforge export` of a new binary always worked; the export half fails only behind a placement flag (`--order`/`--before`/`--after`), which is what routes it through `projectPlacementForLint`. But the same projection sits under `patch move-files-into` and `patch split`, so a binary could not be moved between patches or split out either.
|
|
90
|
-
- **The new-file conflict recovery could not handle a binary target, and restored text where it should have restored bytes.** When `git apply` fails because a patch creates a file that is already on disk, FireForge resolves the conflict by putting the patch's intended content in place and retrying. For a binary there is nothing to extract — but nothing needs to be: `git apply` decodes the payload itself, so the resolution is to REMOVE the blocking file and let the retry create it. A `Binary files … differ` body, which records that the bytes changed and carries none of them, is refused instead of deleted, because nothing could put the file back. The saved originals are now raw `Buffer`s rather than utf-8 text, which also closes a latent corruption on the pre-existing path: a binary file overwritten by this recovery was previously restored through a `readText`/`writeText` round-trip that mangles it.
|
|
83
|
+
- A new binary file can be vendored into a patch. The export was always correct; the forward-import projection refused to decode the binary body. The five duplicated projection loops became one helper that skips binary sections.
|
|
84
|
+
- New-file conflict recovery handles binary targets by removing the blocking file and letting `git apply` recreate it. Saved originals are buffers now, which also fixes a latent corruption on the existing path.
|
|
91
85
|
|
|
92
86
|
### Refusals
|
|
93
87
|
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
88
|
+
- The `--expect` "showed no drift" warning now says what it compares and when, and names the two cases that legitimately produce no drift.
|
|
89
|
+
- The coverage refusal names the test manifests that changed since the recorded build.
|
|
90
|
+
- New `--expect-unmanaged <path>` records an approved exception to `--refuse-adjacent-unmanaged`. Approved paths are always listed, and one that is never met is reported.
|
|
97
91
|
|
|
98
92
|
### Diagnostics
|
|
99
93
|
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
- **`fireforge source set` wrote a pin that a routine revert silently lost.** The pin lands in `fireforge.json` beside hand-maintained policy sections, so `git checkout -- fireforge.json` — undoing an accidental whole-file reformat, say — also reverts an uncommitted pin, and nothing afterwards reported that the pin and the engine checkout had diverged. The field tell was a gate flipping green with no change that should have made it green. `fireforge doctor` now reports the configured pin beside what the checkout actually is — the engine's own `browser/config/version.txt` and the version the last download recorded into state — and warns when they disagree. A report, not a lock and not a new refusal: a tree may legitimately be mid-migration, and the only thing missing was visibility. Scope is limited by what is actually recorded: `product`, `sha256` and `candidate` have no counterpart on disk (state holds only `downloadedVersion` and `baseCommit`), so the check says nothing about them rather than implying it verified them. A blank or whitespace-only `version.txt` — a truncated write, a partial extraction — reads as absent rather than as a mismatch against the empty string. The check reuses `getFirefoxVersion`, which had been sitting in `firefox-extract.ts` with no production caller.
|
|
94
|
+
- `run --smoke-exit` treats `Missing chrome or resource URL:` as a smoke error. It used to exit 0 on a build that hung every browser-chrome run.
|
|
95
|
+
- The no-output-stall triage records the real mechanism: the crash lands in a content process with no crash reporter, so the harness log carries only the timeout. The census names the `.ips` diagnostic report to look for.
|
|
96
|
+
- The green-suite teardown belt names the first condition that rejected a run.
|
|
97
|
+
- The undefined-identifier hint recognises an unmanaged companion file loaded by a managed head and points at `re-export --scan --scan-file` instead of the globals shim.
|
|
98
|
+
- A failing test now raises `TestFailureError` with test-shaped remedies. Exit code stays 5; the `--json` code moves to `test-failure`.
|
|
99
|
+
- The build path prints a recognised teardown traceback verbatim with one labelled line naming it, rather than filtering it like the test path does.
|
|
100
|
+
- `fireforge doctor` reports the configured source pin beside the engine's actual `version.txt` and warns when they disagree.
|
|
108
101
|
|
|
109
102
|
### Builds
|
|
110
103
|
|
|
111
|
-
-
|
|
112
|
-
-
|
|
104
|
+
- The jar-only full-build escalation is narrowed to a new `jar.mn` or a jar declaration with a bracketed base directory. Everything else stays incremental. Probe failures still escalate.
|
|
105
|
+
- Post-build packaging notices name the owning patch, or mark the file `unmanaged`.
|
|
113
106
|
|
|
114
107
|
### Test harness
|
|
115
108
|
|
|
116
|
-
-
|
|
109
|
+
- Every `fireforge test` runs a preflight census of leftover harness processes anchored to this objdir, naming each with its elapsed time and the kill command. `--reap-orphans` terminates them. It never refuses a run.
|
|
117
110
|
|
|
118
111
|
### Patch lint
|
|
119
112
|
|
|
120
|
-
-
|
|
121
|
-
-
|
|
113
|
+
- `patchLint.prettier` (off by default) runs Prettier over patch-owned `.sys.mjs` modules with `cwd` set to `engine/`, which is the only place the check is meaningful.
|
|
114
|
+
- `patchLint.fileSizeThresholds` makes the file-size bands configurable. The message drops the soft/hard wording and says a gate at `--max-warnings 0` treats a warning as a failure.
|
|
122
115
|
|
|
123
116
|
## 0.44.0
|
|
124
117
|
|
|
125
118
|
### Gates
|
|
126
119
|
|
|
127
|
-
-
|
|
128
|
-
- Branch-protection contexts are stable
|
|
129
|
-
-
|
|
130
|
-
-
|
|
120
|
+
- `release:check` runs once per release instead of twice. `prepublishOnly` is the single gate.
|
|
121
|
+
- Branch-protection contexts are stable, and `setup-rulesets.sh` reconciles instead of blindly POSTing.
|
|
122
|
+
- All workflows read the Node version from `.nvmrc`. The old pin was one patch below the floor `eslint-plugin-jsdoc` requires, and `npm ci` only warns about that.
|
|
123
|
+
- A test repo now sets `user.name` as well as `user.email`, so it stops failing only on CI runners. Its git calls reject on non-zero instead of continuing against an empty repo.
|
|
131
124
|
|
|
132
125
|
### Multi-session contention
|
|
133
126
|
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
-
|
|
140
|
-
`binaryStagingLock` never covered it — it is a module-level promise chain, so it serializes within one process while `--parallel-verify` lanes are separate processes. The staging now writes a per-call PRIVATE index (`mintDisposableGitIndex`) and throws it away, so the primary `.git/index` never moves, nothing is restored because nothing shared changed, and two concurrent callers get two indexes rather than contending. The 0.43.0 private-index work closed this for read-only commands; this path is the deliberate writer it did not cover, and it could not have been fixed by leaning on that scope — `export` and `re-export` install none, and `withPrivateGitIndex` is fail-open by design, which on a write path means silently mutating the primary checkout.
|
|
141
|
-
The seed is load-bearing rather than tidy: a tracked, unmodified binary reaches the same branch (its `diff HEAD` is empty), and against an empty index it would report as a fresh add. The shared-index path survives as a fallback for when no private index can be minted — refusing there would fail an export over an optimisation. A real-git test polls `git status` throughout the call and asserts the transient entry is never observable; it fails on the old implementation, which is what makes it worth having.
|
|
142
|
-
|
|
143
|
-
- **`--wait-lock` governed only ONE of the two locks a furnace mutation takes.** `furnace apply`/`deploy`/`sync` routed the resolved budget into the engine session lock and stopped there. `runFurnaceMutation` then acquired `.fireforge/furnace.lock` through `withFileLock`, passing `timeoutMs` only when `options.lockTimeoutMs` was set — and **no production caller ever set it**: the field's only other occurrences were its own declaration and one test, and its doc comment cited a watch-mode caller that does not exist. So the file lock always fell to `DEFAULT_LOCK_TIMEOUT_MS`, a fixed 30 s. A contended `deploy --wait-lock 1800` therefore died at 30 s while its operator believed a half-hour budget was in force, which is the worst shape a budget can have: it pays the entire advertised wait and still refuses. `FIREFORGE_WAIT_LOCK` did not help — it feeds the same resolver into the same single sink.
|
|
144
|
-
The resolved budget now reaches both. It is threaded explicitly into the three commands that declare the flag (`waitLockMutationOptions` does the seconds-to-milliseconds conversion once, rather than at three call sites where dropping the factor is a silent 1000× error), and `runFurnaceMutation` falls back to `sessionWaitLockMs()` — the environment budget — so the twelve furnace mutators with no flag to thread (`create`, `remove`, `rename`, `refresh`, `scan`, `override`, `chrome-doc`, …) are covered too. An explicit `lockTimeoutMs` still wins, and absent both the ~1 s fail-fast and the 30 s default are untouched.
|
|
145
|
-
The invisibility was the other half of the defect: the flag's help text said "the contended lock", singular, and the furnace refusal named neither the lock nor the flag — it advised removing a stale lock directory. The help now says the budget covers every lock the command takes, and the refusal names the FURNACE lock, says it is the second of two, and points at `--wait-lock`/`FIREFORGE_WAIT_LOCK`. A test asserts the budget reaches the command options, and a sibling asserts nothing is threaded without the flag.
|
|
127
|
+
- `--wait-lock` is a budget against a stalled queue. Each improvement in queue position grants a fresh budget, up to four times the request and one hour. The refusal names the position reached.
|
|
128
|
+
- `FIREFORGE_WAIT_LOCK=<seconds>` supplies the budget for any lock-taking command without an explicit `--wait-lock`.
|
|
129
|
+
- SIGTERM and SIGINT emit `FIREFORGE-VERDICT: FAIL reason=killed signal=…` before the shutdown drain, but only if a test run was in flight.
|
|
130
|
+
- The signal sweep releases every lock this process holds, not just furnace ones.
|
|
131
|
+
- Binary-patch staging writes to a private git index. It used to touch the shared index, which made a concurrent `fireforge test` see files flapping between `A` and `??` and fail as inconclusive.
|
|
132
|
+
- `--wait-lock` now reaches both locks a furnace mutation takes. The furnace file lock always used a fixed 30 s, so `deploy --wait-lock 1800` died at 30 s. The refusal names which lock it is and how to raise the budget.
|
|
146
133
|
|
|
147
134
|
### Exit codes
|
|
148
135
|
|
|
149
|
-
-
|
|
136
|
+
- `reason=inconclusive` exits 14 instead of sharing 1 with real failures. `LockContentionError` moves to 15, since the run never started.
|
|
150
137
|
|
|
151
138
|
### Diagnostics
|
|
152
139
|
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
-
-
|
|
164
|
-
-
|
|
165
|
-
|
|
166
|
-
- **A run's only copy of its own diagnosis died with the pipe.** `fireforge test … 2>&1 | tail -40` keeps the summary and discards the `TEST-UNEXPECTED-FAIL` lines that say WHAT broke; it also launders the exit code. Nothing else held the output — `runMachCapture` keeps a 2 MB in-memory tail that dies with the process, and the shard retry loop discards earlier attempts outright — so one downstream failure is now permanently undiagnosable and cost two further full runs to pin as "unreproduced". The operator rule ("never pipe a run") had been written down three times and broken after each writing, which is the tell that it is not an operator problem: piping is the ergonomic default when output is long, and the tool is the only party that can make the log survive the mistake.
|
|
167
|
-
`test` and `build` now write their own complete output to `.fireforge/logs/<command>-<timestamp>.log` as they stream, retained 20 deep per command kind (per-kind, so a busy `test` loop cannot evict the `build` log an operator needs to read beside it). The tee sits at the two mach capture funnels rather than at the commands, so it covers every dispatch — the three test dispatchers, `package`, and the stdio-inheriting path `mach build` uses, which rides the collectors' existing `mirror` hook rather than growing a second one. The log gets the RAW bytes, never the echo filter's collapsed form: the artifact exists to be re-read by whoever is diagnosing the run, so shortening it would reintroduce the loss it prevents.
|
|
168
|
-
The path rides the `FIREFORGE-VERDICT:` line as an additive `log=<path>` key. That is deliberate and not a free choice — the verdict must stay the run's LAST stdout write, so a separate announcement after it would break the contract and one before it is the first thing a `tail` cuts. `build`, which prints no verdict line, announces the path directly. Everything here is best-effort: a log that cannot be opened, written or pruned degrades to no log, because a run must never fail over a diagnostic. Opened before any preflight, so a refusal is logged too — those are exactly the runs whose only output a `tail` throws away.
|
|
169
|
-
- **Post-rebuild failure diagnosis promoted recognized teardown noise over the real assertion.** An export shard failed on its file-count assertion; the diagnosis named the known `AttributeError: 'SystemResourceMonitor' object has no attribute 'stop_time'` instead. The raw Error Summary had the assertion right, so the information was present and the SELECTION was wrong — the expensive kind, because the named cause is a real, documented, unrelated upstream defect and therefore reads as an answer.
|
|
170
|
-
0.44.0 already ships `mach-known-noise-filter.ts`, whose module doc says exactly the right thing about closing the presentation gap — but `findFirstUsefulFailureLine` never consulted it. It matched a bare `/AttributeError:\s+/` among its patterns and selected by first-matching-LINE, not first-matching-pattern, so that arm had precisely the same standing as `TEST-UNEXPECTED-FAIL` and whichever appeared earlier won. The selector now runs two passes: candidates excluding the recognized noise first, the full set only if that finds nothing. The narrowness is preserved exactly — the exclusion is gated on `hasKnownTeardownNoise` for the whole capture, so it still requires both the closed `stop_time`/`poll_interval` allowlist AND a `resourcemonitor.py` frame, and a novel attribute or a traceback from anywhere else stays a real failure. Three tests pin those escape hatches; the one that pins the fix fails on the old code.
|
|
171
|
-
Worth recording that the misdiagnosis was CONDITIONAL, not universal: the classifier reads stdout then stderr, and the traceback usually lands on stderr while the suite output lands on stdout, so a real `TEST-UNEXPECTED-*` normally won on ordering alone. It bites when the traceback shares or precedes the stream, or when the real failure matches none of the other patterns. The reporter could not have known that, and it does not change the fix — but a defect described as always-firing gets re-opened as "not reproducible" by the next reader.
|
|
172
|
-
- **mozbuild's AI-agent output limiter quieted test runs, not just builds.** `is_running_under_coding_agent()` keys on `CLAUDECODE`, which FireForge inherited into every mach dispatch verbatim — it had no handling of the variable anywhere. The build half of that quieting is genuinely useful; the test half removes `TEST_START` and console INFO, exactly the lines a hang diagnosis needs, and the ones FireForge's own classifier reads (`Ran N checks`, `Unexpected results:`, `TEST-UNEXPECTED-*`), so suppression pushes a run toward `reason=no-tests`. Unsetting the variable for the whole process gives up the build half too.
|
|
173
|
-
`fireforge test --full-output` now unsets the marker for TEST dispatches only; the build path is untouched. It is opt-in rather than automatic because it changes how much a third party prints, and an operator who wants the quieting should keep it. The exec layer gained an explicit `envUnset` for this: `env` can only add or overwrite, and a variable read for its PRESENCE is not unset by an empty value — and the removal is a real delete rather than a `Record<string, string | undefined>` widening, because relying on Node dropping `undefined` env entries would be a semantics gamble at the one layer that must not have any.
|
|
140
|
+
- A stale mochitest httpd on port 8888 is detected at preflight and named. It used to stall runs into a 370 s timeout with nothing pointing at a port.
|
|
141
|
+
- The stale-browser refusal reports elapsed time and the full command line, and offers `--kill-stale-marionette` only for a marionette-driven browser. A bare launch may be someone else's live window.
|
|
142
|
+
- The `--wait-lock` progress line reports the budget currently in force and announces extensions.
|
|
143
|
+
- `status --lock` reports the holder's CPU time, so a wedged holder looks different from a healthy long build. It is an observation, never a verdict.
|
|
144
|
+
- The inconclusive refusal names what moved in `engine/`: the HEAD SHAs and up to five worktree entries.
|
|
145
|
+
- A `moz.build` sort violation is translated instead of arriving as a Python traceback. Mozbuild lowercases before comparing, which is the opposite of the intuitive rule.
|
|
146
|
+
- A run whose only unexpected results are the time-driven `lastColdStartupCheck`/`globalprivacycontrol` pair says so and suggests halving the chunk.
|
|
147
|
+
- The packaging-coverage refusal offers `--extend-coverage` and states when it applies.
|
|
148
|
+
- The no-output-stall census hoists the known-good control step above the list and gives cause 3 a probe that separates it from the others.
|
|
149
|
+
- `test` and `build` write their full output to `.fireforge/logs/<command>-<timestamp>.log` as they stream, 20 deep per command kind. The path rides the verdict line as `log=<path>`. Opened before any preflight, so refusals are logged too.
|
|
150
|
+
- Failure diagnosis no longer promotes recognised teardown noise over the real assertion. The selector runs a pass that excludes known noise first.
|
|
151
|
+
- `fireforge test --full-output` unsets `CLAUDECODE` for test dispatches only, so mozbuild's agent output limiter stops hiding the lines the classifier reads.
|
|
174
152
|
|
|
175
153
|
### Build output resolution
|
|
176
154
|
|
|
177
|
-
-
|
|
155
|
+
- A second `obj-*` directory no longer refuses every command when `engine/mozconfig` declares `MOZ_OBJDIR`. Genuinely ambiguous cases still refuse.
|
|
178
156
|
|
|
179
157
|
### Correctness
|
|
180
158
|
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
-
|
|
190
|
-
-
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
- `killProcessTree`'s POSIX branch swallowed `ESRCH`/`EPERM` from the group kill without falling through to a direct `child.kill`, so a failed group kill left the child running.
|
|
198
|
-
|
|
199
|
-
- **A DELETION of an upstream file could not be made durable through `re-export`, and the failure was silent.** With eighteen retired files, `rm` left `fireforge status --json` reporting `"status": "D"` with `"classification": "unmanaged"` — correct as a description, fatal as a state, because `check-engine-clean` refuses an unmanaged file precisely because it is not durable through a reset, and a reset would restore every deleted file. No re-export path captured it: `--scan` pruned the path from `filesAffected` and emitted no deletion hunk, `--scan-file` refused it outright, and **`--files` accepted the path and emitted no hunk** — the dangerous shape, because the run reported success while writing a patch that silently CLAIMED a file it did not restore. On a fresh clone that patch applies cleanly and resurrects exactly what the change meant to remove.
|
|
200
|
-
The cause was one conflation. All three paths filtered "absent from disk" out of the diff scope, on a comment asserting the diff "would fail" on a missing path — true only for a path that was NEVER TRACKED. For a tracked file deleted in the worktree, `git diff HEAD -- <path>` emits a proper `deleted file mode` section, and every downstream consumer already branches on `isDeletedFile`. The two cases are now split by `listTrackedInHead`: a tracked absent path stays in the diff scope and is captured, an untracked one is refused rather than warned about, since the patch cannot express it either way. A captured deletion KEEPS its `filesAffected` entry — the patch is what performs the deletion, so pruning it would leave the body deleting a file the manifest says the patch has nothing to do with — and it still requires the confirmation, because the patch will now remove those files wherever it is applied.
|
|
201
|
-
The report's priority-1 item — make the silent success LOUD — turned out to need two words, not a feature: the `--files` path already refused when a requested path produced no hunk, but its domain was the post-filter subset, so anything dropped upstream was invisible to the check by construction. It now runs against the full requested list. A real-git test asserts the emitted patch contains `deleted file mode`; it fails on the old implementation.
|
|
202
|
-
Recorded because the report says otherwise and the next reader should not chase it: **`export` was never affected.** It accepts a `D` status from `getStatusWithCodes` and routes a HEAD-tracked path to `git diff --binary HEAD`, which has always carried the deletion. Only the batched `re-export` paths dropped it.
|
|
203
|
-
- **`token add --mode override` silently skipped a QUALIFIED `[data-theme="light"]` block.** The override path mirrors into both `:root[data-theme="dark"]` and `:root[data-theme="light"]`, guarded by `variantBlockExists` — and that guard compared canonically against a fragment sliced to the LAST `]` before the opening brace. For `:root[data-theme="light"]:not([data-private]) { }` the last `]` is the one inside `:not(…)`, so the computed fragment was `:root[data-theme="light"]:not([data-private]` and the comparison could never match. The unqualified dark block beside it matched fine. That asymmetry was the whole bug: the override wrote dark, skipped light, and said nothing — so a Light choice under a dark OS inherited the new dark token, and the operator met the gap as a theme bug rather than as tool output. `THEME_ATTRIBUTE_VARIANTS`' own doc comment names the failure this feature exists to prevent — "a guaranteed half-finished edit" — and the half-finished edit is what shipped.
|
|
204
|
-
The last-`]` slice was deliberate, not sloppy: it avoids a `js/polynomial-redos` regex, and taking the last `]` rather than the first keeps a multi-attribute selector comparing as a whole instead of colliding with a single-attribute variant that is its prefix. Both properties survive. The selector is now parsed structurally — consume consecutive `[…]` groups after `:root`, stop at the first character that is not `[` — which is still linear and still matches multi-attribute blocks whole, but excludes a pseudo-class tail instead of swallowing it. A regression test uses a two-attribute block to pin that.
|
|
205
|
-
The qualifier is reported rather than applied silently. `:not([data-private])` is semantically load-bearing — writing through it narrows where the declaration applies, and that may not be what the operator meant — but matching it is still the right call, because silently skipping it is the worse failure and the one that shipped. So the write happens and a warning names the selector it went through. The pinning test fails on the old matcher.
|
|
206
|
-
- **`token add --variant` required a flag that described nothing, rejected its companion, and reported a no-op as success.** Three separate frictions, all in the variant path. `--category` was an unconditional `requiredOption` that had to name a REAL category even under `--variant`, where the declaration is routed into a `:root<selector>` block and never into a category section — the requirement existed so the argument would not be "silently discarded", a fair goal whose effect was a mandatory argument saying nothing about where the token lands. It is now optional there (still validated when supplied, so a typo is not accepted just because the flag is inert), and its absence on a BASE declaration is refused with the REASON stated rather than commander's bare "required option not specified".
|
|
207
|
-
`--create-category` is still rejected with `--variant` — the two really are incompatible — but the working create-then-variant order (author the category with the base token, then add each variant) was discoverable only by failing twice. It is now in both the error and `--variant`'s help text.
|
|
208
|
-
And a token already present in the target variant block was skipped with a bare `{ added: false }`, so a re-run intending to CHANGE a value exited 0 having changed nothing, while the base path had been reporting the category and line for the same situation all along. The variant path now returns the location too, and the skip reads `already exists in :root[…] (line N), unchanged` — the block being the location, since a variant declaration has no category to name.
|
|
209
|
-
- **The CC0 public-domain dedication Mozilla puts on its test files was not a recognized license header.** `containsUpstreamLicenseText` knew MPL, Apache, MIT, GPL and a bare SPDX tag; nothing knew `Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/`. A modified upstream test whose notice was UNCHANGED therefore warned `modified-file-missing-header`, and the downstream workaround — two identifier-only edits, an `SPDX-License-Identifier: CC0-1.0` line above each untouched notice on patch 905 — was editing Mozilla's notice to satisfy FireForge's recognizer. Worse than filed: `hasThirdPartyPermissiveBanner` did not know it either, so a NEW file copied from the upstream test tree was neither "acceptable" nor "vendored", and `export -y` would have prepended the PROJECT's license header onto code Mozilla dedicated to the public domain — precisely the mislicensing the vendored carve-out (0.38.0) exists to prevent.
|
|
210
|
-
Both predicates now recognize the dedication in every spelling the tree carries — the prose, the `creativecommons.org/publicdomain/zero/1.0/` URL with either scheme, and the `CC0-1.0` SPDX identifier — through one shared matcher, so the two cannot drift. Deliberately narrow, with the negative controls pinned: a different Creative Commons license (`licenses/by/4.0/`, `CC-BY-4.0`), a made-up `publicdomain/zero/2.0/`, a bare "public domain" mention, and a dedication below the scanned head are all still refused.
|
|
159
|
+
- The failure-line classifier no longer matches the English word "assertion" or `TEST-KNOWN-FAIL`. Both arms are harness-shaped and case-sensitive now. A green run could previously be turned red by its own diagnostic.
|
|
160
|
+
- `fireforge test --build` accepts `--refuse-unexported-drift` and forwards it to the pre-test build. It is refused without `--build`.
|
|
161
|
+
- A `--wait-lock` waiter no longer gets a free extension on its first probe, so a stalled queue starves on exactly the budget asked for.
|
|
162
|
+
- `re-export` no longer rewrites a tracked binary patch into an un-appliable stub. `--binary` now covers every diff path, a new `binary-body-not-reconstructable` lint fails closed on a payload-less binary section, and classification no longer trusts a stub's index hash.
|
|
163
|
+
- `re-export` reports `Unchanged` for a patch whose body did not move, and does not rewrite the file.
|
|
164
|
+
- `fireforge test --doctor` no longer orphans a browser tree on Ctrl+C.
|
|
165
|
+
- Failures during a patch-queue mutation keep their own class, stack, and exit code instead of all rendering as a patch-compatibility problem.
|
|
166
|
+
- Esc or Ctrl+C at a confirmation exits 130. Answering "no" exits 0.
|
|
167
|
+
- `furnace validate` checks that the engine exists.
|
|
168
|
+
- `furnace create` escapes quotes, newlines, and `*/` in generated modules.
|
|
169
|
+
- The signal-time furnace lock sweeper checks ownership before removing a lock.
|
|
170
|
+
- `killProcessTree` falls through to a direct kill when the group kill fails.
|
|
171
|
+
- A deletion of an upstream file can now be captured through `re-export`. All three paths filtered absent files out of the diff scope; a tracked absent path is kept and produces a real `deleted file mode` section. `--files` refuses against the full requested list, so a path that produces no hunk is reported.
|
|
172
|
+
- `token add --mode override` writes qualified blocks such as `:root[data-theme="light"]:not([data-private])`. It used to write dark and silently skip light. The selector is parsed structurally, and the qualifier is reported in a warning.
|
|
173
|
+
- `token add --variant` no longer requires `--category`, explains the create-then-variant order, and reports an already-present token with its location instead of a silent no-op.
|
|
174
|
+
- Mozilla's CC0 dedication is recognised as a license header in both the modified-file and vendored-file checks. Without it, `export -y` would have prepended the project header onto public-domain code.
|
|
211
175
|
|
|
212
176
|
### Security
|
|
213
177
|
|
|
214
|
-
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
- The variant scanner's replacement deliberately spans to the LAST `]` before the opening brace rather than the first. Taking the first would have been the shorter fix and a silent behaviour change: `:root[data-skin=precision][data-private]` would then satisfy a request for the `[data-skin=precision]` variant it merely starts with, and the declaration would land in the wrong block. A test pins that, and it passes on both implementations — which is the point of it.
|
|
218
|
-
- The regression tests assert wall-clock, because the defect IS a running time; every one of these calls returns the same answer before and after. Each witness fails the old implementation by an order of magnitude or more, and the whole file now runs in ~26 ms.
|
|
219
|
-
- **The prototype-chain guard on `config <key> <value> --force` was correct and invisible** (`js/prototype-pollution-utility`). `mutateConfig` rejected `__proto__`, `constructor` and `prototype` — but once, up front, in a helper, against a `Set`; the descent and the leaf assignment two dozen lines below therefore read as unguarded to anything that is not interprocedural, CodeQL included. The check now happens at the point each segment is USED as a property name, spelled as explicit comparisons, with only the message factored out. That is not just to satisfy the query: a guard that lives at a distance from its write is one refactor of its caller away from being skipped, and the existing tests keep passing untouched because nothing about the behaviour moved.
|
|
220
|
-
- **Two `replace` calls read as incomplete escaping** (`js/incomplete-sanitization`), and both were in fact correct — which is exactly why they are rewritten rather than dismissed, since "correct, trust me" is not a property a scanner can re-derive next quarter. `patch-lint-checkjs` substituted a TypeScript `paths` wildcard with `target.replace('*', captured)`; a target carries at most one `*` and first-occurrence replacement IS the contract, now spelled as index arithmetic mirroring the prefix/suffix split already used on the pattern side, with a test pinning both the wildcard and wildcard-free target shapes. The pack smoke test hand-escaped a version string with `.replace(/\./g, '\\.')`; it now uses `escapeRegex` from `src/utils/regex.ts`, the helper ~40 other call sites already use.
|
|
178
|
+
- Six `tokens.css` scanners were super-linear in the length of a single line. One took 8.2 s on 4,000 repeated characters. Fixed by removing ambiguous adjacent quantifiers and anchoring unanchored prefixes; four became index arithmetic. Regression tests assert wall-clock, and the file now runs in about 26 ms.
|
|
179
|
+
- The prototype-chain guard on `config --force` moved to the point each segment is used as a property name.
|
|
180
|
+
- Two `replace` calls that read as incomplete escaping were rewritten as index arithmetic and the shared `escapeRegex`.
|
|
221
181
|
|
|
222
182
|
### Manifest repair
|
|
223
183
|
|
|
224
|
-
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
- **Two of the report's claims do not hold against this code, and are recorded rather than "fixed".** `lintIgnore`, `tier`, `description`, `category`, `createdAt` and `sourceEsrVersion` were all already preserved on an existing entry — the rebuild has been a merge, not a from-scratch rebuild, since the branding tier landed. Most of the 1823 deletions were `filesAffected` lists being re-sorted (the rebuild normalises them; the narrow repair does too, but only on rows that were already wrong) plus the `stagedDependencies` blocks above. And the three binary-asset patches the report flagged as possible mismatches were an artifact of its own scan: `getAllTargetFilesFromPatch` reads `diff --git` headers, so `GIT binary patch` sections with no `+++ b/` line have been counted correctly since the fix that put them there. Naming what the code actually does is how the next occurrence gets attributed quickly instead of inheriting a guess.
|
|
184
|
+
- `doctor --repair-patches-manifest` no longer drops `stagedDependencies`. The row is built by spreading the existing entry and overriding only `filesAffected` and `order`, so any future field survives too.
|
|
185
|
+
- An unparseable `patches.json` is no longer rebuilt into an invented one. Doctor refuses, names the parse error, and requires `--allow-metadata-loss`.
|
|
186
|
+
- Doctor prints what it wrote before the summary, including on a failing run.
|
|
187
|
+
- New narrow `doctor --repair-files-affected` recomputes only the drifted lists. Both repairs gained `--dry-run`.
|
|
229
188
|
|
|
230
189
|
### Performance
|
|
231
190
|
|
|
232
|
-
-
|
|
233
|
-
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
243
|
-
-
|
|
244
|
-
- `
|
|
245
|
-
- Two content-equality policies were split across five comparison sites, so on a CRLF checkout `furnace status` and `furnace validate` could report drift for files `apply` had just decided were identical. `normalizeForChecksum` moved to `furnace-checksum-utils.ts` and all five use it.
|
|
246
|
-
- The four manifest registrars each carried their own copy of the insert sequence. The two jar.mn ones (browser content, shared CSS) were byte-identical apart from a sort regex, a sort key and a refusal message; the two moz.build ones likewise. They route through `insertJarMnEntry` and `insertMozBuildListEntry` now — deliberately two family-specific helpers rather than one generic pair, because `after` and the section concept are meaningless on the moz.build side, and because `tokenizeMozBuildList` **mutates the line array in place** when it normalizes `FOO += []` into the multi-line form. That rewrite is what makes the caller's splice land inside the list body, so a wrapper tokenizing a copy would have silently reintroduced a fixed bug.
|
|
247
|
-
- `registerToolkitWidget` was the one jar.mn target still hand-scanning lines with its own section-bounds and sort-key regexes, so it never inherited fixes made to the shared tokenizer path. It uses the same helper as its three siblings now. The dead `findAlphabeticalPosition` (its last caller) went with it, and `findAlphabeticalTokenPosition` became module-private.
|
|
248
|
-
- `WIDGETS_DIR` moved out of `furnace-scanner.ts`; the seven-line `withParserFallback` rationale moved onto the shared predicate it explains (one of its three copies had already drifted); 18 open-coded `process.stdin.isTTY && process.stdout.isTTY` checks became `stdioIsInteractive()`.
|
|
249
|
-
|
|
250
|
-
### Structure
|
|
251
|
-
|
|
252
|
-
- `src/commands/doctor/` held a single file while six `doctor-*` siblings sat flat; it moved up to `doctor-post-rebase-audit.ts`, turning its lone `../doctor-check-core.js` reach-back into `./`.
|
|
253
|
-
- `furnace-validate-helpers.ts` was a 572-line spill file with three disjoint subjects and 18 of its 19 exports having exactly one consumer each. Each block moved to the validator that uses it; the module is now 20 lines holding the one export that genuinely had two consumers. **knip then revealed 16 of those exports as module-private** — they had only been exported to cross a boundary that no longer exists.
|
|
254
|
-
- Four ambiguous `manifest-*.ts` modules became `moz-manifest-*.ts` — "manifest" named both `patches.json` and Firefox build files — and `register-test-manifest.ts` became `register-browser-test.ts` so it reads as a `register-*` leaf. `lint-cache.ts` and `lint-projection.ts` joined the `patch-lint-*` family they belong to. The `register-*` family was deliberately left alone: `register-` names the `fireforge register` command those modules implement.
|
|
255
|
-
- The `Furnace manifest sync` doctor check does not look at a manifest — it compares component directories on disk against `furnace.json`. In a codebase where "manifest" already names `patches.json` and Firefox build files, that made the one referent visible to operators the most misleading one. Check, module and symbol are now `Furnace config sync`.
|
|
256
|
-
- Four modules reached for the same two drift predicates through two different paths — `furnace-apply-helpers.js` in two, the `furnace-apply.js` compat barrel in the others. They all use the defining module now, and the three names nothing else re-exported came off the barrel. The direction matters: `furnace-apply.ts` is 650 lines against a 500-line cap, and routing a consumer through it drags the whole apply graph into every suite that mocks it — which is exactly how the first attempt at this cleanup broke seven doctor tests, whose exhaustive `vi.mock` factory for the barrel listed one export. `mach.ts`'s re-export block now carries a comment saying it is a frozen compatibility list, not a barrel to grow.
|
|
257
|
-
- Dead exports deleted: `applyPatches`/`validatePatches` (superseded by `applyPatchesWithContinue`), six unused `platform.ts` helpers, `hasActiveRebaseSession` (every reference was a test using it as an assertion helper), `peekParserFallbackEvents`. knip could not see any of them, because tests count as consumers.
|
|
258
|
-
- Fifteen `await import()` calls became static imports. `patch-export.ts`'s `await import('./patch-manifest.js')` was the notable one: tracing the static graph showed no cycle, so the laziness bought nothing and hid a real edge from the `dpdm` gate — which still reports no cycles after the change.
|
|
191
|
+
- `getAllDiff` batches its `git hash-object` calls instead of one spawn per untracked file. Output bytes are unchanged and now pinned by a test.
|
|
192
|
+
- `last-build.json` carries content hashes for dirty `jar.mn`, `moz.build`, `moz.configure`, `Makefile.in`, and `mozbuild.in`, so a permanently dirty worktree stops escalating every build to a full one and re-running configure. `build --ui` records which kind of build it was.
|
|
193
|
+
|
|
194
|
+
### Cleanup
|
|
195
|
+
|
|
196
|
+
- The engine-precondition refusal was written out 15 times across 13 files; it is now `assertEngineGitReady` or `assertEngineExists`.
|
|
197
|
+
- The furnace two-rung ladder, the 15-line rollback catch, the four manifest registrars, and five legacy scanners all collapsed to shared helpers.
|
|
198
|
+
- `bootstrap.ts` uses the canonical bootstrap-issue scanner instead of a second copy whose regexes had drifted.
|
|
199
|
+
- Content-equality now goes through one `normalizeForChecksum`, so `furnace status` and `furnace validate` cannot disagree with `apply` on a CRLF checkout.
|
|
200
|
+
- 18 open-coded TTY checks became `stdioIsInteractive()`.
|
|
201
|
+
- Modules renamed for clarity: four `manifest-*.ts` became `moz-manifest-*.ts`, the `Furnace manifest sync` check became `Furnace config sync`, and the lint modules joined the `patch-lint-*` family.
|
|
202
|
+
- `furnace-validate-helpers.ts` was a 572-line spill file; each block moved to the validator that uses it and 16 exports turned out to be module-private.
|
|
203
|
+
- Fifteen `await import()` calls became static imports.
|
|
259
204
|
|
|
260
205
|
### Types
|
|
261
206
|
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
- `lintExportedPatch`
|
|
267
|
-
-
|
|
268
|
-
-
|
|
269
|
-
- **Nothing at the CLI boundary ever read an error's `cause`.** Nine classes declare one and 22 throw sites pass one, but a `FireForgeError` rendered as its `userMessage` and stopped, so the underlying git stderr or errno never reached the operator; `download.ts` had hand-rolled the rendering four times to make its own "--verbose for the underlying error" promise true. The boundary now walks the chain under `--verbose`.
|
|
207
|
+
- `DoctorCheck` encoded one ternary result in three fields. `severity` is now required and the only field.
|
|
208
|
+
- Three string unions are derived from their member lists through `makeEnumGuard`, so a stale allowlist is a compile error.
|
|
209
|
+
- `src/index.ts` no longer publishes 32 unreachable types, and gained `ApplyAllComponentsResult`.
|
|
210
|
+
- The per-component apply helpers take `Pick<ComponentApplyContext, …>` instead of four interchangeable strings in three different orders.
|
|
211
|
+
- `lintExportedPatch` takes its optional arguments in an options object.
|
|
212
|
+
- Twelve runtime flag checks raise `InvalidArgumentError` (exit 8) like the other 191.
|
|
213
|
+
- The CLI boundary walks the error `cause` chain under `--verbose`.
|
|
270
214
|
|
|
271
215
|
### CLI
|
|
272
216
|
|
|
273
|
-
-
|
|
274
|
-
- `import` and `setup` accept `-y, --yes`. On `
|
|
275
|
-
-
|
|
276
|
-
-
|
|
277
|
-
- `
|
|
278
|
-
-
|
|
279
|
-
The validator now runs the MATCHER'S parse and requires it to consume the whole fragment, rather than carrying a second grammar that can drift from the first again. Character safety stays a separate check, because that parse finds bracket boundaries and validates no characters at all — it is applied one bracket group at a time rather than as a repeated pattern, which is the shape that backtracks (`js/polynomial-redos`), and a linearity test pins that alongside the matcher's own. Normalization to `="value"` now covers every fragment in a run and the `:not(…)` argument, not just a trailing one.
|
|
280
|
-
`findVariantBlock` had to move with it: it compared a request against a block's ATTRIBUTE run only, so a variant carrying its own qualifier would have matched nothing and spliced a fresh duplicate block on every add. Request and candidate go through the same parse and are compared tail-to-tail. An UNqualified request still matches a qualified block — that is the 0.44.2 behaviour `variantBlockQualifier` exists to report out loud, since silently skipping the block is the worse failure — but a request that names a qualifier means it.
|
|
281
|
-
- `tree list --json` gained the failure envelope `status --json` already emitted, so a scripted consumer no longer gets a parseable refusal from one and a bare non-zero exit from the other. The contract is written down in `docs/machine-output.md`.
|
|
217
|
+
- `patch tier`, `patch lint-ignore`, and `patch staged-dependency` honour `--wait-lock` instead of claiming they take no lock. All seven patch-directory commands honour it now.
|
|
218
|
+
- `import` and `setup` accept `-y, --yes`. On `import` it waives the prompt but keeps the integrity gate armed.
|
|
219
|
+
- `test --doctor` prints its preflight line once.
|
|
220
|
+
- `status --json` outside a project writes one JSON envelope to stdout and the guidance to stderr.
|
|
221
|
+
- `token add --variant` accepts every selector the matcher can find, using the matcher's own parse rather than a second grammar.
|
|
222
|
+
- `tree list --json` emits the same failure envelope as `status --json`.
|
|
282
223
|
|
|
283
224
|
### More correctness
|
|
284
225
|
|
|
285
|
-
-
|
|
286
|
-
-
|
|
287
|
-
- `RebasePatchEntry`
|
|
288
|
-
-
|
|
289
|
-
-
|
|
290
|
-
- **`furnace create --test-dir` was honoured by the scaffolder and ignored by everything that told the operator where the files went.** `create.ts` resolved the override once and passed it to the scaffolders, but `formatDryRunPlan` and `formatSuccessNote` received only `binaryName` and recomputed the DEFAULT from it — so `--dry-run` planned `test/hominis/` plus `hominis/browser.toml` in `moz.build` for a command that would write to `test/hominis-tiles/`, and the success note after the real run named `hominis/` while the files sat in `hominis-tiles/`. The one message an operator gets as confirmation was the one thing that could not be trusted. Both formatters now take the resolved directory, and the default lives in one place: `resolveBrowserChromeTestDir` / `resolveXpcshellTestDir` in `furnace-constants.ts`, called by the scaffolders AND the formatters, so the two cannot disagree again. The moz.build registration line follows the override too (it is the path below `browser/base/content/test/`, which under `--test-dir` is not the binary name), and a test scaffolds for real and asserts the printed directory is the one on disk — default and overridden, browser-chrome and xpcshell.
|
|
226
|
+
- `furnace create`'s tag prompt shows the rule inline instead of dying on a typo. The message-returning validators are now named `describe*Problem`.
|
|
227
|
+
- `register --after` says so when a file type cannot honour it, instead of ignoring the flag.
|
|
228
|
+
- `RebasePatchEntry` is a discriminated union, so a status flip cannot strand the previous status's payload. Older session files still load.
|
|
229
|
+
- A `patches.json` from a newer FireForge is refused by name instead of being reported as malformed.
|
|
230
|
+
- `furnace create --test-dir` is honoured by the dry-run plan and the success note, not just the scaffolder.
|
|
291
231
|
|
|
292
232
|
### Windows
|
|
293
233
|
|
|
294
|
-
-
|
|
295
|
-
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
-
|
|
301
|
-
|
|
302
|
-
On the test side: path expectations and mock keys are rendered through a new `nativePath()` test helper rather than hard-coded POSIX literals; fixture repos pin `core.autocrlf=false` / `core.eol=lf`; the three modules that branch on `process.platform` directly now have that branch forced rather than inherited from the runner; and the doctor summary count is derived rather than pinned at 16, since the orphaned-harness check is `skipIf` win32. The genuinely POSIX-only suites — `tree`, `tree exec`, `run --smoke-exit`, and the `chmod`-as-refusal tests — are skipped explicitly, and each skip is paired with a new platform-stubbed test that pins the win32 refusal itself, which nothing did before.
|
|
303
|
-
|
|
304
|
-
`nativePath()` turned out to be the wrong tool for part of that job, and applying it uniformly produced failures of its own. It swaps separators only, which is correct for a value the code built with `join` but wrong for one it passed through `resolve`: on Windows `resolve` also prefixes the current drive, so the expectation has to be `D:\project\x`, not `\project\x`. A sibling `nativeAbsPath()` covers that case, and the assertions that were wrapped despite never touching a separator at all — pure suffix concatenation, a pass-through argument, and a suite whose `getProjectPaths` mock returns POSIX literals on every platform — go back to comparing the literal. Four doctor summary assertions had also kept their POSIX counts inline while the clean-run count next to them was already derived, so the derivation now covers every summary line in the suite. Two more `chmod`-as-refusal tests joined the skip list: they were skipped for `root` but not for Windows, where mode bits are ignored outright, so the "unreadable" setup was a no-op and the code correctly reported success.
|
|
234
|
+
- `fireforge lint`'s checkJs pass reported zero findings, silently: the path maps were keyed with backslashes while TypeScript hands out forward slashes. `typecheck` had the same defect.
|
|
235
|
+
- `furnace diff` reported every overridden file as new, because `git show ref:path` only understands forward slashes.
|
|
236
|
+
- Engine-relative paths were recorded in two spellings at once; normalised at the five producing sites.
|
|
237
|
+
- `build-audit` collapsed to one path segment, `removeTree` refused every removal, and `initRepository` never pinned `core.autocrlf`, which shifted every blob hash.
|
|
238
|
+
- `furnace init` accepted `--ftl-base-path ../escape/path`, because the traversal guard normalised to backslashes before testing for `../`.
|
|
239
|
+
- `furnace` jsconfig sync did nothing and reported a clean summary while doing it.
|
|
240
|
+
- On the test side: path expectations go through `nativePath()` and `nativeAbsPath()`, fixture repos pin `core.autocrlf=false`, platform branches are forced rather than inherited, and POSIX-only suites are skipped explicitly with a paired win32 refusal test.
|
|
305
241
|
|
|
306
242
|
### Test infrastructure
|
|
307
243
|
|
|
308
|
-
-
|
|
309
|
-
|
|
310
|
-
-
|
|
311
|
-
|
|
312
|
-
- Five rollback tests moved from asserting a module-level spy to asserting the persisted pending-repair marker. The spy stopped working when the sequence moved into `completeJournalRollback` (its call to the recorder is intra-module), and the outcome is the better assertion anyway.
|
|
313
|
-
- **Fourteen verdict assertions passed only because writing to a filesystem root fails.** The command suites pass `/project` as the project root and assert the `FIREFORGE-VERDICT:` line by exact string — which holds only while no run log is open, since the line carries an open log's path as a ` log=<path>` key. On POSIX `mkdir('/project/.fireforge/logs')` fails at the root, the best-effort open degrades to "no log", and the assertions passed for a reason none of them stated. On Windows the same path resolves against the CURRENT DRIVE, the mkdir succeeds, and all fourteen failed — while the run also created real `C:\project\.fireforge\logs` directories on the runner. `createRunLogMock()` joins the two factories in `module-mocks.ts` and the five suites state the precondition instead of inheriting it from a permission error. Because mocking the module away removes the suffix's only coverage, `test-verdict.test.ts` gains the opposite assertion: with a log open, the path rides the line, once, as the run's last stdout write.
|
|
314
|
-
- **A lock-wait test decided its result on a `readdir`.** `a wait whose queue position keeps improving > survives a drain longer than its budget` runs on real timers and a real filesystem, and gave each probe about 20 ms of slack inside a 100 ms budget; it also retired its simulated queue with un-awaited `rm` calls, so a retirement could land after the probe meant to observe it and a release could leave `mkdir` seeing `EEXIST` past the intended free. On a Windows runner the second probe missed the deadline, no extension was ever granted, and the wait expired still three from the head — the exact refusal the test exists to prove does NOT happen. The retirement and release are synchronous now (the callback returns `void` and cannot await), and the constants are scaled to ~100 ms of slack per probe while keeping the two invariants that fix them: the budget must span two probes, and the drain must outlast the budget or the sibling test would acquire instead of expiring. No production change — `withFileLock` checking its deadline before probing is correct, since a wait must not extend itself on a probe taken after it expired.
|
|
315
|
-
- `git-readonly-index.test.ts` carried a latent racy-clean flake: it edited `const b = 2;` to `const b = 3;` — the same byte length, within the same second as the index entry — without invalidating the stat cache, so git could report the file unmodified. Its sibling test already guarded for this.
|
|
244
|
+
- `createLoggerMock()` and `createFsMock()` replace hand-rolled `vi.mock` factories at 110 sites. Each old factory listed a subset of the real module, so adding an export broke a hundred suites at once. The new ones are typed, so `tsc` reports a missing export in one file.
|
|
245
|
+
- `test.test.ts` was 2,943 lines held together by a 190-line mock header. The header moved to `test-command-mocks.ts` and the file split into six. The split exposed a real isolation bug in a Marionette preflight test.
|
|
246
|
+
- Fourteen verdict assertions passed only because writing to a filesystem root fails on POSIX. They state the precondition now via `createRunLogMock()`.
|
|
247
|
+
- A lock-wait test decided its result on a `readdir`; its retirement and release are synchronous and its timing constants scaled.
|
|
316
248
|
|
|
317
249
|
### Documentation
|
|
318
250
|
|
|
319
|
-
-
|
|
320
|
-
-
|
|
321
|
-
-
|
|
322
|
-
-
|
|
323
|
-
-
|
|
251
|
+
- README's reference material moved to `docs/`, split by task: `patch-workflow.md`, `testing.md`, `furnace.md`, `verification-trees.md`, `configuration.md`, with `docs/README.md` as the index.
|
|
252
|
+
- New `docs/run-logs.md` states the `.fireforge/logs/` contract.
|
|
253
|
+
- `machine-output.md` and `lifecycle-invariants.md` were stale against the code and now match it.
|
|
254
|
+
- The published package ships `docs/`, so the README's relative links resolve after install. A smoke test asserts the tree is packed.
|
|
255
|
+
- The four environment variables FireForge reads are tabulated in `configuration.md`.
|
|
324
256
|
|
|
325
257
|
## 0.43.0
|
|
326
258
|
|
|
327
259
|
### Verdict correctness
|
|
328
260
|
|
|
329
|
-
- A suite that ends clean
|
|
330
|
-
- `engine/mozconfig` is written only when
|
|
331
|
-
-
|
|
261
|
+
- A suite that ends clean and then dies in the known mozsystemmonitor teardown traceback is a PASS with a note. Every hard-evidence veto still fails the run.
|
|
262
|
+
- `engine/mozconfig` is written only when its content changes, so mach stops re-running configure on every invocation. Consumers who raised hang-canary bounds for this can restore them.
|
|
263
|
+
- `verify`, `lint --per-patch`, and `typecheck` no longer write the primary engine checkout. They run git plumbing against a private `GIT_INDEX_FILE`, so they stop tripping a concurrent test run's inconclusive refusal.
|
|
332
264
|
|
|
333
265
|
### Diagnostics
|
|
334
266
|
|
|
335
|
-
- An xpcshell
|
|
336
|
-
- `fireforge verify`
|
|
337
|
-
-
|
|
338
|
-
- FireForge's own escalation
|
|
267
|
+
- An xpcshell SIGSEGV with no test evidence names its known cause: an `.sys.mjs` imported from a packaged module with no `EXTRA_JS_MODULES` registration.
|
|
268
|
+
- `fireforge verify` resolves every `resource://` specifier the queue-owned modules import, including lazy `defineESModuleGetters` ones, and names unregistered targets.
|
|
269
|
+
- A headed stall probes the display power state on macOS and reports it, and lists the three recorded causes of that signature. The old `caffeinate` advice was wrong: it prevents sleep, it cannot wake a display.
|
|
270
|
+
- FireForge's own escalation notices carry a `[FireForge] NOTICE:` prefix at warning severity, so agent output filters keep them.
|
|
339
271
|
|
|
340
272
|
### Messages and refusals
|
|
341
273
|
|
|
342
|
-
- The stale-build refusal suggests `--build` first and
|
|
343
|
-
- `--refuse-foreign-drift`
|
|
344
|
-
-
|
|
345
|
-
-
|
|
346
|
-
- `patch move-files --create`, `patch move-files`, and `patch split` refuse a prompt-less non-TTY run **up front**, naming `--yes`, instead of after minutes of diff and projected-lint work.
|
|
274
|
+
- The stale-build refusal suggests `--build` first and says plainly what `--allow-stale-build` accepts.
|
|
275
|
+
- `--refuse-foreign-drift` stops calling the operator's own additions foreign, and tags each file by whether it changed since their last export.
|
|
276
|
+
- Unknown patch identifiers suggest the nearest matches instead of printing the whole manifest.
|
|
277
|
+
- `patch move-files` and `patch split` refuse a prompt-less non-TTY run up front, naming `--yes`.
|
|
347
278
|
|
|
348
279
|
### Features
|
|
349
280
|
|
|
350
|
-
- `--wait-lock` is accepted by every command. Lock-
|
|
351
|
-
- `
|
|
352
|
-
- An export refused by projected cross-patch lint
|
|
353
|
-
- `fireforge typecheck` runs the per-patch checkJs pass
|
|
354
|
-
- The per-patch lint cache records the
|
|
355
|
-
-
|
|
281
|
+
- `--wait-lock` is accepted by every command. Lock-free commands accept and ignore it and say so.
|
|
282
|
+
- `status --lock` reports the engine session lock without acquiring it: holder, command, hold time, liveness, and queue depth. The wait progress line now states queue position.
|
|
283
|
+
- An export refused by projected cross-patch lint prints the working adopt-then-split sequence with real arguments.
|
|
284
|
+
- `fireforge typecheck` runs the per-patch checkJs pass, so a green typecheck means export-clean types.
|
|
285
|
+
- The per-patch lint cache records the waiver set that produced each entry and refuses a replay under a different one.
|
|
286
|
+
- Internal invariants are checked at runtime and exit 11 with a stack trace. Nothing an operator can misconfigure reaches this path.
|
|
356
287
|
|
|
357
288
|
### Hardening
|
|
358
289
|
|
|
359
|
-
- `build-prepare` warns
|
|
360
|
-
-
|
|
361
|
-
-
|
|
290
|
+
- `build-prepare` warns before overwriting engine content matching neither a patch body nor the baseline, and `build --refuse-unexported-drift` makes it a hard stop.
|
|
291
|
+
- A furnace mutation that throws now rolls back the engine, not just one that is interrupted. A rollback that itself fails leaves a `pendingRepair` marker and keeps the original error.
|
|
292
|
+
- The unexported-drift guard expands collapsed untracked directories, so it stops false-positiving on any wholly-untracked directory. It and `status --unmanaged` now answer about the same tree.
|
|
362
293
|
|
|
363
294
|
### Release hygiene
|
|
364
295
|
|
|
365
|
-
- The composed macOS bundle identity is
|
|
296
|
+
- The composed macOS bundle identity is pinned end to end: `MOZ_MACBUNDLE_ID` plus `--with-distribution-id` must equal the configured `appId`.
|
|
366
297
|
|
|
367
298
|
## 0.42.0
|
|
368
299
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
- `MOZ_MACBUNDLE_ID` in the generated branding `configure.sh` now carries only the LEAF segment of `appId`, and the generated mozconfig emits `ac_add_options --with-distribution-id=<appId prefix>` — upstream `toolkit/moz.configure` composes `CFBundleIdentifier` as `<distribution-id>.<MOZ_MACBUNDLE_ID>`, so writing the full reverse-domain `appId` double-prefixed the shipped bundle id (observed: `org.mozilla.org.hominis.browser`). A `configure.sh` carrying the full `appId` now reads as stale and is rewritten.
|
|
300
|
+
- `MOZ_MACBUNDLE_ID` carries only the leaf segment of `appId`, and the mozconfig emits `--with-distribution-id=<prefix>`. Upstream composes the two, so writing the full reverse-domain name double-prefixed the shipped bundle id.
|
|
372
301
|
|
|
373
302
|
## 0.41.0
|
|
374
303
|
|
|
375
304
|
### Release and build integrity
|
|
376
305
|
|
|
377
|
-
- Release publishing commits version bumps before building and enforces a clean tree
|
|
378
|
-
- Builds stamp `dist/build-info.json`;
|
|
379
|
-
- Build baselines
|
|
380
|
-
- `test --build` uses a full build for new `jar.mn` registrations
|
|
381
|
-
- `--extend-coverage` unions scoped
|
|
382
|
-
- Shared build and engine preflights
|
|
306
|
+
- Release publishing commits version bumps before building and enforces a clean tree.
|
|
307
|
+
- Builds stamp `dist/build-info.json`; `--version` includes the commit and dirty-content hash.
|
|
308
|
+
- Build baselines keep deletion tombstones, so removed files cannot look fresh.
|
|
309
|
+
- `test --build` uses a full build for new `jar.mn` registrations.
|
|
310
|
+
- `--extend-coverage` unions scoped coverage only while HEAD, packageable inputs, and mozconfig are unchanged.
|
|
311
|
+
- Shared build and engine preflights reject incomplete builds and unborn engine repositories consistently.
|
|
383
312
|
|
|
384
313
|
### Tests and verification trees
|
|
385
314
|
|
|
386
|
-
- Tests emit exactly one final `FIREFORGE-VERDICT:` line with the
|
|
387
|
-
- `tree exec` preserves the child verdict as the
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
-
|
|
391
|
-
-
|
|
392
|
-
-
|
|
393
|
-
- Tree freshness covers dirty engine content and patch bodies.
|
|
394
|
-
- Tree creation skips unwanted `obj-*` directories before cloning and uses bounded concurrency.
|
|
395
|
-
- `tree create --wait-lock` now matches its lock-refusal guidance.
|
|
396
|
-
- Verification trees permit `re-export --dry-run`; mutating operations remain blocked.
|
|
397
|
-
- Test assertion lint now recognizes real assertion calls, and critical modules have explicit coverage floors.
|
|
315
|
+
- Tests emit exactly one final `FIREFORGE-VERDICT:` line with the right reason, including sharded, lock-timeout, crash, and inconclusive runs.
|
|
316
|
+
- `tree exec` preserves the child verdict as the last stdout line.
|
|
317
|
+
- Browser preflight detects stale apps from the same objdir even after they release Marionette.
|
|
318
|
+
- Mach commands use process groups and reap surviving descendants.
|
|
319
|
+
- `tree create --with-objdir` supports build-less tests, with relocation and reconfiguration checks.
|
|
320
|
+
- Tree creation refuses symlinked objdirs, cleans failed clones, and handles unreadable locks fail-closed.
|
|
321
|
+
- Verification trees permit `re-export --dry-run`; mutating operations stay blocked.
|
|
398
322
|
|
|
399
323
|
### Status and diagnostics
|
|
400
324
|
|
|
401
|
-
- Status classification reuses one patch context and
|
|
402
|
-
- `status --json --summary` returns counts and offenders without the
|
|
403
|
-
-
|
|
404
|
-
-
|
|
405
|
-
- Multi-owner files remain conflicts even when Furnace-managed or generated branding paths.
|
|
406
|
-
- Lock contention and unreadable/corrupt state now produce concise, accurate diagnostics.
|
|
325
|
+
- Status classification reuses one patch context and runs through a bounded pool.
|
|
326
|
+
- `status --json --summary` returns counts and offenders without the file payload; `--include-ownership` adds ownership without a second scan.
|
|
327
|
+
- JSON output drains fully through slow pipes, and machine-mode refusals go to stderr.
|
|
328
|
+
- Multi-owner files stay conflicts even when Furnace-managed or branding-generated.
|
|
407
329
|
|
|
408
330
|
### Patch, export, and lint workflow
|
|
409
331
|
|
|
410
|
-
- `export-all --exclude-furnace` keeps its
|
|
411
|
-
- `re-export --expect <path>` permits intended drift and reports paths skipped by earlier refusals
|
|
412
|
-
- Missing drift baselines fail closed, binary patches compare by content, and partial
|
|
413
|
-
-
|
|
414
|
-
-
|
|
415
|
-
-
|
|
416
|
-
- Patch lint rejects new imported system modules without a projected `moz.build` registration.
|
|
417
|
-
- The checkJs cache hashes both configurable shims.
|
|
418
|
-
- Export placement refusals print projected errors and identify renumbering consequences.
|
|
332
|
+
- `export-all --exclude-furnace` keeps its scope after license-header repair.
|
|
333
|
+
- `re-export --expect <path>` permits intended drift and reports paths skipped by earlier refusals.
|
|
334
|
+
- Missing drift baselines fail closed, binary patches compare by content, and partial re-exports exit non-zero.
|
|
335
|
+
- Re-export builds checkJs once per run and reuses the per-patch cache.
|
|
336
|
+
- Patch lint rejects new imported system modules with no projected `moz.build` registration.
|
|
337
|
+
- Export placement refusals print projected errors and name renumbering consequences.
|
|
419
338
|
- `patch rename` can recategorize and renumber atomically.
|
|
420
|
-
- `patch move-files` and `patch split` use whole-queue projection lint
|
|
421
|
-
-
|
|
422
|
-
- Queue-mutating patch commands support `--wait-lock` and avoid unsafe stale-lock advice.
|
|
423
|
-
- Staged-dependency declarations reject patch-name-shaped file arguments.
|
|
424
|
-
- `CommandError` passes through the top-level handler without duplicate or JSON-corrupting output.
|
|
339
|
+
- `patch move-files` and `patch split` use whole-queue projection lint.
|
|
340
|
+
- Queue-mutating patch commands support `--wait-lock`.
|
|
425
341
|
|
|
426
342
|
### Furnace and validation
|
|
427
343
|
|
|
428
344
|
- Furnace computes checksums once per apply and warns before overwriting divergent patch-owned files.
|
|
429
|
-
- Furnace jsconfig synchronization includes `register: false` components.
|
|
430
345
|
- Manifest-sync failures are visible, and repairs run under the Furnace lock.
|
|
431
346
|
- `furnace validate --fix` changes only reported components and detects missing custom-element registrations.
|
|
432
347
|
- Localization validation recognizes non-Latin text while exempting emoji and punctuation.
|
|
433
348
|
- `furnace rename` uses single-pass, boundary-aware substitutions.
|
|
434
|
-
- Rollback failures
|
|
435
|
-
- The reported no-change/apply-state disagreement was not reproducible and is regression-tested.
|
|
349
|
+
- Rollback failures leave repair breadcrumbs for `doctor --repair-furnace`.
|
|
436
350
|
|
|
437
351
|
### Safety, performance, and maintenance
|
|
438
352
|
|
|
439
353
|
- Nano ID 3.3.18 resolves a high-severity audit finding.
|
|
440
|
-
- Rebase loading distinguishes absent
|
|
441
|
-
- PID liveness treats `EPERM` as alive
|
|
442
|
-
- The moz.build parser handles comments, quoted brackets
|
|
443
|
-
- Parser failures no longer silently certify validation success
|
|
444
|
-
- Toolchain probes, baseline fingerprints,
|
|
445
|
-
- Existing Git tuning remains enabled; fsmonitor stays disabled because of stale-daemon risk.
|
|
446
|
-
- A persistent checkJs daemon remains deferred until safe invalidation is cheaper; see `docs/lint-daemon-design.md`.
|
|
447
|
-
- Shared helpers replace duplicated preflights, error conversion, regex escaping, option parsing, object guards, and ESTree casts.
|
|
354
|
+
- Rebase loading distinguishes absent from corrupt sessions; `rebase --abort` can recover invalid state.
|
|
355
|
+
- PID liveness treats `EPERM` as alive.
|
|
356
|
+
- The moz.build parser handles comments, quoted brackets and apostrophes, and unterminated lists.
|
|
357
|
+
- Parser failures no longer silently certify validation success.
|
|
358
|
+
- Toolchain probes, baseline fingerprints, and manifest reads use bounded concurrency.
|
|
448
359
|
- Four local ESLint rules prevent unsafe error casts, duplicate regex escaping, and empty JSDoc.
|
|
449
|
-
- Oversized modules were split or simplified, silent degradations documented, and stale dead-code suppressions removed.
|
|
450
360
|
- The release gate pins weak critical modules so aggregate coverage cannot hide local regressions.
|
|
451
361
|
|
|
452
362
|
## 0.40.0
|
|
453
363
|
|
|
454
|
-
-
|
|
455
|
-
-
|
|
456
|
-
-
|
|
457
|
-
-
|
|
458
|
-
-
|
|
459
|
-
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
-
|
|
463
|
-
-
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
-
|
|
467
|
-
-
|
|
468
|
-
-
|
|
469
|
-
-
|
|
470
|
-
-
|
|
471
|
-
- **`export --before/--after` no longer refuses placements whose renumber never actually reaches a reserved range** (bug — `computePlacementPlan` shifted every patch at or above the insertion point by +1, so any positional insert below a `patchPolicy.reservedRanges` block dragged the reserved patches into the rename map and tripped the up-front refusal even with free ordinals in between; in a ~230-patch tree with a 900-999 reserved block, `--before`/`--after` were effectively unavailable and every insert needed an explicit `--order`): the +1 cascade now stops at the first unoccupied ordinal — a gap absorbs the insert, so the tail beyond it (including any reserved block) keeps its numbering — while a genuinely contiguous run into the block still refuses with the same single up-front error. The same cascade serves `patch split` and `patch move-files --create` placements. Pinned by the stop-at-gap and contiguous-refusal cases in `export-placement-policy.test.ts` and the rewritten sparse-queue case in `export-flow.test.ts`.
|
|
364
|
+
- Adopted all five open dependabot PRs after a TypeScript 6 compatibility review. Production: `acorn` 8.18.0 and `magic-string` 1.1.0 (a major that only drops the CJS/UMD builds). Dev tooling and SHA-pinned actions bumped alongside. knip 6.29's stricter re-export tracking found seven dead type re-exports, now removed.
|
|
365
|
+
- New `fireforge tree`: copy-on-write verification clones for concurrent read-only work. `tree create <name>` snapshots the project into `.fireforge/trees/<name>` via APFS clonefile or btrfs/XFS reflink, taken under the engine session lock. Read-only commands run inside a tree unmodified; every mutating command is refused through a default-deny table. Filesystems without CoW refuse honestly, with `--force-copy` as the explicit opt-in. `tree list`, `tree remove`, and `tree exec` round it out. Windows is not supported.
|
|
366
|
+
- Concurrent xpcshell runs no longer collide on Firefox's fixed profile directory. Every dispatch that can run xpcshell exports a fresh `mkdtemp` profile dir and removes it afterwards. An operator-supplied `XPCSHELL_TEST_PROFILE_DIR` is respected and never deleted.
|
|
367
|
+
- Per-patch lint can typecheck patch-adopted test `.js` files through `patchLint.checkJsTestFiles` and `patchLint.checkJsTestShim`. Test files were never in the checkJs program at all before, so a harness global that does not exist could not fail at the patch boundary.
|
|
368
|
+
- Patch size metrics are on the public API (`countNonBinaryDiffLines`, `resolvePatchSizeTier`, `getPatchSizeThresholds`), and `lint --per-patch --report <path>` writes a machine-readable per-patch report.
|
|
369
|
+
- A size finding waived by `lintIgnore` still reports its measurement as a NOTICE, so a waived patch's current size can be read back from the tool that enforces it.
|
|
370
|
+
- Smoke-run summaries attribute allowlist hits per entry and flag zero-hit entries as removal candidates.
|
|
371
|
+
- `lint --per-patch <patch…>` accepts positional arguments through the same alias resolution as `--patches`.
|
|
372
|
+
- `patch staged-dependency --remove` accepts `--file` plus `--specifier` and infers `--creates` from a unique match. Error messages name the missing flag instead of restating the command.
|
|
373
|
+
- `token add` can no longer strand or skip a token when its category is wrong. Category banners match exactly, idempotency is checked per category, and `--variant` validates `--category` instead of discarding it.
|
|
374
|
+
- Patch-lint `raw-color-value` no longer fires on hex colors inside comments that open on a context line and close on an added one.
|
|
375
|
+
- `export --name ui-foo --category ui` lands manifest `name: "foo"` in one step, and a `.patch` suffix no longer trips the dot validator.
|
|
376
|
+
- New `status --check` and `--fail-on <class,…>` turn classification drift into a non-zero exit, so CI no longer has to parse `--json` and reimplement the rules.
|
|
377
|
+
- `status --json` file entries carry a `patch` field naming the owning patch.
|
|
378
|
+
- The scan-less `re-export` adjacency notice is classification-aware, and `--refuse-adjacent-unmanaged` upgrades it to a refusal that skips the write.
|
|
379
|
+
- `re-export` retries once after 300 ms on transient git `index.lock` contention.
|
|
380
|
+
- `export --before/--after` no longer refuses placements whose renumber never reaches a reserved range.
|
|
472
381
|
|
|
473
382
|
## 0.39.0
|
|
474
383
|
|
|
475
|
-
-
|
|
476
|
-
-
|
|
477
|
-
-
|
|
478
|
-
-
|
|
479
|
-
-
|
|
480
|
-
-
|
|
481
|
-
-
|
|
482
|
-
-
|
|
483
|
-
-
|
|
484
|
-
-
|
|
485
|
-
-
|
|
486
|
-
-
|
|
487
|
-
-
|
|
488
|
-
-
|
|
489
|
-
-
|
|
490
|
-
-
|
|
491
|
-
- **Headed macOS no-output timeouts hint at display sleep and `caffeinate`** (message-only — a headed single-file run with no `caffeinate` wrapper timed out at 370 s before the first TEST-START; every subsequent `caffeinate -dimsu`-wrapped run passed): when a harness crash with the no-output-timeout shape came from a HEADED run on macOS, the failure message appends: "Hint: this was a HEADED run on macOS that died at the no-output timeout. A common cause is the display going to sleep mid-run, which freezes the headed browser before any test starts. Wrap headed runs in `caffeinate -dimsu <command>` to keep the display awake, or pass --headless." Auto-wrapping dispatches in `caffeinate` was evaluated and DEFERRED — the message half is the clearly-correct, cheap half.
|
|
384
|
+
- `fireforge discard` restores patch-claimed paths to their patch-applied baseline and re-materializes patch-created files. It used to return upstream Firefox artwork and drop every fork line from `jar.mn`.
|
|
385
|
+
- New per-patch check `mozbuild-unsorted-list` catches unsorted `EXTRA_JS_MODULES` before any build is dispatched.
|
|
386
|
+
- Forward-import detection catches a bare getter line added to an existing `defineESModuleGetters` map, and every refusal names the `patch staged-dependency` command to paste.
|
|
387
|
+
- `patch move-files <from> <to>` into an existing patch is a real transaction rather than a printed preview with the two commands in the wrong order.
|
|
388
|
+
- `patch lint-ignore --add` warns that the waiver is subject to the project's patch-policy review.
|
|
389
|
+
- The mixed-harness refusal runs before the pre-test build instead of after it.
|
|
390
|
+
- `token add --mode override` also writes existing `:root[data-theme="dark"]` and `:root[data-theme="light"]` blocks.
|
|
391
|
+
- A `.patch` extension on a patch-name argument no longer double-suffixes, and `patch rename` explains its no-ops.
|
|
392
|
+
- xpcshell-only runs skip the Marionette port preflight and the mochitest client flags.
|
|
393
|
+
- New `fireforge status --test-coverage` reports the last build baseline's packaging coverage.
|
|
394
|
+
- `typecheck` and the checkJs pass no longer suppress undefined free identifiers (TS2304/TS2552).
|
|
395
|
+
- `reset` and `import` warn when `components.conf` diverged from the last full build.
|
|
396
|
+
- The forward-import "closest legal ordinal" hint is suppressed when no legal ordinal exists.
|
|
397
|
+
- `export` never offers to prepend the project license header onto a vendored third-party file.
|
|
398
|
+
- Signal-shaped configure and build exits are labeled as external interruptions.
|
|
399
|
+
- Headed macOS no-output timeouts hint at display sleep.
|
|
492
400
|
|
|
493
401
|
## 0.38.0
|
|
494
402
|
|
|
495
|
-
-
|
|
496
|
-
-
|
|
497
|
-
-
|
|
498
|
-
-
|
|
499
|
-
-
|
|
500
|
-
-
|
|
501
|
-
-
|
|
502
|
-
-
|
|
503
|
-
-
|
|
504
|
-
-
|
|
403
|
+
- `status --ownership` reports a furnace-deployed file whose owning patch body went stale as drift.
|
|
404
|
+
- Scoped `test --build` and build-less runs refuse a `components.conf` changed since the last full build, since those entries bake into the compiled static component table.
|
|
405
|
+
- Per-patch size lint agrees with `wc -l` and fires only above the soft limit.
|
|
406
|
+
- Patch lint accepts both established upstream MPL header wraps.
|
|
407
|
+
- The packaged-test coverage guard accepts same-manifest siblings instead of demanding the exact recorded path set.
|
|
408
|
+
- Positional inserts refuse with one actionable error when they would renumber a reserved range, `patch move-files --create --order <n>` bootstraps a split in one step, and `re-export --scan` refuses forward-import adoptions.
|
|
409
|
+
- Engine-mutating commands can wait for the engine lock instead of failing fast.
|
|
410
|
+
- `furnace validate` credits the implicit wrapping `<label>` association.
|
|
411
|
+
- `furnace create --shared-ftl <path>` validation is pinned by tests.
|
|
412
|
+
- `source set --candidate <buildN>` fetches release-candidate archives.
|
|
505
413
|
|
|
506
414
|
## 0.37.0
|
|
507
415
|
|
|
508
|
-
-
|
|
509
|
-
-
|
|
510
|
-
-
|
|
511
|
-
-
|
|
512
|
-
-
|
|
513
|
-
-
|
|
514
|
-
-
|
|
515
|
-
-
|
|
516
|
-
- **Mach dispatches reap their whole process GROUP, and `doctor` detects pre-existing orphaned harness workers** (friction-log item 9, severe — a mach that died at harness startup stranded a Python `multiprocessing` spawn worker that busy-spun at 100% CPU for ~26 days): (a) every mach-wrapping dispatch (test suites, protected builds, bootstrap, watch, package, run) now spawns mach as a POSIX process-group leader, forwards parent SIGINT/SIGTERM to the group (with the existing grace→SIGKILL escalation), group-kills on abort, and sweeps the group after every exit — survivors are named (multiprocessing workers highlighted), group-SIGTERMed, and SIGKILLed after a 2s grace, so a startup death leaves zero surviving multiprocessing children; a healthy run costs one `pgrep`. Windows stays best-effort tree-kill via `taskkill /T /F`. (b) A new `doctor` check ("Orphaned harness workers") scans `ps` for the detection recipe that found the incident — PPID 1 + ≥10 min accumulated CPU time + a `multiprocessing.spawn`/`forkserver`/`resource_tracker` command line, parsing both linux `dd-hh:mm:ss` and darwin `mm:ss.cc` TIME dialects — and reports each match with its PID, CPU time, command line, and a `kill <pid>` suggestion; report-only by design (FireForge never kills pre-existing processes), warning severity so it cannot fail doctor, skipped on Windows.
|
|
517
|
-
- **Follow-up findings** (pre-release review of the items above; the behavioral corrections are folded into their bullets): the packaging-coverage refusal now guards EVERY non-`--build` run, not just `--allow-stale-build` ones, with a path-less `test --doctor` exempted since it dispatches no test (item 3 — a plain run over an unchanged tree after a scoped `test --build` sailed through the gate into the very fixture hang the feature targets, while the widened gate would otherwise have refused a doctor-only Marionette probe as an uncovered full-suite request); the stale-furnace export gate attributes a localized component's deployed `<ftlDir>/<name>.ftl` (item 4 — exporting only the deployed FTL file never probed its component source); registration-kind staged dependencies validate `creates`/`owner`/ordering like the import kind (item 5 — metadata naming a nonexistent file or wrong owner linted clean); the teardown-noise echo filter is narrowed to the exact `stop_time`/`poll_interval` signature with a resourcemonitor.py frame after a seen `SUITE_END` (item 8 — it previously collapsed ANY traceback mentioning resourcemonitor.py, hiding novel upstream defects from live output); and the process-group reaper's grace timer is no longer `unref()`'d (item 9a — the timer was awaited from a child `close` handler after the signal forwarder was disposed, so Node could exit mid-grace and skip the SIGKILL escalation; a regression test pins the timer's ref-ness).
|
|
416
|
+
- `fireforge test --build` records the stale-build baseline, so a plain `test` right afterwards is not refused.
|
|
417
|
+
- Every non-`--build` test run tracks packaged-runtime coverage, not just tree state.
|
|
418
|
+
- `export` and `re-export` refuse to capture a stale furnace deploy, with `--allow-stale-furnace` to downgrade it to a warning.
|
|
419
|
+
- `stagedDependencies` gains a registration-kind entry shape for jar.mn lines, customElements registrations, and actor registrations.
|
|
420
|
+
- `furnace validate` accepts `kind: "library"` components.
|
|
421
|
+
- The failure summary echoes the first five `TEST-UNEXPECTED-*` lines with their assertion text.
|
|
422
|
+
- The known mozsystemmonitor teardown traceback is collapsed to one labeled line.
|
|
423
|
+
- Mach dispatches reap their whole process group, and `doctor` detects pre-existing orphaned harness workers.
|
|
518
424
|
|
|
519
425
|
## 0.36.0
|
|
520
426
|
|
|
521
|
-
-
|
|
522
|
-
-
|
|
523
|
-
-
|
|
524
|
-
-
|
|
525
|
-
-
|
|
526
|
-
-
|
|
527
|
-
-
|
|
528
|
-
-
|
|
529
|
-
-
|
|
530
|
-
-
|
|
531
|
-
-
|
|
532
|
-
-
|
|
533
|
-
-
|
|
534
|
-
-
|
|
535
|
-
-
|
|
536
|
-
-
|
|
537
|
-
-
|
|
538
|
-
-
|
|
539
|
-
-
|
|
540
|
-
-
|
|
541
|
-
-
|
|
542
|
-
-
|
|
543
|
-
- **Performance**: `validatePatchIntegrity` batches HEAD-existence checks through one chunked `ls-tree` (one spawn instead of one per patch × file), and import's dirty-file classification loads the manifest/patch set once with bounded concurrency instead of O(files × patches) IO.
|
|
544
|
-
- **Structure**: `escapeRegex` (6 copies), `getPrimaryStatusCode` (2), the untracked-directory expander (2 — the shared one now carries the 5000-per-dir cap, and `reset`/`discard` gained status's unborn-HEAD guidance), the pendingRepair-preserving state reset (4 copies → `clearAppliedFurnaceState`), and the cross-patch claim multimap (3 copies → `buildPatchClaims`) are each single implementations; the second patch-delete implementation (`deletePatch`) is gone in favor of `removePatchFileAndManifest`; the furnace batch-apply loops take an `ApplyBatchContext` instead of 12–13 positional parameters; `stripJsComments` tokenizes with acorn so regex literals survive (legacy regex fallback for preprocessor-laden chrome sources).
|
|
545
|
-
- **Follow-up findings** (same review, second pass): `doctor --repair-patches-manifest` now rebuilds `patches.json` **under the patch-directory lock** — the repair used to write the manifest lock-free, so it could race a concurrent export/reorder and clobber the other writer (a lock-wait regression test pins the serialization, alongside the existing `stampPatchVersions` pin). `patch tier` and `patch lint-ignore` now follow the full destructive-operation contract via `confirmDestructive` (summary, `--dry-run`, confirmation or `--yes`, non-TTY refusal with `--yes` guidance) — both accepted `--yes` without ever prompting, so the flag only appeared in the history record; suppressing lint findings in particular is a mutation an operator should consciously approve. The root `--version` edge case flagged in the same pass (`fireforge --verbose --version` failing on the argv-length-3 fast path) was already fixed above; two CLI tests now pin both the multi-flag form and the non-hijacking of `source set --version`.
|
|
546
|
-
- **Infrastructure**: all GitHub Actions are SHA-pinned (release workflow included); `coverage.include: ['src/**']` closes the Vitest 4 hole where never-imported modules were invisible to thresholds (with new per-module pins for the fuzz/state-persist/step-error modules); the ~2-minute pack smoke test runs once via `pack:verify` (own config) instead of in every `npm test` and twice per release:check; CI has concurrency groups and a stable `quality (ubuntu)` job name that `setup-rulesets.sh` now matches (the Node version embedded in the name silently invalidated the required check on every bump); the full-integration runner's masked-build-failure detection works (its `ls obj-*` glob never expanded under spawn); the coverage-pin matcher requires a path-separator boundary; husky hooks only prepend Homebrew paths that exist; `docs/*.md` and `knip.json` are formatted; `isolatedModules` + `verbatimModuleSyntax` are on (zero fallout); the published tarball no longer ships dangling `sourceMappingURL` comments (build emits no maps); machine-mode logging and the lock races have dedicated unit suites; `docs/lifecycle-invariants.md` records the new lock-ownership, manifest-corruption, and state-persistence invariants with their enforcing tests.
|
|
427
|
+
- `fireforge test` with no path requires `--auto`, `--doctor`, or `--canary`, and stale packaged-engine drift fails fast unless `--build` or `--allow-stale-build` is given.
|
|
428
|
+
- A SIGSEGV-truncated run is no longer reported as PASSED.
|
|
429
|
+
- The build toolchain preflight probes the cbindgen mach actually resolves, which is the mozbuild state directory first.
|
|
430
|
+
- Exact-directory test selection was cosmetic on Firefox 153's mach and its exclusion echo was wrong; both fixed.
|
|
431
|
+
- Verbatim upstream MPL-2.0 block headers are accepted on non-JS files too.
|
|
432
|
+
- `rebase --max-fuzz` was dead code: `git apply` has no `--fuzz` flag, so every escalation failed with a swallowed usage error. Reimplemented, and the flag now rejects invalid numbers.
|
|
433
|
+
- A corrupt `patches.json` is no longer silently destroyed on the next export.
|
|
434
|
+
- `furnace apply <name>` no longer wipes every other component's checksum state.
|
|
435
|
+
- Ordinary `config <key> <value>` no longer deletes `--force`-written keys.
|
|
436
|
+
- Downloads verify against Mozilla's published SHA256SUMS by default, failing closed on mismatch.
|
|
437
|
+
- Archive-safety preflight streams tar listings instead of scanning a truncated capture, and `--dry-run` exports are genuinely read-only.
|
|
438
|
+
- `applyPatchIdempotent` checks git exit codes, chunks pathspecs against `E2BIG`, and threads a protected-files set through both apply loops.
|
|
439
|
+
- File locks are race-hardened: PID plus per-acquisition token, rename-aside stale reaping, and a stale probe that re-runs while waiting.
|
|
440
|
+
- The bin signal handler waits up to 12 s for spawned children, making the SIGTERM to SIGKILL escalation reachable. Windows smoke-run cleanup kills the whole tree.
|
|
441
|
+
- `writeFileAtomic` retries its final rename on transient Windows errors.
|
|
442
|
+
- `status --json` and `--raw` own stdout exclusively; every diagnostic goes to stderr as plain text.
|
|
443
|
+
- All captured child output decodes through `StringDecoder`, and patch parsing is CRLF-tolerant through one shared diff walker.
|
|
444
|
+
- `--version` works alongside other root flags, and option parse errors print one clean line.
|
|
445
|
+
- `furnace remove` deletes only the files the component deployed, and `furnace rename` journals the new-name side.
|
|
446
|
+
- `validatePatchIntegrity` batches HEAD-existence checks through one chunked `ls-tree`.
|
|
447
|
+
- `doctor --repair-patches-manifest` runs under the patch-directory lock.
|
|
448
|
+
- All GitHub Actions are SHA-pinned, and `coverage.include` closes the Vitest 4 hole where never-imported modules were invisible to thresholds.
|
|
547
449
|
|
|
548
450
|
## 0.35.0
|
|
549
451
|
|
|
550
|
-
-
|
|
551
|
-
-
|
|
552
|
-
-
|
|
553
|
-
-
|
|
554
|
-
-
|
|
555
|
-
-
|
|
556
|
-
-
|
|
557
|
-
- The installed-package smoke tests
|
|
558
|
-
- The release workflow
|
|
559
|
-
-
|
|
560
|
-
-
|
|
561
|
-
-
|
|
562
|
-
-
|
|
563
|
-
-
|
|
564
|
-
-
|
|
565
|
-
- Added targeted tests and per-module coverage threshold pins for under-covered edge modules that the global threshold was masking: `signal-critical.ts` (first direct tests: body passthrough/cleanup, multi-section waits, bounded timeout, throwing sections), `bootstrap-checks.ts` (bootstrap output pattern detection incl. traceback/403 collapse and all origin-remote phrasings; SDK-probe branching), `discard.ts` (directory-recursion fallback incl. sibling-prefix safety, batch partial failures, dry-run/confirmation paths, Furnace-managed warnings), and the `furnace-validate-compatibility.ts` compose/CSS-hygiene warnings (`compose-not-referenced`/`compose-not-registered`, `excessive-important`, `missing-reduced-motion`).
|
|
452
|
+
- The patch symlink-escape guard was dead code: it normalized the path text but never followed the link. Target validation now resolves the link.
|
|
453
|
+
- `stampPatchVersions` runs under the shared patch-directory lock.
|
|
454
|
+
- Authoritative-state probes use `pathExistsStrict`, so an EACCES surfaces as an error instead of "missing".
|
|
455
|
+
- `extractTarXz` validates the archive listing before writing anything, rejecting absolute members, `..` segments, and escaping link targets.
|
|
456
|
+
- The CI matrix gained a cross-platform unit-test subset on macOS and Windows. The README's "never tested on Windows" claim was replaced with what CI actually covers.
|
|
457
|
+
- New `docs/lifecycle-invariants.md` records the six invariants the destructive-operation contract upholds.
|
|
458
|
+
- `discard <directory>/` no longer produces doubled slashes, which also broke the Furnace fallback.
|
|
459
|
+
- The installed-package smoke tests skip with a clear message when npm is unavailable.
|
|
460
|
+
- The release workflow installs the exact npm version pinned in `packageManager`.
|
|
461
|
+
- New toolchain preflight for Firefox source hops, after a build died 8 s into configure on an out-of-date cbindgen.
|
|
462
|
+
- The per-patch Firefox-globals shim is extensible: `ChromeUtils` and `Localization` are named interfaces a project can add to.
|
|
463
|
+
- `fireforge test <directory>` selects exactly that directory. Mach matches test paths by string prefix, so a bare directory used to sweep in prefix-named siblings.
|
|
464
|
+
- Per-file test sharding announces itself, and `--no-shard` runs a single combined instance.
|
|
465
|
+
- A headed smoke run on a non-CI host warns that input during the window contaminates the console capture. New `fireforge run --headless`.
|
|
466
|
+
- The verbatim upstream MPL-2.0 header is accepted on new JS files regardless of the project license.
|
|
566
467
|
|
|
567
468
|
## 0.34.0
|
|
568
469
|
|
|
569
|
-
-
|
|
570
|
-
-
|
|
571
|
-
-
|
|
572
|
-
-
|
|
573
|
-
-
|
|
574
|
-
-
|
|
575
|
-
-
|
|
576
|
-
-
|
|
577
|
-
-
|
|
578
|
-
-
|
|
579
|
-
-
|
|
580
|
-
-
|
|
581
|
-
- Added `furnace create --test-dir <dir>` to redirect the `--with-tests` scaffold (browser-chrome and xpcshell styles) to any engine-relative directory under `browser/base/content/test/` — nested manifests register correctly — and made all test scaffolds collision-safe: existing `browser.toml`/`xpcshell.toml`/`chrome.toml` manifests are appended to (with a shared-manifest notice) instead of scaffolded over, and existing `head.js`/test implementation files are never overwritten.
|
|
582
|
-
- Fixed the degraded-psutil guard wedging `mach build` at exit on flapping hosts (0.34.1 post-upgrade field report: psutil's vm/swap syscalls flap between working and degraded, the fallback swap reading arrived svmem-shaped (8 fields) where mozsystemmonitor's parent reconstructs a 6-field `sswap` via `self._swap_type(*swap_mem)`, so every collector sample was rejected — `resourcemonitor.py:766: UserWarning: failed to read the received data` — upstream's handler breaks its drain loop, the pipe fills, the collector child blocks in `send()` and never reads the terminate sentinel, and mach hangs forever in its atexit `waitpid`). The guard's fallback readings are now **per-function, arity-correct, module-level namedtuples** (svmem 8 / sswap 6 / scputimes 4 / sdiskio 6; psutil's own result classes are still preferred when resolvable) that pickle by reference across the collector pipe and survive `type(reading)(*values)` reconstruction in **either** flapping direction (real type + degraded reading, or degraded type + real reading); `cpu_percent`/`cpu_times` fallbacks honour `percpu=True` (return `[]`) for the collector child's per-CPU sampling, and the last-resort `_DegradedReading` now tolerates positional-field construction. The report's "factory resolution bypassed in the collector child" observation is consistent with the two-copy load (`sitecustomize` via PYTHONPATH plus the in-venv `.pth`) or degraded-import-time `psutil._psplatform` resolution — the fix no longer depends on psutil internals resolving, so it is robust to both.
|
|
583
|
-
- Added **collector suppression on degradation** to the guard: any observed psutil degradation sets a process-wide flag, after which monitors are kept inert — `start` never spawns the mozsystemmonitor collector child — and a degraded transition (any wrapped monitor method raising) or a raising `stop` best-effort terminates a live collector child (`_process.terminate()` + bounded join) and drains the parent end of the pipe, so a malformed sample stream can never fail the command or wedge shutdown even if a future shape mismatch slips through. (The upstream enabler — mozsystemmonitor warning then `break`ing out of its drain loop without draining, leaving the pipe to fill — is worth reporting to Mozilla but is not FireForge-fixable.)
|
|
584
|
-
- Fixed the same degradation **failing `mach build` after a fully successful compile** ("Error running mach" with complete, valid `dist/` artifacts): degraded aggregate methods now return zeroed shapes instead of `None` where callers subscript the result (`aggregate_io` → zeroed io namedtuple, so mozbuild's `log_resource_usage` no longer dies on `usage["io"].read_bytes` at `mozbuild/controller/building.py:526`; `aggregate_cpu_percent`/`aggregate_cpu_times` return `[]`/`0.0`/zeroed shapes per their `per_cpu` mode), and the guard additionally wraps `mozbuild.controller.building.BuildMonitor.log_resource_usage` to warn-and-continue on any exception. Runtime-verified by new python3-executed regression tests covering per-function fallback arity, pickle/`type(*values)` round-trips in both flapping directions, collector suppression (stub monitor with upstream-named `_process`/`_pipe`), and the BuildMonitor wrap.
|
|
585
|
-
- Taught the harness-crash classifier the post-success `AttributeError: 'NoneType' object has no attribute 'read_bytes'` traceback (protected builds retry it as crash-not-failure — with the fixed guard the incremental retry is cheap and green; real test failures still take precedence), and exempted the upstream drain-loop `failed to read the received data` warning from crash evidence so runs that complete despite the rejected-sample stream never classify as crashes.
|
|
470
|
+
- The macOS resource-monitor crash mitigation is now an in-process guard installed into every discovered mach virtualenv before each dispatch.
|
|
471
|
+
- Every mach build dispatch goes through one protected path with a uniform recognized-crash retry budget.
|
|
472
|
+
- Fixed the degraded-psutil fallback crashing mozsystemmonitor on itself, and taught the crash classifier its two signatures.
|
|
473
|
+
- A fully green run is no longer marked `CRASH (N attempts)`: a completed green summary vetoes signature-based classification.
|
|
474
|
+
- `fireforge test <directory>` dispatches xpcshell-only directories to `mach xpcshell-test`.
|
|
475
|
+
- New `fireforge register --create-manifest` scaffolds a directory `moz.build` and wires the parent `DIRS` chain.
|
|
476
|
+
- `re-export <patch> --files` accepts the space-separated path shape, with clearer dry-run previews.
|
|
477
|
+
- `furnace deploy` no longer leaves a stale toolkit `jar.mn` line after a helper file rename.
|
|
478
|
+
- New `furnace scan --track` persists discovered untracked components non-interactively.
|
|
479
|
+
- New `furnace chrome-doc create --browser-window` scaffolds a browser.xhtml-like main window document.
|
|
480
|
+
- New `furnace create --test-dir <dir>` redirects the test scaffold, and all test scaffolds are collision-safe.
|
|
481
|
+
- Fixed the degraded-psutil guard wedging `mach build` at exit and failing a build after a fully successful compile.
|
|
586
482
|
|
|
587
483
|
## 0.33.0
|
|
588
484
|
|
|
589
|
-
-
|
|
590
|
-
-
|
|
591
|
-
-
|
|
592
|
-
-
|
|
593
|
-
-
|
|
594
|
-
-
|
|
595
|
-
-
|
|
596
|
-
-
|
|
485
|
+
- `furnace deploy` and `apply` prune a dangling per-widget locale `jar.mn` entry for a `sharedFtl` widget, which used to fail the build outright.
|
|
486
|
+
- New `fireforge token add --variant '[data-skin=precision]'` authors a declaration inside an attribute-keyed `:root` block, creating it if absent.
|
|
487
|
+
- `fireforge register` sorts `EXTRA_JS_MODULES` case-insensitively, matching mozbuild's rule.
|
|
488
|
+
- `fireforge typecheck` regenerates the Furnace-managed jsconfig before running, so a stale shim stops reporting phantom errors.
|
|
489
|
+
- `patch split --dry-run` models the forward edge the split introduces, so the preview matches the real gate.
|
|
490
|
+
- `fireforge test <one xpcshell .js>` recognizes the xpcshell result-summary block as an execution signal.
|
|
491
|
+
- `fireforge build` injects a resource-monitor degrade shim so a host `psutil` failure warns instead of aborting the build.
|
|
492
|
+
- New per-project `typecheck.projectOverrides` to override or opt out of the shared shim.
|
|
597
493
|
|
|
598
494
|
## 0.32.0
|
|
599
495
|
|
|
600
|
-
-
|
|
601
|
-
-
|
|
602
|
-
-
|
|
603
|
-
-
|
|
604
|
-
-
|
|
605
|
-
-
|
|
606
|
-
-
|
|
607
|
-
-
|
|
496
|
+
- `fireforge lint <files>` evaluates `large-patch-files` against each file's owning patch instead of the file-list size.
|
|
497
|
+
- The ad-hoc `lint <files>` path honours each file's owning-patch `lintIgnore`, so all three lint modes agree.
|
|
498
|
+
- New `lint --per-patch --patches <name…>` lints a named subset of the queue.
|
|
499
|
+
- The per-patch checkJs pass builds the queue-wide program once per run instead of once per patch.
|
|
500
|
+
- Cross-patch `resource:///` and `chrome://` imports resolve during export and re-export lint.
|
|
501
|
+
- `furnace sync` emits `./`-prefixed relative `compilerOptions.paths` values.
|
|
502
|
+
- `fireforge test` auto-dispatches a single-suite run to the suite-specific mach command.
|
|
503
|
+
- The crash classifier and `--harness-retries` budget cover the pre-test `--build` step.
|
|
608
504
|
|
|
609
505
|
## 0.31.0
|
|
610
506
|
|
|
611
|
-
-
|
|
612
|
-
-
|
|
613
|
-
-
|
|
614
|
-
-
|
|
615
|
-
-
|
|
616
|
-
- Added `ChromeUtils.getClassName`, `ChromeUtils.defineLazyGetter`, and the `Localization` constructor to the shipped
|
|
617
|
-
-
|
|
618
|
-
-
|
|
619
|
-
-
|
|
620
|
-
-
|
|
621
|
-
-
|
|
622
|
-
-
|
|
623
|
-
-
|
|
624
|
-
-
|
|
625
|
-
-
|
|
626
|
-
-
|
|
627
|
-
-
|
|
628
|
-
- Internalized 45 exports
|
|
629
|
-
- Refactored the twelve functions
|
|
630
|
-
-
|
|
631
|
-
-
|
|
632
|
-
- Bumped the TypeScript `target`/`lib` from ES2022 to ES2023 (Node ≥ 22.22.1 fully implements it) and dropped the redundant `@typescript-eslint/eslint-plugin` / `@typescript-eslint/parser` devDependencies already provided by the `typescript-eslint` meta-package.
|
|
507
|
+
- `patch compact` is range-aware: each configured category range compacts independently.
|
|
508
|
+
- Staged-dependency `owner` references are rewritten during every renumber instead of dangling.
|
|
509
|
+
- New `fireforge patch split <source> --files <paths…> --name <name>` moves files into a new patch as one transaction.
|
|
510
|
+
- Plain `re-export --scan` is scoped to the patch's exact directory footprint. Git pathspecs recurse, so a shallow claimed file used to sweep the whole subtree.
|
|
511
|
+
- The per-patch checkJs pass resolves imports of patch-owned modules to their real sources, so JSDoc type guards and generics survive module boundaries.
|
|
512
|
+
- Added `ChromeUtils.getClassName`, `ChromeUtils.defineLazyGetter`, and the `Localization` constructor to the shipped shim.
|
|
513
|
+
- The JSDoc `@param` extractor scans balanced braces, so nested generics parse.
|
|
514
|
+
- `observer-topic-naming` parses balanced multi-line call sites and inspects the actual topic argument, with a known-Firefox-topics allowlist.
|
|
515
|
+
- `fireforge test` classifies harness runs from their output rather than trusting exit codes.
|
|
516
|
+
- Multi-path test runs are sharded into sequential single-file runs with per-shard retries. `--no-shard` restores the combined invocation.
|
|
517
|
+
- New `fireforge test --perf-samples <path>` publishes the artifact path to the harness.
|
|
518
|
+
- Named `furnace deploy <component>` runs the same pipeline as deploy-all, so renames and deletions prune their engine copies.
|
|
519
|
+
- Shared CSS fragments for Furnace widgets through a `/* @fireforge-include <fragment>.css */` directive.
|
|
520
|
+
- Automatic jsconfig `paths` maintenance for multi-file components.
|
|
521
|
+
- `token add` no longer double-prefixes a name that already starts with the configured prefix.
|
|
522
|
+
- New `token add --create-category` declares the banner and inserts the token in one write.
|
|
523
|
+
- Two new release gates: `deadcode:check` (knip) and `cycles:check` (dpdm).
|
|
524
|
+
- Internalized 45 exports with no outside consumers and untangled seven type-only import cycles.
|
|
525
|
+
- Refactored the twelve functions above cyclomatic complexity 30 and now enforce the limit.
|
|
526
|
+
- `process.exit()` is restricted to `bin/fireforge.ts` by lint.
|
|
527
|
+
- TypeScript `target`/`lib` moved from ES2022 to ES2023.
|
|
633
528
|
|
|
634
529
|
## 0.30.0
|
|
635
530
|
|
|
636
|
-
- Added
|
|
531
|
+
- Added repo-local per-patch lint result caching, with `--no-cache` and `lint cache clear`. Warm hits skip diff generation as well as rule execution.
|
|
637
532
|
|
|
638
533
|
## 0.29.0
|
|
639
534
|
|
|
640
|
-
-
|
|
535
|
+
- `test --build` failure reporting names the rebuild command, requested paths, and first failure line separately from stale-artifact advice.
|
|
641
536
|
|
|
642
537
|
## 0.28.0
|
|
643
538
|
|
|
644
|
-
- Restored mach lint compatibility
|
|
645
|
-
-
|
|
646
|
-
-
|
|
647
|
-
-
|
|
648
|
-
-
|
|
649
|
-
-
|
|
650
|
-
-
|
|
651
|
-
-
|
|
652
|
-
- Fixed partial `re-export` manifest writes so legacy source metadata is preserved on unselected patch rows unless `--stamp` or another source metadata update explicitly targets them.
|
|
653
|
-
- Added regression coverage for targeted and full stamped re-export round-trips with blank context lines.
|
|
539
|
+
- Restored mach lint compatibility by materializing a `.hgignore` copy of `.gitignore` when Firefox's ignorefile linter config is present.
|
|
540
|
+
- `source set` prints the resolved archive URL so a pinned checksum can be verified before download.
|
|
541
|
+
- `re-export --dry-run` takes a lock so parallel previews serialize instead of racing on `.git/index.lock`.
|
|
542
|
+
- New `re-export --scan --scan-files <manifest>` for bulk generated-file assignment.
|
|
543
|
+
- Better `fireforge test` diagnostics for harness startup failures and zero-tests-run.
|
|
544
|
+
- Build failure summaries put real make and mach failures above trailing warnings.
|
|
545
|
+
- Blank context lines keep their unified-diff marker, so generated patches stop producing false drift warnings.
|
|
546
|
+
- Partial `re-export` writes preserve legacy source metadata on unselected rows.
|
|
654
547
|
|
|
655
548
|
## 0.27.3
|
|
656
549
|
|
|
657
|
-
-
|
|
658
|
-
-
|
|
659
|
-
-
|
|
550
|
+
- `firefox-devedition` downloads resolve against `/pub/devedition/releases`.
|
|
551
|
+
- `download --force` keeps the existing `engine/` tree until the replacement extracts successfully.
|
|
552
|
+
- Checksum mismatch diagnostics include the resolved URL and product.
|
|
660
553
|
|
|
661
554
|
## 0.27.0
|
|
662
555
|
|
|
663
|
-
-
|
|
664
|
-
-
|
|
665
|
-
-
|
|
666
|
-
-
|
|
667
|
-
-
|
|
668
|
-
-
|
|
669
|
-
-
|
|
670
|
-
- Added cache metadata progress for archive validation, SHA-256 calculation, and sidecar JSON writes.
|
|
671
|
-
- Added elapsed progress for extraction, initial source commits, and rebase/re-export patch refreshes.
|
|
672
|
-
- Added `re-export --files --allow-shrink` so patch ownership shrinkage is refused unless explicitly acknowledged, with clearer dry-run previews.
|
|
673
|
-
- Surfaced likely new sibling files during plain re-export and aligned verify/status ownership reporting for unowned worktree changes.
|
|
556
|
+
- First-class `firefox-devedition` support and atomic `fireforge source set`.
|
|
557
|
+
- `source set --version` accepts both space and equals forms without colliding with the root version flag.
|
|
558
|
+
- New `sourceProduct` and `sourceVersion` patch metadata; `sourceEsrVersion` stays as a deprecated alias.
|
|
559
|
+
- Unified status, ownership, doctor, and verify worktree classification, including an explained patch-owned drift state.
|
|
560
|
+
- Build diagnostics carry exit codes, tails, log hints, and verbose rerun suggestions.
|
|
561
|
+
- Progress output for git indexing, archive validation, extraction, and rebase refreshes.
|
|
562
|
+
- New `re-export --files --allow-shrink`, so ownership shrinkage must be acknowledged.
|
|
674
563
|
- Preserved patch-owned branding `configure.sh` settings during build preflight.
|
|
675
|
-
-
|
|
676
|
-
-
|
|
677
|
-
- Improved rebase conflict summaries and added `doctor --post-rebase-audit` for common registration surfaces.
|
|
564
|
+
- Custom element registration support for Furnace validate and apply.
|
|
565
|
+
- Improved rebase conflict summaries and new `doctor --post-rebase-audit`.
|
|
678
566
|
|
|
679
567
|
## 0.26.0
|
|
680
568
|
|
|
681
|
-
-
|
|
682
|
-
-
|
|
683
|
-
- Kept generated patch context lines unchanged while making release checks use the FireForge whitespace gate.
|
|
569
|
+
- New targeted `re-export --scan --scan-file <path>` for reviewed single-patch new-file assignment.
|
|
570
|
+
- A FireForge-owned whitespace gate excludes generated `patches/*.patch` diff syntax from repository whitespace checks.
|
|
684
571
|
|
|
685
572
|
## 0.25.0
|
|
686
573
|
|
|
687
|
-
- Kept `MOZ_APP_VENDOR` in `browser/moz.configure` for Firefox ESR 140
|
|
688
|
-
- Added a regression for stale xpcshell install symlink repair
|
|
574
|
+
- Kept `MOZ_APP_VENDOR` in `browser/moz.configure` for Firefox ESR 140 trees instead of generated branding `configure.sh`.
|
|
575
|
+
- Added a regression for stale xpcshell install symlink repair.
|
|
689
576
|
|
|
690
577
|
## 0.24.0
|
|
691
578
|
|
|
692
|
-
- Moved branding vendor identity into generated branding configure scripts
|
|
693
|
-
- Added metadata-backed staged forward-import declarations
|
|
579
|
+
- Moved branding vendor identity into generated branding configure scripts.
|
|
580
|
+
- Added metadata-backed staged forward-import declarations and `patch staged-dependency`.
|
|
694
581
|
- Added stale xpcshell `_tests` symlink repair with a single safe retry.
|
|
695
|
-
- Added `patch move-files` for previewable ownership-transfer
|
|
696
|
-
- Improved queue self-containment guidance for staged dependencies and patch repairs.
|
|
582
|
+
- Added `patch move-files` for previewable ownership-transfer plans.
|
|
697
583
|
|
|
698
584
|
## 0.23.0
|
|
699
585
|
|
|
700
|
-
- Improved xpcshell
|
|
586
|
+
- Improved xpcshell argument filtering and mixed-harness diagnostics.
|
|
701
587
|
- Locked pre-test build phases and improved stale harness diagnostics.
|
|
702
588
|
- Fixed binary-safe re-export for new untracked files.
|
|
703
589
|
- Improved additive `re-export --files` and lint warning guidance.
|
|
@@ -708,7 +594,6 @@ On the test side: path expectations and mock keys are rendered through a new `na
|
|
|
708
594
|
- Shared patch queue health checks between `verify` and doctor recovery.
|
|
709
595
|
- Improved Furnace repair for empty custom orphan directories.
|
|
710
596
|
- Enforced patch policy during `patch compact`.
|
|
711
|
-
- Shortened README and changelog into maintainer-facing docs.
|
|
712
597
|
|
|
713
598
|
## 0.21.0
|
|
714
599
|
|
|
@@ -716,7 +601,6 @@ On the test side: path expectations and mock keys are rendered through a new `na
|
|
|
716
601
|
- Added versioned `status --json` output.
|
|
717
602
|
- Added configurable patch queue policy.
|
|
718
603
|
- Hardened export, Furnace deploy, and UI build preflights.
|
|
719
|
-
- Improved Furnace rename, override removal, and interrupt diagnostics.
|
|
720
604
|
|
|
721
605
|
## 0.20.0
|
|
722
606
|
|
|
@@ -748,12 +632,11 @@ On the test side: path expectations and mock keys are rendered through a new `na
|
|
|
748
632
|
- Added `patch tier` and per-patch lint-ignore editing.
|
|
749
633
|
- Fixed `export-all` ownership and Furnace exclusion cases.
|
|
750
634
|
- Improved build, test, and status diagnostics.
|
|
751
|
-
- Cleaned up fork-specific examples.
|
|
752
635
|
|
|
753
636
|
## 0.16.0
|
|
754
637
|
|
|
755
638
|
- Hardened release and config security paths.
|
|
756
|
-
- Fixed `config --force` read
|
|
639
|
+
- Fixed `config --force` read and write behaviour.
|
|
757
640
|
- Improved download, status, and setup feedback.
|
|
758
641
|
- Added safer Furnace init, create, preview, and chrome-doc behaviour.
|
|
759
642
|
- Improved lint, build audit, and rebase reliability.
|
|
@@ -780,7 +663,7 @@ On the test side: path expectations and mock keys are rendered through a new `na
|
|
|
780
663
|
- Added tiered lint severity for large files and patches.
|
|
781
664
|
- Added raw-colour allowlists and inline suppression.
|
|
782
665
|
- Added `fireforge patch compact`.
|
|
783
|
-
- Improved register support for XHTML
|
|
666
|
+
- Improved register support for XHTML and CSS.
|
|
784
667
|
|
|
785
668
|
## 0.12.0
|
|
786
669
|
|