@bluewombat/conductor 0.8.0 → 0.10.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Conductor
2
2
 
3
- Kernel package, not a Transformer: sequences Transformers for one FeatureStandard. It
3
+ Kernel package, not a Transformer: sequences Transformers for one TicketStandard. It
4
4
  writes the WorkLedger before it acts. Isolated workspaces are a consequence of
5
5
  that state.
6
6
 
@@ -30,9 +30,9 @@ npm install
30
30
  | Build | `npm run build` |
31
31
 
32
32
  There is no CLI in this implementation. The caller injects a WorkLedger and a
33
- Transformer Port (Isolation, Breakdown, Implementer, Integration).
33
+ Transformer Port (Isolation, Implementer, Integration).
34
34
 
35
- **Optional Authority.** When injected, Conductor offers the assembled feature
35
+ **Optional Authority.** When injected, Conductor offers the assembled ticket
36
36
  and does not fold it into WorkLineStable itself. See [SPECS.md](./SPECS.md) §8.
37
37
 
38
38
  ```ts
package/SPECS.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Conductor
2
2
 
3
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.
4
+ It sequences other people's jobs: WorkLedger commands, Isolation, Implementer,
5
+ Integration. It does not store TicketStandards, does not isolate a directory,
6
+ does not run a Builder, does not fold a Child, and does not report to a
7
+ TicketTracker.
8
8
 
9
9
  Conductor writes the WorkLedger **before** it acts. Isolated workspaces are a
10
10
  consequence of that state, never the source of it.
@@ -15,7 +15,7 @@ consequence of that state, never the source of it.
15
15
  caller Conductor others
16
16
  ------ --------- ------
17
17
  run a Project check pause, reconcile WorkLedger
18
- claim or resume isolate / breakDown
18
+ claim or resume isolate
19
19
  write ledger, then act implement / integrate
20
20
  ← idle | done | escalated | paused | refused
21
21
  ```
@@ -35,9 +35,9 @@ same functions.
35
35
  | Import | `openConductor({ ledger, transformers, workLineStable, workspaceRoot })` | Import a Transformer package from `src/run/` |
36
36
  | Transformers | A value that implements the Transformer Port | Live under `src/run/` as a concrete Transformer import |
37
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
38
+ The caller builds the transformers (`runIsolator`, `runImplementer`,
39
+ `runIntegrator`, closed over Builder / Gate argv) and passes them in.
40
+ Conductor does not import `@bluewombat/isolator` (or the other two) from its
41
41
  run loop. The composition root that wires Transformers is the caller — Host
42
42
  (`packages/host/runtime`) — or a later thin adapter in this package.
43
43
 
@@ -51,15 +51,15 @@ Transformers do not import this package. WorkLedger does not import this package
51
51
  │
52
52
  │ Transformer Port + WorkLedger
53
53
  ▼
54
- transformers / ledger injected Isolation, Breakdown, Attempts,
54
+ transformers / ledger injected Isolation, Attempts,
55
55
  Integration, durable state
56
56
  ```
57
57
 
58
58
  | Layer | Owns | Does not own |
59
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 |
60
+ | Conductor | Order of steps, pause, orphan destroy, when to call whom | TicketStandard transitions, git, Builder, Gates |
61
+ | Ledger | Legal transitions, bail | Directories, Transformer outcomes |
62
+ | Transformers | One Isolation / Attempt loop / Integration each | WorkLedger commands, pause, claim |
63
63
 
64
64
  A caller never talks to Isolator through Conductor internals. It talks to
65
65
  `runProject`. The run loop never talks to `node:child_process`. It talks to
@@ -72,8 +72,7 @@ orphan destroy). It does not copy, merge, or create a workspace.
72
72
  Hard rules. Breaking one is a spec bug, not a refactor.
73
73
 
74
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
75
+ `@bluewombat/implementer`, `@bluewombat/integrator`). It imports the Transformer Port type and
77
76
  WorkLedger types. A test that fails if `run` grows those imports is part of
78
77
  this contract.
79
78
  2. **`src/run/` does not import `work-ledger` persistence adapters.** It talks
@@ -82,29 +81,21 @@ Hard rules. Breaking one is a spec bug, not a refactor.
82
81
  4. **Domain tests inject a Transformer Port and a WorkLedger.** They may use a
83
82
  test-double ledger (in the test file) and fake transformers that record call order.
84
83
  They do not require a Transformer `dist/`.
85
- 5. **The caller, not Conductor, chooses Planner / Builder / Gate argv.** Those
84
+ 5. **The caller, not Conductor, chooses Builder / Gate argv.** Those
86
85
  slots close over the transformer functions.
87
86
 
88
87
  ## 5. Transformer Port
89
88
 
90
- Four operations. Real Transformers or test doubles; the Port does not care.
89
+ Three operations. Real Transformers or test doubles; the Port does not care.
91
90
 
92
91
  | Operation | In (minimum) | Out (minimum) |
93
92
  | ----------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
94
93
  | `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 |
