@sylad/cadence 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +45 -1732
  4. package/agents/acceptance-writer.md +2 -0
  5. package/agents/code-reviewer.md +1 -1
  6. package/agents/ux-reviewer.md +2 -0
  7. package/dist/audit.js +4 -3
  8. package/dist/cli.js +4 -4
  9. package/dist/config.js +7 -2
  10. package/dist/orchestrate/accept.js +29 -15
  11. package/dist/orchestrate/briefs.js +1 -1
  12. package/dist/orchestrate/command.js +4 -1
  13. package/dist/orchestrate/cycle.js +91 -19
  14. package/dist/orchestrate/fiche.js +3 -3
  15. package/dist/orchestrate/maquette.js +12 -11
  16. package/dist/orchestrate/schemas.js +1 -1
  17. package/docs/cadence-hud.md +50 -0
  18. package/docs/corrections.md +24 -0
  19. package/docs/cycle-vague-dark.svg +42 -0
  20. package/docs/cycle-vague-light.svg +42 -0
  21. package/docs/deliver.md +204 -0
  22. package/docs/fonctionnement-lead-dark.svg +101 -0
  23. package/docs/fonctionnement-lead-light.svg +101 -0
  24. package/docs/fonctionnement-lot-dark.svg +38 -0
  25. package/docs/fonctionnement-lot-light.svg +38 -0
  26. package/docs/fonctionnement-service-dark.svg +23 -0
  27. package/docs/fonctionnement-service-light.svg +23 -0
  28. package/docs/fonctionnement-wave-dark.svg +66 -0
  29. package/docs/fonctionnement-wave-light.svg +66 -0
  30. package/docs/fonctionnement.md +106 -0
  31. package/docs/lead-workflow-dark.svg +200 -0
  32. package/docs/lead-workflow-light.svg +200 -0
  33. package/docs/lead.md +147 -0
  34. package/docs/news.md +245 -0
  35. package/docs/orchestrate.md +639 -0
  36. package/docs/overview.md +27 -0
  37. package/docs/raf.md +192 -0
  38. package/docs/releasing.md +59 -0
  39. package/docs/session.md +107 -0
  40. package/docs/skills.md +76 -0
  41. package/docs/spec.md +60 -0
  42. package/docs/stabilite.md +35 -0
  43. package/docs/whats-new.md +81 -0
  44. package/package.json +28 -1
  45. package/templates/acceptation-runner.mjs +1 -1
  46. package/templates/fiche-back.yaml +1 -1
  47. package/templates/fiche.yaml +1 -1
  48. package/templates/orchestrate/accept-back.md +1 -0
  49. package/templates/orchestrate/accept.md +1 -0
  50. package/templates/orchestrate/fix.md +1 -0
  51. package/templates/orchestrate/implement.md +1 -0
  52. package/templates/orchestrate/maquette.md +2 -0
  53. package/templates/orchestrate/review-small.md +1 -0
  54. package/templates/orchestrate/review.md +1 -0
package/README.md CHANGED
@@ -1,1758 +1,71 @@
1
1
  # cadence
2
2
 
