@hominis/fireforge 0.52.0 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/build-info.json +4 -4
  3. package/dist/src/commands/bootstrap.js +2 -3
  4. package/dist/src/commands/build-prelock.d.ts +3 -3
  5. package/dist/src/commands/build-prelock.js +3 -3
  6. package/dist/src/commands/build.js +4 -0
  7. package/dist/src/commands/declarations-report.d.ts +9 -3
  8. package/dist/src/commands/declarations-report.js +18 -11
  9. package/dist/src/commands/discard.js +3 -3
  10. package/dist/src/commands/doctor-furnace-config-sync.js +4 -4
  11. package/dist/src/commands/doctor-furnace.js +5 -5
  12. package/dist/src/commands/export-adopt-check.d.ts +5 -6
  13. package/dist/src/commands/export-adopt-check.js +5 -6
  14. package/dist/src/commands/export-flow.js +6 -9
  15. package/dist/src/commands/export-staged-registration.d.ts +4 -4
  16. package/dist/src/commands/export-staged-registration.js +4 -4
  17. package/dist/src/commands/forward-import-remedy.d.ts +2 -3
  18. package/dist/src/commands/forward-import-remedy.js +2 -3
  19. package/dist/src/commands/furnace/create-dry-run.d.ts +2 -2
  20. package/dist/src/commands/furnace/deploy-fragment-includers.d.ts +2 -2
  21. package/dist/src/commands/furnace/deploy-fragment-includers.js +2 -2
  22. package/dist/src/commands/furnace/index.js +2 -2
  23. package/dist/src/commands/lint.js +4 -4
  24. package/dist/src/commands/patch/compact.js +1 -1
  25. package/dist/src/commands/patch/lint-ignore-from.d.ts +3 -4
  26. package/dist/src/commands/patch/lint-ignore-from.js +3 -4
  27. package/dist/src/commands/patch/move-files-adopt.d.ts +33 -0
  28. package/dist/src/commands/patch/move-files-adopt.js +65 -0
  29. package/dist/src/commands/patch/move-files-create.js +41 -20
  30. package/dist/src/commands/patch/move-files-into.d.ts +18 -0
  31. package/dist/src/commands/patch/move-files-into.js +125 -60
  32. package/dist/src/commands/patch/move-files-retire.d.ts +39 -0
  33. package/dist/src/commands/patch/move-files-retire.js +91 -0
  34. package/dist/src/commands/patch/move-files.d.ts +3 -0
  35. package/dist/src/commands/patch/move-files.js +42 -12
  36. package/dist/src/commands/patch/move-size-tier.d.ts +27 -0
  37. package/dist/src/commands/patch/move-size-tier.js +66 -0
  38. package/dist/src/commands/patch/rename.js +1 -1
  39. package/dist/src/commands/patch/reorder-projection.d.ts +17 -0
  40. package/dist/src/commands/patch/reorder-projection.js +49 -0
  41. package/dist/src/commands/patch/reorder-shift.d.ts +30 -0
  42. package/dist/src/commands/patch/reorder-shift.js +307 -0
  43. package/dist/src/commands/patch/reorder.d.ts +9 -15
  44. package/dist/src/commands/patch/reorder.js +52 -50
  45. package/dist/src/commands/patch/split-plan.d.ts +31 -3
  46. package/dist/src/commands/patch/split-plan.js +36 -12
  47. package/dist/src/commands/patch/split-staged.d.ts +2 -2
  48. package/dist/src/commands/patch/split-staged.js +2 -2
  49. package/dist/src/commands/patch/split.js +18 -5
  50. package/dist/src/commands/patch/staged-dependency-entries.d.ts +9 -1
  51. package/dist/src/commands/patch/staged-dependency-entries.js +32 -4
  52. package/dist/src/commands/patch/staged-dependency-from.js +15 -9
  53. package/dist/src/commands/patch/staged-dependency-remove.d.ts +26 -4
  54. package/dist/src/commands/patch/staged-dependency-remove.js +60 -23
  55. package/dist/src/commands/patch/staged-dependency.js +35 -24
  56. package/dist/src/commands/patch/tier.js +2 -2
  57. package/dist/src/commands/patch-rename-map.d.ts +14 -3
  58. package/dist/src/commands/patch-rename-map.js +21 -3
  59. package/dist/src/commands/re-export-adjacent.d.ts +4 -5
  60. package/dist/src/commands/re-export-lint.d.ts +4 -3
  61. package/dist/src/commands/re-export-lint.js +4 -3
  62. package/dist/src/commands/re-export-options.js +8 -2
  63. package/dist/src/commands/re-export-preflight.js +1 -2
  64. package/dist/src/commands/re-export-purity-scope.d.ts +1 -1
  65. package/dist/src/commands/re-export-purity-scope.js +13 -7
  66. package/dist/src/commands/re-export-retired-declarations.d.ts +22 -0
  67. package/dist/src/commands/re-export-retired-declarations.js +113 -0
  68. package/dist/src/commands/re-export-single.js +2 -2
  69. package/dist/src/commands/re-export-stale-registrations.d.ts +27 -6
  70. package/dist/src/commands/re-export-stale-registrations.js +65 -21
  71. package/dist/src/commands/re-export.js +4 -0
  72. package/dist/src/commands/rebase/continue.js +3 -3
  73. package/dist/src/commands/register.js +5 -4
  74. package/dist/src/commands/run.js +11 -4
  75. package/dist/src/commands/status.js +1 -1
  76. package/dist/src/commands/test-build-furnace.d.ts +1 -2
  77. package/dist/src/commands/test-build-furnace.js +1 -2
  78. package/dist/src/commands/test-build-phase.d.ts +8 -0
  79. package/dist/src/commands/test-build-phase.js +42 -7
  80. package/dist/src/commands/test-control-restore.d.ts +40 -0
  81. package/dist/src/commands/test-control-restore.js +86 -0
  82. package/dist/src/commands/test-dispatch.js +68 -7
  83. package/dist/src/commands/test-multi-verdict.js +1 -0
  84. package/dist/src/commands/test-orchestrate.d.ts +6 -0
  85. package/dist/src/commands/test-orchestrate.js +32 -1
  86. package/dist/src/commands/test-perf-scope.d.ts +1 -1
  87. package/dist/src/commands/test-perf-scope.js +1 -1
  88. package/dist/src/commands/test-prelock.d.ts +18 -8
  89. package/dist/src/commands/test-prelock.js +42 -14
  90. package/dist/src/commands/test-register.js +4 -3
  91. package/dist/src/commands/test-results.d.ts +10 -1
  92. package/dist/src/commands/test-results.js +20 -3
  93. package/dist/src/commands/test-run.d.ts +6 -0
  94. package/dist/src/commands/test-run.js +23 -4
  95. package/dist/src/commands/test-shards.d.ts +7 -3
  96. package/dist/src/commands/test-shards.js +24 -7
  97. package/dist/src/commands/test-stale-gate.d.ts +6 -1
  98. package/dist/src/commands/test-stale-gate.js +40 -2
  99. package/dist/src/commands/test-verdict.d.ts +5 -3
  100. package/dist/src/commands/test.js +14 -2
  101. package/dist/src/commands/token.js +2 -2
  102. package/dist/src/commands/verify.js +1 -1
  103. package/dist/src/commands/watch.js +1 -1
  104. package/dist/src/commands/wire.js +1 -1
  105. package/dist/src/core/build-audit-resolve.d.ts +4 -5
  106. package/dist/src/core/build-audit-resolve.js +4 -5
  107. package/dist/src/core/build-audit-skip.d.ts +2 -2
  108. package/dist/src/core/build-audit.d.ts +3 -4
  109. package/dist/src/core/build-baseline-types.d.ts +24 -3
  110. package/dist/src/core/build-baseline-validate.d.ts +30 -1
  111. package/dist/src/core/build-baseline-validate.js +127 -35
  112. package/dist/src/core/build-baseline.d.ts +8 -2
  113. package/dist/src/core/build-baseline.js +25 -16
  114. package/dist/src/core/build-jar-escalation.d.ts +4 -4
  115. package/dist/src/core/build-jar-escalation.js +4 -4
  116. package/dist/src/core/build-prepare.js +4 -4
  117. package/dist/src/core/config-validate.js +4 -4
  118. package/dist/src/core/coverage-extend.d.ts +5 -3
  119. package/dist/src/core/coverage-extend.js +8 -3
  120. package/dist/src/core/coverage-records.d.ts +4 -5
  121. package/dist/src/core/coverage-records.js +4 -5
  122. package/dist/src/core/destructive.d.ts +2 -2
  123. package/dist/src/core/destructive.js +2 -2
  124. package/dist/src/core/discard-baseline.d.ts +2 -2
  125. package/dist/src/core/discard-baseline.js +3 -3
  126. package/dist/src/core/engine-session-lock.d.ts +1 -1
  127. package/dist/src/core/engine-session-lock.js +1 -1
  128. package/dist/src/core/file-lock-owner.d.ts +5 -5
  129. package/dist/src/core/file-lock-owner.js +5 -5
  130. package/dist/src/core/file-lock-queue.js +6 -8
  131. package/dist/src/core/file-lock-shared.d.ts +4 -4
  132. package/dist/src/core/file-lock-shared.js +4 -4
  133. package/dist/src/core/file-lock.js +7 -10
  134. package/dist/src/core/firefox.js +2 -2
  135. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +1 -1
  136. package/dist/src/core/furnace-apply-overwrite-warn.js +1 -1
  137. package/dist/src/core/furnace-apply.js +3 -3
  138. package/dist/src/core/furnace-operation.js +5 -5
  139. package/dist/src/core/furnace-registration-jar-inplace.js +5 -6
  140. package/dist/src/core/furnace-registration-rename.d.ts +5 -5
  141. package/dist/src/core/furnace-registration-rename.js +5 -5
  142. package/dist/src/core/furnace-registration-validate.d.ts +2 -1
  143. package/dist/src/core/furnace-registration-validate.js +2 -1
  144. package/dist/src/core/furnace-registration.js +2 -2
  145. package/dist/src/core/git-base.js +4 -3
  146. package/dist/src/core/git-diff.js +1 -1
  147. package/dist/src/core/git.js +1 -1
  148. package/dist/src/core/harness-orphans.d.ts +6 -6
  149. package/dist/src/core/harness-orphans.js +6 -6
  150. package/dist/src/core/harness-port-reservation.d.ts +60 -0
  151. package/dist/src/core/harness-port-reservation.js +122 -0
  152. package/dist/src/core/history-log-file.d.ts +9 -9
  153. package/dist/src/core/history-log-file.js +15 -15
  154. package/dist/src/core/history-log.d.ts +3 -3
  155. package/dist/src/core/history-log.js +3 -3
  156. package/dist/src/core/launchable-build.d.ts +39 -0
  157. package/dist/src/core/launchable-build.js +85 -0
  158. package/dist/src/core/marionette-port.js +1 -3
  159. package/dist/src/core/moz-manifest-rules.js +3 -2
  160. package/dist/src/core/negative-control-compiled.d.ts +13 -3
  161. package/dist/src/core/negative-control-compiled.js +27 -5
  162. package/dist/src/core/negative-control-diff.js +31 -6
  163. package/dist/src/core/parser-fallback.d.ts +5 -5
  164. package/dist/src/core/parser-fallback.js +5 -5
  165. package/dist/src/core/patch-identifier-suggest.d.ts +6 -0
  166. package/dist/src/core/patch-identifier-suggest.js +11 -4
  167. package/dist/src/core/patch-lint-checkjs.d.ts +1 -1
  168. package/dist/src/core/patch-lint-checkjs.js +1 -1
  169. package/dist/src/core/patch-lint-css.js +2 -2
  170. package/dist/src/core/patch-lint-file-size.d.ts +5 -7
  171. package/dist/src/core/patch-lint-file-size.js +5 -7
  172. package/dist/src/core/patch-lint-ignore.d.ts +3 -3
  173. package/dist/src/core/patch-lint-ignore.js +3 -3
  174. package/dist/src/core/patch-lint-prettier.d.ts +4 -6
  175. package/dist/src/core/patch-lint-prettier.js +4 -6
  176. package/dist/src/core/patch-lint-projection.d.ts +1 -1
  177. package/dist/src/core/patch-lint-projection.js +1 -1
  178. package/dist/src/core/patch-lint-queue-types.d.ts +2 -2
  179. package/dist/src/core/patch-lint-queue-types.js +2 -2
  180. package/dist/src/core/patch-manifest-consistency.d.ts +2 -2
  181. package/dist/src/core/patch-manifest-consistency.js +2 -2
  182. package/dist/src/core/patch-manifest-io.d.ts +5 -1
  183. package/dist/src/core/patch-manifest-io.js +12 -10
  184. package/dist/src/core/patch-manifest-key-order.d.ts +3 -4
  185. package/dist/src/core/patch-manifest-key-order.js +3 -4
  186. package/dist/src/core/patch-parse.d.ts +3 -3
  187. package/dist/src/core/patch-parse.js +3 -3
  188. package/dist/src/core/patch-policy.d.ts +1 -1
  189. package/dist/src/core/patch-policy.js +1 -1
  190. package/dist/src/core/patch-source-metadata.d.ts +1 -1
  191. package/dist/src/core/patch-source-metadata.js +1 -1
  192. package/dist/src/core/patch-subset.d.ts +5 -3
  193. package/dist/src/core/patch-subset.js +8 -7
  194. package/dist/src/core/patch-transform.d.ts +2 -2
  195. package/dist/src/core/patch-transform.js +2 -2
  196. package/dist/src/core/rebase-session.js +4 -6
  197. package/dist/src/core/register-xpcshell-test.d.ts +28 -7
  198. package/dist/src/core/register-xpcshell-test.js +76 -27
  199. package/dist/src/core/run-log.d.ts +8 -8
  200. package/dist/src/core/run-log.js +8 -8
  201. package/dist/src/core/status-classify.d.ts +1 -2
  202. package/dist/src/core/status-classify.js +1 -2
  203. package/dist/src/core/test-harness-crash.d.ts +5 -3
  204. package/dist/src/core/test-harness-crash.js +13 -14
  205. package/dist/src/core/test-harness-infra.d.ts +32 -0
  206. package/dist/src/core/test-harness-infra.js +69 -0
  207. package/dist/src/core/test-harness-signature.d.ts +6 -0
  208. package/dist/src/core/test-stale-check.d.ts +38 -0
  209. package/dist/src/core/test-stale-check.js +82 -1
  210. package/dist/src/core/test-stall-triage.js +5 -7
  211. package/dist/src/core/token-category.js +2 -2
  212. package/dist/src/core/token-inventory.js +3 -3
  213. package/dist/src/core/token-manager.d.ts +2 -2
  214. package/dist/src/core/token-manager.js +5 -7
  215. package/dist/src/core/token-variant.d.ts +6 -6
  216. package/dist/src/core/token-variant.js +7 -7
  217. package/dist/src/core/typecheck-shim.js +1 -1
  218. package/dist/src/core/wire-destroy.d.ts +1 -1
  219. package/dist/src/core/wire-destroy.js +1 -1
  220. package/dist/src/core/wire-dom-fragment.js +1 -1
  221. package/dist/src/core/wire-init.d.ts +1 -1
  222. package/dist/src/core/wire-init.js +1 -1
  223. package/dist/src/core/wire-subscript.d.ts +1 -1
  224. package/dist/src/core/wire-subscript.js +1 -1
  225. package/dist/src/core/wire-utils.d.ts +2 -2
  226. package/dist/src/core/wire-utils.js +3 -3
  227. package/dist/src/errors/base.d.ts +1 -1
  228. package/dist/src/errors/base.js +1 -1
  229. package/dist/src/errors/rebase.d.ts +1 -1
  230. package/dist/src/errors/rebase.js +1 -1
  231. package/dist/src/types/commands/options.d.ts +38 -3
  232. package/dist/src/types/config.d.ts +5 -7
  233. package/dist/src/utils/options.js +2 -2
  234. package/dist/src/utils/process.js +2 -2
  235. package/dist/src/utils/regex.d.ts +2 -2
  236. package/dist/src/utils/regex.js +3 -3
  237. package/dist/src/utils/sleep.d.ts +5 -6
  238. package/dist/src/utils/sleep.js +5 -6
  239. package/dist/src/utils/validation.js +2 -2
  240. package/docs/configuration.md +28 -23
  241. package/docs/exit-codes.md +8 -11
  242. package/docs/furnace.md +3 -3
  243. package/docs/lifecycle-invariants.md +36 -45
  244. package/docs/machine-output.md +41 -24
  245. package/docs/patch-workflow.md +140 -29
  246. package/docs/run-logs.md +8 -9
  247. package/docs/testing.md +114 -48
  248. package/package.json +1 -1