94
+ | `implement` | `id`, intention, `workspace`, durations / attempt bound, `report`, `stage`, `produce` | `validated` / `escalated` / `interrupted` / `invalid-invocation`, plus ordered Traces |
97
95
  | `integrate` | `id`, `parent`, `child`, `durationMs` | `integrated` / `conflict` / `failed` / `invalid-invocation` / `interrupted` |
98
96
 
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
97
+ `stage` says what is being judged — the unit of work, or the work once
98
+ assembled. They are not the same situation: a check that is right for one can
108
99
  be wrong for the other. `produce` says whether the Task may make anything: a
109
100
  stage built only of already-validated work has nothing to make, and asking a
110
101
  producer to run there is how a correct result gets rewritten so a check expecting
@@ -121,7 +112,7 @@ whatever refused it — the first Gate that did not pass, or the producer's own
121
112
  detail. `ended` says an Attempt failed; `report` says why, and whoever drives the
122
113
  next one cannot invent it.
123
114
 
124
- `implement` Traces are Implementer's Traces. Conductor records each Subtask
115
+ `implement` Traces are Implementer's Traces. Conductor records each unit
125
116
  Trace as a WorkLedger Attempt (`ended`, and `report` / `refusedBy` when the
126
117
  Attempt did not end well) before it decides escalate vs integrate.
127
118
 
@@ -129,33 +120,31 @@ Attempt did not end well) before it decides escalate vs integrate.
129
120
  They are not required by the Port's outcome.
130
121
 
131
122
  An optional **Authority Port** (`submit` / `fold`) is not a transformer. When the
132
- caller injects one, an assembled feature is offered outside instead of being
123
+ caller injects one, an assembled ticket is offered outside instead of being
133
124
  folded into WorkLineStable by Integrator. `workLineTarget` names that work line
134
- for the Authority. `maxRefusals` (default 3) is how many times the Authority's
125
+ for the Authority; a Ticket that names its own `line` is offered to that one. `maxRefusals` (default 3) is how many times the Authority's
135
126
  Gates may send the work back before Conductor escalates.
136
127
 
137
128
  ## 6. Paths
138
129
 
139
130
  Constructor: `openConductor({ ledger, transformers, workLineStable, workspaceRoot,
140
131
  transformerDurationMs?, authority?, workLineTarget?, maxRefusals?,
141
- assemblyValidate?, assemblyFixDeclared?, observe? })`. `assemblyValidate` says
132
+ validate?, fixDeclared?, observe? })`. `validate` says
142
133
  whether a local judge runs after align, with or without an Authority.
143
- `assemblyFixDeclared` says whether a refusal has anything to repair it with; a
134
+ `fixDeclared` says whether a refusal has anything to repair it with; a
144
135
  validate refusal escalates on the spot when it does not.
145
- `observe` is told, mid-pass, when the plan is recorded, when a Subtask is
146
- integrated (with the count), and when the work is submitted — awaited, and a
147
- throw from it changes nothing about the run.
136
+ `observe` is told, mid-pass, when the work is submitted — awaited, and a throw
137
+ from it changes nothing about the run.
148
138
  `workLineStable` and `workspaceRoot` are absolute directories. `workspaceRoot`
149
139
  must not be `workLineStable`. Conductor does not create `workLineStable`.
150
140
 
151
141
  Physical layout is this package's private choice:
152
142
 
153
143
  ```
154
- <workspaceRoot>/<key-segment>/feature
155
- <workspaceRoot>/<key-segment>/subtask-<id-segment>
144
+ <workspaceRoot>/<key-segment>/ticket
156
145
  ```
157
146
 
158
- `<key-segment>` and `<id-segment>` are one safe path segment each: a name made
147
+ `<key-segment>` is one safe path segment: a name made
159
148
  of letters, digits, `.`, `_` and `-` is kept as it is; anything else becomes a
160
149
  readable slug plus the first 8 hex digits of the SHA-256 of the original, so
161
150
  `github:tilap/site#1` is `github-tilap-site-1-<hash>` and two keys never share a
@@ -163,18 +152,14 @@ directory. There is no `%`: a workspace is where the Builder runs the
163
152
  project's own tools, and several (Vitest among them) refuse a path that looks
164
153
  URL-encoded. Conductor never uses the raw key as a directory name.
165
154
 
166
- **A declared path is the path.** Every step that needs the feature's workspace
167
- reads the one the WorkLedger declared for it and computes the layout's own only
168
- when none is declared, so a feature made under an earlier layout — escalated,
169
- then resumed — keeps its work and its branch where they are. A kept Subtask at
170
- an older path is destroyed with the one at the current path when it starts
171
- over, so its branch is free to be taken again. The Authority is handed the
172
- feature's workspace in `submit` (`workspace`), and works out nothing itself.
155
+ **A declared path is the path.** Every step that needs the workspace reads the
156
+ one the WorkLedger declared for it and computes the layout's own only when none
157
+ is declared. A unit that starts over destroys what is at that path first, so
158
+ its branch is free to be taken again. The Authority is handed the workspace in
159
+ `submit` (`workspace`), and works out nothing itself.
173
160
 
