trekoon 0.3.3 → 0.3.5

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.
@@ -1,11 +1,11 @@
1
1
  # Machine contracts
2
2
 
3
- Use `--toon` for production agent loops. Use `--json` only when an integration
4
- explicitly requires JSON.
3
+ Use `--toon` for agent loops. Use `--json` only when an integration explicitly
4
+ requires JSON.
5
5
 
6
6
  ## Base envelope
7
7
 
8
- All machine responses use the same top-level shape:
8
+ Every machine response uses the same top-level shape:
9
9
 
10
10
  ```text
11
11
  ok: true|false
@@ -16,23 +16,20 @@ metadata:
16
16
  requestId: req-<stable-id>
17
17
  ```
18
18
 
19
- Most subcommand identifiers are dot-namespaced, such as `task.list` or
20
- `sync.pull`. Root-level commands may use single-token IDs such as `help`,
21
- `init`, `quickstart`, `wipe`, or `version`.
19
+ Most subcommand IDs are dot-namespaced (`task.list`, `sync.pull`). Root-level
20
+ commands use single tokens (`help`, `init`, `quickstart`, `wipe`, `version`).
22
21
 
23
- Additional metadata may appear when relevant:
22
+ Additional metadata appears when relevant:
24
23
 
25
24
  - `metadata.compatibility` when `--compat` mode is active
26
25
  - `meta.storageRootDiagnostics` when storage resolves from a non-canonical cwd
27
26
 
28
- ## Ready queue contract
27
+ ## Ready queue
29
28
 
30
29
  ```bash
31
30
  trekoon --toon task ready --limit 3
32
31
  ```
33
32
 
34
- Payload fields:
35
-
36
33
  ```text
37
34
  ok: true
38
35
  command: task.ready
@@ -58,8 +55,6 @@ data:
58
55
  trekoon --toon dep reverse <task-or-subtask-id>
59
56
  ```
60
57
 
61
- Payload fields:
62
-
63
58
  ```text
64
59
  ok: true
65
60
  command: dep.reverse
@@ -69,22 +64,19 @@ data:
69
64
  blockedNodes[]: { id, kind, distance, isDirect }
70
65
  ```
71
66
 
72
- ## Pagination contract for list calls
67
+ ## Pagination
73
68
 
74
69
  ```bash
75
70
  trekoon --toon task list --status todo --limit 2
76
71
  trekoon --toon task list --status todo --limit 2 --cursor 2
77
72
  ```
78
73
 
79
- Cursor rules:
74
+ Rules:
80
75
 
81
76
  - `--cursor <n>` is offset-like pagination for `epic list`, `task list`, and
82
77
  `subtask list`
83
- - do not combine `--all` with `--cursor`
84
- - machine consumers should page using `meta.pagination.hasMore` and
85
- `meta.pagination.nextCursor`
86
-
87
- Payload fields:
78
+ - Don't combine `--all` with `--cursor`
79
+ - Page using `meta.pagination.hasMore` and `meta.pagination.nextCursor`
88
80
 
89
81
  ```text
90
82
  ok: true
@@ -98,14 +90,14 @@ meta:
98
90
  pagination: { hasMore, nextCursor }
99
91
  ```
100
92
 
101
- ## Descendant cascade update contract
93
+ ## Descendant cascade update
102
94
 
103
95
  ```bash
104
96
  trekoon --toon epic update <epic-id> --all --status done
105
97
  trekoon --toon task update <task-id> --all --status todo
106
98
  ```
107
99
 
108
- Success payload fields for epic/task cascade mode:
100
+ Success:
109
101
 
110
102
  ```text
111
103
  ok: true
@@ -129,7 +121,7 @@ data:
129
121
  changedSubtasks
130
122
  ```
131
123
 
132
- Failure contract for blocked epic/task cascade mode:
124
+ Failure (blocked descendants):
133
125
 