@@ -0,0 +1,65 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * `patch move-files --scan-file <path>`: adopt new engine files into the
4
+ * move's destination in the same transaction.
5
+ *
6
+ * Splitting a widget across patches moved existing files and adopted the
7
+ * seam modules that appeared beside them. Adopting first (`re-export
8
+ * --scan --scan-file`) grew the source past its own `large-patch-lines`
9
+ * ceiling, which refused although the move that followed would have
10
+ * shrunk it again. Adopting straight into the destination of the move
11
+ * means no patch ever carries the intermediate body: both sides are linted
12
+ * on what the transaction leaves behind.
13
+ */
14
+ import { join } from 'node:path';
15
+ import { InvalidArgumentError } from '../../errors/base.js';
16
+ import { pathExists } from '../../utils/fs.js';
17
+ import { normalizeEngineRelativeInput } from '../re-export-scan.js';
18
+ /**
19
+ * Validates `--scan-file` paths: each must be engine-relative, exist in
20
+ * `engine/`, be owned by no patch and not be one of the moved files.
21
+ * Returns the sorted, de-duplicated list.
22
+ *
23
+ * @param rawFiles - The `--scan-file` values
24
+ * @param patches - Manifest rows
25
+ * @param enginePath - Engine directory
26
+ * @param movedFiles - The `--file` paths of the same move
27
+ */
28
+ export async function resolveAdoptedFiles(rawFiles, patches, enginePath, movedFiles) {
29
+ const files = [
30
+ ...new Set((rawFiles ?? []).map((raw) => normalizeEngineRelativeInput(raw, '--scan-file'))),
31
+ ].sort();
32
+ const moved = new Set(movedFiles);
33
+ for (const file of files) {
34
+ if (moved.has(file)) {
35
+ throw new InvalidArgumentError(`${file} is passed with both --file and --scan-file. --file moves a file the source owns; ` +
36
+ '--scan-file adopts a file no patch owns yet.', '--scan-file');
37
+ }
38
+ const owner = patches.find((patch) => patch.filesAffected.includes(file));
39
+ if (owner !== undefined) {
40
+ throw new InvalidArgumentError(`--scan-file path is already owned by ${owner.filename}: ${file}. ` +
41
+ '--scan-file adopts files no patch owns; move an owned file with --file from its owner.', '--scan-file');
42
+ }
43
+ if (!(await pathExists(join(enginePath, file)))) {
44
+ throw new InvalidArgumentError(`--scan-file path not found in engine/: ${file}. --scan-file adopts a file the worktree ` +
45
+ 'already carries, so it needs content to diff.', '--scan-file');
46
+ }
47
+ }
48
+ return files;
49
+ }
50
+ /**
51
+ * Re-checks under the patch-directory lock that no patch claimed an
52
+ * adopted file while the move waited for confirmation.
53
+ *
54
+ * @param patches - Manifest rows read under the lock
55
+ * @param adoptedFiles - Files the move adopts
56
+ * @param command - Command name for the error
57
+ */
58
+ export function assertAdoptedStillUnowned(patches, adoptedFiles, command) {
59
+ for (const file of adoptedFiles ?? []) {
60
+ const owner = patches.find((patch) => patch.filesAffected.includes(file));
61
+ if (owner !== undefined) {
62
+ throw new InvalidArgumentError(`Patch queue changed while waiting for confirmation: ${owner.filename} now owns ${file}. Re-run the command.`, command);
63
+ }
64
+ }
65
+ }
@@ -24,10 +24,35 @@ import { normalizePatchDisplayName } from '../../utils/validation.js';
24
24
  import { withDeclarationsReport } from '../declarations-report.js';
