@sylad/cadence 1.8.0 → 1.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.
Files changed (105) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +27 -54
  4. package/agents/code-reviewer.md +2 -2
  5. package/agents/doc-keeper.md +19 -0
  6. package/agents/precheck-reader.md +1 -1
  7. package/agents/qa-reviewer.md +2 -2
  8. package/agents/reference-analyst.md +25 -0
  9. package/agents/reference-verifier.md +19 -0
  10. package/agents/spec-writer.md +1 -1
  11. package/agents/ux-reviewer.md +1 -1
  12. package/bin/cadence.js +9 -1
  13. package/dist/audit.js +16 -4
  14. package/dist/check.js +24 -2
  15. package/dist/cli.js +258 -30
  16. package/dist/config.js +101 -5
  17. package/dist/corrections.js +24 -2
  18. package/dist/deliver.js +125 -37
  19. package/dist/docsync.js +19 -3
  20. package/dist/git.js +16 -3
  21. package/dist/lead.js +45 -12
  22. package/dist/migrate.js +166 -0
  23. package/dist/orchestrate/briefs.js +46 -6
  24. package/dist/orchestrate/changelog-merge.js +94 -0
  25. package/dist/orchestrate/command.js +358 -41
  26. package/dist/orchestrate/cycle.js +922 -285
  27. package/dist/orchestrate/guard.js +2 -2
  28. package/dist/orchestrate/integrate.js +129 -0
  29. package/dist/orchestrate/launch.js +48 -6
  30. package/dist/orchestrate/mutex.js +30 -0
  31. package/dist/orchestrate/no-test.js +41 -0
  32. package/dist/orchestrate/pool.js +74 -36
  33. package/dist/orchestrate/registry.js +146 -14
  34. package/dist/orchestrate/result.js +1 -1
  35. package/dist/orchestrate/snapshot.js +54 -1
  36. package/dist/orchestrate/spec.js +89 -5
  37. package/dist/orchestrate/state.js +56 -15
  38. package/dist/orchestrate/table.js +74 -3
  39. package/dist/orchestrate/timeouts.js +40 -0
  40. package/dist/orchestrate/worktree.js +368 -0
  41. package/dist/perf.js +188 -25
  42. package/dist/plan.js +112 -7
  43. package/dist/proc.js +6 -0
  44. package/dist/ready-free.js +109 -0
  45. package/dist/ref/add.js +129 -0
  46. package/dist/ref/analyse.js +220 -0
  47. package/dist/ref/check.js +338 -0
  48. package/dist/ref/command.js +92 -0
  49. package/dist/ref/corpus-render.js +83 -0
  50. package/dist/ref/corpus.js +201 -0
  51. package/dist/ref/diff.js +262 -0
  52. package/dist/ref/evaluate.js +241 -0
  53. package/dist/ref/figures.js +252 -0
  54. package/dist/ref/measure.js +330 -0
  55. package/dist/ref/pdf.js +116 -0
  56. package/dist/ref/png.js +125 -0
  57. package/dist/ref/regard.js +102 -0
  58. package/dist/ref/session.js +34 -0
  59. package/dist/ref/store.js +40 -0
  60. package/dist/ref/tools.js +51 -0
  61. package/dist/resources.js +51 -0
  62. package/dist/session.js +5 -2
  63. package/dist/state.js +75 -7
  64. package/dist/testgate.js +15 -0
  65. package/docs/cadence-hud.md +12 -0
  66. package/docs/cycle-vague-dark.svg +1 -1
  67. package/docs/cycle-vague-light.svg +1 -1
  68. package/docs/deliver.md +76 -5
  69. package/docs/fonctionnement-wave-dark.svg +1 -1
  70. package/docs/fonctionnement-wave-light.svg +1 -1
  71. package/docs/fonctionnement.md +6 -6
  72. package/docs/lead.md +40 -13
  73. package/docs/news.md +30 -1
  74. package/docs/orchestrate.md +275 -33
  75. package/docs/overview.md +65 -1
  76. package/docs/perf.md +14 -8
  77. package/docs/raf.md +90 -11
  78. package/docs/ref.md +415 -0
  79. package/docs/releasing.md +3 -3
  80. package/docs/session.md +2 -2
  81. package/docs/skills.md +21 -2
  82. package/docs/spec.md +1 -1
  83. package/docs/stabilite.md +4 -4
  84. package/docs/whats-new.md +30 -1
  85. package/package.json +2 -1
  86. package/skills/deliver/SKILL.md +6 -1
  87. package/skills/lead/SKILL.md +28 -10
  88. package/skills/session-close/SKILL.md +1 -1
  89. package/skills/session-start/SKILL.md +2 -2
  90. package/templates/inventaire.json +96 -0
  91. package/templates/orchestrate/accept-back.md +1 -0
  92. package/templates/orchestrate/accept.md +1 -0
  93. package/templates/orchestrate/fiche.md +1 -0
  94. package/templates/orchestrate/fix-docs.md +18 -0
  95. package/templates/orchestrate/fix-minors.md +2 -1
  96. package/templates/orchestrate/fix.md +5 -2
  97. package/templates/orchestrate/implement.md +7 -4
  98. package/templates/orchestrate/maquette.md +2 -1
  99. package/templates/orchestrate/precheck.md +2 -1
  100. package/templates/orchestrate/review-recheck.md +2 -0
  101. package/templates/orchestrate/review-small.md +5 -1
  102. package/templates/orchestrate/review.md +2 -0
  103. package/templates/orchestrate/spec-back.md +2 -1
  104. package/templates/orchestrate/spec.md +4 -3
  105. package/templates/orchestrate/ux.md +3 -1
package/docs/overview.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Overview: the tools and the skills
4
4
 
5
- The longer introduction the README used to open with.
5
+ The longer introduction the README used to open with, and everything the README handed over to keep itself a short home page (L326): the introduction in full, the start in detail, the plugin installation, the map of the documentation and the four tools in short.
6
6
 
7
7
  A small working method that lives in your repository. Solo developers and
8
8
  AI-assisted sessions lose track of *what is left to do* and *what each commit
@@ -25,3 +25,67 @@ reports a page left empty or in error.
25
25
 
26
26
  And a second plugin, [`cadence-hud`](cadence-hud.md): a status band above the Claude
27
27
  Code prompt that shows the session's context, quota, cost and the orchestrate waves running.