134
126
  ```text
135
127
  ok: false
@@ -157,16 +149,13 @@ data:
157
149
 
158
150
  Notes:
159
151
 
160
- - `subtask update <subtask-id> --all --status done|todo` is accepted, but it
161
- returns the normal single-subtask `subtask.update` payload because there are
162
- no descendants to traverse
163
- - Cascade mode is reserved for status-only close/reopen operations; combine
164
- append/title/description changes in separate commands
152
+ - `subtask update <subtask-id> --all --status done|todo` is accepted but
153
+ returns the normal single-subtask payload (no descendants to traverse)
154
+ - Cascade is status-only; use separate commands for append/title/description
165
155
 
166
- ## Batch create and expand payloads
156
+ ## Batch create and expand
167
157
 
168
- Trekoon uses stable batch payloads for one-shot graph creation and sibling batch
169
- creation commands.
158
+ Stable batch payloads for one-shot graph creation and sibling batch commands.
170
159
 
171
160
  ### `epic create` and `epic expand`
172
161
 
@@ -175,8 +164,6 @@ trekoon --toon epic create --title "..." --description "..." --task "..."
175
164
  trekoon --toon epic expand <epic-id> --task "..."
176
165
  ```
177
166
 
178
- Payload fields:
179
-
180
167
  ```text
181
168
  ok: true
182
169
  command: epic.create | epic.expand
@@ -197,8 +184,6 @@ data:
197
184
  trekoon --toon task create-many --epic <epic-id> --task "..."
198
185
  ```
199
186
 
200
- Payload fields:
201
-
202
187
  ```text
203
188
  ok: true
204
189
  command: task.create-many
@@ -215,8 +200,6 @@ data:
215
200
  trekoon --toon subtask create-many --task <task-id> --subtask "..."
216
201
  ```
217
202
 
218
- Payload fields:
219
-
220
203
  ```text
221
204
  ok: true
222
205
  command: subtask.create-many
@@ -229,57 +212,54 @@ data:
229
212
 
230
213
  ## Sync compatibility mode
231
214
 
232
- Compatibility mode exists for integrations that still consume legacy sync
233
- command IDs:
215
+ For integrations that still use legacy sync command IDs:
234
216
 
235
217
  ```bash
236
218
  trekoon --json --compat legacy-sync-command-ids sync status
237
219
  trekoon --toon --compat legacy-sync-command-ids sync pull --from main
238
220
  ```
239
221
 
240
- Behavior:
222
+ - Default output uses canonical dotted IDs (`sync.status`)
223
+ - Compat mode rewrites to legacy forms (`sync_status`)
224
+ - Machine-only, valid only for `sync` commands
225
+ - Output includes `metadata.compatibility` with migration guidance and removal
226
+ timing
241
227
 
242
- - default output uses canonical dotted IDs such as `sync.status`
243
- - compatibility mode rewrites sync command IDs to legacy forms such as
244
- `sync_status`
245
- - compatibility mode is machine-only and valid only for `sync` commands
246
- - machine output includes `metadata.compatibility` with migration guidance and
247
- removal timing
248
-
249
- ## Compact envelope mode
228
+ ## Compact envelope
250
229
 
251
230
  ```bash
252
231
  trekoon --toon --compact task list
253
232
  ```
254
233
 
255
- When `--compact` is passed, the `metadata` key is omitted from the TOON/JSON
256
- envelope. The `ok`, `command`, `data`, `error`, and `meta` keys are unaffected.
234
+ `--compact` omits the `metadata` key from the envelope. `ok`, `command`, `data`,
235
+ `error`, and `meta` are unaffected.
257
236
 
258
- ## Status transition error contract
237
+ ## Status transition errors
259
238
 
260
- Invalid status transitions return:
239
+ Invalid transitions return:
261
240
 
262
241
  ```text
263
242
  ok: false
264
243
  error:
265
244
  code: status_transition_invalid
266
245
  message: "cannot transition <kind> <id> from '<from>' to '<to>'"
267
- details:
268
- entity: epic|task|subtask
269
- id: <entity-id>
270
- fromStatus: <current-status>
271
- toStatus: <attempted-status>
272
- allowedTransitions[]: <valid targets from current status>
246
+ data:
247
+ entity: epic|task|subtask
248
+ id: <entity-id>
249
+ fromStatus: <current-status>
250
+ toStatus: <attempted-status>
251
+ allowedTransitions[]: <valid targets from current status>
273
252
  ```
274
253
 
275
- ## Epic progress contract
254
+ Transition details are in `data`, not `error.details`. `error` only has `code`
255
+ and `message`.
256
+
257
+ ## Epic progress
276
258
 
277
259
  ```bash
