@bluewombat/integrator 0.1.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 (51) hide show
  1. package/README.md +92 -0
  2. package/SPECS.md +381 -0
  3. package/dist/args/parse-args.d.ts +6 -0
  4. package/dist/args/parse-args.d.ts.map +1 -0
  5. package/dist/args/parse-args.js +222 -0
  6. package/dist/args/parse-args.js.map +1 -0
  7. package/dist/child/run-child.d.ts +41 -0
  8. package/dist/child/run-child.d.ts.map +1 -0
  9. package/dist/child/run-child.js +145 -0
  10. package/dist/child/run-child.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +99 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/fold/backend.d.ts +32 -0
  16. package/dist/fold/backend.d.ts.map +1 -0
  17. package/dist/fold/backend.js +2 -0
  18. package/dist/fold/backend.js.map +1 -0
  19. package/dist/fold/fold.d.ts +32 -0
  20. package/dist/fold/fold.d.ts.map +1 -0
  21. package/dist/fold/fold.js +49 -0
  22. package/dist/fold/fold.js.map +1 -0
  23. package/dist/fold/working-files.d.ts +28 -0
  24. package/dist/fold/working-files.d.ts.map +1 -0
  25. package/dist/fold/working-files.js +122 -0
  26. package/dist/fold/working-files.js.map +1 -0
  27. package/dist/index.d.ts +17 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +12 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/progress/emit.d.ts +5 -0
  32. package/dist/progress/emit.d.ts.map +1 -0
  33. package/dist/progress/emit.js +17 -0
  34. package/dist/progress/emit.js.map +1 -0
  35. package/dist/run/run-integrator.d.ts +35 -0
  36. package/dist/run/run-integrator.d.ts.map +1 -0
  37. package/dist/run/run-integrator.js +172 -0
  38. package/dist/run/run-integrator.js.map +1 -0
  39. package/dist/status/announce.d.ts +14 -0
  40. package/dist/status/announce.d.ts.map +1 -0
  41. package/dist/status/announce.js +28 -0
  42. package/dist/status/announce.js.map +1 -0
  43. package/dist/status/make-status.d.ts +7 -0
  44. package/dist/status/make-status.d.ts.map +1 -0
  45. package/dist/status/make-status.js +19 -0
  46. package/dist/status/make-status.js.map +1 -0
  47. package/dist/types.d.ts +32 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +3 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # Integrator