174
161
  It **declares** that path on the WorkLedger, **then** calls `isolate`. After
175
- `integrate` of a Subtask returns `integrated`, it deletes the Subtask directory
176
- and `clearWorkspace` for the Subtask. After `markDone`, it deletes the feature
177
- directory and `clearWorkspace` for the feature.
162
+ `markDone`, it deletes the directory and `clearWorkspace`.
178
163
 
179
164
  ## 7. Commands
180
165
 
@@ -184,97 +169,105 @@ command does not start a Transformer.
184
169
 
185
170
  | Command | Meaning | Refused when |
186
171
  | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
187
- | `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) |
188
- | `pause` | In-process flag: no new `claim`, no new `startSubtask`, no `markMerging` / final fold into WorkLineStable. Current `implement` / `isolate` / `integrate` may finish | — |
172
+ | `reconcile` | `expireBail` on expired keys. Destroy directories under `workspaceRoot` that are not on `listDeclaredWorkspaces`. Delete the declared directory of a unit whose lease expired. | Paths not absolute (constructor already threw) |
173
+ | `pause` | In-process flag: no new `claim`, no new unit start, no `markMerging` / final fold into WorkLineStable. Current `implement` / `isolate` / `integrate` may finish | — |
189
174
  | `resume` | Clear the pause flag. Does not itself take work | — |
190
- | `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 |
175
+ | `runProject` | Reconcile, then take or resume the active TicketStandard on that Project, and drive it until a terminal or wait state | `workLineStable` missing on disk |
191
176
  | `cancel` | `ledger.cancel(key)` then destroy declared workspaces for that key | Ledger `point-of-no-return` |
192
- | `release` | `ledger.releaseBail(key)` then destroy the declared Subtask directory when one was recorded | Ledger `illegal-transition` / `not-found` |
177
+ | `release` | `ledger.releaseBail(key)` then destroy the declared directory when one was recorded | Ledger `illegal-transition` / `not-found` |
193
178
 
194
179
  `runProject` outcomes:
195
180
 
196
181
  | Outcome | Meaning |
197
182
  | ----------- | --------------------------------------------------------------------------------------------- |
198
- | `idle` | No `received` FeatureStandard and no active one on that Project |
199
- | `done` | The FeatureStandard is `done` in WorkLineStable |
200
- | `escalated` | Frozen for a human. Isolated Subtask workspace kept when a Subtask was in play |
183
+ | `idle` | No `received` TicketStandard and no active one on that Project |
184
+ | `done` | The TicketStandard is `done` in WorkLineStable |
185
+ | `escalated` | Frozen for a human. The workspace is kept for inspection |
201
186
  | `paused` | Stopped at a pause boundary. Ledger holds the current state |
202
187
  | `refused` | Constructor-level or ledger refusal the loop cannot handle (`persist-failed`, missing stable) |
203
188
 
204
- `runProject` does **not** wait for a human `ready`. After `escalated`, the
205
- caller (FeatureManager path) calls `ledger.resumeReady(key)` then `runProject`
189
+ `runProject` does **not** wait for a human `resume`. After `escalated`, the
190
+ caller (TicketTracker path) calls `ledger.resumeTicket(key)` then `runProject`
206
191
  again.
207
192
 
208
193
  ## 8. Happy path (ledger before act)
209
194
 
210
- One FeatureStandard, one Project. Pause flag is off.
195
+ One TicketStandard, one Project. Pause flag is off.
211
196
 
212
197
  1. `reconcile`.
213
- 2. If the Project already has an active FeatureStandard, resume it. Else `claim`.
198
+ 2. If the Project already has an active TicketStandard, resume it. Else `claim`.
214
199
  Empty → `idle`.
