@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
|
@@ -3,313 +3,474 @@
|
|
|
3
3
|
# Mutation lifecycle invariants (internal)
|
|
4
4
|
|
|
5
5
|
Internal architecture notes for contributors. FireForge's safety behavior
|
|
6
|
-
around destructive operations is
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
around destructive operations is spread across small modules on purpose. This
|
|
7
|
+
document is the one place that states the invariants those modules uphold
|
|
8
|
+
together, and says which module owns which piece. If you add a new mutating
|
|
9
|
+
command, read the decision table at the bottom first.
|
|
10
10
|
|
|
11
11
|
## The invariants
|
|
12
12
|
|
|
13
13
|
1. **Only `bin/fireforge.ts` terminates the process.** Library code
|
|
14
|
-
propagates `CommandError`
|
|
15
|
-
`process.exit`.
|
|
16
|
-
(`eslint.config.js`), which bans
|
|
17
|
-
`bin/**` override.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
propagates `CommandError` and `FireForgeError`. It never calls
|
|
15
|
+
`process.exit`. This is enforced at lint time by
|
|
16
|
+
`no-restricted-properties` (`eslint.config.js`), which bans
|
|
17
|
+
`process.exit` everywhere and grants a `bin/**` override.
|
|
18
|
+
|
|
19
|
+
2. **At most one real engine or patch mutation runs at a time** per
|
|
20
|
+
resource. CLI build, test, export, re-export and furnace mutations first
|
|
21
|
+
take the `.fireforge/engine-session.lock`, then subsystem locks such as
|
|
22
|
+
build, furnace, or patch-directory locks. Furnace mutations serialize on a
|
|
23
|
+
per-root lock, and patch directory mutations (filename allocation plus
|
|
24
|
+
manifest writes) on a per-directory lock. Dry runs hold the engine-session
|
|
25
|
+
lock too, because a `re-export --dry-run` over an untracked binary legally
|
|
26
|
+
touches the git index, and holding the lock serializes even "read-only"
|
|
26
27
|
previews against real writers.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
|
|
29
|
+
3. **A furnace mutation that does not finish restores the engine or says
|
|
30
|
+
so.** Both a signal (SIGINT or SIGTERM) and a thrown error trigger a
|
|
31
|
+
journal-based rollback. If the rollback is incomplete or uncertain, a
|
|
32
|
+
`pendingRepair` marker blocks all further furnace mutations until
|
|
31
33
|
`fireforge doctor --repair-furnace` reconciles.
|
|
32
34
|
|
|
33
35
|
The thrown-error half landed in 0.43.0. Before it, `runFurnaceMutation`'s
|
|
34
|
-
`finally` only deregistered the operation
|
|
36
|
+
`finally` only deregistered the operation, and because it also set
|
|
35
37
|
`completed`, a body that threw removed itself from the signal handler's
|
|
36
|
-
view as well
|
|
37
|
-
torn with no marker. `applyAllComponents` was the live instance:
|
|
38
|
-
back on collected `result.errors
|
|
39
|
-
pre
|
|
40
|
-
the journal, and rethrows the
|
|
41
|
-
becomes a marker plus a warning, never a replacement
|
|
38
|
+
view as well. A throw outside a body's own catch therefore left the
|
|
39
|
+
checkout torn with no marker. `applyAllComponents` was the live instance:
|
|
40
|
+
it rolls back on collected `result.errors` and `stepErrors`, but a throw
|
|
41
|
+
from its pre-loop or post-loop work escaped both. The wrapper now runs
|
|
42
|
+
cleanups, restores the journal, and rethrows the original error. A
|
|
43
|
+
rollback failure becomes a marker plus a warning, never a replacement
|
|
44
|
+
error.
|
|
42
45
|
|
|
43
46
|
Both paths race for the same journal, so each operation carries a
|
|
44
|
-
`rollbackState` claimed synchronously before either path's first await
|
|
45
|
-
|
|
46
|
-
already restore on their own
|
|
47
|
-
`furnace-apply`, `preview`
|
|
48
|
-
keeps its hands off.
|
|
49
|
-
that never registered a journal writes
|
|
50
|
-
|
|
51
|
-
repair with nothing to reconcile
|
|
47
|
+
`rollbackState` claimed synchronously before either path's first await.
|
|
48
|
+
Whichever gets there first restores, and the other skips. Bodies that
|
|
49
|
+
already restore on their own (the eight `furnace/*` command bodies,
|
|
50
|
+
`furnace-apply`, `preview`) call `ctx.markRolledBack()` so the wrapper
|
|
51
|
+
keeps its hands off. There is one deliberate asymmetry: on the throw path,
|
|
52
|
+
a body that never registered a journal writes no marker, because a
|
|
53
|
+
preflight refusal would otherwise block every later mutation behind a
|
|
54
|
+
repair with nothing to reconcile. The signal path still writes one there,
|
|
52
55
|
since an interrupt genuinely may have landed mid-write.
|
|
53
56
|
|
|
54
|
-
4. **A signal during a compound "apply
|
|
55
|
-
pair.** Rebase-style operations intentionally leave the engine mutated,
|
|
56
|
-
|
|
57
|
+
4. **A signal during a compound "apply plus persist" pair never tears the
|
|
58
|
+
pair.** Rebase-style operations intentionally leave the engine mutated, so
|
|
59
|
+
rollback is the wrong tool. Instead the exit is held, with a bound, until
|
|
57
60
|
the bookkeeping write lands, so `--continue` never sees stale progress.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
61
|
+
|
|
62
|
+
5. **No lock outlives the process it belongs to** (best effort, in two
|
|
63
|
+
layers): the signal pipeline force-releases furnace locks before exiting,
|
|
64
|
+
and stale-lock recovery (PID first, then age) unblocks the next command
|
|
65
|
+
for anything that escaped.
|
|
66
|
+
|
|
67
|
+
6. **Destructive patch operations are gated and audited.** That means an
|
|
68
|
+
explicit change summary, a confirmation (or `--yes`), `--dry-run`, a hard
|
|
69
|
+
refusal on structural conflicts (`--force-unsafe` to override), and a
|
|
70
|
+
JSONL history entry appended only after the mutation succeeded. This
|
|
71
|
+
covers metadata mutations too: `patch tier` and `patch lint-ignore` route
|
|
72
|
+
through `confirmDestructive`. They used to accept `--yes` without ever
|
|
73
|
+
prompting, so the flag only showed up in the history record. Enforced by
|
|
74
|
+
`patch-tier-and-lint-ignore.integration.test.ts`.
|
|
75
|
+
|
|
76
|
+
7. **A verification tree never mutates shared state.** Trees are full
|
|
77
|
+
project-root CoW snapshots under `.fireforge/trees/<name>`, with no
|
|
78
|
+
merge-back model.
|
|
79
|
+
|
|
80
|
+
Enforcement is layered. The `.fireforge/tree.json` marker plus a
|
|
81
|
+
default-deny command-verdict table (`src/core/tree-guard.ts`) refuse every
|
|
82
|
+
mutating command through one commander `preAction` hook in `cli.ts`, so a
|
|
83
|
+
newly added command is refused in trees until someone classifies it (there
|
|
84
|
+
is a drift test for this). Because a tree is a full project root, its
|
|
85
|
+
build and engine-session locks key on the tree root, so in-tree reads
|
|
86
|
+
never contend with the primary.
|
|
87
|
+
|
|
88
|
+
`tree create` snapshots under the primary engine-session lock, so it never
|
|
89
|
+
captures a mid-mutation state. With `--with-objdir` it additionally holds
|
|
90
|
+
the primary build lock, under which the objdir is resolved and its build
|
|
91
|
+
artifacts validated immediately before cloning. Any probe before the lock
|
|
92
|
+
is a fast-fail courtesy whose result is discarded, never the authoritative
|
|
93
|
+
answer. A concurrent `fireforge build` can therefore neither tear the
|
|
94
|
+
`obj-*` snapshot nor swap it between preflight and clone.
|
|
95
|
+
|
|
96
|
+
A symlinked (or engine-escaping) primary `obj-*` is refused before any
|
|
97
|
+
copying. Every `cp` mode the clone uses preserves symlinks, so the clone
|
|
98
|
+
would carry a link back to the original build, and the mozinfo rewrite
|
|
99
|
+
plus `_virtualenvs` removal would mutate that original through it.
|
|
100
|
+
`assertCloneSafeObjdir` (`tree-store.ts`) checks lstat and realpath
|
|
101
|
+
containment, which a lexical `resolve()` cannot see, on the primary objdir
|
|
102
|
+
and again on the tree's copy before any write goes through it. This is
|
|
103
|
+
pinned by `tree-store.integration.test.ts` and `tree.integration.test.ts`.
|
|
104
|
+
Tree create and remove serialise on a primary-side `.fireforge/trees.lock`,
|
|
105
|
+
and `tree remove` refuses while a live PID holds a tree lock and checks
|
|
106
|
+
that its `rm -rf` target is contained in the tree path.
|
|
98
107
|
|
|
99
108
|
In-tree `test` (0.41.0) is the one conditional carve-out, and it is
|
|
100
|
-
marker-gated
|
|
109
|
+
marker-gated. mach objdirs embed absolute `topsrcdir` and `topobjdir`
|
|
101
110
|
paths, so `tree create --with-objdir` rewrites the cloned objdir's
|
|
102
|
-
`mozinfo.json` to the tree
|
|
103
|
-
`build --rewrite-mozinfo` uses, scrubs the cloned `_virtualenvs`
|
|
104
|
-
|
|
105
|
-
inside the tree so the configure-generated root files are
|
|
106
|
-
against the tree's paths rather than
|
|
107
|
-
verifies exactly that set afterwards (config.status, backend.mk,
|
|
108
|
-
Makefile, config/autoconf.mk, via `findObjdirRelocationViolation`,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
in-tree
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(`assertObjdirMatchesTreeMarker`), so an objdir
|
|
121
|
-
any path other than `tree create --with-objdir` is
|
|
122
|
-
`test --build` stays refused
|
|
123
|
-
model
|
|
124
|
-
verdict.
|
|
125
|
-
|
|
126
|
-
**Both halves of that enforcement fail
|
|
127
|
-
because "we cannot tell" is not "there is nothing there."** A marker
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
111
|
+
`mozinfo.json` to the tree using the same safe-relocation rewriter that
|
|
112
|
+
`build --rewrite-mozinfo` uses, scrubs the cloned `_virtualenvs` (they
|
|
113
|
+
carry primary shebangs, and mach rebuilds them in-tree), and runs
|
|
114
|
+
`mach configure` inside the tree so the configure-generated root files are
|
|
115
|
+
regenerated against the tree's paths rather than keeping the primary's. It
|
|
116
|
+
then verifies exactly that set afterwards (config.status, backend.mk,
|
|
117
|
+
Makefile, config/autoconf.mk, via `findObjdirRelocationViolation`, pinned
|
|
118
|
+
by `mach-objdir-relocation.test.ts`). Nested Makefiles are products of the
|
|
119
|
+
verified config.status, and `.deps` build products are out of scope as
|
|
120
|
+
read-only staleness that the first in-tree rebuild corrects. Finally it
|
|
121
|
+
copies `last-build.json` so the in-tree stale-build gates anchor
|
|
122
|
+
correctly.
|
|
123
|
+
|
|
124
|
+
Both relocation steps fail closed: a rewrite refusal or a failed in-tree
|
|
125
|
+
configure aborts the create and removes the partial tree. Only after both
|
|
126
|
+
succeed is the marker's `clonedObjdir` field written, and that field is
|
|
127
|
+
what the guard's `test` predicate consults. At run time, `test`
|
|
128
|
+
additionally refuses when the objdir its preflight found is not the one
|
|
129
|
+
the marker vouches for (`assertObjdirMatchesTreeMarker`), so an objdir
|
|
130
|
+
that appeared through any path other than `tree create --with-objdir` is
|
|
131
|
+
never run against. `test --build` stays refused, since a tree is a
|
|
132
|
+
snapshot with no merge-back model, and a corrupt marker refuses `test`
|
|
133
|
+
like every other conditional verdict.
|
|
134
|
+
|
|
135
|
+
**Both halves of that enforcement fail closed on an unreadable state,
|
|
136
|
+
because "we cannot tell" is not "there is nothing there."** A marker that
|
|
137
|
+
exists but does not parse reports `corrupt`, not `absent`, and refuses the
|
|
138
|
+
command. Reading it as absent granted a snapshot the full mutating command
|
|
139
|
+
set on the strength of a file that failed to parse. A lock directory whose
|
|
140
|
+
owner cannot be identified reports `unknown`, not free, and refuses
|
|
141
|
+
removal. Since 0.45.1 `withFileLock` treats its owner-record write as
|
|
142
|
+
fatal (`writeLockOwner` in `file-lock-owner.ts` removes the just-created
|
|
143
|
+
directory and throws a `GeneralError` if the record cannot be written or
|
|
144
|
+
does not read back as written), so an owner-less lock is only ever left by
|
|
145
|
+
a pre-0.45.1 holder or by a crash inside that write. It is still refused,
|
|
146
|
+
for the same reason. The one exception is a lock directory that vanished
|
|
147
|
+
between the probe and the owner read: that is a clean release, and reads
|
|
148
|
+
as free.
|
|
149
|
+
|
|
150
|
+
Each case has one named escape (`--ignore-corrupt-tree-marker`,
|
|
151
|
+
`tree remove --force`), and neither has a silent one. The corrupt-marker
|
|
152
|
+
refusal spares the read-only commands that are unconditionally allowed
|
|
143
153
|
(`status`, `lint`, `typecheck`, `verify`): their verdict never consults
|
|
144
154
|
marker fields, so an unreadable marker cannot change their answer, and
|
|
145
155
|
blocking them left the operator unable to diagnose the very tree that
|
|
146
|
-
needs inspecting. `conditional` verdicts can write and stay refused.
|
|
147
|
-
Enforced by
|
|
148
|
-
`tree-
|
|
149
|
-
|
|
150
|
-
refusal, rewrite-then-reconfigure-then-marker ordering),
|
|
156
|
+
needs inspecting. `conditional` verdicts can write, and stay refused.
|
|
157
|
+
Enforced by `tree-guard.test.ts` (verdict drift gate and corrupt-marker
|
|
158
|
+
refusal), `tree-store.integration.test.ts` (marker tri-state,
|
|
159
|
+
unknown-owner refusal, rewrite-then-reconfigure-then-marker ordering),
|
|
151
160
|
`tree.test.ts` (under-lock objdir re-validation ordering) and
|
|
152
161
|
`tree.integration.test.ts` (real-program refusal).
|
|
153
162
|
|
|
154
|
-
8. **A verdict is only reported when it was actually verified.**
|
|
155
|
-
test` brackets the harness run with `snapshotEngineGeneration`
|
|
156
|
-
fails the run if `engine/` changed underneath it. The probe can
|
|
157
|
-
legitimately fail
|
|
158
|
-
that fails on
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
a durable failure (a non-git `engine/`, an unreadable `.git`) still
|
|
168
|
-
reports immediately. Enforced by
|
|
163
|
+
8. **A verdict is only reported when it was actually verified.**
|
|
164
|
+
`fireforge test` brackets the harness run with `snapshotEngineGeneration`
|
|
165
|
+
and fails the run if `engine/` changed underneath it. The probe can
|
|
166
|
+
legitimately fail, since `engine/` need not be a git checkout, so a probe
|
|
167
|
+
that fails on both sides warns and proceeds. A one-sided transition
|
|
168
|
+
throws, because either the second probe measured nothing about a checkout
|
|
169
|
+
that had something to measure, or there was no baseline to compare
|
|
170
|
+
against. Enforced by `engine-session-lock.test.ts`.
|
|
171
|
+
|
|
172
|
+
A probe that fails on a transient `.git/index.lock`, meaning another
|
|
173
|
+
writer held the index for the instant we looked, is retried on a bounded
|
|
174
|
+
budget rather than latched as unmeasurable. A durable failure (a non-git
|
|
175
|
+
`engine/`, an unreadable `.git`) still reports immediately. Enforced by
|
|
169
176
|
`engine-generation-probe-retry.test.ts`.
|
|
170
177
|
|
|
171
|
-
The same invariant reaches across the `tree exec` boundary.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
consumers
|
|
178
|
+
The same invariant reaches across the `tree exec` boundary. `tree exec`
|
|
179
|
+
hands stdout to the child (`stdio: 'inherit'`), so the child's
|
|
180
|
+
`FIREFORGE-VERDICT:` line reaches the caller byte for byte. The parent
|
|
181
|
+
therefore seals stdout the moment the spawn settles, so its own "exited
|
|
182
|
+
with code N" refusal cannot print after the verdict and displace it. A
|
|
183
|
+
refusal raised before the child spawns emits no verdict at all, so
|
|
184
|
+
consumers have to read a missing verdict as a failed step, never a pass.
|
|
178
185
|
|
|
179
186
|
9. **FireForge's own read-only commands are not writers of the primary
|
|
180
|
-
engine checkout
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
engine checkout.** `verify`, `lint --per-patch` and `typecheck` are
|
|
188
|
+
read-only to the operator but not to git: `git status` and `git diff HEAD`
|
|
189
|
+
refresh and rewrite `.git/index`, and the untracked-binary diff path
|
|
190
|
+
stages and unstages for real. Each of those commands therefore runs its
|
|
191
|
+
whole body inside `withPrivateGitIndex`, which seeds a temp
|
|
192
|
+
`GIT_INDEX_FILE` from the repository's own index so that every refresh
|
|
193
|
+
lands there and is discarded. `typecheck` additionally forces
|
|
194
|
+
`incremental: false` and drops `tsBuildInfoFile`, so no sidecar is written
|
|
195
|
+
under `engine/`. The scope fails open, meaning an unresolvable git dir
|
|
196
|
+
simply does not install it, and it is not reentrant. This exists so that
|
|
190
197
|
invariant 8 stays evidential: a concurrent `fireforge test` must not be
|
|
191
198
|
invalidated by FireForge's own read-only lane. Enforced by
|
|
192
|
-
`git-readonly-index.test.ts
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
`<lock>.waiters/` named
|
|
197
|
-
in a `finally` that covers
|
|
198
|
-
|
|
199
|
-
waiter cannot inflate it forever,
|
|
200
|
-
failures to register are logged verbosely
|
|
201
|
-
acquisition. `fireforge status --lock` and the
|
|
202
|
-
line read it. Enforced by `lock-visibility.test.ts`
|
|
203
|
-
waiter) and `engine-session-lock.test.ts` (queue
|
|
204
|
-
line).
|
|
199
|
+
`git-readonly-index.test.ts`, against a real repository and a real index
|
|
200
|
+
mtime.
|
|
201
|
+
|
|
202
|
+
10. **Lock queues are observable, and leaving one is as reliable as joining
|
|
203
|
+
it.** A waiter that contends registers a file in `<lock>.waiters/` named
|
|
204
|
+
`<startedAtMs>-<pid>-<uuid>` and deregisters in a `finally` that covers
|
|
205
|
+
both outcomes: acquired the lock, or timed out. Depth counts only entries
|
|
206
|
+
whose PID is still alive, so a killed waiter cannot inflate it forever,
|
|
207
|
+
and the registry is advisory: failures to register are logged verbosely
|
|
208
|
+
and never fail an acquisition. `fireforge status --lock` and the
|
|
209
|
+
`--wait-lock` progress line read it. Enforced by `lock-visibility.test.ts`
|
|
210
|
+
(a real concurrent waiter) and `engine-session-lock.test.ts` (queue
|
|
211
|
+
position in the wait line).
|
|
205
212
|
|
|
206
213
|
11. **A manifest repair preserves what it cannot recompute, or refuses.**
|
|
207
|
-
`doctor --repair-patches-manifest` is a
|
|
214
|
+
`doctor --repair-patches-manifest` is a merge: only `filesAffected` and
|
|
208
215
|
`order` are derived from the patch files, and every other field on an
|
|
209
|
-
existing entry is carried forward by spreading it
|
|
210
|
-
body cannot express
|
|
211
|
-
whatever is added to `PatchMetadata` next
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
existing entry is carried forward by spreading it. A field a `.patch`
|
|
217
|
+
body cannot express (`stagedDependencies`, `lintIgnore`, `tier`, and
|
|
218
|
+
whatever is added to `PatchMetadata` next) therefore survives without
|
|
219
|
+
having to be listed. The one case where nothing can be carried forward is
|
|
220
|
+
a `patches.json` that exists but does not parse. The rebuild refuses it,
|
|
221
|
+
naming what would be reinvented, unless `--allow-metadata-loss` is
|
|
222
|
+
passed. `doctor --repair-files-affected` is the narrow repair for drift
|
|
223
|
+
that is only in the derived list: it goes through
|
|
224
|
+
`mutatePatchRowsInManifest`, so untouched rows keep their exact JSON.
|
|
225
|
+
Both repairs take the patch-directory lock (invariant 2), both support
|
|
226
|
+
`--dry-run`, and a repair that wrote is reported before the doctor
|
|
227
|
+
summary in every branch. The run can still exit non-zero on an unrelated
|
|
228
|
+
check, and a non-zero exit otherwise reads as "nothing happened".
|
|
229
|
+
Enforced by `patch-manifest-repair.test.ts` (real temp queues) and the
|
|
230
|
+
repair cases in `doctor.test.ts`.
|
|
224
231
|
|
|
225
232
|
## Lock release (0.44.0)
|
|
226
233
|
|
|
227
|
-
`releaseLockIfOwned(lockPath)` in `src/core/file-lock.ts` is the
|
|
228
|
-
checked removal for callers that hold no acquisition token
|
|
229
|
-
signal-time furnace sweeper
|
|
230
|
-
It verifies the PID before
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
234
|
+
`releaseLockIfOwned(lockPath)` in `src/core/file-lock.ts` is the
|
|
235
|
+
ownership-checked removal for callers that hold no acquisition token.
|
|
236
|
+
Currently that is the signal-time furnace sweeper
|
|
237
|
+
(`forceReleaseFurnaceLocksForActiveOperations`). It verifies the PID before
|
|
238
|
+
removing, which is the half of `releaseLock`'s guard a sweeper can still
|
|
239
|
+
perform: the sweeper knows which lock paths its own operations opened, but
|
|
240
|
+
not the per-acquisition UUID they minted. The bare `rm` it replaced could
|
|
241
|
+
delete a lock that a different process had acquired in the window between our
|
|
242
|
+
operation dying and the sweep running.
|
|
235
243
|
|
|
236
244
|
`completeJournalRollback` in `src/core/furnace-operation.ts` owns the
|
|
237
245
|
mutation-body rollback sequence that eight furnace commands previously
|
|
238
246
|
duplicated verbatim. The ordering is the invariant: `markRolledBack()` runs
|
|
239
|
-
|
|
240
|
-
lifecycle wrapper replaying the same journal on the way out
|
|
247
|
+
before the restore, so a restore that itself fails does not leave the
|
|
248
|
+
lifecycle wrapper replaying the same journal on the way out, and the original
|
|
241
249
|
error is rethrown unless the rollback is what failed.
|
|
242
250
|
|
|
243
251
|
## Who owns what
|
|
244
252
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
253
|
+
### `bin/fireforge.ts`
|
|
254
|
+
|
|
255
|
+
The only `process.exit` call sites, the SIGINT/SIGTERM pipeline, the
|
|
256
|
+
re-entrancy guard (a second Ctrl+C exits immediately), and exit codes 130 and 143.
|
|
257
|
+
|
|
258
|
+
### `src/core/file-lock.ts`
|
|
259
|
+
|
|
260
|
+
The generic directory-as-lock, using `mkdir` for atomicity. The owner file
|
|
261
|
+
carries the PID, a per-acquisition token, the acquisition time and the start
|
|
262
|
+
tick. Stale recovery works by renaming the lock aside and reaping it: a dead
|
|
263
|
+
PID unblocks immediately, with an age-based fallback after 5 minutes for
|
|
264
|
+
records with no usable liveness answer. The probe re-runs about every 5
|
|
265
|
+
seconds while waiting, so a holder dying mid-wait cannot strand waiters until
|
|
266
|
+
`timeoutMs`. Acquisition then polls until the timeout.
|
|
267
|
+
|
|
268
|
+
The owner-record write is fatal. A failed or unverifiable write releases the
|
|
269
|
+
just-created lock and throws a `GeneralError` instead of running the
|
|
270
|
+
operation. Without that, a lock with no readable owner is reaped by the age
|
|
271
|
+
heuristic after 5 minutes, which for a multi-hour build meant a second holder
|
|
272
|
+
inside the critical section. Covered by `file-lock.test.ts`, "owner record
|
|
273
|
+
write at acquisition": write failure, corrupt read-back, recorded acquisition
|
|
274
|
+
time.
|
|
275
|
+
|
|
276
|
+
Release verifies that the lock still carries this acquisition's token before
|
|
277
|
+
removing, so a lock that a reaper has replaced is never deleted by its
|
|
278
|
+
previous owner. Enforced by `file-lock.test.ts` (two-waiter reap race,
|
|
279
|
+
mid-wait holder death, foreign-owner release). Releases happen in a `finally`,
|
|
280
|
+
which never runs across `process.exit`, and that is why invariant 5 needs its
|
|
281
|
+
other layer.
|
|
282
|
+
|
|
283
|
+
Wait ergonomics are opt-in: `pollMaxMs` (the poll interval doubles from
|
|
284
|
+
`pollMs` up to the cap) and `waitProgressMs` with `onWaitProgress` (a periodic
|
|
285
|
+
progress callback carrying the holder's PID, liveness, and owner-metadata
|
|
286
|
+
lines from the pid file). All three are inert when unset, so existing callers
|
|
287
|
+
keep byte-identical behavior.
|
|
288
|
+
|
|
289
|
+
### `src/core/file-lock-owner.ts`
|
|
290
|
+
|
|
291
|
+
The owner record (the `pid` file) itself: its format (PID, token,
|
|
292
|
+
`acquired-at-ms=` and `start-tick=` lines, plus free-form metadata, and older
|
|
293
|
+
records without the mechanical lines still parse), the write-and-verify
|
|
294
|
+
contract described above (a read-back that errors is retried before it counts
|
|
295
|
+
as a failed write, while a missing or mismatching record fails immediately),
|
|
296
|
+
and PID-reuse liveness.
|
|
297
|
+
|
|
298
|
+
`isLockOwnerAlive` is the liveness check that every stale probe, reap
|
|
299
|
+
re-verification, status snapshot and timeout message uses. Beyond
|
|
300
|
+
`isProcessAlive`, on Linux the record carries the writer's boot-relative start
|
|
301
|
+
tick (`/proc/self/stat` field 22), and the probe reads the candidate's tick
|
|
302
|
+
from `/proc/<pid>/stat`. A different tick means a different process, so the
|
|
303
|
+
lock is stale. A crashed build's PID handed to a long-lived daemon therefore
|
|
304
|
+
no longer keeps the dead lock honoured until the waiter's timeout. It uses
|
|
305
|
+
ticks rather than wall-clock times because `btime` moves with clock steps, and
|
|
306
|
+
a wall-clock comparison would reap a live holder after NTP steps the clock
|
|
307
|
+
forward. macOS and Windows have no cheap start-time source (a `ps` spawn per
|
|
308
|
+
poll is not worth it) and keep the PID-only answer, and so do records without
|
|
309
|
+
a start tick. Enforced by `file-lock-owner.test.ts`: backward-compatible
|
|
310
|
+
parsing, forged mechanical lines dropped, transient read-back error retried
|
|
311
|
+
versus persistent error versus mismatch, fake-procfs tick parsing, reused-PID
|
|
312
|
+
versus genuine-holder verdicts, stepped-clock immunity, and on Linux a
|
|
313
|
+
real-procfs `withFileLock` reap of a live-PID stale record.
|
|
314
|
+
|
|
315
|
+
### `src/core/engine-session-lock.ts`
|
|
316
|
+
|
|
317
|
+
The project-wide engine-session lock under `.fireforge/`, stamped with PID,
|
|
318
|
+
command, and start time metadata. CLI `build`, `test`, `export`, `re-export`,
|
|
319
|
+
`furnace apply`, `furnace deploy` and `furnace sync` take it before subsystem
|
|
320
|
+
locks, unconditionally, including dry runs: a `re-export --dry-run` over an
|
|
321
|
+
untracked binary legally touches the git index (temporary `--intent-to-add`
|
|
322
|
+
staging), so holding the lock serializes even "read-only" previews against
|
|
323
|
+
real writers. Dry-run purity is separately enforced at runtime by
|
|
324
|
+
`withDryRunPurityGuard` (`re-export-bulk-scan.ts`), which fingerprints the
|
|
325
|
+
engine tree and every patch artifact around the dry run and fails hard on
|
|
326
|
+
divergence.
|
|
327
|
+
|
|
328
|
+
Contention fails fast after about a second by default, with the fixed message
|
|
329
|
+
"Another FireForge engine-mutating command is already running. Wait for it to
|
|
330
|
+
finish, then retry `<command>`." That message contract is unchanged, and it
|
|
331
|
+
also closes the bounded wait. Each of those commands accepts
|
|
332
|
+
`--wait-lock [seconds]` (bare flag means 60, valid 1 to 3600) for a bounded
|
|
333
|
+
operator wait instead: polling backs off exponentially from 100 ms to 2 s, and
|
|
334
|
+
roughly every 5 s a progress line identifies the holder from the lock's
|
|
335
|
+
owner-metadata lines, in the form `Waiting for the FireForge engine lock held
|
|
336
|
+
by PID 12345 (command=build, started=…)` followed by how long the wait has
|
|
337
|
+
run and how large the budget is.
|
|
338
|
+
|
|
339
|
+
The budget is not the engine lock's alone. It covers every lock the command
|
|
340
|
+
takes, which for the three furnace commands means the per-root furnace lock as
|
|
341
|
+
well (see the `furnace-operation.ts` section). `FIREFORGE_WAIT_LOCK=<seconds>`
|
|
342
|
+
sets the same budget for a whole session, parsed with the same bounds as the
|
|
343
|
+
flag so the two cannot drift, and an explicit budget at a call site always
|
|
344
|
+
wins. Commands that take no lock accept `--wait-lock` and ignore it, so a
|
|
345
|
+
scripted sequence can append the flag everywhere without a usage error killing
|
|
346
|
+
the run. `fireforge test` also snapshots the engine git generation before and
|
|
347
|
+
after the harness run, and fails the verdict as invalid if the tree changes
|
|
348
|
+
mid-run.
|
|
349
|
+
|
|
350
|
+
### `src/core/furnace-operation.ts`
|
|
351
|
+
|
|
352
|
+
The `runFurnaceMutation` lifecycle wrapper: the per-root furnace lock, the
|
|
353
|
+
`pendingRepair` preflight refusal, the in-flight operation registry (journal,
|
|
354
|
+
cleanups and a `completed` flag), signal rollback with bounded timeouts,
|
|
355
|
+
`pendingRepair` persistence, and the furnace-lock force-release sweep.
|
|
356
|
+
|
|
357
|
+
The lock's wait budget is `options.lockTimeoutMs`, threaded from `--wait-lock`
|
|
358
|
+
by `waitLockMutationOptions`, which does the seconds-to-milliseconds
|
|
359
|
+
conversion once. It falls back to `sessionWaitLockMs()`
|
|
360
|
+
(`FIREFORGE_WAIT_LOCK`) and then to `DEFAULT_LOCK_TIMEOUT_MS`. That fallback
|
|
361
|
+
is what covers the twelve furnace mutators that declare no flag (`create`,
|
|
362
|
+
`remove`, `rename`, `refresh`, `scan`, `override`, `chrome-doc`, and the
|
|
363
|
+
rest). A contended refusal here names the furnace lock, says it is the second
|
|
364
|
+
of two, and points at `--wait-lock` and `FIREFORGE_WAIT_LOCK`. A budget that
|
|
365
|
+
is paid in full and then refuses anyway is the worst shape a budget can have,
|
|
366
|
+
so the refusal must not read as the engine lock's.
|
|
367
|
+
|
|
368
|
+
### `src/core/furnace-rollback.ts`
|
|
369
|
+
|
|
370
|
+
The rollback journal itself: file snapshots and their restoration.
|
|
371
|
+
|
|
372
|
+
### `src/utils/assert.ts`
|
|
373
|
+
|
|
374
|
+
The runtime-assertion primitive (`assert`, `expectDefined`) and, through
|
|
375
|
+
`InternalInvariantError`, exit code 11. It checks state FireForge itself
|
|
376
|
+
established (journal before mutation, lock held before write, manifest order
|
|
377
|
+
well-formedness) and is deliberately not input validation: anything derived
|
|
378
|
+
from user input, the filesystem, or a subprocess stays a typed error from
|
|
379
|
+
`src/errors/`. Every check routes through the helper rather than an inline
|
|
380
|
+
`if (!c) throw`, so the failure branch is covered once here instead of being
|
|
381
|
+
uncovered at every call site.
|
|
382
|
+
|
|
383
|
+
### `src/core/signal-critical.ts`
|
|
384
|
+
|
|
385
|
+
The registry of signal-deferred critical sections
|
|
386
|
+
(`runInSignalCriticalSection`). It is a pure registry and installs no
|
|
387
|
+
handlers. The bin pipeline drains it with a bounded wait.
|
|
388
|
+
|
|
389
|
+
### `src/core/patch-lock.ts`
|
|
390
|
+
|
|
391
|
+
`withPatchDirectoryLock`, which serializes patch filename allocation and
|
|
392
|
+
manifest read-modify-writes. It is not reentrant: callers must not already
|
|
393
|
+
hold it.
|
|
394
|
+
|
|
395
|
+
### `src/core/destructive.ts`
|
|
396
|
+
|
|
397
|
+
The destructive-operation contract: `confirmDestructive` (summary, prompt,
|
|
398
|
+
`--yes`, `--dry-run`, conflict refusal, `--force-unsafe`) and the
|
|
399
|
+
`.fireforge-history.jsonl` audit log.
|
|
400
|
+
|
|
401
|
+
### `src/core/tree-store.ts`
|
|
402
|
+
|
|
403
|
+
Verification-tree lifecycle: CoW snapshot contents and exclusions, the objdir
|
|
404
|
+
clone relocation (mozinfo rewrite plus a caller-supplied in-tree
|
|
405
|
+
`mach configure`, both before the marker records `clonedObjdir`, with
|
|
406
|
+
`assertObjdirMatchesTreeMarker` re-checking at `test` time), the
|
|
407
|
+
`.fireforge/tree.json` marker and its absent/valid/corrupt tri-state,
|
|
408
|
+
staleness fingerprints, the primary-side `trees.lock`, and removal that
|
|
409
|
+
refuses both a live holder and a lock of unknown ownership, with containment
|
|
410
|
+
checks on the removal target.
|
|
411
|
+
|
|
412
|
+
### `src/core/tree-guard.ts`
|
|
413
|
+
|
|
414
|
+
Read-only enforcement inside trees: the default-deny per-command verdict table
|
|
415
|
+
and the `preAction` hook body `runTreeGuardHook` that `createProgram()`
|
|
416
|
+
installs (invariant 7).
|
|
259
417
|
|
|
260
418
|
## The signal pipeline, end to end
|
|
261
419
|
|
|
262
|
-
On SIGINT
|
|
263
|
-
|
|
264
|
-
1.
|
|
265
|
-
2. Otherwise runs
|
|
266
|
-
- `rollbackActiveOperationsForSignal(signal)
|
|
267
|
-
operation
|
|
268
|
-
bound), `pendingRepair` marker if anything
|
|
269
|
-
registered yet. Operations whose body already
|
|
270
|
-
a signal in the finally-window
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
420
|
+
On SIGINT or SIGTERM, `bin/fireforge.ts`:
|
|
421
|
+
|
|
422
|
+
1. Exits immediately if a rollback is already in flight (a second Ctrl+C).
|
|
423
|
+
2. Otherwise runs both of these in parallel and waits for both:
|
|
424
|
+
- `rollbackActiveOperationsForSignal(signal)`, which for each active
|
|
425
|
+
furnace operation runs the cleanup callbacks (15 s bound each), restores
|
|
426
|
+
the journal (15 s bound), and writes a `pendingRepair` marker if anything
|
|
427
|
+
failed or no journal was registered yet. Operations whose body already
|
|
428
|
+
completed are skipped, so a signal in the finally-window cannot roll back
|
|
429
|
+
a committed mutation.
|
|
430
|
+
- `waitForActiveCriticalSections(5 s)`, which holds the exit for in-flight
|
|
431
|
+
"apply plus persist" pairs (invariant 4). It is bounded so that a stuck
|
|
432
|
+
write cannot postpone the exit the user asked for.
|
|
433
|
+
3. Force-releases the furnace lock directories of active operations, since
|
|
434
|
+
`withFileLock`'s `finally` will never run past `process.exit`.
|
|
435
|
+
4. Calls `process.exit(130 | 143)`.
|
|
277
436
|
|
|
278
437
|
## Choosing a primitive for a new mutation
|
|
279
438
|
|
|
280
|
-
| Your operation
|
|
281
|
-
| -------------------------------------------------------------------------------------------------------------------- |
|
|
282
|
-
| Mutates the engine and should be
|
|
283
|
-
| Intentionally leaves the engine mutated but must finish a paired bookkeeping write (rebase-style `--continue` state) | `runInSignalCriticalSection` around the apply
|
|
284
|
-
| Touches the patch directory
|
|
285
|
-
| Deletes or rewrites user-owned patch state | The `confirmDestructive` contract, history appended only on success
|
|
286
|
-
| Only reads | Nothing
|
|
287
|
-
| Runs read verification concurrently beside a busy primary | A `fireforge tree` (CoW snapshot
|
|
439
|
+
| Your operation | Use |
|
|
440
|
+
| -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
441
|
+
| Mutates the engine and should be undone on Ctrl+C (apply, deploy, preview, remove) | `runFurnaceMutation` plus `ctx.registerJournal` (and `ctx.registerCleanup` for extra teardown) |
|
|
442
|
+
| Intentionally leaves the engine mutated but must finish a paired bookkeeping write (rebase-style `--continue` state) | `runInSignalCriticalSection` around the apply plus persist pair, keeping the body short |
|
|
443
|
+
| Touches the patch directory or manifest | `withPatchDirectoryLock` (never nested) |
|
|
444
|
+
| Deletes or rewrites user-owned patch state | The `confirmDestructive` contract, with history appended only on success |
|
|
445
|
+
| Only reads | Nothing. Dry runs must not block or be blocked |
|
|
446
|
+
| Runs read verification concurrently beside a busy primary | A `fireforge tree` (a CoW snapshot, with mutation refused inside, invariant 7) |
|
|
288
447
|
|
|
289
448
|
## Where this is tested
|
|
290
449
|
|
|
291
|
-
- `src/core/__tests__/file-lock.test.ts
|
|
450
|
+
- `src/core/__tests__/file-lock.test.ts`: lock acquisition, staleness, PID
|
|
292
451
|
recovery, fatal owner-record write.
|
|
293
|
-
- `src/core/__tests__/file-lock-owner.test.ts
|
|
452
|
+
- `src/core/__tests__/file-lock-owner.test.ts`: owner-record format
|
|
294
453
|
compatibility and PID-reuse liveness.
|
|
295
|
-
- `src/core/__tests__/furnace-operation.test.ts
|
|
454
|
+
- `src/core/__tests__/furnace-operation.test.ts`: wrapper lifecycle, signal
|
|
296
455
|
rollback outcomes, `pendingRepair` semantics, lock force-release.
|
|
297
|
-
- `src/core/__tests__/signal-critical.test.ts
|
|
298
|
-
|
|
299
|
-
- `src/core/__tests__/signal-compound-mutation-scenario.test.ts
|
|
300
|
-
|
|
301
|
-
hold for a stuck section, rollback
|
|
302
|
-
|
|
303
|
-
- `src/core/__tests__/
|
|
456
|
+
- `src/core/__tests__/signal-critical.test.ts`: the critical-section registry
|
|
457
|
+
contract in isolation.
|
|
458
|
+
- `src/core/__tests__/signal-compound-mutation-scenario.test.ts`: the composed
|
|
459
|
+
bin-handler pipeline, covering exit held for a compound mutation, the
|
|
460
|
+
bounded hold for a stuck section, and rollback plus drain plus lock release
|
|
461
|
+
together.
|
|
462
|
+
- `src/core/__tests__/destructive.test.ts`: the destructive-operation
|
|
463
|
+
contract.
|
|
464
|
+
- `src/core/__tests__/tree-guard.test.ts`: invariant 7's verdict table
|
|
304
465
|
(default-deny drift gate, conditional predicates, corrupt-marker refusal).
|
|
305
|
-
- `src/core/__tests__/tree-store.integration.test.ts
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
- `src/commands/__tests__/tree.test.ts
|
|
466
|
+
- `src/core/__tests__/tree-store.integration.test.ts`: clone contents, objdir
|
|
467
|
+
rewrite/reconfigure/marker ordering, marker tri-state, unknown-owner removal
|
|
468
|
+
refusal.
|
|
469
|
+
- `src/commands/__tests__/tree.test.ts`: create and remove command flow,
|
|
309
470
|
including objdir re-validation under the primary build lock, and the
|
|
310
471
|
`tree exec` stdout seal (invariant 8).
|
|
311
|
-
- `src/__tests__/tree-exec-verdict.test.ts
|
|
312
|
-
|
|
472
|
+
- `src/__tests__/tree-exec-verdict.test.ts`: invariant 8 across a real process
|
|
473
|
+
boundary, with exactly one verdict line, last on stdout, and the refusal on
|
|
313
474
|
stderr.
|
|
314
|
-
- `src/commands/__tests__/tree.integration.test.ts
|
|
315
|
-
refusals and `--with-objdir` end
|
|
475
|
+
- `src/commands/__tests__/tree.integration.test.ts`: real-program in-tree
|
|
476
|
+
refusals and `--with-objdir` end to end against a synthetic objdir.
|