2
+
3
+ Transformer: runs **one Integration**. It takes a Child directory and a
4
+ Parent directory, both already existing, and folds the Child's working files
5
+ into the Parent. Never forced: `integrated` or `conflict`.
6
+
7
+ Behavioural contract: [SPECS.md](./SPECS.md).
8
+
9
+ ## Stack
10
+
11
+ Repository stack — TypeScript, Node 24.20, Biome, `node:test`. See [docs/DEVELOPMENT.md](../../../docs/DEVELOPMENT.md).
12
+
13
+ ## Setup
14
+
15
+ From the repository root:
16
+
17
+ ```bash
18
+ nvm use
19
+ npm install
20
+ ```
21
+
22
+ ## Commands
23
+
24
+ | Task | Command |
25
+ | --------- | ---------------- |
26
+ | Lint | `npm run lint` |
27
+ | Format | `npm run format` |
28
+ | Typecheck | `npm run tsc` |
29
+ | Test | `npm test` |
30
+ | Build | `npm run build` |
31
+
32
+ Binary after build: `dist/cli.js` (package bin name `integrator`).
33
+
34
+ ## Run
35
+
36
+ Parent and Child must already exist. Example:
37
+
38
+ ```bash
39
+ npm run build
40
+ node dist/cli.js \
41
+ --id feat-1 \
42
+ --parent /absolute/path/to/parent \
43
+ --child /absolute/path/to/child \
44
+ --duration-ms 60000 \
45
+ --strategy @bluewombat/isolation-copy
46
+ ```
47
+
48
+ The CLI requires `--strategy`: a package name or path that exports `strategy` with a `fold` backend. Integrator does not sniff `.git` and does not choose git versus copy.
49
+
50
+ Progress is JSON lines on stdout (`status`, `integration-started`,
51
+ `integration-finished`). Exit codes: `0` integrated, `1` conflict, `2`
52
+ invalid-invocation, `3` failed, `130` interrupted.
53
+
54
+ ## Import
55
+
56
+ Same Transformer, in process. `cli.ts` is an adapter over this function — it reads argv
57
+ and stdin, calls `runIntegrator`, and maps the outcome to an exit code. Nothing the CLI
58
+ can do is unreachable from here.
59
+
60
+ ```ts
61
+ import { strategy } from "@bluewombat/isolation-copy";
62
+ import { runIntegrator } from "@bluewombat/integrator";
63
+
64
+ const result = await runIntegrator({
65
+ invocation, // same shape the CLI builds from argv
66
+ backend: strategy.fold, // Host / CLI loads a strategy package
67
+ write: (line) => events.push(line), // progress as values, not stdout
68
+ });
69
+ ```
70
+
71
+ `src/index.ts` exports `runIntegrator`, `FoldBackend`, `RunOptions` /
72
+ `RunResult`, `ProgressWriter`, and this Transformer's domain types. Isolation
73
+ strategies live under `packages/plugins/isolation-*`.
74
+
75
+ ## Layout
76
+
77
+ ```
78
+ SPECS.md behavioural contract
79
+ README.md how to build and run
80
+ src/
81
+ cli.ts entry
82
+ types.ts Transformer-owned types
83
+ args/ CLI parsing
84
+ status/ Status labels and --on-status
85
+ progress/ JSON-line progress writer
86
+ child/ spawn (--on-status)
87
+ fold/ fold via injected FoldBackend (snapshot restore + contract)
88
+ run/ invocation orchestration
89
+ fixtures/ fake --on-status scripts for tests
90
+ ```
91
+
92
+ Sources stay colocated: helpers used once live next to their caller.
package/SPECS.md ADDED
@@ -0,0 +1,381 @@
1
+ # Integrator
2
+
3
+ Independent Transformer: it has a binary, a contract, and nothing else. Integrator is
4
+ alone. Its code, identifiers, comments, and messages name only what it owns: the
5
+ Integration, its arguments, the Parent path, the Child path, Status, clocks, and
6
+ the Integration outcome. No other component is mentioned there, because none of
7
+ them exist inside this perimeter.
8
+
9
+ Integrator runs **one Integration**. It takes a Child directory and a Parent
10
+ directory, both of which already exist. It folds the Child's working files into
11
+ the Parent's working files. It never forces that fold: when the two trees cannot
12
+ be combined without choosing a side, the outcome is `conflict`.
13
+
14
+ The invocation is always the same. How Integrator folds is an injected
15
+ **Fold backend** (`fold`). Integrator does not choose a strategy. The CLI
16
+ requires `--strategy <specifier>`; Host loads the package named in
17
+ `workLine.isolation`. Strategies live under `packages/plugins/isolation-*`.
18
+
19
+ Integrator does not create either directory. It does not delete either
20
+ directory. It does not run work in either directory. A directory that is Parent
21
+ of one Integration may be Child of another; Integrator does not know or care.
22
+
23
+ Stack and binary layout: [README.md](./README.md).
24
+
25
+ ## 1. Job
26
+
27
+ ```
28
+ arguments Integrator disk
29
+ --------- ---------- ----
30
+ Integration one invocation write Parent only on `integrated`
31
+ Parent fold Child working files do not write Child working files
32
+ Child into Parent, never forced do not create or delete either
33
+ bounds
34
+ ← Integration outcome + Status
35
+ ```
36
+
37
+ **In:** arguments. **Out:** one Integration outcome, and a live Status each time
38
+ it changes. Integrator does not wait for a listener to acknowledge.
39
+
40
+ ## 2. Options
41
+
42
+ Integrator is invoked with arguments. It does not read an input file. It does
43
+ not read configuration from the Parent or the Child.
44
+
45
+ | Argument | Content |
46
+ | ------------------------ | ------------------------------------------------------------------------------------------------------- |
47
+ | `--id` | Integration id. Copied onto every Status and progress line. Non-empty. |
48
+ | `--parent` | Absolute path. Must already exist as a directory. Destination of the fold. |
49
+ | `--child` | Absolute path. Must already exist as a directory. Source of the fold. Integrator does not create this. |
50
+ | `--duration-ms` | Positive integer. Wall clock of the Integration, from the moment Integrator starts folding into Parent. |
51
+ | `--strategy` | Package name or path. CLI only. Loads `strategy.fold`. `runIntegrator` receives the backend as an argument; it never chooses one. |
52
+ | `--on-status -- <argv…>` | Optional. Command to run each time Status changes. Omitted: Status still goes to stdout. |
53
+
54
+ The import face may also pass `subject` and `mergeSubject` through to the Fold backend (commit messages for the fold). They are not CLI arguments.
55
+
56
+ No other argument is read.
57
+
58
+ Malformed arguments, empty id, non-absolute path, missing Parent, missing Child,
59
+ Parent or Child not a directory, Parent and Child the same path, Parent a prefix
60
+ of Child, Child a prefix of Parent, or a non-positive `--duration-ms` →
61
+ Integration outcome `invalid-invocation`. No fold is attempted. Parent and Child
62
+ are left as they were.
63
+
64
+ ## 3. Workflow
65
+
66
+ One invocation runs one Integration. There is no loop.
67
+
68
+ ```
69
+ Check arguments and paths (§2). Unusable → `invalid-invocation`.
70
+ Announce Status `integrating`.
71
+ Start the Integration clock.
72
+ Fold the Child's working files into the Parent (§4).
73
+ If the fold cannot complete without choosing a side
74
+ → restore Parent to invocation start → Integration outcome `conflict`.
75
+ If the clock fires, a stop signal arrives, or the fold cannot complete
76
+ for a reason that is not a conflict
77
+ → restore Parent to invocation start → Integration outcome `failed` or `interrupted` (§5).
78
+ If the fold completes without choosing a side
79
+ → Integration outcome `integrated`. Parent holds the result. Child stays as it was.
80
+ ```
81
+
82
+ ```mermaid
83
+ flowchart TD
84
+ START[Check arguments] --> OK{Usable?}
85
+ OK -->|no| INV[invalid-invocation]
86
+ OK -->|yes| MER[Fold Child into Parent]
87
+ MER -->|complete, no side chosen| DONE[integrated]
88
+ MER -->|cannot combine without choosing a side| CON[conflict — Parent restored]
89
+ MER -->|clock| FAIL[failed — Parent restored]
90
+ MER -->|stop signal| INT[interrupted — Parent restored]
91
+ MER -->|cannot complete| FAIL
92
+ ```
93
+
94
+ ## 4. Integration
95
+
96
+ Integration is a property of two directories after a successful invocation. It
97
+ is not a later process, not a snapshot, and not a lock on either directory.
98
+
99
+ ### Fold backend
100
+
101
+ Integrator does not choose a strategy. The caller injects a `FoldBackend`.
102
+ Strategies live under `packages/plugins/isolation-*` and export
103
+ `strategy: { isolation, fold }`.
104
+
105
+ The binary's `cli.ts` requires `--strategy <specifier>` and loads that package.
106
+ Host resolves `workLine.isolation` the same way. There is no `.git` sniff and no
107
+ `"git"` / `"copy"` alias. A strategy **conflict** is `conflict`. A strategy
108
+ failure that is not a conflict is `failed`, not a silent switch to another
109
+ method.
110
+
111
+ `.git` at the root of Parent or Child is not a working file. Integrator does
112
+ not fold it as content.
113
+
114
+ Same `--id`, `--parent`, `--child`. Same outcomes. The working-file contract
115
+ (§4) holds either way.
116
+
117
+ ### Working files
118
+
119
+ At Integration start, Integrator reads each directory's **working files**: the
120
+ tree a process with that directory as its working directory would see as the
121
+ work.
122
+
123
+ When the Integration outcome is `integrated`:
124
+
125
+ - The Parent's working files are the combination of both trees: every change
126
+ that exists only in the Child is present in the Parent, and every change that
127
+ exists only in the Parent is still present in the Parent.
128
+ - The Child's working files are what they were at invocation start.
129
+
130
+ The contract is the working files. Git vs copy is how Integrator combines them.
131
+
132
+ Uncommitted working-file changes participate. Integrator does not ignore a
133
+ change because it is not a commit.
134
+
135
+ ### Never force
136
+
137
+ Integrator never chooses a side to make a **conflict** succeed. It never
138
+ overwrites a conflicting Parent path with the Child, or the reverse. It never
139
+ deletes a conflicting Parent path to accept the Child. It never writes
140
+ conflict-marker content into Parent working files. It never passes a force
141
+ option to git. It never rewrites the Parent's git history (no hard reset of
142
+ existing commits, no discard of Parent commits) to make a merge succeed.
143
+
144
+ A merge commit that **adds** history is not a rewrite. Force-taking one side of
145
+ a conflict is.
146
+
147
+ ### Conflict
148
+
149
+ A **conflict** is when the fold cannot complete without choosing a side.
150
+
151
+ On git, that is an overlapping independent change on the same path (both sides
152
+ edited it, or the types cannot be combined). Different bytes on a path is not
153
+ by itself a conflict: a change that exists on only one side is applied.
154
+
155
+ On copy, that is an incompatible type at the same path (file vs directory vs
156
+ symlink). Copy has no third tree, so it cannot see overlapping independent
157
+ edits; a same-type bytes difference is incoming Child work (§4 Copy merge), not
158
+ a conflict.
159
+
160
+ On `conflict`:
161
+
162
+ - Parent working files are what they were at invocation start. No partial fold
163
+ remains. No conflict-marker files remain.
164
+ - Child working files are what they were at invocation start.
165
+ - Both directories still exist.
166
+
167
+ `conflict` is a complete Integration outcome. It is not `failed`. The fold was
168
+ possible to attempt and must not be forced.
169
+
170
+ ### Git merge
171
+
172
+ When Integrator uses git, it merges the Child into the Parent using git. This
173
+ is a three-way merge of working files. The Child is the incoming side. The
174
+ Parent is the destination side. Git's merge finds whether a path changed on
175
+ both sides independently.
176
+
177
+ | Situation | Result on Parent | Outcome |
178
+ | -------------------------------------------------------- | -------------------- | ----------- |
179
+ | Path changed only in the Child | Child's content | (continues) |
180
+ | Path changed only in the Parent | Parent's content | (continues) |
181
+ | Path unchanged on both, or still the same bytes | Parent's content | (continues) |
182
+ | Path changed on both, changes cannot be combined | restored, not merged | `conflict` |
183
+ | Git cannot run or cannot complete (not a merge conflict) | restored | `failed` |
184
+
185
+ When every path continues and the fold completes → `integrated`.
186
+
187
+ Integrator does not fall back to copy after a git conflict or a git failure.
188
+
189
+ If `--parent/.git` exists and the Child is not usable as a git incoming side
190
+ (missing `.git`, unrelated repo, git refuses the merge for a reason that is not
191
+ a content conflict), the outcome is `failed`, not `conflict`, not copy.
192
+
193
+ ### Copy merge
194
+
195
+ When Integrator uses copy, it merges working files without git. Copy has no
196
+ ancestor tree, so it cannot tell a Parent-only edit from a Child-only edit on
197
+ the same path. The Child is the incoming working tree. Integrator applies this
198
+ path-wise fold, skipping `.git` only at the root of Parent and the root of
199
+ Child:
200
+
201
+ | Parent path | Child path | Action |
202
+ | -------------------------- | -------------------------- | ----------------------------------------- |
203
+ | Same type and bytes | Same type and bytes | Leave Parent as is |
204
+ | Missing | Present | Copy the Child path onto Parent |
205
+ | Present | Missing | Leave Parent as is (copy does not delete) |
206
+ | Same type, different bytes | Same type, different bytes | Take the Child's bytes (incoming work) |
207
+ | Incompatible types | Incompatible types | **conflict** |
208
+
209
+ Copy does not choose a side of a type conflict. A bytes difference is not a
210
+ type conflict: it is the Child's work being folded. Parent-only paths stay.
211
+ Child deletions are not applied: copy cannot tell a Child delete from a Parent
212
+ addition, so it does not delete.
213
+
214
+ On a `conflict` row, Integrator stops, restores Parent to invocation start,
215
+ and does not continue the walk as a partial fold.
216
+
217
+ ### Parent
218
+
219
+ Integrator writes Parent working files only when the outcome is `integrated`.
220
+ On every other outcome, Parent working files are what they were at invocation
221
+ start.
222
+
223
+ If Integrator began to write and then hit `conflict`, `failed`, or
224
+ `interrupted`, it restores Parent before returning. A leftover merge in
225
+ progress (index, merge state) does not remain.
226
+
227
+ Integrator may write git bookkeeping that is not working content, if the
228
+ Parent's storage requires it to record a completed merge. That bookkeeping is
229
+ not a working file.
230
+
231
+ Integrator never deletes the Parent.
232
+
233
+ ### Child
234
+
235
+ Both directories already exist. Integrator creates neither. Integrator does not
236
+ add, remove, or edit the Child's working files. After any outcome, those files
237
+ are what they were at invocation start.
238
+
239
+ Integrator does not delete the Child, including after `integrated`. Callers
240
+ that destroy a Child do so outside this process.
241
+
242
+ ### What Integrator does not do
243
+
244
+ - Interpret files in the Parent or the Child (no language-aware merge of its own).
245
+ - Spawn a worker whose job is the content.
246
+ - Create a directory.
247
+ - Delete the Parent or the Child.
248
+ - Choose a Parent or Child path of its own: `--parent` and `--child` are the paths.
249
+ - Reuse a previous Integration's in-memory state: every invocation is the same job.
250
+ - Force a fold.
251
+
252
+ ## 5. Clocks and interrupt
253
+
254
+ **Integration clock.** Starts when Integrator begins folding into the Parent. On
255
+ `--duration-ms`, Integrator stops the fold, restores Parent to invocation start,
256
+ Integration outcome `failed`. The report names the clock.
257
+
258
+ **Interrupt.** SIGINT and SIGTERM stop this invocation. Integrator stops the
259
+ fold, restores Parent to invocation start, Integration outcome `interrupted`.
260
+ It does **not** delete the Parent. It does **not** delete the Child. It does
261
+ **not** undo an Integration that already reached `integrated` (that Integration
262
+ is finished).
263
+
264
+ A stop signal that races a clock uses `interrupted`. A stop signal that races a
265
+ just-completed `integrated` fold uses `integrated`.
266
+
267
+ ## 6. Status
268
+
269
+ Status is the live snapshot of this invocation. It changes at phase boundaries
270
+ (Integration about to run, Integration outcome known). It is not a history. The
271
+ `label` is meant to be shown as-is.
272
+
273
+ ### Shape
274
+
275
+ | Field | Content |
276
+ | ------- | ------------------------------------------------------------------------------ |
277
+ | `phase` | `integrating`, `integrated`, `conflict`, `failed`, `interrupted`, or `invalid` |
278
+ | `id` | From `--id`. Absent on `invalid` when `--id` itself is unusable. |
279
+ | `label` | Canonical display string. See below. |
280
+
281
+ ### Label
282
+
283
+ Built by Integrator. Do not invent another spelling.
284
+
285
+ | `phase` | `label` | Example |
286
+ | ------------- | ------------------ | -------------------- |
287
+ | `integrating` | `integrating:<id>` | `integrating:feat-1` |
288
+ | `integrated` | `integrated:<id>` | `integrated:feat-1` |
289
+ | `conflict` | `conflict:<id>` | `conflict:feat-1` |
290
+ | `failed` | `failed:<id>` | `failed:feat-1` |
291
+ | `interrupted` | `interrupted:<id>` | `interrupted:feat-1` |
292
+ | `invalid` | `invalid` | `invalid` |
293
+
294
+ `<id>` is the `--id` argument, as given.
295
+
296
+ ### When it is announced
297
+
298
+ | Moment | `phase` |
299
+ | ------------------------------------------------------------------------ | ------------- |
300
+ | Arguments or paths unusable | `invalid` |
301
+ | Fold about to start | `integrating` |
302
+ | Integration outcome `integrated` / `conflict` / `failed` / `interrupted` | that outcome |
303
+
304
+ Announced on stdout as a JSON line (`event`: `status`) carrying the fields
305
+ above. If `--on-status` was given, Integrator also runs that command, cwd
306
+ unchanged (not the Parent, not the Child), and appends:
307
+
308
+ | Argument | Content |
309
+ | --------- | ---------------------------- |
310
+ | `--id` | Integration id, when present |
311
+ | `--label` | The label |
312
+ | `--phase` | The phase |
313
+
314
+ `--on-status` does not count against the Integration clock. A non-zero exit, a
315
+ missing command, or a hang that Integrator must kill does **not** change the
316
+ Integration outcome. Visibility must not take the Integration down.
317
+
318
+ ## 7. Progress
319
+
320
+ JSON lines on stdout. A `status` line is the live snapshot (§6). The other
321
+ lines record the Integration.
322
+
323
+ | `event` | When |
324
+ | ---------------------- | ---------------------------- |
325
+ | `status` | Each time Status changes |
326
+ | `integration-started` | Fold about to start |
327
+ | `integration-finished` | Integration outcome is known |
328
+
329
+ Each line carries `id` when the Integration id is usable.
330
+
331
+ `integration-finished` also carries:
332
+
333
+ | Field | Content |
334
+ | --------- | --------------------------------------------------------------------------------------- |
335
+ | `outcome` | The Integration outcome (§8). |
336
+ | `parent` | Absolute `--parent` path. Present when the outcome is `integrated`. |
337
+ | `report` | Why, when `outcome` is `conflict`, `failed`, or `invalid-invocation`. Absent otherwise. |
338
+
339
+ Integrator creates no working file under the Child. It writes Parent working
340
+ files only on `integrated`. It deletes nothing under the Child. It deletes
341
+ nothing under the Parent except, on `integrated`, paths the fold must replace
342
+ as part of applying a Child-only change that is not a conflict.
343
+
344
+ ## 8. Integration outcomes
345
+
346
+ Exactly one per invocation. The meaning is entirely inside this process.
347
+
348
+ | Outcome | Meaning |
349
+ | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
350
+ | `invalid-invocation` | Arguments or paths unusable. No fold attempted. Parent and Child untouched. |
351
+ | `integrated` | Parent working files hold the combined trees. Child unchanged. Both stay. |
352
+ | `conflict` | Fold cannot complete without choosing a side. Parent restored to invocation start. Child unchanged. Both stay. |
353
+ | `failed` | Integration started and could not complete (clock, I/O, spawn of Integrator's own tools, git failure that is not a merge conflict). Parent restored. Child unchanged. |
354
+ | `interrupted` | Stop signal before `integrated` or `conflict`. Parent restored. Child unchanged. |
355
+
356
+ No partial Integration. After this process exits, Parent working files are
357
+ either what they were at invocation start, or the complete `integrated` result.
358
+
359
+ ## 9. Acceptance
360
+
361
+ 1. Child has a path the Parent does not; Integration completes → `integrated`; Parent has that path with the Child's content; Child unchanged.
362
+ 2. Parent has a path the Child does not; Integration completes → `integrated`; that Parent path is still there; Child unchanged.
363
+ 3. A path exists in both with the same content; Integration completes → `integrated`; that path unchanged on Parent; Child unchanged.
364
+ 4. `@bluewombat/isolation-git`; a path changed only in the Child (Parent still has the pre-change bytes) → `integrated`; Parent has the Child's bytes; Child unchanged.
365
+ 5. `@bluewombat/isolation-git`; a path changed only in the Parent (Child still has the pre-change bytes) → `integrated`; Parent keeps its bytes; Child unchanged.
366
+ 6. `@bluewombat/isolation-git`; both sides changed the same path with different bytes that git cannot combine → `conflict`; Parent still has its invocation-start content; Child unchanged; no conflict-marker files on Parent.
367
+ 7. `@bluewombat/isolation-copy`; a path exists in both with different bytes of the same type → `integrated`; Parent has the Child's bytes (copy incoming work); Child unchanged.
368
+ 8. A path exists in both with incompatible types → `conflict`; Parent restored; Child unchanged.
369
+ 9. `--child` missing, `--parent` missing, relative path, empty `--id`, `--child` inside `--parent`, or `--parent` inside `--child` → `invalid-invocation`; both directories left as they were when they existed.
370
+ 10. SIGTERM during the fold → `interrupted`; Parent working files are the invocation-start tree; Child remains on disk with its invocation-start tree.
371
+ 11. Integration clock fires during the fold → `failed`; Parent restored; Child remains; report names the clock.
372
+ 12. Integrator never deletes the Parent, never deletes the Child, never writes a working file of its own into the Child, never chooses a side on a conflict, never leaves a partial fold on Parent.
373
+ 13. A directory that was Parent of one Integration can be `--child` of another; that second Integration is the same job.
374
+ 14. Entering the fold with `--id feat-1` announces `label` `integrating:feat-1` on stdout, and via `--on-status` when that argument is set.
375
+ 15. `--on-status` exiting non-zero does not change an `integrated` Integration.
376
+ 16. `@bluewombat/isolation-git` → git inside; a git merge conflict is `conflict`, not copy; Parent restored; Child unchanged.
377
+ 17. `@bluewombat/isolation-copy` → copy inside; a same-type bytes difference is taken from the Child, not `conflict`.
378
+ 18. `@bluewombat/isolation-git` and git cannot complete for a reason that is not a merge conflict → `failed`, not a copy, not `conflict`.
379
+ 19. The CLI requires `--strategy <specifier>`; `runIntegrator` never chooses a strategy.
380
+
381
+ How to build and run this Transformer: [README.md](./README.md).
@@ -0,0 +1,6 @@
1
+ import type { ParseResult } from "../types.js";
2
+ /**
3
+ * Parse Integrator CLI argv (without node/script). Malformed input → not ok.
4
+ */
5
+ export declare function parseArgs(argv: string[]): ParseResult;
6
+ //# sourceMappingURL=parse-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-args.d.ts","sourceRoot":"","sources":["../../src/args/parse-args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,aAAa,CAAC;AAgH3D;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,CAoJrD"}