28
+
29
+ ## The README's introduction, in full
30
+
31
+ A small working method that lives in your repository: a versioned plan linked to your commits, waves of fresh AI sessions driven by a program, and deliveries proved by their effect.
32
+ Solo developers and AI-assisted sessions lose track of *what is left to do* and *what each commit was for*; cadence keeps both in plain files next to the code.
33
+ **raf** is the plan, **news** the changelog with screenshots, **session** the facts to open and close a day, **deliver** waits for the CI, deploys, then checks the effect.
34
+ **orchestrate** runs a lot through spec, review, code and UX passes, each in its own session (with a short `TMPDIR` of its own, `<os tmpdir>/cadence-<hash>`, outside the repository and the wave's state folder, removed at the end of each pass of the lot; short because Chrome's Unix sockets live there and are limited to 107 characters); the **lead** decides, re-verifies and delivers. The **worktree mode** (`orchestrate.worktrees: true` in `cadence.yaml`: independent lots of one repository in parallel, each in its own git worktree) is on in cadence's own `cadence.yaml` since 2026-10-25 (L320, after the trials L310 and L316: −45 % and −22 % of wall time, three automatic integrations without incident); a project that does not set the key keeps one lot at a time in the repository ([docs/orchestrate.md](orchestrate.md#one-worktree-per-lot-orchestrateworktrees-l279)).
35
+ Four [Claude Code](https://claude.com/claude-code) skills, three reviewer agents and a status-band plugin (`cadence-hud`) turn it into rituals.
36
+
37
+ ## Start
38
+
39
+ ```sh
40
+ npm install -g @sylad/cadence # or: npx -p @sylad/cadence raf …
41
+ raf init # .cadence/raf.yaml + .cadence/notes/ + a post-commit hook
42
+ # (a legacy docs/plan/raf.yaml is still read until moved: `raf migrate` moves it, see docs/raf.md)
43
+ raf add "My first lot" --estimate 1
44
+ ```
45
+
46
+ Then `raf start L1`, commit with `feat(L1): …`, `raf now` to see where you are, `raf done L1` when it is finished. A title gone stale after a change of scope is replaced with `raf title L1 "new title"` (any status, the old one is kept in a dated note). A lot that occupies a resource shared between projects says so with `raf add --uses banc` or `raf uses L1 banc`, the folder's `cadence.yaml` lists the resources (`resources: [banc]`), and `raf check` refuses a name that is not declared ([docs/raf.md](raf.md#shared-resources-uses-and-resources)); `cadence lead hold <label>` / `lead release <label>` declare the sessions the lead starts by hand so that they are subtracted from the free slots of `orchestrate --status` and `lead tour` ([docs/lead.md](lead.md#lead-hold-and-release)); `cadence lead tour` and `orchestrate --continue` list, next to the free slots, the ready lots whose resources no `doing` lot (in any project sharing them, at every `depth:` of the declaring `cadence.yaml`) or live wave holds (`--json`: `free`); the `lead` skill reads that list again each time an agent comes back or a lot is blocked, before waiting).
47
+
48
+ ## Install as a Claude Code plugin
49
+
50
+ The repository is its own [Claude Code](https://claude.com/claude-code) plugin marketplace. In a Claude Code session:
51
+
52
+ ```
53
+ /plugin marketplace add Sylad/cadence
54
+ /plugin install cadence@cadence # skills /cadence:session-start, session-close, deliver, lead + the three reviewer agents
55
+ /plugin install cadence-hud@cadence # the status band above the prompt
56
+ ```
57
+
58
+ The two plugins are independent. `cadence@cadence` carries the rituals, which call the **`cadence` CLI** (`raf`, `session`, `deliver`, …): install it too with `npm install -g @sylad/cadence`, the plugin does not bring it. `cadence-hud` works without the CLI; only its per-project plan progress needs `cadence` on the `PATH`. Without the plugin, `cadence skills install` copies the skills and agents into the repository. Details: [docs/skills.md](skills.md) and [docs/cadence-hud.md](cadence-hud.md).
59
+
60
+ ## Where to read more
61
+
62
+ | Page | What it covers |
63
+ |---|---|
64
+ | [docs/raf.md](raf.md) | the plan: commands, plan file, foreign plan formats, Gantt |
65
+ | [docs/orchestrate.md](orchestrate.md) | waves of sessions: budgets, spec, accept and review steps, neighbouring repositories, one worktree per lot (`orchestrate.worktrees: true`, on in cadence's own `cadence.yaml`, off by default elsewhere; `--integrate`, `--replay`, `--remove-worktree`; recovery of a handed-back lot's branch and the `CHANGELOG.md` union at the integration are described there) |
66
+ | [docs/deliver.md](deliver.md) | delivery: wait for CI, deploy, verify the effect |
67
+ | [docs/spec.md](spec.md) | the screen and back-end sheets, `cadence spec check\|qa\|reseal` |
68
+ | [docs/lead.md](lead.md) | the lead workflow and `cadence lead tour` |
69
+ | [docs/ref.md](ref.md) | reference documents: `cadence ref add` (rendered pages, positioned text, images, figure index), `cadence ref measure` (colours, WCAG contrast, boxes, OCR, font size), `cadence ref check` (the inventory schema, `templates/inventaire.json`), `cadence ref eval` (recall, precision, false « read », tokens per page against a known inventory), `cadence ref corpus` (the synthetic corpus with a known truth, rendered by Playwright) `cadence ref diff` (reconciles two readings of a document by program) and `cadence ref analyse` (one reading by the `reference-analyst` agent; `--double` for two independent readings, `ref diff`, a third look by `reference-verifier` and the reconciled inventory, an option whose measurement showed no gain; 20 pages at most per reading) |
70
+ | [docs/stabilite.md](stabilite.md) | what is guaranteed from 1.0.0 and what is not |
71
+
72
+ Also: [news](news.md) (changelog, UX, code and QA reviews, documentation that follows the code), [session](session.md), [corrections](corrections.md), [perf](perf.md), [skills and agents](skills.md), [cadence-hud](cadence-hud.md), [releasing](releasing.md), [overview](overview.md), [how it all works](fonctionnement.md) (four figures).
73
+
74
+ <details>
75
+ <summary>The four tools in short</summary>
76
+
77
+ - **raf** (French *reste à faire*, "what is left to do"): the plan, linked to your commits by the lot id in the message, computed from `git log` and never stored.
78
+ - **news**: a user-facing changelog with screenshots, tied to the plan; a lot is not done until its entry is written.
79
+ - **session**: `session start` and `session close` gather the facts of a work session (each lot in progress shows its commit count, `0 commit` included, next to its last activity).
80
+ - **deliver**: wait for the CI of the pushed commit, run the deploy commands, then **verify the effect**; a delivery is done only when its checks pass. A failed delivery is logged as a *red delivery* with its class (refusal, CI, deploy, check) and the last lines of the failing command's output. While a wave runs, `cadence deliver --sha <sha>` is accepted when no commit of the sha belongs to a lot that is not ready, so a ready lot ships while the wave runs.
81
+ - **auto-verification** (L236): before it reports, an `implement` or `fix` session re-reads the complete diff of its lot (of its fix commits, in `fix`) against the grid of the `code-reviewer` (code bug, architecture drift, architecture documentation, edge cases, one test per changed behaviour), fixes what it finds and writes an « auto-vérification » paragraph in its report; the measure is `cadence corrections --classify` and the share right the first time of `cadence perf`.
82
+ - **perf**: `cadence perf` is the dashboard of the workflow (speed next to quality, `⚠` on a trend), including the red deliveries and the blocking QA findings; `cadence lead tour` opens with its *perf header*, one line per `⚠`, nothing when stable; a rise of the tokens per lot names the step that carries it, and when the share right the first time rose too it reads as quality paid for, not a wrong estimate ([docs/perf.md](perf.md)).
83
+ - **doc-keeper** (L306): a generic agent that reads the diff of a lot and the project's map of documents (`docs.sync`, README, `docs/`, CHANGELOG, news), says which documents must change and updates them (or only lists them, read-only); `cadence orchestrate` calls it before the review when `docs.sync` reports a document behind the lot (a lot never ends ready with that gap open: the documentation is written, or the gap is acknowledged with `raf docs-ok <lot> --reason`, L317), the lead by hand; a document outside the repository only through the `docs.articles` key of the project's `cadence.yaml` (it then proposes a lot, never writes it) ([docs/news.md](news.md#the-librarian-doc-keeper)).
84
+ - **reference-analyst** (L265): an agent that reads one reference document (mock-ups, a spec with figures, a norm) page by page and zone by zone, measures with `cadence ref measure`, and writes a reading (`lectures/a.json`) in the format of `templates/inventaire.json`; `cadence ref analyse` launches it for one reading and has the program check the file and count the tokens (once by default; with `--double` (L266) twice, readings A and B in `lectures/a.json` and `b.json`, which must not see each other; [docs/ref.md](ref.md#cadence-ref-analyse-documentpdf---sections-3-59---slug-name---model-sonnetopushaiku---model-b-sonnetopushaiku---seuil-30---double)).
85
+ Reviews are opt-in gates: `ux-reviewer` (no user-facing change is done before its usability review), `code-reviewer` (no lot with commits is done before its code review) and `qa-reviewer`, which walks the delivered app in a real browser.
86
+
87
+ </details>
88
+
89
+ ## What's new
90
+
91
+ Two lines per version, then one paragraph per version, in [whats-new.md](whats-new.md); the full history, with dates, is in [CHANGELOG.md](../CHANGELOG.md).
package/docs/perf.md CHANGED
@@ -5,7 +5,7 @@
5
5
  `cadence perf [folder] [--since date] [--json]` is the permanent dashboard of the workflow. It calls no model, writes nothing
6
6
  (no journal, no plan, no cache) and reads, from the folder (default: the current one), the wave journals
7
7
  `<folder>/.cadence/runs/<wave>/<project>--<lot>.json` (with each wave's `wave.json`) and the plan of every project of the folder
8
- (one nesting level, like `cadence corrections`: `group/gamma`). Exit code `0`, even when a `⚠` is printed; `1` on a usage or read error.
8
+ (one nesting level, like `cadence corrections`: `group/gamma`; a `tmp/` folder is never read: its working copies would count their lots twice, L241). Exit code `0`, even when a `⚠` is printed; `1` on a usage or read error.
9
9
 
10
10
  It compares two windows of 7 calendar days (local days): the **current** one (the last 7 days, today included) and the **previous**
11
11
  one (the 7 days before). Every speed indicator is printed on the same line as its **quality twin**, never alone ("fast *and* well"):
@@ -15,35 +15,39 @@ one (the 7 days before). Every speed indicator is printed on the same line as it
15
15
  | Throughput | lots ready, lots handed back per day, lots delivered (plan `done` with `finished` in the window), share of handed-back lots within their token budget | escaped defects | is the flow dropping? look for the bottleneck before adding lots; are the budgets right? |
16
16
  | Corrections | median of `fix` passes per lot, share of lots with 2 passes or more | share of lots right the first time (no `fix` pass) | is the first write good enough? harden the sheet or the brief |
17
17
  | Corrections | dominant causes (top 3 of the classified passes, the unclassified share apart) | — | which link to fix |
18
- | Lead interventions | lots handed back by a wave then finished outside it, `ux` sessions per visible lot | escaped defects | does the wave stop too early? does the ergonomics review cost more than one round? |
18
+ | Lead interventions | lots handed back by a wave then finished outside it, sessions that see the application per visible lot (`ux` steps, plus the `review-small` of a small visible lot), share of visible lots reviewed by a dedicated `ux` step | escaped defects (sessions per lot only) | does the wave stop too early? does the ergonomics review cost more than one round? |
19
19
 
20
20
  A line starts with `⚠` and ends with the decision it informs when its value is **worse than the previous window by more than 25 %**
21
21
  (a drop for the throughput and the share right the first time; a rise for passes, interventions and escaped defects). No `⚠`, and the line says
22
- why, when one window holds fewer than 5 lots for the indicator, when the previous value is zero, or when `--since` truncates a window.
22
+ why, when one window holds fewer than 5 lots for the indicator (10 steps for a wall-time duration, L280), when the previous value is zero, or when `--since` truncates a window.
23
23
  A window with no lot for an indicator (no rendered lot for ready lots, no delivered lot for delivered, none at all for a window entirely before `--since`) gives `n/d` (`null` in JSON), never `0`. A **clear bottleneck** (one cause weighing at least half of the classified passes of the
24
24
  current window, with at least 10 classified passes) is flagged by a `⚠` even without a trend.
25
25
 
26
26
  - **Dating.** A journal is dated by the end of its last step (else by the creation of its wave); a journal without a readable date is left out. A correction pass is dated
27
27
  by the end of its `fix` step. The classification of a pass is `cadence corrections`' (same reading, `src/corrections.ts`): without finding to read it is `sans-constat`,
28
- otherwise the cause of `<folder>/.cadence/corrections.json` (a manual cause first), `non-classe` when absent from it. `perf` never classifies: run `cadence corrections --classify`.
28
+ otherwise the cause of `<folder>/.cadence/corrections.json` (a manual cause first), `non-classe` when absent from it. `perf` never classifies: run `cadence corrections --classify`, which the cause line recalls as soon as a pass with findings is unclassified. Without any model, the line below it, `origine des passes à constats`, counts the passes of the window by the kind of review that raised them (`review`, `review-small`, `ux`), read in the journals (`corrections.causes.<window>.byReview` in `--json`) (L240).
29
29
  - **Escaped defects** = distinct lots with a plan note dated in the window, written after the lot was done, that starts with `QA` (after the `DD-MM` date prefix) and
30
30
  contains `bloquant` or `majeur` not preceded by `0` or `aucun`. Until a plan holds such a note the value is `n/d` ("aucune note QA reconnue : convention à fixer (L227)"). Reopened lots are not counted: the plan does not date a reopening.
31
31
  - **Lead interventions.** A lot counts when its last journal (all waves of the folder) is `handed-back` or `failed` and its plan lot is `done` with `finished` on or after the day it was handed back.
32
32
  A journal joins its plan by the project name (the path of the project relative to the folder) and the `lot` field of the journal; an id `Q4/accueil-4-ux12` joins the plan lot `Q4`. Journals with no plan lot are counted aside (`N lot(s) de journal sans plan`) and never counted as finished outside the wave.
33
33
  An unreadable plan skips its project with a warning on the error output and the other projects are read; the indicators that read the plans are `n/d` only when no plan is readable.
34
+ - **Sessions that see the application.** `sessions ux par lot visible` counts, per visible lot rendered in the window, its `ux` steps that ended `ok` **and** the `review-small` steps that ended `ok` of a small visible lot (journal `small: true`): the single pass of a small visible lot is its only ergonomics review, run with the application launched (L283; measured on 24-10, 139 of 140 small lots were visible and the line showed 0.0, −97 %, a false signal). The `review-small` of a lot that is not small is only a code review and is not counted; a journal written before the `small` field counts none.
35
+ `part des lots visibles revus par une étape ux` is the share of the visible lots rendered in the window with at least one `ux` step that ended `ok` (so a small visible lot, reviewed in `review-small`, is not in it). It is informational: no `⚠`, no quality twin (`interventions.dedicatedUx` in `--json`), `n/d` without a visible lot.
36
+ - **Pre-check share of `oui`** (L288). `part de « oui » du précheck` is the share of the lots rendered in the window whose journal records a pre-check verdict (`precheckVerdict`) that is `oui` (the deliverable was already there; an unproven `oui` is recorded `partiel`). Compare the current window with the previous one before and after a change of `orchestrate.precheck` or of the pre-check criterion (the title rule of L288): a share that stays low means the pre-check costs more than it saves. A journal older than L288 has no `precheckVerdict`: its verdict is read back from the lot (`livrable déjà présent` → `oui`, pre-check finding in the brief → `partiel`, otherwise `non`), so the window before the change is measurable. A journal written since L288 whose pre-check report was unreadable (it keeps the `contrôle préalable illisible` warning, no `precheckVerdict`) has no verdict and is not counted, not read back as `non` (L293). Lots with no successful `precheck` step are not counted; informational, no `⚠`, no quality twin (`precheck.yes` in `--json`), `n/d` without a verdict.
37
+ - **Pre-check measured per lot** (L322). Under the section « Contrôle préalable », for the current window: the split `oui` / `partiel` / `non` of the rendered lots (`précheck : oui 1 · partiel 0 · non 1`); the local sessions (`précheck local : n sessions, k échecs, m min`: how many, how many ended without a usable report, wall time; a local session that ended `ok` but whose report was unreadable, so that the lot fell back on Sonnet, counts as a failure, and its line says `local échec`, `--json` `local.status: "illisible"`); the Sonnet sessions of the pre-check, which are the fallback of a local failure and the confirmation of a local `oui` (`précheck Sonnet : n sessions, t k tokens, m min`; the local model counts no token); the gain of the `oui` (`gain des « oui » : n sessions d'implémentation évitées (≈ t k tokens)`: the lots handed back without implementation, times the median of the `implement` steps of the window; without any, the tokens read `(gain en tokens : n/d)` and are `null` in `--json`); then one line per lot (`projet:lot · verdict · local échec 10 min · Sonnet 2 min, 20 k tokens`). Nothing is printed for a window without pre-check session or verdict. In `--json`: `precheck.verdicts`, `precheck.cost` (`local`, `sonnet`, `avoided`, each `{ current, previous }` as a whole) and `precheck.lots` (current window). Read it before changing `orchestrate.precheck` or the lots it applies to: the cost of the pre-check is what it spends (`local` minutes, `sonnet` tokens), its worth is `avoided`.
34
38
  - **`--since date`** is an ISO date that ignores everything before that day in both windows (a wave created before that day is not read, even if its lots end after it); a window starting before it is *truncated* (its per-day rates divide by the days covered, no `⚠`).
35
39
  It is **not** the instant of `cadence corrections --since` (waves created strictly after).
36
- - **`--json`** prints one object `{ since, today, windows: { current, previous }, throughput, corrections, interventions, tokens, delays, durations, cost, orphans, warnings }`; every indicator carries
40
+ - **`--json`** prints one object `{ since, today, windows: { current, previous }, throughput, corrections, interventions, precheck, tokens, delays, durations, cost, orphans, warnings }`; every indicator carries
37
41
  `{ current, previous, change, warn, twin, lots, note }` (`twin`: the path of its quality twin, e.g. `throughput.escaped`). Same values as the text.
38
42
 
39
43
  ## Tokens, delays, wall time and cost (L226)
40
44
 
41
- The same two windows, the same `⚠` (more than 25 % worse, at least 5 lots or steps in each window, previous value not zero, no truncated window) and the same
45
+ The same two windows, the same `⚠` (more than 25 % worse, at least 5 lots in each window, or 10 steps for a wall-time duration (L280: under 10 steps the change from one window to the next cannot be interpreted, `docs/perf/recalage-2026-10-24.md`), previous value not zero, no truncated window) and the same
42
46
  quality twin (the share of lots right the first time) on the same line. A rise is the worse direction for each of them.
43
47
 
44
48
  | Indicator | Reading | Decision it informs |
45
49
  |---|---|---|
46
- | tokens per lot, by estimate | median of the counted tokens (sum of `tokens.counted` of every step) of the rendered journals of the window whose lot joins the plan, one class per exact estimate (`0.5`, `1`, `2`…); a failed lot counts, a journal with no readable tokens or without plan lot does not | is the estimate calibrated? redo the split or the budgets |
50
+ | tokens per lot, by estimate | median of the counted tokens (sum of `tokens.counted` of every step) of the rendered journals of the window whose lot joins the plan, one class per exact estimate (`0.5`, `1`, `2`…), a lot that went through the spec chain (a `spec`, `fiche`, `maquette` or `accept` step; 450 k is the gap of their *budget* (`SPEC_STAGE_ALLOCATION`), the measured gap is smaller, about 150 k at estimate 0.5 and 30 k at estimate 1 on 12–18-10, `docs/perf/goulot-2026-10-18.md`; on 17–23-10 the `⚠` on estimates 0.25 and 0.5 (+27 %) came with a share right the first time up from 37 to 59 %, so the reading is « heavier first write, fewer corrections », `docs/perf/recalage-2026-10-24.md`, L280) in its own class `0.5+spec`, printed after `0.5`, so that a change in the mix of lots is not read as a heavier ordinary lot (L240); a failed lot counts, a journal with no readable tokens or without plan lot does not. Each class also carries `carrier` in the JSON (L312): the kind of step whose tokens per lot, averaged over the lots of the class, rise most from one window to the other (`review` and `review-small` count as one step, the size of the lot picks one or the other; `{ kind, delta }`; `null` when the total per lot does not rise on average) | is the estimate calibrated? redo the split or the budgets. **The advice names the step** (L312): « la hausse est portée par l'étape `implement` (+20 k par lot) », and when the share right the first time rose in the same window the line reads « de la qualité payée, pas une estimate fausse ; juger cette étape (modèle, effort, passes) avant de recaler l'estimate » instead of « l'estimate est mal calibrée » (17–24-10 against 10–17-10: `implement` carries the rise at estimates 0.25 and 0.5, `docs/perf/recalage-2026-10-24-soir.md`) |
47
51
  | share of review tokens | counted tokens of the steps whose kind starts with `review` or is `ux`, over the counted tokens of all steps of the rendered journals; a ratio, `n/d` without journal | does the review weigh too much against the writing? lighten it (`orchestrate.review.threshold`) |
48
52
  | delay created → done | median of `finished − created` (calendar days) of the plan lots `done` with `finished` in the window and both dates readable | is the queue growing? |
49
53
  | delay done → delivery | median of the days between `finished` and the first line of the repository's `deliveries.log` on or after it (`<git common dir>/cadence/`, one repository per project, worktrees included; read without creating anything); `n/d` for a project that is not a git repository, has no log or no later line | deliver in smaller batches? |
@@ -72,7 +76,7 @@ No indicator of produced volume (lines of code, commits, total tokens or euros o
72
76
 
73
77
  ## Delivery quality and the tour header (L227)
74
78
 
75
- `cadence deliver` appends one line `day sha reason` to `<git common dir>/cadence/deliveries-failed.log` for every failure after the lock is taken (script, CI, deploy or check; the reason is the first line of the failure message). A green delivery or a refusal before the lock writes nothing there; `deliveries.log` is unchanged. A failed write of that log leaves the exit code and the failure message of the delivery as they are and adds the warning `deliver : journal des livraisons rouges non écrit : <cause>` on the error output. An exception or a crash of `deliver` writes no line.
79
+ `cadence deliver` appends one line `day sha [class] reason ⏎ line ⏎ line` to `<git common dir>/cadence/deliveries-failed.log` for every failure after the lock is taken (script, CI, deploy or check; the reason is the first line of the failure message, the class one of `refus`, `ci`, `deploiement`, `verification`, and up to 5 bounded lines of the failing command's output follow, see [deliver](deliver.md)). A green delivery or a refusal before the lock writes nothing there; `deliveries.log` is unchanged. A failed write of that log leaves the exit code and the failure message of the delivery as they are and adds the warning `deliver : journal des livraisons rouges non écrit : <cause>` on the error output. An exception or a crash of `deliver` writes no line.
76
80
 
77
81
  | Indicator | Reading | Twin | Decision it informs |
78
82
  |---|---|---|---|
@@ -81,4 +85,6 @@ No indicator of produced volume (lines of code, commits, total tokens or euros o
81
85
 
82
86
  Both are judged like the other indicators (`⚠` above +25 %). For red deliveries the sample is the deliveries of the window, green and red (at least 5 per window); for blocking QA findings it is the plan lots `done` in the window (at least 5 per window). `--json` adds `quality: { redDeliveries, qaBlocking }` (Indicator format, `twin` `throughput.delivered` and `throughput.escaped`); every other key is unchanged, and `throughput.delivered.twin` stays `throughput.escaped`.
83
87
 
88
+ **Red deliveries by class and project** (L282): when the current window holds at least one red delivery, `cadence perf` prints under the « Débit » section two lines, `livraisons rouges par classe (fenêtre courante)` and `livraisons rouges par projet`, each sorted by count (ties: refus, CI, déploiement, vérification, then `non classée` for lines whose class is unknown). `--json` adds `quality.reds: { current, previous }`, each `{ count, byClass: { refus, ci, deploiement, verification, inconnue }, byProject: { <project>: { <class>: n } } }`; the other keys are unchanged. **Lines written before the class** (L331): a line with no `[class]` is classed from its wording, the one `deliver` writes itself — `script de livraison en échec (code 2)` → refus, any other code (or a timeout) → déploiement, `déploiement en échec` → déploiement, `CADENCE_DEPLOYED_FILE …` (declared sha invalid or unreadable) → déploiement, `CI en échec` / `CI non terminée après N s` / `gh run list en échec` / `aucun run CI pour ce sha après N min` → ci, `vérification en échec` → verification; the wording must start the reason. Any other line stays `non classée`. The deduction happens at read time: `deliveries-failed.log` is never rewritten. The reason itself (the last lines of the failing command's output) stays in `deliveries-failed.log`: read it there.
89
+
84
90
  `cadence lead tour` prints the `⚠` lines of `perf` as its **perf header** (see [lead tour](lead.md#lead-tour)).
package/docs/raf.md CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  # raf
4
4
 
5
- - The plan is a YAML file in the repo (`docs/plan/raf.yaml`), edited by the CLI.
6
- Your comments and hand edits are preserved.
5
+ - The plan is a YAML file in the repo, edited by the CLI: `.cadence/raf.yaml` by default (versioned by git, never published with `docs/`),
6
+ with its notes in `.cadence/notes/`. Your comments and hand edits are preserved. A project that still has `docs/plan/raf.yaml` keeps it:
7
+ see [Where the plan is](#where-the-plan-is).
7
8
  - A commit belongs to a lot when its message cites the id: `feat(L3): …`,
8
9
  `fix: L3/t1 …`. The link is **computed from `git log`**, never stored, so
9
10
  committing never dirties the plan. The id is read as a whole word: `XL3`,
@@ -14,7 +15,7 @@
14
15
  count. Without a scope citing a lot, the whole message is read as before.
15
16
  - `raf check` audits drift between the plan and the history.
16
17
  - Plan upkeep needs no lot. A commit is plan upkeep when **every file it
17
- touches is a plan file**: the plan itself, its Gantt page (only at its default place, `gantt.html` next to the plan — written elsewhere with `raf gantt -o`, declare it under `plan.files`), the `plan:` key of the config file in use (`cadence.yaml`, or
18
+ touches is a plan file**: the plan itself, its Gantt page (only at its default place, `gantt.html` next to the plan — written elsewhere with `raf gantt -o`, declare it under `plan.files`), with the default plan `.cadence/raf.yaml`, also the legacy `docs/plan/raf.yaml` and `docs/plan/gantt.html` (so that the old commits of the plan stay plan upkeep after the `git mv`), the `plan:` key of the config file in use (`cadence.yaml`, or
18
19
  the `--config` file; a change to `deliver:`/`session:` is work), or a file the
19
20
  project lists under `plan.files` in `cadence.yaml` (a page it generates from
20
21
  the plan, a journal), or the QA expectations file (`docs/qa/expectations.md`,
@@ -29,7 +30,7 @@
29
30
  ```sh
30
31
  npm install -g @sylad/cadence # or: npx -p @sylad/cadence raf …
31
32
 
32
- raf init # docs/plan/raf.yaml + post-commit hook + .playwright-mcp/ in .gitignore
33
+ raf init # .cadence/raf.yaml + .cadence/notes/.gitkeep + post-commit hook + .playwright-mcp/ in .gitignore
33
34
  raf add "Monthly dedup on merge" --estimate 2
34
35
  raf add "Typo in footer" --quickwin
35
36
  raf add "Loan cache" --after L1
@@ -39,7 +40,7 @@ git commit -m "feat(L1): dedup by calendar month"
39
40
  raf now # in progress, next up (quickwins first), recently done
40
41
  raf done L1/t1 && raf done L1
41
42
  raf check # exit 1 on drift
42
- raf gantt # docs/plan/gantt.html
43
+ raf gantt # .cadence/gantt.html (next to the plan)
43
44
  ```
44
45
 
45
46
  ## Commands
@@ -47,21 +48,26 @@ raf gantt # docs/plan/gantt.html
47
48
  | Command | Effect |
48
49
  |---|---|
49
50
  | `raf init [--project name] [--prefix L] [--no-hook]` | create the plan, install the hook, add `.playwright-mcp/` to `.gitignore` |
50
- | `raf add "title" [--estimate d] [--quickwin] [--visible [--screen page\|component\|journey]] [--spec] [--public "title"] [--after L2,L4] [--parent L3]` | add a lot or a sub-task, print its id (`--public`: the lot's title in the public's words, written as `public:` right after `title:`; `--screen`: the screen a visible lot creates or changes — a new `page`, a new `component` or a modified `journey` — written as `screen:` right after `visible:`, shown by `raf show`; it requires `--visible`, and `raf check` reports a `screen:` without `visible:` or with another value as an error. Only `page` triggers the spec step of `orchestrate` (L209), never inferred from the title; `--spec`: the lot changes a behaviour (something a user, an API client or another service can observe), written as `spec: true` right after `screen:`, `visible:`, `quickwin:` or `estimate:` (whichever comes last), shown by `raf show`, refused on a sub-task. Also set by the lead at planning time, never inferred from the title) |
51
+ | `raf add "title" [--estimate d] [--quickwin] [--visible [--screen page\|component\|journey]] [--spec] [--public "title"] [--after L2,L4] [--uses banc,passerelle] [--parent L3]` | add a lot or a sub-task, print its id (`--public`: the lot's title in the public's words, written as `public:` right after `title:`; `--screen`: the screen a visible lot creates or changes — a new `page`, a new `component` or a modified `journey` — written as `screen:` right after `visible:`, shown by `raf show`; it requires `--visible`, and `raf check` reports a `screen:` without `visible:` or with another value as an error. Only `page` triggers the spec step of `orchestrate` (L209), never inferred from the title; `--spec`: the lot changes a behaviour (something a user, an API client or another service can observe), written as `spec: true` right after `screen:`, `visible:`, `quickwin:` or `estimate:` (whichever comes last), shown by `raf show`, refused on a sub-task. Also set by the lead at planning time, never inferred from the title); `--uses`: the shared resources the lot occupies, written as `uses: [banc, passerelle]` after `after:` and before `created:` ([shared resources](#shared-resources-uses-and-resources), L251), refused on a sub-task) |
51
52
  | `raf add "title" --every <days>` · `raf did <id> ["text"]` | recurring lot: `every` (days) and `last` (last time done) fields; the lot stays `todo`, `raf now` and `session start` list it under « Récurrent » with « dû depuis N j » / « prochain dans N j »; `raf did` resets the count (spec: `docs/superpowers/specs/2026-10-04-L11-tache-recurrente.md`) |
52
53
  | `raf screen <id> page\|component\|journey\|none` | set, replace or remove `screen:` on an existing lot, to adopt the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen) on a plan already written (only `page` adopts the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen); `component` and `journey` do not); refused on a lot without `visible:` (like `raf add --screen`) and on a sub-task; `none` removes the key and leaves `visible:` alone |
54
+ | `raf visible <id> on\|off` | set or remove `visible:` on an existing lot (a user-facing change: a news entry is expected, and `cadence orchestrate` adds its UX step), written right after `quickwin:`, `estimate:` or `status:` (whichever comes last); `on` on a visible lot changes nothing, `off` on a lot without the key neither; refused on a sub-task, and `off` is refused while the lot has a `screen:` (`raf screen <id> none` first: a screen is a visible change). It is the fix `raf check` points to for an open « Revue UX — » lot without `visible:` (L273) |
53
55
  | `raf spec <id> on\|off` | set or remove `spec:` on an existing lot (a lot that changes a behaviour, for the back-end spec step of `cadence orchestrate`, see [The spec step of a lot with `spec: true`](orchestrate.md#the-spec-step-of-a-lot-with-spec-true)); refused on a sub-task; `off` is refused on a lot that has `screen: page` (the page implies the spec: remove `screen:` first) and `on` there only says so and changes nothing; on `component` and `journey` (ordinary path since L209) both work and set or remove the back-end `spec:`. `raf spec` only takes `on`/`off`: `cadence spec check\|qa <fiche.yaml>` keeps its own subcommands. `raf check` reports a `spec:` that is neither `true` nor `false` (`spec : true ou false attendu`) |
56
+ | `raf uses <lot> [<resource>…]` | replace the `uses:` list of a lot of any status (`done` and `dropped` included) by the names given (duplicates removed, order kept); with no name, remove the key. Prints nothing. A name is a word of lowercase letters, digits, `-` and `_` (else `ressource invalide : <name> (lettres minuscules, chiffres, - et _)`, plan unchanged); a sub-task is refused (`uses se pose sur un lot, pas sur une sous-tâche : L1/t1`); an unknown lot is `lot inconnu : <id>`. A name not yet declared is accepted: `raf check` refuses it (L251) |
54
57
  | `raf public <id> "title"` · `raf public <id> --clear` | set, replace or remove the public title of a lot |
55
58
  | `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`) |
56
59
  | `raf todo <id> [--reason text]` | put a started lot back to `todo` (`doing` → `todo`, `started:` removed, dated note « remis en attente : reason »): refused when work commits cite the lot (plan-only and version commits do not count, as `raf commits`), when the lot is not `doing` (a `done` or `dropped` lot is reopened by hand), or on a sub-task; sub-tasks and other keys are left as they are |
60
+ | `raf migrate [--dry-run]` | move `docs/plan/raf.yaml` to `.cadence/raf.yaml` and rewrite the readers of the old path in the repository, see [Moving the plan: `raf migrate`](#moving-the-plan-raf-migrate) |
57
61
  | `raf note <id> "text"` | dated note — keep decisions next to the work |
58
- | `raf show <id> [--notes]` | one lot: status, dates, `after`, public title, dated notes in order, then the counted commits (as `raf commits`, neighbouring repositories included); `--notes` prints the notes alone |
62
+ | `raf title <id> "new title"` | replace the title of a lot of any status (`done` included) and add a dated note « titre remplacé : « old title » »; `raf now`, `raf show`, `raf list`, `session start` and the proposals read the new one. Refused on an empty or identical title, an unknown lot or a sub-task |
63
+ | `raf show <id> [--notes]` | one lot: status, dates, `goal : …` right after the title when the lot has a `goal:` (native key or `plan.fields.goal`, L272: the objective that replaces the title in the `orchestrate` briefs), `after`, public title, `screen` and `spec`, `uses : banc, passerelle` when the lot has `uses:`, dated notes in order, then the counted commits (as `raf commits`, neighbouring repositories included); `--notes` prints the notes alone |
59
64
  | `raf commits <id>` | the commits counted for a lot (the set the code review gate uses), one `<sha> <subject>` per line, oldest first; a lot that declares `repos:` ([neighbouring repositories](orchestrate.md#a-lot-whose-work-is-in-a-neighbouring-repository)) gets, after them, one `dépôt <path> :` section per neighbour with the commits there that cite the lot and fall within the lot's period (`started` to `finished`) and, when the lot declares a `cite:` for that neighbour, contain that string (`aucun commit du lot`, or `introuvable` for a path that is not there) |
60
65
  | `raf now` | what to do next |
61
66
  | `raf list [--status s]` | flat list |
62
- | `raf ignore <sha> \| "exact subject" [--reason text]` | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) without rewriting history: a dated, reasoned line in the plan's `acknowledged:` section; a sha is exact, a subject covers every commit carrying it |
67
+ | `raf docs-ok <lot> --reason text` | acknowledge the `docs.sync` gap of a lot (a document behind its commits, `raf check` red) when the documentation really stays true: a `docs_ok: { date, reason }` key on the lot, **the reason is required** (refused without it, for an unknown lot or for a sub-task); `raf check` and `cadence lead tour` no longer count the lot, `raf show` prints it, and `cadence orchestrate` neither calls the librarian nor holds the lot back for it. Commit the plan with it |
68
+ | `raf ignore <sha> \| "exact subject" [--reason text]` | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) without rewriting history: a dated, reasoned line in the plan's `acknowledged:` section; a sha is exact, a subject covers every commit carrying it; exactly one commit argument, the reason goes through `--reason "text"` (`raf ignore 1a2b3c4 "tooling"` is refused with « un seul commit attendu ; la raison se passe par --reason "texte" », exit 2) |
63
69
  | `raf check --ignored` | list the acknowledged commits with their date and reason |
64
- | `raf check [--since date] [--idle 7]` | since the plan's adoption date by default (a visible lot without a public title is only a `⚠` warning: it never changes the exit code): commits without a lot (commits touching only plan files, or only version fields, `CHANGELOG.md`, the README's « What's new » section and `docs/whats-new.md` are exempt), unknown ids, `todo` lots that already have commits, idle lots, `done` lots with open sub-tasks, bad or circular dependencies |
70
+ | `raf check [--since date] [--idle 7]` | since the plan's adoption date by default (a visible lot without a public title is only a `⚠` warning: it never changes the exit code): commits without a lot (commits touching only plan files, or only version fields, `CHANGELOG.md`, the README's « What's new » section and `docs/whats-new.md` are exempt), unknown ids, `todo` lots that already have commits, idle lots (an open lot whose sub-lots, entries carrying `parent:` in a plan kept by another tool, have a recent commit or are in progress is not idle: L321), `done` lots with open sub-tasks, bad or circular dependencies, and an open lot titled « Revue UX — <screen> » without `visible: true` (`ux-review-not-visible`, an error: such a lot goes through precheck → implement → review with no UX step, so the screen is never reviewed — finance L32 on 2026-10-23; L271; the message names the fix, `raf visible <id> on`, L273; on a read-only plan, plan kept by another tool, it never names a `raf` command, which would refuse: with `plan.fields.visible` mapped it says to set `visible:` with the project's tool, without it it says the plan carries no `visible`, to map the field in `cadence.yaml` (`plan.fields.visible`) if the project's tool writes it, and that otherwise the UX review of that screen happens outside orchestrate, L321) |
65
71
  | `raf gantt [-o file]` | standalone Gantt page |
66
72
  | `raf hook install` | add the post-commit hook (read-only, never blocks) and the pre-commit hook (does nothing unless `hook.autostart` says so, see below) |
67
73
 
@@ -79,7 +85,7 @@ hook:
79
85
  - `warn`: today's behaviour, the commit goes through, the post-commit hook warns.
80
86
  - `refuse`: the commit fails, loudly, with `raf start <id>` in the message (`--no-verify` still bypasses it).
81
87
  - `start`: the hook runs `raf start <id>` and stages the plan, so the plan change is **in the same commit** —
82
- no amend afterwards, no dirty plan left behind. Only when `docs/plan/raf.yaml` is identical to `HEAD` (no
88
+ no amend afterwards, no dirty plan left behind. Only when the plan is identical to `HEAD` (no
83
89
  uncommitted change to the plan, staged or not): otherwise nothing is started, the hook says why
84
90
  (`plan modifié non commité : démarrage automatique sauté, raf start <id> à la main`) and behaves like `warn`.
85
91
 
@@ -91,6 +97,76 @@ pre-commit block to an existing repository. A read-only plan is never started by
91
97
  Global options: `--file path` or `RAF_FILE`; `RAF_TODAY=YYYY-MM-DD` to freeze
92
98
  the date. `cadence raf …` is the same as `raf …`.
93
99
 
100
+ ## Shared resources: `uses:` and `resources:`
101
+
102
+ Some projects of a folder share one thing that only one lot at a time can use (a test bench, a gateway, a
103
+ device). L251 (GitHub issue #13, 1/3) lets the plan **declare** it; nothing locks on it yet (`raf start` and
104
+ `cadence orchestrate` ignore `uses:`).
105
+
106
+ - The folder that holds the projects lists the resources in its `cadence.yaml`, next to `depth:` and `priority:`:
107
+ `resources: [banc, passerelle]`. A name is a word of lowercase letters, digits, `-` and `_`.
108
+ - A lot says which ones it occupies with `uses: [banc]` (a flow list; optional): `raf add "title" --uses banc,passerelle`, or on an existing lot
109
+ `raf uses L3 banc passerelle` (`raf uses L3` removes the key). `uses:` belongs to a lot, never to a sub-task, and `raf show` prints it.
110
+ It is a native key of raf's own plan; a plan kept by another tool is read-only and has no `uses` mapping.
111
+ - The list is read from the nearest `cadence.yaml` that carries the `resources:` key, starting at the parent of the project's root and going up
112
+ (a project at `depth: 2`, `folder/group/project`, reads `folder/cadence.yaml` when `group` carries none): the first file that has the key
113
+ wins, even with `resources: []` or with no value (`resources:`), and the search goes no higher. No file carries it: nothing is declared. Duplicates in the list are ignored.
114
+ The search is not bounded: without a nearer file it goes up to the root of the file system.
115
+ - `raf check` reads that file only when an open lot (`todo`, `doing`) carries `uses:`, and writes one gap per name it does not declare:
116
+ `✗ L3 utilise la ressource « banc », non déclarée dans <folder>/cadence.yaml (resources:)` (exit code 1). Without a declaring file the
117
+ gap names the `cadence.yaml` of the parent folder. `done` and `dropped` lots are not checked, so removing a resource from the list does not
118
+ fail the history. A `uses:` that is not a list of valid names is a gap of the lot (`✗ L3 : uses doit être une liste de noms de ressources`);
119
+ an unreadable `cadence.yaml`, or a `resources:` that is not a list of valid names, is a gap naming the file
120
+ (`<file> : resources doit être une liste de noms de ressources`), never an empty list. A plan where no open lot carries `uses:` gets the same
121
+ `raf check` as before.
122
+
123
+ ## Where the plan is
124
+
125
+ The plan is resolved in this order: `--file`, then `RAF_FILE`, then the `plan:` key of `cadence.yaml`, then `.cadence/raf.yaml`,
126
+ then the legacy `docs/plan/raf.yaml`.
127
+
128
+ - `raf init` creates `.cadence/raf.yaml` and `.cadence/notes/` (with a `.gitkeep`, so git tracks the empty folder). It refuses
129
+ when a plan already exists where it would create one, a legacy `docs/plan/raf.yaml` included: it never creates a second plan
130
+ beside it. With `plan: path` in `cadence.yaml`, or `--file`, the plan and nothing else is created there.
131
+ - A project that still has `docs/plan/raf.yaml` (and no `.cadence/raf.yaml`) keeps working, read and written as before. Every command
132
+ that loads the plan prints one line on **stderr** (standard output, `--json` included, is unchanged) with the move to do —
133
+ `cd <repository root> && mkdir -p .cadence && git mv docs/plan/raf.yaml .cadence/raf.yaml` (`mv` outside a git repository or when git does not track the file, where `git mv` would fail; the root is spelled out, quoted if
134
+ needed, so the line runs as is from any folder and, under `session --all`, names its project). `raf` never moves the file itself on its own: `raf migrate` does it, with the readers of the path. The line is not
135
+ printed when the path comes from `--file`, `RAF_FILE` or `plan:`.
136
+ - When both files exist, `.cadence/raf.yaml` is read and a stderr warning says that `docs/plan/raf.yaml` is ignored; nothing is written
137
+ or deleted.
138
+ - `.cadence/` is versioned, except `.cadence/runs/` (the state of the waves) and `.cadence/corrections.json` (the cache of
139
+ `cadence corrections`): `cadence orchestrate` adds those two to `.git/info/exclude` (it replaces the `/.cadence/` line an older
140
+ version wrote there) and the clean-tree guard ignores them, but a change to `.cadence/raf.yaml` or `.cadence/notes/` counts.
141
+ - `raf gantt` writes `gantt.html` next to the plan, so `.cadence/gantt.html` by default.
142
+ - `cadence lead tour`, `session --all` and `cadence orchestrate` recognise a project by `.cadence/raf.yaml`, `docs/plan/raf.yaml` or a
143
+ `cadence.yaml` with `plan:`.
144
+
145
+ ## Moving the plan: `raf migrate`
146
+
147
+ `raf migrate [--dry-run]` moves a legacy `docs/plan/raf.yaml` to `.cadence/raf.yaml` and updates what reads the old path, so the
148
+ « ancien emplacement » line stops appearing in every session. It is the one command that works on the files of the repository
149
+ rather than on the plan; it prints no legacy warning and never commits (review the result with `git status`, then commit).
150
+
151
+ - **The plan** moves with `git mv` (`mv` outside a git repository, or when git does not track the file), `.cadence/` created if needed.
152
+ - **The readers** are the files that cite `docs/plan/raf.yaml` — plan-data scripts, tests, CI workflows, skills, documentation: git's
153
+ tracked and not-ignored files (the whole folder, without `.git` and `node_modules`, outside a repository). Every occurrence is
154
+ rewritten to `.cadence/raf.yaml`. The history is not a reader and is left alone: `CHANGELOG*`, `*.ndjson` journals and `.cadence/`.
155
+ - **`--dry-run`** prints the move and every file it would touch, with its number of references, and changes nothing.
156
+ - **It refuses** (code 2, nothing moved or written) while a reader is *not handled*: a file that cites `docs/plan` in a way a text
157
+ replacement cannot follow — another file of the folder (`docs/plan/journal.ndjson`), the folder itself, a path built piece by piece
158
+ (`join(root, 'docs', 'plan', 'raf.yaml')`, even split over several lines, `Path("docs") / "plan"`; reported at the line where it starts), a binary file, a text file that is not UTF-8 (rewriting it would alter its bytes), or a file over 2 MiB that cites the path (too big to rewrite; a big file that does not cite it is ignored). Without `--dry-run`, a refusal prints nothing on stdout: nothing is announced as done. Each one is listed as `file:line`. Fix it by hand and run the command again, or
159
+ declare a file that cites the path without reading the plan (a fixture, a document about the old place) under `migrate.keep` in
160
+ `cadence.yaml`: a list of paths or patterns (`*`, `**`, `?`, as `docs.sync`), whose matches are listed as left as they are. A document that names the legacy place next to the new one (a stability contract, a description of the fallback) belongs there too: a line citing both would otherwise be rewritten to cite `.cadence/raf.yaml` twice.
161
+ - **It refuses** when `docs/plan/raf.yaml` and `.cadence/raf.yaml` both exist, and when neither does.
162
+ - **It can be run again**: with the plan already at `.cadence/raf.yaml` (moved by hand, or by a first run that stopped), it only handles the
163
+ readers that remain, and says `rien à migrer` when there is none.
164
+
165
+ ```yaml
166
+ migrate:
167
+ keep: [docs/specs/**, test/legacy-plan.test.ts] # cite docs/plan/raf.yaml without reading the plan
168
+ ```
169
+
94
170
  ## Plan file
95
171
 
96
172
  ```yaml
@@ -106,6 +182,7 @@ lots:
106
182
  estimate: 2 # working days
107
183
  quickwin: false
108
184
  visible: true # user-facing: a news entry is expected when done
185
+ goal: Dedup on merge, bank line kept # optional (L258): replaces the title as the objective of the `orchestrate` briefs when the title is stale
109
186
  after: [L0]
110
187
  gate: { count: 8, spec: true } # optional: not before 8 lots with `spec: true` are done (or `screen: page`)
111
188
  created: 2026-09-28
@@ -125,6 +202,8 @@ format, and the plan stays writable:
125
202
  plan: planning/todo.yaml
126
203
  ```
127
204
 
205
+ A relative `plan:` path is read from the repository root, whatever folder you run `raf` from; `--file` and `RAF_FILE` are read from the current folder.
206
+
128
207
  A project that publishes its plan (a JSON generated from it and committed with it)
129
208
  declares that file, so a commit touching only the plan and its published copy is
130
209
  plan upkeep; the plan keeps raf's format and stays writable:
@@ -173,7 +252,7 @@ plan:
173
252
  ```
174
253
 
175
254
  - Fields: `id`, `title`, `status`, `estimate`, `quickwin`, `visible`, `screen`, `spec`, `public`, `every`, `last`, `after`, `created`, `started`,
176
- `finished`, `notes`, `repos`, `parent`, `gate`; one left out is read under its own name. A timestamp counts for
255
+ `finished`, `notes`, `repos`, `parent`, `gate`, `goal` (L258: the objective that replaces the title in the `orchestrate` briefs); one left out is read under its own name. A timestamp counts for
177
256
  its day; a note written as plain text is one note.
178
257
  - `gate` (L219) is a drawing condition added to `after`: `gate: { count: N, spec: true }` or `gate: { count: N, screen: page }` (N an integer ≥ 1, one criterion). It is written by hand in the plan (no `raf add` option). The count is the number of `done` lots of the same plan that carry the criterion (`todo`, `doing`, `dropped` lots and sub-tasks do not count; recurring lots do). Until the count reaches N, a `todo` lot is *waiting*, never *ready*: `raf now` lists it on a line `en attente de condition de tirage : L190 (2/8 lots spec: true faits)` (the existing `en attente de dépendances : …` line stays for lots waiting on `after`), `cadence session start` adds an `En attente` section (nothing when no lot is gated) and leaves the lot out of `À suivre` and of the proposals, `cadence lead tour` skips it for `prochain` and adds `attend L190` to the project's line (and `waiting` to `--json`, only when a lot waits), `cadence orchestrate` refuses it before any action (`a:L190 : condition de tirage non remplie : 2/8 lots spec: true faits`) and `--continue` never draws it, and `raf show` prints `gate : 8 lots spec: true faits`. Once the count is reached the lot is ready like any other (the field stays in the plan); a lot already `doing` is not refused for its condition. A malformed `gate` is reported by `raf check` (`gate invalide : …`) and the lot is never drawable. A plan without `gate` behaves exactly as before; `after` read by the Gantt scheduling is unchanged.
179
258
  - `spec` is the exception to « a left-out field is read under its own name »: a read-only plan's `spec:` is often the path of a spec document, so it is read only when `plan.fields.spec` maps it, and then only `true` counts (any other value is neither an error nor a spec step). Natively, `spec:` stays `true` or `false` (L198). Consequence for a `gate: { count: N, spec: true }` in a read-only plan: it counts only lots whose `spec` is mapped by `plan.fields.spec`; without that mapping the count stays 0 and the lot waits for good, with no `raf check` problem. `gate: { count: N, screen: page }` has no such condition.