25
25
  import { proceedAfterDecision } from '../destructive-decision.js';
26
26
  import { resolvePlacementPlan } from '../export-flow.js';
27
- import { runPatchLint } from '../export-shared.js';
27
+ import { resolveAdoptedFiles } from './move-files-adopt.js';
28
+ import { runMoveSideLint } from './move-size-tier.js';
28
29
  import { commitPatchSplit, reportSplitStagedChanges } from './split.js';
29
- import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, projectSplitManifest, runProjectedSplitLint, splitDeclarationRenamer, } from './split-plan.js';
30
+ import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, projectSplitManifest, runProjectedSplitLint, splitDeclarationReason, splitDeclarationRenamer, } from './split-plan.js';
30
31
  import { emptyStagedMaintenance, findOwnerRewriteHolders } from './split-staged.js';
32
+ /**
33
+ * Validates `--file` and `--scan-file` for a `--create` move: the source
34
+ * owns every moved file and keeps at least one, and every adopted file is
35
+ * new and unowned.
36
+ */
37
+ async function resolveCreateMoveFiles(source, patches, enginePath, options) {
38
+ const movedFiles = [...new Set((options.file ?? []).map((f) => f.trim()).filter(Boolean))].sort();
39
+ if (movedFiles.length === 0) {
40
+ throw new InvalidArgumentError('Specify at least one --file path to move.', '--file');
41
+ }
42
+ assertSourceOwnsFiles(source, movedFiles);
43
+ const movedSet = new Set(movedFiles);
44
+ const remainingFiles = source.filesAffected.filter((f) => !movedSet.has(f));
45
+ if (remainingFiles.length === 0) {
46
+ throw new InvalidArgumentError(`Moving every file out of ${source.filename} would leave it empty. ` +
47
+ 'Use "fireforge patch rename" / "fireforge patch reorder" to repurpose or move the whole patch instead, ' +
48
+ 'or fold it into an existing patch with "patch move-files <from> <to> --retire-empty".', '--file');
49
+ }
50
+ // Adopted files join the new patch with the moved ones: one body, linted
51
+ // as the transaction leaves it.
52
+ const adoptedFiles = await resolveAdoptedFiles(options.scanFile, patches, enginePath, movedFiles);
53
+ const newPatchFiles = [...new Set([...movedFiles, ...adoptedFiles])].sort();
54
+ return { movedFiles, remainingFiles, adoptedFiles, newPatchFiles };
55
+ }
31
56
  /**
32
57
  * Runs `patch move-files --create --order <n>`: plans the create+move as a
33
58
  * split, lints the projection, confirms, and commits transactionally.
@@ -53,18 +78,8 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
53
78
  throw new InvalidArgumentError(`--create target "${newPatchName}" already exists as ${existingTarget.filename}. ` +
54
79
  'Omit --create to preview a move into the existing patch, or pick a new patch name.', '--create');
55
80
  }
56
- const movedFiles = [...new Set((options.file ?? []).map((f) => f.trim()).filter(Boolean))].sort();
57
- if (movedFiles.length === 0) {
58
- throw new InvalidArgumentError('Specify at least one --file path to move.', '--file');
59
- }
60
- assertSourceOwnsFiles(source, movedFiles);
61
- const movedSet = new Set(movedFiles);
62
- const remainingFiles = source.filesAffected.filter((f) => !movedSet.has(f));
63
- if (remainingFiles.length === 0) {
64
- throw new InvalidArgumentError(`Moving every file out of ${source.filename} would leave it empty. ` +
65
- 'Use "fireforge patch rename" / "fireforge patch reorder" to repurpose or move the whole patch instead.', '--file');
66
- }
67
- const movedDiff = await buildSplitDiff(paths.engine, movedFiles, 'moved', source.filename);
81
+ const { movedFiles, remainingFiles, adoptedFiles, newPatchFiles } = await resolveCreateMoveFiles(source, manifest.patches, paths.engine, options);
82
+ const movedDiff = await buildSplitDiff(paths.engine, newPatchFiles, 'moved', source.filename);
68
83
  const remainingDiff = await buildSplitDiff(paths.engine, remainingFiles, 'remaining', source.filename);
69
84
  const category = options.category ?? source.category;
70
85
  const placementOptions = { order: options.order };
@@ -78,7 +93,7 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
78
93
  }
79
94
  const plan = {
80
95
  source,
81
- movedFiles,
96
+ movedFiles: newPatchFiles,
82
97
  remainingFiles,
83
98
  movedDiff,
84
99
  remainingDiff,
@@ -87,11 +102,13 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
87
102
  category,
88
103
  name: displayName,
89
104
  description: options.description ?? '',
90
- ownerRewrites: findOwnerRewriteHolders(manifest.patches, source.filename, movedSet),
105
+ ownerRewrites: findOwnerRewriteHolders(manifest.patches, source.filename, new Set(newPatchFiles)),
91
106
  // Populated by runProjectedSplitLint below (forward edges into the new
92
107
  // patch, and the declarations the move re-homes or makes unused).
93
108
  stagedDependencyAdditions: new Map(),
94
109
  stagedMaintenance: emptyStagedMaintenance(),
110
+ operation: 'patch move-files',
111
+ ...(adoptedFiles.length > 0 ? { adoptedFiles } : {}),
95
112
  };
96
113
  // Policy first: the projected manifest (orders, ranges, the gap rule)
97
114
  // does not depend on the lint below, so a refused --order fails before
@@ -117,9 +134,11 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
117
134
  // projection lint is blind.
118
135
  const patchQueueCtx = await buildPatchQueueContext(paths.patches, config);
119
136
  const ignoreChecks = buildLintIgnoreChecks(source.lintIgnore);
120
- await runPatchLint({
137
+ await runMoveSideLint({
121
138
  engineDir: paths.engine,
122
139
  filesAffected: remainingFiles,
140
+ filesBefore: source.filesAffected,
141
+ role: 'source',
123
142
  diffContent: remainingDiff,
124
143
  config,
125
144
  skipLint: options.skipLint,
@@ -127,9 +146,10 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
127
146
  ignoreChecks,
128
147
  patchTier: source.tier,
129
148
  });
130
- await runPatchLint({
149
+ await runMoveSideLint({
131
150
  engineDir: paths.engine,
132
- filesAffected: movedFiles,
151
+ filesAffected: newPatchFiles,
152
+ role: 'new patch',
133
153
  diffContent: movedDiff,
134
154
  config,
135
155
  skipLint: options.skipLint,
@@ -145,6 +165,7 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
145
165
  regressions: projected.regressions,
146
166
  out: options.declarationsOut,
147
167
  rename: splitDeclarationRenamer(plan),
168
+ reason: splitDeclarationReason(plan),
148
169
  });
149
170
  const newMetadata = buildNewPatchMetadata(plan, config);
150
171
  const decision = await confirmDestructive({
@@ -160,7 +181,7 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
160
181
  return;
161
182
  await commitPatchSplit(paths.patches, plan, newMetadata, options, config);
162
183
  success(`Created ${placement.newFilename} (order ${formatPatchOrder(placement.insertionOrder)}) ` +
163
- `and moved ${plan.movedFiles.length} file(s) out of ${source.filename}`);
184
+ `and moved ${movedFiles.length} file(s) out of ${source.filename}`);
164
185
  reportSplitStagedChanges(plan);
165
186
  outro('Move complete');
166
187
  }
@@ -12,6 +12,11 @@
12
12
  * Preconditions match `re-export`: the engine worktree must currently
13
13
  * reflect both patches' content, because both bodies are regenerated from
14
14
  * the worktree.
15
+ *
16
+ * The same transaction can also adopt new, unowned engine files
17
+ * into the target (`--scan-file`, see move-files-adopt.ts) and fold the
18
+ * whole source into the target, retiring its slot (`--retire-empty`, see
19
+ * move-files-retire.ts).
15
20
  */