215
- 3. **Planning.** `breakDown`. On `planned`: `recordPlan` **before** any Isolation.
216
- On `refused`: `refusePlan`, return `escalated`. On `unavailable`: return
217
- `refused` with code `unavailable` (ledger stays `planning`; retryable). On
218
- `interrupted`: return `refused` with code `interrupted`. On
219
- `invalid-invocation`: return `refused` with code `transformer-invalid`.
220
- 4. **Feature workspace.** `declareWorkspace({ feature })` **then** `isolate`
221
- (parent = WorkLineStable, child = feature path). Failure: do not start
222
- Subtasks; `runProject` returns `refused` (`isolate-failed` or `transformer-invalid`)
223
- and leaves the ledger in `running` so a retry isolates again if the directory
224
- is missing. If a Submission is already recorded and the feature directory is
225
- gone, Conductor escalates `kind: "submitted"` rather than isolating again
226
- over what the Authority was shown. When a `warm` port is configured, prepare
227
- the Feature workspace next (install deps, …) before any Subtask Isolation —
228
- once per Feature in this Conductor instance, and again after a fresh
229
- Isolation. Failure: `refused` with `warm-failed` (or `interrupted` /
230
- `transformer-invalid`); the Feature directory is kept so the next
231
- `runProject` retries warm without re-isolating.
232
- 5. **Subtask loop** while `nextRunnable` is a Subtask and pause is off:
233
- 1. `startSubtask`.
234
- 2. `declareWorkspace({ subtask })` **then** `isolate` (parent = feature path).
235
- 3. `implement` in that child.
236
- 4. For each Trace, `recordAttempt` with the next number and that Trace's
200
+ 3. **The unit.** When the ledger is `running`, no lease is held and pause is
201
+ off: destroy what is at the workspace path, `declareWorkspace` **then**
202
+ `isolate` (parent = WorkLineStable, child = the workspace). Failure: no
203
+ lease was taken, so `runProject` returns `refused` (`isolate-failed` or
204
+ `transformer-invalid`) and a retry starts the unit again. When a `warm` port
205
+ is configured, prepare the workspace next (install deps, …) — each time the
206
+ workspace is made. Failure: `refused` with `warm-failed` (or `interrupted` /
207
+ `transformer-invalid`). Then `startUnit` (the lease) and `implement` in that
208
+ workspace.
209
+ 1. For each Trace, `recordAttempt` with the next number and that Trace's
237
210
  `ended`, plus `report` / `refusedBy` when present.
238
- 5. `validated` → `integrate` child into feature. `integrated` →
239
- `markSubtaskIntegrated`, delete child, `clearWorkspace` subtask.
240
- `conflict` → `escalate({ kind: "subtask", subtaskId })`, **keep** the child.
241
- 6. `escalated` from Implementer → `escalate({ kind: "subtask", subtaskId })`
242
- (Trace already recorded). Keep the child. The next `startSubtask` for
243
- that id **destroys** the kept Child first (resume from zero).
244
- 7. After a Subtask is marked integrated, if `pending_fingerprint` is set,
245
- `escalate({ kind: "plan" })` with a report that says the intention was
246
- edited in flight and what resume does — not that a Plan was refused,
247
- which is what `kind: "plan"` reads as without it. The ledger consumes
248
- the pending field. Resume of `kind: "plan"` unfreezes the Plan
249
- (`resumeReady`). A newer FeatureStandard body lands by `admit` of the
250
- corrected document (from `escalated` → `received`).
211
+ 2. `validated` → `markIntegrating`. The result stays loose in the
212
+ workspace; the Integration in 4.3 takes it up.
213
+ 3. `escalated` from Implementer → `escalate({ kind: "unit" })` (Trace
214
+ already recorded). Keep the workspace. `resumeTicket` returns the
215
+ Ticket to `running`, forgets the workspace, and the unit starts again from zero.
216
+ 4. After `markIntegrating`, if `pending_fingerprint` is set,
217
+ `escalate({ kind: "unit" })` with a report that says the intention was
218
+ edited in flight and what resume does. The ledger consumes the pending
219
+ field. A newer TicketStandard body lands by `admit` of the corrected
220
+ document (from `escalated` → `received`).
221
+ A `running` Ticket that holds a lease is `paused`: its lease runs out on
222
+ its own when nobody is on it.
223
+ 4. **The workspace must still be there** from `integrating` on. When it is
224
+ gone, Conductor does not make it again from the work line, which would drop
225
+ what was made: it escalates `kind: "submitted"` when the Authority holds a
226
+ Submission, `kind: "assembly"` otherwise.
227
+ 5. Pause is honoured before the unit starts, after Isolation, before align and
228
+ before `markMerging`.
251
229
  6. When the ledger is `integrating` and pause is off:
252
- 1. If `pending_fingerprint` is set, escalate `kind: "plan"` as in 5.7 and
230
+ 1. If `pending_fingerprint` is set, escalate `kind: "unit"` as in 3.4 and
253
231
  stop. Do not align.
254
232
  2. If a parked refusal or a Submission `last_report` is set (the parked one
255
- wins when both are), `implement` on the feature workspace with
256
- `stage: "assembly"`, `produce: true`, and that report **before** align.
233
+ wins when both are), `implement` on the ticket workspace with
234
+ `stage: "ticket"`, `produce: true`, and that report **before** align.
257
235
  `escalated` → `escalate({ kind: "assembly" })`.