278
260
  trekoon --toon epic progress <epic-id>
279
261
  ```
280
262
 
281
- Payload fields:
282
-
283
263
  ```text
284
264
  ok: true
285
265
  command: epic.progress
@@ -295,14 +275,12 @@ data:
295
275
  nextCandidate: { id, title } | null
296
276
  ```
297
277
 
298
- ## Task done enhanced contract
278
+ ## Task done (enhanced)
299
279
 
300
280
  ```bash
301
281
  trekoon --toon task done <task-id>
302
282
  ```
303
283
 
304
- Payload fields:
305
-
306
284
  ```text
307
285
  ok: true
308
286
  command: task.done
@@ -324,14 +302,12 @@ data:
324
302
  blockedCount
325
303
  ```
326
304
 
327
- ## Suggest command contract
305
+ ## Suggest
328
306
 
329
307
  ```bash
330
308
  trekoon --toon suggest [--epic <epic-id>]
331
309
  ```
332
310
 
333
- Payload fields:
334
-
335
311
  ```text
336
312
  ok: true
337
313
  command: suggest
@@ -352,9 +328,9 @@ data:
352
328
  pendingConflicts
353
329
  ```
354
330
 
355
- ## Owner field in update payloads
331
+ ## Owner field in updates
356
332
 
357
- Task and subtask update payloads now include `owner` in their event data:
333
+ Task and subtask update payloads include `owner`:
358
334
 
359
335
  ```text
360
336
  data:
@@ -366,6 +342,202 @@ data:
366
342
  The board API accepts `owner` on `PATCH /api/tasks/{id}` and
367
343
  `PATCH /api/subtasks/{id}`.
368
344
 