16
21
  import type { PatchMetadata, PatchMoveFilesOptions, PatchStagedForwardImport } from '../../types/commands/index.js';
17
22
  import type { FireForgeConfig } from '../../types/config.js';
@@ -28,6 +33,14 @@ export interface MoveIntoPlan {
28
33
  ownerRewrites: string[];
29
34
  /** Forward-import declarations the move introduces, keyed by importing patch. */
30
35
  stagedDependencyAdditions: Map<string, PatchStagedForwardImport[]>;
36
+ /** New engine files no patch owned that the target adopts (`--scan-file`). */
37
+ adoptedFiles: string[];
38
+ /**
39
+ * `--retire-empty`: the move empties the source, so every declaration
40
+ * owned by the source re-points to the target and the source's slot is
41
+ * retired. `sourceDiff` is empty then.
42
+ */
43
+ retireSource: boolean;
31
44
  }
32
45
  /**
33
46
  * Plans, lints, confirms, and commits a file-ownership move into an
@@ -41,7 +54,12 @@ export declare function runMoveFilesInto(args: {
41
54
  target: PatchMetadata;
42
55
  files: string[];
43
56
  sourceAfter: string[];
57
+ /** Includes `adoptedFiles`. */
44
58
  targetAfter: string[];
59
+ /** New, unowned engine files the target adopts (`--scan-file`). */
60
+ adoptedFiles?: string[];
61
+ /** The move empties the source and retires its slot (`--retire-empty`). */
62
+ retireSource?: boolean;
45
63
  options: PatchMoveFilesOptions;
