@sylad/cadence 1.7.0 → 1.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +26 -8
- package/agents/code-reviewer.md +2 -2
- package/agents/doc-keeper.md +19 -0
- package/agents/qa-reviewer.md +2 -2
- package/agents/spec-writer.md +1 -1
- package/agents/ux-reviewer.md +1 -1
- package/bin/cadence.js +12 -1
- package/dist/audit.js +14 -4
- package/dist/check.js +6 -0
- package/dist/cli.js +295 -29
- package/dist/config.js +147 -5
- package/dist/corrections.js +78 -22
- package/dist/deliver.js +85 -21
- package/dist/docsync.js +19 -3
- package/dist/git.js +16 -3
- package/dist/lead.js +37 -12
- package/dist/migrate.js +166 -0
- package/dist/orchestrate/briefs.js +28 -6
- package/dist/orchestrate/command.js +303 -34
- package/dist/orchestrate/cycle.js +720 -273
- package/dist/orchestrate/guard.js +2 -2
- package/dist/orchestrate/integrate.js +85 -0
- package/dist/orchestrate/launch.js +37 -5
- package/dist/orchestrate/mutex.js +30 -0
- package/dist/orchestrate/pool.js +74 -36
- package/dist/orchestrate/registry.js +123 -14
- package/dist/orchestrate/result.js +1 -1
- package/dist/orchestrate/snapshot.js +25 -0
- package/dist/orchestrate/spec.js +89 -5
- package/dist/orchestrate/state.js +42 -6
- package/dist/orchestrate/table.js +61 -2
- package/dist/orchestrate/timeouts.js +40 -0
- package/dist/orchestrate/worktree.js +368 -0
- package/dist/perf.js +620 -0
- package/dist/plan.js +92 -6
- package/dist/ready-free.js +109 -0
- package/dist/ref/add.js +116 -0
- package/dist/ref/check.js +338 -0
- package/dist/ref/command.js +78 -0
- package/dist/ref/corpus-render.js +83 -0
- package/dist/ref/corpus.js +201 -0
- package/dist/ref/evaluate.js +241 -0
- package/dist/ref/figures.js +179 -0
- package/dist/ref/measure.js +330 -0
- package/dist/ref/pdf.js +104 -0
- package/dist/ref/png.js +125 -0
- package/dist/ref/store.js +40 -0
- package/dist/ref/tools.js +51 -0
- package/dist/resources.js +51 -0
- package/dist/session.js +5 -2
- package/dist/state.js +82 -0
- package/dist/testgate.js +15 -0
- package/docs/cadence-hud.md +12 -0
- package/docs/corrections.md +1 -1
- package/docs/cycle-vague-dark.svg +1 -1
- package/docs/cycle-vague-light.svg +1 -1
- package/docs/deliver.md +58 -3
- package/docs/fonctionnement-wave-dark.svg +1 -1
- package/docs/fonctionnement-wave-light.svg +1 -1
- package/docs/fonctionnement.md +5 -5
- package/docs/lead.md +37 -12
- package/docs/news.md +24 -1
- package/docs/orchestrate.md +230 -19
- package/docs/perf.md +89 -0
- package/docs/raf.md +89 -11
- package/docs/ref.md +316 -0
- package/docs/session.md +2 -2
- package/docs/skills.md +9 -2
- package/docs/spec.md +1 -1
- package/docs/stabilite.md +4 -4
- package/docs/whats-new.md +4 -0
- package/package.json +3 -1
- package/skills/deliver/SKILL.md +6 -1
- package/skills/lead/SKILL.md +31 -6
- package/skills/session-close/SKILL.md +1 -1
- package/skills/session-start/SKILL.md +2 -2
- package/templates/inventaire.json +96 -0
- package/templates/orchestrate/accept-back.md +1 -0
- package/templates/orchestrate/accept.md +1 -0
- package/templates/orchestrate/fiche.md +1 -0
- package/templates/orchestrate/fix-docs.md +18 -0
- package/templates/orchestrate/fix-minors.md +2 -1
- package/templates/orchestrate/fix.md +5 -2
- package/templates/orchestrate/implement.md +6 -3
- package/templates/orchestrate/maquette.md +2 -1
- package/templates/orchestrate/precheck.md +2 -1
- package/templates/orchestrate/review-recheck.md +2 -0
- package/templates/orchestrate/review-small.md +5 -1
- package/templates/orchestrate/review.md +2 -0
- package/templates/orchestrate/spec-back.md +2 -1
- package/templates/orchestrate/spec.md +4 -3
- package/templates/orchestrate/ux.md +3 -1
package/docs/perf.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
[← cadence](../README.md)
|
|
2
|
+
|
|
3
|
+
# perf
|
|
4
|
+
|
|
5
|
+
`cadence perf [folder] [--since date] [--json]` is the permanent dashboard of the workflow. It calls no model, writes nothing
|
|
6
|
+
(no journal, no plan, no cache) and reads, from the folder (default: the current one), the wave journals
|
|
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`; 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
|
+
|
|
10
|
+
It compares two windows of 7 calendar days (local days): the **current** one (the last 7 days, today included) and the **previous**
|
|
11
|
+
one (the 7 days before). Every speed indicator is printed on the same line as its **quality twin**, never alone ("fast *and* well"):
|
|
12
|
+
|
|
13
|
+
| Family | Indicator | Twin | Decision it informs |
|
|
14
|
+
|---|---|---|---|
|
|
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
|
+
| 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
|
+
| 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, 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
|
+
|
|
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
|
+
(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 (10 steps for a wall-time duration, L280), when the previous value is zero, or when `--since` truncates a window.
|
|
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
|
+
current window, with at least 10 classified passes) is flagged by a `⚠` even without a trend.
|
|
25
|
+
|
|
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
|
+
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`, 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
|
+
- **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
|
+
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
|
+
- **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
|
+
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
|
+
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
|
+
- **`--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 `⚠`).
|
|
38
|
+
It is **not** the instant of `cadence corrections --since` (waves created strictly after).
|
|
39
|
+
- **`--json`** prints one object `{ since, today, windows: { current, previous }, throughput, corrections, interventions, precheck, tokens, delays, durations, cost, orphans, warnings }`; every indicator carries
|
|
40
|
+
`{ current, previous, change, warn, twin, lots, note }` (`twin`: the path of its quality twin, e.g. `throughput.escaped`). Same values as the text.
|
|
41
|
+
|
|
42
|
+
## Tokens, delays, wall time and cost (L226)
|
|
43
|
+
|
|
44
|
+
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
|
|
45
|
+
quality twin (the share of lots right the first time) on the same line. A rise is the worse direction for each of them.
|
|
46
|
+
|
|
47
|
+
| Indicator | Reading | Decision it informs |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| 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 | is the estimate calibrated? redo the split or the budgets |
|
|
50
|
+
| 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`) |
|
|
51
|
+
| 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? |
|
|
52
|
+
| 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? |
|
|
53
|
+
| wall time per step kind | median of `ended − started` in minutes of the `ok` steps with both bounds readable, ranked in the window by the local day of `ended`; one line per kind seen | which step is slow? timeouts, effort or model |
|
|
54
|
+
| cost per delivered lot | median in euros of the sum, over every step of every journal of the lot (all waves), of the step cost; lots `done` in the window with at least one journal | is the model chosen for each step right? |
|
|
55
|
+
|
|
56
|
+
**Price grid.** Declared in the `cadence.yaml` of the folder, euros per million tokens, the three prices mandatory for a declared model:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
perf:
|
|
60
|
+
prices:
|
|
61
|
+
sonnet: { input: 3, output: 15, cacheRead: 0.3 }
|
|
62
|
+
opus: { input: 15, output: 75, cacheRead: 1.5 }
|
|
63
|
+
haiku: { input: 1, output: 5, cacheRead: 0.1 }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
An unknown key (under `perf`, under a model, or a model other than `sonnet`, `opus`, `haiku`), a negative or non-numeric price, or an unreadable file refuses the command before any journal is read (`raf: …`, exit 1, no output).
|
|
67
|
+
Without `perf.prices` (absent or empty) the cost is `n/d` and a warning says where to declare it; the other indicators are served.
|
|
68
|
+
|
|
69
|
+
**Cost of a step** = (`input` + `cacheWrite`) × input price + `output` × output price + `cacheRead` × cacheRead price, per million tokens, at the price of the step's own model. Assumptions written in the sheet (`docs/specs/perf-tokens-delais-cout/fiche.yaml`, R8):
|
|
70
|
+
cache writes are billed at the input price (the grid has no write price, so the cost is slightly underestimated if they are billed more); a step that only carries `counted` (an old journal) is split 90 % input, 10 % output, no cache read;
|
|
71
|
+
a step without any readable token costs 0 € (the lot stays in the population); a `local` step costs 0 € whatever its counters (outside quota and budget) and needs no price.
|
|
72
|
+
A lot with a step of a model that has no price in the grid (or no readable `model`, named `inconnu`) is left out of the cost and a warning on the error output names the model and the number of lots; delivered lots without any journal are counted aside (`N lot(s) livré(s) sans journal`). No `⚠` on a cost that is `n/d`.
|
|
73
|
+
|
|
74
|
+
No indicator of produced volume (lines of code, commits, total tokens or euros of a window) is printed or exposed: only per-lot medians, median durations and shares come out. Tokens and euros compare lots of the same size; they never set a spending target.
|
|
75
|
+
|
|
76
|
+
## Delivery quality and the tour header (L227)
|
|
77
|
+
|
|
78
|
+
`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.
|
|
79
|
+
|
|
80
|
+
| Indicator | Reading | Twin | Decision it informs |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| red deliveries | lines of every project's `deliveries-failed.log` dated in the window; a repository without the file counts `0` (not `n/d`), an unreadable line, or an unreadable file, counts nothing; printed on the « livrés » line | delivered lots | the delivery breaks: fix CI, deploy or checks before adding lots |
|
|
83
|
+
| blocking QA findings | plan notes dated in the window, on a lot already `done` at the note's date, that start with `QA` and contain `bloquant` not preceded by `0` or `aucun` (`majeur` alone does not count); counted note by note, unlike the escaped defects; `n/d` until a note is recognised; printed on the escaped-defects line | escaped defects | the review lets serious defects through: harden the review before delivering more |
|
|
84
|
+
|
|
85
|
+
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`.
|
|
86
|
+
|
|
87
|
+
**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 written before the class existed). `--json` adds `quality.reds: { current, previous }`, each `{ count, byClass: { refus, ci, deploiement, verification, inconnue }, byProject: { <project>: { <class>: n } } }`; the other keys are unchanged. The reason itself (the last lines of the failing command's output) stays in `deliveries-failed.log`: read it there.
|
|
88
|
+
|
|
89
|
+
`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
|
|
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 #
|
|
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 #
|
|
43
|
+
raf gantt # .cadence/gantt.html (next to the plan)
|
|
43
44
|
```
|
|
44
45
|
|
|
45
46
|
## Commands
|
|
@@ -47,21 +48,25 @@ 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
|
|
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 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
68
|
| `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 |
|
|
69
|
+
| `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, 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) |
|
|
65
70
|
| `raf gantt [-o file]` | standalone Gantt page |
|
|
66
71
|
| `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
72
|
|
|
@@ -79,7 +84,7 @@ hook:
|
|
|
79
84
|
- `warn`: today's behaviour, the commit goes through, the post-commit hook warns.
|
|
80
85
|
- `refuse`: the commit fails, loudly, with `raf start <id>` in the message (`--no-verify` still bypasses it).
|
|
81
86
|
- `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
|
|
87
|
+
no amend afterwards, no dirty plan left behind. Only when the plan is identical to `HEAD` (no
|
|
83
88
|
uncommitted change to the plan, staged or not): otherwise nothing is started, the hook says why
|
|
84
89
|
(`plan modifié non commité : démarrage automatique sauté, raf start <id> à la main`) and behaves like `warn`.
|
|
85
90
|
|
|
@@ -91,6 +96,76 @@ pre-commit block to an existing repository. A read-only plan is never started by
|
|
|
91
96
|
Global options: `--file path` or `RAF_FILE`; `RAF_TODAY=YYYY-MM-DD` to freeze
|
|
92
97
|
the date. `cadence raf …` is the same as `raf …`.
|
|
93
98
|
|
|
99
|
+
## Shared resources: `uses:` and `resources:`
|
|
100
|
+
|
|
101
|
+
Some projects of a folder share one thing that only one lot at a time can use (a test bench, a gateway, a
|
|
102
|
+
device). L251 (GitHub issue #13, 1/3) lets the plan **declare** it; nothing locks on it yet (`raf start` and
|
|
103
|
+
`cadence orchestrate` ignore `uses:`).
|
|
104
|
+
|
|
105
|
+
- The folder that holds the projects lists the resources in its `cadence.yaml`, next to `depth:` and `priority:`:
|
|
106
|
+
`resources: [banc, passerelle]`. A name is a word of lowercase letters, digits, `-` and `_`.
|
|
107
|
+
- 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
|
|
108
|
+
`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.
|
|
109
|
+
It is a native key of raf's own plan; a plan kept by another tool is read-only and has no `uses` mapping.
|
|
110
|
+
- 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
|
|
111
|
+
(a project at `depth: 2`, `folder/group/project`, reads `folder/cadence.yaml` when `group` carries none): the first file that has the key
|
|
112
|
+
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.
|
|
113
|
+
The search is not bounded: without a nearer file it goes up to the root of the file system.
|
|
114
|
+
- `raf check` reads that file only when an open lot (`todo`, `doing`) carries `uses:`, and writes one gap per name it does not declare:
|
|
115
|
+
`✗ L3 utilise la ressource « banc », non déclarée dans <folder>/cadence.yaml (resources:)` (exit code 1). Without a declaring file the
|
|
116
|
+
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
|
|
117
|
+
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`);
|
|
118
|
+
an unreadable `cadence.yaml`, or a `resources:` that is not a list of valid names, is a gap naming the file
|
|
119
|
+
(`<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
|
|
120
|
+
`raf check` as before.
|
|
121
|
+
|
|
122
|
+
## Where the plan is
|
|
123
|
+
|
|
124
|
+
The plan is resolved in this order: `--file`, then `RAF_FILE`, then the `plan:` key of `cadence.yaml`, then `.cadence/raf.yaml`,
|
|
125
|
+
then the legacy `docs/plan/raf.yaml`.
|
|
126
|
+
|
|
127
|
+
- `raf init` creates `.cadence/raf.yaml` and `.cadence/notes/` (with a `.gitkeep`, so git tracks the empty folder). It refuses
|
|
128
|
+
when a plan already exists where it would create one, a legacy `docs/plan/raf.yaml` included: it never creates a second plan
|
|
129
|
+
beside it. With `plan: path` in `cadence.yaml`, or `--file`, the plan and nothing else is created there.
|
|
130
|
+
- A project that still has `docs/plan/raf.yaml` (and no `.cadence/raf.yaml`) keeps working, read and written as before. Every command
|
|
131
|
+
that loads the plan prints one line on **stderr** (standard output, `--json` included, is unchanged) with the move to do —
|
|
132
|
+
`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
|
|
133
|
+
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
|
|
134
|
+
printed when the path comes from `--file`, `RAF_FILE` or `plan:`.
|
|
135
|
+
- When both files exist, `.cadence/raf.yaml` is read and a stderr warning says that `docs/plan/raf.yaml` is ignored; nothing is written
|
|
136
|
+
or deleted.
|
|
137
|
+
- `.cadence/` is versioned, except `.cadence/runs/` (the state of the waves) and `.cadence/corrections.json` (the cache of
|
|
138
|
+
`cadence corrections`): `cadence orchestrate` adds those two to `.git/info/exclude` (it replaces the `/.cadence/` line an older
|
|
139
|
+
version wrote there) and the clean-tree guard ignores them, but a change to `.cadence/raf.yaml` or `.cadence/notes/` counts.
|
|
140
|
+
- `raf gantt` writes `gantt.html` next to the plan, so `.cadence/gantt.html` by default.
|
|
141
|
+
- `cadence lead tour`, `session --all` and `cadence orchestrate` recognise a project by `.cadence/raf.yaml`, `docs/plan/raf.yaml` or a
|
|
142
|
+
`cadence.yaml` with `plan:`.
|
|
143
|
+
|
|
144
|
+
## Moving the plan: `raf migrate`
|
|
145
|
+
|
|
146
|
+
`raf migrate [--dry-run]` moves a legacy `docs/plan/raf.yaml` to `.cadence/raf.yaml` and updates what reads the old path, so the
|
|
147
|
+
« ancien emplacement » line stops appearing in every session. It is the one command that works on the files of the repository
|
|
148
|
+
rather than on the plan; it prints no legacy warning and never commits (review the result with `git status`, then commit).
|
|
149
|
+
|
|
150
|
+
- **The plan** moves with `git mv` (`mv` outside a git repository, or when git does not track the file), `.cadence/` created if needed.
|
|
151
|
+
- **The readers** are the files that cite `docs/plan/raf.yaml` — plan-data scripts, tests, CI workflows, skills, documentation: git's
|
|
152
|
+
tracked and not-ignored files (the whole folder, without `.git` and `node_modules`, outside a repository). Every occurrence is
|
|
153
|
+
rewritten to `.cadence/raf.yaml`. The history is not a reader and is left alone: `CHANGELOG*`, `*.ndjson` journals and `.cadence/`.
|
|
154
|
+
- **`--dry-run`** prints the move and every file it would touch, with its number of references, and changes nothing.
|
|
155
|
+
- **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
|
|
156
|
+
replacement cannot follow — another file of the folder (`docs/plan/journal.ndjson`), the folder itself, a path built piece by piece
|
|
157
|
+
(`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
|
|
158
|
+
declare a file that cites the path without reading the plan (a fixture, a document about the old place) under `migrate.keep` in
|
|
159
|
+
`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.
|
|
160
|
+
- **It refuses** when `docs/plan/raf.yaml` and `.cadence/raf.yaml` both exist, and when neither does.
|
|
161
|
+
- **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
|
|
162
|
+
readers that remain, and says `rien à migrer` when there is none.
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
migrate:
|
|
166
|
+
keep: [docs/specs/**, test/legacy-plan.test.ts] # cite docs/plan/raf.yaml without reading the plan
|
|
167
|
+
```
|
|
168
|
+
|
|
94
169
|
## Plan file
|
|
95
170
|
|
|
96
171
|
```yaml
|
|
@@ -106,6 +181,7 @@ lots:
|
|
|
106
181
|
estimate: 2 # working days
|
|
107
182
|
quickwin: false
|
|
108
183
|
visible: true # user-facing: a news entry is expected when done
|
|
184
|
+
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
185
|
after: [L0]
|
|
110
186
|
gate: { count: 8, spec: true } # optional: not before 8 lots with `spec: true` are done (or `screen: page`)
|
|
111
187
|
created: 2026-09-28
|
|
@@ -125,6 +201,8 @@ format, and the plan stays writable:
|
|
|
125
201
|
plan: planning/todo.yaml
|
|
126
202
|
```
|
|
127
203
|
|
|
204
|
+
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.
|
|
205
|
+
|
|
128
206
|
A project that publishes its plan (a JSON generated from it and committed with it)
|
|
129
207
|
declares that file, so a commit touching only the plan and its published copy is
|
|
130
208
|
plan upkeep; the plan keeps raf's format and stays writable:
|
|
@@ -173,7 +251,7 @@ plan:
|
|
|
173
251
|
```
|
|
174
252
|
|
|
175
253
|
- Fields: `id`, `title`, `status`, `estimate`, `quickwin`, `visible`, `screen`, `spec`, `public`, `every`, `last`, `after`, `created`, `started`,
|
|
176
|
-
`finished`, `notes`, `repos`, `parent`, `gate
|
|
254
|
+
`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
255
|
its day; a note written as plain text is one note.
|
|
178
256
|
- `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
257
|
- `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.
|