345
+ ## Sync resolve dry-run
346
+
347
+ ```bash
348
+ trekoon --toon sync resolve <conflict-id> --use ours|theirs --dry-run
349
+ ```
350
+
351
+ ```text
352
+ ok: true
353
+ command: sync.resolve
354
+ data:
355
+ conflictId: <conflict-id>
356
+ resolution: ours|theirs
357
+ entityKind: epic|task|subtask
358
+ entityId: <entity-id>
359
+ fieldName: <conflicted field>
360
+ oursValue: <current DB value>
361
+ theirsValue: <source branch value>
362
+ wouldWrite: <value that would be written>
363
+ dryRun: true
364
+ ```
365
+
366
+ No mutation occurs. The conflict stays pending.
367
+
368
+ ## Sync batch resolve
369
+
370
+ ```bash
371
+ trekoon --toon sync resolve --all --use ours|theirs [--entity <id>] [--field <name>]
372
+ ```
373
+
374
+ ```text
375
+ ok: true
376
+ command: sync.resolve
377
+ data:
378
+ resolution: ours|theirs
379
+ resolvedCount: <number>
380
+ resolvedIds: [<conflict-id>, ...]
381
+ filters:
382
+ entity: <entity-id> | null
383
+ field: <field-name> | null
384
+ ```
385
+
386
+ Human-mode note: `sync resolve --all --use theirs` asks for confirmation before
387
+ execution. Cancellation returns `error.code: cancelled` with the requested
388
+ `resolution`, `cancelled: true`, and the normalized `filters`.
389
+
390
+ When confirmation is required, execution is bound to the previewed conflict ID
391
+ set. If another process resolves one of those conflicts before the confirmed
392
+ write happens, the command fails with `error.code: conflict_set_changed`
393
+ instead of partially resolving a drifted batch.
394
+
395
+ ## Sync batch resolve dry-run
396
+
397
+ ```bash
398
+ trekoon --toon sync resolve --all --use ours|theirs [--entity <id>] [--field <name>] --dry-run
399
+ ```
400
+
401
+ ```text
402
+ ok: true
403
+ command: sync.resolve
404
+ data:
405
+ resolution: ours|theirs
406
+ matchedCount: <number>
407
+ matchedIds: [<conflict-id>, ...]
408
+ filters:
409
+ entity: <entity-id> | null
410
+ field: <field-name> | null
411
+ dryRun: true
412
+ ```
413
+
414
+ No mutation occurs. Returns `no_matching_conflicts` error when no pending
415
+ conflicts match the filters.
416
+
417
+ ## Sync resolve hardening errors
418
+
419
+ Recent `sync.resolve` hardening added explicit machine-visible failure modes for
420
+ race conditions and invalid persisted conflict targets.
421
+
422
+ ### Single resolve — cancelled
423
+
424
+ Returned in human mode when the user rejects or times out a confirmation prompt.
425
+ Single-conflict prompts only appear for `--use theirs`.
426
+
427
+ ```text
428
+ ok: false
429
+ command: sync.resolve
430
+ data:
431
+ conflictId: <conflict-id>
432
+ resolution: ours|theirs
433
+ cancelled: true
434
+ error:
435
+ code: cancelled
436
+ message: "Resolution cancelled by user."
437
+ ```
438
+
439
+ ### Batch resolve — cancelled
440
+
441
+ Returned in human mode when the user rejects or times out the batch prompt.
442
+
443
+ ```text
444
+ ok: false
445
+ command: sync.resolve
446
+ data:
447
+ resolution: ours|theirs
448
+ cancelled: true
449
+ filters:
450
+ entity: <entity-id> | null
451
+ field: <field-name> | null
452
+ error:
453
+ code: cancelled
454
+ message: "Batch resolution cancelled by user."
455
+ ```
456
+
457
+ ### Single resolve — already_resolved
458
+
459
+ Returned when a conflict is still pending at preview time but another process
460
+ resolves it before the confirmed write happens.
461
+
462
+ ```text
463
+ ok: false
464
+ command: sync.resolve
465
+ data:
466
+ conflictId: <conflict-id>
467
+ resolution: ours|theirs
468
+ reason: already_resolved
469
+ error:
470
+ code: already_resolved
471
+ message: "Conflict '<conflict-id>' already resolved."
472
+ ```
473
+
474
+ ### Resolve write hardening errors
475
+
476
+ These surface as domain failures when persisted conflict metadata no longer maps
477
+ to a valid writable target.
478
+
479
+ ```text
480
+ ok: false
481
+ command: sync.resolve
482
+ data:
483
+ reason: unsupported_entity_kind | disallowed_field | row_not_found
484
+ ...details
485
+ error:
486
+ code: unsupported_entity_kind | disallowed_field | row_not_found
487
+ message: <stable human-readable message>
488
+ ```
489
+
490
+ Per-code details:
491
+
492
+ - `unsupported_entity_kind`
493
+ - `data.entityKind`
494
+ - `disallowed_field`
495
+ - `data.tableName`
496
+ - `data.fieldName`
497
+ - `row_not_found`
498
+ - `data.tableName`
499
+ - `data.entityKind`
500
+ - `data.entityId`
501
+
502
+ ## Sync batch resolve — no_matching_conflicts error
503
+
504
+ Applies to both the execute and dry-run variants of `sync resolve --all`.
505
+ Returned when the given filters match zero pending conflicts.
506
+
507
+ ```text
508
+ ok: false
509
+ command: sync.resolve
510
+ data:
511
+ filters:
512
+ entity: <entity-id> | null
513
+ field: <field-name> | null
514
+ reason: no_matching_conflicts
515
+ error:
516
+ code: no_matching_conflicts
517
+ message: "No pending conflicts match the given filters."
518
+ ```
519
+
520
+ ## Sync batch resolve — conflict_set_changed error
521
+
522
+ Returned in human mode when batch confirmation was based on one pending conflict
523
+ set but one or more of those conflicts were resolved before the confirmed write
524
+ was applied.
525
+
526
+ ```text
527
+ ok: false
528
+ command: sync.resolve
529
+ data:
530
+ filters:
531
+ entity: <entity-id> | null
532
+ field: <field-name> | null
533
+ expectedConflictIds: [<conflict-id>, ...]
534
+ availableConflictIds: [<conflict-id>, ...]
535
+ reason: conflict_set_changed
536
+ error:
537
+ code: conflict_set_changed
538
+ message: "Pending conflicts changed before batch resolution could be applied."
539
+ ```
540
+
369
541
  ## Related docs
370
542
 
371
543
  - [Quickstart](quickstart.md)