46
64
  config: FireForgeConfig;
47
65
  }): Promise<void>;
@@ -13,6 +13,11 @@
13
13
  * Preconditions match `re-export`: the engine worktree must currently
14
14
  * reflect both patches' content, because both bodies are regenerated from
15
15
  * the worktree.
16
+ *
17
+ * The same transaction can also adopt new, unowned engine files
18
+ * into the target (`--scan-file`, see move-files-adopt.ts) and fold the
19
+ * whole source into the target, retiring its slot (`--retire-empty`, see
20
+ * move-files-retire.ts).
16
21
  */
17
22
  import { dirname, join, resolve } from 'node:path';
18
23
  import { assertConfirmationAvailable, confirmDestructive, } from '../../core/destructive.js';
@@ -22,17 +27,45 @@ import { buildLintIgnoreChecks } from '../../core/patch-lint-ignore.js';
22
27
  import { computeProjectedLintRegressions } from '../../core/patch-lint-projection.js';
23
28
  import { withPatchDirectoryLock } from '../../core/patch-lock.js';
24
29
  import { loadPatchesManifest, savePatchesManifest, validatePatchesManifest, } from '../../core/patch-manifest.js';
30
+ import { removePatchFileAndManifest } from '../../core/patch-manifest.js';
25
31
  import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
