@bluewombat/work-ledger 0.7.0 → 0.9.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 +1 -1
- package/SPECS.md +112 -93
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ledger/feature-commands.d.ts +50 -0
- package/dist/ledger/feature-commands.d.ts.map +1 -0
- package/dist/ledger/feature-commands.js +196 -0
- package/dist/ledger/feature-commands.js.map +1 -0
- package/dist/ledger/open-work-ledger.d.ts +24 -54
- package/dist/ledger/open-work-ledger.d.ts.map +1 -1
- package/dist/ledger/open-work-ledger.js +141 -290
- package/dist/ledger/open-work-ledger.js.map +1 -1
- package/dist/ledger/results.d.ts +11 -0
- package/dist/ledger/results.d.ts.map +1 -0
- package/dist/ledger/results.js +4 -0
- package/dist/ledger/results.js.map +1 -0
- package/dist/persist/port.d.ts +4 -4
- package/dist/persist/port.d.ts.map +1 -1
- package/dist/records.d.ts +90 -41
- package/dist/records.d.ts.map +1 -1
- package/dist/records.js +6 -1
- package/dist/records.js.map +1 -1
- package/package.json +2 -2
- package/dist/ledger/check-plan.d.ts +0 -17
- package/dist/ledger/check-plan.d.ts.map +0 -1
- package/dist/ledger/check-plan.js +0 -73
- package/dist/ledger/check-plan.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# WorkLedger
|
|
2
2
|
|
|
3
|
-
Kernel package: the durable store of
|
|
3
|
+
Kernel package: the durable store of TicketStandards, states,
|
|
4
4
|
Attempts, and Traces. Glue writes here before it acts.
|
|
5
5
|
|
|
6
6
|
Behavioural contract: [SPECS.md](./SPECS.md). Persistence adapters live under
|
package/SPECS.md
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Kernel package, not a Transformer. It has many operations, not one IN and one
|
|
4
4
|
OUT. Its code, identifiers, comments, and messages name only what it owns:
|
|
5
|
-
|
|
5
|
+
Ticket records, Attempts, Traces, states, bails, and the Persistence
|
|
6
6
|
Port. No Transformer is named inside that perimeter, because none of them exist
|
|
7
7
|
there.
|
|
8
8
|
|
|
9
|
-
WorkLedger is the durable store of
|
|
10
|
-
|
|
9
|
+
WorkLedger is the durable store of TicketStandards, states, Attempts, and
|
|
10
|
+
Traces. It is the system's source of truth. Glue writes here **before** it
|
|
11
11
|
acts. Isolated workspaces are a consequence of that state, never the source of
|
|
12
12
|
it.
|
|
13
13
|
|
|
14
14
|
WorkLedger does not isolate a directory, run a Builder, fold a Child, or report
|
|
15
|
-
to a
|
|
15
|
+
to a TicketTracker. It accepts an already-normalized TicketStandard (or an
|
|
16
16
|
`invalid` verdict). It does not convert raw intentions.
|
|
17
17
|
|
|
18
18
|
Callers inject a persistence adapter. Adapters are their own packages under
|
|
@@ -54,7 +54,7 @@ Transformers do not import this package. Conductor does. Host chooses the adapte
|
|
|
54
54
|
## 3. Two layers
|
|
55
55
|
|
|
56
56
|
```
|
|
57
|
-
commands / queries src/ledger/
|
|
57
|
+
commands / queries src/ledger/ TicketStandard states,
|
|
58
58
|
bail, Attempt, Trace, refusals
|
|
59
59
|
│
|
|
60
60
|
│ Persistence Port (plain records)
|
|
@@ -65,7 +65,7 @@ Transformers do not import this package. Conductor does. Host chooses the adapte
|
|
|
65
65
|
| Layer | Owns | Does not own |
|
|
66
66
|
| ------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------- |
|
|
67
67
|
| Ledger | Legal transitions, idempotence, bound counters, bail | `node:fs`, `sqlite`, `pg`, paths |
|
|
68
|
-
| Port | The record shapes that cross the boundary, and the verbs `load` / `save` / `listSummaries` |
|
|
68
|
+
| Port | The record shapes that cross the boundary, and the verbs `load` / `save` / `listSummaries` | TicketStandard state names as a state machine |
|
|
69
69
|
| Adapter | How those records are stored and listed | Whether `merging` may be cancelled |
|
|
70
70
|
|
|
71
71
|
A caller never talks to an adapter. It talks to the ledger. The ledger never
|
|
@@ -92,7 +92,7 @@ Hard rules. Breaking one is a spec bug, not a refactor.
|
|
|
92
92
|
honest. Its package does not exist until someone implements it.
|
|
93
93
|
7. **Domain tests inject a Port.** They use a test double, or an adapter package
|
|
94
94
|
on a temp path. They do not reach into adapter internals. Adapter tests do
|
|
95
|
-
not run
|
|
95
|
+
not run TicketStandard transitions.
|
|
96
96
|
|
|
97
97
|
## 5. Persistence Port
|
|
98
98
|
|
|
@@ -101,15 +101,15 @@ does not care.
|
|
|
101
101
|
|
|
102
102
|
| Operation | In | Out |
|
|
103
103
|
| --------------- | ------------------------- | ------------------------------------ |
|
|
104
|
-
| `load` |
|
|
105
|
-
| `save` |
|
|
104
|
+
| `load` | Ticket `key` | Aggregate, or missing |
|
|
105
|
+
| `save` | Ticket `key` + aggregate | Durable replace of that key. Atomic. |
|
|
106
106
|
| `listSummaries` | none | One summary per known key |
|
|
107
107
|
|
|
108
108
|
`save` replaces the whole aggregate for that key. The ledger read-modify-writes.
|
|
109
109
|
The adapter does not merge. If `save` cannot complete, it fails; it does not
|
|
110
110
|
leave a half-written key that `load` could return.
|
|
111
111
|
|
|
112
|
-
`listSummaries` exists so `claim` can pick the next `received`
|
|
112
|
+
`listSummaries` exists so `claim` can pick the next `received` TicketStandard
|
|
113
113
|
without loading every Trace. A summary carries at least: `key`, `project`,
|
|
114
114
|
`state`, `priority`, `received_at`, bail expiry if any.
|
|
115
115
|
|
|
@@ -135,7 +135,7 @@ segment (`fake%3A42.json`). The adapter never uses the raw key as a filename.
|
|
|
135
135
|
file or the new file, never a truncated JSON document that parses as a
|
|
136
136
|
different aggregate.
|
|
137
137
|
|
|
138
|
-
This adapter knows directories and JSON. It does not know `merging`, `
|
|
138
|
+
This adapter knows directories and JSON. It does not know `merging`, `resume`,
|
|
139
139
|
or Isolator.
|
|
140
140
|
|
|
141
141
|
## 7. Adapter packages
|
|
@@ -154,53 +154,46 @@ through the Port.
|
|
|
154
154
|
|
|
155
155
|
## 8. Aggregate
|
|
156
156
|
|
|
157
|
-
One aggregate per
|
|
157
|
+
One aggregate per TicketStandard `key` (`<manager>:<external_id>`). The Adapter
|
|
158
158
|
produced `key` and `fingerprint`; WorkLedger owns everything else.
|
|
159
159
|
|
|
160
160
|
| Part | Content |
|
|
161
161
|
| ------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
162
162
|
| Identity | `key`, `project`, `fingerprint` |
|
|
163
|
-
| Intention |
|
|
164
|
-
|
|
|
163
|
+
| Intention | TicketStandard fields this package stores (intention, title, priority, source, …) and its place in a Feature: `feature`, `blocked_by` (Tickets that must be `done` first), `line` (the line it lands on) |
|
|
164
|
+
| Ticket state | Exactly one of §9 |
|
|
165
165
|
| Invalid | `code` + `reason` when state is `invalid` |
|
|
166
|
-
| Pending fingerprint | Set when an update arrives during `
|
|
167
|
-
|
|
|
168
|
-
| Subtask states | Per Subtask, exactly one of §9 |
|
|
169
|
-
| Attempts | Numbered, each with a Trace when an Attempt ran, and stamped with the round (`resumes` at the time) it belongs to |
|
|
166
|
+
| Pending fingerprint | Set when an update arrives during `running` / `integrating`; applied only after the in-flight work stops |
|
|
167
|
+
| Attempts | Numbered from 1, each with a Trace when an Attempt ran, and stamped with the round (`resumes` at the time) it belongs to |
|
|
170
168
|
| Bound counters | Attempts used, as this package tracks them |
|
|
171
|
-
| Resumes | How many `
|
|
169
|
+
| Resumes | How many `resumeTicket` were taken. A round tells two escalations of one kind apart |
|
|
172
170
|
| Parked refusal | A report `assembly.validate` sent back, held on the aggregate because there may be no Submission yet. Present only while `integrating`; cleared once validate accepts |
|
|
173
171
|
| Parked refusals | How many times `assembly.validate` sent the work back with no Submission's own counter to hold it. Shares the `maxRefusals` budget with `submission.refusals` |
|
|
174
|
-
| Bail | Optional. `expires_at`
|
|
175
|
-
| Declared
|
|
172
|
+
| Bail | Optional. `expires_at` while a unit is held (from `startUnit`) and while the work is assembled and judged |
|
|
173
|
+
| Declared workspace | Optional absolute path glue wrote **before** creating that directory |
|
|
176
174
|
|
|
177
|
-
Unknown fields from a
|
|
175
|
+
Unknown fields from a TicketStandard that Adapter would drop are not
|
|
178
176
|
invented here either. WorkLedger does not reopen the Adapter's closed shape.
|
|
179
177
|
|
|
180
|
-
|
|
181
|
-
rewritten; a new Attempt is appended.
|
|
178
|
+
An Attempt is never rewritten; a new Attempt is appended.
|
|
182
179
|
|
|
183
180
|
## 9. States
|
|
184
181
|
|
|
185
|
-
|
|
182
|
+
TicketStandard states, copied from the product glossary. WorkLedger is the
|
|
186
183
|
component that actually holds them.
|
|
187
184
|
|
|
188
185
|
| State | Meaning |
|
|
189
186
|
| ------------- | -------------------------------------------------- |
|
|
190
187
|
| `received` | Admitted, not yet claimed |
|
|
191
188
|
| `invalid` | Stored as unusable, with a code; not silent |
|
|
192
|
-
| `
|
|
193
|
-
| `running` | Subtask loop (bail held) |
|
|
189
|
+
| `running` | The unit is made (bail held once it starts) |
|
|
194
190
|
| `escalated` | Frozen for a human |
|
|
195
|
-
| `integrating` |
|
|
191
|
+
| `integrating` | The unit passed its Gates; assembly Gates |
|
|
196
192
|
| `submitted` | Offered to an Authority; waiting on its judgement |
|
|
197
193
|
| `merging` | Final fold into WorkLineStable. Point of no return |
|
|
198
194
|
| `done` | Terminal. In WorkLineStable |
|
|
199
195
|
| `cancelled` | Terminal. Abandoned before final integration |
|
|
200
196
|
|
|
201
|
-
Subtask states: `pending`, `runnable`, `running`, `escalated`, `integrated`,
|
|
202
|
-
`cancelled`.
|
|
203
|
-
|
|
204
197
|
A command that would require a transition not in this section is `refused`.
|
|
205
198
|
The aggregate is unchanged.
|
|
206
199
|
|
|
@@ -214,43 +207,39 @@ Idempotence is on `key` unless a row says otherwise.
|
|
|
214
207
|
|
|
215
208
|
| Command | Meaning | Refused when |
|
|
216
209
|
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
217
|
-
| `admit` | Store a
|
|
210
|
+
| `admit` | Store a TicketStandard in `received`. Same `key` already `received` with the same fingerprint → success, no rewrite. Same `key`, different fingerprint, not in-flight → update the stored intention. Same `key` in-flight (`running` / `integrating` / `submitted`) with the same fingerprint (or the fingerprint already pending) → success, no rewrite. A different fingerprint while in-flight is recorded as pending; the live work is not rewritten. | `key` in `merging` or `done`; `key` in `cancelled` |
|
|
218
211
|
| `recordInvalid` | Store `invalid` with code, reason and the fingerprint of what was received | `key` already in `merging` or `done` |
|
|
219
|
-
| `claim` | Take the next `received`
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
| `recordRefusal` | Authority / assembly Gates sent the work back. Increment `refusals`, store `last_report`, Feature → `integrating`. Clears a stale parked refusal, if one is held | Not `submitted` |
|
|
227
|
-
| `recordParkedRefusal` | `assembly.validate` sent the work back before any Submission exists. Store the parked report, increment `parked_refusals` | Not `integrating` |
|
|
212
|
+
| `claim` | Take the next `received` TicketStandard for a Project (priority then `received_at`). State → `running`, with no bail yet. No-op with `empty` when none | That Project already has `running`, `escalated`, `integrating`, `submitted`, or `merging` |
|
|
213
|
+
| `startUnit` | The unit starts: take the bail | Not `running`; a bail is already held (`another-running`) |
|
|
214
|
+
| `recordAttempt` | Append an Attempt + Trace (`ended`, optional `report` and `refusedBy`) | Not `running`; Attempt number not the next integer |
|
|
215
|
+
| `markIntegrating` | The unit passed its Gates. State → `integrating`, bail renewed | Not `running` |
|
|
216
|
+
| `markSubmitted` | Ticket → `submitted`. Records the Authority's opaque `reference`. A second call keeps the first reference and any `last_report` | Not `integrating` or `submitted` |
|
|
217
|
+
| `recordRefusal` | Authority / assembly Gates sent the work back. Increment `refusals`, store `last_report`, Ticket → `integrating`. Clears a stale parked refusal, if one is held | Not `submitted` |
|
|
218
|
+
| `recordParkedRefusal` | `assembly.validate` sent the work back before any Submission exists. Store the parked report, increment `parked_refusals` | Not `integrating` |
|
|
228
219
|
| `clearParkedRefusal` | `assembly.validate` accepted. Drop the parked report; the counter stays — it is the budget already spent | — |
|
|
229
|
-
| `markMerging` |
|
|
230
|
-
| `markDone` |
|
|
231
|
-
| `escalate` |
|
|
232
|
-
| `
|
|
233
|
-
| `cancel` |
|
|
234
|
-
| `declareWorkspace` | Record
|
|
235
|
-
| `clearWorkspace` | Drop
|
|
236
|
-
| `expireBail` | Bail past expiry:
|
|
237
|
-
| `releaseBail` | Same ledger outcome as an expired bail
|
|
220
|
+
| `markMerging` | Ticket → `merging` | Not `integrating` or `submitted` |
|
|
221
|
+
| `markDone` | Ticket → `done`. Records the fold's `reference` when given (the work line's own name for it, opaque). Clear bail | Not `merging` |
|
|
222
|
+
| `escalate` | Ticket → `escalated`. For kind `unit`, a Trace is required if an Attempt exists. Consumes `pending_fingerprint` | Ticket already `done` or `cancelled`; a `unit` escalation when the last Attempt has no Trace |
|
|
223
|
+
| `resumeTicket` | Human `resume`. Escalation `unit` → `running` from zero, no bail. Escalation from `merging`, `submitted`, or `assembly` → `integrating`. Counts a round | Not `escalated` or `invalid`; `invalid` without a new admissible TicketStandard |
|
|
224
|
+
| `cancel` | Ticket → `cancelled` | `merging` or `done`; an escalation that was born in `merging` |
|
|
225
|
+
| `declareWorkspace` | Record the workspace path **before** glue creates it | Ticket `done` or `cancelled` |
|
|
226
|
+
| `clearWorkspace` | Drop the declared path after glue deleted that directory | — |
|
|
227
|
+
| `expireBail` | Bail past expiry: a `running` Ticket starts the unit over (consume an Attempt, forget the workspace); any other state only loses its bail | No bail, or bail not expired |
|
|
228
|
+
| `releaseBail` | Same ledger outcome as an expired bail on a `running` Ticket, without waiting for the clock. A `running` Ticket with no bail is success with no rewrite | Ticket not `running` |
|
|
238
229
|
| `renewBail` | Push bail expiry forward by the configured duration. Allowed while a bail is held | No bail |
|
|
239
230
|
|
|
240
|
-
`
|
|
231
|
+
`resumeTicket` from `invalid` requires `admit` of a corrected TicketStandard
|
|
241
232
|
first (`invalid` → `received` is `admit` after correction, then claim).
|
|
242
233
|
|
|
243
|
-
In-flight update (`admit` while `
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
integrated, and when entering `integrating`, if the field is set. WorkLedger
|
|
234
|
+
In-flight update (`admit` while `running` / `integrating` / `submitted`): same
|
|
235
|
+
fingerprint as the live intention, or as the fingerprint already pending →
|
|
236
|
+
success, no rewrite (an echo of a report). A different fingerprint is stored as
|
|
237
|
+
pending; do not rewrite the live intention. `escalate` consumes
|
|
238
|
+
`pending_fingerprint`. Conductor calls `escalate({ kind: "unit" })` after the
|
|
239
|
+
unit ends, and when entering `integrating`, if the field is set. WorkLedger
|
|
250
240
|
does not watch a Transformer's clock.
|
|
251
241
|
|
|
252
|
-
Escalation `kind` is one of `
|
|
253
|
-
`merging`.
|
|
242
|
+
Escalation `kind` is one of `unit`, `assembly`, `submitted`, `merging`.
|
|
254
243
|
|
|
255
244
|
## 11. Queries
|
|
256
245
|
|
|
@@ -258,34 +247,68 @@ Escalation `kind` is one of `plan`, `subtask`, `assembly`, `submitted`,
|
|
|
258
247
|
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
259
248
|
| `get` | Aggregate for a `key`, or missing |
|
|
260
249
|
| `list` | One summary per known key, every state. No persist, no bail, no transition |
|
|
261
|
-
| `listInFlight` | The `list` subset that is mid-flight (`
|
|
262
|
-
| `activeOn` | The active
|
|
250
|
+
| `listInFlight` | The `list` subset that is mid-flight (`running` / `integrating` / `submitted`) |
|
|
251
|
+
| `activeOn` | The active TicketStandard on a Project (`running` / `escalated` / `integrating` / `submitted` / `merging`), or none |
|
|
263
252
|
| `nextReceived` | What `claim` would take, without taking it |
|
|
264
|
-
| `nextRunnable` | A `runnable` Subtask of a Feature (ties: the one that unblocks the most others). Glue still serializes |
|
|
265
253
|
| `listDeclaredWorkspaces` | Keys and paths the ledger believes exist. Glue reconciles disk to this list. Orphans on disk that are not listed are glue's to destroy |
|
|
266
254
|
|
|
267
255
|
Queries do not persist. They do not take a bail.
|
|
268
256
|
|
|
257
|
+
### Features
|
|
258
|
+
|
|
259
|
+
A Feature is stored through the same Port, beside the Tickets, with
|
|
260
|
+
`kind: "feature"`. Ticket queries (`list`, `listInFlight`, `activeOn`,
|
|
261
|
+
`nextReceived`, `listDeclaredWorkspaces`) never return it; its own queries do.
|
|
262
|
+
|
|
263
|
+
| Feature state | Meaning |
|
|
264
|
+
| ------------- | ------------------------------------------------------- |
|
|
265
|
+
| `waiting` | Admitted, not yet opened |
|
|
266
|
+
| `open` | Its line exists and its Tickets are made, one at a time |
|
|
267
|
+
| `submitted` | Its line is with the Authority |
|
|
268
|
+
| `escalated` | Frozen for a human |
|
|
269
|
+
| `done` | Terminal. Its line is folded |
|
|
270
|
+
| `cancelled` | Terminal |
|
|
271
|
+
|
|
272
|
+
| Command | Meaning | Refused when |
|
|
273
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
274
|
+
| `admitFeature` | Store as `waiting`. While waiting, an edit replaces the intention; once held (`open` / `submitted` / `escalated`), it is recorded as pending and the echo of an edit is a success | `done` (`too-late`); `cancelled` (`illegal-transition`) |
|
|
275
|
+
| `openFeature` | `waiting` → `open`, recording the line and the keys of its Tickets | Not `waiting`; the Project already holds a Feature (`project-busy`) |
|
|
276
|
+
| `markFeatureSubmitted` | `open` → `submitted`, recording the Authority's opaque `reference` | Not `open` |
|
|
277
|
+
| `markFeatureDone` | `submitted` → `done`, recording the fold's reference when given | Not `submitted` |
|
|
278
|
+
| `escalateFeature` | `open` or `submitted` → `escalated` with the report; consumes a pending edit | Any other state |
|
|
279
|
+
| `resumeFeature` | `escalated` → `open`, counting a round. The Submission, if there is one, stays | Not `escalated` |
|
|
280
|
+
| `cancelFeature` | Any state but `done` → `cancelled` | `done` (`point-of-no-return`) |
|
|
281
|
+
|
|
282
|
+
| Query | Out |
|
|
283
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
284
|
+
| `getFeature` | The aggregate, or missing |
|
|
285
|
+
| `listFeatures` | One summary per Feature |
|
|
286
|
+
| `activeFeatureOn` | The Feature that holds a Project: `open`, `submitted` or `escalated` |
|
|
287
|
+
| `nextFeature` | What would open next: `waiting`, every Feature it waits on `done`, highest priority then oldest. Nothing while a Feature holds the Project |
|
|
288
|
+
|
|
289
|
+
A Ticket that waits on others is not claimed until each is `done`; a blocker the
|
|
290
|
+
ledger does not know blocks. `claim` and `nextReceived` skip a received Ticket
|
|
291
|
+
that may not start and take the next one that may.
|
|
292
|
+
|
|
269
293
|
## 12. Bail and reconcile
|
|
270
294
|
|
|
271
|
-
While a
|
|
272
|
-
or `merging`, it holds a bail (`expires_at`). There is no holder id
|
|
273
|
-
implementation. Glue renews
|
|
274
|
-
|
|
275
|
-
Attempt.
|
|
295
|
+
While a unit is held (`startUnit`) and while a TicketStandard is `integrating`,
|
|
296
|
+
`submitted`, or `merging`, it holds a bail (`expires_at`). There is no holder id
|
|
297
|
+
in this implementation. Glue renews with `startUnit` / `markIntegrating` /
|
|
298
|
+
`markSubmitted`; `renewBail` exists if expiry would otherwise pass during a
|
|
299
|
+
long Attempt.
|
|
276
300
|
|
|
277
|
-
Expired bail: `expireBail`.
|
|
278
|
-
consumes an Attempt.
|
|
301
|
+
Expired bail: `expireBail`. A `running` Ticket starts the unit over from zero
|
|
302
|
+
and consumes an Attempt. The declared workspace is forgotten (glue deletes the
|
|
279
303
|
directory). The ledger decides; glue acts.
|
|
280
304
|
|
|
281
305
|
A clean stop (or an operator) that knows the holder is gone calls
|
|
282
|
-
`releaseBail`: the same `
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
`running` is success with no rewrite.
|
|
306
|
+
`releaseBail`: the same `running` outcome, without the clock. It refuses
|
|
307
|
+
`integrating`, `submitted`, `merging`, and every terminal state — those do not
|
|
308
|
+
hold a unit. A `running` Ticket that holds no bail is success with no rewrite.
|
|
286
309
|
|
|
287
310
|
After a process start, glue calls `listDeclaredWorkspaces` and destroys
|
|
288
|
-
directories that are not on that list. It does not invent
|
|
311
|
+
directories that are not on that list. It does not invent Ticket state from
|
|
289
312
|
disk.
|
|
290
313
|
|
|
291
314
|
## 13. Refusals
|
|
@@ -297,11 +320,8 @@ A refused command leaves the store as it was. `code` is one token.
|
|
|
297
320
|
| `not-found` | `key` is unknown |
|
|
298
321
|
| `duplicate` | Reserved; `admit` of an identical fingerprint is success, not this |
|
|
299
322
|
| `illegal-transition` | State does not allow this command |
|
|
300
|
-
| `project-busy` | `claim` while that Project already has an active
|
|
301
|
-
| `
|
|
302
|
-
| `plan-not-frozen` | A Subtask command before `recordPlan` |
|
|
303
|
-
| `subtask-not-runnable` | `startSubtask` on a Subtask whose dependencies are not `integrated` |
|
|
304
|
-
| `another-running` | `startSubtask` while one Subtask is already `running` |
|
|
323
|
+
| `project-busy` | `claim` while that Project already has an active TicketStandard; `openFeature` while it holds a Feature |
|
|
324
|
+
| `another-running` | `startUnit` while a unit is already held |
|
|
305
325
|
| `missing-trace` | `escalate` after an Attempt with no Trace |
|
|
306
326
|
| `point-of-no-return` | `cancel` in `merging` or `done`, or abandon after merging-born escalation |
|
|
307
327
|
| `too-late` | `admit` / `recordInvalid` / intention edit in `done` |
|
|
@@ -345,22 +365,21 @@ directories here.
|
|
|
345
365
|
|
|
346
366
|
## 16. Acceptance
|
|
347
367
|
|
|
348
|
-
1. `admit` of a new
|
|
368
|
+
1. `admit` of a new TicketStandard → `get` returns it in `received` with that
|
|
349
369
|
`key` and `fingerprint`.
|
|
350
370
|
2. `admit` of the same `key` and fingerprint again → success, aggregate
|
|
351
371
|
unchanged (byte-for-byte on the stored fingerprint and intention).
|
|
352
372
|
3. `admit` of the same `key` with a new fingerprint while `received` → stored
|
|
353
373
|
intention updates.
|
|
354
374
|
4. `admit` while `running` with a new fingerprint → pending fingerprint set;
|
|
355
|
-
|
|
375
|
+
Ticket state still `running`. Same fingerprint
|
|
356
376
|
while `running` or `submitted` → success, no pending field.
|
|
357
|
-
5. `claim` on a Project with one `received` → that
|
|
358
|
-
|
|
359
|
-
6. `
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
`integrating`.
|
|
377
|
+
5. `claim` on a Project with one `received` → that Ticket is `running`, with
|
|
378
|
+
no bail. A second `claim` on the same Project → `project-busy`.
|
|
379
|
+
6. `startUnit` takes the bail once; a second call → `another-running`.
|
|
380
|
+
7. `recordAttempt` numbers the Attempts from 1; a gap → `illegal-transition`.
|
|
381
|
+
8. `markIntegrating` on a `running` Ticket → `integrating`; on any other
|
|
382
|
+
state → `illegal-transition`.
|
|
364
383
|
9. `cancel` in `merging` → `point-of-no-return`; state still `merging`.
|
|
365
384
|
10. `escalate` after `recordAttempt` without a Trace → `missing-trace`; state
|
|
366
385
|
unchanged.
|
|
@@ -382,8 +401,8 @@ consequence: another answer was possible.
|
|
|
382
401
|
|
|
383
402
|
1. **Not a Transformer.** A store with many verbs is not IN/OUT-shaped. One IN
|
|
384
403
|
and one OUT would force a toolbox CLI and hide the transition table.
|
|
385
|
-
2. **Commands, not `set(state)`.** The product rules (
|
|
386
|
-
|
|
404
|
+
2. **Commands, not `set(state)`.** The product rules (one active
|
|
405
|
+
TicketStandard per Project, no abandon in `merging`) live here or they live
|
|
387
406
|
nowhere.
|
|
388
407
|
3. **Injected Persistence Port.** Sniffing env or a path to pick SQLite later
|
|
389
408
|
would couple the ledger to ops. Glue chooses; the ledger stays portable.
|
|
@@ -394,12 +413,12 @@ consequence: another answer was possible.
|
|
|
394
413
|
verbs would leak SQL into the ledger. Adapters may split storage internally.
|
|
395
414
|
6. **Filesystem and SQLite both ship.** Enough to be truth on disk or in one
|
|
396
415
|
file. Postgres is an adapter, not a prerequisite.
|
|
397
|
-
7. **Keys encoded for filenames.**
|
|
416
|
+
7. **Keys encoded for filenames.** Adapter keys contain `:`. Using them
|
|
398
417
|
raw would create extra directories or illegal paths.
|
|
399
418
|
8. **Refused commands do not persist.** A failed transition must not look like
|
|
400
419
|
history.
|
|
401
420
|
9. **`persist-failed` is not `invalid`.** Disk or database blips must not brand
|
|
402
|
-
a
|
|
421
|
+
a TicketStandard unusable.
|
|
403
422
|
10. **Declared workspaces are ledger facts, not discoveries.** Glue writes the
|
|
404
423
|
path before creating the directory, so crash reconcile has a list that
|
|
405
424
|
disk cannot lie about.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
export type { ClaimResult,
|
|
1
|
+
export type { FeatureAdmission, FeatureCommands, FeatureGetResult, } from "./ledger/feature-commands.js";
|
|
2
|
+
export type { ClaimResult, CommandOk, CommandRefused, CommandResult, DeclaredWorkspace, DeclareWorkspaceInput, EscalateInput, GetResult, OpenWorkLedgerOptions, RecordAttemptInput, TicketAdmission, WorkLedger, } from "./ledger/open-work-ledger.js";
|
|
3
3
|
export { openWorkLedger } from "./ledger/open-work-ledger.js";
|
|
4
4
|
export type { PersistencePort, PersistSaveResult } from "./persist/port.js";
|
|
5
5
|
export type * from "./records.js";
|
|
6
|
+
export { isFeatureAggregate, isFeatureSummary } from "./records.js";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,SAAS,EACT,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,UAAU,GACX,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,mBAAmB,cAAc,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PersistencePort } from "../persist/port.js";
|
|
2
|
+
import type { FeatureAggregate, FeatureIntention, FeatureSummary } from "../records.js";
|
|
3
|
+
import { type CommandResult } from "./results.js";
|
|
4
|
+
export type FeatureAdmission = FeatureIntention;
|
|
5
|
+
export type FeatureGetResult = {
|
|
6
|
+
ok: true;
|
|
7
|
+
aggregate: FeatureAggregate;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
code: "not-found";
|
|
11
|
+
};
|
|
12
|
+
export type FeatureCommands = {
|
|
13
|
+
/** Store a Feature as `waiting`. An edit while it waits replaces the intention; while it is held, it is recorded as pending. */
|
|
14
|
+
admitFeature(feature: FeatureAdmission): Promise<CommandResult>;
|
|
15
|
+
/** What would open next on the Project: waiting, every blocker `done`, highest priority then oldest. Nothing while the Project holds a Feature. */
|
|
16
|
+
nextFeature(project: string): Promise<FeatureSummary | undefined>;
|
|
17
|
+
/** `waiting` → `open`, with the line its Tickets land on (none without an Authority) and the keys of those Tickets. */
|
|
18
|
+
openFeature(input: {
|
|
19
|
+
key: string;
|
|
20
|
+
line?: string;
|
|
21
|
+
tickets: string[];
|
|
22
|
+
}): Promise<CommandResult>;
|
|
23
|
+
/** `open` → `submitted`: the line is with the Authority. */
|
|
24
|
+
markFeatureSubmitted(input: {
|
|
25
|
+
key: string;
|
|
26
|
+
reference: string;
|
|
27
|
+
}): Promise<CommandResult>;
|
|
28
|
+
/** `submitted` → `done`. */
|
|
29
|
+
markFeatureDone(key: string, input?: {
|
|
30
|
+
reference?: string | undefined;
|
|
31
|
+
}): Promise<CommandResult>;
|
|
32
|
+
/** `open` or `submitted` → `escalated`, for a human. Consumes a pending edit. */
|
|
33
|
+
escalateFeature(input: {
|
|
34
|
+
key: string;
|
|
35
|
+
report?: string;
|
|
36
|
+
}): Promise<CommandResult>;
|
|
37
|
+
/** `escalated` → `open`, a round later. */
|
|
38
|
+
resumeFeature(key: string): Promise<CommandResult>;
|
|
39
|
+
/** Anything but `done` → `cancelled`. */
|
|
40
|
+
cancelFeature(key: string): Promise<CommandResult>;
|
|
41
|
+
getFeature(key: string): Promise<FeatureGetResult>;
|
|
42
|
+
listFeatures(): Promise<FeatureSummary[]>;
|
|
43
|
+
/** The Feature that holds the Project: `open`, `submitted` or `escalated`. */
|
|
44
|
+
activeFeatureOn(project: string): Promise<FeatureSummary | undefined>;
|
|
45
|
+
};
|
|
46
|
+
export declare function openFeatureCommands(options: {
|
|
47
|
+
persist: PersistencePort;
|
|
48
|
+
now: () => number;
|
|
49
|
+
}): FeatureCommands;
|
|
50
|
+
//# sourceMappingURL=feature-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-commands.d.ts","sourceRoot":"","sources":["../../src/ledger/feature-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAEV,gBAAgB,EAChB,gBAAgB,EAEhB,cAAc,EACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,KAAK,aAAa,EAAW,MAAM,cAAc,CAAC;AAK3D,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEhD,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,gBAAgB,CAAA;CAAE,GACzC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAErC,MAAM,MAAM,eAAe,GAAG;IAC5B,gIAAgI;IAChI,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAChE,mJAAmJ;IACnJ,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAClE,uHAAuH;IACvH,WAAW,CAAC,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9F,4DAA4D;IAC5D,oBAAoB,CAAC,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACxF,4BAA4B;IAC5B,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACjG,iFAAiF;IACjF,eAAe,CAAC,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACjF,2CAA2C;IAC3C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACnD,yCAAyC;IACzC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACnD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnD,YAAY,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC1C,8EAA8E;IAC9E,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;CACvE,CAAC;AA2BF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,OAAO,EAAE,eAAe,CAAC;IACzB,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB,GAAG,eAAe,CA2LlB"}
|