3
- A small working method that lives in your repository. Solo developers and
4
- AI-assisted sessions lose track of *what is left to do* and *what each commit
5
- was for*; cadence keeps both in plain files next to the code.
3
+ A small working method that lives in your repository: a versioned plan linked to your commits, waves of fresh AI sessions driven by a program, and deliveries proved by their effect.
4
+ Solo developers and AI-assisted sessions lose track of *what is left to do* and *what each commit was for*; cadence keeps both in plain files next to the code.
5
+ **raf** is the plan, **news** the changelog with screenshots, **session** the facts to open and close a day, **deliver** waits for the CI, deploys, then checks the effect.
6
+ **orchestrate** runs a lot through spec, review, code and UX passes, each in its own session; the **lead** decides, re-verifies and delivers.
7
+ Four [Claude Code](https://claude.com/claude-code) skills, three reviewer agents and a status-band plugin (`cadence-hud`) turn it into rituals.
6
8
 
7
- Four tools:
9
+ <picture>
10
+ <source media="(prefers-color-scheme: dark)" srcset="docs/cycle-vague-dark.svg">
11
+ <img alt="A lot goes through spec, mockup, accept, implement, review, ux and deliver; the lead picks the lots, answers the questions, re-verifies and delivers" src="docs/cycle-vague-light.svg">
12
+ </picture>
8
13
 
9
- - **raf** (French *reste à faire*, "what is left to do"): the plan, linked to your commits;
10
- - **news**: a user-facing changelog with screenshots, tied to the plan;
11
- - **session**: the facts to start and to close a work session;
12
- - **deliver**: wait for the CI of the pushed commit, deploy, then **verify the effect**.
13
-
14
- And four [Claude Code](https://claude.com/claude-code) skills that turn them
15
- into rituals — `session-start`, `session-close`, `deliver`, and `lead` to pilot
16
- several projects through subagents — plus three reviewer agents: `ux-reviewer`
17
- (no user-facing change is done before its usability review) and `code-reviewer`
18
- (no lot with commits is done before its code review), each behind an opt-in
19
- gate, and `qa-reviewer`, which walks the delivered app in a real browser and
20
- reports a page left empty or in error.
21
-
22
- And a second plugin, [`cadence-hud`](#cadence-hud): a status band above the Claude
23
- Code prompt that shows the session's context, quota, cost and the orchestrate waves running.
24
-
25
- ### Spec step (design, first steps delivered)
26
-
27
- For a visible lot that adds a page (`screen: page`; a component or a journey follows the ordinary path since L209), `orchestrate` gets a step
28
- before the code: a static HTML mockup in the project's own style (1440 and 390 px; empty, loading and
29
- error states) and a machine-readable screen sheet (fields, numbered rules `R1…` each marked *sourced*
30
- or *hypothesis*, actions → API calls). The `ux-reviewer` agent reads the mockup before any code; the
31
- acceptance tests are written from the sheet, red, by a session other than the implementer's; the
32
- implementation gets the sheet, the code review checks rule → test → green, the hypotheses go into the
33
- lot's news entry, and a human is asked only about a choice that is costly to undo. The design is in
34
- `docs/superpowers/specs/2026-10-10-etape-spec-design.md` (with an example sheet and the plan of the
35
- lots that deliver it). Delivered so far: the sheet templates `templates/fiche.yaml` (screen) and `templates/fiche-back.yaml` (a lot without a screen: `sujet`, `cas`, `regles`, `contrats`, `donnees`), `cadence spec check`
36
- (the sheet's invariants) and `cadence spec qa` (the screen's `docs/qa/expectations.md` section, derived
37
- from the sheet), see [spec](#spec) below, and the `spec` step of `orchestrate` itself (the `spec-writer` agent, the
38
- render at 1440 and 390 px, the commit of the spec), see [The spec step](#the-spec-step-of-a-lot-with-screen) in `orchestrate`; the `accept` step (red acceptance tests and the lock that protects them), see [The accept step](#the-accept-step-red-acceptance-tests-and-their-lock); the UX review of
39
- the mockup (the `ux-reviewer` agent in mockup mode), see [The mockup review](#the-mockup-review-before-any-code); and the downstream briefs (the sheet given to `implement`, rule → test → green in the code review, a light UX conformity pass, the `## Hypothèses` section of the news entry), see [The downstream briefs](#the-downstream-briefs-sheet-rule--test--green-conformity-hypothèses).
40
-
41
- ## What's new
42
-
43
- **1.5.1**: 1.5.0 was tagged but never published (a test of `session --all` was red on the GitHub runner, a fault of the test and not of the tool, L224); 1.5.1 publishes it with what followed: one UX review per lot (L221), the replay of a lot handed back for its budget (L223), a self-check list in the briefs (L222), and a `tests.yml` workflow that plays the suite on the publish job's environment. **1.5.0**: `gate: { count: N, spec: true }` or `{ count: N, screen: page }` on a lot is a drawing condition added to `after`: until N `done` lots carry the criterion, `raf now`, `session start` and `lead tour` show the lot as waiting with its progress, `cadence orchestrate` refuses it and `--continue` never draws it (L219); `raf todo <id> [--reason]` puts a started lot without work commits back to `todo` with a dated note (L220). **1.4.0**: `cadence spec reseal` accepts a sheet amended by commits of the lot, in progress or delivered, and records `ficheAmendee` (L217); the budget of a wave keeps 155 k for a fix pass plus a short review, the floor of an ordinary lot goes to 540 k and the spec allocation to 450 k, and a refused fix says `correction non jouée (budget)` in the final table (L218); a session that runs `git checkout` no longer stops the wave with a false « push détecté » (L214, L216); tests already red at the start sha no longer hand a lot back (L213, L215). **1.3.0**: the spec step's threshold is narrowed to `screen: page` after the measure of 2026-10-12 (no row of the success table reached, about 570 k tokens per lot): `component` and `journey` lots follow the ordinary path again (L176, L209, L210); `cadence spec reseal … --porteur <lot>` re-seals the unstable sealed test of a delivered lot whose contract does not change (L212); the session cap shown and followed is the human's (`CADENCE_MAX_SESSIONS`, L208); `cadence orchestrate --waits` measures slot waits (L206); a third correction pass when the review converges (L205); `"e2e": true` acceptations stay out of the unit runner (L203); a dropped lot fully reverted no longer blocks `npm run service` (L207). **1.2.0**: the spec step reaches lots without a screen: a lot that declares `spec: true` (`raf add --spec`, `raf spec`) gets a back-end sheet of cases, rules, contracts and data (`cadence spec check`), reviewed before any code, then acceptance tests locked before the implementation, and `raf screen` lets a plan already written adopt the step; `orchestrate.accept` may be a list of runners (front and back), each run under its own root; `cadence spec reseal <lot> <test> --reason` is the lead's way out when a sealed test contradicts its unchanged sheet; `lead.ux: lead` lets the lead form the UX verdict itself; `cadence lead tour --depth n` looks for projects deeper in the folder; the budgets of a wave are recalibrated on the measured runs (750 k for an ordinary lot of 0.5 to 1 day, 1.3 M for a lot with a screen); the code in service on the cadence repository is a separate worktree, updated only by `npm run service`, so a wave can no longer put unreviewed code in service (L185–L189, L191, L192, L194–L198, L200, L201). **1.1.0**: the spec step of `cadence orchestrate`: a lot that declares `screen:` gets a machine-readable screen sheet (`cadence spec check|qa`), a mockup reviewed against it, acceptance tests committed red and locked before the implementation, and briefs that carry the sheet and rule → test → green to `implement`, `fix` and the code review; the news entry gains a *Hypothèses* section (L166, L169–L174, L177–L184). **1.0.0**: the first major release, a stable contract: the commands and options, the keys of `cadence.yaml`, the plan file and the names of the skills and agents follow semantic versioning, and the README's *Stability* section lists what is guaranteed and what is not (L164). **0.28.0**: `orchestrate.precheck: local` runs the « deliverable already present? » pre-check of `cadence orchestrate` on the local model (`claude-local`, Ollama), off the Anthropic quota, Sonnet staying the default and the fallback (L146); the `qa-reviewer` agent walks only the pages a lot touched, with a **Scope** line in its report (L123); the sub-tasks a review closes are also read in a lot's neighbouring repositories (L156) and a commit's sub-task list is read with the plan's left guard (L155); an unstable test is fixed (L90). **0.27.1**: `cadence orchestrate` no longer leaves a session's child processes alive: a dev server started in its own process group, or orphaned by a shell that exits (`nohup srv &`), is killed with the session through its process tree and its `CADENCE_SESSION` mark, a tmux server, `screen`, `gpg-agent` or `dirmngr` started on demand being spared; a session that exits with code 0 while a descendant holds its output open is no longer reported as « délai dépassé » (L83); the `ux-reviewer` and `qa-reviewer` agents carry the briefs' Playwright rule outside `cadence orchestrate` (L84). **0.27.0**: a compliant review of `cadence orchestrate` settles the open sub-tasks that commits of the lot cite (`feat(L3/t2): …`, comma lists included), closed in the same plan commit as the verdict or, on a read-only plan, returned as `[sous-tâche clore]` proposals, so `raf done` no longer refuses a lot over sub-tasks whose work is done; on a foreign-format plan the ids are read as the plan reads them (L82); the test suite stays green with two full suites running in parallel (L150). **0.26.0**: `cadence orchestrate` raises the floor of a lot's budget from 200 k to 450 k tokens, enough to pay a write pass, its review, one fix pass and the short review that follows, so a lot of 0.5 or 1 day is no longer handed back at its second or third pass; a `--continue` wave draws fewer lots as a result (L128). **0.25.0**: the project line of the plans' progress in the `cadence-hud` band names the lots in progress after their count, three at most then `…` (L153, `cadence-hud` 0.4.2); the progress follows the folder the session was launched from, so a `cd` into a sub-project no longer shrinks the band, and the session context keeps the `session.start` folder (L154). **0.24.0**: `cadence orchestrate --drop <project:lot>` and `--stop-after-current` steer a live wave from another terminal, `--resume --drop` removes a lot before replaying a stopped wave (L79); the final table of a wave says « livrable jusqu'à <sha> » for a ready lot stacked under a lot handed back, with `git push origin <sha>:main && cadence deliver --sha <sha>` (L76); a version commit (version fields, `CHANGELOG.md`, the README's « What's new ») no longer asks for a new code review (L72). **0.23.0**: the progress bar of each project in the `cadence-hud` band is coloured by the share of lots done, red below 33 %, orange below 66 %, green beyond (L152, `cadence-hud` 0.4.1); `cadence deliver` under a wave lock refuses only when a lot of the wave still works in that very repository, a linked worktree included (L127). **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
44
- stops with the wave) and the lead skill that names the wave and follows its journal; lot ids with `/`
45
- (sub-tasks of a read-only plan) accepted by `orchestrate`; `cadence-hud`'s first line measured in
46
- 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),
47
- `raf ignore` to acknowledge a commit without a lot, waves that size the review to the lot,
48
- hand back a lot whose deliverable is already there and start the app themselves for the UX
49
- review (`orchestrate.ux`), and briefs that require the README to follow the change. **0.11.0**: waves on different repositories run side by side under a shared cap of
50
- simultaneous sessions (`--max-sessions`, 2 by default), and every release now has its
51
- changelog section and GitHub release. **0.10.1**: the QA expectations file (`docs/qa/expectations.md`) is kept with the plan —
52
- a commit that only touches it no longer has to cite a lot. **0.10.0**: `cadence orchestrate`
53
- sends an already-committed lot straight to review, and sessions only ask questions that
54
- name what they would change.
55
-
56
- Every version, with what it brings and since when: [CHANGELOG.md](CHANGELOG.md).
57
-
58
- ## raf
59
-
60
- - The plan is a YAML file in the repo (`docs/plan/raf.yaml`), edited by the CLI.
61
- Your comments and hand edits are preserved.
62
- - A commit belongs to a lot when its message cites the id: `feat(L3): …`,
63
- `fix: L3/t1 …`. The link is **computed from `git log`**, never stored, so
64
- committing never dirties the plan. The id is read as a whole word: `XL3`,
65
- `L3x` and `L3.4` do not cite `L3`, while `L3.` at the end of a sentence does.
66
- When the subject has a scope that cites lots (`feat(L3): …`,
67
- `chore(L31,L32): …`), **the scope alone decides**: a passing mention
68
- (`page équipe (L27)`), a range (`L28–L31`, `L45 à L48`) or the body does not
69
- count. Without a scope citing a lot, the whole message is read as before.
70
- - `raf check` audits drift between the plan and the history.
71
- - Plan upkeep needs no lot. A commit is plan upkeep when **every file it
72
- 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
73
- the `--config` file; a change to `deliver:`/`session:` is work), or a file the
74
- project lists under `plan.files` in `cadence.yaml` (a page it generates from
75
- the plan, a journal), or the QA expectations file (`docs/qa/expectations.md`,
76
- or the file named by `qa.expectations`; the lead commits the `qa-reviewer`'s draft of it). Such a commit is never a "commit without a lot", and it
77
- does not count as work on the lots it cites: it is absent from `raf commits`,
78
- does not start a `todo` lot, does not make a code review stale, and
79
- `cadence deliver` does not announce the lots it cites as delivered. The files
80
- decide, never the subject: a `chore(plan): …` commit that touches a source
81
- file is a commit like any other.
82
- - `raf gantt` writes a single self-contained HTML page (no server, no CDN).
14
+ ## Start
83
15
 
84
16
  ```sh
85
17
  npm install -g @sylad/cadence # or: npx -p @sylad/cadence raf …
86
-
87
- raf init # docs/plan/raf.yaml + post-commit hook + .playwright-mcp/ in .gitignore
88
- raf add "Monthly dedup on merge" --estimate 2
89
- raf add "Typo in footer" --quickwin
90
- raf add "Loan cache" --after L1
91
- raf add "write the migration" --parent L1 # → L1/t1
92
- raf start L1
93
- git commit -m "feat(L1): dedup by calendar month"
94
- raf now # in progress, next up (quickwins first), recently done
95
- raf done L1/t1 && raf done L1
96
- raf check # exit 1 on drift
97
- raf gantt # docs/plan/gantt.html
98
- ```
99
-
100
- ### Commands
101
-
102
- | Command | Effect |
103
- |---|---|
104
- | `raf init [--project name] [--prefix L] [--no-hook]` | create the plan, install the hook, add `.playwright-mcp/` to `.gitignore` |
105
- | `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) |
106
- | `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`) |
107
- | `raf screen <id> page\|component\|journey\|none` | set, replace or remove `screen:` on an existing lot, to adopt the [spec step](#the-spec-step-of-a-lot-with-screen) on a plan already written (only `page` adopts the [spec step](#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 |
108
- | `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`](#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`) |
109
- | `raf public <id> "title"` · `raf public <id> --clear` | set, replace or remove the public title of a lot |
110
- | `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`) |
111
- | `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 |
112
- | `raf note <id> "text"` | dated note — keep decisions next to the work |
113
- | `raf show <id> [--notes]` | one lot: status, dates, `after`, public title, dated notes in order, then the counted commits (as `raf commits`, neighbouring repositories included); `--notes` prints the notes alone |
114
- | `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](#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) |
115
- | `raf now` | what to do next |
116
- | `raf list [--status s]` | flat list |
117
- | `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 |
118
- | `raf check --ignored` | list the acknowledged commits with their date and reason |
119
- | `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` and the README's « What's new » section, are exempt), unknown ids, `todo` lots that already have commits, idle lots, `done` lots with open sub-tasks, bad or circular dependencies |
120
- | `raf gantt [-o file]` | standalone Gantt page |
121
- | `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) |
122
-
123
- #### A commit on a lot that is still `todo`
124
-
125
- By default the post-commit hook only warns (`L1 est encore todo — raf start L1`). The `hook.autostart`
126
- key of `cadence.yaml` changes what the **pre-commit** hook does when the message cites a `todo` lot
127
- (a recurring lot, a lot already `doing`, a commit without a lot and a plan-upkeep commit are never concerned):
128
-
129
- ```yaml
130
- hook:
131
- autostart: refuse # warn (default) | refuse | start
132
- ```
133
-
134
- - `warn`: today's behaviour, the commit goes through, the post-commit hook warns.
135
- - `refuse`: the commit fails, loudly, with `raf start <id>` in the message (`--no-verify` still bypasses it).
136
- - `start`: the hook runs `raf start <id>` and stages the plan, so the plan change is **in the same commit** —
137
- no amend afterwards, no dirty plan left behind. Only when `docs/plan/raf.yaml` is identical to `HEAD` (no
138
- uncommitted change to the plan, staged or not): otherwise nothing is started, the hook says why
139
- (`plan modifié non commité : démarrage automatique sauté, raf start <id> à la main`) and behaves like `warn`.
140
-
141
- The pre-commit hook reads the message from the command line of the `git commit` that runs it (`-m`, `-am`,
142
- `--message`, `-F`; Linux, through `/proc`) or from `CADENCE_COMMIT_MESSAGE`. When the message is not known yet
143
- (editor, `--amend`) it does nothing and the post-commit warning stays. Run `raf hook install` again to add the
144
- pre-commit block to an existing repository. A read-only plan is never started by the hook. Only a refusal (exit code 3 of `raf hook pre-commit`) stops a commit: an older or broken `raf` lets it through.
145
-
146
- Global options: `--file path` or `RAF_FILE`; `RAF_TODAY=YYYY-MM-DD` to freeze
147
- the date. `cadence raf …` is the same as `raf …`.
148
-
149
- ### Plan file
150
-
151
- ```yaml
152
- version: 1
153
- project: my-app
154
- prefix: L
155
- since: 2026-09-28 # commits before this date are not audited
156
- ignore: ['^chore\(batch\):'] # optional: subjects of automated commits, neither audited nor counted for a lot
157
- lots:
158
- - id: L1
159
- title: Monthly dedup on merge
160
- status: doing # todo | doing | done | dropped
161
- estimate: 2 # working days
162
- quickwin: false
163
- visible: true # user-facing: a news entry is expected when done
164
- after: [L0]
165
- gate: { count: 8, spec: true } # optional: not before 8 lots with `spec: true` are done (or `screen: page`)
166
- created: 2026-09-28
167
- started: 2026-09-29
168
- notes:
169
- - { date: 2026-09-29, text: "keep the bank line when both sources exist" }
170
- tasks:
171
- - { id: t1, title: write the migration, status: done }
172
- ```
173
-
174
- ### A plan elsewhere, or in another format
175
-
176
- `cadence.yaml`, at the repository root, can say where the plan is. The short form keeps raf's own
177
- format, and the plan stays writable:
178
-
179
- ```yaml
180
- plan: planning/todo.yaml
181
- ```
182
-
183
- A project that publishes its plan (a JSON generated from it and committed with it)
184
- declares that file, so a commit touching only the plan and its published copy is
185
- plan upkeep; the plan keeps raf's format and stays writable:
186
-
187
- ```yaml
188
- plan:
189
- files: [frontend/public/plan-data/plan.json]
190
- ```
191
-
192
- Until the file is declared, such a commit is reported as a "commit without a
193
- lot" when it cites none, and counts as work on the lots it cites.
194
-
195
- A project that already keeps its plan with its own tool is read **without migrating it**: describe
196
- the file, and `raf now`, `raf list`, `raf commits`, `raf check`, `raf gantt` and
197
- `cadence session start|close` work on it. Such a plan is **read-only** —
198
- `raf add|start|done|note|ux|review` refuse and leave the file to the project's tool, and the two
199
- review gates do not apply to it (a `uxSince` or `reviewSince` written in it is ignored).
200
-
201
- ```yaml
202
- plan:
203
- path: docs/plan/taches.yaml
204
- project: my-app # what the file does not say itself: project, since, ignore
205
- since: 2026-10-02
206
- ignore: ['^plan: ']
207
- files: [docs/plan/journal.ndjson] # kept with the plan: a commit touching only these is a plan commit
208
- lots: taches # root key holding the list (default: lots)
209
- fields: # raf field: key in the file (a list = first one present)
210
- title: titre
211
- public: titre_public # the lot's title for the public; read when it is text, else ignored
212
- screen: ecran # page, component or journey; only on a visible lot (only `page` enters the spec step)
213
- spec: fiche # true or false: the lot changes a behaviour
214
- status: etat
215
- estimate: effort
216
- created: cree_le
217
- started: demarre_le
218
- finished: [livre_le, ferme_le]
219
- notes: note
220
- repos: depots # the lot's neighbouring repositories (a list of paths and/or { path, cite }, or one of them)
221
- parent: parent
222
- statuses: # raf status: their states
223
- todo: [prevu, specifie]
224
- doing: [en_cours, teste]
225
- done: [deploye, valide]
226
- dropped: caduc
227
- estimates: { S: 0.5, M: 1, L: 3 } # their effort labels, in working days
228
- ```
229
-
230
- - Fields: `id`, `title`, `status`, `estimate`, `quickwin`, `visible`, `screen`, `spec`, `public`, `every`, `last`, `after`, `created`, `started`,
231
- `finished`, `notes`, `repos`, `parent`, `gate`; one left out is read under its own name. A timestamp counts for
232
- its day; a note written as plain text is one note.
233
- - `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.
234
- - `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.
235
- - `parent`: an entry `B33/t1-fusion` whose parent is `B33` becomes the sub-task `t1-fusion` of `B33`.
236
- - Ids need no prefix: a commit belongs to a lot when its message cites one of the plan's ids as a
237
- whole word (`E-A2`, `NC2.4`, `B33/t1-fusion`).
238
- An id that is not in the plan cannot be told from ordinary text: such a commit counts as
239
- "without a lot", never as an unknown reference.
240
- - A state missing from `statuses`, or an effort label missing from `estimates`, is reported by
241
- `raf check`.
242
-
243
- ### Gantt scheduling
244
-
245
- One lane of work. Finished lots use their real dates (or their commits' dates);
246
- lots in progress run until `max(start + estimate, today)`; lots to do follow in
247
- file order, after their dependencies, on working days.
248
-
249
- ## news
250
-
251
- What changed *for the user*, one entry per visible lot, each with a screenshot.
252
-
253
- - Mark a lot as user-facing with `raf add … --visible` (or `visible: true` in
254
- the YAML). `raf done` and the post-commit hook remind you to write its entry;
255
- `raf check` fails while a visible lot is done without one.
256
- - An entry is a Markdown file in `docs/nouveautes/`, with a YAML header.
257
- Screenshots are files you take yourself, stored next to the entries.
258
- - `cadence news build` writes `nouveautes.json` for your app to display, a
259
- self-contained `index.html`, and copies the screenshots.
260
-
261
- ```sh
262
- raf add "Amounts like 3.000 read as three thousand" --visible # → L8
263
- raf start L8 && git commit -m "fix(L8): thousands separator" && raf done L8
264
- cadence news new L8 # docs/nouveautes/2026-09-29-amounts-like-3-000-….md (title: the lot's public title if any)
265
- # add docs/nouveautes/captures/l8.png, list it under `captures:`, write the text
266
- cadence news check # exit 1 on drift (also part of raf check)
267
- cadence news build -o frontend/public/nouveautes
268
- ```
269
-
270
- ```markdown
271
- ---
272
- title: Amounts like 3.000 read as three thousand
273
- date: 2026-09-29
274
- created: 2026-09-29T14:32+02:00
275
- lots: [L8]
276
- captures: [captures/l8.png]
277
- # nocapture: reason, when a screenshot makes no sense
278
- ---
279
- Imported statements now read **3.000** as three thousand, not three.
280
- ```
281
-
282
- A screenshot can say what it shows: write it as `{ file, alt }` instead of a
283
- bare path, one text per screenshot.
284
-
285
- ```yaml
286
- captures:
287
- - { file: captures/l8-before.png, alt: "Statement total read as 3 instead of 3,000" }
288
- - captures/l8-after.png # a bare path still works: no alternative text
289
- ```
290
-
291
- | Command | Effect |
292
- |---|---|
293
- | `cadence news new <lot…> [--title t]` | entry skeleton, dated and timed now (`date`, `created`), titled after the lot's public title (`public`), else its title; `--title` wins |
294
- | `cadence news list` | entries, newest first (see *Order* below) |
295
- | `cadence news check` | visible lots done without entry, unknown lots, missing or undeclared screenshots, entries without creation time, bad headers, a lot whose screen sheet has hypotheses and whose entry lacks the `## Hypothèses` section (or a line for one of them) |
296
- | `cadence news stamp` | migration: writes `created:` into entries without one (or with an empty one), from the author date of the commit that added the file under its current name (now if not committed yet) |
297
- | `cadence news build [-o dir]` | `nouveautes.json` + `index.html` + screenshots (default `docs/nouveautes/site`) |
298
-
299
- `--dir` changes the entries folder (default `docs/nouveautes` at the git root).
300
- The Markdown is deliberately small: headings (`#` to `######`, rendered one level lower, `<h2>` to `<h6>`; a heading takes its own line and cuts the paragraph or list around it), paragraphs, `-` lists (a bullet may continue on the next lines, indented), `**bold**`,
301
- `` `code` ``, `[links](url)`; everything else is escaped text. The JSON holds
302
- `{ project, generated, entries: [{ slug, title, date, lots, captures, html }] }`,
303
- with screenshot paths relative to the JSON file. `captures` is always a list of
304
- paths; an entry that gives at least one alternative text also carries `alts`,
305
- the texts in the same order (`""` for a screenshot without one) — an entry
306
- without any keeps exactly the shape above. The built page puts the text in the
307
- image's `alt`, and falls back to "Capture : <title>".
308
-
309
- **Order.** Everywhere (`list`, `build`, the JSON), entries are strictly newest
310
- first: by `date`, then, on the same day, by creation time. Every entry carries
311
- it to the minute in its `created` header, which `cadence news new` writes as
312
- local time with an explicit offset (`2026-09-29T14:32+02:00`, or `Z`). The
313
- offset is required, so the order does not depend on the machine's time zone:
314
- `news check` (and `raf check`) flags a value without it, an impossible one, an
315
- empty `created:`, and an entry without `created`. `cadence news stamp` fills
316
- it in older entries (or replaces an empty one) from the author date of the
317
- commit that added the file under its current name; until then, that date is
318
- used for sorting (an entry not committed yet counts as the newest). Renames
319
- are not followed: a renamed entry counts as added on the day of the rename,
320
- so stamp it before renaming it. The file name only breaks the remaining ties,
321
- since it follows the title, not the chronology. The page and the JSON still
322
- show the day only.
323
-
324
- ### UX review
325
-
326
- ```sh
327
- raf ux enable # from today, a --visible lot needs a UX review before done
328
- raf ux L4 "compliant after 2 fixes" # record the verdict (from the ux-reviewer agent)
329
- raf ux L8 "no screen: calculation fix"
330
- ```
331
-
332
- With the rule on, `raf done` refuses a visible lot without a review (`--force`
333
- to override) and `raf check` reports visible lots finished after the `uxSince`
334
- day without one. An empty verdict is refused, and one left empty or blank by hand
335
- in the YAML counts as no review. Plans without `uxSince` are not affected.
336
-
337
- ### Code review
338
-
339
- ```sh
340
- raf review enable # from today, a lot with commits needs a code review before done
341
- raf commits L4 # what there is to review: the commits the gate counts for the lot
342
- raf review L4 "compliant after 2 fixes" # record the verdict (from the code-reviewer agent)
343
- ```
344
-
345
- The counterpart of the UX review, off by default. With the rule on, `raf done`
346
- refuses a lot that has at least one commit citing it and no recorded verdict
347
- (`--force` to override), and `raf check` reports such lots finished after the
348
- `reviewSince` day. A lot with no commit has nothing to review; neither does a
349
- lot whose only commits touch plan files alone (the plan, a file listed under
350
- `plan.files`, or the QA expectations file), are version commits (see [Releasing](#releasing)), predate the plan's `since` or match an `ignore:` pattern — `raf commits <id>` prints exactly the counted set.
351
-
352
- The verdict is tied to what was reviewed: `raf review` stores it on the lot with
353
- the sha of the lot's latest counted commit (`review: { date, verdict, commit }`,
354
- `commit: null` when the lot had none). A counted commit made after that one
355
- makes the review stale: `raf done` refuses (`--force` to override), and
356
- `raf check` reports a finished lot, until the lot is reviewed again and
357
- `raf review` is rerun. A verdict
358
- written by hand without a `commit` field is not checked for staleness. An empty
359
- verdict is refused, and one left empty or blank by hand in the YAML counts as no
360
- review. Plans without `reviewSince` are not affected.
361
-
362
- A lot that works in neighbouring repositories (`repos:`, see [orchestrate](#a-lot-whose-work-is-in-a-neighbouring-repository))
363
- has its verdict tied to each of them: `raf review` also stores, under `repos`, the sha of the latest counted commit of the
364
- lot in each neighbour (the one `raf commits` lists last: it cites the lot, falls within the lot's period and contains the `cite:` string if any) (`review: { date, verdict, commit, repos: { ../aetherwx-gitops: <sha> } }`, `null` when a
365
- neighbour has none), and refuses to record when a listed repository cannot be read. `raf check` does not audit the
366
- neighbours: their staleness is not checked, only the project's own commits are.
367
-
368
- ### Documentation that follows the code
369
-
370
- A stale README misleads. `cadence.yaml` can declare which documents must follow which code:
371
-
372
- ```yaml
373
- docs:
374
- sync:
375
- - paths: [src/**, bin/*.js] # what changes…
376
- docs: [README.md, docs/usage.md] # …must change one of these, in the same lot
377
- - paths: [templates/]
378
- docs: [CLAUDE.md]
379
- - paths: [frontend/src/**, '!**/*.test.tsx'] # a pattern starting with ! leaves files out
380
- docs: [README.md]
381
- since: 2026-10-09 # optional: also audit lots finished on or after this day
382
- ```
383
-
384
- A pattern is an exact path, with `*` (inside a folder), `**` (across folders), `?`, or a trailing `/` for
385
- a whole folder. `raf check` takes the **work commits** of every lot in progress (plan-only commits and
386
- version commits do not count — so a "What's new" line written in the version commit is not documentation; a lot finished on or after `docs.since` is audited too) and, for each pair, reports the lot when
387
- those commits touch `paths` without any of them touching a file of `docs`:
388
-
389
- ```
390
- ✗ L7 : documentation en retard — src/a.ts, src/b.ts sans toucher README.md ou docs/usage.md (docs.sync)
18
+ raf init # docs/plan/raf.yaml + a post-commit hook
19
+ raf add "My first lot" --estimate 1
391
20
  ```
392
21
 
393
- It counts as drift: exit code 1, shown in the `dérive` column of `cadence lead tour`, and the review
394
- brief of `cadence orchestrate` (`review` and `review-small`) carries the list the program computed,
395
- to be reported as one **major** finding per line — a stale document is not left to the reviewer's
396
- memory of a sentence. Touching a document is what the program checks; whether its text is true is
397
- still the review's reading. The commits of neighbouring repositories (`repos:`) are not audited.
22
+ Then `raf start L1`, commit with `feat(L1): …`, `raf now` to see where you are, `raf done L1` when it is finished.
398
23
 
399
- #### An article elsewhere that follows the delivery
24
+ ## Where to read more
400
25
 
401
- A case study that lives in a neighbouring repository (the project's article on a showcase site) goes stale
402
- the same way. `docs.articles` names it:
403
-
404
- ```yaml
405
- docs:
406
- articles:
407
- - repo: ../claude-code-codex # neighbouring repository, relative to the project
408
- file: src/pages/cas/demo.md # the article in it
409
- name: demo # optional: the name in the lot title (default: the plan's project)
410
- ```
411
-
412
- After a **green** `cadence deliver`, for the lots it lists as delivered **that have a public title** (`public:`, or
413
- the title of their Nouveautés entry), cadence opens in this repository's plan the lot
414
- `Article demo à rafraîchir` (todo, estimate 0.5, `repos: [{ path: ../claude-code-codex, cite: demo }]`), with a note
415
- that quotes the delivery, the public titles delivered, the article and the **sections to read again**: the headings
416
- (`#` lines, or `<h1>`…`<h6>` of an Astro page) that share a word of five letters or more with a delivered title,
417
- else all the `##` headings. The lot is then played like any other, in an ordinary wave (`cadence orchestrate`: an
418
- implementer rewrites the article in the neighbouring repository, the review checks it against the note). If a lot
419
- of that title is still open, it receives a note instead of a duplicate. Two entries with the same name (the same
420
- project told in two neighbouring repositories) share one lot, which declares both repositories in `repos:`. Internal lots (no public title) open nothing;
421
- a red delivery opens nothing; `cadence deliver --dry-run` announces the articles. The plan is written but **not
422
- committed** (`deliver` never commits): the line `article à rafraîchir : L9 …` says so. With a plan kept by another
423
- tool (read-only), the same line carries the instruction to open the lot with that tool. This replaces the manual
424
- `sync-site-docs` skill.
425
-
426
- ### QA review
427
-
428
- No gate and no command here: the QA review comes **after** a delivery, and
429
- `raf done` does not wait for it. It follows any delivery that changes what a
430
- page shows or what it is served (screen, API, data source, configuration of
431
- either) — in practice every delivery except docs-, plan- or tests-only ones: a
432
- backend-only lot can empty a page without touching a screen, and the agent then
433
- walks the pages that call the changed endpoints. It also walks the pages that consume the services the lot changed; when the diff changes no route and no screen, or cannot be linked to pages, it walks every page and says so. The `qa-reviewer` agent
434
- opens each page of the running app in a real browser and judges it from the
435
- user's side. A page can be empty while everything else is green — no code
436
- changed, a data source went down upstream, the unit tests replace the network,
437
- `/api/health` answers ok, and the "nothing found" on screen is the message the
438
- code was written to show.
439
-
440
- The agent cannot tell such an empty state from a normal one by itself: the
441
- project says what each page must show, in `docs/qa/expectations.md` — one
442
- `## <route>` section per page, three kinds of lines:
443
-
444
- ```markdown
445
- # QA expectations
446
-
447
- ## *
448
- - shows: the header and the navigation links
449
- - never: "Loading failed", "Too Many Requests"
450
- - api: /api/live/current — may be empty when no match is within 24 hours
451
-
452
- ## /players
453
- - shows: the squad of the last match — at least 11 players
454
- - shows: the season statistics table, 8 columns — 1440 only
455
- - never: "No recent line-up found"
456
- - api: /api/squad/last — a non-empty list
457
-
458
- ## /fixtures/:id (the first match linked from /fixtures)
459
- - shows: both team names, the date, the score once the match is played
460
- - never: "Unknown match"
461
- - api: /api/fixtures/:id
462
- ```
463
-
464
- - `shows:` — content that must be present and non-empty, with a count where one exists;
465
- - `never:` — texts that must not appear: error messages, and empty-state messages that mean
466
- missing data;
467
- - `api:` — the calls the page depends on: each must answer 2xx with a non-empty body (a 200 with
468
- `[]`, `{}` or `null` is a failure, unless its line says `may be empty when …`).
469
-
470
- An optional `## *` section holds what every page must show, never show and call. A line may end
471
- with a condition in plain words, which the agent honours: `may be empty when …`, `1440 only`,
472
- `390 only` (a line without a width holds at both). Content hidden on the phone by design is not a
473
- defect unless a `shows:` line requires it at 390; content pushed outside the visible area (it
474
- needs a sideways scroll) is reported as suspect and handed to `ux-reviewer` in one line.
475
-
476
- The rest is free text, written for a reader: a line can be repeated, and a route with a parameter
477
- names a real value to visit or says where to find one. The file can live elsewhere:
478
-
479
- ```yaml
480
- # cadence.yaml
481
- qa:
482
- expectations: docs/quality/pages.md
483
- ```
484
-
485
- The agent reads that key, and so does `raf check`: a commit touching only that file is plan upkeep (no lot to cite, see above). Without an expectations file the agent walks
486
- the routes it discovers and still runs its universal checks: an error shown, a failed API call
487
- whose content is missing on screen, a broken or missing content image are defects with or without a
488
- file; an empty 2xx body, like whatever else would need an expectation to judge, is suspect at most
489
- (it may be a normal absence). For a route with a
490
- parameter, it finds a real value in the app's links or its API responses and says how it built the
491
- URL. The agent then returns a draft for you to correct — it never writes the file itself.
492
-
493
- ## session
494
-
495
- ```sh
496
- cadence session start # notes from the last close, lots in progress (silent ones flagged),
497
- # work done since yesterday by lot, drift, repo state, 3 proposals
498
- cadence session start --since "3 days ago" --idle 2
499
- cadence session close # commits by lot since the last `session start`,
500
- # commits without a lot, lots in progress with no commit in that window,
501
- # drift, uncommitted / unpushed work; exit 1 while something is still open
502
- cadence session close --since "2 days ago" # --since overrides the window
503
- cadence session start --all # from a folder that is not a repository: one "## project" section per
504
- cadence session close --all --depth 2 # sub-folder holding a plan (--depth n: n levels down, 1 by default);
505
- # exit = the worst code of the projects (2 error, 1 open, 0 closed)
506
- cadence session next "finish L3" "review L4" # shown by the next session start; replaces the previous notes
507
- cadence session next --clear # erase those notes, on purpose
508
- cadence session context # `ctx 42 % (84000/200000)`: the context of YOUR session, as the cadence-hud band publishes
509
- # it: the one named by $CLAUDE_CODE_SESSION_ID (set in Claude Code's Bash); --session <id>
510
- # overrides it; with no variable, the folder decides only if a single session publishes for it
511
- # (two sessions in one folder = exit 2, "pass --session"); exit 2 too when no fresh figure
512
- # (band not loaded) = treat as at the threshold
513
- ```
514
-
515
- `session start` records its time in the worktree's state (`session.json`, under the git dir); the next
516
- `session close` takes it as its window and names it in the section title: `Commits de la période (depuis
517
- l'ouverture de 09:12)` (with the date when it is not today). A close never moves the boundary, refused
518
- or not: run again without a `session start` in between (the `session-close` skill does, after fixing a
519
- point), it still reports since that opening instead of an empty period. With no recorded opening, or an
520
- unreadable file, the window is the current day, said as such (`depuis 2026-09-28 00:00`); `--since` is
521
- quoted as given. (A `session.json` written by 0.18.0 after a close, `depuis la clôture de …`, is still read.)
522
-
523
- On success `cadence session next` prints what it recorded (`2 lignes enregistrées pour la prochaine
524
- ouverture`, then the lines as written, blank ones dropped), so `session-close` can report
525
- them without running `session start` again.
526
-
527
- `--all` runs the same command inside each project found under the current folder —
528
- `docs/plan/raf.yaml`, or a `cadence.yaml` with `plan:` — and never enters a project
529
- to look for another. `--since` and `--idle` go to every project; an error in one
530
- project is printed in its section and the others still run. It replaces the shell
531
- loop over the sub-folders. A project that is not the root of its own git repository gets a `✗` line
532
- (and exit 2) instead of the parent repository's plan. `--all` and `--depth` are refused outside `session` (`--depth` is also read by `cadence lead tour`),
533
- and `--all` refuses `--file`, `--config` and `RAF_FILE`: each project reads its own plan. For a one-line-per-project overview, `cadence lead tour`.
534
-
535
- `cadence session next` without a line refuses (exit 2) and leaves the notes of the
536
- last close as they are — it used to erase them silently; erasing is `--clear`.
537
-
538
- Proposals come from the plan only: lots in progress, then ready lots (dependencies
539
- done), quick wins first. Local state lives in the git directory, never committed:
540
- the close notes per worktree, the delivery lock and log in `.git/cadence/`, shared
541
- by all the worktrees of a clone.
542
-
543
- A project that already has its own morning and evening scripts keeps them: name
544
- them in `cadence.yaml` and their output is added to the report, under "Faits
545
- propres au projet", before the proposals (start) or the verdict (close).
546
-
547
- ```yaml
548
- session:
549
- start: ./scripts/morning.sh "$CADENCE_SINCE" # sh, at the repo root, 120 s at most
550
- close: ./scripts/evening.sh "$CADENCE_SINCE"
551
- ```
552
-
553
- ```yaml
554
- session:
555
- clean: [ "~/projects/tmp/*", "tmp/*", "test-output-*" ] # working files to propose for removal at close
556
- cleanDays: 7 # older than this many days (default 7)
557
- ```
558
-
559
- `cadence session close` then lists, under "Nettoyage proposé", the entries matching these
560
- patterns (`*` stands for part of a name and never crosses a `/`; `~` is the home folder, a
561
- relative pattern starts at the repo root) that were not modified for more than `cleanDays`
562
- days, oldest first — a folder's age is that of the most recent entry it contains, at any depth,
563
- and an entry's age runs from the later of its modification time and its status-change time (ctime):
564
- what `tar xf`, `cp -a` or `rsync -a` just brought in with an old modification time is not old.
565
-
566
- An entry is proposed only if it could be measured entirely. Never proposed:
567
-
568
- - a git repository, a folder that contains one at any depth, and anything under a `.git` folder;
569
- - a git directory without a `.git` entry — a bare repository (`git init --bare`, `git clone
570
- --mirror`, `backup.git/`), a `--separate-git-dir`, a worktree's admin folder —, recognised by
571
- its content as git does (`HEAD` with `objects` and `refs`, or `HEAD` with `commondir`): neither
572
- it, nor anything inside it, nor a folder that contains one;
573
- - anything `git` tracks, in any repository that has a `.git` entry above it. One limit, stated
574
- plainly: a bare repository driven with an external work tree (`git --git-dir=~/.dotfiles
575
- --work-tree=~`) leaves no `.git` beside the files, so its tracked files cannot be recognised and
576
- can be proposed — never point a pattern at such a work tree;
577
- - a name starting with `.` unless the pattern itself starts that name with `.` (as in the
578
- shell, `tmp/*` does not match `tmp/.env`; `tmp/.cache-*` does);
579
- - anything reached through a symbolic link matched by a `*` segment: unlike the shell, the walk
580
- never descends into such a link (`tmp/*/*` lists nothing behind `tmp/link → ../outside`), and a
581
- folder's age never looks behind a link; the link itself can be proposed, as a link — removing it
582
- leaves its target alone. Only segments written in full *before* the first `*` follow a link, as
583
- `cd` would (`~/shared/*` with `~/shared` a link lists the target's entries); once a `*` has been
584
- crossed no link is followed any more, even on a segment written in full (`tmp/*/out/*` with
585
- `tmp/run1/out → /elsewhere` lists nothing behind `out`);
586
- - anything that could not be read entirely: a folder that cannot be listed — nor anything
587
- inside it, even named in full —, an entry that cannot be examined or vanished during the
588
- walk, an entry under a `.git` that `git` could not answer for. These are listed apart,
589
- under "Nettoyage : N élément(s) illisible(s), jamais proposé(s)";
590
- - the repository itself, or a folder that contains it.
591
-
592
- The command deletes nothing, never fails on the cleanup and does not change the exit code: the
593
- `session-close` skill shows the list and removes the entries only after the human agrees.
594
-
595
- The commands get `CADENCE_SINCE` and `CADENCE_TODAY`. `CADENCE_SINCE` is the `--since` as given; on `session close` without `--since` it is the instant of the last `session start` as an ISO 8601 UTC string (`2026-09-28T07:12:00.000Z`), or `<today> 00:00` when no mark exists.
596
- The commands add facts and decide nothing: a failing command is reported and changes neither
597
- the exit code nor the verdict.
598
-
599
- ## lead tour
600
-
601
- `cadence lead tour [folder] [--idle 3] [--depth n] [--json]` is the lead's morning table, with no model in
602
- the loop. From a parent folder (default: the current one) it takes every direct sub-folder that
603
- holds `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key (a plan kept by another tool
604
- included), and for each one reads the same facts as `cadence session start` — in-process, no
605
- sub-process, no call to `claude` — then prints **one line per project**:
606
-
607
- ```
608
- alpha · en cours L1 (silencieux 4j) · dérive 1 (✗ L2 a 1 commit(s) mais est encore todo) · notes : finir L1 puis livrer · prochain L3 Typo · dépôt non commité, non poussé
609
- beta · en cours rien · dérive aucune · notes : aucune · prochain T2 Second · dépôt propre
610
- ```
611
-
612
- | Column | Content |
26
+ | Page | What it covers |
613
27
  |---|---|
614
- | `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) |
615
- | `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 |
616
- | `notes` | the notes left by the last `session close`, joined with ` / ` and cut at 120 characters |
617
- | `prochain` | the first ready lot (quick wins first) — id and title cut at 60 characters; a lot whose [`gate`](#plan-file) is not met is not ready |
618
- | `attend` | only when a lot waits on its `gate`: `attend L190` |
619
- | `dépôt` | `non commité` (modified or untracked files), `non poussé` (commits ahead of the upstream), `livraison en cours` (live delivery lock); `propre` otherwise |
620
-
621
- 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). The `sur M` is the cap the human set (`CADENCE_MAX_SESSIONS`, 2 by default), not a fixed 2 (L208); an invalid value falls back to 2 in this display.
622
-
623
- `--json` prints the same content as an array of objects (`project`, `doing`, `drift`, `notes`, `waiting` (only when a lot waits on its `gate`),
624
- `next`, `repo`, and `error` when the project could not be read), plus `progress` — `{ done, doing, todo, added7 }`,
625
- the lots done / in progress / to do (dropped and recurring lots left out) and those created in the last 7 days (L149), which
626
- [`cadence-hud`](plugins/cadence-hud/README.md) draws as one progress bar per project. Projects come in the order of the
627
- `priority:` key of the folder's `cadence.yaml` (the one `orchestrate --continue` reads), the others after, alphabetically; an unreadable `cadence.yaml` or
628
- 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`).
629
- Sub-projects are looked for one level down by default. `--depth n` goes `n` levels down (same meaning as in `cadence session start --all --depth n`:
630
- a folder that is a project is not entered; an integer ≥ 1, otherwise refused), and the `depth:` key of the folder's `cadence.yaml`, next to `priority:`, sets it once for all
631
- (`depth: 2`); `--depth` wins over the key, and a `depth:` that is not an integer ≥ 1 adds the same first row `cadence.yaml · ✗ erreur : …` and leaves one level read.
632
- `cadence-hud` asks the tour from the folder of the session, so it follows the key with no change. A project is named by its folder's name; one found below the first level is named by its path from the folder (`groupe/gamma`), unique and ready for `cadence orchestrate groupe/gamma:L1` run from there, while `priority:` and the alphabetical order read the folder's own name.
633
- "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
634
- (this is what `cadence-hud` asks from a session opened inside a project). The tour is read-only: it changes
635
- no plan. It exits 0 even when a project is in error — that project's line reads
636
- `beta · ✗ erreur : <cause>`. The `lead` skill runs it instead of one subagent per project.
637
-
638
- ## corrections
639
-
640
- `cadence corrections [folder] [--classify [--model m]] [--cache file] [--sample n] [--until date] [--since date] [--json]` measures how often a wave
641
- has to correct itself. From the folder a wave was launched from (default: the current one) it reads the journals
642
- `<folder>/.cadence/runs/<wave>/<project>--<lot>.json` of **every project** and prints:
643
-
644
- - the **lots with a correction**: of the lots that went through a successful write session, the share with at least one
645
- and at least two `fix` passes, per project and for visible versus non-visible lots (`61 %` and `29 %` of 263 lots in the 10-10 baseline);
646
- - the **passes by cause**, in total, per project and **per visibility** (visible lots apart from the others, with the share of `spec-floue`, `regle-inventee` and `test-manquant` among each one's passes — the reference an evolution aimed at screens is compared to is that of the visible lots, not « all lots »: 16 % of 45 passes against 31 % overall in the 10-10 baseline). The cause of a pass is read from the findings of the review that
647
- precedes it, among: unclear spec or missing criterion (`spec-floue`), invented business rule (`regle-inventee`),
648
- architecture drift (`derive-architecture`), data / SQL (`donnees-sql`), code bug (`bug-code`), missing test
649
- (`test-manquant`), false review finding (`faux-constat`). One cause per pass: the root of what forced the fix. A pass with no review finding to read (triggered by red tests right after the write session) is counted apart as `sans-constat` and never sent to the model.
650
-
651
- Without `--classify` the command calls no model: the passes not yet classified count as `non classé`. With `--classify`
652
- each pass is classified by a **local model served by Ollama** (`OLLAMA_HOST`, default `http://localhost:11434`;
653
- `--model`, default `mistral-small3.2:24b`; temperature 0, output constrained to the seven causes), off the Anthropic quota.
654
- The classification is cached in `<folder>/.cadence/corrections.json` (or `--cache file`): a re-run classifies only the
655
- new passes, and a pass the model could not answer stays out of the cache. `--sample n` prints `n` passes with findings spread
656
- evenly over the sorted keys, with their findings and the model's cause, to be read by hand; a `manual` field written
657
- beside a pass in the cache file (one of the seven causes, else the command refuses the file) overrides the model's cause in every table and gives the **agreement rate** of the model
658
- on the reviewed sample. `--until date` (ISO, e.g. `2026-10-10T18:31Z`) reads only the waves created up to then (`created` in the wave's `wave.json`) and, in them, only the steps ended by then (`ended`), so that neither later waves nor a wave still running at that time change a published baseline; a wave or a step with no readable date is left out. `--since date` (ISO, same format) keeps only the lots of the waves **created after** that date (`created` in the wave's `wave.json`), **with all their passes**: a wave renders every brief from the copy of the tool frozen when it is created (`<wave>/tool/`), so the creation date of the wave, not the start of a lot, tells which brief the lot was given. A `--continue` wave created before a change of brief therefore does not give it to its lots, even those that start after the change (a difference of two unbounded runs is wrong as soon as a wave straddles the date); the exception is a wave resumed (`--resume`) without a snapshot (older than L61) or, with `--budget project:lot=+N`, from a snapshot older than L223: it runs with the current `dist/` and templates, so it does receive the new brief. A wave with no readable `created` is left out, by `--since` as by `--until`. `--since` is strict and `--until` inclusive, so a wave created exactly at the date is on the `--until` side only and the two sets never overlap; `--since` and `--until` can be combined (a window of creation dates). Together `--until d` and `--since d` split the waves, and they add up to the unbounded total only when `d` is set while no wave is running (between two sessions, as `npm run service` requires; `cadence orchestrate --status` shows none, **and** no wave created before `d` is still resumable: `--status` lists only the live waves, an interrupted or suspended one is not shown and `--resume` would replay passes after `d`; list them with `jq -r 'select(.status != "done") | "\(.id) \(.status) \(.created)"' <folder>/.cadence/runs/*/wave.json`, and finish each before setting `d` or never resume it): a wave created before `d` that ends a pass after `d` leaves that pass in neither (`--until` does not read it, so that a published baseline does not move), and a gap between the sum and the unbounded total means `d` fell inside a wave. `--json` prints the measure as data. The command reads the journals and writes nothing but the cache.
659
- The first baseline is in [docs/mesures/corrections-2026-10-10.md](docs/mesures/corrections-2026-10-10.md); `test/corrections-mesure.test.ts` replays its command on the journals and checks the document's results word for word (skipped when the journals are not there). The measure of the screen-sheet step against the visible lots of this baseline (L176: 8 lots with a sheet, verdict per row of the success table, token cost beside the passes avoided) is in [docs/mesures/etape-spec-2026-10-12.md](docs/mesures/etape-spec-2026-10-12.md); the target of its third row is ≤ 8 %, half the 16 % reference.
660
-
661
- ## spec
662
-
663
- `cadence spec check <fiche.yaml>` checks a **sheet** (`fiche.yaml`, the machine-readable output of the spec step): a **screen sheet** (a lot with `screen:`), a **back-end sheet** (a lot that changes a behaviour without a screen) or a **mixed sheet** with both parts. The screen part is designed in
664
- [docs/superpowers/specs/2026-10-10-etape-spec-design.md](docs/superpowers/specs/2026-10-10-etape-spec-design.md)). Start from the template
665
- [`templates/fiche.yaml`](templates/fiche.yaml) (it passes the check as is), a worked example sits next to the design. It prints one
666
- `✗ …` line per violation and exits **0** when the sheet is valid, **1** with violations, **2** when the file is missing, is not YAML or the usage is wrong. The invariants:
667
-
668
- - `ecran` has `id`, `titre`, `route` (starts with `/`), `lot`, `maquette`, and `largeurs: [1440, 390]`; `etats` holds at least `vide`, `chargement`, `erreur`, none twice;
669
- - `champs[]`: a unique kebab-case `id` (it is also the mockup's `data-testid`), `libelle`, `type`, `source`, and a boolean `obligatoire`;
670
- - `regles[]` are numbered `R1`, `R2`… in order with no gap; each has an `etat` that is one of `etats`, an `enonce`, a `message` (the exact text on screen, or `null`),
671
- a `statut` — `sourcee` with the `source` it comes from, or `hypothese` with the `si_faux` that says what would change — and at least one planned test, named `Rn — …`;
672
- - `masque` (optional, only on a rule of state `vide`) lists the fields the rule makes disappear: every id must be a field of the sheet and none may be one the rule shows in `champs`;
673
- - `actions[]` are numbered `A1`… in order, their `declencheur` is a field id (or `null` for the page load), their `appel` reads `VERB /path`, and the `regles` they carry exist.
674
-
675
- **Back-end sheet** (L187; design: [docs/superpowers/specs/2026-10-11-etape-spec-back-design.md](docs/superpowers/specs/2026-10-11-etape-spec-back-design.md)). Start from [`templates/fiche-back.yaml`](templates/fiche-back.yaml) (it passes the check as is); a worked example, a real lot of ol-companion, sits next to the design. The sheet has `sujet` instead of `ecran` (no `etats`, `champs`, mockup), and the same `regles`. The back-end invariants, one `✗ …` line each:
676
-
677
- - `sujet` has `id` (kebab-case, the folder name `docs/specs/<id>/`), `titre`, `lot`, and a `perimetre` with `modules`, `endpoints` and `jobs` (lists of texts, at least one entry in the three) and an optional `hors_perimetre`;
678
- - `cas[]` are numbered `C1`, `C2`… with no gap, each with `titre`, `acteur`, `declencheur`, `preconditions`, `nominal` (a non-empty list of steps), `resultat`, and `alternatives` — a list of `{ si, alors, regles }`, each citing at least one existing rule — or `alternatives: []` with its reason in `sans_alternative`; every case is cited by at least one rule;
679
- - a back-end rule has `cas` (at least one existing case id) instead of `etat`, plus the screen rule's `enonce`, `statut` (`sourcee` + `source`, or `hypothese` + `si_faux`) and tests named `Rn — …`; `message`, `champs` and `masque` only make sense on a rule attached to a state;
680
- - `contrats[]` are numbered `A1`… with no gap, each with an `appel` (`VERB /path`, `job <name>` or `commande <name>`), `entree`, `sortie`, `erreurs` — a list of `{ code, message }` where `code` is `HTTP <n>` or `log debug|log|warn|warning|error|info|critical|fatal|verbose` and `message` the exact text — or `erreurs: []` with its reason in `sans_erreur`, and the `regles` it carries exist;
681
- - `donnees[]` are numbered `D1`…, each with `objet`, `note`, a boolean `existant` and a `modification` (`aucune`, `ajout` or `changement`); an existing object that is modified carries the `decision` of the lead (the answer to the targeted question). The list may be empty (`[]`: the lot touches no data).
682
-
683
- **Mixed sheet** (a lot with `screen:` and a back-end part): `sujet` **and** `ecran` side by side, with the same `id` (the folder name) and the same `lot`. A rule is attached to a state (`etat`, a screen rule) or to cases (`cas`), at least one of the two. The `A` ids are shared: the actions of the screen and the `contrats` are numbered `A1…An` with no gap over the whole, and an action that calls an API already listed in `contrats` points to it with `contrat: Ax` instead of repeating its `appel`. An action that has its own `appel` equal to the `appel` of a contract is reported (it must point to the contract). A sheet without `ecran` refuses `actions`, `etats` and `champs` (they only make sense with a screen part), and a rule with `etat` there gets that single report. A sheet that only has `ecran` stays valid as it is (no migration); `sujet` is required as soon as the lot is back-end or mixed.
684
-
685
- `cadence spec check` prints the same summary line for each kind (`N cas, N règles (N hypothèses), N contrats, N données` for a back-end sheet) and keeps its exit codes: 0 valid, 1 violations, 2 usage.
28
+ | [docs/raf.md](docs/raf.md) | the plan: commands, plan file, foreign plan formats, Gantt |
29
+ | [docs/orchestrate.md](docs/orchestrate.md) | waves of sessions: budgets, spec, accept and review steps, neighbouring repositories |
30
+ | [docs/deliver.md](docs/deliver.md) | delivery: wait for CI, deploy, verify the effect |
31
+ | [docs/spec.md](docs/spec.md) | the screen and back-end sheets, `cadence spec check\|qa\|reseal` |
32
+ | [docs/lead.md](docs/lead.md) | the lead workflow and `cadence lead tour` |
33
+ | [docs/stabilite.md](docs/stabilite.md) | what is guaranteed from 1.0.0 and what is not |
686
34
 
687
- `cadence spec reseal <lot> <test> --reason texte` is the lead's way to correct one sealed acceptance test of the lot in progress that contradicts its sheet (unchanged, or amended by commits of the lot, L217) (of a delivered lot: add `--porteur <lot in progress>`), see [The accept step](#the-accept-step-red-acceptance-tests-and-their-lock).
35
+ Also: [news](docs/news.md) (changelog, UX, code and QA reviews, documentation that follows the code), [session](docs/session.md), [corrections](docs/corrections.md), [skills and agents](docs/skills.md), [cadence-hud](docs/cadence-hud.md), [releasing](docs/releasing.md), [overview](docs/overview.md), [how it all works](docs/fonctionnement.md) (four figures).
688
36
 
689
- `cadence spec qa <fiche.yaml>` derives from a valid sheet the section of `docs/qa/expectations.md` for its screen and prints it on the standard output
690
- (it never writes the file: you paste the section, and the lead commits it like the rest of the plan files). A sheet with violations prints them on the
691
- error output, nothing on the standard one, and exits 1. The derivation is built so that the planned empty and error states are never reported as defects:
37
+ <details>
38
+ <summary>The four tools in short</summary>
692
39
 
693
- - `## <route>` is the screen's route;
694
- - `shows:` — one line per mandatory field (its `libelle`), **except** the fields that belong to a state (listed in `champs` of a rule of state `vide` or `erreur`, like the empty-state message);
695
- the line of a field listed in the `masque` of a `sourcee` rule of state `vide` ends with `— unless <the rule's statement>`; the other fields (a static title) never get one;
696
- - `never:` — the messages of the rules of state `erreur`, on one line; the message of a rule of state `vide` is a planned display and never goes there;
697
- - `api:` — the load actions only (`declencheur: null`) in `GET` (the QA pass never sends a POST); the line ends with `may be empty when <the rule's statement>` when a `sourcee` rule of state `vide` carries the action.
698
- Actions triggered by a field stay with the acceptance tests.
40
+ - **raf** (French *reste à faire*, "what is left to do"): the plan, linked to your commits by the lot id in the message, computed from `git log` and never stored.
41
+ - **news**: a user-facing changelog with screenshots, tied to the plan; a lot is not done until its entry is written.
42
+ - **session**: `session start` and `session close` gather the facts of a work session.
43
+ - **deliver**: wait for the CI of the pushed commit, run the deploy commands, then **verify the effect**; a delivery is done only when its checks pass.
699
44
 
700
- `spec qa` only derives the **screen** part: a back-end sheet, which has no `ecran`, is refused (exit 2, « la fiche n'a pas de partie écran »); on a mixed sheet it reads the actions' calls through their `contrat`.
45
+ Reviews are opt-in gates: `ux-reviewer` (no user-facing change is done before its usability review), `code-reviewer` (no lot with commits is done before its code review) and `qa-reviewer`, which walks the delivered app in a real browser.
701
46
 
47
+ </details>
702
48
 
703
- ## deliver
704
-
705
- A delivery is done when its checks pass, not when a tool says "success".
706
-
707
- ```yaml
708
- # cadence.yaml, at the repository root
709
- deliver:
710
- ci: github # github | none | { command: "…" } (default: none)
711
- ciTimeout: 1800 # seconds
712
- deploy: # sh commands, in order, at the repo root
713
- - ./scripts/deploy.sh "$CADENCE_SHORT"
714
- verify: # at least one; retried every 10 s until verifyTimeout
715
- - url: https://app.example.com/api/health
716
- status: 200 # default 200
717
- - url: https://app.example.com/version.txt
718
- contains: "${SHORT}"
719
- - command: kubectl rollout status deploy/app --timeout=60s
720
- verifyTimeout: 300
721
- deployTimeout: 1800 # seconds, per deploy command
722
- ```
723
-
724
- A public title (`public:`) longer than the site's Plan page accepts fails its build. Declare the limit
725
- in `cadence.yaml` and it is caught before the push:
726
-
727
- ```yaml
728
- news:
729
- publicTitleMax: 80 # characters; without the key, `raf check` warns at 80 in a project that has a news directory
730
- ```
731
-
732
- `raf check` also warns (`⚠`, not an error: exit code 0 if nothing else is wrong) about every visible lot, dropped ones excepted, that has no public title: the site would show its technical title (a done lot cited by a News entry is spared: the site reuses that entry's title). Fix it with `raf public <id> "…"`. On a plan kept by another tool (read-only), the warning only applies if `plan.fields` maps a `public` field, or if some lot already carries a literal `public:` key (a field left out is read under its own name): without either there is no public title to set, so it would come back at every session.
733
-
734
- With the key, `raf public`, `raf add --public`, `raf done` (on a lot whose public title is too long) and
735
- `cadence news new` (its title) refuse above it; without it, only `raf check` warns.
736
-
737
- ```sh
738
- cadence deliver --dry-run # preconditions, then the resolved steps; nothing runs
739
- cadence deliver # 0 delivered and verified · 1 a step failed · 2 refused before acting
740
- ```
741
-
742
- - **Preconditions**: no modified tracked file; `HEAD` is on a remote branch (the
743
- CI can only build what was pushed); no other delivery running (a lock whose
744
- process died is removed with a warning); with `ci: github`, `gh` installed and
745
- logged in.
746
- - Every command is killed when it exceeds its budget (CI, `deployTimeout`, what is
747
- left of `verifyTimeout`) and reported as "délai dépassé". Killed means the
748
- whole tree, followed while the command runs: every 200 ms cadence lists the
749
- command's descendants and remembers each process it has seen with its start
750
- time, so a process whose parent exits (re-parented to init — a background
751
- child of a script, `sh`'s fork for each command) stays known. To kill, cadence
752
- takes every remembered process still alive with the same start time (never a
753
- recycled pid) plus all its current descendants, stops each one first
754
- (`SIGSTOP`, the tree is re-read until nothing new appears), then kills them
755
- (`SIGKILL`). This happens on a delay overrun, and at once when cadence
756
- receives `SIGTERM`. On Ctrl-C or a hangup the tree has already received the
757
- signal from the terminal: cadence gives the whole followed set, not just the
758
- command, up to 2 seconds to finish (a script's `trap`, git removing its
759
- `index.lock`), carries on the moment none of it is alive, and kills what is
760
- left after the 2 seconds, a background child that ignores Ctrl-C included. If
761
- the command was never listed (process list unreadable from the start), the
762
- grace lasts as long as the command itself is alive, and what remains after
763
- 2 seconds is killed through the same fallback (the command itself only). The
764
- same fallback applies when the process list fails while the command runs and
765
- is still failing when the kill comes (the last listing is not trusted after
766
- 1 second): only the command is killed, its descendants survive, and cadence
767
- says so once on stderr.
768
- A delay overrun and `SIGTERM` give no grace: a script's own `trap` does not
769
- get to finish there. The lock is held until the followed set is empty: when
770
- cadence dies of the signal it dies after the tree, and its stale lock, which
771
- the next delivery removes, leaves nothing of the first delivery running.
772
- What can still outlive cadence and run concurrently with the next delivery:
773
- a process that leaves the tree before cadence first sees it (a daemon's
774
- double fork, `setsid`, `nohup … &` from a shell that exits, all within less
775
- than 200 ms), a process of another user that cadence may not signal (`sudo`),
776
- a background process still running when the command returns normally (it is
777
- neither waited for nor killed), and everything if cadence itself is killed
778
- with `SIGKILL` (`kill -9`, the OOM killer).
779
- Where the process list cannot be read (no `/proc` and `ps` failing), cadence
780
- can only kill the root command (`sh`): it leads no group of its own, since it
781
- is deliberately kept in cadence's group, so its descendants are not killed and
782
- survive it. A list that fails for more than a second is no longer
783
- trusted (a followed pid may have been recycled): nothing is signalled from it.
784
- cadence says so once on stderr when the list is unavailable.
785
- - Commands run in cadence's own process group and session, attached to the
786
- terminal: `ssh`, `sudo` or `pinentry` can prompt on `/dev/tty`, and Ctrl-C or
787
- closing the terminal stops the running command together with cadence (within
788
- the limits above).
789
- - **CI** `github`: polls `gh run list --commit <sha>` every 15 s; no run after
790
- 5 minutes is a failure (you probably pushed another commit than the one you
791
- deliver); every run must end `success`, `skipped` or `neutral`. `gh` errors
792
- are retried, and reported with their cause after 5 minutes.
793
- - Commands get `CADENCE_SHA`, `CADENCE_SHORT` (7 characters) and `CADENCE_BRANCH`;
794
- `${SHA}` and `${SHORT}` are replaced in `url` and `contains`.
795
- - On success the lots cited by the commits since the previous delivery are
796
- listed, so you can `raf done` those whose effect you have seen. With a
797
- read-only plan, only the lots that were in progress when the delivery started
798
- are listed: an id quoted in a message for context (a finished lot, a
799
- reservation number that looks like one) is not a delivered lot. Plan upkeep
800
- commits (see "Plan upkeep needs no lot") are skipped in that list: they cite
801
- lots without delivering anything.
802
- - With [`docs.articles`](#an-article-elsewhere-that-follows-the-delivery), a green delivery of lots with a
803
- public title also opens the lot `Article <project> à rafraîchir` in the plan.
804
-
805
- ### A project with its own delivery script
806
-
807
- A project that already delivers with its own script (CI wait, deploy, business
808
- checks) plugs it in instead of rewriting it as `ci` / `deploy` / `verify`:
809
-
810
- ```yaml
811
- deliver:
812
- script: ./scripts/ship.sh "$CADENCE_SHORT" # replaces ci and deploy
813
- allowDirty: true # optional: a modified tree is reported, not refused
814
- deployTimeout: 3600 # seconds, for the whole script
815
- verify: [] # optional here: the script's own checks count
816
- ```
817
-
818
- ```sh
819
- cadence deliver -- api frontend --news docs/changelog/x.md -- map # everything after the first « -- » goes to the script
820
- cadence deliver --dry-run -- api # shows the full command, runs nothing
821
- cadence deliver --sha 6b0d9aa -- api # an earlier pushed commit instead of HEAD
822
- ```
823
-
824
- `--sha` (any mode) delivers a pushed commit other than `HEAD` — for a CI that
825
- builds each service only on the commit that touched it. `allowDirty` suits a
826
- working tree shared by several sessions when the delivery starts from a pushed
827
- sha and never from local files; without it a modified tree is refused.
828
-
829
- cadence keeps what the script usually lacks: the preconditions (clean tree, pushed
830
- `HEAD`), the lock (never two deliveries at once), the delivery log and the lots
831
- delivered. Arguments are quoted for `sh`, so spaces and quotes reach the script
832
- intact. If the script commits and pushes during the delivery (stamping a
833
- changelog entry, say), the new `HEAD` is the sha recorded as delivered. Exit code
834
- 0 of the script means delivered; `verify` checks, if any, run after it.
835
-
836
- ### verify: replay the effect checks, outside a delivery
837
-
838
- A green delivery says the effect was right *then*. `cadence verify` replays the
839
- `deliver.verify` checks of `cadence.yaml` at any time — no CI, no deploy, no lock,
840
- no journal entry, nothing written.
841
-
842
- ```sh
843
- cadence verify # one pass, one line per check, then a one-line summary
844
- cadence verify --retry 60 # retry each failing check for up to 60 s (deliver's 300 s is not applied)
845
- cadence verify --sha 6b0d9aa # the sha that replaces ${SHA} / ${SHORT} (default: see below)
846
- ```
847
-
848
- ```
849
- ✓ GET https://ol.example/api/health → 200
850
- ✗ GET https://ol.example/api/lineup → 200, contient « "starters" » — « "starters" » absent de la réponse
851
- verify : 1 effet rouge sur 2 vérifications
852
- ```
853
-
854
- Exit code: **0** every check green · **1** at least one red effect · **2** nothing
855
- to verify or invalid configuration.
856
-
857
- **Which sha is expected.** By default `${SHA}` / `${SHORT}` is the last recorded
858
- delivery (else `HEAD`). One case differs: a project with `ci: none` (the default) and
859
- **neither** deploy commands (`deliver.deploy`) nor a delivery script — a host that
860
- builds every push, like Cloudflare Pages — delivers by pushing, and every push counts,
861
- plan-maintenance commits included. The expected sha is then the head of the tracked
862
- upstream branch (`origin/<branch>`, as last fetched — `verify` does no network git),
863
- **provided that upstream is the remote's production branch**: read without network from
864
- `refs/remotes/origin/HEAD` (set by `git clone` or `git remote set-head origin <branch>`);
865
- when that reference does not exist locally (repository created with `git init` + push),
866
- `main` then `master` are taken as the production branch. A work branch pushed with `-u`
867
- is not published by such a host, so it keeps the default rule. A project with `ci: github`
868
- or a `ci.command` and no deploy also keeps the last delivery. When local `HEAD` is ahead
869
- of the upstream, the report says so first — `2 commit(s) non poussé(s) — l'effet vérifié
870
- est celui de origin/main` — so a red there reads "waiting for a push", not "broken
871
- effect". `session start` uses the same rule; `--sha` always wins.
872
-
873
- Time limits differ from deliver's. `verify` runs all the checks **in parallel**,
874
- each try with the whole 120 s limit (a `url` request gives up after 20 s; a slow
875
- check is killed at its limit, "délai dépassé", and takes nothing from the others); results are printed in the order
876
- of `cadence.yaml`. Retries repeat every 10 s (deliver's interval) up to `--retry`
877
- seconds for each check on its own, so a run lasts at most `--retry` + 120 s.
878
- deliver, by contrast, runs its checks one after the other, retries each until
879
- `verifyTimeout` (300 s by default) is spent, and stops at the first check that
880
- never turns green. The check code is deliver's own (`url` / `status` /
881
- `contains` / `command`, same messages). A project with
882
- a delivery script (`deliver.script`) and no `verify` declares no effect checks —
883
- `verify` says so and exits 2 (its script's own checks stay its business); add
884
- `deliver.verify` to replay some.
885
-
886
- Each `command` check of `verify` (and of `session start`) runs in a process group
887
- of its own, detached from the terminal (no `/dev/tty`: a check must not prompt).
888
- At its time limit the whole group is killed, so no child survives it; Ctrl-C,
889
- `SIGTERM` or a closed terminal is passed on to the running checks before cadence
890
- exits. As the checks run together, the output of their commands may interleave;
891
- the result lines come after it, in order.
892
-
893
- Make `verify` count: a health endpoint stays green while the data is wrong (a
894
- lineup served empty for 38 h behind a green `/api/health`). Add a check on the
895
- content that matters, e.g. `url: …/api/lineup` with `contains: '"starters"'`.
896
-
897
- `cadence session start` runs the same checks in parallel (one try each, command
898
- output muted), each with a 10 s limit, so the whole step takes about 10 s at worst;
899
- it is not deliver's `verifyTimeout`, and there is no retry. It adds an
900
- **"Effets en production"** section to the morning report: a single `✓` line when
901
- everything is green, the red effects and the summary otherwise. It is a fact like
902
- the others: it never changes the exit code, and an unreachable network shows up
903
- as red lines ("erreur réseau") without blocking the session. No section for a
904
- project without `deliver.verify`.
905
-
906
- ## orchestrate
907
-
908
- `cadence orchestrate` is a program above `/lead`, not a conversation: for each lot you choose, it runs
909
- a **fresh** `claude -p` session per step with a short brief, reads the result, writes the state in
910
- files and moves on. The lead session keeps only the decision (which lots), the final table and the
911
- questions. Nothing is resumed: a correction is a new session, never the author's reopened. The one
912
- exception is a **formatting retry** (below).
913
-
914
- ```sh
915
- cadence orchestrate finance-tracker:L41 ol-companion:L22 cadence:L18@haiku
916
- cadence orchestrate L18 # from inside a project
917
- cadence orchestrate maritime-atlas:Q4/accueil-4-ux12@haiku # a lot id may contain "/" (sub-tasks of read-only plans)
918
- cadence orchestrate … --budget 1.5M # 1500000, 1.5M, 800k; default 2M
919
- cadence orchestrate … --max-sessions 3 # sessions running at the same moment, all waves together; default 2
920
- cadence orchestrate … --dry-run # preconditions + the plan of the wave; nothing is started
921
- cadence orchestrate --continue [--until 18:00] [--priority cadence,maritime] # the wave draws the next ready lot itself (below)
922
- cadence orchestrate --waits [--since 2026-10-12] # time spent waiting for a session slot, per day and per wave (below)
923
- cadence orchestrate --status [<wave>] # the live waves and the repositories they hold, then the table (default: the last wave of this folder)
924
- 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)
925
- cadence orchestrate --resume [<wave>] [--budget 1M] [--answer ol-companion:L22 "reply"] [--drop ccc:L29]
926
- cadence orchestrate --resume <wave> --budget maritime:B26.4=+300k # replay a lot handed back for its budget, with the budget raised (below)
927
- cadence orchestrate --drop ccc:L29 [--wave <wave>] # from another terminal: take a lot out of the running wave
928
- cadence orchestrate --stop-after-current [--wave <wave>] # from another terminal: let the running sessions finish, start nothing else
929
- ```
930
-
931
- **`--drop` and `--stop-after-current` — steering a running wave (L79)**: both are run from the folder the wave was launched
932
- from (`--wave <id>` when several waves run from it), and write a request in the wave's state folder
933
- (`.cadence/runs/<wave>/control.log`, append-only), which the wave reads before each session — no signal, no edit of
934
- `journal.log`. `--drop <project:lot>` (repeatable) takes a lot out of the wave: a lot not started yet never starts (no
935
- `raf start`); a lot with a session running **lets that session finish** (it is not killed), then plays no other: it is
936
- *handed back* (`retiré de la vague (--drop)`), its commits stay, and a lot that depended on it is handed back too. A lot
937
- waiting for an answer (`question`) is handed back as well, at the end of the wave or on `--resume --drop`, instead of staying in
938
- question forever. A lot that is unknown or already finished is refused (exit 2). `--stop-after-current` lets the sessions running finish, starts no
939
- other session nor lot (`arrêt demandé (--stop-after-current)`), and leaves the wave **interrupted** and resumable
940
- (`--resume`, which forgets the stop; `--continue` draws nothing more). Neither combines with lots to launch,
941
- `--continue`, `--dry-run`, `--budget` or `--answer`, nor `--status`; without a live wave from that folder they are refused
942
- (exit 2). A stopped wave is no longer live, so to take a lot out of it use **`--resume --drop <project:lot>`**: the lot is
943
- handed back instead of being replayed (an unknown or finished lot is refused, exit 2, before anything runs). A lot taken out
944
- this way is left out of the resume checks (repository lock, `.nvmrc` Node, dirty tree): only the lots that will be replayed can
945
- refuse the resume, and a refusal writes nothing to `control.log`.
946
- `--resume --stop-after-current` is refused (exit 2): resuming contradicts stopping.
947
-
948
- **`--resume --budget <project>:<lot>=+N` — replaying a lot handed back for its budget (L223)**: a lot handed back with
949
- `correction non jouée (budget)` or `budget du lot atteint` kept its findings but not its budget, and a plain `--resume` does not replay a
950
- finished lot. `--budget maritime:B26.4=+300k` (the project may be left out when only one project carries the lot; the option is
951
- repeatable for several lots, and `--budget 1M` still raises the wave's budget — the `=` tells them apart) puts the lot back in play:
952
- its budget becomes **what its sessions already counted plus N** (849 k spent of 750 k, `=+300k`: 1 149 000), its outcome is cleared,
953
- and the step that was not played (`fix`, `implement`, `spec` or `accept` — the only ones a lot budget bounds; a review is never handed
954
- back for it) runs in a **new session** (never `claude --resume` on the earlier one) with the findings the lot kept, then the normal cycle
955
- goes on: review, verdict recorded with the sha re-read and the plan committed. Nothing is recorded twice, since a lot handed back for
956
- its budget never had a compliant review. The reserve rule is unchanged: a `fix` leaves only if spent + 155 000 < the new budget,
957
- so N of at most 155 000 on a `fix` is refused, and a budget still too small hands the lot back again with the same text. The wave's own budget
958
- and quota still apply (exhausted: the lot stays suspended, exit 3). A wave that is `done` is resumed this way when a lot is named; without the
959
- option it is still refused. The named lot undergoes the checks of a live lot (dirty tree, repository held by another orchestration, `.nvmrc`
960
- Node). Refused with exit 2, before anything is written: a malformed `projet:lot=+N` or an option without `--resume`, an unknown or
961
- ambiguous lot (`projet:lot` is then asked), a lot that is not handed back for its budget with a write step to replay, a lot named twice
962
- (`p:L` then `L` counts) or also named by `--drop` or `--answer`.
963
- A wave runs on its own snapshot of the tool, but one launched before L223 would refuse `projet:lot=+N` as an invalid budget:
964
- for such a wave (B26.4's included) the resume that names a lot runs on the current `dist/` instead, with a warning on the error output.
965
- A wave runs on its own snapshot of the tool: one launched before L79 never reads `control.log`, so the request is written but the
966
- command warns `ne lit pas les demandes de contrôle` on the error output.
967
-
968
- **`--continue` — idle time (L147)**: lots given on the command line open the wave (none: the first ones are drawn from the
969
- plan too); then, each time the lots in play are finished, the wave **draws the next ready lots of the plan** — up to the
970
- session cap — and plays them in the same wave (same budget, same state folder, journal line `continue : tire …`). A lot
971
- is drawn when it is `todo`, its `after` is lifted, it is not recurring, neither its title nor a note says « à décider »
972
- (« à 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; a lot with `screen: page` is worth 1.3 M whatever its estimate (`component` and `journey` have their ordinary budget), or its ordinary budget plus the 450 k spec-stage allocation if that is more, and a lot with `spec: true` gets that allocation on top of its ordinary budget)
973
- 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
974
- 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
975
- repository** while other repositories have ready lots — the pool plays the lots of one repository one after the other, so a
976
- 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**:
977
- `--priority cadence,maritime`, or a `priority: [cadence, maritime]` list in the `cadence.yaml` of the folder you launch from
978
- (the parent of the projects); a name also covers `name-…` (`maritime` → `maritime-atlas`), the other projects come after, in
979
- alphabetical order. Launched from inside a project, only that project is drawn. A `priority:` key that cannot be read is refused before anything
980
- starts, lots given or not. A candidate that preflight refuses (dirty tree, Node missing…) is skipped with its cause in the
981
- journal (`continue : lot sauté — <cause>`; for the first draw also on the error output). Drawing stops — `continue : arrêt — <reason>` — at: the budget, the
982
- time window (`--until HH:MM`, today's clock: nothing new is drawn from then on, a running lot is not cut), the usage limit,
983
- a question asked (the wave stays resumable with `--answer`), two lots handed back in a row, an interrupted wave, a `--stop-after-current` request (`arrêt demandé`, even when the last running
984
- lots finished cleanly), or no ready
985
- lot whose estimate fits the budget left. `--resume … --continue` draws again after the resumed lots. `--dry-run --continue` replays the
986
- 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
987
- 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.
988
-
989
- **Chaining without the human (L148)**: the `lead` skill, once the human has chosen the first lots and the order, does not ask « next? »
990
- after a lot that comes back ready: it re-verifies it, records `raf done`, pushes, delivers, has the `qa-reviewer` check the
991
- delivered app, then starts the next wave with `--continue`, in the declared priority — one project at a time, within the two-session
992
- 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
993
- **60 %** upward it starts nothing: it runs `session-close` in each project touched, writes its memory and records three lines for next
994
- time (`cadence session next`). Stays with the human: the first choice and the order, the questions a session raised, the UX
995
- reservations; a lot that is not ready, a red delivery or a blocking QA finding is reported and not chained over.
996
-
997
- **Who gives the UX verdict (L197)**: by default the human looks at the `ux-reviewer`'s captures and gives the verdict of a `visible`
998
- lot (`raf ux`). A human who is not a UX judge can set, in the `cadence.yaml` of the folder the `lead` skill works from:
999
-
1000
- ```yaml
1001
- lead:
1002
- ux: lead # lead | human — default human, nothing changes without the key
1003
- ```
1004
-
1005
- With `lead.ux: lead` the lead forms the verdict itself, from the reviewer's report and its own look at the captures, records
1006
- `raf ux` and goes on; the human is consulted only at the slightest doubt (a reservation it cannot settle, a capture it
1007
- cannot read, a matter of taste), and the chain goes on with the other lots meanwhile. `cadence lead tour` prints
1008
- `verdict UX : lead …` as a last line when the setting is on (not part of `--json`), and `cadence.yaml · ✗ erreur : lead.ux doit
1009
- être « lead » ou « human »` for any other value. The setting is read from the parent folder only: a project's own `cadence.yaml` ignores it.
1010
-
1011
- **Lot ids containing `/` (L120)**: the first `:` separates the project from the lot, a final `@` the model, and the lot
1012
- keeps its `/` (`maritime-atlas:Q4/accueil-4-ux12@haiku`); this holds for `--dry-run`, `--status`, `--resume` and
1013
- `--answer` too. In `.cadence/runs/<wave>/` the `/` of the lot becomes `__` (`maritime-atlas--Q4__accueil-4-ux12.json`,
1014
- same for the lot folder and the dry-run briefs); ids without `/` are unchanged.
1015
-
1016
- You choose the lots; the order is the order given (one queue per repository, as many repositories at the
1017
- same time as the session cap allows — 2 by default). `@haiku|@sonnet|@opus` sets the model of the implementation and corrections of that
1018
- lot (default Sonnet; reviews are Opus, except the light review below; Haiku only when you write it, for a mechanical lot). Run it
1019
- in the background and read `--status`: it prints one line per transition and the final table.
1020
-
1021
- **Following a wave (L49)**: `--status --watch` clears the screen and redraws the live waves and the table every
1022
- `--interval` seconds (default 10), then stops by itself, leaving the last render, as soon as no wave is running
1023
- (exit code 0, like `--status`; a wave already finished gives a single render). The `cadence-lead` skill names the background
1024
- task « vague `<id>` : `<project>:<lot>`, … », starts the wave with `--wave <id>` and arms a follow-up on
1025
- `.cadence/runs/<id>/journal.log` that pushes each transition (lot → state, question, ready, handed back) into the conversation.
1026
- The Claude Code status band is the `cadence-hud` plugin.
1027
-
1028
- **Cycle of a lot**: preconditions (clean tracked files, lot `todo` or `doing`, dependencies met) →
1029
- `raf start` (committed alone) → implementation → **code review** → compliant (no blocking, no
1030
- major finding) → **UX review** if the lot is `visible` and the app is declared, **once**, on the code the review
1031
- found compliant (L221) → `raf review` is recorded by the orchestrator with the sha the *code* review read (the UX
1032
- verdict stays in the lot state, `uxVerdict`), then "ready to deliver". Not compliant → a correction in a new session,
1033
- then a new code review, with no UX session in between; a UX review that finds a blocking or major point sends the lot
1034
- to a correction (which receives the UX findings), then the code review, then the UX review again. **Two passes at most** (a third when the review converges: the count of blocking + major findings dropped
1035
- since the previous review and exactly one is left; the lot budget still applies), then the lot goes back to
1036
- you with the findings. A small lot (`estimate` ≤ 0.5 or `quickwin`) gets one single Opus pass for code and
1037
- usability (code only when the lot is not `visible`). Failing tests (reported red, or red when
1038
- `orchestrate.test` is run) go straight to a correction.
1039
-
1040
- **One UX review per lot (L221)**: the UX review of a `visible` lot used to run before the code review and again after every correction (finance L75 on 2026-10-15: three Opus UX sessions for one useful verdict). It now runs once, as the last step, on the code the review found compliant: after the single pass on minor findings and its short review when there is one. Without `orchestrate.ux`, or when the app does not start (port taken, silent app), no UX session is played: the lot concludes on the compliant code review and keeps its note (« UX à faire par le lead », « UX non vérifiée : … »). The lot budget bounds writing, never this review; an exhausted wave budget suspends it like any session. Every write step (implementation, correction) clears the lot's UX state (`ux`, `uxVerdict`, UX findings), so a stale UX verdict never sends a compliant code review back to a correction. A resumed wave whose state says `next = ux` (written before this change) replays the code review first unless the last compliant one read the current `HEAD`. The recorded `raf review` verdict and sha are the code review's; a commit made by someone else since that review hands the lot back, as before. A small lot and a lot with no screen keep their single review. The measure before/after is in [`docs/mesures/ux-une-fois-2026-10-15.md`](docs/mesures/ux-une-fois-2026-10-15.md).
1041
-
1042
- **Tests already red at the start (L213)**: a lot is no longer handed back « tests rouges » for failures that were there before it. When `orchestrate.test` fails (or the session reports red tests), the program reads the failing tests from the runner's output (vitest/jest `FAIL`/`●`, pytest `FAILED`, go `--- FAIL:`, TAP `not ok`), replays the same command at the sha the lot started from (the parent of the lot's oldest work commit, plan-only commits left out, else the `HEAD` before its first session) in a throwaway `git worktree` outside the repository, and compares: if every failing test was already red, it is not a defect of the lot — no correction pass, the code review takes place, the reviewer is told, and the final table lists them apart (`déjà rouge au départ (<sha>) : <test>`, 30 at most then a count). The worktree gets links to the repository's ignored folders at any depth, such as `.venv` and, in a monorepo, `packages/*/node_modules`, when their parent exists at that sha; build outputs such as `dist` or `coverage` are left out. Each `node_modules` is rebuilt entry by entry, every entry linked to the live one except the workspace links of npm, pnpm and yarn (`node_modules/@x/shared -> ../../packages/shared`, a link to code of the repository outside any `node_modules`), which are remade towards the worktree's own copy so that the start sha is tested against its own sources, not the lot's (a workspace package that does not exist at the start sha gets no link). Known limit: only `node_modules` links are remade; whatever points into the live repository by an absolute path, such as an editable Python install (`.venv` with a `.pth` file naming the live `src`, `pip install -e`) or an absolute symlink in another ignored folder, still runs the lot's code in the baseline. Likewise, a workspace package consumed through its build output (`main: dist/...`) cannot load in the baseline worktree: output dirs are not linked and nothing is built there, so such a monorepo keeps the pre-L213 « tests rouges » outcome. A lot resumed in a later wave keeps the same baseline: the sha is the parent of its oldest work commit, not the `HEAD` of the new wave, so a failure the lot itself introduced in an earlier wave is never excused. A test red now and not at the start stays a blocking finding, and only that one is given to the correction (the others are counted as « already red »). When the output names no test, or the baseline cannot be run, the tests stay red as before (no comparison is guessed). A failure the output does not tie to a test (unhandled error, a spec file that fails to load such as jest « Test suite failed to run » or vitest `FAIL a.test.ts [ a.test.ts ]`, coverage threshold) keeps the tests red. Without `orchestrate.test` the program cannot replay anything: a session that reports green tests and lists under `echecsDeBase` the failures it saw at the start sha gets them listed apart, marked « non vérifié » in the table, and the review takes place; a session that reports the tests red stays red, the brief asking it not to report red for base failures alone — unless (L215) the report says so in a structured way: with `vert: false`, a non-empty `echecsDeBase` and an empty `nouveauxEchecs` (the list of tests red now that were not red at the start sha), `vert: false` is not a reason to correct, the lot carries a « non vérifié » warning, the reviewer is told that the baseline is unverified and the review takes place. A non-empty `nouveauxEchecs` with `vert: false` triggers a correction pass (the new failures are in the finding); an absent `nouveauxEchecs`, or an empty `echecsDeBase`, keeps the correction. The program never guesses from the text of the result (« 24 failed »). The `implement` and `fix` briefs ask for `echecsDeBase` and `nouveauxEchecs`.
1043
-
1044
- **Review sized to the lot (L108)**: a lot whose `estimate` is ≤ `orchestrate.review.threshold` (0.25 day by default)
1045
- is *light*: one single review (Sonnet by default, `code-reviewer` agent, same criteria), no minors pass — the minors are
1046
- returned to the lead as notes (proposals). A blocking or major finding on a light lot still triggers a correction, and
1047
- the review that follows is the full one (Opus); the cap of two correction passes is unchanged (except the convergence pass above). A bigger lot keeps the
1048
- chain described above (Opus, two corrections, minors pass). `--dry-run` shows « revue légère » on a light lot.
1049
-
1050
- ```yaml
1051
- orchestrate:
1052
- review: { threshold: 0.25, light: sonnet, full: opus } # defaults; threshold in days (0 = no light lot), models haiku|sonnet|opus
1053
- ```
1054
-
1055
- `full` is also the model of the UX review and of the single pass of a small lot that is not light.
1056
-
1057
- **Effort level per pass** (L137). Each session is launched with `claude -p --effort <level>` (Claude Code 2.1.284; an
1058
- older `claude` is refused at launch unless every pass is `default`). Defaults: `precheck` low (it only reads),
1059
- `implement` and `fix` medium, `review` high (`review-small` follows `review`), `ux` high. A project overrides any of them
1060
- in `cadence.yaml`; the levels are `low`, `medium`, `high`, `xhigh`, `max`, and `default` sends no `--effort` (the
1061
- level of the session, as before this key). The format-retry session (`--resume`) keeps the level of its pass, and
1062
- `--dry-run` shows the flag in each command line, except for a `precheck: local` session: `claude-local` is launched
1063
- without `--effort` (see below), so `effort.precheck` has no effect on it and the `precheck (local)` line does not show it.
1064
-
1065
- ```yaml
1066
- orchestrate:
1067
- effort: { precheck: low, spec: medium, maquette: high, fiche: medium, accept: medium, implement: medium, fix: medium, review: high, ux: high } # defaults
1068
- ```
1069
-
1070
- To measure what a level costs, run the same kind of wave before and after changing the key: each step of
1071
- `<wave>/<project>--<lot>.json` records its `effort`, its `tokens` and its `started` / `ended` times (a step from a wave
1072
- older than L137, or with `default`, has no `effort`); compare the totals per `kind` between the two waves.
1073
-
1074
- **Pre-check « deliverable already present? »** (L77). Before the first implementation of a lot that has no commit yet, a
1075
- short read-only session (Sonnet, `precheck` step, brief `templates/orchestrate/precheck.md`) looks in the repository for
1076
- what the lot asks for (another lot, or a correction, may have done it already). It answers `oui` (everything is there,
1077
- with proofs), `partiel` or `non`: on `oui` no implementation session is opened and the lot is handed back
1078
- (`livrable déjà présent : <résumé> (<preuves>)`) for the lead to drop or close it; on `partiel` the finding goes into the
1079
- implementation brief and a warning; on `non` — or an unreadable report, which only adds a warning — the wave goes on. A
1080
- lot that already has commits is never pre-checked (resuming it is legitimate). `orchestrate.precheck: false` turns it off.
1081
-
1082
- ### The spec step of a lot with `screen:`
1083
-
1084
- A lot that declares `screen:` (`raf add … --visible --screen page|component|journey`, or `raf screen <id> page|component|journey` on a lot that already exists, see [raf](#raf)) gets, when it is `page`, one more session, `spec`, **after** the pre-check and **before**
1085
- the first implementation: `precheck → spec → maquette → accept → implement → review → ux` (`maquette` unless `orchestrate.maquette: false`, see [The mockup review](#the-mockup-review-before-any-code); `accept` only when the project declares `orchestrate.accept`, see below). A lot without `screen:` or `spec: true` follows the usual path, unchanged (a lot with `spec: true` and no screen has its own chain, see [The spec step of a lot with `spec: true`](#the-spec-step-of-a-lot-with-spec-true)). `--dry-run` lists the step
1086
- (`spec (sonnet)`), its command line and its brief `templates/orchestrate/spec.md`.
1087
-
1088
- - **Threshold** (L209): only `screen: page` enters this chain. The measure of 2026-10-12 (no row of the success table reached, about 570 k tokens per lot, 4.5 correction passes per journey lot) narrowed it: `component` and `journey` stay valid values of `screen:` (no existing plan breaks, `raf screen` still sets them) but a lot that carries one follows the ordinary path and gets the ordinary budget, without the 1.3 M flat rate. A lot already in the chain (its sheet, or its `acceptation-<lot>.json`, exists) keeps its sheet and its lock. The `spec: true` step and the back part of a mixed lot do not change. The next measure covers 8 `page` lots.
1089
- - **The session** (L171): Sonnet, the `spec-writer` agent (it writes only the spec folder, never application code), Playwright loaded like for `implement`. It reads the lot,
1090
- the project's CLAUDE.md and its charter (CSS, neighbouring components, `docs/qa/expectations.md`, existing routes and API contracts), then writes in
1091
- `<orchestrate.spec.dir>/<écran>/` (`docs/specs` by default): `maquette.html` (static HTML + CSS to the project's own style; one section `#etat-vide`, `#etat-chargement`,
1092
- `#etat-erreur` and `#etat-charge` per state; every field of the sheet carries `data-testid="<field id>"`), one capture per state and per width
1093
- `maquette-<état>-<largeur>.png` (1440 and 390), and `fiche.yaml` (the template `templates/fiche.yaml` is part of its brief). The mockup is rendered by
1094
- `orchestrate.spec.render` when the project declares it (the session runs that command), otherwise with Playwright; the captures land in the wave's output directory and the session copies the
1095
- kept ones into the folder. `effort.spec` (default `medium`) sets its effort level. It commits the whole spec in one commit that cites the lot (`spec(L7): …`).
1096
- - **What the program checks** afterwards, with no model: the sheet passes `cadence spec check`, is the lot's (`ecran.lot`) and sits in the folder named by `ecran.id`; `maquette.html` exists with a
1097
- `#etat-<état>` section per state and a `data-testid` per field; every state has its capture at both widths; everything is committed and the tree is clean. Anything missing is listed back to **one second
1098
- `spec` session** (a fresh session whose brief names the points); if the second spec is still not valid the lot is handed back (`spec non conforme après 2 passes : …`).
1099
- - **A costly choice is a question, not a hypothesis.** A rule marked `hypothese` whose `si_faux` involves a data model or storage, a public or shared API contract or an already published route,
1100
- a destructive or irreversible action, or a contradiction with a sourced rule, puts the lot in `question` (the program recognises these by keywords as a safety net behind the agent's own
1101
- judgement; a keyword right after a negation — « rien d'irréversible », « sans suppression », « pas de paiement » — does not count); `--resume --answer <lot> "…"` gives the answer to the resumed `spec` session. Every other hypothesis (wording, order of fields, error state, spacing) stays written in the sheet.
1102
- The agent can also ask a question itself in its report.
1103
- - **Under the threshold**: if the session finds that the lot changes no screen, it gives the reason in one line (`sousSeuil`), writes no mockup, and the lot goes on to `implement` as before; the reason is
1104
- kept as a warning of the lot. On resume the spec step is skipped only when a sheet of the lot (`ecran.lot` is the lot) already passes the program's checks (`cadence spec check`, mockup, captures) and carries no costly hypothesis; otherwise the spec is played again. The sheet path in the report is relative to the repository (an absolute or `./` path is normalised, a path outside the repository is a finding).
1105
-
1106
- ### The spec step of a lot with `spec: true`
1107
-
1108
- A lot that changes a behaviour but has no screen (`raf add … --spec`, or `raf spec <id> on`, see [raf](#raf); L188, design: [docs/superpowers/specs/2026-10-11-etape-spec-back-design.md](docs/superpowers/specs/2026-10-11-etape-spec-back-design.md)) is specified **before** it is coded, on a sheet, like a screen:
1109
- `precheck → spec → fiche → accept → implement → review` (`fiche` unless `orchestrate.fiche: false`; `accept` only when the project declares `orchestrate.accept`). A lot with neither `screen:` nor `spec: true` keeps its path. `--dry-run` lists
1110
- `spec (sonnet)`, `fiche (sonnet)` and `accept (sonnet)` with their command lines and briefs `templates/orchestrate/spec-back.md`, `fiche.md` and `accept-back.md`; the lot's budget gets a 450 k allocation on top of its estimate-derived budget (the back chain measured on L120: 388 k).
1111
-
1112
- - **The spec session**: Sonnet, the `spec-writer` agent, no Playwright. It reads the lot, the project's CLAUDE.md and the code and tests of the modules the lot touches (a rule already coded is a source, cited `file:line`), then writes only
1113
- `<orchestrate.spec.dir>/<sujet>/fiche.yaml` in the format of [`templates/fiche-back.yaml`](templates/fiche-back.yaml) (`sujet`, `cas`, `regles`, `contrats`, `donnees`; no mockup, no capture, no state) and commits it with a message that cites the lot.
1114
- The same rules as for a screen: a rule is `sourcee` or `hypothese` with its `si_faux`, and a hypothesis that hides a costly choice is a question (a stored model, a published contract or route, an irreversible action, a contradiction with a sourced or coded rule — the program's keyword safety net applies as is).
1115
- An optional field added to an existing response is not a break when a rule of the sheet proves the old response unchanged. A lot that changes no behaviour is reported under the threshold (`sousSeuil`, warning `étape spec : sous le seuil, aucune fiche — …`) and follows the usual path.
1116
- - **What the program checks**: the sheet passes `cadence spec check`, has a `sujet` block, is the lot's (`sujet.lot`), sits in the folder named by `sujet.id`, and is committed with a clean tree. Anything missing goes back to **one second `spec` session**; still invalid, the lot is handed back (`spec non conforme après 2 passes : …`).
1117
- On a resume the step is skipped when a sheet of the lot (`sujet.lot`) already passes these checks and has no costly hypothesis.
1118
- - **The sheet review** (`fiche`): a fresh session of the `code-reviewer` agent in **sheet mode** (Sonnet, read-only, no Playwright; `effort.fiche`, default `medium`). It reads the sheet, then the code, tests and sources of the perimeter, and checks (1) every `sourcee` rule cites a source that exists (it opens the file:line) and no `hypothese` hides a costly choice,
1119
- (2) no contradiction with a rule already coded or an existing contract, (3) every contract has its empty / absent / expired / oversized / duplicate inputs and the failures of its external sources covered by a rule or a motivated `sans_alternative` / `sans_erreur`, (4) every rule has a planned test whose name says what could fail.
1120
- Findings are ranked blocking / major / minor, each with its measurement. A blocking or major one sends the sheet back for a **second `spec` session** (its brief carries the findings), then the sheet is reviewed again; still not conforming, the lot is handed back with what remains (`fiche non conforme après 2 revues : …`).
1121
- The `spec` session has **one budget of two sessions**, shared by the program's check and the reviews: if the check already used the second one, a non-conforming review hands the lot back at once (`fiche non conforme et la spec a déjà joué 2 sessions`), with no second review. A question to the lead (the session's own, or a costly hypothesis) and its answer open a new budget.
1122
- Minor findings become notes of the lot (`Revue de la fiche — mineur : …`, one per finding, committed with the plan; on a read-only plan they become `[note à poser]` proposals), not lines of the final table. This verdict is **not** `raf review`: the review of the code keeps that role. `orchestrate.fiche: false` skips the step.
1123
- - **A lot with a screen and a behaviour**: it is a lot with `screen:` whose **sheet** has a back part (a `sujet` block next to `ecran`, same folder), never a `spec: true` beside `screen: page` (`raf spec <id> on` only says so on such a lot and changes nothing; on `component` or `journey`, which follow the ordinary path since L209, `spec: true` makes a back-end spec lot, ordinary budget plus 450 k). The program reads the sheet, not the plan: as soon as the sheet has a `sujet` block (the contracts), the `code-reviewer` also reviews it,
1124
- the chain is `spec → fiche → maquette → accept`, and the findings of the two reviews go together to a single second `spec` session. The brief of the spec of a lot with a screen offers the back part (and joins its template) without making it compulsory; a lot whose sheet has no `sujet` keeps its chain, without `fiche`. `--dry-run` cannot list `fiche` for a lot with a screen (the sheet does not exist yet).
1125
- - **Acceptance and code**: `accept` is the screen's step, with a back brief (`accept-back.md`: cases call the contracts of `contrats[]` as close as possible, external sources simulated from `entree`; no `data-testid`). `implement`, `fix` and `review` receive the sheet (cases, rules, contracts) as for a screen, without mockup; the review also checks that each contract is held (output, exact codes and messages of the errors).
1126
- - **Hypotheses**: a lot without a screen has no News entry. Once the sheet is final — after its review and the second `spec` session if there was one, just before `accept` or `implement`, and also for a sheet already present on a resume — the program writes **one note of the lot per hypothesis** (`Hypothèses (fiche <sujet>) — R3 : … ; si faux : …`, not duplicated on a resume) and commits it with the plan; on a plan kept by another tool the lines become proposals (`[note à poser] …`). The end-of-wave table lists them under each lot (`hypothèse de la fiche : …`) for the lead to relay.
1127
-
1128
- ### The mockup review: before any code
1129
-
1130
- After a valid spec, a lot with a sheet gets a UX review of its mockup, `maquette` (L172), unless `cadence.yaml` sets `orchestrate.maquette: false` (default `true`): `precheck → spec → maquette → accept → implement → review → ux`.
1131
- `--dry-run` lists the step (`maquette (opus)`, the `review.full` model), its `ux-reviewer` command line and its brief `templates/orchestrate/maquette.md`.
1132
-
1133
- - **The session**: the `ux-reviewer` agent in **mockup mode**, read-only, Playwright loaded, a fresh session. It opens `maquette.html` as a local file (no application, no server), looks at the captures
1134
- `maquette-<état>-<largeur>.png` (1440 and 390 px) of every state, reads `fiche.yaml`, and reports findings ranked blocking / major / minor, each grounded in a rule (Nielsen, WCAG 2.2 AA) or a measurement. On top of
1135
- that it checks the **consistency mockup ↔ sheet**: every field of the sheet in the mockup with its `data-testid`, every rule message visible, word for word, in the state its `etat` names, every state with its
1136
- `#etat-<état>` section, the `masque` of an empty-state rule really hiding the fields it names. `effort.maquette` (default `high`) sets its effort level. Its verdict is **not** `raf ux`: the conformity pass after the code keeps that role.
1137
- - **The loop, two reviews at most.** A blocking or major finding sends the mockup back to the spec writer: a **second `spec` session** (fresh, its brief carries the findings), then the mockup is reviewed again. If the second review is
1138
- still not conforming, the lot is handed back with what remains (`maquette non conforme après 2 revues : …`) and nothing is implemented. The same budget of two `spec` sessions applies as for the sheet review: if the program's check already used the second one, a non-conforming review hands the lot back at once (`maquette non conforme et la spec a déjà joué 2 sessions`); an answer to a question of the lead opens a new budget. A conforming review goes on to `accept` (or `implement`). On a resume, a **sealed** `acceptation.json` (its `empreintes` set by the program), still red or already green (`vert: true`), proves the review was done and is not replayed; an unsealed one proves nothing. A `maquette` step with no sheet warns (`revue de la maquette ignorée : aucune fiche …`) and goes to `implement`.
1139
- - **Sample data** (L181): the spec brief asks the writer to compute the weekday of every date of the mockup (not to write it from memory), and the review recomputes them. A wrong sample datum (weekday, date, figure that does not add up) is a **minor** finding, never a major one, so it cannot by itself send the mockup back for a second review.
1140
- - **Minor findings** are not sent back: they are kept as proposals of the lot (`[mineur maquette] …`), like the minors of the other reviews. A report whose totals announce a major finding is not taken for conforming.
1141
- - **On resume**, a sheet already committed is reviewed before the next step, unless the lot already has a sealed, conforming `acceptation.json` (it can only have been written after the review), or already has code committed after the sheet (a commit citing the lot that touches a file outside the sheet's folder: `implement` only started once the review was done), including without `orchestrate.accept`.
1142
-
1143
- ### The accept step: red acceptance tests and their lock
1144
-
1145
- After a valid spec, and only when `cadence.yaml` declares `orchestrate.accept` (the project's **dedicated command**, or a list of them, which run *only* the acceptance tests of one lot), a lot with a sheet gets one more
1146
- session, `accept` (L173): `precheck → spec → maquette → accept → implement → review → ux`. Without `orchestrate.accept` the step is skipped, with a warning on the lot (`étape accept ignorée …`), and the lot goes from `spec` to `implement` as before.
1147
- `--dry-run` lists the step (`accept (sonnet)`) and its brief `templates/orchestrate/accept.md`.
1148
-
1149
- - **The session**: Sonnet, the `acceptance-writer` agent, a session other than the implementer's. Its brief gives it the sheet, the mockup and how to run the project's tests (the dedicated command, and the runner template below) — not the lot's objective, notes or
1150
- sub-tasks, and no implementation. It writes one or more cases per rule, **named as the sheet plans them** (`regles[].tests`, e.g. `R3 — erreur : message et bouton Réessayer`), selectors = the `data-testid` of the fields, API calls simulated from `actions[]`,
1151
- and commits them **red** in one commit (`test(L7): acceptation R1–Rn (rouge)`) with `<spec.dir>/<écran>/acceptation-<lot>.json` (**one file per lot**: two lots on the same screen never share a lock or a file; the id is reduced to `A-Za-z0-9._-`, so `app/L7` gives `acceptation-app-L7.json`; an older `acceptation.json` is still read, by its `lot` key) — `{ "lot", "fiche", "fichiers": [...], "vert": false }` — and the runner configuration. A second lot's tests that rewrite the first lot's test files hand the lot back, with the file named (the lock below). `effort.accept` (default `medium`) sets its effort.
1152
- - **One command or a list of runners** (L189). `orchestrate.accept` is a string or a **list** of commands, for a project with a front and a back runner (Vitest in `frontend/`, Jest in `backend/`):
1153
-
1154
- ```yaml
1155
- orchestrate:
1156
- accept:
1157
- - cd frontend && npx vitest run -c vitest.acceptation.config.ts
1158
- - cd backend && npx jest -c jest.acceptation.config.js
1159
- ```
1160
-
1161
- The program runs **each** command from the repository root with `CADENCE_ACCEPTATION` (and `{acceptation}` substituted) and `CADENCE_ACCEPTATION_ROOT`, the command's **root**: the folder of its leading `cd <folder> &&`, else the repository (`.`). A command takes **only the files listed under its root**: `acceptationOnly()` of the runner template returns them relative to that root, where the runner starts. A command with **no listed file under its root is not run**; a listed file under **no root** is a deviation of the step (`fichiers listés … sous la racine d'aucune commande de orchestrate.accept`, one second pass, then `accept non conforme après 2 passes`). **Red check**: every command that is run must fail (the deviation names the one that passes); **green**: `vert: true` is set only when all of them pass, and a lot sent back to `fix` is told which commands are still red. A string keeps its behaviour: **it ignores its `cd` for the root** — the whole repository is its root and `CADENCE_ACCEPTATION_ROOT` is `.`, so `acceptationOnly()` returns paths relative to the repository; only a list derives a root from each command's leading `cd`. One lot per app wires its back runner (dedicated config, `exclude` in the ordinary one, a line in `cadence.yaml`): that is the app's plan, not cadence's.
1162
-
1163
- **Moving from a string to a list.** The two forms are not interchangeable: a string keeps the repository as its root, a list (even of **one** element) derives each root from the command's leading `cd <folder> &&` and sets `CADENCE_ACCEPTATION_ROOT` to it. The brief of the step and the cycle decide by the same test (a YAML list or not), so they always agree. To switch: (1) write the entries as `cd <folder> && <runner>`; (2) make the runner's config resolve from its own root (its `include` and `rootDir` relative to `<folder>`, since the runner starts there and `acceptationOnly()` returns the paths relative to that root); (3) **copy the runner template again** into the project (`templates/acceptation-runner.mjs` reads `CADENCE_ACCEPTATION_ROOT`; an older copy lists the repository-relative paths and matches nothing from a folder). A project that has not migrated stays on its string, unchanged.
1164
- - **Out of the ordinary test run and the CI while the lot is not green.** Red tests committed would fail `orchestrate.test` (after every `implement`/`fix`) and the project's CI (which `cadence deliver` waits for), for lots that are not theirs. So the
1165
- runner configuration shared by `orchestrate.test` and the CI excludes the files listed in every acceptation file (`acceptation-<lot>.json`) whose `vert` is `false` — and, always, red or green, those of an acceptation with `"e2e": true` (a browser test needing the build, L203: `acceptationE2e()` lists them for the e2e suite run after the build) — and only the dedicated command runs them. `templates/acceptation-runner.mjs` is the helper the session copies
1166
- into the project: `acceptationIgnored()` (Vitest `exclude`; `acceptationIgnored(root, dir, 'regex')` for Jest `testPathIgnorePatterns`) and `acceptationOnly()` (the `include` of the dedicated config, which reads the environment variable `CADENCE_ACCEPTATION`:
1167
- the program sets it to the absolute path of the lot's acceptation file when it runs `orchestrate.accept`, and also replaces `{acceptation}` in the command with that path — quote it if the path may hold spaces). If the CI or the test command do not use that config
1168
- (paths hard-coded), the session must not commit red tests and puts the question in its report.
1169
- **Run by hand, outside a wave**, the dedicated command fails: `acceptationOnly()` requires `CADENCE_ACCEPTATION`, which only the program sets. Set it yourself, to the path of the lot's acceptation file:
1170
- `CADENCE_ACCEPTATION=<path of the acceptation file> <command>` (with a `{acceptation}` in the command, write the path there too). For a command of an `orchestrate.accept` **list**, also set its root, which `acceptationOnly()` uses to return the files relative to where the runner starts: `CADENCE_ACCEPTATION=<path of the acceptation file> CADENCE_ACCEPTATION_ROOT=<root of the command> <command>` (the folder of its leading `cd <folder> &&`; without the variable the root is the repository `.`, and the paths are relative to it, which no runner started in a folder will match). A single string needs no root. Once the lot's tests are green (`vert: true`), they join the ordinary runner and the CI, so the usual test command runs them without that variable — **except an `"e2e": true` acceptation**, which never joins them: wire its `acceptationE2e()` files into the e2e suite run after the build (`npm run build && npx playwright test`). The dedicated `orchestrate.accept` command still runs those files for the red check and the return to green, so it must build and start what the browser needs.
1171
- - **What the program checks** afterwards, with no model: `acceptation.json` is the lot's (`lot`, `fiche`, listed files committed, `vert: false`), every test name planned in the sheet appears in the listed files, **each listed test file is new** (absent from the head before the first `accept` session: tests written into an existing test file would leave the ordinary run and the CI while the lot is red, so the check refuses `existait avant la session accept`), the other lots' acceptation tests are untouched, the dedicated command
1172
- **fails** (a test green before the code proves nothing), `orchestrate.test` stays **green** with the red tests committed (proof that they are excluded; without `orchestrate.test` nothing proves it, and the step passes with a lot warning only), and the tree is clean. Anything missing goes to **one second `accept` session**; still not valid, the lot is handed back
1173
- (`accept non conforme après 2 passes : …`). A question in the report puts the lot in `question`, like the other steps. The check that each listed test *individually* fails is not made: only the dedicated command's exit code is (it fails if any of them fails).
1174
- - **The seal.** Once the checks pass, **the program** (not the session) adds to `acceptation.json` the git fingerprint of each listed file (`empreintes`) and commits it (`test(L7): acceptation scellée …`): that commit is the **red commit**. Its sha is written nowhere in the file (a session could rewrite it): it is
1175
- the latest commit in git history that changed the file otherwise than by its `vert` key, and the wave keeps it in the lot's state (`accept.rouge`) too.
1176
- - **The lock, for every session of the repository.** At the end of each `implement` and `fix` the cycle compares, for the acceptation files of **all** lots found under `orchestrate.spec.dir` (a repository has one queue: the next lot runs on the same branch), the listed test files with the red commit
1177
- (`git diff <red commit> -- <files>`, empty expected, working tree included, and the fingerprints), and each `acceptation.json` with its version before the session. The only difference tolerated on `acceptation.json` is the `vert` key, and only **the cycle** sets it: a session that changes it, rewrites the
1178
- file, deletes it, creates one outside the accept step, or modifies a locked test hands the lot back (`verrou des tests d'acceptation : …`) — a useful change is a question to the lead or a review finding, never a silent gesture. The lock does not depend on `orchestrate.accept` being declared. **Exception — a delivered lot (`vert: true`):** a later lot that makes the rule evolve may modify the listed tests of that acceptation when **the sheet of the screen changes in the commits of the same lot** (compared with the head before the lot's first session, working tree included, or in a commit that cites the lot, even made before the wave): the sheet is what evolves the rules, and tests and sheet change together. The exception never covers the acceptation of the lot in progress (its `vert: true` is set right after the implement): that one keeps the whole lock. Without a sheet change the difference is still a deviation — except one narrow case (L184): a listed file whose content is identical to what it was at the head before the lot's first **work** commit (commits found by the plan's refs) is not a deviation, the lot having taken its own addition back (a lot handed back for writing in a locked file and resumed in a new wave, which then removes that addition). This applies to every acceptation, `vert: false` included, but only to a file that **exists** at that head and now: a file absent at that head (e.g. created by the lot's own red commit) or deleted since is never exempted, so deleting a locked test stays a deviation; nor when the sheet of the screen has changed since that head (an evolution admitted for another lot: going back to the old content would cancel it, not the lot's addition); apart from that, with `vert: false` the whole lock applies, sheet change or not; `acceptation.json` itself stays locked (only its `vert` key moves).
1179
- - **A sealed test that contradicts the sheet: `cadence spec reseal` (L200).** The lock is total for the lot in progress (the delivered-lot exception above never covers it; for a delivered lot, see the end of this item), so a test that is wrong against its sheet — e.g. the regex `/\d{1,2} /` of an acceptance test rejecting « T3 2027 annoncée », which the sheet allows — would leave the lot handed back with no way out. The way out is the lead's, outside any wave: correct the test in the working tree (the sheet is left as it is: unchanged since the red commit, or amended by commits of the lot and committed, see « A sheet amended in the same lot » below), then `cadence spec reseal <lot> <test> --reason "why the test contradicts the sheet"`. The program checks that the lot is in progress (`doing`; for a delivered lot, see the end of this item), that the test is listed in the lot's sealed `acceptation-<lot>.json`, that the sheet is unchanged since the red commit or amended by commits of the lot (below), that **no other listed test** changed (the lock keeps everything else) and that the test did change; it then redoes the fingerprint of that one test, appends `{ test, raison, date, avant, apres }` (plus `ficheAmendee` for an amended sheet, below) to the `resceaux` key of `acceptation.json`, commits the test and `acceptation.json` (`test(L35): acceptation rescellée, <test> : <reason>`, the new red commit) and the lot's note with the reason (`plan: …`). It is refused during an orchestrated wave (`CADENCE_ORCHESTRATED`, or the repository's wave lock while one of its lots still works, like `cadence deliver`), with no `--reason`, and on a lot that is neither in progress nor delivered with a green acceptation. Then, depending on how the lot stands: a lot waiting on a `question` is continued by `--resume --answer` (the cycle moves its red-commit anchor to the resealed commit, a rewrite of `acceptation.json` without a `resceaux` entry still being a violation); a lot handed back with `verrou des tests d'acceptation` is finished for `--resume`, so it is replayed by a new wave `cadence orchestrate <project>:<lot>`, which starts again from the red commit. A session never does it, three guards: (1) `cadence spec reseal` is in the sessions' `--disallowedTools` (like `raf done`/`review`/`ux`); (2) a resealed commit is adopted as the new red commit only if it belongs to **no session of the wave** (commits listed in each `steps[].commits`, or in `headBefore..headAfter` of any session of any lot of the wave) — otherwise it stays a violation of the lock; (3) the lock is checked at the end of **every** work session, a session that ends with a question included, so a session that replaces the test, forges the fingerprint, commits and asks a question is handed back with `verrou des tests d'acceptation` without waiting for the lead's `--answer`. The reseal is atomic: if the commit fails (a `pre-commit` hook exiting 1, say) `acceptation.json` and the test are put back as they were before the command (the lead's correction in the working tree is kept, nothing staged) and a readable error says nothing was resealed. With a plan kept read-only by another tool, the reseal itself succeeds but the plan is not touched: the note of the lot is left to the lead (the reason stays in `acceptation.json` and in the commit message; the command only says a note is to be added). With a plan outside the repository or untracked, the note **is** written in the plan file but not committed (the command says `plan à commiter`); do not add it a second time. If the commit of the note is refused (a hook), the reseal stays done: the error gives its commit and says the note, already written, is to be committed by hand. **A sheet amended in the same lot (L217):** a sheet amended since the red commit is accepted, for a lot in progress as for a delivered one (the sheet is then not put back to its red state to be restored afterwards). When its net difference with the red commit (`git diff <red> -- <sheet>`) is not empty, the command reads `git log <red>..HEAD -- <sheet>`: every commit that touched it must cite the lot (read like `raf commits`, i.e. `citedRefs`: the scope of the subject first, the body otherwise; for a delivered lot, the lot or the `--porteur`), and the sheet must be committed. The fingerprint of the test is then redone against the current sheet and the `resceaux` entry gains the optional key `ficheAmendee` (short sha of the last commit that touched the sheet); the lot's note and the commit message say `fiche amendée en <sha>` instead of `fiche inchangée`. Still refused, with nothing written: a sheet modified by a commit that cites neither the lot nor (delivered lot) the `--porteur` (`la fiche … a été modifiée depuis le commit rouge … par un commit qui ne cite pas <lot> : <sha>`), a sheet deleted or renamed since the red commit, a sheet with uncommitted changes (working tree or index: `a des modifications non commitées : la commiter (en citant <lot>) avant le rescellé`), as before any other listed test touched, a test left unchanged, an orchestrated wave and a missing `--reason`; the atomicity is unchanged. A sheet amended then brought back to the identical content is treated as unchanged, whoever committed in between. After the reseal the new red commit is the reseal commit, so a second reseal sees the sheet as unchanged. **A delivered lot (L212):** a lot whose acceptation is green (`vert: true`) and whose sealed test turns out unstable while the contract does not change (seen: a tab read as « blank » in CI) had no way out but the « sheet modified in the same lot » exception. `cadence spec reseal <lot> <test> --reason "why" --porteur <lot in progress>` now covers it, with the same guards (the sheet unchanged since the red commit, or amended as above by commits that cite the delivered lot or the carrier; no other listed test touched, no wave, `--reason`) and three differences: `--porteur` is required and names the lot in progress that carries the fix (refused if it is the delivered lot itself, unknown or not `doing`); the commit cites the **carrier** (`test(L113): acceptation rescellée du lot livré L47, <test> : <reason>`) and so does the plan note and its commit, the delivered lot being closed; the dated `resceaux` entry also records `porteur`, and `vert` stays `true`. `--porteur` on a lot in progress, or `spec reseal` on a delivered lot without `--porteur`, is refused.
1180
- - **Back to green, set by the cycle.** At the end of each `implement`/`fix`, once the session reports green, the cycle runs the dedicated command. Red: the findings go to a `fix` pass (the tests are made green without editing the locked files). Green: the cycle sets `vert: true` and commits it (`chore(L7): acceptation verte …`;
1181
- `commitPlan` may commit the `acceptation.json` files, otherwise the dirty tree would stop the lot). The files then join `orchestrate.test` and the CI, which keep the lot green from then on (an `"e2e": true` acceptation excepted: its files stay in the e2e suite after the build); the following lot, running with `vert: true`, is compared with the head before its own work, not with the red commit, only once the sheet has changed between the red commit and that head: after an admitted evolution (tests changed along with the sheet) it sees no difference either; only a test changed *during* that lot without the sheet is a deviation.
1182
- - **When the lot is handed back** (question, budget, passes exhausted) its tests stay red but excluded from the project's runner and CI: the next lots and `cadence deliver` do not see them; `--resume` continues with the same lock. On resume, the step is skipped when the lot already has a valid committed **and sealed** `acceptation.json` (its `empreintes` set by the program); a conforming file without them proves nothing and `accept` runs again. The fingerprints are keyed by normalised path, so a listed `./test/x.ts` is recognised like `test/x.ts`, on resume and by the lock (which compares it to its fingerprint).
1183
-
1184
- ### The downstream briefs: sheet, rule → test → green, conformity, Hypothèses
1185
-
1186
- Once the sheet exists, the steps after it all read it (L174, `src/orchestrate/aval.ts`, the `{{fiche}}` marker of the templates `implement.md`, `fix.md`, `fix-minors.md`, `review.md`, `review-small.md` and `ux.md`). A lot without a sheet (no `screen:`, or under the threshold) keeps exactly the briefs it had.
1187
-
1188
- - **`implement` and `fix`** get the path of the sheet and of the mockup, and **one line per rule** (`- R3 [erreur, hypothese] statement. Message: « … ». Tests: « R3 — … »`). The session makes the acceptance tests green **without modifying them** (they are named when the `accept` step ran; the lock above hands the lot back otherwise), **the tests it writes in addition cite the rule** in their name (`R2 — …`), and it **does not invent a rule**: a case the sheet does not cover is written in the report (`nonVerifie` or `choix`), not coded. In a `fix` the sheet stays the reference: a finding that contradicts it is contested under `choix`.
1189
- - **A self-check list before handing back (L222).** Both `implement.md` and `fix.md` carry the same fixed list (plain text, no marker, no new variable), taken from the recurring findings of the reviews of 2026-10-15: the usage documentation (README and architecture documentation) updated in the same commits as the behaviour; one test named `Rn — …` per rule of the sheet (one per business rule of the lot when there is no sheet); no dead code and no test that cannot fail (an assertion always true, or a test that does not exercise its rule); for a modified screen, the capture due of each state at 1440 and 390 px with a relative file name (a lot with no modified screen owes none and says so); no false comment, docstring or message. The session goes through it point by point on its own commits before it reports, and a point it cannot hold is written under `choix` or `nonVerifie` of its report: the report schema has no new field. In `fix` the list covers only what the session changed, and "fix them, and nothing else" stays. The other briefs (`fix-minors`, `spec`, `spec-back`, `accept`, `review-small`…) are unchanged. The before/after measure — correction passes per lot, replayed with `cadence corrections` (`--until <bound>` for the "before", `--since <bound>` for the "after", the bound being the time of the `npm run service` that puts the list in service) — is in [docs/mesures/autocontrole-avant-revue-2026-10-15.md](docs/mesures/autocontrole-avant-revue-2026-10-15.md).
1190
- - **The code review (`review`, `review-small`) checks rule → test → green** (`règle → test → vert`). For each `Rn` of the sheet the reviewer checks that its test exists (named after the rule), that it is green, and that the behaviour is in the diff; when an acceptance file is named, that each listed test is unchanged since the red commit (`git diff <red commit> -- <file>` empty; the brief gives the red commit). A rule without a test, an acceptance test modified since the red commit, a rule coded that is not in the sheet, and a test that is green without exercising its rule are **major** findings. The reviewer no longer rebuilds the cases from the code.
1191
- - **The UX pass becomes a conformity pass.** For a lot with a sheet, the `ux` session (the `ux-reviewer` agent, **conformity mode**) no longer audits the whole screen: it compares the running app to the mockup and to its captures `maquette-<état>-<largeur>.png`, state by state, at 1440 and 390 px, and reports the differences of rendering and ergonomics the tests do not cover, each tied to a rule or a measure. The rules of the sheet are the tests' job. Its verdict feeds `raf ux`, which stays the lead's, decided on the captures. A small visible lot (single `review-small` pass) gets the conformity instruction in that pass.
1192
- - **The `## Hypothèses` section of the news entry.** The `implement` brief asks for an entry that ends with `## Hypothèses`: one line `- Rn — …` per rule whose status is `hypothese`, with what changes if it is wrong (its `si_faux`) — it is what the lead shows for the `raf ux` decision, the choices nobody validated.
1193
-
1194
- ```markdown
1195
- ## Hypothèses
1196
- - R3 — erreur de chargement : message « Impossible de charger le prochain match. » et bouton Réessayer. À confirmer ; sinon seul ce bloc change.
1197
- ```
1198
-
1199
- **`cadence news check`** (not `build`) flags a lot whose sheet has hypotheses — found under `orchestrate.spec.dir` (default `docs/specs`), by `ecran.lot` — when none of its entries has the section, or when the section lacks a `- Rn` line for one of the rules (`L7 : la fiche a des hypothèses (R3, R4) mais l'entrée … n'a pas la section « ## Hypothèses »`). A lot without hypothesis needs no section.
1200
-
1201
- **Production access of the sessions** (L168): a session of a wave (`implement`, `fix`, `precheck`, `spec`, `review`, `ux`) is
1202
- started with no *default* access to the production cluster (a guard against the ordinary path, not a barrier). What a session inherits was measured on the dev machine: `KUBECONFIG` (pointing at
1203
- `~/.kube/config`, the production cluster), the `kubectl` of the `PATH` (a link to `k3s`, which without `KUBECONFIG`
1204
- falls back on `/etc/rancher/k3s/k3s.yaml`) and the cloud and GitHub tokens of the environment. `cadence orchestrate` now
1205
- starts every session with `KUBECONFIG=/dev/null` (`kubectl get pods` answers « connection refused » on `localhost:8080`)
1206
- and without `KUBERNETES_SERVICE_HOST`, `KUBERNETES_SERVICE_PORT`, `KUBE_TOKEN`, `ARGOCD_SERVER`, `ARGOCD_AUTH_TOKEN`, `ARGOCD_OPTS`, `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_API_KEY`, `CF_API_TOKEN`, `CF_API_KEY`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` (exactly these names: `AWS_PROFILE` or `AWS_WEB_IDENTITY_TOKEN_FILE` are kept), `GOOGLE_APPLICATION_CREDENTIALS`,
1207
- `AZURE_CLIENT_SECRET`, `GH_TOKEN`, `GITHUB_TOKEN`, `GH_ENTERPRISE_TOKEN` (idea taken from the sandbox of docker/docker-agent,
1208
- closed by default). `HOME` is left alone, so the credential files `gh` keeps under `~/.config` stay readable (a session
1209
- can neither push nor deliver anyway) and so does `~/.kube/config`: a session that deliberately runs
1210
- `kubectl --kubeconfig ~/.kube/config …` or `KUBECONFIG=~/.kube/config kubectl …` still reaches the cluster. The restriction
1211
- stops the accidental access, not a deliberate workaround; making the file unreadable needs a filesystem sandbox
1212
- (a mount namespace, or the file sandbox of Claude Code), which cadence does not set up. `cadence deliver` and `cadence verify` are the lead's and keep their access; so do
1213
- the project's own `test`, `build` and `ux.command`, which the program launches. A lot that really needs the cluster is run in a
1214
- project with `orchestrate.prodAccess: true`: the restriction is lifted for all its sessions (default `false`).
1215
-
1216
- `orchestrate.precheck: local` (L146) runs the pre-check on the local model of the dev machine instead of Sonnet: the
1217
- orchestrator launches `claude-local` with the same brief, the `precheck-reader` prompt and tools, no quota used and nothing counted in the
1218
- lot's budget (the step shows `local` as its model). `claude-local` is **not shipped with cadence**: it is a personal wrapper (a script on the `PATH`, `qwen3-coder:30b` by default) around
1219
- `claude` pointed at Ollama. `CADENCE_CLAUDE_LOCAL_BIN` names another binary, which must take the prompt as its **first argument**
1220
- and add itself `-p`, `--bare`, `--strict-mcp-config` and `--model` (cadence passes none of them), then accept `--output-format`,
1221
- `--json-schema`, `--tools`, `--session-id`, `--permission-mode`, `--add-dir` and `--disallowedTools`. A bare `claude` does not fit.
1222
- Without such a binary every local check fails and is replayed on Sonnet: keep `true`. Sonnet stays the safety net: a local session with no result (Ollama
1223
- down, timeout, no structured report) or an unreadable report is replayed on Sonnet with a warning, and a local `oui` — the
1224
- only answer that hands the lot back without an implementation — is confirmed by Sonnet before it counts. A local `partiel`
1225
- or `non` is taken as is. The default stays `true` (Sonnet): switch to `local` only after comparing verdicts and durations
1226
- against Sonnet on a few lots, and go back to `true` the first time the local model gets a `partiel` wrong. `--dry-run`
1227
- prints the step as `precheck (local)` with its `claude-local` arguments. Those arguments carry neither `--model` nor `--effort` nor `--agents`: `effort.precheck` applies only to the Sonnet
1228
- session (the fallback, or the confirmation of a local `oui`).
1229
-
1230
- The `precheck-reader` agent does not set `omitClaudeMd` (Claude Code 2.1.271), on purpose: measured on a project with a
1231
- 297-line `CLAUDE.md` (haiku, `--agents` + `--agent`, same prompt, with and without the flag), the session context is
1232
- identical (22 779 tokens, the project `CLAUDE.md` still answered when asked) — the flag only applies to an agent run as a
1233
- *subagent*, and cadence launches the step as the main agent of its own session. The same goes for `qa-reviewer`, which
1234
- is not launched by the wave: leave it as it is.
1235
-
1236
- **A lot that already has commits** (a spec commit, an interrupted wave, a lot committed by hand, a lot sent back after a
1237
- review) still starts with the implementation session: its brief tells it to read the lot, its notes and its open
1238
- sub-tasks, which carry the findings of any earlier review. If that session ends without a new commit on a lot that
1239
- already has commits, the wave goes on to the review (warning `implement sans nouveau commit`) instead of handing the
1240
- lot back; with no commit on the lot at all it is handed back as `implement sans commit`. The same goes for a
1241
- correction pass: a `fix` session that adds no commit to a lot that already has work commits goes on to a fresh review
1242
- (warning `fix sans nouveau commit : revue lancée sur les commits du lot`), the pass still counted — the cap of two
1243
- correction passes stays the guard against a loop; with no commit on the lot it is handed back as `fix sans commit`.
1244
-
1245
- **Compliant with minor findings**: the minors are not left for a follow-up lot. One **minors pass** runs
1246
- before concluding: a new Sonnet session with its own brief (`fix-minors.md`) fixes the minors that are right
1247
- and lists in `choix`, with the reason, the ones it rejects (it never stops to ask); it does not count among the
1248
- two defect passes. A short code re-review follows (for a visible lot, the UX review comes after it), and
1249
- concludes even when it finds new minors, which are returned to you as proposals (no second minors pass).
1250
- When the pass makes no commit (every minor judged wrong) and HEAD has not moved since the compliant review,
1251
- the lot concludes on that original review (if HEAD moved, e.g. a resume after a cut-off session that had
1252
- committed, a short re-review of that commit runs instead): `ready`, verdict recorded with the sha that review read (`… + passe des mineurs sans commit`),
1253
- the untreated minors returned as proposals. For a visible, non-small lot with `orchestrate.ux` declared, the
1254
- final UX review plays first and the lot concludes on that same original review once the UX is compliant. Same when the
1255
- budget is exhausted right after a compliant review with minors: it concludes on that review instead of staying
1256
- suspended, except for such a visible lot, where the UX session needs budget and the wave stays `suspended`
1257
- (budget reached) like any other session.
1258
-
1259
- **Sub-tasks covered by commits (L82)**: when the review concludes compliant, the open sub-tasks of the lot that a work commit of the lot cites (`feat(L3/t2): …`, in the project or in one of the lot's neighbouring repositories; a commit that cites only the lot covers none) are closed in the same plan commit as the verdict, with the warning `sous-tâches closes : L3/t2 (sha)`, so `raf done` does not refuse the lot over finished work. A read-only plan is never written: each such sub-task is returned as a proposal `[sous-tâche clore] L3/t2 — couverte par <sha>`, for you to close with the project's tool. On a plan in a foreign format (read-only, `cadence.yaml` `plan:`), sub-task ids are read as the plan reads them: `B53/t4-ux1` covers `t4-ux1` only, not `t4`, and a list such as `feat(B53/t4-ux1,t5)` covers each of its sub-tasks. The lot id is not read inside another id (`fix(E-A2/t3,t4, A2/t1)` covers `A2/t1` only for lot `A2`), and a list may repeat the lot id (`fix(L1/t1, L1/t2,t3)` covers `t1`, `t2` and `t3`). A non-compliant review closes nothing.
1260
-
1261
- **Choices, not questions**: the author brief tells the session to decide minor interpretation questions itself and to list them under `choix` in its report; the reviewer receives that list to re-read, and the final table prints each one (`choix fait : …`). A session stops with a question only on a real blocker: a decision that changes the scope or the architecture or is costly to undo, AND that the plan, its notes and CLAUDE.md do not settle; everything else is a choice.
1262
-
1263
- **Frozen copy of the tool (L61)**: at the real start of a wave (not with `--dry-run` or `--status`), cadence copies the
1264
- parts of its package that run the wave (`bin/`, `dist/`, `templates/`, `agents/`, `skills/`, `package.json`; `node_modules`
1265
- is linked, not copied) into `.cadence/runs/<wave>/tool/` and relaunches the orchestrate process from that copy (the
1266
- parent process waits, relays the signals and exits with the same code or signal). Code, brief templates and agents
1267
- therefore come from the snapshot: a wave can orchestrate cadence itself, and a session of the wave may edit
1268
- `templates/` or rebuild `dist/` without touching the wave running. `--resume` relaunches from the snapshot of the
1269
- resumed wave, not from the current `dist/`; a wave without a snapshot (started before L61) resumes with the current
1270
- package and a warning. One more exception (L223): a wave whose snapshot predates L223 (its `dist/orchestrate/command.js`
1271
- has no `lotBudgets`) resumed with `--budget <project>:<lot>=+N` runs on the current `dist/` and templates, with a warning
1272
- on the error output, because that snapshot would refuse the option as an invalid budget; the sessions of such a resume still
1273
- get `<wave>/tool/bin` first in their `PATH` (see below), so the `raf` and `cadence` they call are the snapshot's. The copy is removed with the wave folder. The wave's pid in `--status` is the relaunched process.
1274
- The sessions of the wave also get `.cadence/runs/<wave>/tool/bin` (the `raf` and `cadence` entries of the snapshot) **first in their
1275
- `PATH`**, ahead of the per-project Node below and of the usual `PATH`: the `raf` a reviewer runs (`raf commits`…) is the
1276
- snapshot's, not the installed one. A wave without a snapshot leaves the `PATH` as it is.
1277
-
1278
- **The code in service (L195)**: on cadence's own repository, `bin/raf.js` and `bin/cadence.js` load `dist/`, so the `raf` and `cadence` of your
1279
- `PATH` used to be whatever the working repository last built. The snapshot protects the wave running, not that installed tool, and a wave's
1280
- sessions run `npm run build`, `npm install`… The code in service now lives in a **separate copy**: a detached git worktree built there,
1281
- `<root>/releases/<sha12>`, with `<root>/service` pointing at the current one (`<root>` is `~/.local/share/cadence`), and `~/.local/bin/raf` and
1282
- `~/.local/bin/cadence` linked to `<root>/service/bin/`. Nothing in the working repository (`npm run build`, `prepare`, `npm install`, `npm ci`,
1283
- an uncommitted `src/` file) touches it. `npm run build` only type-checks, which is what a session of a wave runs, with the tests, before its report.
1284
-
1285
- The lead updates the copy after the review, once the lot is accepted (`raf done`):
1286
-
1287
- ```
1288
- npm run service -- <sha> # [--root dir] [--bin-dir dir] [--repo dir]
1289
- ```
1290
-
1291
- The script refuses a sha that is not an ancestor of HEAD, and a sha whose commits (since the one in service; only the sha itself the first time)
1292
- cite a lot whose status is not `done` (`plan:` commits are ignored); a lot `dropped` is let through when every one of its commits is undone by a `git revert` present under the sha (L207), and still blocks when one is not. It builds the new copy aside (`npm ci --ignore-scripts`, then `tsc`) and switches
1293
- `service` and the two links only once the build succeeded (atomic renames): a failed install changes nothing. The previous copy is kept for a
1294
- rollback, the older ones are removed only once they are more than 7 days old, counted from the day they left service (a wave's snapshot links its `node_modules` to the copy that launched it). The first run is also what installs the links in `~/.local/bin`. It prints the fingerprint of the copy
1295
- (bin, dist, package.json).
1296
-
1297
- ### A lot whose work is in a neighbouring repository
1298
-
1299
- Some lots are done in another repository that is not a cadence project (B64 of `maritime-atlas` lives in
1300
- `aetherwx-gitops`, a Helm chart repository). The lot declares it with the **lot key `repos:`** — a list of paths relative to
1301
- the project's repository, one path being enough as a string. There is no project-wide key: a neighbour belongs to a lot.
1302
-
1303
- ```yaml
1304
- lots:
1305
- - id: B64
1306
- title: …
1307
- repos: [../aetherwx-gitops]
1308
- ```
1309
-
1310
- **Which commits count in a neighbour.** Nothing is inferred from the neighbour or from the project's name. A commit counts
1311
- when it cites the lot's id (as in the project: `feat(B64): …`, `maritime: … (B64)`) **and** is dated within the lot's period,
1312
- `started` to `finished` (an open end is open). That is all, whatever the prefix or scope of the subject says and whatever
1313
- the commit mentions. When a neighbour is shared between projects whose lots share an id prefix (`developpeur-gitops` holds
1314
- the charts of `finance-tracker`, `warhammer40k` and `ol-companion`, all `L…`), the lot says so with an **explicit key**: an
1315
- entry of `repos:` can be an object, and the list can mix both forms.
1316
-
1317
- ```yaml
1318
- repos:
1319
- - ../aetherwx-gitops # id + period
1320
- - { path: ../developpeur-gitops, cite: ol-companion } # id + period + this string
1321
- ```
1322
-
1323
- With `cite`, a commit must also contain that string (a substring, case-insensitive, searched in the subject and the body);
1324
- a commit of another app that cites the same id is dropped from `raf commits`, from the briefs, from the sha a review
1325
- records and from the orchestrator's « already has work » test. Without `cite`, in a shared neighbour, the commits of the
1326
- other apps that cite the same id **do** count: that is the default, so declare `cite` for such a neighbour. `cite` applies
1327
- to the neighbour it is written on, not to the project's own repository. The commits of the lot in the neighbour must carry the
1328
- string too (the briefs say so); one that does not is not counted.
1329
-
1330
- A read-only plan declares it in `cadence.yaml` with `plan.fields.repos: <the file's key>` (a left-out field is read under
1331
- the name `repos`). The effect, for `cadence orchestrate`:
1332
-
1333
- - **Preconditions** (refused before acting, exit code 2, like the project's own repository): every listed path exists and
1334
- is inside a git repository, has no tracked file modified, has no `pre-push` hook of its own, and is not held by another
1335
- live orchestration. `--dry-run` applies the same refusals and prints `dépôts voisins : <paths>`. The project's own
1336
- repository and a duplicate listed twice are counted once.
1337
- - **Lock and guard**: the neighbour gets the repository lock (`orchestrate.lock`, so `cadence deliver` refuses it too, and no
1338
- other wave can hold it), the temporary `pre-push` hook, and the same after-session checks (push, tampered hook, a review
1339
- that changed it). Two lots that share a repository, as project or as neighbour, run one after the other, never two
1340
- sessions in one repository; `--status` lists the neighbours among the repositories a wave still holds (a repository released once all its lots are finished no longer appears).
1341
- - **Sessions**: every session gets each neighbour as `--add-dir`, and the briefs name them: the implementation and the
1342
- corrections must commit there too, with a message that cites the lot (`feat(B64): …`), and never push; the reviewers read
1343
- the commits that cite the lot in the project **and** in each neighbour (`raf commits <lot>` lists them all; in a neighbour only the commits that cite the lot and are dated within the lot's period count, and also contain the `cite:` string when the lot declares one for it — a `fix(B64): …` outside the period, or one without the `cite:` string, is not listed.) A lot whose
1344
- whole work is in the neighbour is not « without commit »; a commit there that does not cite the lot is a warning; a
1345
- neighbour left with a tracked file modified hands the lot back.
1346
- - **Verdict**: `raf review` as recorded by the orchestrator stores the sha read in the project and in each neighbour
1347
- (`review.repos`), and the verdict text ends with `dépôts relus : <path>@<sha>`. A commit made in a neighbour after the
1348
- review hands the lot back, like one in the project.
1349
- - **Out of scope**: `raf check` does not audit the neighbours, and their delivery (push, CI, deploy) stays with the lead.
1350
-
1351
- **What stays with you**: choosing the lots, the questions raised (`--resume --answer`), re-verifying
1352
- after the wave (`git log`, tests, `raf check`), `raf done`, **`raf ux`** (the orchestrator reports the UX
1353
- verdict and screenshots, it does not record it), the push and the deliveries, one project at a time.
1354
-
1355
- **Several waves at once**: the lock is per **repository** (a lock in the repository's shared state,
1356
- `orchestrate.lock`, which also makes `cadence deliver` refuse that repository), not per folder. A wave is
1357
- refused only when one of its repositories is held by a live wave (`<repo> : une orchestration y est déjà en
1358
- cours`; the lock of a dead process is detected and cleared); two waves on different repositories run side by
1359
- side, even when started from the same parent folder. A repository is released **as soon as every lot of the wave that
1360
- touches it is finished** (ready, handed back or failed): its lock and its `pre-push` guard are lifted while the wave goes
1361
- on elsewhere, so `cadence deliver` accepts it (a repository with a lot still to play, a question or a suspended lot stays
1362
- held until the wave ends). `cadence deliver` also reads the wave's own state when a lock is still held: it refuses only if the wave still has a lot running, queued or suspended **in this repository**, and goes ahead when the lots left are elsewhere (an unreadable wave state still refuses). They share a **cap on simultaneous sessions**, counted
1363
- across all live waves: 2 by default, `--max-sessions N` (or `CADENCE_MAX_SESSIONS=N`) to change it — give
1364
- every wave the same value: each wave counts ALL live sessions, whatever their slot, and waits while that
1365
- count has reached ITS OWN cap, so with different caps the highest one can push the total past the lowest
1366
- (which then waits). A session that finds no free slot **waits** (the wave is not refused; the journal says
1367
- `en attente d'un créneau de session depuis …`, repeated every minute, then `créneau de session obtenu après …`).
1368
- `cadence orchestrate --waits [--since YYYY-MM-DD]` adds those waits up from the journals of `.cadence/runs/` of the launch folder — per day (cumulated wait, number of waits, waves, quota stops — one per "→ suspended — quota atteint" line of the journals, counted on the day of that line, so a wave resumed and stopped again on another day counts once on each day, not once as a wave, tokens) and per wave that waited (the longest wait, a wait that never got its slot) — to decide the cap from figures; each wait is paired with its lot (the journal line carries `project:lot`), so two lots of one wave waiting at the same time (one per repository) are each counted once, and only a wait that never got its slot is reported as such; journals from before this carry no lot and are paired per wave. it reads only, and refuses any other action in the same call. The figures of 2026-10-12 and the cost of a third slot are in `docs/mesures/attente-creneaux-2026-10-12.md`.
1369
- A slot or a registry entry is owned by a pid **and** its start time: a reused pid is a dead owner. Wave
1370
- identifiers are reserved atomically (`-2`, `-3` suffix when two waves start in the same minute; an existing
1371
- `--wave` is refused). The registry of live waves and the slots live under `~/.cadence/orchestrate/` (`CADENCE_HOME`
1372
- to move it): `cadence orchestrate --status` lists, from any folder, the live waves, the repositories each
1373
- still holds (those not yet released), the cap of each wave, the slots in use and the **free slots** (`créneaux libres : N sur M`: M — the cap set with `--max-sessions` or `CADENCE_MAX_SESSIONS`, 2 by default — 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; the lead never raises the cap itself, that is the human's decision, L208). 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).
1374
-
1375
- **Guards, imposed by the code**: a global cap of simultaneous sessions, one wave per repository at
1376
- a time (above); `Agent`, `git push`, `cadence deliver`, `raf done|review|ux` are denied to the sessions; a
1377
- temporary `pre-push` hook, installed for the duration of the wave and removed at its end (or, for a repository, as soon as all the lots that touch it are finished), refuses any push
1378
- from a session (`CADENCE_ORCHESTRATED` is in their environment; a repository that already has another
1379
- `pre-push` hook is refused before anything starts — `pushurl` is never touched); after every session the
1380
- upstream ref and `git ls-remote` are compared with the "before" (a `git checkout <sha>` or `git switch` that only moves `HEAD` — detached `HEAD`, another branch, no upstream any more — is not a push: the upstream ref is compared only when it is the same branch on both sides, `git ls-remote` stays the judge; the write briefs forbid `git checkout` and `git switch` in the repository and point to a throwaway `git worktree add --detach <tmp> <sha>`; should a write session still end off its starting branch, a clean tree with nothing outside the branch gets the branch put back (`git checkout <branch>`, a warning in the log, the lot carries on from the right `HEAD`), anything else — including a put-back that fails (`index.lock`, a conflicting file), cause « HEAD déplacé — remise de la branche <b> impossible : <git message> » — hands the lot back with the cause « HEAD déplacé » and holds the queue of that repository, the other repositories carrying on; a push or a removed guard hook in ANY watched repository, project or neighbour, is checked first and wins over a moved `HEAD`, L214), and a read-only step that left only UNTRACKED files (screenshots written at the root by a script with a relative path) is not an incident: the files are moved to `.cadence/runs/<wave>/<project>--<lot>/stray/` (kept in their tree; for a neighbour repository, under `stray/<its relative path, `/` replaced by `_`>/`, e.g. `stray/.._gitops/`), a warning naming them goes to the lot's warnings and the wave log, the verdict is kept and the lot continues (L162; a move that is impossible, or an untracked file that vanished during the step, stays an incident); a review that changed `HEAD` or a tracked file (a commit, a modified file) is an incident of that lot only: the lot is handed back to the lead, the lots of the other repositories carry on while the lots still queued behind it in the same repository are suspended (resumable, nothing started on a dirty tree), and a verdict the review had already produced is reported in the outcome and the lot's warnings (never recorded in the plan). A push or a removed guard hook still stops the wave. `raf done|ux|review` and `cadence deliver` refuse when
1381
- `CADENCE_ORCHESTRATED` is set.
1382
-
1383
- **Budget**: the wave counts input + cache writes + output tokens (default 2 M); cache reads are kept and
1384
- shown apart. Each lot also has its own budget, the 90th percentile of the *total* measured per lot on `.cadence/runs` (all the waves a lot went through, 250 lots) for its category (L194): an ordinary lot of up to 0.5 day gets 540 k (floor: a write pass, its review, one fix pass and the short review after it, 155 k being reserved for those last two; measured p90 374 k), 0.5 to 1 day 750 k (p90 732 k), then 570 k per day of estimate, 750 k staying the floor until that exceeds it, i.e. up to 1.3 day (p90 1.14 M for 2 days); a lot with `screen: page` gets 1.3 M whatever its estimate (`component` and `journey` keep the ordinary budget; p90 1.28 M: spec, mockup, accept, conformity UX and passes), or its ordinary budget plus 450 k beyond that; a lot with `spec: true` gets its ordinary budget plus 450 k (the back chain measured on L120: 388 k); shown by
1385
- `--dry-run` with its basis): it bounds the *writing* passes, never the reviews. A lot that spent it gets no further implementation or
1386
- fix and is handed back to the lead, but its review (and its single UX review, once the code review is compliant) is played first; a fix pass starts only if
1387
- the remaining budget can also pay the fix and the short review that follows it (155 k tokens reserved: the p90 of a fix
1388
- pass, 65 k over 497 passes, plus the p90 of a short Opus review, 88 k over 272; measured on 2026-10-15 in the wave journals
1389
- `.cadence/runs`). A lot refused that fix is handed back with the cause `correction non jouée (budget) :` first, so the lead
1390
- takes the correction by hand (L218).
1391
- 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
1392
- refused for lack of budget the lot is handed back to the lead without a review, with its findings, and the cause names
1393
- 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
1394
- finish, the wave is *suspended* (exit code 3) and `--resume --budget …` continues. A session that returns
1395
- nothing readable, times out (45 min for work, 25 for a review) or fails is not retried; the lot is handed
1396
- back with the cause. Exit codes: 0 every lot ready · 1 at least one lot handed back (question, failure,
1397
- review still not compliant after two passes, or three when it converges) · 2 refused before acting · 3 wave suspended.
1398
-
1399
- **Stacked lots** (L76): lots of one repository commit on top of each other on `main`. When a ready lot sits under a lot that
1400
- is not ready (handed back, failed…), the final table adds a line `<project>:<lot> — livrable jusqu'à <sha> … : git push origin <sha>:main && cadence deliver --sha <sha>`:
1401
- the last commit of the highest ready lot with no unfinished lot's commit beneath it. Ship it without the lot above by pushing
1402
- that sha alone, then delivering it: `git push origin <sha>:main && cadence deliver --sha <sha>` (with `ci: github` the CI wait
1403
- looks for the run of that exact sha, and GitHub only builds the tip of a push; pushing HEAD would also put the unreviewed lot
1404
- on `main`). A lot's commits are read from git (the commits that cite it, whatever session or wave made them; plan-only and
1405
- version commits do not count), not only from the sessions' reports, so a failed or timed-out session that committed still
1406
- bounds the line. Nothing is printed when every lot is ready or when the ready lots sit above the unfinished one.
1407
-
1408
- **Formatting retry** (the only `--resume` of a session): when a session ends successfully but in plain text,
1409
- without the `structured_output` the schema asks for (the verdict is there, not in the required shape), the
1410
- orchestrator resumes **that same session once** (`claude -p --resume <session-id> --json-schema <same schema>`,
1411
- same model, permission mode and denied tools) with a short prompt that only asks for the report in the required
1412
- format. It applies to every step that has a schema (implementation, correction, reviews). The tokens of the
1413
- retry count in the wave budget and in the step (`formatRetry: true`); if the output is still missing after it,
1414
- the step fails as usual — never a second retry.
1415
-
1416
- State is in `.cadence/runs/<wave>/` of the folder where the command is run (added to `.git/info/exclude`
1417
- when that folder is in a repository; `.cadence/` and `.playwright-mcp/`, where the Playwright MCP writes its
1418
- output, never count as a dirty repository, even when `.gitignore` does not list them): `wave.json`, one `<project>--<lot>.json` per lot (steps, tokens
1419
- kept apart, session ids, commits, verdicts), the JSON output of every session and a `journal.log`. After a
1420
- cut (Ctrl-C, WSL closed) `--resume` replays an interrupted step entirely in a new session whose brief
1421
- lists the commits already present; finished steps are never replayed.
1422
-
1423
- When a session ends — success, error, time limit (`timeouts`) or interruption of the wave — nothing it started
1424
- survives it: besides its process group, the tree of its descendants is tracked while it runs and killed with it, and
1425
- every session carries a `CADENCE_SESSION=<id>` mark in its environment that all its descendants inherit, even those
1426
- re-parented to init (`nohup srv &` or `setsid srv &` from a shell that exits at once): at the end — as soon as the session's root process exits, not when its output closes, so a descendant holding stdout open cannot turn a session that exited with code 0 into a "time limit" failure — every process
1427
- bearing the mark is killed (read from `/proc/<pid>/environ`, or, where there is no `/proc`, from `ps -axEww` on macOS — `-E` is the BSD option that prints the environment, `-e` only means "all processes" there — and `ps axeww` with procps on Linux).
1428
-
1429
- Limits of the mark, stated rather than hidden: it is read from the process environment, so a descendant that
1430
- dropped it (`env -i`, `env -u CADENCE_SESSION`) is only reached by the tree tracking, and only if it was seen under
1431
- the session between two samples (every 200 ms); a process whose environment is unreadable (another user) is never
1432
- found; a process started through a daemon that already ran before the session (`docker compose up -d` talks to the
1433
- Docker daemon, which owns the containers) is out of reach. **Shared daemons are spared**: a tmux server, `screen`,
1434
- `gpg-agent` or `dirmngr` started on demand by a session carries its mark and would pass it to clients and panes opened
1435
- later from elsewhere, so killing by mark would kill the lead's own tmux panes; those daemons (only when they carry the mark: the lead's own tmux, where cadence itself may run, does not, and spares nothing), and everything that
1436
- descends from them, are left alone (a dev server started inside a tmux pane therefore survives its session). Not in
1437
- that list, because their name does not tell them from an ordinary client: the ssh master (`ControlPersist`), the
1438
- Gradle daemon (java) and pm2 (node) — they are killed if they carry the mark.
1439
-
1440
- Briefs are the templates of `templates/orchestrate/` (`implement.md` is the `lead` skill's standard
1441
- brief; `--dry-run` writes the rendered ones). The `implement` brief of a `visible` lot also carries the
1442
- News instruction (`cadence news new <lot>`, factual user-side text, a screenshot in `docs/nouveautes/captures/` or
1443
- `nocapture:`, `cadence news check` green); the brief gives the absolute path of the wave's Playwright output directory and the `implement` session gets `--add-dir` on it, to copy the capture into the repository; the `fix` and `fix-minors` briefs of a `visible` lot give the same folder (and their sessions the same `--add-dir`), to fetch a screenshot a finding asks for; a lot that is not `visible` gets nothing. A `--dry-run` brief of a `visible` lot has no wave folder (the wave does not exist yet), nor does a brief delegated by hand: the News instruction then says to save the screenshot under the OS temp directory, never inside the repository, then copy it into `docs/nouveautes/captures/`. A project can declare, in `cadence.yaml`:
1444
-
1445
- ```yaml
1446
- orchestrate:
1447
- test: npm test # run by the orchestrator after a work step (optional)
1448
- build: npm run build # run after the tests; both results go to the reviewer, who does not redo them (optional)
1449
- 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, local runs it on claude-local (Ollama) with Sonnet as fallback
1450
- accept: npx vitest run -c vitest.acceptation.config.ts # accept step (see « The accept step »): the dedicated command that runs ONLY the acceptance tests of the lot (optional; without it the step is skipped with a warning)
1451
- maquette: true # default: UX review of the mockup between spec and accept (see « The mockup review »); false skips it
1452
- fiche: true # default: review of the back-end sheet of a lot with spec: true between spec and accept (see « The spec step of a lot with spec: true »); false skips it
1453
- spec: { dir: docs/specs } # spec step (see « The spec step of a lot with screen: »): folder of the screen specs, relative to the repository (default docs/specs); render: the command the spec session runs to render the mockups at 1440 and 390 px (optional, Playwright otherwise)
1454
- prodAccess: false # default: wave sessions get no access to production (see « Production access of the sessions »); true lifts it for this project
1455
- effort: { review: xhigh } # effort level per pass (see « Effort level per pass »): precheck, spec, maquette, fiche, accept, implement, fix, review, ux
1456
- ux: http://localhost:4200 # a URL, a launch command, or { command, url, timeout? } — for the UX review (see below)
1457
- permissionMode: auto # default
1458
- addDirs: [/home/me/projects/tmp] # extra directories the sessions may use
1459
- timeouts: { implement: 45, review: 25 } # minutes
1460
- # a plan kept by the project's own tool is read-only for raf: the orchestrator calls these instead
1461
- start: python3 scripts/raf.py start {lot}
1462
- verdict: python3 scripts/raf.py note {lot} "revue de code : {verdict}"
1463
- ```
1464
-
1465
- **`orchestrate.ux`, who starts the app**: a string is a URL if it starts with `http://` or `https://`, a launch
1466
- command otherwise; both forms behave as before (the `ux` brief gives the URL, or tells the session to start the app
1467
- with the command and stop it). The object form `{ command, url, timeout? }` makes **the program** start the app, not
1468
- the UX session. Before the `ux` step (played once, after the compliant code review, L221) and before the `review-small` single pass of a small `visible` lot, which follows
1469
- the same rule, the orchestrator:
1470
-
1471
- 0. takes a lock on the `host:port` of `url` (a file `cadence-ux-<host>-<port>.lock` in the OS temp folder, created
1472
- exclusively, containing the pid of the orchestrator; a lock whose pid is dead is taken over). Another wave — in
1473
- this process or another — that declares the same URL waits for it, and the wait counts in `timeout`; past it the
1474
- note is « UX non vérifiée : url tenue par une autre vague » and nothing is started. The lock is given back after
1475
- the app is stopped. Declaring **distinct ports per project** is still recommended: the lock serialises two waves
1476
- on one URL, it does not make them fast;
1477
- 1. probes `url`; if it already answers it does **not** start anything, notes « port occupé » (`uxNote`: the UX is not
1478
- verified) and the lot goes on;
1479
- 2. starts `command` with `sh -c` from the repository root, in its own detached process group, its output in
1480
- `<wave>/<project>--<lot>/ux-app.log` (the command carries its own prefixes, e.g. `cd web && PORT=4300 npm start`;
1481
- there is no `env`, `cwd` or account/PIN key);
1482
- 3. probes `url` until an HTTP status below 500, for `timeout` seconds (default 300); the answer only counts while
1483
- the process group it started is still alive. A command that exits early, or
1484
- no answer in time, means « UX not verified » with the end of the log in the note: the UX session is skipped,
1485
- the code review being already compliant the lot concludes with its note — it is never a failure of the wave (the single pass of a small lot still runs,
1486
- on the code alone, its brief saying the app could not be verified);
1487
- 4. gives the session « The running app is at `<url>` », with no instruction to start anything;
1488
- 5. at the end of the step — success, error or SIGTERM of the wave — kills the group: SIGTERM, then SIGKILL after 10 s.
1489
-
1490
- ```yaml
1491
- orchestrate:
1492
- ux: { command: 'cd web && PORT=4300 npm start', url: 'http://localhost:4300', timeout: 120 }
1493
- ```
1494
-
1495
- In the object form `url` must start with `http://` or `https://`, and `timeout` is only accepted together with both
1496
- `command` and `url` (the program only waits when it starts the app). A wave stopped (incident, quota, budget) before
1497
- the step suspends the lot without starting the app.
1498
-
1499
- **Node per project (`.nvmrc`)**: when a project has a `.nvmrc` at its root, every session the
1500
- orchestrator launches for it (implementation, UX and code reviews, corrections) runs with the matching Node
1501
- first in its `PATH`, so `node`, `npm` and `npx` resolve to it (Astro needs 22 while the default may be 20) — and so does
1502
- the app the program starts for the UX review (`ux.command`): same links directory first in its `PATH`, no
1503
- `. ~/.nvm/nvm.sh && nvm use` prefix needed (it exits with code 3 under `sh`). Only
1504
- those four names (`node`, `npm`, `npx`, `corepack`) are exposed, through symlinks in
1505
- `.cadence/runs/<wave>/node-bin/<version>/` (recreated at start and at every `--resume`): the rest of that Node's
1506
- `bin` (globally installed `raf`, `cadence`, `claude`…) is never exposed, so it does not shadow the tools in the usual `PATH`;
1507
- only the snapshot's `tool/bin` (above) comes first, ahead of this Node directory. `--resume` resolves
1508
- the `.nvmrc` again for every live lot and refuses it like at start if it can no longer be resolved.
1509
- The file is read trimmed (`22`, `v22`, `22.22`, `v22.22.3`); the highest matching version installed under
1510
- `$NVM_DIR/versions/node` (default `~/.nvm/versions/node`) **that has an executable `bin/node`** is used: a higher
1511
- version with an empty or broken `bin` is skipped for a lower valid one (and `--dry-run` says so); if none is valid
1512
- the lot is refused. Only the sessions' environment changes,
1513
- never the orchestrator's own. No `.nvmrc` → nothing changes. A `.nvmrc` that cannot be resolved (`lts/*`, an
1514
- alias, a version not installed) refuses the lot before anything is started (exit 2), naming the requested
1515
- version and the folder searched — there is no silent fallback to the default Node. `--dry-run` prints
1516
- `node : v22.22.3 (.nvmrc 22)` under each lot that has one, with the skipped versions when it happens
1517
- (`node : v22.9.0 (.nvmrc 22 ; v22.22.3 écartée : pas de node exécutable)`).
1518
-
1519
- **Minimal MCP servers per step**: every session the orchestrator launches gets `--strict-mcp-config --mcp-config
1520
- <file>`, with the file written by the orchestrator in the lot's folder of the wave
1521
- (`.cadence/runs/<wave>/<project>--<lot>/mcp-<step>.json`). `--strict-mcp-config` makes `claude` ignore every other
1522
- source (user, project, plugin servers: Serena, context7, Cloudflare…), so a session no longer starts a handful of
1523
- `npx`/`uvx` servers it does not use. `review` and `review-recheck` always get an empty set
1524
- (`{"mcpServers":{}}`); so do `implement`, `fix` and `fix-minors` on a lot without a screen. Playwright, launched as
1525
- `npx -y @playwright/mcp@latest --output-dir <wave>/<project>--<lot>/playwright`, is loaded for the `ux` and `maquette` steps and, on a
1526
- `visible` lot, for `spec`, `implement` and `fix` (`fix-minors` included) and, on a small `visible` lot, for `review-small` (its only
1527
- usability review; `review-small` of a small lot without a screen loads nothing), so the screenshots and snapshots of the work and of
1528
- the review stay with the wave, outside the repository, where the lead can look at them; the final table prints that
1529
- folder whenever Playwright was loaded for the lot, even if no `ux` step ran
1530
- (`<project>:<lot> — captures Playwright : <dir>`; `uxCaptures` in the wave state). For `maquette` and `spec` the server also gets
1531
- `--allow-unrestricted-file-access`: the mockup is a local file, and by default the server blocks every `file://` URL. The `implement`, `fix`,
1532
- `fix-minors`, `review-small` and `ux` briefs tell the session to give relative file names if a Playwright server is available, and
1533
- otherwise to keep any Playwright CLI output outside the repository.
1534
- `--dry-run` prints, under each step, `mcp : aucun` or `mcp : playwright (…)`. There is no per-project override in
1535
- `cadence.yaml` yet.
1536
-
1537
- Without `start`, a read-only plan's `todo` lot is refused (start it with the project's tool); without
1538
- `verdict`, the review verdict stays in the wave's state and you report it. Only the plan's files
1539
- (`plan.path`, `plan.files`, the QA expectations file) are committed from those commands; anything else dirty stops the lot (`.cadence/` and `.playwright-mcp/` excepted, as above).
1540
-
1541
- ## Claude Code skills
1542
-
1543
- As a plugin:
1544
-
1545
- ```
1546
- /plugin marketplace add Sylad/cadence
1547
- /plugin install cadence@cadence
1548
- ```
1549
-
1550
- gives `/cadence:session-start`, `/cadence:session-close`, `/cadence:deliver`,
1551
- `/cadence:lead` and the `ux-reviewer`, `code-reviewer` and `qa-reviewer` agents. Or copy them into the
1552
- repository with `cadence skills install` (to `.claude/skills/cadence-*` and
1553
- `.claude/agents/cadence-*.md`; `--dir` for another `.claude` folder,
1554
- `--force` to overwrite local edits).
1555
-
1556
- - **session-start**: reports the facts briefly, proposes three lots from the
1557
- plan, then waits for your priority — nothing starts before your answer.
1558
- - **session-close**: plan hygiene, clean repository, a cleanup proposal for stale
1559
- working files (`session.clean` patterns, older than `session.cleanDays`; never
1560
- anything git tracks, a git repository or a folder holding one, anything under
1561
- `.git`, a bare repository or separate git directory or anything inside one,
1562
- anything behind a symbolic link a `*` matched (only the link itself), a hidden `.xxx` name a `*` would not match, nor anything it could not
1563
- read entirely — it asks before deleting), memory limited to what the
1564
- repository does not say, new skills or agents proposed but never created, three
1565
- lines for next time.
1566
- - A project with its own tooling keeps it: its plan is read where it is (`plan:`),
1567
- its delivery script is called by `cadence deliver` (`deliver.script`), its
1568
- morning and evening scripts feed the session report (`session:`), and its own
1569
- skills can become one-line aliases of `session-start` / `session-close`.
1570
- - **deliver**: dry run, delivery, and on failure the cause fixed rather than a
1571
- blind retry; after a green delivery that changes what a page shows or what it
1572
- is served, the `qa-reviewer` agent walks the delivered app.
1573
- - **lead**: from a folder holding several projects, `cadence lead tour` (a program,
1574
- no model: see [lead tour](#lead-tour)) prints one line of facts per project, you choose the priorities, the lots are delegated with
1575
- `cadence orchestrate` (fresh short sessions with a standard brief — test first,
1576
- README and usage documentation updated with the change, commits citing the lot, no push — reviewed by the `code-reviewer` agent, see
1577
- [orchestrate](#orchestrate); a lot with `screen: page` first goes through the [spec, mockup review and accept steps](#the-spec-step-of-a-lot-with-screen)), re-verified by the lead, then delivered
1578
- one project at a time; a delivery that changes what a page shows or what it is
1579
- served is then checked in the running app by the `qa-reviewer` agent, whose
1580
- blocking findings come back to you. As many
1581
- subagents as the session cap at most (2 by default, `CADENCE_MAX_SESSIONS`), never two in the same repository. The whole flow is drawn in
1582
- [docs/lead.md](docs/lead.md); how a lot, a wave, the lead and the service of cadence itself fit together, in four figures, is in
1583
- [docs/fonctionnement.md](docs/fonctionnement.md).
1584
- - **ux-reviewer** (agent): captures at 1440 and 390 px, findings grounded in a
1585
- named rule (Nielsen, WCAG 2.2 AA) or a measurement, ranked, turned into
1586
- `raf add --parent` sub-tasks, and a one-line verdict for `raf ux`. It never
1587
- edits code. Read-only for real: its `tools:` list is `Read, Grep, Glob, Bash` and the
1588
- Playwright tools (`mcp__playwright`, and `mcp__plugin_playwright_playwright` when the
1589
- browser comes from the Playwright plugin) — no `Edit`, no `Write`. Outside a wave it gives its captures a relative file name only, which the Playwright MCP writes under `.playwright-mcp/` (the same rule as the wave's briefs); `qa-reviewer` follows it too.
1590
- - **code-reviewer** (agent): any stack; given a repository and a lot id, it reads
1591
- the diff itself from the commits that cite the lot — not the author's summary —
1592
- and the project's CLAUDE.md, when there is one, for its conventions; findings grounded in a
1593
- measurement (a failing command, changed code without a test, a duplicated
1594
- block, dead code) or a named rule, each with `file:line` and a concrete
1595
- scenario; real defects only, ranked, what it could not verify, and a one-line
1596
- verdict for `raf review`. It takes the lot's commits from `raf commits`, never
1597
- runs a build whose output is used live, and never edits code. A README or usage
1598
- documentation that does not follow the lot's change is a *major* finding.
1599
- - **qa-reviewer** (agent): any web app (read-only like `ux-reviewer`: same tool list, no `Edit`, no `Write`); given a repository and a base URL (and
1600
- optionally a lot id, to walk only the pages it touched — those that call the
1601
- changed endpoints, those of the screens it changed, the pages that consume the services the lot changed,
1602
- and the home page; a backend-only lot stays in scope, and when the lot changes no route
1603
- and no screen, or the endpoints cannot be mapped to pages, every page is walked; the others are named « Not walked »), it opens each page of
1604
- the project's expectations file in a real browser at 1440 and 390 px and
1605
- measures: expected content present and non-empty, no error or missing-data
1606
- message, every API call answered 2xx with a non-empty body, no console error,
1607
- no broken content image. It reads the page as text first and takes a capture only for a gap. Findings are defects (a line of the expectations
1608
- broken, or a universal check failing with a visible effect, with or without an
1609
- expectations file), suspects (it looks like missing or wrong data and no
1610
- expectation settles it) or noise (a console error or a failed request with no
1611
- visible effect, ranked minor) or already planned (an open lot covers it: returned in one line, the lot id and its title), ranked, each with
1612
- the route, what was expected, what was measured and the evidence; pages checked
1613
- N/N, follow-ups as `raf add` lines, what it could not verify, a one-line
1614
- verdict. Read-only: GET only, no login, nothing submitted; it stops at a PIN.
1615
-
1616
- ## cadence-hud
1617
-
1618
- A band above the Claude Code prompt (terminal and desktop app), refreshed every 5 s:
1619
-
1620
- ```
1621
- ctx ▰▰▰▰▱▱▱▱▱▱ 42 % 84k/200k │ 5h 23 % ↻ 2 h 10 │ 7j 61 % ↻ 6 j 15 h │ $1.23 │ fable 410k sonnet 85k │ ⚙ 2 agents │ 2 cmd
1622
- ⟳ cadence · 2026-10-07-2131 en cours │ budget ▱▱▱▱▱▱▱▱▱▱ 1 % 14k/2M │ 1 session/2
1623
- L112 implémente implement@sonnet 34 s global-setup coupe le cache de compilation de Node...
1624
- ```
1625
-
1626
- Context of the session (green / orange / red at 50 and 75 %), the 5-hour and 7-day quota
1627
- windows with the time to their reset, the cost of the session and its split per model, the
1628
- subagents of this session, the background commands in progress (`2 cmd`: Bash run in the background and Monitor, subagents' included, as the app's "background commands" card counts them), and every live `cadence orchestrate` wave: budget, sessions, one
1629
- aligned line per active lot (status, step, model, elapsed, start of the title), the lots still
1630
- waiting. When no wave is running, the last finished one stays on a grey line for 30 minutes
1631
- after it ended, then disappears. A background command counted for more than an hour, or with no
1632
- record of when it started, is no longer shown as running: it moves to a grey `N cmd sans fin
1633
- vue` ("end never seen") right after `N cmd`, because its end notification may never have reached
1634
- the session. Waves are read from disk (`~/.cadence/orchestrate/waves/`, then `.cadence/runs/`); no
1635
- cadence command is run, and the CLI is not required for those. One more block needs it: the **progress of the plans**,
1636
- one line per active project (a project with open lots), in the order of `priority:`:
1637
-
1638
- ```
1639
- ol ▮▮▮▮▮▯▯▯▯▯ 39/78 · 0 en cours · +3 cette semaine
1640
- cadence ▮▮▮▮▮▯▯▯▯▯ 10/20 · 2 en cours : L149 L153
1641
- ```
1642
-
1643
- The bar is lots done over lots done + in progress + to do (dropped and recurring lots left out); the count of lots in progress is followed by their ids (`2 en cours : L149 L153`), cut to three then `…` (`5 en cours : L1 L2 L3 …`), the count alone at zero;
1644
- `+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
1645
- `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),
1646
- read again at every wave transition and otherwise every minute; without `cadence` on the PATH, the
1647
- lines are simply not drawn.
1648
-
1649
- Install it as a plugin from the same marketplace:
1650
-
1651
- ```
1652
- /plugin marketplace add Sylad/cadence
1653
- /plugin install cadence-hud@cadence
1654
- ```
1655
-
1656
- `/hud` hides or shows the band. `/hud cmd` lists the background commands being counted (id, tool,
1657
- origin `session` or `agent <id>`, age, name; `(sans fin vue)` on the ones above). Any other argument
1658
- `/hud projets` folds the project list to one line, and opens it again. Any other argument
1659
- 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)
1660
- (its own README has the details of every cell); it is not part of the npm package. To work on it:
1661
- `claude plugin validate plugins/cadence-hud`, `claude plugin test plugins/cadence-hud`, and
1662
- `tsc -p plugins/cadence-hud` once Claude Code has loaded the plugin at least once (it generates
1663
- the `.claude-plugin/types` the tsconfig extends).
1664
-
1665
- ## Releasing
1666
-
1667
- A version exists in three places and is published in two; a release does all of it, in this order:
1668
-
1669
- 1. Bump `version` in `package.json` (then `npm install` to refresh `package-lock.json`),
1670
- `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`, and write the version's section of
1671
- `CHANGELOG.md` (`## [x.y.z] - date`, what changes for the user, lots cited), in **one commit that holds
1672
- only those files** (the version fields and the CHANGELOG, nothing else in the manifests, and the « What's new »
1673
- section of this README, the one place of it a release rewrites). Such a commit is
1674
- exempt from the code-review gate like a plan commit: it closes the lot after its review, and `raf check`
1675
- does not ask for a new one (nor does it count it as a commit without a lot). The « What's new »
1676
- summary is ordered by version, a number known only at the release and shared by several lots: write it
1677
- in that commit, not before the review. A commit that touches any other file
1678
- — or another field of a manifest, or a dependency in `package-lock.json` — is work like any other and has
1679
- to be reviewed.
1680
- 2. Run the plugin evaluation, `npm run eval:plugin` (billed, see [Evaluating the plugin](#evaluating-the-plugin-before-a-release)),
1681
- on that version commit; a case below the threshold stops the release until you have read it in the report.
1682
- 3. `git tag v<version> && git push origin main v<version>` — the tag starts `.github/workflows/publish.yml`,
1683
- which publishes to npm through Trusted Publishing (OIDC, no token stored anywhere): it checks the tag
1684
- matches `package.json`, `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json` and that `CHANGELOG.md` has a `## [x.y.z]` section for it (no section, no publication), then `npm publish --provenance`, where `prepublishOnly` runs the type-check and the
1685
- tests and `prepare` builds `dist/`; a red suite stops the publication; then it creates the GitHub release with that CHANGELOG section as its text. The trusted publisher is declared
1686
- once on npmjs.com (package settings → Trusted Publisher → GitHub Actions, `Sylad/cadence`, `publish.yml`).
1687
- 4. Watch the run: `gh run watch` (or `gh run list --workflow publish.yml`).
1688
- 5. Check the effect: `npm view @sylad/cadence version` answers the new version.
1689
- A run is safe to re-run, and two runs for one tag queue instead of racing (`concurrency` per ref, never cancelling the one that publishes):
1690
- a version already on npm skips `npm publish`, and a GitHub release that is missing is created (`--verify-tag`)
1691
- while an existing one is left alone. If the package is on npm but the release is still missing, re-run the job;
1692
- the by-hand fallback is `gh release create v<version> --title v<version> --notes-file <the section of CHANGELOG.md> --verify-tag`.
1693
-
1694
- `.github/workflows/tests.yml` runs the type-check and the test suite on the same runner image, Node 24 and `npm ci` as the publish job, and never publishes: start it with `gh workflow run tests.yml --ref <branch>` or by pushing a `diag/**` branch, to see on the runner what a red publish run saw.
1695
-
1696
- The Claude Code plugin is read from the repository, so pushing `main` is what updates it; npm is what
1697
- `npx @sylad/cadence` and a global install read, and only the tag publishes there. A missing tag, or a red
1698
- publish run, leaves npm behind without any other error — 0.3.0 and 0.4.0 were never published — hence
1699
- step 5.
1700
-
1701
- ### Evaluating the plugin (before a release)
1702
-
1703
- `evals/` holds an evaluation suite for the plugin itself, run by `claude plugin eval` (Claude Code 2.1.263 and later). Each case is
1704
- a folder with a `prompt.md` and `graders/*.md`, and replays a trap found by hand: `raf-done-sans-revue` (`raf done` refused on a lot whose
1705
- commits have no code review, with no `--force` to get round it), `deliver-sha` (`git push origin <sha>:main` then
1706
- `cadence deliver --sha <sha>` for a pushed commit that is not `HEAD`), `orchestrate-id-avec-slash` (`maritime-atlas:Q4/accueil-4-ux12@haiku`
1707
- keeps its `/`) and `livrer-sh-sans-bloc` (a `./livrer.sh` without a `deliver:` block in `cadence.yaml` is not
1708
- run by `cadence deliver`). Every case also runs a **baseline without the plugin**, so the report gives the score of each arm and the
1709
- difference: a trap the baseline already avoids proves nothing about the plugin. The cases are read-only questions (tools `Read`, `Glob`, `Grep`,
1710
- `Skill`, no scaffold script), each graded by a regular expression and by a model-judged criterion.
1711
-
1712
- ```sh
1713
- npm run eval:plugin # claude plugin eval . --max-cost-usd 5 --no-publish; 4 cases × 3 runs × 2 arms = 24 runs
1714
- claude plugin eval . --case deliver-sha --runs 1 --trust-plugin --no-publish --max-cost-usd 1 # one case, one run (the first run asks to trust the plugin)
1715
- ```
1716
-
1717
- **Every run is billed** (a full `claude` session on your own credential, plus the judge): the suite is run at the release, once the
1718
- version commit is ready, never in `npm test`, in `prepublishOnly` or in CI, and `--max-cost-usd 5` aborts a runaway. A case below
1719
- the threshold (1.0 by default, `--threshold`) makes the command exit 1: read the case in the report before deciding it is the plugin
1720
- and not the wording of the case. `npm test` only checks that the
1721
- files are well formed (`test/plugin-evals.test.ts`); `evals/` is not part of the npm package.
1722
-
1723
- ## Stability
1724
-
1725
- From 1.0.0 cadence follows [semantic versioning](https://semver.org): what is listed under
1726
- *Guaranteed* only changes incompatibly in a major version; a minor or a patch never breaks it.
49
+ ## What's new
1727
50
 
1728
- **Guaranteed**
51
+ Two lines per version; the long form is in [docs/whats-new.md](docs/whats-new.md) and the full history, with dates, in [CHANGELOG.md](CHANGELOG.md).
1729
52
 
1730
- - **Commands and options** of `raf` and `cadence` (`cadence raf …` is `raf …`): `init`, `add`, `start`, `done`, `drop`,
1731
- `note`, `did`, `public`, `screen`, `spec`, `ux`, `review`, `commits`, `show`, `list`, `now`, `ignore`, `check`, `gantt`, `hook`;
1732
- `cadence news`, `session` (`start`, `close`, `next`, `context`), `lead tour`, `corrections`, `deliver`, `verify`, `orchestrate`,
1733
- `skills`, `spec` (`cadence spec <lot> on|off` is `raf spec`; `check`, `qa`: exit **0** when the sheet is valid and the section is printed, **1** with violations, **2** on a missing or unreadable file or a wrong usage);
1734
- their options and exit codes as this README documents them. A new command or option is a minor change.
1735
- - **The keys of `cadence.yaml`**: `plan`, `news`, `hook`, `session`, `deliver`, `orchestrate`, `docs`, `qa`, `priority`, `depth`, `lead` (`lead.ux`)
1736
- and the keys each of them takes. A key is not renamed nor removed, nor does its meaning change; a new key is a minor change.
1737
- - **The plan file** (`docs/plan/raf.yaml`): `project`, `prefix`, `since`, `ignore`, `lots` (with their
1738
- fields, `tasks`, `notes`, `repos`) and `acknowledged`, and the plan formats `plan:` in `cadence.yaml` can read.
1739
- The `version` key is kept in the file but ignored (`src/plan.ts` does not read it): it is not a schema version.
1740
- - **The names of the skills** (`session-start`, `session-close`, `deliver`, `lead`) **and of the agents**
1741
- (`ux-reviewer`, `code-reviewer`, `qa-reviewer`).
53
+ - **1.7.0**: `cadence orchestrate` no longer replays `accept` on a green sealed acceptation and refuses `--resume --dry-run`; `spec reseal --porteur` accepts a sheet amended by a delivered lot; the README becomes a short home page.
54
+ - **1.6.0**: the mockup review sweeps everything in its first report and runs up to `orchestrate.maquetteReviews` times (3 by default); the acceptance lock names the first offending commit.
55
+ - **1.5.1**: publishes 1.5.0 (tagged, never published): one UX review per lot, replay of a lot handed back for its budget, a self-check list in the briefs, a `tests.yml` workflow.
56
+ - **1.5.0**: `gate: { count: N, … }` makes a lot wait for N done lots of a kind; `raf todo <id>` puts a started lot without work commits back to `todo`.
57
+ - **1.4.0**: `cadence spec reseal` accepts a sheet amended by the lot's commits; wave budgets recalibrated; a `git checkout` in a session no longer stops the wave.
58
+ - **1.3.0**: the spec step narrows to `screen: page`; `cadence spec reseal … --porteur` re-seals an unstable test; `cadence orchestrate --waits`; a third correction pass when the review converges.
59
+ - **1.2.0**: the spec step reaches lots without a screen (`spec: true`), `orchestrate.accept` may be a list of runners, `lead.ux: lead`, and the code in service is a separate worktree.
60
+ - **1.1.0**: the spec step of `cadence orchestrate`: screen sheet, reviewed mockup, red locked acceptance tests, and briefs that carry rule → test → green.
61
+ - **1.0.0**: the first major release, a stable contract that follows semantic versioning; the [stability page](docs/stabilite.md) lists what is guaranteed.
1742
62
 
1743
- **Not guaranteed** (may change in any release)
63
+ <details>
64
+ <summary>Older versions (0.10.0 to 0.28.0)</summary>
1744
65
 
1745
- - The **text of the outputs**: wording, layout, order of the lines and columns of every report, table and message
1746
- (the `--json` outputs and the exit codes are part of the commands above). In a `--json` output a new field is a
1747
- minor change, so consumers must tolerate unknown fields; removing or renaming a field is a major change.
1748
- - The **`precheck-reader`**, **`spec-writer`** and **`acceptance-writer`** agents, and the mockup and conformity modes of **`ux-reviewer`**: internal to `cadence orchestrate`, not meant to be called by hand.
1749
- - The **`cadence-hud` band**: its segments, colours and layout; it is versioned on its own.
1750
- - The **wave journals** under `.cadence/runs` and the files `cadence orchestrate` keeps there.
1751
- - The wording of the briefs and of the instructions inside the skills and the agents.
1752
- - The **format of the sheets** (`fiche.yaml`: the screen sheet `templates/fiche.yaml`, the back-end sheet `templates/fiche-back.yaml`, the mixed sheet) and so the invariants `cadence spec check` enforces: experimental while the next bricks of the spec step (S3–S6) still reshape it, a sheet that passes today may be refused by a later release. The `spec` commands and their exit codes are guaranteed, the keys they read are not yet.
66
+ Each is a paragraph in [docs/whats-new.md](docs/whats-new.md) and a full section in [CHANGELOG.md](CHANGELOG.md): `cadence-hud` (0.12.0 to 0.25.0), `cadence orchestrate --continue` and `--status --watch`, `cadence lead tour`, `hook.autostart`, `docs.sync` and `docs.articles`, neighbouring repositories, `raf ignore`.
1753
67
 
1754
- There is no schema version and no migration: a file written by an earlier release keeps working, and a key a
1755
- release adds is optional.
68
+ </details>
1756
69
 
1757
70
  ## License
1758
71