@misterhuydo/cairn-mcp 1.38.0 → 1.40.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
@@ -196,6 +196,11 @@ cairn_roadmap { action: "focus", id: 3 } → make phase 3 the current f
196
196
  cairn_roadmap { action: "set_status", id: 3, status: "done" } → ship it
197
197
  cairn_roadmap { action: "add", parent: 3, text: "..." } → ad-hoc sub-task under a phase
198
198
  cairn_roadmap { action: "deps_add", from: 2, to: 4 } → phase 2 blocks phase 4
199
+ cairn_roadmap { action: "reorder", order: "1 -> 8 -> 9 -> 5" } → state the priority order
200
+ cairn_roadmap { action: "reorder", move: 8, before: 5 } → "do phase 8 before phase 5"
201
+ cairn_roadmap { action: "reorder", decline: true } → "no stated order, deliberately" — and stop asking
202
+ cairn_roadmap { action: "set_status", id: 3, status: "deferred" } → park it: still in the plan, out of the live set
203
+ cairn_roadmap { action: "deferrable" } → propose which phases look parked, from the markers already in their titles
199
204
  cairn_roadmap { action: "publish" } → (re)write the human-readable ROADMAP.md at the repo root
200
205
  ```
201
206
 
@@ -218,7 +223,8 @@ invisible:
218
223
  flat markdown. Collapsing costs nothing: the fence keeps blank lines around it, so it
219
224
  stays a valid CommonMark code block and both line-anchored and AST consumers still
220
225
  find it. It carries stable
221
- phase `id`s, `number`, `title`, `status`, `goal`, `blocked_by`, `slices`, the full
226
+ phase `id`s, `ref` (the name that actually identifies a phase — see below), `number`,
227
+ `label`, `group`, `title`, `status`, `goal`, `blocked_by`, `slices`, the full
222
228
  per-phase `detail` (the body you wrote in `.cairn/roadmap.md`, verbatim and in full —
223
229
  thinning the prose never thins this), and `links` — the `[[wikilinks]]` in that body
224
230
  resolved to the memory files behind each phase, so the reasoning is one tap away
@@ -231,10 +237,20 @@ plan **complete** — `done` and `stale` phases stay in `phases` with their real
231
237
  because a consumer that cannot see finished work will confidently render an incomplete
232
238
  plan. A shipped phase also carries `shipped` (the date) and `commit` (short HEAD when it
233
239
  was marked done), which is what turns "this was done" into something you can point an
234
- agent at when you are tracing a bug back to where it came from. Phases from *superseded*
235
- plans, whose numbers a later plan reclaimed, move to a separate `shipped_phases` array —
236
- identity and provenance only, no `detail`, so a long-lived repo's history cannot bloat
237
- the file. That key is always present; its absence means the file predates this.
240
+ agent at when you are tracing a bug back to where it came from.
241
+
242
+ **`shipped_phases` is the shipping log** — every phase ever marked done, newest first,
243
+ read from the same `.cairn/roadmap_completed.md` the reader half's *Recently shipped*
244
+ section is generated from, so the two halves of one file cannot disagree about the same
245
+ fact. (They used to: two real projects published `shipped_phases: []` beside a markdown
246
+ list of six and eight shipped phases, and a consumer taking the JSON at its word saw two
247
+ projects that had shipped nothing.) The markdown shows the most recent few; this carries
248
+ all of them. Entries are identity and provenance only — `id`, `ref`, `number`, `title`,
249
+ `shipped`, `commit`, never `detail` — so a long-lived repo's history cannot bloat the
250
+ file. A phase still in the current plan appears in **both** `phases[]` and here: they are
251
+ two indexes of one plan, the tree and the chronology, joined by `id`. Entries logged
252
+ before the log carried ids publish no `id` and no `ref` rather than a guessed one. The
253
+ key is always present; its absence means the file predates this.
238
254
 
239
255
  Three rules the format leaves implicit, each of which has cost a consumer real time:
240
256
 
@@ -249,7 +265,163 @@ Three rules the format leaves implicit, each of which has cost a consumer real t
249
265
  one phase current, use `cursor_phase`, which is always the top-level ancestor and
250
266
  always resolves in `phases`; `cursor_path` is the root-to-focus chain for breadcrumbs.
251
267
  All three are `null` when nothing is active, so "nothing is in progress" is never
252
- ambiguous with a broken pointer.
268
+ ambiguous with a broken pointer. A `cursor` you cannot find in `phases[]` is normal
269
+ and not a dangling pointer — look in `slices[]`, or just follow `cursor_path`.
270
+ - **`number` does not identify a phase; `ref` does.** `number` is the phase number
271
+ authored in the plan, and it is unique only *within a decision group* — one real plan
272
+ publishes `number: 1` on forty separate rows. Print or say **`ref`**, which is always
273
+ present and unique in the document: the project's own name for the phase (`TC-4`)
274
+ where the titles carry one, otherwise a bare number where that identifies, otherwise
275
+ a group-qualified one (`roadmap-3`), otherwise the row id. `ref` is a *name* and can
276
+ lengthen when phases are added elsewhere; `id` stays the stable machine key.
277
+
278
+ ### The priority order
279
+
280
+ `## Phases` in `.cairn/roadmap.md` says what the phases are. It does not say what order
281
+ to work them in, and **renumbering to express that is destructive** — the file-to-DB
282
+ sync matches rows by phase number, so moving "Phase 8" to "Phase 2" reads as "phase 2's
283
+ text changed" and rewires which row is which.
284
+
285
+ So the order is its own statement. Author it as an `## Order` section, in the numbers
286
+ the phase headers already use:
287
+
288
+ ```markdown
289
+ ## Order
290
+
291
+ 1 -> 8 -> 9 -> 5
292
+ ```
293
+
294
+ or let a working session set it — `cairn_roadmap reorder` takes either the whole list or
295
+ a single relative move, so "do phase 8 before phase 5" mid-session lands in the file and
296
+ in the next publish. A relative move splices against the *effective* order (what was
297
+ stated, then the rest in plan order), so phases the stated list omits keep their place
298
+ instead of being silently demoted.
299
+
300
+ It publishes as **`order`** — phase ids, most important first — with
301
+ **`order_source: "authored"`**. A partial list is fine; anything it omits follows in plan
302
+ order. Two rules hold it honest:
303
+
304
+ - **It never contradicts `blocked_by`.** If A is blocked by B, B comes first. An order
305
+ that disagrees is *repaired* rather than refused (publishing is a background side
306
+ effect and must not fail) — an unmentioned blocker is pulled in, a late one is pulled
307
+ forward, and everything the deps do not implicate keeps its stated position. The
308
+ repair is reported in the `reorder` result, never applied silently.
309
+ - **cairn never publishes an order it worked out itself.** When nothing is stated the
310
+ `order` key is *absent*, so a consumer is free to derive one and label it as derived.
311
+ A guessed sequence and a stated one are different claims and must not look alike.
312
+
313
+ `next` follows the stated order too. A plan that publishes a priority the tool then
314
+ advances past is publishing a decoration.
315
+
316
+ ### Deferred: work that is parked, not in progress
317
+
318
+ A phase is `deferred` when it is **finished, with a tail somebody deliberately parked**.
319
+ Not `open` — nobody is picking it up. Not `done` — the work is real and the plan should
320
+ still show it.
321
+
322
+ Without that status, one slice titled `S5 Billing (paid gate)` keeps its whole phase in
323
+ the live set forever, and anything that ranks live phases ranks parked work above
324
+ genuinely open work. Measured on a real plan: four of eleven live phases were live
325
+ *solely* because of one slice whose own title said `(later)` or `(paid gate)`, and the
326
+ phase with two genuinely open slices and nothing blocking it sorted **eighth**.
327
+
328
+ A phase is deferred either because you said so:
329
+
330
+ ```
331
+ cairn_roadmap { action: "set_status", id: 3, status: "deferred" }
332
+ ```
333
+
334
+ or because **every slice it has left is** — that half is *derived*, not stored, on every
335
+ read, from the same rows everything else already loads. Storing it would mean
336
+ re-deriving on each path that can change a slice, and one missed path leaves a phase
337
+ permanently mis-parked. The rule is exact: a phase with at least one deferred slice and
338
+ no slice still open, in progress or blocked is deferred; a phase whose slices are *all*
339
+ done keeps the status it was given, because whether it is finished is a judgement about
340
+ work outside its slices and cairn does not make it for you.
341
+
342
+ What parking does, and does not do:
343
+
344
+ - It **leaves the live set**: out of `next`, out of the published order, out of the
345
+ `M/N done` denominator (with the exclusion stated on the line, never quiet), and
346
+ published as `status: "deferred"`.
347
+ - It **stays in the plan**: shown in the tree as `[>]`, published in `phases[]`, *not*
348
+ pruned from `roadmap.md` and *not* logged to `roadmap_completed.md`. Parked is not
349
+ shipped. `set_status <id> open` picks it back up.
350
+ - The `## Order` section **still remembers it**, even though it drops out of the
351
+ published `order` — unparking should not cost you your place in the queue.
352
+ - A blocker that is parked still blocks: `next` refuses to advance past it and says
353
+ which phase did it. What changes is that the order stops dragging parked work forward.
354
+
355
+ Plans said this in prose long before there was a status for it. `deferrable` reads those
356
+ markers back:
357
+
358
+ ```
359
+ cairn_roadmap { action: "deferrable" }
360
+ ```
361
+
362
+ It proposes — it never writes. Candidates are titles carrying a parenthetical park
363
+ marker (`(later)`, `(paid gate)`, `(design-later)`, `(on hold)`, …), gated on the
364
+ parenthetical rather than the bare word, because "do this later" appears in half the
365
+ phase titles ever written. Each candidate comes with the consequence that makes it worth
366
+ confirming: which phases would leave the live set, and whether that is because of the
367
+ marker itself or because of what would be left under them. You confirm the ones you
368
+ mean, one `set_status` each.
369
+
370
+ ### Being asked for the order
371
+
372
+ cairn shipped `order` and `reorder` — documented, with relative moves and cycle repair —
373
+ and two days later `order` was still unset in every project that had one. Not because
374
+ anyone disliked it: because nothing ever mentioned it. **A capability nobody is prompted
375
+ to use is indistinguishable, from the outside, from one that does not exist.**
376
+
377
+ So plan-*changing* actions — a `sync` that inserted or retired phases, `set_status` to
378
+ `done` or `deferred`, and `reorder` itself — may carry an advisory field:
379
+
380
+ ```json
381
+ "order_advice": {
382
+ "state": "never_set",
383
+ "live_phases": 11,
384
+ "unordered": ["phase-25", "phase-60", "phase-101"],
385
+ "why": "11 live phases and no stated order, so `next` follows plan number order — the sequence they were written in, which is not a priority; 3 of them are waiting on another phase",
386
+ "ask": true
387
+ }
388
+ ```
389
+
390
+ Four things about it are deliberate:
391
+
392
+ - **It never fires on `tree`, `status` or `next`.** The end-of-turn hook calls those
393
+ after every assistant turn, and a nudge there is noise on a loop — which is precisely
394
+ how a cursor sat on a finished phase for weeks, on screen the whole time, in a line
395
+ everybody had stopped reading.
396
+ - **It is advisory.** Never a refusal, never a blocked action, and cairn never writes an
397
+ order itself. cairn is an MCP server and cannot ask a question; the agent can, and it
398
+ has the code in front of it, so it brings you a recommendation with reasons rather
399
+ than a bare "what order?". An order the agent invented would be published as
400
+ `order_source: "authored"` and shown to you as your own decision.
401
+ - **`why` is generated from the plan, and it is the point.** "No order is set" produces a
402
+ vague question and a vague answer.
403
+ - **It can go quiet.** "No order has been set" and "we decided not to have one" are
404
+ different facts. `cairn_roadmap reorder decline: true` records the second in the
405
+ `## Order` section, in a line you can read and delete, and `ask` goes false. It is not
406
+ a lock: any later `reorder` replaces it with no ceremony. The decline records the
407
+ phases live at the time, so a phase *added* later revives the question — and even then
408
+ the advisory says the decline stands and asks only where the newcomer goes.
409
+
410
+ A stated order is only reported as `stale` when it **points at something that is not
411
+ there** — a phase that shipped, got parked, or left the plan. Phases it simply does not
412
+ mention are reported in `unordered` and nothing more: *"phase 8 first, sort the rest out
413
+ when we get there"* is a complete answer, and an advisory that treats a partial order as
414
+ unfinished makes stating one a chore nobody completes.
415
+
416
+ `cursor_advice` is the same shape for the other thing nothing ever said out loud — a
417
+ cursor left on finished (`on_done_phase`), parked (`on_parked_phase`) or removed
418
+ (`dangling`) work, or unset while live phases remain. The cursor is cairn's own state,
419
+ so no consumer can warn about it on cairn's behalf.
420
+
421
+ Both ride on the **tool result**, not in the published `ROADMAP.md`. A file is read
422
+ continuously by panels and dashboards; an advisory in it would be a permanent nag on a
423
+ screen that cannot act on it. It goes to the one reader who can put the question to a
424
+ person.
253
425
 
254
426
  The first line is the marker that says who generated the file and when
255
427
  (`<!-- slot:roadmap format=roadmap/1 provider=cairn generated=… -->`). cairn declares