@sylad/cadence 0.20.0 → 0.22.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.
@@ -8,7 +8,7 @@
8
8
  {
9
9
  "name": "cadence",
10
10
  "description": "Session start and close rituals driven by a versioned plan (raf), and deliveries proven by their effect. Needs the cadence CLI (npm i -g @sylad/cadence).",
11
- "version": "0.20.0",
11
+ "version": "0.22.0",
12
12
  "source": "./",
13
13
  "author": {
14
14
  "name": "Sylvain Ladoire"
@@ -16,8 +16,8 @@
16
16
  },
17
17
  {
18
18
  "name": "cadence-hud",
19
- "description": "A status band above the Claude Code prompt: context, 5 h / 7 d quota windows, cost per model, agents of the session and the running cadence orchestrate waves. No CLI needed.",
20
- "version": "0.3.4",
19
+ "description": "A status band above the Claude Code prompt: context, 5 h / 7 d quota windows, cost per model, agents of the session and the running cadence orchestrate waves. Works without the cadence CLI, except the per-project plan progress, which needs `cadence` on the PATH.",
20
+ "version": "0.4.0",
21
21
  "source": "./plugins/cadence-hud",
22
22
  "author": {
23
23
  "name": "Sylvain Ladoire"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cadence",
3
3
  "description": "A repo-native working method: session start and close rituals driven by a versioned plan (raf), deliveries proven by their effect, and three reviewer agents (UX, code, QA).",
4
- "version": "0.20.0",
4
+ "version": "0.22.0",
5
5
  "author": { "name": "Sylvain Ladoire" },
6
6
  "homepage": "https://github.com/Sylad/cadence",
7
7
  "repository": "https://github.com/Sylad/cadence",
package/README.md CHANGED
@@ -24,7 +24,7 @@ Code prompt that shows the session's context, quota, cost and the orchestrate wa
24
24
 
25
25
  ## What's new
26
26
 
27
- **0.20.0**: `cadence-hud` no longer misleads once the work is over: `/hud cmd` lists the background commands it counts (id, tool, origin, age, name), any other argument answers `argument inconnu` instead of hiding the band, commands with no end seen for an hour or no record go to a grey `N cmd sans fin vue`, a finished subagent takes its commands with it, two races that left a `1 cmd` counted forever are closed, and a finished wave shows grey without its budget then disappears after 30 minutes (L142); the `qa-reviewer` and `ux-reviewer` agents are read-only for real, their frontmatter listing Read, Grep, Glob, Bash and the Playwright tools (L140). **0.19.1**: `cadence-hud`'s `N cmd` counter goes back down when a Monitor expires, when the background commands a subagent started end with it, and at session start or plugin reload (L135); its `tool.call` and `prompt.submit` guard hooks carry a `.catch` on registration, which clears the `claude plugin validate` warning (L136). **0.19.0**: `cadence orchestrate` lifts a repository's lock and `pre-push` guard as soon as every lot of the wave that touches it is finished, so `cadence deliver` works there while the wave continues elsewhere (L132); a review that leaves a repository dirty hands back that lot only, suspends the lots still queued in that repository and keeps the other repositories running, its verdict reported instead of lost (L133); `cadence session close` re-run without a `session start` keeps the window since the last opening (L131); `cadence-hud` draws the lot rows of a wave in aligned columns on every surface (L134). **0.18.0**: `cadence session next` prints what it records (issue #6); `cadence session close` covers the period since the last `session start` (or the last close) and says so in its title, `--since` still wins (issue #7). **0.17.0**: `cadence-hud` shows an `N cmd` segment next to the agents, the background commands of the session (Bash run in the background and Monitor, subagents' included), tracked from the tool results and ended by the task's notification or `TaskStop`. **0.16.0**: a lot can declare **neighbouring repositories** (`repos:` key of the lot, `{ path, cite }` for a repository shared between projects): `cadence orchestrate` refuses, locks and guards them, gives them to every session as `--add-dir`, and `raf commits`, `raf show` and the review verdict read the commits that cite the lot there too. **0.15.0**: `hook.autostart` in `cadence.yaml` (`warn` by default, `refuse` or `start`): a pre-commit hook that refuses, or starts in the same commit, a lot still todo that the commit cites; `cadence session start|close --all [--depth n]`, one section per project under a folder. **0.14.0**: `cadence lead tour`, the lead's morning table of every project in a folder, printed by a program (one line per project: lots in progress, gaps, last notes, next ready lot, repository) in place of one subagent per project; the `lead` skill calls it. **0.13.1**: `cadence-hud` takes back the segments that fit once a big one has dropped (reset times, then the per-model consumption, now short: `opus 1.2M sonnet 800k`); `CADENCE_HUD_AMBIGUOUS=1` for a terminal that draws `▰ ▱ ⚙ │ ↻` in one cell. **0.13.0**: `cadence orchestrate --status --watch` (a wave's table redrawn in the terminal, which
27
+ **0.22.0**: `cadence orchestrate --continue` draws the next ready lot of the plan itself, in the declared priority, until the budget, the time window or a question stops it (L147); `cadence session context` prints the context of the session, which the `cadence-hud` band publishes, and the `lead` skill chains lots without the human while it stays under 60 % (L148); the band shows the progress of each project's plan, and `cadence lead tour --json` gains `progress` (L149, `cadence-hud` 0.4.0); the final review of a lot is always played, the lot's own budget bounding the writing passes only, with 65 k tokens reserved for it (L145); the cause « tests rouges après … » names the pass whose tests were red (L151). **0.21.0**: `cadence orchestrate --status` and `cadence lead tour` print the free session slots, which the `lead` skill reads (L141); `orchestrate.effort` sets the effort level of each pass of a wave (L137); `docs.sync` makes `raf check`, `lead tour` and the review brief report a document that does not follow the code (L143); `docs.articles` opens, after a green `cadence deliver`, a lot `Article <project> à rafraîchir` in the neighbouring repository (L144). **0.20.0**: `cadence-hud` no longer misleads once the work is over: `/hud cmd` lists the background commands it counts (id, tool, origin, age, name), any other argument answers `argument inconnu` instead of hiding the band, commands with no end seen for an hour or no record go to a grey `N cmd sans fin vue`, a finished subagent takes its commands with it, two races that left a `1 cmd` counted forever are closed, and a finished wave shows grey without its budget then disappears after 30 minutes (L142); the `qa-reviewer` and `ux-reviewer` agents are read-only for real, their frontmatter listing Read, Grep, Glob, Bash and the Playwright tools (L140). **0.19.1**: `cadence-hud`'s `N cmd` counter goes back down when a Monitor expires, when the background commands a subagent started end with it, and at session start or plugin reload (L135); its `tool.call` and `prompt.submit` guard hooks carry a `.catch` on registration, which clears the `claude plugin validate` warning (L136). **0.19.0**: `cadence orchestrate` lifts a repository's lock and `pre-push` guard as soon as every lot of the wave that touches it is finished, so `cadence deliver` works there while the wave continues elsewhere (L132); a review that leaves a repository dirty hands back that lot only, suspends the lots still queued in that repository and keeps the other repositories running, its verdict reported instead of lost (L133); `cadence session close` re-run without a `session start` keeps the window since the last opening (L131); `cadence-hud` draws the lot rows of a wave in aligned columns on every surface (L134). **0.18.0**: `cadence session next` prints what it records (issue #6); `cadence session close` covers the period since the last `session start` (or the last close) and says so in its title, `--since` still wins (issue #7). **0.17.0**: `cadence-hud` shows an `N cmd` segment next to the agents, the background commands of the session (Bash run in the background and Monitor, subagents' included), tracked from the tool results and ended by the task's notification or `TaskStop`. **0.16.0**: a lot can declare **neighbouring repositories** (`repos:` key of the lot, `{ path, cite }` for a repository shared between projects): `cadence orchestrate` refuses, locks and guards them, gives them to every session as `--add-dir`, and `raf commits`, `raf show` and the review verdict read the commits that cite the lot there too. **0.15.0**: `hook.autostart` in `cadence.yaml` (`warn` by default, `refuse` or `start`): a pre-commit hook that refuses, or starts in the same commit, a lot still todo that the commit cites; `cadence session start|close --all [--depth n]`, one section per project under a folder. **0.14.0**: `cadence lead tour`, the lead's morning table of every project in a folder, printed by a program (one line per project: lots in progress, gaps, last notes, next ready lot, repository) in place of one subagent per project; the `lead` skill calls it. **0.13.1**: `cadence-hud` takes back the segments that fit once a big one has dropped (reset times, then the per-model consumption, now short: `opus 1.2M sonnet 800k`); `CADENCE_HUD_AMBIGUOUS=1` for a terminal that draws `▰ ▱ ⚙ │ ↻` in one cell. **0.13.0**: `cadence orchestrate --status --watch` (a wave's table redrawn in the terminal, which
28
28
  stops with the wave) and the lead skill that names the wave and follows its journal; lot ids with `/`
29
29
  (sub-tasks of a read-only plan) accepted by `orchestrate`; `cadence-hud`'s first line measured in
30
30
  terminal cells and kept to ASCII; `publish.yml` on the v5 actions. **0.12.0**: the `cadence-hud` plugin (a status band above the Claude Code prompt),
@@ -341,6 +341,64 @@ lot in each neighbour (the one `raf commits` lists last: it cites the lot, falls
341
341
  neighbour has none), and refuses to record when a listed repository cannot be read. `raf check` does not audit the
342
342
  neighbours: their staleness is not checked, only the project's own commits are.
343
343
 
344
+ ### Documentation that follows the code
345
+
346
+ A stale README misleads. `cadence.yaml` can declare which documents must follow which code:
347
+
348
+ ```yaml
349
+ docs:
350
+ sync:
351
+ - paths: [src/**, bin/*.js] # what changes…
352
+ docs: [README.md, docs/usage.md] # …must change one of these, in the same lot
353
+ - paths: [templates/]
354
+ docs: [CLAUDE.md]
355
+ - paths: [frontend/src/**, '!**/*.test.tsx'] # a pattern starting with ! leaves files out
356
+ docs: [README.md]
357
+ since: 2026-10-09 # optional: also audit lots finished on or after this day
358
+ ```
359
+
360
+ A pattern is an exact path, with `*` (inside a folder), `**` (across folders), `?`, or a trailing `/` for
361
+ a whole folder. `raf check` takes the **work commits** of every lot in progress (plan-only commits do
362
+ not count; a lot finished on or after `docs.since` is audited too) and, for each pair, reports the lot when
363
+ those commits touch `paths` without any of them touching a file of `docs`:
364
+
365
+ ```
366
+ ✗ L7 : documentation en retard — src/a.ts, src/b.ts sans toucher README.md ou docs/usage.md (docs.sync)
367
+ ```
368
+
369
+ It counts as drift: exit code 1, shown in the `dérive` column of `cadence lead tour`, and the review
370
+ brief of `cadence orchestrate` (`review` and `review-small`) carries the list the program computed,
371
+ to be reported as one **major** finding per line — a stale document is not left to the reviewer's
372
+ memory of a sentence. Touching a document is what the program checks; whether its text is true is
373
+ still the review's reading. The commits of neighbouring repositories (`repos:`) are not audited.
374
+
375
+ #### An article elsewhere that follows the delivery
376
+
377
+ A case study that lives in a neighbouring repository (the project's article on a showcase site) goes stale
378
+ the same way. `docs.articles` names it:
379
+
380
+ ```yaml
381
+ docs:
382
+ articles:
383
+ - repo: ../claude-code-codex # neighbouring repository, relative to the project
384
+ file: src/pages/cas/demo.md # the article in it
385
+ name: demo # optional: the name in the lot title (default: the plan's project)
386
+ ```
387
+
388
+ After a **green** `cadence deliver`, for the lots it lists as delivered **that have a public title** (`public:`, or
389
+ the title of their Nouveautés entry), cadence opens in this repository's plan the lot
390
+ `Article demo à rafraîchir` (todo, estimate 0.5, `repos: [{ path: ../claude-code-codex, cite: demo }]`), with a note
391
+ that quotes the delivery, the public titles delivered, the article and the **sections to read again**: the headings
392
+ (`#` lines, or `<h1>`…`<h6>` of an Astro page) that share a word of five letters or more with a delivered title,
393
+ else all the `##` headings. The lot is then played like any other, in an ordinary wave (`cadence orchestrate`: an
394
+ implementer rewrites the article in the neighbouring repository, the review checks it against the note). If a lot
395
+ of that title is still open, it receives a note instead of a duplicate. Two entries with the same name (the same
396
+ project told in two neighbouring repositories) share one lot, which declares both repositories in `repos:`. Internal lots (no public title) open nothing;
397
+ a red delivery opens nothing; `cadence deliver --dry-run` announces the articles. The plan is written but **not
398
+ committed** (`deliver` never commits): the line `article à rafraîchir : L9 …` says so. With a plan kept by another
399
+ tool (read-only), the same line carries the instruction to open the lot with that tool. This replaces the manual
400
+ `sync-site-docs` skill.
401
+
344
402
  ### QA review
345
403
 
346
404
  No gate and no command here: the QA review comes **after** a delivery, and
@@ -423,6 +481,11 @@ cadence session close --all --depth 2 # sub-folder holding a plan (--depth n:
423
481
  # exit = the worst code of the projects (2 error, 1 open, 0 closed)
424
482
  cadence session next "finish L3" "review L4" # shown by the next session start; replaces the previous notes
425
483
  cadence session next --clear # erase those notes, on purpose
484
+ cadence session context # `ctx 42 % (84000/200000)`: the context of YOUR session, as the cadence-hud band publishes
485
+ # it: the one named by $CLAUDE_CODE_SESSION_ID (set in Claude Code's Bash); --session <id>
486
+ # overrides it; with no variable, the folder decides only if a single session publishes for it
487
+ # (two sessions in one folder = exit 2, "pass --session"); exit 2 too when no fresh figure
488
+ # (band not loaded) = treat as at the threshold
426
489
  ```
427
490
 
428
491
  `session start` records its time in the worktree's state (`session.json`, under the git dir); the next
@@ -525,13 +588,21 @@ beta · en cours rien · dérive aucune · notes : aucune · prochain T2 Second
525
588
  | Column | Content |
526
589
  |---|---|
527
590
  | `en cours` | ids of the lots in progress; `(aucune activité)` when it has none at all, `(silencieux Nj)` when the lot's last activity (commit, note or start) is more than `--idle` days old (default 3) |
528
- | `dérive` | the gaps `raf check` reports, counted and the first two shown |
591
+ | `dérive` | the gaps `raf check` reports (a lot whose commits leave a document behind, [`docs.sync`](#documentation-that-follows-the-code), included), counted and the first two shown |
529
592
  | `notes` | the notes left by the last `session close`, joined with ` / ` and cut at 120 characters |
530
593
  | `prochain` | the first ready lot (quick wins first) — id and title cut at 60 characters |
531
594
  | `dépôt` | `non commité` (modified or untracked files), `non poussé` (commits ahead of the upstream), `livraison en cours` (live delivery lock); `propre` otherwise |
532
595
 
596
+ When an orchestrated wave is live, one more line closes the tour (not part of `--json`): `vagues en cours : 1 · sessions en cours : 0 · créneaux libres : 1 sur 2` — the lead reads the free slots there instead of computing them (L141).
597
+
533
598
  `--json` prints the same content as an array of objects (`project`, `doing`, `drift`, `notes`,
534
- `next`, `repo`, and `error` when the project could not be read). The tour is read-only: it changes
599
+ `next`, `repo`, and `error` when the project could not be read), plus `progress` — `{ done, doing, todo, added7 }`,
600
+ the lots done / in progress / to do (dropped and recurring lots left out) and those created in the last 7 days (L149), which
601
+ [`cadence-hud`](plugins/cadence-hud/README.md) draws as one progress bar per project. Projects come in the order of the
602
+ `priority:` key of the folder's `cadence.yaml` (the one `orchestrate --continue` reads), the others after, alphabetically; an unreadable `cadence.yaml` or
603
+ a `priority:` that is not a list of names (`priority: ol`, `priority: []`) keeps the alphabetical order and adds a first row `cadence.yaml · ✗ erreur : <cause>` (`project: "cadence.yaml"` with `error` in `--json`).
604
+ "Created in the last 7 days" means today and the six days before. When the folder has no sub-project but is itself a project, the tour is that project's single line
605
+ (this is what `cadence-hud` asks from a session opened inside a project). The tour is read-only: it changes
535
606
  no plan. It exits 0 even when a project is in error — that project's line reads
536
607
  `beta · ✗ erreur : <cause>`. The `lead` skill runs it instead of one subagent per project.
537
608
 
@@ -634,6 +705,8 @@ cadence deliver # 0 delivered and verified · 1 a step failed · 2
634
705
  reservation number that looks like one) is not a delivered lot. Plan upkeep
635
706
  commits (see "Plan upkeep needs no lot") are skipped in that list: they cite
636
707
  lots without delivering anything.
708
+ - With [`docs.articles`](#an-article-elsewhere-that-follows-the-delivery), a green delivery of lots with a
709
+ public title also opens the lot `Article <project> à rafraîchir` in the plan.
637
710
 
638
711
  ### A project with its own delivery script
639
712
 
@@ -751,11 +824,40 @@ cadence orchestrate maritime-atlas:Q4/accueil-4-ux12@haiku # a lot id may cont
751
824
  cadence orchestrate … --budget 1.5M # 1500000, 1.5M, 800k; default 2M
752
825
  cadence orchestrate … --max-sessions 3 # sessions running at the same moment, all waves together; default 2
753
826
  cadence orchestrate … --dry-run # preconditions + the plan of the wave; nothing is started
827
+ cadence orchestrate --continue [--until 18:00] [--priority cadence,maritime] # the wave draws the next ready lot itself (below)
754
828
  cadence orchestrate --status [<wave>] # the live waves and the repositories they hold, then the table (default: the last wave of this folder)
755
829
  cadence orchestrate --status [<wave>] --watch [--interval 10] # the same, redrawn every 10 s (--interval in seconds); stops by itself when no wave is running (with an explicit `<wave>`: when that wave is no longer running)
756
830
  cadence orchestrate --resume [<wave>] [--budget 1M] [--answer ol-companion:L22 "reply"]
757
831
  ```
758
832
 
833
+ **`--continue` — idle time (L147)**: lots given on the command line open the wave (none: the first ones are drawn from the
834
+ plan too); then, each time the lots in play are finished, the wave **draws the next ready lots of the plan** — up to the
835
+ session cap — and plays them in the same wave (same budget, same state folder, journal line `continue : tire …`). A lot
836
+ is drawn when it is `todo`, its `after` is lifted, it is not recurring, neither its title nor a note says « à décider »
837
+ (« à décider avec … », « (à décider) »: a lot waiting for a human decision), it is not already in the wave, and its own budget (derived from its estimate)
838
+ fits in the budget left — otherwise the next one that fits is taken (a lot that does not fit in what is left of the current
839
+ round is not dropped: it is tried again at the next round, once the budget allows it). A round takes at most **one lot per
840
+ repository** while other repositories have ready lots — the pool plays the lots of one repository one after the other, so a
841
+ second one would leave a session idle — and is completed with the same repository when nothing else is ready. Projects are visited in the declared **priority**:
842
+ `--priority cadence,maritime`, or a `priority: [cadence, maritime]` list in the `cadence.yaml` of the folder you launch from
843
+ (the parent of the projects); a name also covers `name-…` (`maritime` → `maritime-atlas`), the other projects come after, in
844
+ alphabetical order. Launched from inside a project, only that project is drawn. A `priority:` key that cannot be read is refused before anything
845
+ starts, lots given or not. A candidate that preflight refuses (dirty tree, Node missing…) is skipped with its cause in the
846
+ journal (`continue : lot sauté — <cause>`; for the first draw also on the error output). Drawing stops — `continue : arrêt — <reason>` — at: the budget, the
847
+ time window (`--until HH:MM`, today's clock: nothing new is drawn from then on, a running lot is not cut), the usage limit,
848
+ a question asked (the wave stays resumable with `--answer`), two lots handed back in a row, an interrupted wave, or no ready
849
+ lot whose estimate fits the budget left. `--resume … --continue` draws again after the resumed lots. `--dry-run --continue` replays the
850
+ draws round by round with the same function as the wave (one lot per repository per round, pre-check, budget counted on the estimates) and
851
+ names the candidates skipped with their cause: `tour 1 : a:L2, b:L2 · tour 2 : a:L3, a:L4`. Drawing happens between rounds: a round of up to `--max-sessions` lots must finish before the next draw.
852
+
853
+ **Chaining without the human (L148)**: the `lead` skill, once the human has chosen the first lots and the order, does not ask « next? »
854
+ after a lot that comes back ready: it re-verifies it, records `raf done`, pushes, delivers, has the `qa-reviewer` check the
855
+ delivered app, then starts the next wave with `--continue`, in the declared priority — one project at a time, within the two-session
856
+ limit. Before each new wave it reads the context of its own session with `cadence session context` (the figure of the `ctx` segment of the `cadence-hud` band, which publishes the context of each session to `~/.cadence/orchestrate/hud-context/<session id>.json`; the command picks the file named by `$CLAUDE_CODE_SESSION_ID`, set in the environment of the session's Bash, never "the latest file of the folder" — two sessions of one folder alternate; without the band loaded in that session the command exits with code 2, and a missing or stale figure counts as at the threshold); from
857
+ **60 %** upward it starts nothing: it runs `session-close` in each project touched, writes its memory and records three lines for next
858
+ time (`cadence session next`). Stays with the human: the first choice and the order, the questions a session raised, the UX
859
+ reservations; a lot that is not ready, a red delivery or a blocking QA finding is reported and not chained over.
860
+
759
861
  **Lot ids containing `/` (L120)**: the first `:` separates the project from the lot, a final `@` the model, and the lot
760
862
  keeps its `/` (`maritime-atlas:Q4/accueil-4-ux12@haiku`); this holds for `--dry-run`, `--status`, `--resume` and
761
863
  `--answer` too. In `.cadence/runs/<wave>/` the `/` of the lot becomes `__` (`maritime-atlas--Q4__accueil-4-ux12.json`,
@@ -796,6 +898,22 @@ orchestrate:
796
898
 
797
899
  `full` is also the model of the UX review and of the single pass of a small lot that is not light.
798
900
 
901
+ **Effort level per pass** (L137). Each session is launched with `claude -p --effort <level>` (Claude Code 2.1.284; an
902
+ older `claude` is refused at launch unless every pass is `default`). Defaults: `precheck` low (it only reads),
903
+ `implement` and `fix` medium, `review` high (`review-small` follows `review`), `ux` high. A project overrides any of them
904
+ in `cadence.yaml`; the levels are `low`, `medium`, `high`, `xhigh`, `max`, and `default` sends no `--effort` (the
905
+ level of the session, as before this key). The format-retry session (`--resume`) keeps the level of its pass, and
906
+ `--dry-run` shows the flag in each command line.
907
+
908
+ ```yaml
909
+ orchestrate:
910
+ effort: { precheck: low, implement: medium, fix: medium, review: high, ux: high } # defaults
911
+ ```
912
+
913
+ To measure what a level costs, run the same kind of wave before and after changing the key: each step of
914
+ `<wave>/<project>--<lot>.json` records its `effort`, its `tokens` and its `started` / `ended` times (a step from a wave
915
+ older than L137, or with `default`, has no `effort`); compare the totals per `kind` between the two waves.
916
+
799
917
  **Pre-check « deliverable already present? »** (L77). Before the first implementation of a lot that has no commit yet, a
800
918
  short read-only session (Sonnet, `precheck` step, brief `templates/orchestrate/precheck.md`) looks in the repository for
801
919
  what the lot asks for (another lot, or a correction, may have done it already). It answers `oui` (everything is there,
@@ -919,7 +1037,7 @@ A slot or a registry entry is owned by a pid **and** its start time: a reused pi
919
1037
  identifiers are reserved atomically (`-2`, `-3` suffix when two waves start in the same minute; an existing
920
1038
  `--wave` is refused). The registry of live waves and the slots live under `~/.cadence/orchestrate/` (`CADENCE_HOME`
921
1039
  to move it): `cadence orchestrate --status` lists, from any folder, the live waves, the repositories each
922
- still holds (those not yet released), the cap of each wave and the slots in use.
1040
+ still holds (those not yet released), the cap of each wave, the slots in use and the **free slots** (`créneaux libres : N sur 2`: 2 minus, for each live wave, the smaller of its cap and the repositories it still holds — the figure the `lead` skill reads before starting a subagent). Without an identifier it then prints the table of the wave launched most recently from this folder (by launch time, not by alphabetical order of the identifier).
923
1041
 
924
1042
  **Guards, imposed by the code**: a global cap of simultaneous sessions, one wave per repository at
925
1043
  a time (above); `Agent`, `git push`, `cadence deliver`, `raf done|review|ux` are denied to the sessions; a
@@ -932,8 +1050,14 @@ tree (screenshots left at the root, a commit) is an incident of that lot only: t
932
1050
 
933
1051
  **Budget**: the wave counts input + cache writes + output tokens (default 2 M); cache reads are kept and
934
1052
  shown apart. Each lot also has its own budget derived from its estimate (400 k tokens per day, floor 200 k, shown by
935
- `--dry-run`): a lot that spent it gets no further session and is handed back to the lead, the wave budget stays for
936
- the others. When the budget (or the usage limit) is reached no new session starts, the running ones
1053
+ `--dry-run`): it bounds the *writing* passes, never the reviews. A lot that spent it gets no further implementation or
1054
+ fix and is handed back to the lead, but its review (and UX review) is played first; a fix pass starts only if
1055
+ the remaining budget can also pay the review that follows it (65 k tokens reserved: a fixed amount close to the 90th
1056
+ percentile of the full reviews, 62.7 k over 187 reviews; the short review after the minors pass has a higher p90, 67.6 k
1057
+ over 114; measured on 2026-10-09 in the wave journals `.cadence/runs`, `tokens.counted`, 67 waves from 10-04 to 10-09).
1058
+ Exception: when the tests are red after a write pass, the lot goes to a fix pass without a review; if that fix pass is
1059
+ refused for lack of budget the lot is handed back to the lead without a review, with its findings, and the cause names
1060
+ the red tests: the pass it names is the one whose tests were red, counted by its number (« la passe fix 2 », « l'implémentation »), and « la passe des mineurs » appears only when that pass was the one played. The wave budget stays for the others. When the budget (or the usage limit) is reached no new session starts, the running ones
937
1061
  finish, the wave is *suspended* (exit code 3) and `--resume --budget …` continues. A session that returns
938
1062
  nothing readable, times out (45 min for work, 25 for a review) or fails is not retried; the lot is handed
939
1063
  back with the cause. Exit codes: 0 every lot ready · 1 at least one lot handed back (question, failure,
@@ -964,6 +1088,7 @@ orchestrate:
964
1088
  test: npm test # run by the orchestrator after a work step (optional)
965
1089
  build: npm run build # run after the tests; both results go to the reviewer, who does not redo them (optional)
966
1090
  precheck: true # default: before the first implementation of a lot with no commit, a read-only Sonnet session checks whether the deliverable is already in the repository (see below); false skips it
1091
+ effort: { review: xhigh } # effort level per pass (see « Effort level per pass »): precheck, implement, fix, review, ux
967
1092
  ux: http://localhost:4200 # a URL, a launch command, or { command, url, timeout? } — for the UX review (see below)
968
1093
  permissionMode: auto # default
969
1094
  addDirs: [/home/me/projects/tmp] # extra directories the sessions may use
@@ -1138,7 +1263,19 @@ after it ended, then disappears. A background command counted for more than an h
1138
1263
  record of when it started, is no longer shown as running: it moves to a grey `N cmd sans fin
1139
1264
  vue` ("end never seen") right after `N cmd`, because its end notification may never have reached
1140
1265
  the session. Waves are read from disk (`~/.cadence/orchestrate/waves/`, then `.cadence/runs/`); no
1141
- cadence command is run, and the CLI is not required.
1266
+ cadence command is run, and the CLI is not required for those. One more block needs it: the **progress of the plans**,
1267
+ one line per active project (a project with open lots), in the order of `priority:`:
1268
+
1269
+ ```
1270
+ ol ▮▮▮▮▮▯▯▯▯▯ 39/78 · 0 en cours · +3 cette semaine
1271
+ cadence ▮▮▮▮▮▯▯▯▯▯ 10/20 · 2 en cours
1272
+ ```
1273
+
1274
+ The bar is lots done over lots done + in progress + to do (dropped and recurring lots left out);
1275
+ `+N cette semaine` counts the lots created in the last 7 days (today and the six before) and is left out at zero. The source is
1276
+ `cadence lead tour <folder> --json` (the folder a wave was launched from, otherwise the session's: a folder of projects gives one line per project, a project with no sub-project gives its own single line),
1277
+ read again at every wave transition and otherwise every minute; without `cadence` on the PATH, the
1278
+ lines are simply not drawn.
1142
1279
 
1143
1280
  Install it as a plugin from the same marketplace:
1144
1281
 
@@ -1149,7 +1286,8 @@ Install it as a plugin from the same marketplace:
1149
1286
 
1150
1287
  `/hud` hides or shows the band. `/hud cmd` lists the background commands being counted (id, tool,
1151
1288
  origin `session` or `agent <id>`, age, name; `(sans fin vue)` on the ones above). Any other argument
1152
- answers `argument inconnu : … (attendu : cmd)` and leaves the band as it is. The source lives in [`plugins/cadence-hud`](plugins/cadence-hud/README.md)
1289
+ `/hud projets` folds the project list to one line, and opens it again. Any other argument
1290
+ answers `argument inconnu : … (attendu : cmd, projets)` and leaves the band as it is. The source lives in [`plugins/cadence-hud`](plugins/cadence-hud/README.md)
1153
1291
  (its own README has the details of every cell); it is not part of the npm package. To work on it:
1154
1292
  `claude plugin validate plugins/cadence-hud`, `claude plugin test plugins/cadence-hud`, and
1155
1293
  `tsc -p plugins/cadence-hud` once Claude Code has loaded the plugin at least once (it generates
package/bin/cadence.js CHANGED
@@ -29,6 +29,7 @@ if (tool === 'raf') {
29
29
  cadence session start|close --all [--depth 1] depuis un dossier non dépôt : une section par projet des sous-dossiers
30
30
  cadence session next "ligne" … notes pour la prochaine session (remplacent les précédentes)
31
31
  cadence session next --clear efface ces notes ; sans ligne ni --clear, la commande refuse
32
+ cadence session context [--session <id>] contexte de la session (variable CLAUDE_CODE_SESSION_ID, sinon la seule du dossier) publié par la bande cadence-hud ; code 2 sans chiffre frais
32
33
  cadence deliver [--dry-run] [--sha rév] [--config cadence.yaml] [-- arguments du script du projet]
33
34
  CI du sha poussé → déploiement → vérifications de l'effet ;
34
35
  ou le script de livraison du projet (deliver.script), sous verrou et journal
@@ -38,8 +39,10 @@ if (tool === 'raf') {
38
39
  cadence orchestrate <projet>:<lot>[@modèle]… [--budget 2M] [--max-sessions 2] [--wave id] [--dry-run]
39
40
  une session claude neuve par étape (implémentation, revues, corrections) ; --status, --resume ;
40
41
  --max-sessions : sessions simultanées, toutes vagues confondues (CADENCE_MAX_SESSIONS)
42
+ --continue [--until HH:MM] [--priority a,b] : tire seul les lots prêts du plan, jusqu'à une borne
41
43
  cadence lead tour [dossier] [--idle 3] [--json]
42
- le tableau du lead, sans modèle : une ligne par sous-dossier qui a un plan
44
+ le tableau du lead, sans modèle : une ligne par sous-dossier qui a un plan, ou la ligne du dossier courant
45
+ s'il est lui-même un projet ; une première ligne « cadence.yaml · ✗ erreur » si priority: est illisible
43
46
  cadence skills install [--dir .claude] [--force]
44
47
  installe les skills Claude Code session-start, session-close, deliver et l'agent ux-reviewer`);
45
48
  process.exitCode = !tool || ['help', '--help', '-h'].includes(tool) ? 0 : 2;
@@ -0,0 +1,86 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { isOpen } from './plan.js';
4
+ /** Titre du lot ouvert à la livraison. */
5
+ export const articleTitle = (name) => `Article ${name} à rafraîchir`;
6
+ const SECTIONS_SHOWN = 12;
7
+ /** Titres d'un article : lignes `#` du Markdown (hors en-tête YAML) et balises `<h1>`…`<h6>` d'une page Astro ou MDX. */
8
+ export function articleHeadings(text) {
9
+ const body = text.replace(/^/, '').replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, '');
10
+ const found = [];
11
+ for (const m of body.matchAll(/^(#{1,6})[ \t]+(.+?)[ \t]*#*[ \t]*$/gm))
12
+ found.push({ at: m.index, heading: { level: m[1].length, text: m[2].trim() } });
13
+ for (const m of body.matchAll(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi)) {
14
+ const t = m[2].replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
15
+ if (t)
16
+ found.push({ at: m.index, heading: { level: Number(m[1]), text: t } });
17
+ }
18
+ return found.sort((a, b) => a.at - b.at).map((f) => f.heading);
19
+ }
20
+ const words = (s) => new Set(s
21
+ .normalize('NFD')
22
+ .replace(/[̀-ͯ]/g, '')
23
+ .toLowerCase()
24
+ .split(/[^a-z0-9]+/)
25
+ .filter((w) => w.length >= 5));
26
+ /**
27
+ * Sections à relire : celles dont le titre partage un mot (5 lettres au moins, sans accents ni casse) avec un titre public
28
+ * livré ; sans recoupement, toutes les sections de premier niveau (`##`), faute de mieux.
29
+ */
30
+ export function articleSections(text, titles) {
31
+ const headings = articleHeadings(text);
32
+ const wanted = new Set(titles.flatMap((t) => [...words(t)]));
33
+ const matching = headings.filter((h) => [...words(h.text)].some((w) => wanted.has(w))).map((h) => h.text);
34
+ const picked = matching.length ? matching : headings.filter((h) => h.level === 2).map((h) => h.text);
35
+ return picked.slice(0, SECTIONS_SHOWN);
36
+ }
37
+ /**
38
+ * Après une livraison verte (L144) : pour chaque article déclaré, ouvre dans ce dépôt le lot « Article <projet> à rafraîchir »
39
+ * — ou, s'il en est déjà un d'ouvert, y ajoute une note. La note cite les titres publics livrés et les sections à relire ;
40
+ * le lot déclare le ou les dépôts voisins (`repos`, filtrés par le nom du projet : le dépôt de l'article est partagé entre projets).
41
+ * Les entrées de même nom partagent un seul lot, qui déclare tous leurs dépôts.
42
+ * Ne sauve pas le plan. Aucun titre public livré : rien à rafraîchir.
43
+ */
44
+ export function openArticleLots(o) {
45
+ const result = { opened: [], noted: [], manual: [], warnings: [] };
46
+ const titles = [...new Set(o.delivered.map(o.publicTitle).filter((t) => !!t))];
47
+ if (!titles.length)
48
+ return result;
49
+ // Un lot par nom de projet : deux articles de même nom (deux dépôts voisins) se jouent ensemble dans le même lot.
50
+ const byName = new Map();
51
+ for (const rule of o.rules) {
52
+ const name = rule.name ?? o.plan.project;
53
+ byName.set(name, [...(byName.get(name) ?? []), rule]);
54
+ }
55
+ for (const [name, rules] of byName) {
56
+ const title = articleTitle(name);
57
+ const repos = [...new Set(rules.map((r) => r.repo))];
58
+ const articles = rules.map((rule) => {
59
+ const path = resolve(o.root, rule.repo, rule.file);
60
+ const sections = existsSync(path) ? articleSections(readFileSync(path, 'utf8'), titles) : null;
61
+ if (sections === null)
62
+ result.warnings.push(`article introuvable : ${path}`);
63
+ return (`Article : ${rule.repo}/${rule.file}. ` +
64
+ (sections === null ? 'Fichier introuvable à l\'ouverture du lot : vérifier le chemin (docs.articles). ' : `Sections à relire : ${sections.length ? sections.join(' ; ') : '(aucun titre trouvé : relire tout l\'article)'}. `));
65
+ });
66
+ const note = `Livraison ${o.sha.slice(0, 7)} : titres publics livrés ${titles.map((t) => `« ${t} »`).join(', ')}. ` +
67
+ articles.join('') +
68
+ `Réécrire ce que ces changements rendent faux ou incomplet, sans rien inventer ; les commits ${repos.length > 1 ? 'des dépôts' : 'du dépôt'} ${repos.join(', ')} citent le lot et « ${name} ».`;
69
+ if (o.plan.readonly) {
70
+ result.manual.push(`${title} — plan tenu par un autre outil, ouvrir le lot avec lui : ${note}`);
71
+ continue;
72
+ }
73
+ const open = o.plan.lots().find((l) => l.title === title && isOpen(l.status));
74
+ if (open) {
75
+ o.plan.note(open.id, note, o.today);
76
+ result.noted.push(open.id);
77
+ continue;
78
+ }
79
+ const id = o.plan.add(title, o.today, { estimate: 0.5 });
80
+ o.plan.setRepos(id, repos.map((path) => ({ path, cite: name })));
81
+ o.plan.note(id, note, o.today);
82
+ result.opened.push(id);
83
+ }
84
+ return result;
85
+ }
86
+ export const describeArticle = (rule) => join(rule.repo, rule.file);
package/dist/audit.js CHANGED
@@ -4,7 +4,8 @@ import { check } from './check.js';
4
4
  import { parse } from 'yaml';
5
5
  import { changedFiles, fileAt, readCommits } from './git.js';
6
6
  import { linkCommits } from './link.js';
7
- import { readNewsConfig } from './config.js';
7
+ import { readDocsConfig, readNewsConfig } from './config.js';
8
+ import { docSyncGaps, filesOf, gapMessage } from './docsync.js';
8
9
  import { loadEntries, newsIssues, PUBLIC_TITLE_DEFAULT, publicTitleTooLong, reusedNewsTitle } from './news.js';
9
10
  import { isRecurring } from './recurring.js';
10
11
  import { isOpen } from './plan.js';
@@ -109,11 +110,24 @@ export function audit(plan, root, newsDir, today, opts = {}) {
109
110
  const m = l.public ? publicTitleTooLong(l.public, max) : reused ? publicTitleTooLong(reused, max) : null;
110
111
  return m ? [{ message: `${l.id} : ${reused && !l.public ? m.replace('titre public', 'titre public repris de la Nouveauté') : m}` }] : [];
111
112
  });
112
- const issues = [...check(lots, { ...linked, byLot: all.byLot }, today, opts.idle ?? 7), ...newsIssues(lots, entries, newsDir), ...titles, ...gates, ...(plan.hasPublicField ? missingPublicTitles(lots, entries) : []),
113
+ const issues = [...check(lots, { ...linked, byLot: all.byLot }, today, opts.idle ?? 7), ...newsIssues(lots, entries, newsDir), ...titles, ...gates, ...docSyncIssues(plan, root, all.byLot), ...(plan.hasPublicField ? missingPublicTitles(lots, entries) : []),
113
114
  ...plan.ignore.invalid.map((src) => ({ message: `ignore : motif invalide « ${src} »` }))];
114
115
  // Un plan en lecture seule se corrige avec l'outil du projet : ne pas conseiller une commande raf qui refuserait.
115
116
  return plan.readonly ? issues.map((i) => ({ ...i, message: i.message.replace(/ — raf (start|public) .*$/, '') })) : issues;
116
117
  }
118
+ /**
119
+ * Documentation en retard (L143, clé `docs.sync`) : un lot en cours — ou terminé à partir de `docs.since` — dont les commits de
120
+ * travail touchent des chemins sans toucher le document que la règle désigne. Les commits de plan ne comptent pas.
121
+ */
122
+ export function docSyncIssues(plan, root, byLot) {
123
+ const { sync, since } = readDocsConfig(plan.configFile ?? join(root, 'cadence.yaml'));
124
+ if (!sync.length)
125
+ return [];
126
+ return plan
127
+ .lots()
128
+ .filter((l) => l.status === 'doing' || (l.status === 'done' && !!since && !!l.finished && l.finished >= since))
129
+ .flatMap((l) => docSyncGaps(sync, filesOf(root, workCommits(plan, root, byLot.get(l.id) ?? []))).map((g) => ({ message: gapMessage(l.id, g) })));
130
+ }
117
131
  /** Commits qui portent du travail sur un lot : ni antérieurs à l'adoption du plan, ni réduits au plan. */
118
132
  function workCommits(plan, root, commits) {
119
133
  const adopted = plan.since;
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
2
2
  import { basename, dirname, join, relative, resolve } from 'node:path';
3
3
  import { parseArgs } from 'node:util';
4
- import { readHookConfig, readNewsConfig, readPlanConfig, readSessionConfig } from './config.js';
4
+ import { readDocsConfig, readHookConfig, readNewsConfig, readPlanConfig, readSessionConfig } from './config.js';
5
5
  import { repoSections, repoShas, resolveLotRepos } from './repos.js';
6
6
  import { audit, exemptPlanOnly, ownFiles, isPlanOnly, lotWork, nextUp, planCommits, unreviewedWork } from './audit.js';
7
7
  import { short } from './check.js';
@@ -18,8 +18,9 @@ import { dueLine, isRecurring, recurringByDue } from './recurring.js';
18
18
  import { schedule } from './schedule.js';
19
19
  import { AGENTS_DIR, installAgents, installSkills, SKILLS_DIR } from './skills.js';
20
20
  import { findProjects, leadTour, tourLine } from './lead.js';
21
- import { orchestrate, realOrchestrateDeps } from './orchestrate/command.js';
21
+ import { liveLines, orchestrate, realOrchestrateDeps } from './orchestrate/command.js';
22
22
  import { activeLock, REPO_LOCK } from './orchestrate/lock.js';
23
+ import { readHudContext } from './hud-context.js';
23
24
  import { sessionClose, sessionStart } from './session.js';
24
25
  import { clearNext, readNext, sharedStateDir, stateDir, writeNext } from './state.js';
25
26
  const HELP = `raf — plan « reste à faire » versionné dans le dépôt, relié aux commits
@@ -42,8 +43,8 @@ const HELP = `raf — plan « reste à faire » versionné dans le dépôt, reli
42
43
  raf ignore <sha> | "<sujet exact>" [--reason texte] acquitte un commit sans lot (ou citant un id inconnu) sans réécrire l'historique ; raf check --ignored les liste
43
44
  raf gantt [-o docs/plan/gantt.html]
44
45
  raf hook install (pre-commit et post-commit ; cadence.yaml hook.autostart: warn|refuse|start)
45
- cadence orchestrate <projet>:<lot>[@modèle]… [--budget 2M] [--dry-run] [--wave nom] une session claude neuve par étape ; --status [vague] [--watch [--interval s]] ; --resume [vague] [--budget 1M] [--answer projet:lot "réponse"]
46
- cadence lead tour [dossier] [--idle 3] [--json] le tableau du lead, sans modèle : une ligne par sous-dossier qui a un plan (en cours, dérive, notes de clôture, prochain lot prêt, dépôt)
46
+ cadence orchestrate <projet>:<lot>[@modèle]… [--budget 2M] [--dry-run] [--wave nom] [--continue [--until HH:MM] [--priority a,b]] une session claude neuve par étape ; --status [vague] [--watch [--interval s]] ; --resume [vague] [--budget 1M] [--answer projet:lot "réponse"]
47
+ cadence lead tour [dossier] [--idle 3] [--json] le tableau du lead, sans modèle : une ligne par sous-dossier qui a un plan, ou la ligne du dossier seul s'il est lui-même un projet (en cours, dérive, notes de clôture, prochain lot prêt, dépôt)
47
48
  cadence verify [--retry s] [--sha rév] rejoue deliver.verify hors livraison : 0 vert, 1 effet rouge, 2 rien à vérifier
48
49
  raf news new <lot…> [--title t] | list | check | stamp | build [-o dossier] (aussi « cadence news … »)
49
50
 
@@ -115,6 +116,7 @@ function dispatch(argv, io) {
115
116
  status: { type: 'string' },
116
117
  since: { type: 'string' },
117
118
  idle: { type: 'string' },
119
+ session: { type: 'string' },
118
120
  all: { type: 'boolean' },
119
121
  depth: { type: 'string' },
120
122
  output: { type: 'string', short: 'o' },
@@ -140,7 +142,7 @@ function dispatch(argv, io) {
140
142
  if (!isDay(today))
141
143
  throw new RafError(`RAF_TODAY invalide : ${today} (attendu AAAA-MM-JJ)`);
142
144
  const root = gitRoot(io.cwd) ?? io.cwd;
143
- // Le tour parcourt les sous-dossiers : il ne lit ni le plan ni le cadence.yaml du dossier courant.
145
+ // Le tour parcourt les sous-dossiers (ou, sans sous-projet, prend le dossier lui-même s'il est un projet) : il ne lit pas le plan du dossier courant.
144
146
  if (command === 'lead') {
145
147
  if (rest[0] !== 'tour' || rest.length > 2)
146
148
  throw new RafError('usage : cadence lead tour [dossier] [--idle 3] [--json]');
@@ -155,9 +157,14 @@ function dispatch(argv, io) {
155
157
  io.out(JSON.stringify(rows, null, 2));
156
158
  else if (rows.length === 0)
157
159
  io.out(`aucun projet sous ${parent} (docs/plan/raf.yaml ou cadence.yaml avec plan:)`);
158
- else
160
+ else {
159
161
  for (const r of rows)
160
162
  io.out(tourLine(r));
163
+ // les créneaux libres viennent du registre, le lead les lit au lieu de les calculer (L141)
164
+ const [slots] = liveLines();
165
+ if (slots)
166
+ io.out(slots);
167
+ }
161
168
  return 0;
162
169
  }
163
170
  // --all balaie les sous-dossiers : il ne lit ni le plan ni le cadence.yaml du dossier courant.
@@ -401,6 +408,9 @@ function dispatch(argv, io) {
401
408
  throw new RafError('usage : raf hook install|pre-commit|post-commit');
402
409
  }
403
410
  case 'session': {
411
+ // Le contexte de la session ne dépend d'aucun dépôt : le lead le lit depuis le dossier parent.
412
+ if (rest[0] === 'context')
413
+ return sessionContext(io, values.session);
404
414
  if (!gitRoot(io.cwd))
405
415
  throw new RafError('session : à lancer dans un dépôt git');
406
416
  // « next » n'écrit que les notes : la commande du projet ne se joue qu'à la reprise et à la clôture.
@@ -426,7 +436,10 @@ function dispatch(argv, io) {
426
436
  throw new RafError(`pas de configuration de livraison : ${configPath} (voir « cadence.yaml » dans le README)`);
427
437
  const config = parseDeliverConfig(readFileSync(configPath, 'utf8'), configPath);
428
438
  const plan = existsSync(planPath) ? loadPlan() : null;
429
- const ctx = { root, state: sharedStateDir(root), plan, config, today, dryRun: !!values['dry-run'], sha: values.sha, args: rest, out: io.out, err: io.err };
439
+ const articles = readDocsConfig(configPath).articles;
440
+ // Sans public: d'un lot visible, le site reprend le titre de sa Nouveauté la plus récente.
441
+ const publicTitle = (lot) => lot.public ?? (lot.visible ? loadEntries(newsDir).find((e) => e.lots.includes(lot.id))?.title : undefined);
442
+ const ctx = { root, state: sharedStateDir(root), plan, config, today, dryRun: !!values['dry-run'], sha: values.sha, args: rest, articles, publicTitle, out: io.out, err: io.err };
430
443
  return deliver(ctx, realDeps(root));
431
444
  }
432
445
  case 'verify': {
@@ -764,6 +777,14 @@ async function sessionAll(rest, values, io) {
764
777
  }
765
778
  return worst;
766
779
  }
780
+ /** Le contexte de la session de `--session <id>`, sinon de `CLAUDE_CODE_SESSION_ID`, sinon (variable absente) de la seule session du dossier courant, tel que la bande cadence-hud le publie ; illisible = code 2, à lire comme « au seuil ». */
781
+ function sessionContext(io, session) {
782
+ const r = readHudContext(io.env, io.now().getTime(), { cwd: io.cwd, session });
783
+ if (!r.ok)
784
+ throw new RafError(`session context : contexte illisible — ${r.reason} ; à traiter comme au seuil`);
785
+ io.out(`ctx ${Math.round(r.ctx.percent)} % (${r.ctx.tokens}/${r.ctx.window})`);
786
+ return 0;
787
+ }
767
788
  function session([sub, ...args], ctx, values) {
768
789
  switch (sub) {
769
790
  case 'start': {
@@ -798,6 +819,6 @@ function session([sub, ...args], ctx, values) {
798
819
  return 0;
799
820
  }
800
821
  default:
801
- throw new RafError('usage : cadence session start [--since …] [--idle 2] | close [--since …] | start|close --all [--depth n] | next "ligne" … | next --clear');
822
+ throw new RafError('usage : cadence session start [--since …] [--idle 2] | close [--since …] | start|close --all [--depth n] | next "ligne" … | next --clear | context [--session id]');
802
823
  }
803
824
  }