26
32
  import { toError } from '../../utils/errors.js';
27
- import { readText, writeText } from '../../utils/fs.js';
33
+ import { pathExists, readText, writeText } from '../../utils/fs.js';
28
34
  import { info, outro, success, warn } from '../../utils/logger.js';
29
35
  import { resolveWaitLockSeconds } from '../../utils/options.js';
30
36
  import { withDeclarationsReport } from '../declarations-report.js';
31
37
  import { proceedAfterDecision } from '../destructive-decision.js';
32
- import { runPatchLint } from '../export-shared.js';
33
38
  import { projectEntryBody } from './entry-projection.js';
34
- import { buildSplitDiff, mergeStagedForwardImports } from './split-plan.js';
39
+ import { assertAdoptedStillUnowned } from './move-files-adopt.js';
40
+ import { foldSourceRows, retiredSlotForFold } from './move-files-retire.js';
41
+ import { runMoveSideLint } from './move-size-tier.js';
42
+ import { buildSplitDiff, mergeStagedForwardImports, moveDeclarationReason } from './split-plan.js';
35
43
  import { findOwnerRewriteHolders, rewriteSplitOwners } from './split-staged.js';
44
+ /**
45
+ * Applies the manifest side of a move to its rows: owner rewrites (every
46
+ * source-owned declaration on a fold, the moved files' otherwise), the
47
+ * auto-declared forward imports and both `filesAffected` lists. A folded
48
+ * source row is left as is for the retire step to remove.
49
+ */
50
+ function applyMoveToRows(rows, plan) {
51
+ const movedSet = new Set([...plan.movedFiles, ...plan.adoptedFiles]);
52
+ const rewritten = plan.retireSource
53
+ ? foldSourceRows(rows, plan.source.filename, plan.target.filename).patches
54
+ : rows.map((row) => rewriteSplitOwners(row, plan.source.filename, movedSet, plan.target.filename));
55
+ return rewritten.map((row) => {
56
+ let updated = row;
57
+ const decls = plan.stagedDependencyAdditions.get(updated.filename);
58
+ if (decls?.length)
59
+ updated = mergeStagedForwardImports(updated, decls);
60
+ if (updated.filename === plan.source.filename && !plan.retireSource) {
61
+ return { ...updated, filesAffected: plan.sourceAfter };
62
+ }
63
+ if (updated.filename === plan.target.filename) {
64
+ return { ...updated, filesAffected: plan.targetAfter };
65
+ }
66
+ return updated;
67
+ });
68
+ }
36
69
  /**
37
70
  * Projects the post-move queue through cross-patch lint, auto-declaring the
38
71
  * forward edges the move introduces into the target (an importer that used
@@ -40,12 +73,16 @@ import { findOwnerRewriteHolders, rewriteSplitOwners } from './split-staged.js';
40
73
  * target is later-ordered). Reports only regressions the move itself adds.
41
74
  */