258
- 3. **Align.** `integrate` with WorkLineStable as **child** and the feature
259
- workspace as **parent**. `conflict` → return `refused` (`align-conflict`);
260
- state stays `integrating`. Cancel is still allowed. Retry `runProject`
261
- retries align. Do not `escalate({ kind: "merging" })` here: that would set
236
+ 3. **Align.** `integrate` with WorkLineStable as **child** and the
237
+ workspace as **parent**. What is loose in the workspace is first taken up
238
+ as one commit whose subject is the Ticket's title (or its intention's
239
+ headline); after a refusal it is the repair, with its own subject. `conflict` → the work is **sent back** like any
240
+ refusal (see below), with a report that names the line and says what to do;
241
+ it is not a stop. Do not `escalate({ kind: "merging" })` here: that would set
262
242
  `born_in_merging` and block cancel before WorkLineStable is the fold parent.
263
- 4. **Validate**, when `assemblyValidate` is set — with or without an
264
- Authority. `implement` on the feature workspace with `stage: "assembly"`
243
+ 3b. **Recheck.** Aligning brought other tickets' code in, so the unit's own
244
+ Gates run again: `implement` on the ticket workspace with
245
+ `stage: "ticket"`, `produce: false`, `recheck: true` (one attempt; Host
246
+ judges it by `builder.gates`, no other list). `validated` →
247
+ `clearParkedRefusal`. `escalated` → sent back with the Gate's report and name.
248
+
249
+ **Sent back** (align conflict, failed recheck, `ticket.validate` refusal): with
250
+ `fixDeclared`, a retryable cause and budget left → `recordParkedRefusal`,
251
+ return `paused`; the next pass produces the fix, then aligns, rechecks and
252
+ validates again. Otherwise (blocking, budget spent, no fix declared) →
253
+ `escalate({ kind: "assembly" })`. **One budget** covers every loop of a ticket:
254
+ `submission.refusals + parked_refusals + submission.realigns`, against
255
+ `maxRefusals`.
256
+ 4. **Validate**, when `validate` is set — with or without an
257
+ Authority. `implement` on the ticket workspace with `stage: "ticket"`
265
258
  and `validate: true` (no gates, one attempt). `validated` →
266
259
  `clearParkedRefusal`, then continue to 5 or 6. `escalated`, retryable,
267
260
  under `maxRefusals` (the budget shared with `submission.refusals`), and
268
- `assemblyFixDeclared` → `recordParkedRefusal`, return `paused`. Do not
261
+ `fixDeclared` → `recordParkedRefusal`, return `paused`. Do not
269
262
  continue to 5 or 6. `escalated` otherwise (blocking, at the budget, or no
270
- `assembly.fix` declared) → `escalate({ kind: "assembly" })`.
271
- 5. **Without an Authority.** `implement` on the feature workspace with
272
- `stage: "assembly"` and `produce: false` (judgement only). `validated` →
263
+ `ticket.fix` declared) → `escalate({ kind: "assembly" })`.
264
+ 5. **Without an Authority.** `implement` on the ticket workspace with
265
+ `stage: "ticket"` and `produce: false` (judgement only). `validated` →
273
266
  `markMerging`. `escalated` → `escalate({ kind: "assembly" })`.
274
- Then **final fold**: `integrate` feature child into WorkLineStable.
275
- `integrated` → `markDone`, delete feature directory, `clearWorkspace`.
276
- `conflict` → `escalate({ kind: "merging" })`, keep the feature directory.
277
- 6. **With an Authority.** `submit` the assembled feature. `submitted` →
267
+ Then **final fold**: `integrate` ticket child into WorkLineStable.
268
+ `integrated` → `markDone`, delete ticket directory, `clearWorkspace`.
269
+ `conflict` → `escalate({ kind: "merging" })`, keep the ticket directory.
270
+ 6. **With an Authority.** `submit` the assembled ticket. `submitted` →
278
271
  `markSubmitted`, return `paused`. `refused` → `escalate({ kind: "submitted" })`
279
272
  with the refusal's reason as its report — the Publisher's or the
280
273
  Authority's own words, the only record of why the offer failed.
@@ -283,28 +276,33 @@ One FeatureStandard, one Project. Pause flag is off.
283
276
  `escalate({ kind: "submitted" })`. Retryable refusal → `recordRefusal`
284
277
  (which also drops a stale parked refusal, if one is held),
285
278
  return `paused` (next pass repairs then aligns, then offers again).
