@bluewombat/conductor 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.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Conductor
2
+
3
+ Kernel package, not a Transformer: sequences Transformers for one FeatureStandard. It
4
+ writes the WorkLedger before it acts. Isolated workspaces are a consequence of
5
+ that state.
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
+ There is no CLI in this implementation. The caller injects a WorkLedger and a
33
+ Transformer Port (Isolation, Breakdown, Implementer, Integration).
34
+
35
+ **Optional Authority.** When injected, Conductor offers the assembled feature
36
+ and does not fold it into WorkLineStable itself. See [SPECS.md](./SPECS.md) §8.
37
+
38
+ ```ts
39
+ import { openConductor } from "@bluewombat/conductor";
40
+
41
+ const conductor = openConductor({
42
+ ledger,
43
+ transformers,
44
+ workLineStable: "/absolute/path/to/stable",
45
+ workspaceRoot: "/absolute/path/to/workspaces",
46
+ });
47
+ ```
48
+
49
+ ## Layout
50
+
51
+ ```
52
+ SPECS.md behavioural contract
53
+ README.md how to build and run
54
+ src/
55
+ index.ts openConductor
56
+ transformers/port.ts Transformer Port
57
+ run/ loop, pause, reconcile (imports Port + WorkLedger)
58
+ ```
package/SPECS.md ADDED
@@ -0,0 +1,385 @@
1
+ # Conductor
2
+
3
+ Not a Transformer. It has many operations, not one IN and one OUT.
4
+ It sequences other people's jobs: WorkLedger commands, Isolation, Breakdown,
5
+ Implementer, Integration. It does not store FeatureStandards, does not isolate a
6
+ directory, does not split a Plan, does not run a Builder, does not fold a Child,
7
+ and does not report to a FeatureManager.
8
+
9
+ Conductor writes the WorkLedger **before** it acts. Isolated workspaces are a
10
+ consequence of that state, never the source of it.
11
+
12
+ ## 1. Job
13
+
14
+ ```
15
+ caller Conductor others
16
+ ------ --------- ------
17
+ run a Project check pause, reconcile WorkLedger
18
+ claim or resume isolate / breakDown
19
+ write ledger, then act implement / integrate
20
+ ← idle | done | escalated | paused | refused
21
+ ```
22
+
23
+ **In:** an opened WorkLedger, injected transformers, a WorkLineStable path, a workspace
24
+ root, and a Project. **Out:** a typed result. No JSON lines, no exit code, no
25
+ Status protocol. Those belong to Transformers. This package is a function set imported
26
+ in process.
27
+
28
+ There is no required CLI. If a CLI appears later, it is a thin adapter over the
29
+ same functions.
30
+
31
+ ## 2. Faces and wiring
32
+
33
+ | Face | Entry | Must not |
34
+ | ------ | ------------------------------------------------------------------ | ------------------------------------------------ |
35
+ | Import | `openConductor({ ledger, transformers, workLineStable, workspaceRoot })` | Import a Transformer package from `src/run/` |
36
+ | Transformers | A value that implements the Transformer Port | Live under `src/run/` as a concrete Transformer import |
37
+
38
+ The caller builds the transformers (`runIsolator`, `runBreakdown`, `runImplementer`,
39
+ `runIntegrator`, closed over Planner / Builder / Gate argv) and passes them in.
40
+ Conductor does not import `@bluewombat/isolator` (or the other three) from its
41
+ run loop. The composition root that wires Transformers is the caller — Host
42
+ (`packages/host/runtime`) — or a later thin adapter in this package.
43
+
44
+ Transformers do not import this package. WorkLedger does not import this package.
45
+
46
+ ## 3. Two layers
47
+
48
+ ```
49
+ run loop src/run/ order, pause, reconcile,
50
+ ledger-before-act, path choice
51
+ │
52
+ │ Transformer Port + WorkLedger
53
+ ▼
54
+ transformers / ledger injected Isolation, Breakdown, Attempts,
55
+ Integration, durable state
56
+ ```
57
+
58
+ | Layer | Owns | Does not own |
59
+ | --------- | ----------------------------------------------------------- | ------------------------------------------------ |
60
+ | Conductor | Order of steps, pause, orphan destroy, when to call whom | FeatureStandard transitions, git, Builder, Gates |
61
+ | Ledger | Legal transitions, bail, Plan freeze | Directories, Transformer outcomes |
62
+ | Transformers | One Isolation / Breakdown / Attempt loop / Integration each | WorkLedger commands, pause, claim |
63
+
64
+ A caller never talks to Isolator through Conductor internals. It talks to
65
+ `runProject`. The run loop never talks to `node:child_process`. It talks to
66
+ the Transformer Port. Conductor may use `node:fs` only to **delete** directories it
67
+ previously declared (after a successful Integration, on bail expiry, or as
68
+ orphan destroy). It does not copy, merge, or create a workspace.
69
+
70
+ ## 4. Isolation
71
+
72
+ Hard rules. Breaking one is a spec bug, not a refactor.
73
+
74
+ 1. **`src/run/` does not import a Transformer package** (`@bluewombat/isolator`,
75
+ `@bluewombat/implementer`, `@bluewombat/integrator`,
76
+ `@bluewombat/feature-breakdown`). It imports the Transformer Port type and
77
+ WorkLedger types. A test that fails if `run` grows those imports is part of
78
+ this contract.
79
+ 2. **`src/run/` does not import `work-ledger` persistence adapters.** It talks
80
+ to the WorkLedger command surface the caller opened.
81
+ 3. **No `if (git)` / `if (copy)` in Conductor.** Isolation strategy is Isolator's.
82
+ 4. **Domain tests inject a Transformer Port and a WorkLedger.** They may use a
83
+ test-double ledger (in the test file) and fake transformers that record call order.
84
+ They do not require a Transformer `dist/`.
85
+ 5. **The caller, not Conductor, chooses Planner / Builder / Gate argv.** Those
86
+ slots close over the transformer functions.
87
+
88
+ ## 5. Transformer Port
89
+
90
+ Four operations. Real Transformers or test doubles; the Port does not care.
91
+
92
+ | Operation | In (minimum) | Out (minimum) |
93
+ | ----------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
94
+ | `isolate` | `id`, `parent`, `child` (absolute paths), `durationMs` | `isolated` / `failed` / `invalid-invocation` / `interrupted` |
95
+ | `breakDown` | FeatureStandard JSON (key, intention, optional title) | `planned` + subtasks + `planned_at`, or `refused` + code + reason, or `unavailable` / `interrupted` |
96
+ | `implement` | `id`, intention, `workspace`, durations / attempt bound, optional definition of done, `report`, `stage`, `produce` | `validated` / `escalated` / `interrupted` / `invalid-invocation`, plus ordered Traces |
97
+ | `integrate` | `id`, `parent`, `child`, `durationMs` | `integrated` / `conflict` / `failed` / `invalid-invocation` / `interrupted` |
98
+
99
+ Conductor maps ledger fields into `breakDown` JSON. It does not reopen
100
+ FeatureAdapter's closed shape. Recognised keys are `key`, `intention`, `title`.
101
+
102
+ A Subtask carries the `definition_of_done` its Plan gave it. An assembly carries
103
+ none: what it must meet is the Subtasks it is made of, and each was judged on its
104
+ own.
105
+
106
+ `stage` says what is being judged — one unit of work, or the feature they
107
+ assemble into. They are not the same situation: a check that is right for one can
108
+ be wrong for the other. `produce` says whether the Task may make anything: a
109
+ stage built only of already-validated work has nothing to make, and asking a
110
+ producer to run there is how a correct result gets rewritten so a check expecting
111
+ a change is satisfied. `report` is what a pass before this one left unresolved,
112
+ and it reaches the producer as any Gate's report does.
113
+
114
+ A Trace carries `ended` and, when the Attempt did not end well, the `report` of
115
+ whatever refused it — the first Gate that did not pass, or the producer's own
116
+ detail. `ended` says an Attempt failed; `report` says why, and whoever drives the
117
+ next one cannot invent it.
118
+
119
+ `implement` Traces are Implementer's Traces. Conductor records each Subtask
120
+ Trace as a WorkLedger Attempt (`ended`, and `report` / `refusedBy` when the
121
+ Attempt did not end well) before it decides escalate vs integrate.
122
+
123
+ `integrate` may also carry `subject` and `mergeSubject` for the fold's history.
124
+ They are not required by the Port's outcome.
125
+
126
+ An optional **Authority Port** (`submit` / `fold`) is not a transformer. When the
127
+ caller injects one, an assembled feature is offered outside instead of being
128
+ folded into WorkLineStable by Integrator. `workLineTarget` names that work line
129
+ for the Authority. `maxRefusals` (default 3) is how many times the Authority's
130
+ Gates may send the work back before Conductor escalates.
131
+
132
+ ## 6. Paths
133
+
134
+ Constructor: `openConductor({ ledger, transformers, workLineStable, workspaceRoot,
135
+ transformerDurationMs?, authority?, workLineTarget?, maxRefusals?, observe? })`.
136
+ `observe` is told, mid-pass, when the plan is recorded, when a Subtask is
137
+ integrated (with the count), and when the work is submitted — awaited, and a
138
+ throw from it changes nothing about the run.
139
+ `workLineStable` and `workspaceRoot` are absolute directories. `workspaceRoot`
140
+ must not be `workLineStable`. Conductor does not create `workLineStable`.
141
+
142
+ Physical layout is this package's private choice:
143
+
144
+ ```
145
+ <workspaceRoot>/<encoded-key>/feature
146
+ <workspaceRoot>/<encoded-key>/subtask-<encoded-id>
147
+ ```
148
+
149
+ `<encoded-key>` and `<encoded-id>` are `encodeURIComponent` so `fake:42` is one
150
+ path segment. Conductor never uses the raw key as a directory name.
151
+
152
+ It **declares** that path on the WorkLedger, **then** calls `isolate`. After
153
+ `integrate` of a Subtask returns `integrated`, it deletes the Subtask directory
154
+ and `clearWorkspace` for the Subtask. After `markDone`, it deletes the feature
155
+ directory and `clearWorkspace` for the feature.
156
+
157
+ ## 7. Commands
158
+
159
+ Each command is a function on the opened conductor. Illegal input returns
160
+ `refused` with a `code`. It does not throw for domain reasons. A refused
161
+ command does not start a Transformer.
162
+
163
+ | Command | Meaning | Refused when |
164
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
165
+ | `reconcile` | `expireBail` on expired keys. Destroy directories under `workspaceRoot` that are not on `listDeclaredWorkspaces`. Delete declared Subtask dirs after expiry. | Paths not absolute (constructor already threw) |
166
+ | `pause` | In-process flag: no new `claim`, no new `startSubtask`, no `markMerging` / final fold into WorkLineStable. Current `implement` / `isolate` / `integrate` may finish | — |
167
+ | `resume` | Clear the pause flag. Does not itself take work | — |
168
+ | `runProject` | Reconcile, then take or resume the active FeatureStandard on that Project, and drive it until a terminal or wait state | `workLineStable` missing on disk |
169
+ | `cancel` | `ledger.cancel(key)` then destroy declared workspaces for that key | Ledger `point-of-no-return` |
170
+
171
+ `runProject` outcomes:
172
+
173
+ | Outcome | Meaning |
174
+ | ----------- | --------------------------------------------------------------------------------------------- |
175
+ | `idle` | No `received` FeatureStandard and no active one on that Project |
176
+ | `done` | The FeatureStandard is `done` in WorkLineStable |
177
+ | `escalated` | Frozen for a human. Isolated Subtask workspace kept when a Subtask was in play |
178
+ | `paused` | Stopped at a pause boundary. Ledger holds the current state |
179
+ | `refused` | Constructor-level or ledger refusal the loop cannot handle (`persist-failed`, missing stable) |
180
+
181
+ `runProject` does **not** wait for a human `ready`. After `escalated`, the
182
+ caller (FeatureManager path) calls `ledger.resumeReady(key)` then `runProject`
183
+ again.
184
+
185
+ ## 8. Happy path (ledger before act)
186
+
187
+ One FeatureStandard, one Project. Pause flag is off.
188
+
189
+ 1. `reconcile`.
190
+ 2. If the Project already has an active FeatureStandard, resume it. Else `claim`.
191
+ Empty → `idle`.
192
+ 3. **Planning.** `breakDown`. On `planned`: `recordPlan` **before** any Isolation.
193
+ On `refused`: `refusePlan`, return `escalated`. On `unavailable`: return
194
+ `refused` with code `unavailable` (ledger stays `planning`; retryable). On
195
+ `interrupted`: return `refused` with code `interrupted`. On
196
+ `invalid-invocation`: return `refused` with code `transformer-invalid`.
197
+ 4. **Feature workspace.** `declareWorkspace({ feature })` **then** `isolate`
198
+ (parent = WorkLineStable, child = feature path). Failure: do not start
199
+ Subtasks; `runProject` returns `refused` (`isolate-failed` or `transformer-invalid`)
200
+ and leaves the ledger in `running` so a retry isolates again if the directory
201
+ is missing. If a Submission is already recorded and the feature directory is
202
+ gone, Conductor escalates `kind: "submitted"` rather than isolating again
203
+ over what the Authority was shown.
204
+ 5. **Subtask loop** while `nextRunnable` is a Subtask and pause is off:
205
+ 1. `startSubtask`.
206
+ 2. `declareWorkspace({ subtask })` **then** `isolate` (parent = feature path).
207
+ 3. `implement` in that child.
208
+ 4. For each Trace, `recordAttempt` with the next number and that Trace's
209
+ `ended`, plus `report` / `refusedBy` when present.
210
+ 5. `validated` → `integrate` child into feature. `integrated` →
211
+ `markSubtaskIntegrated`, delete child, `clearWorkspace` subtask.
212
+ `conflict` → `escalate({ kind: "subtask", subtaskId })`, **keep** the child.
213
+ 6. `escalated` from Implementer → `escalate({ kind: "subtask", subtaskId })`
214
+ (Trace already recorded). Keep the child. The next `startSubtask` for
215
+ that id **destroys** the kept Child first (resume from zero).
216
+ 7. After a Subtask is marked integrated, if `pending_fingerprint` is set,
217
+ `escalate({ kind: "plan" })` with a report that says the intention was
218
+ edited in flight and what resume does — not that a Plan was refused,
219
+ which is what `kind: "plan"` reads as without it. The ledger consumes
220
+ the pending field. Resume of `kind: "plan"` unfreezes the Plan
221
+ (`resumeReady`). A newer FeatureStandard body lands by `admit` of the
222
+ corrected document (from `escalated` → `received`).
223
+ 6. When the ledger is `integrating` and pause is off:
224
+ 1. If `pending_fingerprint` is set, escalate `kind: "plan"` as in 5.7 and
225
+ stop. Do not align.
226
+ 2. If a Submission `last_report` is set (the Authority sent the work back),
227
+ `implement` on the feature workspace with `stage: "assembly"`,
228
+ `produce: true`, and that report **before** align. `escalated` →
229
+ `escalate({ kind: "assembly" })`.
230
+ 3. **Align.** `integrate` with WorkLineStable as **child** and the feature
231
+ workspace as **parent**. `conflict` → return `refused` (`align-conflict`);
232
+ state stays `integrating`. Cancel is still allowed. Retry `runProject`
233
+ retries align. Do not `escalate({ kind: "merging" })` here: that would set
234
+ `born_in_merging` and block cancel before WorkLineStable is the fold parent.
235
+ 4. **Without an Authority.** `implement` on the feature workspace with
236
+ `stage: "assembly"` and `produce: false` (judgement only). `validated` →
237
+ `markMerging`. `escalated` → `escalate({ kind: "assembly" })`.
238
+ Then **final fold**: `integrate` feature child into WorkLineStable.
239
+ `integrated` → `markDone`, delete feature directory, `clearWorkspace`.
240
+ `conflict` → `escalate({ kind: "merging" })`, keep the feature directory.
241
+ 5. **With an Authority.** `submit` the assembled feature. `submitted` →
242
+ `markSubmitted`, return `paused`. `refused` → `escalate({ kind: "submitted" })`.
243
+ `unavailable` → return `paused` (retry later). On a later `runProject` in
244
+ `submitted`: judge with `produce: false`. Blocking or over `maxRefusals` →
245
+ `escalate({ kind: "submitted" })`. Retryable refusal → `recordRefusal`,
246
+ return `paused` (next pass repairs then aligns, then offers again).
247
+ `validated` → Authority `fold`. `folded` → `markMerging` then `markDone` (with the fold's `reference`, when the Authority named one)
248
+ (the Authority moved the work line; Conductor does not `integrate` into
249
+ WorkLineStable). `conflict` → `escalate({ kind: "submitted" })`.
250
+
251
+ ## 9. Pause and interrupt
252
+
253
+ Pause is an in-process flag on the opened conductor. It is not stored in the
254
+ WorkLedger (pause is Conductor admission, not a FeatureStandard state).
255
+
256
+ Boundaries where pause is honoured: before `claim`, before `startSubtask`,
257
+ before align, before `markMerging`. A running `implement` finishes.
258
+
259
+ Interrupt is the Transformer Port's problem (the same `interruptFlag` the Transformers
260
+ already take). If a transformer returns `interrupted`, Conductor does not mark the
261
+ Subtask integrated. It leaves the ledger as the last successful command left
262
+ it (typically `running` with that Subtask `running`) and returns `refused`
263
+ with code `interrupted`. Reconcile + `expireBail` after process restart is how
264
+ that Subtask becomes `runnable` again when the bail expires. First
265
+ implementation may also `expireBail` immediately on interrupt if the bail is
266
+ already expired; it does not invent a new ledger command.
267
+
268
+ ## 10. Reconcile
269
+
270
+ After process start, and at the beginning of every `runProject`:
271
+
272
+ 1. `listDeclaredWorkspaces`.
273
+ 2. Delete any directory under `workspaceRoot` whose absolute path is **not**
274
+ on that list (orphans). Never delete `workLineStable`.
275
+ 3. `expireBail` for those declared keys, and for `activeOn(project)` when that
276
+ FeatureStandard's bail is past expiry. Then delete a Subtask directory if
277
+ the ledger cleared it.
278
+
279
+ The ledger decides Feature state. Disk never does. Conductor does not scan
280
+ every Project; `runProject` reconciles the workspaces on disk plus the Project
281
+ it was asked to run.
282
+
283
+ ## 11. Refusals
284
+
285
+ | Code | When |
286
+ | ---------------- | ---------------------------------------------------------------- |
287
+ | `stable-missing` | `workLineStable` is not a directory |
288
+ | `root-invalid` | `workspaceRoot` is not absolute, or equals `workLineStable` |
289
+ | `persist-failed` | A ledger command returned `persist-failed` |
290
+ | `interrupted` | A transformer returned `interrupted` |
291
+ | `isolate-failed` | Feature Isolation did not return `isolated` |
292
+ | `align-conflict` | Align Integration returned `conflict`; state stays `integrating` |
293
+ | `transformer-invalid` | A transformer returned `invalid-invocation` |
294
+ | `unavailable` | `breakDown` returned `unavailable` (Planner did not answer) |
295
+
296
+ Ledger codes (`project-busy`, `point-of-no-return`, …) pass through on `cancel`
297
+ and when `claim` refuses.
298
+
299
+ ## 12. What is implemented
300
+
301
+ Shipped:
302
+
303
+ - Import face, `openConductor({ ledger, transformers, workLineStable, workspaceRoot })`
304
+ - Optional Authority (`submit` / `fold`), `workLineTarget`, `maxRefusals`
305
+ - `reconcile`, `pause`, `resume`, `runProject`, `cancel`
306
+ - Happy path: align, assembly judgement (`produce: false`), `markMerging`, final fold
307
+ - Authority path: offer, judge, `recordRefusal` / fold, no Integrator fold into WorkLineStable
308
+ - `pending_fingerprint` consumed after a Subtask integrates and when entering `integrating`
309
+ - Ledger-before-act, declared workspaces, orphan destroy
310
+ - Isolation tests in §4
311
+ - Pause boundaries
312
+
313
+ Not shipped:
314
+
315
+ - FeatureListener / FeatureAdapter / FeatureEmitter (the caller wires them
316
+ through a manager package; Host's suite is the first such assembly)
317
+ - CLI
318
+ - Persistent pause across processes
319
+ - A human `ready` daemon (caller invokes `resumeReady` then `runProject`)
320
+ - SQLite, a second workspace layout
321
+ - Consuming `pending_fingerprint` while the Feature is `submitted` (admit stores
322
+ it; the next Subtask integrate / integrating entry is what consumes it)
323
+
324
+ ## 13. Layout
325
+
326
+ ```
327
+ SPECS.md this contract
328
+ README.md how to build and run
329
+ src/
330
+ index.ts openConductor
331
+ transformers/port.ts Transformer Port only
332
+ run/ loop, pause, reconcile, paths; imports Port + WorkLedger
333
+ ```
334
+
335
+ ## 14. Acceptance
336
+
337
+ 1. `runProject` on a Project with one `received` FeatureStandard calls `claim`
338
+ then `breakDown` then `recordPlan` **before** any `isolate`.
339
+ 2. `declareWorkspace` for the feature path is recorded **before** `isolate` of
340
+ that path. Same for a Subtask path.
341
+ 3. Fake transformers, one Subtask, all `validated` / `integrated`, no Authority:
342
+ Feature ends `done`; `isolate` was called twice (feature, then Subtask);
343
+ `integrate` was called three times (Subtask fold, align, final fold);
344
+ assembly `implement` ran once on the feature workspace with `produce: false`.
345
+ 4. `breakDown` `refused` → `refusePlan`; no `isolate`; outcome `escalated`.
346
+ 5. `implement` `escalated` after Traces → each Trace is `recordAttempt`d;
347
+ then `escalate`; no Subtask `integrate`; child directory not deleted by
348
+ Conductor.
349
+ 6. Subtask `integrate` `conflict` → `escalate`; child kept.
350
+ 7. `cancel` of a `running` Feature → ledger `cancelled`; declared directories
351
+ destroyed. `cancel` after `markMerging` → ledger `point-of-no-return`;
352
+ WorkLineStable untouched.
353
+ 8. A directory under `workspaceRoot` that is not declared is gone after
354
+ `reconcile`. `workLineStable` is never deleted.
355
+ 9. `pause` after `recordPlan` and feature isolate, before the first
356
+ `startSubtask` → outcome `paused`; no `startSubtask`.
357
+ 10. `src/run/` has no import of `@bluewombat/isolator`, `@bluewombat/implementer`,
358
+ `@bluewombat/integrator`, or `@bluewombat/feature-breakdown`.
359
+ 11. Opening Conductor with a Transformer Port test double and a WorkLedger test
360
+ double still passes 1–9. No Transformer `dist/` and no filesystem persist are
361
+ required for those.
362
+
363
+ Gap, named: align `conflict` leaves `integrating` (`align-conflict`) rather than
364
+ a first-class ledger kind. Do not hide that. A later WorkLedger command can
365
+ close it. Assembly `escalated` uses `escalate({ kind: "assembly" })`;
366
+ `resumeReady` returns that Feature to `integrating`.
367
+
368
+ ## 15. Choices
369
+
370
+ 1. **Not a Transformer.** Sequencing many verbs is not Implementer-shaped.
371
+ 2. **Injected Transformer Port.** Depending on four Transformer packages would make the
372
+ run loop untestable without `dist/` and would freeze Isolation strategy
373
+ inside Conductor. The caller wires Transformers.
374
+ 3. **Injected WorkLedger.** Conductor does not open persist. Glue already chose
375
+ the adapter.
376
+ 4. **Ledger before act, always.** Directories that exist without a declaration
377
+ are orphans and die.
378
+ 5. **No Emitter in this package.** Reporting is FeatureEmitter. Conductor
379
+ returns a result; the caller reports.
380
+ 6. **Pause is in-process.** A FeatureStandard state for pause would confuse
381
+ `escalated` and `ready`. Restart clears pause; reconcile + bail remain.
382
+ 7. **Stack: TypeScript on Node.js 24.20, Biome, `node:test` run through `tsx`.**
383
+ Same as WorkLedger. Recorded here; commands live in this package's README.
384
+
385
+ How this package is built and run: [README.md](./README.md).
@@ -0,0 +1,5 @@
1
+ export type { Conductor, ConductorMoment, ConductorRefusalCode, OpenConductorOptions, ProjectRunResult, } from "./run/open-conductor.js";
2
+ export { foldMessageOf, openConductor, subjectOf } from "./run/open-conductor.js";
3
+ export { featureWorkspacePath, subtaskWorkspacePath } from "./run/paths.js";
4
+ export type { AuthorityFoldResult, AuthorityPort, BreakDownResult, ImplementResult, SubmitInput, SubmitResult, TransformerPort, } from "./transformers/port.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC5E,YAAY,EACV,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,eAAe,EACf,WAAW,EACX,YAAY,EACZ,eAAe,GAChB,MAAM,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { foldMessageOf, openConductor, subjectOf } from "./run/open-conductor.js";
2
+ export { featureWorkspacePath, subtaskWorkspacePath } from "./run/paths.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,76 @@
1
+ import type { CommandResult, WorkLedger } from "@bluewombat/work-ledger";
2
+ import type { AuthorityPort, TransformerPort } from "../transformers/port.js";
3
+ export type ConductorRefusalCode = "stable-missing" | "root-invalid" | "persist-failed" | "interrupted" | "isolate-failed" | "align-conflict" | "transformer-invalid" | "unavailable" | "not-found" | "point-of-no-return" | "project-busy" | "illegal-transition";
4
+ export type ProjectRunResult = {
5
+ outcome: "idle";
6
+ } | {
7
+ outcome: "done";
8
+ key: string;
9
+ } | {
10
+ outcome: "escalated";
11
+ key: string;
12
+ } | {
13
+ outcome: "paused";
14
+ key?: string;
15
+ } | {
16
+ outcome: "refused";
17
+ code: ConductorRefusalCode;
18
+ };
19
+ export type OpenConductorOptions = {
20
+ ledger: WorkLedger;
21
+ transformers: TransformerPort;
22
+ workLineStable: string;
23
+ workspaceRoot: string;
24
+ transformerDurationMs?: number;
25
+ /**
26
+ * The outside judge of a Submission. Absent: an assembled feature is folded
27
+ * into the work line directly, with nobody else asked.
28
+ */
29
+ authority?: AuthorityPort;
30
+ /** The work line a Submission is offered to. Required with an Authority. */
31
+ workLineTarget?: string;
32
+ /** How many times the Authority may send the work back before it escalates. */
33
+ maxRefusals?: number;
34
+ /**
35
+ * Told at the moments a pass changes what an outsider would want to know:
36
+ * the plan is in, a Subtask is integrated, the work is submitted. Awaited,
37
+ * so what the caller says lands before the next step; what it throws is
38
+ * its own problem and does not stop the run.
39
+ */
40
+ observe?: (moment: ConductorMoment) => Promise<void> | void;
41
+ };
42
+ /** One thing that happened inside a pass, for whoever is watching from outside. */
43
+ export type ConductorMoment = {
44
+ kind: "planned";
45
+ key: string;
46
+ } | {
47
+ kind: "subtask-integrated";
48
+ key: string;
49
+ subtaskId: string;
50
+ integrated: number;
51
+ total: number;
52
+ } | {
53
+ kind: "submitted";
54
+ key: string;
55
+ reference: string;
56
+ };
57
+ export type Conductor = {
58
+ reconcile(project: string): Promise<void>;
59
+ pause(): void;
60
+ resume(): void;
61
+ runProject(project: string): Promise<ProjectRunResult>;
62
+ cancel(key: string): Promise<CommandResult>;
63
+ };
64
+ /**
65
+ * A fold's subject out of prose. Text written the way a commit is — a headline,
66
+ * a blank line, the rest — gives its headline. Otherwise the first sentence,
67
+ * cut at a word when even that runs past what one line of history shows. A
68
+ * subject is not markdown, so the backticks prose uses for names come off; and
69
+ * it ends on a word, not on an ellipsis — the whole text follows in the body
70
+ * for whoever wants it.
71
+ */
72
+ export declare function subjectOf(text: string): string;
73
+ /** Subject line, then the whole text when the subject did not carry it all. */
74
+ export declare function foldMessageOf(text: string): string;
75
+ export declare function openConductor(options: OpenConductorOptions): Conductor;
76
+ //# sourceMappingURL=open-conductor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-conductor.d.ts","sourceRoot":"","sources":["../../src/run/open-conductor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EAIb,UAAU,EACX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,aAAa,EAGb,eAAe,EAChB,MAAM,yBAAyB,CAAC;AAMjC,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,aAAa,GACb,WAAW,GACX,oBAAoB,GACpB,cAAc,GACd,oBAAoB,CAAC;AAEzB,MAAM,MAAM,gBAAgB,GACxB;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,eAAe,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7D,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAChC;IACE,IAAI,EAAE,oBAAoB,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;IACf,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC7C,CAAC;AAyDF;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAc9C;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAqtBtE"}