42
75
  function runProjectedMoveLint(plan, baseCtx) {
43
- const movedSet = new Set(plan.movedFiles);
44
- const entries = baseCtx.entries.map((entry) => {
45
- let metadata = entry.metadata;
46
- if (metadata) {
47
- metadata = rewriteSplitOwners(metadata, plan.source.filename, movedSet, plan.target.filename);
48
- }
76
+ const movedSet = new Set([...plan.movedFiles, ...plan.adoptedFiles]);
77
+ const reason = moveDeclarationReason(plan.source.filename, plan.target.filename, 'patch move-files');
78
+ // The projected rows go through the same rewrite the commit applies, so
79
+ // the projection and the written manifest cannot disagree.
80
+ const rows = baseCtx.entries.flatMap((entry) => (entry.metadata ? [entry.metadata] : []));
81
+ const projectedRows = new Map(applyMoveToRows(rows, { ...plan, stagedDependencyAdditions: new Map() }).map((row) => [row.filename, row]));
82
+ const entries = baseCtx.entries
83
+ .filter((entry) => !(plan.retireSource && entry.filename === plan.source.filename))
84
+ .map((entry) => {
85
+ const metadata = entry.metadata ? (projectedRows.get(entry.filename) ?? null) : null;
49
86
  if (entry.filename === plan.source.filename) {
50
87
  return { ...entry, metadata, ...projectEntryBody(plan.sourceDiff) };
51
88
  }
@@ -63,6 +100,7 @@ function runProjectedMoveLint(plan, baseCtx) {
63
100
  specifier: edge.specifier,
64
101
  creates: edge.creates,
65
102
  owner: plan.target.filename,
103
+ reason,
66
104
  };
67
105
  const list = stagedDependencyAdditions.get(edge.entry) ?? [];
68
106
  if (!list.some((d) => d.file === decl.file && d.specifier === decl.specifier && d.creates === decl.creates)) {
@@ -77,10 +115,10 @@ function runProjectedMoveLint(plan, baseCtx) {
77
115
  }
78
116
  }
79
117
  const baselineIssues = lintPatchQueue(baseCtx).filter((i) => i.severity === 'error');
80
- const projectedIssues = lintPatchQueue({
81
- entries,
82
- ...(baseCtx.patchPolicy ? { patchPolicy: baseCtx.patchPolicy } : {}),
83
- }).filter((i) => i.severity === 'error');
118
+ // Keep every queue-wide lint setting (patch policy, forward-registration
119
+ // scope, token scales) so the projection checks what the committed gate
120
+ // checks, test-manifest suite entries included.
121
+ const projectedIssues = lintPatchQueue({ ...baseCtx, entries }).filter((i) => i.severity === 'error');
84
122
  const regressions = computeProjectedLintRegressions(baselineIssues, projectedIssues);
85
123
  if (baselineIssues.length > 0 && regressions.length === 0) {
86
124
  warn(`Note: projected queue still has ${baselineIssues.length} pre-existing cross-patch ` +
@@ -94,6 +132,15 @@ function runProjectedMoveLint(plan, baseCtx) {
94
132
  };
95
133
  return { conflicts, regressions, stagedDependencyAdditions };
96
134
  }
135
+ /** Restores one body during rollback, warning instead of throwing. */
136
+ async function restoreBody(path, body, label) {
137
+ try {
138
+ await writeText(path, body);
139
+ }
140
+ catch (rollbackError) {
141
+ warn(`Rollback warning: could not restore ${label} body: ${toError(rollbackError).message}`);
142
+ }
143
+ }
97
144
  /**
98
145
  * Commits a confirmed move under the patch-directory lock: write both
99
146
  * rewritten bodies, then one manifest rewrite (filesAffected on both rows +
@@ -113,7 +160,7 @@ async function commitMoveInto(patchesDir, plan, waitLockSeconds) {
113
160
  currentTarget.filesAffected.join('\n') !== plan.target.filesAffected.join('\n')) {
114
161
  throw new InvalidArgumentError('Patch queue changed while waiting for move confirmation. Re-run the command.', 'patch move-files');
115
162
  }
116
- const movedSet = new Set(plan.movedFiles);
163
+ assertAdoptedStillUnowned(manifest.patches, plan.adoptedFiles, 'patch move-files');
117
164
  const sourcePath = join(patchesDir, plan.source.filename);
118
165
  const targetPath = join(patchesDir, plan.target.filename);
119
166
  const originalSourceBody = await readText(sourcePath);
@@ -121,25 +168,20 @@ async function commitMoveInto(patchesDir, plan, waitLockSeconds) {
121
168
  let sourceWritten = false;
122
169
  let targetWritten = false;
123
170
  try {
124
- await writeText(sourcePath, plan.sourceDiff);
125
- sourceWritten = true;
171
+ if (!plan.retireSource) {
172
+ await writeText(sourcePath, plan.sourceDiff);
173
+ sourceWritten = true;
174
+ }
126
175
  await writeText(targetPath, plan.targetDiff);
127
176
  targetWritten = true;
128
- const updatedPatches = manifest.patches.map((patch) => {
129
- let updated = rewriteSplitOwners(patch, plan.source.filename, movedSet, plan.target.filename);
130
- const decls = plan.stagedDependencyAdditions.get(updated.filename);
131
- if (decls?.length)
132
- updated = mergeStagedForwardImports(updated, decls);
133
- if (updated.filename === plan.source.filename) {
134
- return { ...updated, filesAffected: plan.sourceAfter };
135
- }
136
- if (updated.filename === plan.target.filename) {
137
- return { ...updated, filesAffected: plan.targetAfter };
138
- }
139
- return updated;
140
- });
177
+ const updatedPatches = applyMoveToRows(manifest.patches, plan);
141
178
  const updated = validatePatchesManifest({ ...manifest, patches: updatedPatches });
142
179
  await savePatchesManifest(patchesDir, updated);
180
+ if (plan.retireSource) {
181
+ // Same write `patch retire` makes: the row and the .patch file
182
+ // leave, and the order stays spent as a retired slot.
183
+ await removePatchFileAndManifest(patchesDir, plan.source.filename, retiredSlotForFold(plan.source, plan.target.filename));
184
+ }
143
185
  await appendHistoryBestEffort(patchesDir, {
144
186
  operation: 'patch-move-files',
145
187
  args: {
@@ -147,26 +189,19 @@ async function commitMoveInto(patchesDir, plan, waitLockSeconds) {
147
189
  target: plan.target.filename,
148
190
  files: plan.movedFiles,
149
191
  ownerRewrites: plan.ownerRewrites,
192
+ ...(plan.adoptedFiles.length > 0 ? { adoptedFiles: plan.adoptedFiles } : {}),
193
+ ...(plan.retireSource
194
+ ? { retiredSource: { filename: plan.source.filename, order: plan.source.order } }
195
+ : {}),
150
196
  },
151
197
  result: 'ok',
152
198
  }, `patch move-files committed`);
153
199
  }
154
200
  catch (error) {
155
- if (targetWritten) {
156
- try {
157
- await writeText(targetPath, originalTargetBody);
158
- }
159
- catch (rollbackError) {
160
- warn(`Rollback warning: could not restore target body: ${toError(rollbackError).message}`);
161
- }
162
- }
163
- if (sourceWritten) {
164
- try {
165
- await writeText(sourcePath, originalSourceBody);
166
- }
167
- catch (rollbackError) {
168
- warn(`Rollback warning: could not restore source body: ${toError(rollbackError).message}`);
169
- }
201
+ if (targetWritten)
202
+ await restoreBody(targetPath, originalTargetBody, 'target');
203
+ if (sourceWritten || !(await pathExists(sourcePath))) {
204
+ await restoreBody(sourcePath, originalSourceBody, 'source');
170
205
  }
171
206
  try {
172
207
  await savePatchesManifest(patchesDir, manifest);
@@ -185,28 +220,40 @@ async function commitMoveInto(patchesDir, plan, waitLockSeconds) {
185
220
  */
186
221
  export async function runMoveFilesInto(args) {
187
222
  const { enginePath, patchesDir, source, target, files, sourceAfter, targetAfter, options } = args;
223
+ const adoptedFiles = args.adoptedFiles ?? [];
224
+ const retireSource = args.retireSource === true;
188
225
  // Refuse a prompt-less run before the diff/lint work, not after it.
189
226
  assertConfirmationAvailable('patch move-files', options);
190
- const sourceDiff = await buildSplitDiff(enginePath, sourceAfter, 'remaining', source.filename);
227
+ const sourceDiff = retireSource
228
+ ? ''
229
+ : await buildSplitDiff(enginePath, sourceAfter, 'remaining', source.filename);
191
230
  const targetDiff = await buildSplitDiff(enginePath, targetAfter, 'moved', target.filename);
192
- // Per-patch lint both projected bodies with each patch's own waivers.
193
- // The whole-queue context (built once, with the config for committed-gate
231
+ // Per-patch lint both projected bodies with each patch's own waivers,
232
+ // judged on what the whole transaction leaves (adopted files land in the
233
+ // target directly, so no patch is judged on an intermediate body). The
234
+ // whole-queue context (built once, with the config for committed-gate
194
235
  // parity) resolves cross-patch imports and sibling head.js harness roots
195
236
  // instead of linting each projected body blind.
196
237
  const patchQueueCtx = await buildPatchQueueContext(patchesDir, args.config);
197
- await runPatchLint({
198
- engineDir: enginePath,
199
- filesAffected: sourceAfter,
200
- diffContent: sourceDiff,
201
- config: args.config,
202
- skipLint: options.skipLint,
203
- patchQueueCtx,
204
- ignoreChecks: buildLintIgnoreChecks(source.lintIgnore),
205
- patchTier: source.tier,
206
- });
207
- await runPatchLint({
238
+ if (!retireSource) {
239
+ await runMoveSideLint({
240
+ engineDir: enginePath,
241
+ filesAffected: sourceAfter,
242
+ filesBefore: source.filesAffected,
243
+ role: 'source',
244
+ diffContent: sourceDiff,
245
+ config: args.config,
246
+ skipLint: options.skipLint,
247
+ patchQueueCtx,
248
+ ignoreChecks: buildLintIgnoreChecks(source.lintIgnore),
249
+ patchTier: source.tier,
250
+ });
251
+ }
252
+ await runMoveSideLint({
208
253
  engineDir: enginePath,
209
254
  filesAffected: targetAfter,
255
+ filesBefore: target.filesAffected,
256
+ role: 'destination',
210
257
  diffContent: targetDiff,
211
258
  config: args.config,
212
259
  skipLint: options.skipLint,
@@ -223,8 +270,12 @@ export async function runMoveFilesInto(args) {
223
270
  targetAfter,
224
271
  sourceDiff,
225
272
  targetDiff,
226
- ownerRewrites: findOwnerRewriteHolders(manifest?.patches ?? [], source.filename, new Set(files)),
273
+ ownerRewrites: retireSource
274
+ ? foldSourceRows(manifest?.patches ?? [], source.filename, target.filename).repointed
275
+ : findOwnerRewriteHolders(manifest?.patches ?? [], source.filename, new Set([...files, ...adoptedFiles])),
227
276
  stagedDependencyAdditions: new Map(),
277
+ adoptedFiles,
278
+ retireSource,
228
279
  };
229
280
  const projected = runProjectedMoveLint(plan, patchQueueCtx);
230
281
  plan.stagedDependencyAdditions = projected.stagedDependencyAdditions;
@@ -232,13 +283,21 @@ export async function runMoveFilesInto(args) {
232
283
  projectRoot: dirname(resolve(patchesDir)),
233
284
  regressions: projected.regressions,
234
285
  out: options.declarationsOut,
286
+ reason: moveDeclarationReason(source.filename, target.filename, 'patch move-files'),
235
287
  });
236
288
  const decision = await confirmDestructive({
237
289
  operation: 'patch-move-files',
238
290
  title: `Move ${files.length} file(s) from ${source.filename} to ${target.filename}`,
239
291
  summary: [
240
292
  `moved files (${files.length}): ${files.join(', ')}`,
241
- `${source.filename} keeps (${sourceAfter.length}): ${sourceAfter.join(', ')}`,
293
+ ...(adoptedFiles.length > 0
294
+ ? [
295
+ `adopted from the worktree (no patch owned them) (${adoptedFiles.length}): ${adoptedFiles.join(', ')}`,
296
+ ]
297
+ : []),
298
+ retireSource
299
+ ? `${source.filename} is emptied and retired; order ${source.order} stays spent as a retired slot`
300
+ : `${source.filename} keeps (${sourceAfter.length}): ${sourceAfter.join(', ')}`,
242
301
  `${target.filename} grows to (${targetAfter.length}): ${targetAfter.join(', ')}`,
243
302
  ...(plan.ownerRewrites.length > 0
244
303
  ? [`staged-dependency owners re-pointed in: ${plan.ownerRewrites.join(', ')}`]
@@ -253,8 +312,14 @@ export async function runMoveFilesInto(args) {
253
312
  return;
254
313
  await commitMoveInto(patchesDir, plan, resolveWaitLockSeconds(options.waitLock));
255
314
  success(`Moved ${files.length} file(s) from ${source.filename} to ${target.filename}`);
315
+ if (adoptedFiles.length > 0) {
316
+ info(`Adopted ${adoptedFiles.length} new file(s) into ${target.filename}`);
317
+ }
256
318
  if (plan.ownerRewrites.length > 0) {
257
319
  info(`Re-pointed staged-dependency owners in: ${plan.ownerRewrites.join(', ')}`);
258
320
  }
321
+ if (retireSource) {
322
+ info(`Retired ${source.filename}; order ${source.order} is kept as a retired slot.`);
323
+ }
259
324
  outro('Move complete');
260
325
  }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `patch move-files --retire-empty`: fold every file of a patch into
3
+ * another and retire the emptied patch's slot in the same transaction.
4
+ *
5
+ * Without the flag `move-files` refuses to empty the source, and
6
+ * `patch retire` on an emptied patch refuses while another patch's
7
+ * declaration still names it as the owner. With the flag the move re-points
8
+ * those declarations to the destination first (after the fold the
9
+ * destination creates every file the source did), carries the source's own declarations over with its files, and then
10
+ * retires the source the way `patch retire` does: the row and `.patch` file
11
+ * leave, and the order stays spent as a `patches.json#retired` slot so
12
+ * `patchPolicy.allowGaps: false` sees no gap.
13
+ */
14
+ import type { PatchMetadata, PatchRetiredSlot } from '../../types/commands/index.js';
15
+ /** The rows of a fold, before the source row is retired. */
16
+ export interface FoldedRows {
17
+ /** Every row: owners re-pointed, the destination carrying the source's declarations. */
18
+ patches: PatchMetadata[];
19
+ /** Patches (other than the source) whose declarations named the source as owner. */
20
+ repointed: string[];
21
+ }
22
+ /**
23
+ * Re-points every declaration whose owner is `source` to `destination` and
24
+ * carries the source's own declarations onto the destination. The source
25
+ * row is returned unchanged; the caller retires it.
26
+ *
27
+ * @param patches - Manifest rows
28
+ * @param source - Filename of the patch the fold empties
29
+ * @param destination - Filename of the patch that receives its files
30
+ */
31
+ export declare function foldSourceRows(patches: readonly PatchMetadata[], source: string, destination: string): FoldedRows;
32
+ /**
33
+ * The retired slot the emptied source leaves, shaped as `patch retire`
34
+ * writes it.
35
+ *
36
+ * @param source - The emptied source row
37
+ * @param destination - Filename of the patch that took its files
38
+ */
39
+ export declare function retiredSlotForFold(source: PatchMetadata, destination: string): PatchRetiredSlot;