286
- `validated` → Authority `fold`. `folded` → `markMerging` then `markDone` (with the fold's `reference`, when the Authority named one)
279
+ `validated` → ask the Authority whether the line moved on (`behind`, when
280
+ it can say): `unavailable` → `paused`; `behind` → `recordRealign` (state
281
+ `integrating`, no report kept, no fix owed) and `paused`, so the next pass
282
+ aligns, rechecks, validates, offers again and judges again; with the budget spent →
283
+ `escalate({ kind: "submitted" })`; `current`, or no `behind` → Authority
284
+ `fold`. `folded` → `markMerging` then `markDone` (with the fold's `reference`, when the Authority named one)
287
285
  (the Authority moved the work line; Conductor does not `integrate` into
288
- WorkLineStable). `conflict` → `escalate({ kind: "submitted" })`.
286
+ WorkLineStable). `conflict` → the same as `behind`: `recordRealign` and back to align
287
+ (the alignment conflicts, the fix is told); `escalate({ kind: "submitted" })` only
288
+ when the budget is spent.
289
289
 
290
290
  ## 9. Pause and interrupt
291
291
 
292
292
  Pause is an in-process flag on the opened conductor. It is not stored in the
293
- WorkLedger (pause is Conductor admission, not a FeatureStandard state).
293
+ WorkLedger (pause is Conductor admission, not a TicketStandard state).
294
294
 
295
- Boundaries where pause is honoured: before `claim`, before `startSubtask`,
295
+ Boundaries where pause is honoured: before `claim`, before the unit starts,
296
296
  before align, before `markMerging`. A running `implement` finishes.
297
297
 
298
298
  Interrupt is the Transformer Port's problem (the same `interruptFlag` the Transformers
299
- already take). If a transformer returns `interrupted` after `startSubtask`
300
- (Subtask isolate, `implement`, or fold into the feature), Conductor does not
301
- mark the Subtask integrated. It calls `releaseBail` — the Subtask becomes
302
- `runnable` from zero, the declared Subtask directory is destroyed — and
303
- returns `refused` with code `interrupted`. The next process resumes without
304
- waiting for the bail clock. Interrupt during planning, feature isolate,
305
- align, assembly, or a Submission judgement leaves the ledger as the last
306
- successful command left it: those paths resume on the next `runProject`
307
- without a held Subtask.
299
+ already take). If `implement` returns `interrupted` once the lease is taken,
300
+ Conductor does not mark the unit done. It calls `releaseBail` — the Attempt
301
+ is spent, the declared directory is destroyed — and returns `refused` with
302
+ code `interrupted`. The next process starts the unit again without waiting for
303
+ the bail clock. Interrupt during Isolation, warm, align, assembly, or a
304
+ Submission judgement leaves the ledger as the last successful command left it:
305
+ those paths resume on the next `runProject` without a held lease.
308
306
 
309
307
  ## 10. Reconcile
310
308
 
@@ -314,10 +312,10 @@ After process start, and at the beginning of every `runProject`:
314
312
  2. Delete any directory under `workspaceRoot` whose absolute path is **not**
315
313
  on that list (orphans). Never delete `workLineStable`.
316
314
  3. `expireBail` for those declared keys, and for `activeOn(project)` when that
317
- FeatureStandard's bail is past expiry. Then delete a Subtask directory if
315
+ TicketStandard's bail is past expiry. Then delete the unit's directory if
318
316
  the ledger cleared it.
319
317
 
320
- The ledger decides Feature state. Disk never does. Conductor does not scan
318
+ The ledger decides Ticket state. Disk never does. Conductor does not scan
321
319
  every Project; `runProject` reconciles the workspaces on disk plus the Project
322
320
  it was asked to run.
323
321
 
@@ -329,11 +327,9 @@ it was asked to run.
329
327
  | `root-invalid` | `workspaceRoot` is not absolute, or equals `workLineStable` |
330
328
  | `persist-failed` | A ledger command returned `persist-failed` |
331
329
  | `interrupted` | A transformer returned `interrupted` |
332
- | `isolate-failed` | Feature Isolation did not return `isolated` |
333
- | `warm-failed` | Feature warm (when configured) did not return `warmed` |
334
- | `align-conflict` | Align Integration returned `conflict`; state stays `integrating` |
330
+ | `isolate-failed` | Isolation did not return `isolated` |
331
+ | `warm-failed` | Warm (when configured) did not return `warmed` |
335
332
  | `transformer-invalid` | A transformer returned `invalid-invocation` |
336
- | `unavailable` | `breakDown` returned `unavailable` (Planner did not answer) |
337
333
 
338
334
  Ledger codes (`project-busy`, `point-of-no-return`, …) pass through on `cancel`
339
335
  and when `claim` refuses.
@@ -344,28 +340,28 @@ Shipped:
344
340
 
345
341
  - Import face, `openConductor({ ledger, transformers, workLineStable, workspaceRoot })`
346
342
  - Optional Authority (`submit` / `fold`), `workLineTarget`, `maxRefusals`
347
- - Optional local `assemblyValidate`, before offer or the local judge, with or
343
+ - Optional local `validate`, before offer or the local judge, with or
348
344
  without an Authority. Its refusal is parked on the aggregate and shares the
349
- `maxRefusals` budget; `assemblyFixDeclared` says whether it escalates on the
345
+ `maxRefusals` budget; `fixDeclared` says whether it escalates on the
350
346
  spot instead
351
347
  - `reconcile`, `pause`, `resume`, `runProject`, `cancel`, `release`
352
348
  - Happy path: align, assembly judgement (`produce: false`), `markMerging`, final fold
353
349
  - Authority path: offer, judge, `recordRefusal` / fold, no Integrator fold into WorkLineStable
354
- - `pending_fingerprint` consumed after a Subtask integrates and when entering `integrating`
350
+ - `pending_fingerprint` consumed when the unit ends and when entering `integrating`
355
351
  - Ledger-before-act, declared workspaces, orphan destroy
356
352
  - Isolation tests in §4
357
353
  - Pause boundaries
358
354
 
359
355
  Not shipped:
360
356
 
361
- - FeatureListener / FeatureAdapter / FeatureEmitter (the caller wires them
357
+ - Listener / Adapter / Emitter (the caller wires them
362
358
  through a manager package; Host's suite is the first such assembly)
363
359
  - CLI
364
360
  - Persistent pause across processes
365
- - A human `ready` daemon (caller invokes `resumeReady` then `runProject`)
361
+ - A human `resume` daemon (caller invokes `resumeTicket` then `runProject`)
366
362
  - SQLite, a second workspace layout
367
- - Consuming `pending_fingerprint` while the Feature is `submitted` (admit stores
368
- it; the next Subtask integrate / integrating entry is what consumes it)
363
+ - Consuming `pending_fingerprint` while the Ticket is `submitted` (admit stores
364
+ it; the next unit end / integrating entry is what consumes it)
369
365
 
370
366
  ## 13. Layout
371
367
 
@@ -380,51 +376,71 @@ src/
380
376
 
381
377
  ## 14. Acceptance
382
378
 
383
- 1. `runProject` on a Project with one `received` FeatureStandard calls `claim`
384
- then `breakDown` then `recordPlan` **before** any `isolate`.
385
- 2. `declareWorkspace` for the feature path is recorded **before** `isolate` of
386
- that path. Same for a Subtask path.
387
- 3. Fake transformers, one Subtask, all `validated` / `integrated`, no Authority:
388
- Feature ends `done`; `isolate` was called twice (feature, then Subtask);
389
- `integrate` was called three times (Subtask fold, align, final fold);
390
- assembly `implement` ran once on the feature workspace with `produce: false`.
391
- 4. `breakDown` `refused` → `refusePlan`; no `isolate`; outcome `escalated`.
392
- 5. `implement` `escalated` after Traces → each Trace is `recordAttempt`d;
393
- then `escalate`; no Subtask `integrate`; child directory not deleted by
394
- Conductor.
395
- 6. Subtask `integrate` `conflict` → `escalate`; child kept.
396
- 7. `cancel` of a `running` Feature → ledger `cancelled`; declared directories
379
+ 1. `runProject` on a Project with one `received` TicketStandard calls `claim`
380
+ then `isolate` then `implement`, and the workspace path is declared **before**
381
+ `isolate`.
382
+ 2. Fake transformers, all `validated` / `integrated`, no Authority: Ticket ends
383
+ `done`; `isolate` was called once; `integrate` was called twice (align, final
384
+ fold); `implement` ran twice — the unit, then the assembly judgement with
385
+ `produce: false`.
386
+ 3. A failed Isolation → `refused` (`isolate-failed`); no lease, no Attempt, no
387
+ `implement`.
388
+ 4. `implement` `escalated` after Traces → each Trace is `recordAttempt`d;
389
+ then `escalate({ kind: "unit" })`; no `integrate`; the directory is not
390
+ deleted by Conductor. After `resumeTicket` the unit starts from a fresh
391
+ workspace.
392
+ 4a. A Ticket whose admission says `no_tests` (the tracker's label, carried beside its
393
+ words) has no stage before its producer: no `implement` for the tests, no checkpoint,
394
+ and `first_stage_done` is never set. Everything else about the unit is the same.
395
+ 4b. `implement` `interrupted` (the process was stopped) → the Attempts that
396
+ finished are `recordAttempt`d (the interrupted one is not an Attempt), then
397
+ `releaseBail`: no Attempt is spent, the workspace stays declared and on disk.
398
+ The next pass over the Ticket finds a declared workspace that still holds
399
+ something and picks it up: no `isolate`, no second `warm`; the stage made
400
+ before the producer (the tests) is not made again when the ledger says it is
401
+ done; `implement` is given `firstAttempt` (the Attempts already recorded this
402
+ round, plus one) and, as `report`, the refusal of the last Attempt, or a
403
+ note that the pass before was stopped and left its work there. A declared
404
+ workspace that is gone or empty starts the unit from zero. `release` is the
405
+ way to start over: it drops the bail, destroys the directory and forgets it.
406
+ Conductor does not look inside the workspace, nor at the line it was cut
407
+ from: a Project that cannot pick up a stale one uses `release`.
408
+ 5. `cancel` of a `running` Ticket → ledger `cancelled`; declared directories
397
409
  destroyed. `cancel` after `markMerging` → ledger `point-of-no-return`;
398
410
  WorkLineStable untouched.
399
- 8. A directory under `workspaceRoot` that is not declared is gone after
411
+ 6. A directory under `workspaceRoot` that is not declared is gone after
400
412
  `reconcile`. `workLineStable` is never deleted.
401
- 9. `pause` after `recordPlan` and feature isolate, before the first
402
- `startSubtask` → outcome `paused`; no `startSubtask`.
413
+ 7. `pause` after Isolation, before the unit starts → outcome `paused`; no
414
+ `implement`.
415
+ 8. `implement` `interrupted` → lease released, directory destroyed, `refused`
416
+ (`interrupted`).
417
+ 9. A workspace that is gone while the Authority holds a Submission → escalated
418
+ `kind: "submitted"`, and no second Isolation.
403
419
  10. `src/run/` has no import of `@bluewombat/isolator`, `@bluewombat/implementer`,
404
- `@bluewombat/integrator`, or `@bluewombat/feature-breakdown`.
420
+ or `@bluewombat/integrator`.
405
421
  11. Opening Conductor with a Transformer Port test double and a WorkLedger test
406
422
  double still passes 1–9. No Transformer `dist/` and no filesystem persist are
407
423
  required for those.
408
424
 
409
- Gap, named: align `conflict` leaves `integrating` (`align-conflict`) rather than
410
- a first-class ledger kind. Do not hide that. A later WorkLedger command can
411
- close it. Assembly `escalated` uses `escalate({ kind: "assembly" })`;
412
- `resumeReady` returns that Feature to `integrating`.
425
+ An align `conflict` is not a stop: it is sent back to the fix like a refusal
426
+ (`recordParkedRefusal`), so no ledger kind is needed for it. Assembly `escalated` uses `escalate({ kind: "assembly" })`;
427
+ `resumeTicket` returns that Ticket to `integrating`; a `unit` escalation returns
428
+ it to `running`.
413
429
 
414
430
  ## 15. Choices
415
431
 
416
432
  1. **Not a Transformer.** Sequencing many verbs is not Implementer-shaped.
417
- 2. **Injected Transformer Port.** Depending on four Transformer packages would make the
433
+ 2. **Injected Transformer Port.** Depending on three Transformer packages would make the
418
434
  run loop untestable without `dist/` and would freeze Isolation strategy
419
435
  inside Conductor. The caller wires Transformers.
420
436
  3. **Injected WorkLedger.** Conductor does not open persist. Glue already chose
421
437
  the adapter.
422
438
  4. **Ledger before act, always.** Directories that exist without a declaration
423
439
  are orphans and die.
424
- 5. **No Emitter in this package.** Reporting is FeatureEmitter. Conductor
440
+ 5. **No Emitter in this package.** Reporting is Emitter. Conductor
425
441
  returns a result; the caller reports.
426
- 6. **Pause is in-process.** A FeatureStandard state for pause would confuse
427
- `escalated` and `ready`. Restart clears pause; reconcile + bail remain.
442
+ 6. **Pause is in-process.** A TicketStandard state for pause would confuse
443
+ `escalated` and `resume`. Restart clears pause; reconcile + bail remain.
428
444
  7. **Stack: TypeScript on Node.js 24.20, Biome, `node:test` run through `tsx`.**
429
445
  Same as WorkLedger. Recorded here; commands live in this package's README.
430
446
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- export type { Conductor, ConductorMoment, ConductorRefusalCode, OpenConductorOptions, ProjectRunResult, WarmOutcome, WarmPort, } from "./run/open-conductor.js";
1
+ export type { Delivery, DeliveryOptions, DeliveryRead, DeliveryStore, DeliveryUnit, } from "./run/delivery.js";
2
+ export { openDelivery, reasonOfTraces } from "./run/delivery.js";
3
+ export type { Conductor, ConductorMoment, ConductorRefusalCode, LinePort, OpenConductorOptions, ProjectRunResult, WarmOutcome, WarmPort, } from "./run/open-conductor.js";
2
4
  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
+ export { pathSegment, ticketWorkspacePath } from "./run/paths.js";
6
+ export type { AuthorityBehindResult, AuthorityFoldResult, AuthorityPort, ImplementResult, SubmitInput, SubmitResult, TransformerPort, } from "./transformers/port.js";
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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,EAChB,WAAW,EACX,QAAQ,GACT,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,aAAa,EACb,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjE,YAAY,EACV,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,QAAQ,EACR,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,QAAQ,GACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,WAAW,EACX,YAAY,EACZ,eAAe,GAChB,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { openDelivery, reasonOfTraces } from "./run/delivery.js";
1
2
  export { foldMessageOf, openConductor, subjectOf } from "./run/open-conductor.js";
2
- export { featureWorkspacePath, subtaskWorkspacePath } from "./run/paths.js";
3
+ export { pathSegment, ticketWorkspacePath } from "./run/paths.js";
3
4
  //# sourceMappingURL=index.js.map