@sylad/cadence 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +24 -67
- package/agents/doc-keeper.md +2 -2
- package/agents/precheck-reader.md +1 -1
- package/agents/reference-analyst.md +25 -0
- package/agents/reference-verifier.md +19 -0
- package/dist/audit.js +3 -1
- package/dist/check.js +19 -3
- package/dist/cli.js +28 -5
- package/dist/corrections.js +2 -1
- package/dist/deliver.js +47 -16
- package/dist/lead.js +8 -0
- package/dist/orchestrate/briefs.js +19 -1
- package/dist/orchestrate/changelog-merge.js +94 -0
- package/dist/orchestrate/command.js +108 -60
- package/dist/orchestrate/cycle.js +226 -36
- package/dist/orchestrate/integrate.js +52 -8
- package/dist/orchestrate/launch.js +14 -4
- package/dist/orchestrate/no-test.js +41 -0
- package/dist/orchestrate/registry.js +23 -0
- package/dist/orchestrate/snapshot.js +29 -1
- package/dist/orchestrate/state.js +14 -9
- package/dist/orchestrate/table.js +13 -1
- package/dist/perf.js +109 -7
- package/dist/plan.js +20 -1
- package/dist/proc.js +6 -0
- package/dist/ref/add.js +24 -11
- package/dist/ref/analyse.js +220 -0
- package/dist/ref/command.js +15 -1
- package/dist/ref/diff.js +262 -0
- package/dist/ref/evaluate.js +2 -2
- package/dist/ref/figures.js +86 -13
- package/dist/ref/pdf.js +20 -8
- package/dist/ref/regard.js +102 -0
- package/dist/ref/session.js +34 -0
- package/dist/state.js +17 -2
- package/docs/deliver.md +19 -3
- package/docs/fonctionnement.md +3 -3
- package/docs/lead.md +8 -4
- package/docs/news.md +6 -0
- package/docs/orchestrate.md +71 -40
- package/docs/overview.md +65 -1
- package/docs/perf.md +3 -2
- package/docs/raf.md +2 -1
- package/docs/ref.md +102 -3
- package/docs/releasing.md +3 -3
- package/docs/skills.md +12 -0
- package/docs/spec.md +1 -1
- package/docs/whats-new.md +28 -1
- package/package.json +1 -1
- package/skills/lead/SKILL.md +6 -6
- package/templates/orchestrate/fix-docs.md +2 -2
- package/templates/orchestrate/implement.md +1 -1
- package/templates/orchestrate/precheck.md +1 -1
package/docs/orchestrate.md
CHANGED
|
@@ -20,12 +20,17 @@ cadence orchestrate --continue [--until 18:00] [--priority cadence,maritime] #
|
|
|
20
20
|
cadence orchestrate --waits [--since 2026-10-12] # time spent waiting for a session slot, per day and per wave (below)
|
|
21
21
|
cadence orchestrate --status [<wave>] # the live waves and the repositories they hold, then the table (default: the last wave of this folder)
|
|
22
22
|
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)
|
|
23
|
-
cadence orchestrate --resume [<wave>] [--budget 1M] [--answer ol-companion:L22 "reply"] [--drop ccc:L29]
|
|
24
|
-
cadence orchestrate --resume <wave> --budget maritime:B26.4=+300k # replay a lot handed back for its budget, with the budget raised (below)
|
|
25
|
-
cadence orchestrate --resume <wave> --integrate ccc:L29 # replay a lot handed back at the integration step of a worktree wave (« One worktree per lot
|
|
26
|
-
cadence orchestrate --resume <wave> --replay ccc:L29 # same, and also a lot handed back on a refused plan write (fiche notes) at the step it had reached
|
|
27
|
-
cadence orchestrate --remove-worktree ccc:L29 [--abandon-commits] # remove the kept worktree and branch of a lot (« One worktree per lot
|
|
23
|
+
cadence orchestrate --resume [--wave <wave> | <wave>] [--budget 1M] [--answer ol-companion:L22 "reply"] [--drop ccc:L29]
|
|
24
|
+
cadence orchestrate --resume --wave <wave> --budget maritime:B26.4=+300k # replay a lot handed back for its budget, with the budget raised (below)
|
|
25
|
+
cadence orchestrate --resume --wave <wave> --integrate ccc:L29 # replay a lot handed back at the integration step of a worktree wave (« One worktree per lot »)
|
|
26
|
+
cadence orchestrate --resume --wave <wave> --replay ccc:L29 # same, and also a lot handed back on a refused plan write (fiche notes) at the step it had reached
|
|
27
|
+
cadence orchestrate --remove-worktree ccc:L29 [--abandon-commits] # remove the kept worktree and branch of a lot (« One worktree per lot »)
|
|
28
|
+
cadence orchestrate --resume … a:L1 # refused: a resume replays the lots of the wave, it launches none
|
|
28
29
|
cadence orchestrate --resume … --dry-run # refused: a resume cannot be simulated, it writes the wave state and relaunches sessions
|
|
30
|
+
# `--resume` without a wave resumes the only unfinished wave of the folder; with several, it lists them and is refused (exit 2): name one with `--resume --wave <wave>`
|
|
31
|
+
# every resume command the program prints (under a question, after `--stop-after-current`, on a lot handed back at the integration step, in the `spec reseal` refusal, in the several-waves refusal) has the form `cadence orchestrate --resume --wave <wave> …`; `--resume <wave>` with an id that looks like a lot (`suite2`) is read as a lot to launch and refused, and the refusal prints the `--wave` form
|
|
32
|
+
# the table of a wave (end of wave and `--status`) prints, under each question, the command that answers it with the wave named: `cadence orchestrate --resume --wave <wave> --answer <project>:<lot> "…"` (`--wave` rather than `--resume <wave>` so that an id that looks like a lot, `suite2`, is read as a wave too)
|
|
33
|
+
# a resume running on the wave's snapshot receives `--resume <wave>` in place of `--wave <wave>`, so an older snapshot cannot pick another wave; a wave whose id looks like a lot (`suite2`) cannot be named that way, so it is resumed with the current `dist/` and templates
|
|
29
34
|
cadence orchestrate --drop ccc:L29 [--wave <wave>] # from another terminal: take a lot out of the running wave
|
|
30
35
|
cadence orchestrate --stop-after-current [--wave <wave>] # from another terminal: let the running sessions finish, start nothing else
|
|
31
36
|
```
|
|
@@ -36,16 +41,16 @@ from (`--wave <id>` when several waves run from it), and write a request in the
|
|
|
36
41
|
`journal.log`. `--drop <project:lot>` (repeatable) takes a lot out of the wave: a lot not started yet never starts (no
|
|
37
42
|
`raf start`); a lot with a session running **lets that session finish** (it is not killed), then plays no other: it is
|
|
38
43
|
*handed back* (`retiré de la vague (--drop)`), its commits stay, and a lot that depended on it is handed back too. A lot
|
|
39
|
-
waiting for an answer (`question`) is handed back as well, at the end of the wave or on `--resume --drop`, instead of staying in
|
|
44
|
+
waiting for an answer (`question`) is handed back as well, at the end of the wave or on `--resume --wave <wave> --drop`, instead of staying in
|
|
40
45
|
question forever. A lot that is unknown or already finished is refused (exit 2). `--stop-after-current` lets the sessions running finish, starts no
|
|
41
46
|
other session nor lot (`arrêt demandé (--stop-after-current)`), and leaves the wave **interrupted** and resumable
|
|
42
|
-
(`--resume
|
|
47
|
+
(`--resume --wave <wave>`, which forgets the stop; `--continue` draws nothing more). Neither combines with lots to launch,
|
|
43
48
|
`--continue`, `--dry-run`, `--budget` or `--answer`, nor `--status`; without a live wave from that folder they are refused
|
|
44
|
-
(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
|
|
49
|
+
(exit 2). A stopped wave is no longer live, so to take a lot out of it use **`--resume --wave <wave> --drop <project:lot>`**: the lot is
|
|
45
50
|
handed back instead of being replayed (an unknown or finished lot is refused, exit 2, before anything runs). A lot taken out
|
|
46
51
|
this way is left out of the resume checks (repository lock, `.nvmrc` Node, dirty tree): only the lots that will be replayed can
|
|
47
52
|
refuse the resume, and a refusal writes nothing to `control.log`.
|
|
48
|
-
`--resume --stop-after-current` is refused (exit 2): resuming contradicts stopping.
|
|
53
|
+
`--resume --wave <wave> --stop-after-current` is refused (exit 2): resuming contradicts stopping.
|
|
49
54
|
|
|
50
55
|
**`--goal "…"` and `goal:` — the objective of the briefs when the title is stale (L258)**: the first line of the `{{objectif}}` of every
|
|
51
56
|
brief of a lot (and the title quoted in their first sentence) is the lot's title; when a change of scope made it stale, the lead no longer
|
|
@@ -60,7 +65,7 @@ has to delegate by hand. Three ways to give the goal, in this order of precedenc
|
|
|
60
65
|
The key is read for every lot of a launch, so it is the way to give a goal to several lots at once, on both plan kinds (cadence only reads the plan,
|
|
61
66
|
it never writes a goal into it). The notes and sub-tasks of the lot stay in the objective. The goal read at launch is kept in the state
|
|
62
67
|
of the lot and is the only source afterwards (briefs, `--dry-run`, table): a `--resume` replays the sessions with the goal of the launch, and
|
|
63
|
-
a `goal:` added or changed in the plan since is not read (to change it: `--resume --drop` the lot and launch it again). `--dry-run` prints
|
|
68
|
+
a `goal:` added or changed in the plan since is not read (to change it: `--resume --wave <wave> --drop` the lot and launch it again). `--dry-run` prints
|
|
64
69
|
`objectif donné : …` under the lot, and the table of the wave repeats it (`<project>:<lot> — objectif donné : …`) so the lead sees which
|
|
65
70
|
objective the sessions worked from.
|
|
66
71
|
The review and UX briefs carry it too, on a line `Goal of the lot (it takes precedence over the title in the plan): …`, and `code-reviewer` / `qa-reviewer` measure the commits against it, not against the stale title still in a read-only plan.
|
|
@@ -134,7 +139,7 @@ keeps its `/` (`maritime-atlas:Q4/accueil-4-ux12@haiku`); this holds for `--dry-
|
|
|
134
139
|
same for the lot folder and the dry-run briefs); ids without `/` are unchanged.
|
|
135
140
|
|
|
136
141
|
You choose the lots; the order is the order given (one queue per repository, as many repositories at the
|
|
137
|
-
same time as the session cap allows — 2 by default; the
|
|
142
|
+
same time as the session cap allows — 2 by default; the `orchestrate.worktrees: true` mode (on in cadence's own `cadence.yaml`, off where the key is absent) plays the lots of one repository in parallel, each in its own worktree — « One worktree per lot »). `@haiku|@sonnet|@opus` sets the model of the implementation and corrections of that
|
|
138
143
|
lot (default Sonnet; reviews are Opus, except the light review below; Haiku only when you write it, for a mechanical lot). Run it
|
|
139
144
|
in the background and read `--status`: it prints one line per transition and the final table.
|
|
140
145
|
|
|
@@ -159,7 +164,11 @@ usability (code only when the lot is not `visible`). Failing tests (reported red
|
|
|
159
164
|
|
|
160
165
|
**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`](mesures/ux-une-fois-2026-10-15.md).
|
|
161
166
|
|
|
162
|
-
**
|
|
167
|
+
**A project without `orchestrate.test` is flagged (L327)**: without the key the wave never runs the suite itself — only the review does, if it decides to (it ran 12 test files of a project on 2026-10-25 and a red test reached « ready »). `cadence orchestrate --dry-run` writes one line per repository, under the first lot of that repository: `<repo> · porte de tests absente : orchestrate.test n'est pas déclaré, la vague ne lancera pas la suite (seule la revue le fera si elle le décide) · commande probable : npm test (à déclarer en orchestrate.test)`, and [`cadence lead tour`](lead.md#lead-tour) writes the same line for every project of the folder. The probable command is read from the repository, never run: `npm test` when `package.json` has a `scripts.test` (the `npm init` placeholder « no test specified » does not count), `pytest` when `pyproject.toml` names pytest, `mvn test` when there is a `pom.xml`; with none of them the line carries no command. A `cadence.yaml` that cannot be read gives no such line (it has its own error). Declaring `test:` (and `build:`) in the project's `cadence.yaml`, once the command is known to be green, removes the line.
|
|
168
|
+
|
|
169
|
+
**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, so such a monorepo keeps the pre-L213 « tests rouges » outcome unless `orchestrate.build` is declared (the build is then played first in the baseline worktree, see below). 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`.
|
|
170
|
+
|
|
171
|
+
**The build is played before the tests (L333)**: the gate of the wave runs `orchestrate.build` first, then `orchestrate.test`, because tests that read the build output (end-to-end tests over `dist`) are red without it — on 2026-10-25 three projects had to declare `test: astro build && npm test` (13 tests out of 15 red without `dist`), which played the build twice on every pass. A red build hands the lot back as a single « build » finding and the tests are not run. The L213 baseline plays the build first in its throwaway worktree too, then the tests: without it the start sha, with no `dist`, would excuse a test wrongly or turn everything red. A build that fails at the start sha gives no baseline (the build says nothing about the tests there): the tests stay red as before, no comparison is guessed. Without `orchestrate.build` nothing changes. A project that wrote `build && test` into its `orchestrate.test` can drop the duplicate build.
|
|
163
172
|
|
|
164
173
|
**Review sized to the lot (L108)**: a lot whose `estimate` is ≤ `orchestrate.review.threshold` (0.25 day by default)
|
|
165
174
|
is *light*: one single review (Sonnet by default, `code-reviewer` agent, same criteria), no minors pass — the minors are
|
|
@@ -201,8 +210,7 @@ orchestrate:
|
|
|
201
210
|
the commits of the lot touch code without touching the document that describes it, the lot goes to a documentation pass
|
|
202
211
|
before its review: a `fix` session launched with the `doc-keeper` agent (`agents/doc-keeper.md`) and its own brief
|
|
203
212
|
(`fix-docs`), which lists the lines the program computed and asks for the documents to be brought up to date, nothing else. The
|
|
204
|
-
pass counts as one of the two correction passes and plays once per lot: if the librarian
|
|
205
|
-
ahead and carries the gap as before (one major finding per line). It does not play during the minors pass, nor when a
|
|
213
|
+
pass counts as one of the two correction passes and plays once per lot: if the librarian leaves the gap open — it commits no document, and has not acknowledged the gap with `raf docs-ok <lot> --reason "…"` (L317) — the lot is **handed back** (cause « documentation en retard (docs.sync) ») and the review does not play: a lot never ends ready while `raf check` reports a documentation gap. The same check runs at the end of the conforming review, for a lot whose librarian gate could not play (passes or budget used up). An acknowledged gap (`docs_ok:` on the lot, committed with the plan) is no gap: no pass, no finding in the review brief. In a lot's worktree (`orchestrate.worktrees`) only the program writes the plan: the librarian's brief there does not ask it to run `raf docs-ok` — it makes no commit and says why under "choix", the lot is handed back and the lead acknowledges in the main checkout; an acknowledgement the lead committed before the wave is read from the lead's plan (L320). It does not play during the minors pass, nor when a
|
|
206
214
|
correction pass is already used (its pass would take the last one), nor when the lot budget no longer pays it and the review after
|
|
207
215
|
it (a warning `bibliothécaire non appelé` says so each time; the review goes ahead). Without `docs.sync`, or with no gap: no pass.
|
|
208
216
|
When the project's `cadence.yaml` declares `docs.articles` (the same key as the article refreshed after a delivery, see [news](news.md#an-article-elsewhere-that-follows-the-delivery)), the brief names those
|
|
@@ -232,7 +240,7 @@ what the lot asks for (another lot, or a correction, may have done it already).
|
|
|
232
240
|
with proofs), `partiel` or `non`: on `oui` no implementation session is opened and the lot is handed back
|
|
233
241
|
(`livrable déjà présent : <résumé> (<preuves>)`) for the lead to drop or close it; on `partiel` the finding goes into the
|
|
234
242
|
implementation brief and a warning; on `non` — or an unreadable report, which only adds a warning — the wave goes on. A
|
|
235
|
-
lot that already has commits is never pre-checked (resuming it is legitimate), and neither is a lot **whose title and `goal:` cite neither a finding nor another lot** (L288, L292): the pre-check runs only when the title, the `goal:` of the lot (or the goal given at launch) contains the word `constat` (any ending), `revue`, `review` or `audit` (any case, plural allowed; whole words, so `qa-reviewer` and `review-small` do not count), or a whole plan lot id: an `L<number>` earlier than the lot (`L12` in the title of `L30`; `L30` itself, a later lot or `L120` for `L12` do not count), any other id of the plan, whatever the lot (`Q-CAN5`, `B28.1`, `Q4/qa-expectations`; `B28.1` is not `B28.10`), or a lot of another project written `project:L<number>` (`cadence:L68`). Measured on 24-10: 13.4 M tokens (7.1 %) of pre-check for 10 `oui` and 20 `partiel` out of the sessions read, hence the restriction. The restriction costs real `oui`: 7 of the 17 measured (L171, L68, Q-LBL3, Q-NOW5, `Q4/dashboard-21-mesure-prod`, `Q4/qa-expectations`, `Q4/qa-expectations-nouveautes`) have a title that cites nothing, against about a third of the pre-checks avoided (about 20 k tokens each). With `orchestrate.precheck: local` (below) the criterion is therefore dropped (L296): every lot with no commit is pre-checked, on the local model, off the quota. The local/Sonnet agreement on the 37 `oui` and `partiel` verdicts could not be replayed (the repositories now hold every deliverable, so any replay answers `oui`): the safety nets of `local` stay (a local `oui` is confirmed by Sonnet) and the key stays opt-in. The verdict the pre-check reached (`oui`, `partiel`, `non`; an unproven `oui` is recorded `partiel`) is kept in the lot's journal (`precheckVerdict`; for a journal older than L288, `cadence perf` reads it back from the journal: `oui` for a lot handed back `livrable déjà présent`, `partiel` when the lot carries the pre-check finding, `non` for any other lot whose `precheck` step succeeded), and `cadence perf` prints the share of `oui` per window ([perf](perf.md)): change the key or the criterion, then compare the current window with the previous one. `orchestrate.precheck: false` turns it off.
|
|
243
|
+
lot that already has commits is never pre-checked (resuming it is legitimate), and neither is a lot **whose title and `goal:` cite neither a finding nor another lot** (L288, L292): the pre-check runs only when the title, the `goal:` of the lot (or the goal given at launch) contains the word `constat` (any ending), `revue`, `review` or `audit` (any case, plural allowed; whole words, so `qa-reviewer` and `review-small` do not count), or a whole plan lot id: an `L<number>` earlier than the lot (`L12` in the title of `L30`; `L30` itself, a later lot or `L120` for `L12` do not count), any other id of the plan, whatever the lot (`Q-CAN5`, `B28.1`, `Q4/qa-expectations`; `B28.1` is not `B28.10`), or a lot of another project written `project:L<number>` (`cadence:L68`). Measured on 24-10: 13.4 M tokens (7.1 %) of pre-check for 10 `oui` and 20 `partiel` out of the sessions read, hence the restriction. The restriction costs real `oui`: 7 of the 17 measured (L171, L68, Q-LBL3, Q-NOW5, `Q4/dashboard-21-mesure-prod`, `Q4/qa-expectations`, `Q4/qa-expectations-nouveautes`) have a title that cites nothing, against about a third of the pre-checks avoided (about 20 k tokens each). With `orchestrate.precheck: local` (below) the criterion is therefore dropped (L296): every lot with no commit is pre-checked, on the local model, off the quota. The local/Sonnet agreement on the 37 `oui` and `partiel` verdicts could not be replayed (the repositories now hold every deliverable, so any replay answers `oui`): the safety nets of `local` stay (a local `oui` is confirmed by Sonnet) and the key stays opt-in. The verdict the pre-check reached (`oui`, `partiel`, `non`; an unproven `oui` is recorded `partiel`) is kept in the lot's journal (`precheckVerdict`; for a journal older than L288, `cadence perf` reads it back from the journal: `oui` for a lot handed back `livrable déjà présent`, `partiel` when the lot carries the pre-check finding, `non` for any other lot whose `precheck` step succeeded), and `cadence perf` prints the share of `oui` per window ([perf](perf.md)): change the key or the criterion, then compare the current window with the previous one. **A lot created after the last code commit is not pre-checked unless it cites a lot that has commits** (L322, local or Sonnet): such a lot has nothing to find, everything the repository holds is older than the lot, so no other lot can have delivered it. The skip is narrowed to lots whose title and `goal:` (and the goal given at launch) cite no other lot that already has commits of its own, with the same recognition of lot ids as the criterion above (an earlier `L<number>`, any other plan id) and a cited lot « has commits » when at least one commit of work (not a plan-only one) cites it. A lot of another project cited as `project:L<number>` keeps the pre-check too (its commits are not read). Why: a « suite de revue L<n> » written right after the last fix commit of `L<n>` lists findings that the correction passes of `L<n>` may already have settled before the new lot existed; replayed on the journals since 24-10, the skip would have fired on 5 such lots (L292, L302, L304, L305, L308), two of them (L304, L305) with a real `partiel` verdict. The program dates the lot by the commit that added its id to the plan (`git log -G`, following renames), bounded by the lot's `created:` day (a plan move or migration does not make an old lot young); the last code commit is the last one that does not touch only the plan files (the same test as `raf check`: a plan-only commit is not code). The pre-check session is skipped, the lot goes straight to its first write step, and the lot's journal says it (`contrôle préalable sauté : lot créé après le dernier commit de code du dépôt (<sha>) : rien à retrouver`; `--dry-run` prints the same line and no `precheck` step). The check stays when in doubt: a lot that cites a lot with commits (or a lot of another project), a lot that works in neighbouring repositories (`repos:`), an id not found in the plan history, a repository with no code commit, equal instants. Older lots keep the pre-check. `orchestrate.precheck: false` turns it off.
|
|
236
244
|
|
|
237
245
|
## The spec step of a lot with `screen:`
|
|
238
246
|
|
|
@@ -252,7 +260,7 @@ the first implementation: `precheck → spec → maquette → accept → impleme
|
|
|
252
260
|
`#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 a fresh `spec` session whose brief names the points, within the lot's session budget: `orchestrate.maquetteReviews` sessions (3 by default) when the mockup review is on (see [The mockup review](#the-mockup-review-before-any-code)), 2 otherwise; if the spec is still not valid on the last one the lot is handed back (`spec non conforme après 3 passes : …`, `après 2 passes` with `orchestrate.maquette: false`).
|
|
253
261
|
- **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,
|
|
254
262
|
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
|
|
255
|
-
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, **and the program itself turns the rules it asked about into `statut: sourcee`** (L297) — `source: réponse du lead, <date>, vague <id>`, no `si_faux`, the sheet committed alone as `spec(<lot>): R3 sourcée par la réponse du lead (orchestrate <wave>)` — before that session starts, so a session that is lost or writes nothing no longer leaves the rule an `hypothese` for the sheet review (seen on 24-10: L279 handed back « fiche non conforme » for R27 although the lead had answered). Only the `statut`, `source` and `si_faux` lines of the rule are edited, the rest of the sheet is kept byte for byte (L302: `[R2]` stays `[R2]`, comments and folded lines stay; a rule written in flow style `{id: R1, …}` hands the lot back instead of being edited). An answer that deletes or changes the rule (« non, supprime R3 », « change en X »: a word of refusal or correction such as `non`, `pas`, `supprime`, `change`, `plutôt`, `remove`, `instead`) sources nothing: the rule stays an `hypothese` for the resumed `spec` session, which applies the answer, and if it does not, the question goes back to the lead. The session only rewrites a rule the lead corrected; only the rules named by the program's question are touched, and an answer to a question of the session itself leaves the sheet alone. If the commit is refused (a hook), the lot is handed back saying so, with no `spec` session played. Every other hypothesis (wording, order of fields, error state, spacing) stays written in the sheet.
|
|
263
|
+
judgement; a keyword right after a negation — « rien d'irréversible », « sans suppression », « pas de paiement » — does not count); `--resume --wave <wave> --answer <lot> "…"` gives the answer to the resumed `spec` session, **and the program itself turns the rules it asked about into `statut: sourcee`** (L297) — `source: réponse du lead, <date>, vague <id>`, no `si_faux`, the sheet committed alone as `spec(<lot>): R3 sourcée par la réponse du lead (orchestrate <wave>)` — before that session starts, so a session that is lost or writes nothing no longer leaves the rule an `hypothese` for the sheet review (seen on 24-10: L279 handed back « fiche non conforme » for R27 although the lead had answered). Only the `statut`, `source` and `si_faux` lines of the rule are edited, the rest of the sheet is kept byte for byte (L302: `[R2]` stays `[R2]`, comments and folded lines stay; a rule written in flow style `{id: R1, …}` hands the lot back instead of being edited). An answer that deletes or changes the rule (« non, supprime R3 », « change en X »: a word of refusal or correction such as `non`, `pas`, `supprime`, `change`, `plutôt`, `remove`, `instead`) sources nothing: the rule stays an `hypothese` for the resumed `spec` session, which applies the answer, and if it does not, the question goes back to the lead. The session only rewrites a rule the lead corrected; only the rules named by the program's question are touched, and an answer to a question of the session itself leaves the sheet alone. If the commit is refused (a hook), the lot is handed back saying so, with no `spec` session played. Every other hypothesis (wording, order of fields, error state, spacing) stays written in the sheet.
|
|
256
264
|
The agent can also ask a question itself in its report.
|
|
257
265
|
- **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
|
|
258
266
|
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).
|
|
@@ -332,7 +340,7 @@ session, `accept` (L173): `precheck → spec → maquette → accept → impleme
|
|
|
332
340
|
- **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
|
|
333
341
|
(`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
|
|
334
342
|
file, deletes it, creates one outside the accept step, or modifies a locked test hands the lot back (`verrou des tests d'acceptation : …`) — for a modified test the message ends with `; premier commit fautif : <sha7> <subject>`, the oldest commit after the reference (red commit, or the head before the lot) that touched the sealed path (a deletion or a rename included, renames not followed), and names no commit when the deviation is only in the working tree (L231) — 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 **a commit that cites the lot in progress amends the sheet of the screen and comes after the red commit** (a strict descendant of it; the commit may have been made before the wave, and once one exists the exception covers the whole acceptation: the tests may change in the same commit as the sheet, in a later one or in a following session, a `fix` after the `implement` for instance, L231): the sheet is what evolves the rules, and tests and sheet change together. A sheet amended by another lot, by a commit that cites no lot, by a commit that is not after the red commit, or only in the working tree (not committed) opens nothing. 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).
|
|
335
|
-
- **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`), **while a wave that is not `done` (interrupted, suspended, or still alive with a lot waiting on a question) keeps open any lot of this repository that is still `doing` in this repository's plan (L238)** — the resealed lot, the `--porteur` or any other lot of the repository: the lock hands back every open lot of the repository, so a reseal committed in the meantime was handed back to any of them as the « first offending commit » of the lock (ol-companion L172, then L173). Open means any non-final state in the wave (a question waiting for its `--answer`, a lot suspended by the quota or the budget, a queued lot, a session running): a stopped wave holds no lock, but its `--resume` replays those lots. The filter is by repository (the repository of the lot's project, not its neighbour repositories: lot ids belong to each plan, so a finance-tracker L172 never blocks an ol-companion L172) and by plan: a lot that is `done` or absent in the plan never refuses, so an old interrupted wave whose lots are long delivered blocks nothing, and neither does a `done` wave. The error is a single message that names **every** wave concerned, with its launch folder and its open lots, the wave that holds the repository lock included (alive: while it has an open lot the lock refuses first, so the same message lists it together with the stopped waves of the repository, not the bare « orchestration en cours » line `cadence deliver` gives), and gives for each stopped wave ONE command, runnable as it stands, with a `--drop` per open lot of the repository in that wave, the queued ones that are not `doing` in the plan included (L239: `--resume` would relaunch them, and the lot would take the lock back and find the tree dirty with the corrected test): `cd <launch folder> && cadence orchestrate --resume <wave> --drop <project>:<lot> --drop <project>:<lot>` (`--resume` replays the whole wave except the dropped lots, so one command per lot would start sessions on the others; it looks for the wave in the folder it is run from only); a wave that is still alive is not called suspended, the error says it is running and is to be waited for. A repository reached by another path (a worktree, a symbolic link) is the same repository: the `.git` directories are compared by their real path. The waves read are those of `.cadence/runs/` of the repository root, of the plan's folder and of every folder above them (a wave keeps its state where `cadence orchestrate` was launched, usually the parent folder of the projects), 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` sits in a wave that is not `done`, which refuses the reseal (L238): answer it first (`--resume --answer`, the lot then goes on or is handed back) or close it (`--resume --drop`), reseal afterwards if the lot is handed back (a rewrite of `acceptation.json` without a `resceaux` entry stays a violation of the lock); 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, the `--porteur` or any delivered lot of the plan, status `done`, L229: the living sheet is the reference; one cited delivered lot among several is enough), 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` or a delivered lot (`la fiche … a été modifiée depuis le commit rouge … par un commit qui ne cite pas <lot> : <sha>`; for a delivered lot `… ne cite ni <lot>, ni le porteur <porteur>, ni un lot livré : <sha> (cite : <lots ou aucun lot>) — faire citer le porteur ou un lot livré par ce commit (le réécrire avant qu'il soit poussé) ou, sinon, rescelle à la main avec le lead`), 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, the carrier or another delivered lot; 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.
|
|
343
|
+
- **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`), **while a wave that is not `done` (interrupted, suspended, or still alive with a lot waiting on a question) keeps open any lot of this repository that is still `doing` in this repository's plan (L238)** — the resealed lot, the `--porteur` or any other lot of the repository: the lock hands back every open lot of the repository, so a reseal committed in the meantime was handed back to any of them as the « first offending commit » of the lock (ol-companion L172, then L173). Open means any non-final state in the wave (a question waiting for its `--answer`, a lot suspended by the quota or the budget, a queued lot, a session running): a stopped wave holds no lock, but its `--resume` replays those lots. The filter is by repository (the repository of the lot's project, not its neighbour repositories: lot ids belong to each plan, so a finance-tracker L172 never blocks an ol-companion L172) and by plan: a lot that is `done` or absent in the plan never refuses, so an old interrupted wave whose lots are long delivered blocks nothing, and neither does a `done` wave. The error is a single message that names **every** wave concerned, with its launch folder and its open lots, the wave that holds the repository lock included (alive: while it has an open lot the lock refuses first, so the same message lists it together with the stopped waves of the repository, not the bare « orchestration en cours » line `cadence deliver` gives), and gives for each stopped wave ONE command, runnable as it stands, with a `--drop` per open lot of the repository in that wave, the queued ones that are not `doing` in the plan included (L239: `--resume` would relaunch them, and the lot would take the lock back and find the tree dirty with the corrected test): `cd <launch folder> && cadence orchestrate --resume --wave <wave> --drop <project>:<lot> --drop <project>:<lot>` (`--resume` replays the whole wave except the dropped lots, so one command per lot would start sessions on the others; it looks for the wave in the folder it is run from only); a wave that is still alive is not called suspended, the error says it is running and is to be waited for. A repository reached by another path (a worktree, a symbolic link) is the same repository: the `.git` directories are compared by their real path. The waves read are those of `.cadence/runs/` of the repository root, of the plan's folder and of every folder above them (a wave keeps its state where `cadence orchestrate` was launched, usually the parent folder of the projects), 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` sits in a wave that is not `done`, which refuses the reseal (L238): answer it first (`--resume --wave <wave> --answer`, the lot then goes on or is handed back) or close it (`--resume --wave <wave> --drop`), reseal afterwards if the lot is handed back (a rewrite of `acceptation.json` without a `resceaux` entry stays a violation of the lock); 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, the `--porteur` or any delivered lot of the plan, status `done`, L229: the living sheet is the reference; one cited delivered lot among several is enough), 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` or a delivered lot (`la fiche … a été modifiée depuis le commit rouge … par un commit qui ne cite pas <lot> : <sha>`; for a delivered lot `… ne cite ni <lot>, ni le porteur <porteur>, ni un lot livré : <sha> (cite : <lots ou aucun lot>) — faire citer le porteur ou un lot livré par ce commit (le réécrire avant qu'il soit poussé) ou, sinon, rescelle à la main avec le lead`), 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, the carrier or another delivered lot; 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.
|
|
336
344
|
- **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 …`;
|
|
337
345
|
`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.
|
|
338
346
|
- **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. A sealed green one (`vert: true`) also skips the step when its seal no longer holds (L233); a green one without `empreintes`, with `empreintes: {}`, or whose `empreintes` lack one of the listed files (copied from another file or lot) proves nothing and `accept` runs again (the contents are not compared here, only the presence of one fingerprint per listed file). 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).
|
|
@@ -370,6 +378,10 @@ stops the accidental access, not a deliberate workaround; making the file unread
|
|
|
370
378
|
the project's own `test`, `build` and `ux.command`, which the program launches. A lot that really needs the cluster is run in a
|
|
371
379
|
project with `orchestrate.prodAccess: true`: the restriction is lifted for all its sessions (default `false`).
|
|
372
380
|
|
|
381
|
+
**Cadence's own pre-check runs on Sonnet (L328, 2026-10-25).** `cadence.yaml` sets `orchestrate.precheck: true`, no longer `local`. Measured on 25 October: the local
|
|
382
|
+
model failed on about one lot in three since 23 October and, when it answered, said `oui` wrongly on L275, L326 and L327 (Sonnet: `non` / `partiel`), 2 to 3 minutes each;
|
|
383
|
+
Sonnet decides in about 16 s for about 20 k tokens.
|
|
384
|
+
|
|
373
385
|
`orchestrate.precheck: local` (L146) runs the pre-check on the local model of the dev machine instead of Sonnet: the
|
|
374
386
|
orchestrator launches `claude-local` with the same brief, the `precheck-reader` prompt and tools, no quota used and nothing counted in the
|
|
375
387
|
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
|
|
@@ -380,7 +392,7 @@ Without such a binary every local check fails and is replayed on Sonnet: keep `t
|
|
|
380
392
|
down, timeout, no structured report) or an unreadable report is replayed on Sonnet with a warning, and a local `oui` — the
|
|
381
393
|
only answer that hands the lot back without an implementation — is confirmed by Sonnet before it counts. A local `partiel`
|
|
382
394
|
or `non` is taken as is. The default stays `true` (Sonnet): switch to `local` only after comparing verdicts and durations
|
|
383
|
-
against Sonnet on a few lots, and go back to `true` the first time the local model gets a `partiel` wrong. `--dry-run`
|
|
395
|
+
against Sonnet on a few lots, and go back to `true` the first time the local model gets a `partiel` wrong. Measured on 23–25-10 over 32 lots: the local pre-check failed on 10 (7 « Autocompact is thrashing », the 64 k window of `qwen3-coder:30b` filled by a plan of several hundred kB; 3 tool calls written as text, `<function=Bash>`, with no structured output), each time replayed in full on Sonnet. L322 therefore (1) gives the local session the lot itself in its brief (title, goal, last five notes, sub-tasks) with the order not to open the plan file and to keep every read small, where Sonnet still reads the plan; (2) takes a session that ends without a structured output (a tool call written as text included) as a failure at once — a local session is never relaunched to format its answer — and (3) **sets the local model aside for the rest of the wave** after the first failure of a local session or the first unreadable local report: the next lots go straight to Sonnet, with `contrôle préalable : modèle local écarté pour la vague (<cause>), Sonnet directement` in their journal. A local `oui` is still confirmed by Sonnet. `cadence perf` measures the pre-check per lot ([perf](perf.md)). `--dry-run`
|
|
384
396
|
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
|
|
385
397
|
session (the fallback, or the confirmation of a local `oui`).
|
|
386
398
|
|
|
@@ -427,7 +439,7 @@ resumed wave, not from the current `dist/`; a wave without a snapshot (started b
|
|
|
427
439
|
package and a warning. One more exception (L223): a wave whose snapshot predates L223 (its `dist/orchestrate/command.js`
|
|
428
440
|
has no `lotBudgets`) resumed with `--budget <project>:<lot>=+N` runs on the current `dist/` and templates, with a warning
|
|
429
441
|
on the error output, because that snapshot would refuse the option as an invalid budget; the sessions of such a resume still
|
|
430
|
-
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.
|
|
442
|
+
get `<wave>/tool/bin` first in their `PATH` (see below), so the `raf` and `cadence` they call are the snapshot's. Before relaunching the snapshot, `--resume` checks that `tool/node_modules` still resolves (L330): that link points to the `node_modules` of the copy that launched the wave, and `npm run service` may have purged that copy since (a suspended wave outlives the few most recent versions put in service). A dangling link is relinked to the `node_modules` of the current package (the copy in service), the repair is written to the wave's `journal.log` and to the error output, and the resume goes on; when the current package cannot resolve its dependencies either, the resume is refused with a message (exit code 2), no stack trace. The copy is removed with the wave folder. The wave's pid in `--status` is the relaunched process.
|
|
431
443
|
The sessions of the wave also get `.cadence/runs/<wave>/tool/bin` (the `raf` and `cadence` entries of the snapshot) **first in their
|
|
432
444
|
`PATH`**, ahead of the per-project Node below and of the usual `PATH`: the `raf` a reviewer runs (`raf commits`…) is the
|
|
433
445
|
snapshot's, not the installed one. A wave without a snapshot leaves the `PATH` as it is.
|
|
@@ -456,16 +468,19 @@ npm run service -- <sha> # [--root dir] [--bin-dir dir] [--repo dir]
|
|
|
456
468
|
|
|
457
469
|
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)
|
|
458
470
|
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
|
|
459
|
-
`service` and the two links only once the build succeeded (atomic renames): a failed install changes nothing.
|
|
460
|
-
rollback
|
|
471
|
+
`service` and the two links only once the build succeeded (atomic renames): a failed install changes nothing. It then purges the old copies (L330): it keeps the copy in service and the **3 put in service before it**
|
|
472
|
+
(a rollback is `npm run service -- <old sha>`; the order is the order of the puts in service, not the install date or the age), and removes the others (`git worktree remove`, then `prune`) —
|
|
473
|
+
except a copy that is still needed, which stays and is said: a copy that a live process uses (its working directory, its binary or an argument of its command line, read in `/proc` and resolved with `realpath`; and the copy a `node_modules` link points to, found on the folders above those paths), and a copy that the snapshot of a wave **not finished** points to (`tool/node_modules` of a suspended, interrupted or running wave, status other than `done`), even with no process alive. The output ends with the purge:
|
|
474
|
+
`purge : 37 copies retirées, 2,3 Go libérés` (`aucune copie retirée` when there was nothing to remove, `; 1 gardée, un processus ou une vague non terminée l'utilise encore` for a copy spared). The copies grew without bound before (41 copies, 2.5 GB on 2026-10-25).
|
|
475
|
+
The waves that count are those of the launch folders the script knows (`<folder>/.cadence/runs/<wave>/wave.json`): the repository itself and the working directory of every wave the orchestrator's registry still lists (`~/.cadence/orchestrate/waves/<pid>.json`; the registry drops a dead wave when another registers, so it does not know suspended waves by itself). There is no search over the whole disk: a suspended wave launched from another folder is not protected here, and `--resume` repairs its snapshot instead (above). A wave never marked `done` keeps its copy until it is, or until its folder is removed. A failure of the purge (unreadable copy, busy file) once `service` and the links have switched is a warning on the error output (`avertissement : purge des anciennes copies interrompue …`): the new version is in service, the fingerprint is printed, the exit code is 0, the next put in service tries again. The first run is also what installs the links in `~/.local/bin`. It prints the fingerprint of the copy
|
|
461
476
|
(bin, dist, package.json).
|
|
462
477
|
|
|
463
478
|
## One worktree per lot (`orchestrate.worktrees`, L279)
|
|
464
479
|
|
|
465
|
-
> **
|
|
466
|
-
>
|
|
467
|
-
>
|
|
468
|
-
>
|
|
480
|
+
> **On by default in cadence (L320, 2026-10-25).** After the trials L310 and L316 (−45 % and −22 % of wall time, three automatic integrations without incident) cadence's own
|
|
481
|
+
> `cadence.yaml` sets `orchestrate.worktrees: true`. The key is the whole switch: the guard that refused it without `CADENCE_EXPERIMENTAL_WORKTREES=1` is gone and the variable is no longer read,
|
|
482
|
+
> so a resumed wave (`--resume`, `--integrate`, `--replay`, `--continue`) needs nothing more than its configuration or its `cadence/<wave>/…` branch. The other projects keep the default (key absent
|
|
483
|
+
> or `false`: one lot at a time, in the repository) until the lead decides otherwise.
|
|
469
484
|
|
|
470
485
|
By default a wave plays the lots of one repository one after the other, in the repository itself: a lot with four correction passes
|
|
471
486
|
holds the whole queue. With `orchestrate: { worktrees: true }` in the project's `cadence.yaml`, two independent lots of the same repository
|
|
@@ -506,9 +521,25 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
506
521
|
fast-forward (the replayed checks and the short review can last) and right before the plan is written: a lead moved to another branch, or whose
|
|
507
522
|
tree got dirty, in the meantime gets neither the fast-forward nor a plan commit, and the lot is handed back with `next: integrate` (L299);
|
|
508
523
|
2. the lot's branch is rebased on the tip of the repository's branch (`git rebase`); a conflict is **aborted** (`git rebase --abort`), the lot
|
|
509
|
-
is handed back with the files in conflict, its worktree and branch untouched, and no session tries to resolve it
|
|
510
|
-
|
|
511
|
-
|
|
524
|
+
is handed back with the files in conflict, its worktree and branch untouched, and no session tries to resolve it. **One exception** (L314): two
|
|
525
|
+
parallel lots that each add an entry under `## [Unreleased]` of `CHANGELOG.md` (what `docs.sync` imposes on every lot) always collide, so a commit
|
|
526
|
+
whose only conflicting file is `CHANGELOG.md` and whose conflict is a **pure addition** is resolved by the program: union per section, one title per
|
|
527
|
+
section (duplicated titles are merged), sections in the Keep a Changelog order (`Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`, any
|
|
528
|
+
other title after), the entries of the repository's branch first, then those of the lot; the rest of the file follows the side that changed it. **A lot may rewrite its own entry** (L323): an
|
|
529
|
+
entry of the replayed commit's parent that was not in the `CHANGELOG.md` at the branch's starting point (the merge-base with the repository's
|
|
530
|
+
branch) was added by an earlier commit of the same lot, so the replayed commit may reword or remove it — its version wins, the earlier one is dropped
|
|
531
|
+
from the repository's side — while an entry that was already there at the starting point, reworded, stays a conflict by hand. The
|
|
532
|
+
journal says `<project>:<lot> CHANGELOG.md : conflit d'ajout pur résolu par union sous [Unreleased] (<n> commit(s))`. Anything else — an entry that
|
|
533
|
+
was already there removed or reworded, free text under `[Unreleased]`, the rest of the
|
|
534
|
+
file changed on both sides, another file in conflict with it — aborts the rebase and hands the lot back as above. The rebased commits differ, so
|
|
535
|
+
steps 3 and 4 apply: tests replayed, and a short review since the patch-id changed;
|
|
536
|
+
3. if the rebase changed the commits, the build and the tests (`orchestrate.build`, `orchestrate.test`, in that order, L333) are run again at the rebased tip, a red that
|
|
537
|
+
was not already red at the lot's base sha (L213) hands the lot back and leaves the repository's branch alone. **A red is played once more before the lot is
|
|
538
|
+
handed back** (L328): on 2026-10-25 L326 was handed back for a load-induced red while L325 was running, and the same tip replayed alone was green (3559
|
|
539
|
+
tests). Green the second time: the lot goes on, the journal says `<project>:<lot> tests rouges au sommet intégré, relancés une fois (…)` and the lot
|
|
540
|
+
gets a warning `rouge au sommet intégré, vert à la relance`. Red twice: handed back with the result of the second run. **The refusal names the red
|
|
541
|
+
tests** (up to ten, then `… (+n)`), or the end of the output when the runner's output names none; it used to quote only `npx vitest run en échec
|
|
542
|
+
(code 1) :`;
|
|
512
543
|
4. the patch-id of the lot's diff (`git patch-id --stable`, work commits only) is compared before and after the rebase. **Same patch-id**: the
|
|
513
544
|
reviewed diff is the integrated diff, the verdict is **carried over** to the integrated sha, with a `reporte: { de, vers, patchId }` trace in the plan
|
|
514
545
|
(and in the journal: `<project>:<lot> verdict reporté : <old sha> → <new sha> (patch-id identique)`); no new review. **Different patch-id** (context
|
|
@@ -523,12 +554,12 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
523
554
|
and the advance are skipped and the verdict is written once.
|
|
524
555
|
- **A lot handed back at the integration step** (lock held for more than 10 minutes, repository's branch changed, tracked file modified, rebase conflict,
|
|
525
556
|
red tests, blocking finding of the short review, plan write refused) keeps `next: integrate`, its worktree and its branch. **Only
|
|
526
|
-
`--resume <wave> --integrate <project:lot>` replays it** (repeatable): a plain `--resume`, `--drop`, `--answer` or `--budget` never does. The lot
|
|
557
|
+
`--resume --wave <wave> --integrate <project:lot>` replays it** (repeatable): a plain `--resume`, `--drop`, `--answer` or `--budget` never does. The lot
|
|
527
558
|
restarts at `suspended`, step `integrate` (`<project>:<lot> → suspended — intégration reprise (--resume --integrate)`), and the rebase and the checks run
|
|
528
559
|
again — after a conflict the lead resolves it by hand in the worktree and commits, otherwise the lot is handed back again.
|
|
529
560
|
**A lot handed back on a refused plan write outside the integration** (the fiche's hypothesis notes, or the minor findings of the fiche review: the repository
|
|
530
561
|
is not on its start branch, holds a modified tracked file or the lock stayed held) keeps the step it had reached (`next`, e.g. `implement` or `fiche`) and its
|
|
531
|
-
worktree, and is replayed the same way by the general option `--resume <wave> --replay <project:lot>` (lead's decision of 24 October: the integration option,
|
|
562
|
+
worktree, and is replayed the same way by the general option `--resume --wave <wave> --replay <project:lot>` (lead's decision of 24 October: the integration option,
|
|
532
563
|
generalised and renamed; `--integrate` keeps meaning the integration step only). The lot restarts at `suspended` on that step
|
|
533
564
|
(`<project>:<lot> → suspended — écriture du plan reprise à l'étape <step> (--resume --replay)`): the notes are written again without duplicates (a review of the
|
|
534
565
|
fiche is replayed and counted once). `--replay` is refused for a lot handed back in any other way, with the same exit code and rules as `--integrate`. Refused (exit 2, before
|
|
@@ -537,16 +568,16 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
537
568
|
review, tests or budget has `next` empty and takes the recovery path below), and, like any resume, a repository with a modified tracked file.
|
|
538
569
|
- **A lot handed back outside the integration step** (non-compliant review, red tests, `HEAD` moved, plan touched, acceptance lock, quota…) keeps its worktree and its
|
|
539
570
|
branch `cadence/<wave>/<project>--<lot>` but is not replayed by `--resume`. Its recovery path (L301) is **a new wave of the same lot** (`cadence orchestrate <project>:<lot>`):
|
|
540
|
-
with the worktree mode on (`
|
|
571
|
+
with the worktree mode on (`orchestrate.worktrees: true`), the new wave looks for the **most recent** (creation time noted by cadence) recognised branch `cadence/<other wave>/<project>--<lot>`
|
|
541
572
|
of a past wave that carries unintegrated commits and started from the repository's current branch, renames it `cadence/<this wave>/<project>--<lot>` (git keeps the cadence config
|
|
542
573
|
with it), moves its worktree to this wave's folder and plays the lot there, so the sessions start from the work already done. The journal says it
|
|
543
574
|
(`orchestrate : <project>:<lot> : branche cadence/<wave>/… d'une vague précédente reprise (N commit(s) non intégré(s))`). Nothing is recovered when the branch is integrated,
|
|
544
|
-
started from another branch, is not cadence's, or belongs to a live wave; the lot then starts afresh as before. A branch whose worktree holds a modified tracked file (the same
|
|
575
|
+
started from another branch, is not cadence's, or belongs to a live wave; the lot then starts afresh as before. A lot that recovers its branch **never goes through the pre-check** (L311: its own commits would make the pre-check answer « livrable déjà présent » and hand the lot back with the major of its last review uncorrected): it takes over the findings of the last review recorded by the wave that handed it back and starts at the `fix` step with them, or at the review when that review left none. That starting correction **counts as a pass** (like any `fix` step: the verdict and the table say « 1 passe(s) de correction » after it, and « Two passes at most » bounds the total, recovery included). A lot handed back before its first review (no review step, no finding, or an unreadable state) skips the pre-check too, but starts at the writing step (spec, acceptance or implementation, as a new lot would). If such a lot is handed back for budget with red tests before any correction pass was played (L315), the cause the lead reads is « tests rouges après la dernière revue de la branche reprise » for a recovered lot already reviewed, and « tests rouges après l'implémentation » for a lot never reviewed, recovered or not. A lot counts as reviewed only through a `review*` step or a finding from a review (`code`): the `tests` findings of a lot handed back for red tests are not a review (L318). A branch whose worktree holds a modified tracked file (the same
|
|
545
576
|
guard as `--resume`), or one of whose commits modifies the plan or a file generated with it, is not recovered either (it would end in an integration conflict): the journal says why
|
|
546
577
|
(`… d'une vague précédente non reprise : <cause>`), the lot starts afresh and the kept branch stays untouched for the lead to clean or integrate by hand. The same refusal, with git's own message as the cause,
|
|
547
578
|
answers a `git worktree move` or `git branch -m` that fails (a locked worktree for instance): the move is undone and nothing is renamed. A lot handed back **at the integration step** (`next: integrate`) never takes this path: its
|
|
548
|
-
branch keeps its own wave's name and is replayed by `--resume --integrate`, so a kept branch of another wave is not renamed in its place. The recovery path needs the worktree mode on (`
|
|
549
|
-
|
|
579
|
+
branch keeps its own wave's name and is replayed by `--resume --integrate`, so a kept branch of another wave is not renamed in its place. The recovery path needs the worktree mode on (`orchestrate.worktrees: true`): without the
|
|
580
|
+
key a new wave leaves every kept branch where it is. The branch of a lot of a wave started at the repository root (`.` as project) is `cadence/<wave>/_--<lot>` (the leading dot of the project becomes `_`).
|
|
550
581
|
**Integrating by hand** (a refused branch, or a lead who prefers to): in the kept worktree (`cadence orchestrate --status` lists it), (1) `git rebase <repository's branch>` and settle the conflicts; (2) run the project's tests and
|
|
551
582
|
build there; (3) from the repository, `git merge --ff-only <branch>` (the branch of the repository must not have moved since the rebase: otherwise rebase again); (4) write the verdict of the lot in the plan (by
|
|
552
583
|
the lead, never by a session); (5) `cadence orchestrate --remove-worktree <project>:<lot> [--wave <wave>]`, which removes the worktree and the branch now that all its commits are integrated.
|
|
@@ -627,7 +658,7 @@ the name `repos`). The effect, for `cadence orchestrate`:
|
|
|
627
658
|
review hands the lot back, like one in the project.
|
|
628
659
|
- **Out of scope**: `raf check` does not audit the neighbours, and their delivery (push, CI, deploy) stays with the lead.
|
|
629
660
|
|
|
630
|
-
**What stays with you**: choosing the lots, the questions raised (`--resume --answer
|
|
661
|
+
**What stays with you**: choosing the lots, the questions raised (`--resume --wave <wave> --answer`, the command printed under the question), re-verifying
|
|
631
662
|
after the wave (`git log`, tests, `raf check`), `raf done`, **`raf ux`** (the orchestrator reports the UX
|
|
632
663
|
verdict and screenshots, it does not record it), the push and the deliveries, one project at a time.
|
|
633
664
|
|
|
@@ -643,7 +674,7 @@ across all live waves: 2 by default, `--max-sessions N` (or `CADENCE_MAX_SESSION
|
|
|
643
674
|
every wave the same value: each wave counts ALL live sessions, whatever their slot, and waits while that
|
|
644
675
|
count has reached ITS OWN cap, so with different caps the highest one can push the total past the lowest
|
|
645
676
|
(which then waits). A session that finds no free slot **waits** (the wave is not refused; the journal says
|
|
646
|
-
`en attente d'un créneau de session depuis …`, repeated every minute, then `créneau de session obtenu après …`).
|
|
677
|
+
`en attente d'un créneau de session depuis …`, repeated every minute, then `créneau de session obtenu après …`). A free slot is always taken, even when a request is already written (relaunching a cut review is not a wait; the request applies afterwards). Once the wait has really started (a first try found no free slot), it reads the wave's requests at every turn (L332): a `--drop` aimed at that lot ends it (the slot is never taken, no session starts, the lot is *handed back* `retiré de la vague (--drop)` like a lot not started) and a `--stop-after-current` ends it too (the lot is *suspended* `arrêt demandé (--stop-after-current)`, the wave **interrupted** and resumable); a `--drop` aimed at another lot does not. The step announced but never started is removed from the state and the lead's answer it was carrying (`pendingAnswer`, taken out of the lot when the brief was built) is put back: `--resume` replays the step with the answer in its brief, and a lot handed back by `--drop` says « la réponse du lead (« … ») n'a pas été jouée » in its outcome. When it is the relaunch of a session already run in the step (a cut review relaunched in a fresh session, L256; the formatting retry) that was waiting, the step ends `failed` with its cause (« relance en session neuve non faite : … ») and keeps what the first session left: its id, its tokens (step and wave budget), its stderr in `<n>-<kind>.first.err` (and its output in `.first.json` for the formatting retry).
|
|
647
678
|
`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`.
|
|
648
679
|
A slot or a registry entry is owned by a pid **and** its start time: a reused pid is a dead owner. Wave
|
|
649
680
|
identifiers are reserved atomically (`-2`, `-3` suffix when two waves start in the same minute; an existing
|
|
@@ -660,7 +691,7 @@ upstream ref and `git ls-remote` are compared with the "before" (a `git checkout
|
|
|
660
691
|
`CADENCE_ORCHESTRATED` is set.
|
|
661
692
|
|
|
662
693
|
**Budget**: the wave counts input + cache writes + output tokens (default 2 M); cache reads are kept and
|
|
663
|
-
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 600 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 on 11-10, 451 k on 16–22-10: 3 of 145 lots over 540 k, all maritime-atlas, hence 600 k, L247; remeasured on 17–23-10: 0 of 309 lots over budget, p90 452 k, so unchanged, L280), 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
|
|
694
|
+
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 600 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 on 11-10, 451 k on 16–22-10: 3 of 145 lots over 540 k, all maritime-atlas, hence 600 k, L247; remeasured on 17–23-10: 0 of 309 lots over budget, p90 452 k, so unchanged, L280; and on 18–24-10: 0 of 322 over, p90 460 k at 600 k, 557 k at 750 k, 744 k at 1.3 M, unchanged again although the tokens per lot rose by a third at estimates 0.25 and 0.5: the rise is the write pass, which the budget bounds with room, L312), 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
|
|
664
695
|
`--dry-run` with its basis): it bounds the *writing* passes, never the reviews. A lot that spent it gets no further implementation or
|
|
665
696
|
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
|
|
666
697
|
the remaining budget can also pay the fix and the short review that follows it (155 k tokens reserved: the p90 of a fix
|
|
@@ -739,8 +770,8 @@ News instruction (`cadence news new <lot>`, factual user-side text, a screenshot
|
|
|
739
770
|
```yaml
|
|
740
771
|
orchestrate:
|
|
741
772
|
test: npm test # run by the orchestrator after a work step (optional)
|
|
742
|
-
build: npm run build # run
|
|
743
|
-
precheck: true # default: before the first implementation of a lot with no commit whose title cites a finding or an earlier lot, and whose title or `goal:` cites a finding (`constat`, `revue`, `review`, `audit`) or a plan id (an earlier `L<number>`, also followed by a hyphen as in `L12-L15` or `L12-bis`; any other id such as `Q-CAN5`; `project:L<number>`), 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, for every lot with no commit whatever its title (L296)
|
|
773
|
+
build: npm run build # run BEFORE the tests (L333); both results go to the reviewer, who does not redo them (optional)
|
|
774
|
+
precheck: true # default: before the first implementation of a lot with no commit whose title cites a finding or an earlier lot, and whose title or `goal:` cites a finding (`constat`, `revue`, `review`, `audit`) or a plan id (an earlier `L<number>`, also followed by a hyphen as in `L12-L15` or `L12-bis`; any other id such as `Q-CAN5`; `project:L<number>`), 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, for every lot with no commit whatever its title (L296); a lot created after the last code commit of the repository is not pre-checked, local or Sonnet, unless it cites a lot that already has commits (L322)
|
|
744
775
|
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)
|
|
745
776
|
maquette: true # default: UX review of the mockup between spec and accept (see « The mockup review »); false skips it
|
|
746
777
|
maquetteReviews: 3 # default 3: how many reviews of the mockup per lot (integer ≥ 1); also the `spec` session budget of a lot with a screen
|
|
@@ -748,7 +779,7 @@ orchestrate:
|
|
|
748
779
|
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)
|
|
749
780
|
testGate: { code: [src/**] } # code changed without a test goes to a correction pass before the review (see « Code changed without a test »); absent: no gate
|
|
750
781
|
prodAccess: false # default: wave sessions get no access to production (see « Production access of the sessions »); true lifts it for this project
|
|
751
|
-
worktrees: false #
|
|
782
|
+
worktrees: false # on in cadence's own cadence.yaml since L320; default: one lot at a time per repository, in the repository itself; true plays independent lots of one repository in parallel, each in its own git worktree (see « One worktree per lot »)
|
|
752
783
|
effort: { review: xhigh } # effort level per pass (see « Effort level per pass »): precheck, spec, maquette, fiche, accept, implement, fix, review, ux
|
|
753
784
|
ux: http://localhost:4200 # a URL, a launch command, or { command, url, timeout? } — for the UX review (see below)
|
|
754
785
|
permissionMode: auto # default
|
package/docs/overview.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Overview: the tools and the skills
|
|
4
4
|
|
|
5
|
-
The longer introduction the README used to open with.
|
|
5
|
+
The longer introduction the README used to open with, and everything the README handed over to keep itself a short home page (L326): the introduction in full, the start in detail, the plugin installation, the map of the documentation and the four tools in short.
|
|
6
6
|
|
|
7
7
|
A small working method that lives in your repository. Solo developers and
|
|
8
8
|
AI-assisted sessions lose track of *what is left to do* and *what each commit
|
|
@@ -25,3 +25,67 @@ reports a page left empty or in error.
|
|
|
25
25
|
|
|
26
26
|
And a second plugin, [`cadence-hud`](cadence-hud.md): a status band above the Claude
|
|
27
27
|
Code prompt that shows the session's context, quota, cost and the orchestrate waves running.
|
|
28
|
+
|
|
29
|
+
## The README's introduction, in full
|
|
30
|
+
|
|
31
|
+
A small working method that lives in your repository: a versioned plan linked to your commits, waves of fresh AI sessions driven by a program, and deliveries proved by their effect.
|
|
32
|
+
Solo developers and AI-assisted sessions lose track of *what is left to do* and *what each commit was for*; cadence keeps both in plain files next to the code.
|
|
33
|
+
**raf** is the plan, **news** the changelog with screenshots, **session** the facts to open and close a day, **deliver** waits for the CI, deploys, then checks the effect.
|
|
34
|
+
**orchestrate** runs a lot through spec, review, code and UX passes, each in its own session (with a short `TMPDIR` of its own, `<os tmpdir>/cadence-<hash>`, outside the repository and the wave's state folder, removed at the end of each pass of the lot; short because Chrome's Unix sockets live there and are limited to 107 characters); the **lead** decides, re-verifies and delivers. The **worktree mode** (`orchestrate.worktrees: true` in `cadence.yaml`: independent lots of one repository in parallel, each in its own git worktree) is on in cadence's own `cadence.yaml` since 2026-10-25 (L320, after the trials L310 and L316: −45 % and −22 % of wall time, three automatic integrations without incident); a project that does not set the key keeps one lot at a time in the repository ([docs/orchestrate.md](orchestrate.md#one-worktree-per-lot-orchestrateworktrees-l279)).
|
|
35
|
+
Four [Claude Code](https://claude.com/claude-code) skills, three reviewer agents and a status-band plugin (`cadence-hud`) turn it into rituals.
|
|
36
|
+
|
|
37
|
+
## Start
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm install -g @sylad/cadence # or: npx -p @sylad/cadence raf …
|
|
41
|
+
raf init # .cadence/raf.yaml + .cadence/notes/ + a post-commit hook
|
|
42
|
+
# (a legacy docs/plan/raf.yaml is still read until moved: `raf migrate` moves it, see docs/raf.md)
|
|
43
|
+
raf add "My first lot" --estimate 1
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then `raf start L1`, commit with `feat(L1): …`, `raf now` to see where you are, `raf done L1` when it is finished. A title gone stale after a change of scope is replaced with `raf title L1 "new title"` (any status, the old one is kept in a dated note). A lot that occupies a resource shared between projects says so with `raf add --uses banc` or `raf uses L1 banc`, the folder's `cadence.yaml` lists the resources (`resources: [banc]`), and `raf check` refuses a name that is not declared ([docs/raf.md](raf.md#shared-resources-uses-and-resources)); `cadence lead hold <label>` / `lead release <label>` declare the sessions the lead starts by hand so that they are subtracted from the free slots of `orchestrate --status` and `lead tour` ([docs/lead.md](lead.md#lead-hold-and-release)); `cadence lead tour` and `orchestrate --continue` list, next to the free slots, the ready lots whose resources no `doing` lot (in any project sharing them, at every `depth:` of the declaring `cadence.yaml`) or live wave holds (`--json`: `free`); the `lead` skill reads that list again each time an agent comes back or a lot is blocked, before waiting).
|
|
47
|
+
|
|
48
|
+
## Install as a Claude Code plugin
|
|
49
|
+
|
|
50
|
+
The repository is its own [Claude Code](https://claude.com/claude-code) plugin marketplace. In a Claude Code session:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
/plugin marketplace add Sylad/cadence
|
|
54
|
+
/plugin install cadence@cadence # skills /cadence:session-start, session-close, deliver, lead + the three reviewer agents
|
|
55
|
+
/plugin install cadence-hud@cadence # the status band above the prompt
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The two plugins are independent. `cadence@cadence` carries the rituals, which call the **`cadence` CLI** (`raf`, `session`, `deliver`, …): install it too with `npm install -g @sylad/cadence`, the plugin does not bring it. `cadence-hud` works without the CLI; only its per-project plan progress needs `cadence` on the `PATH`. Without the plugin, `cadence skills install` copies the skills and agents into the repository. Details: [docs/skills.md](skills.md) and [docs/cadence-hud.md](cadence-hud.md).
|
|
59
|
+
|
|
60
|
+
## Where to read more
|
|
61
|
+
|
|
62
|
+
| Page | What it covers |
|
|
63
|
+
|---|---|
|
|
64
|
+
| [docs/raf.md](raf.md) | the plan: commands, plan file, foreign plan formats, Gantt |
|
|
65
|
+
| [docs/orchestrate.md](orchestrate.md) | waves of sessions: budgets, spec, accept and review steps, neighbouring repositories, one worktree per lot (`orchestrate.worktrees: true`, on in cadence's own `cadence.yaml`, off by default elsewhere; `--integrate`, `--replay`, `--remove-worktree`; recovery of a handed-back lot's branch and the `CHANGELOG.md` union at the integration are described there) |
|
|
66
|
+
| [docs/deliver.md](deliver.md) | delivery: wait for CI, deploy, verify the effect |
|
|
67
|
+
| [docs/spec.md](spec.md) | the screen and back-end sheets, `cadence spec check\|qa\|reseal` |
|
|
68
|
+
| [docs/lead.md](lead.md) | the lead workflow and `cadence lead tour` |
|
|
69
|
+
| [docs/ref.md](ref.md) | reference documents: `cadence ref add` (rendered pages, positioned text, images, figure index), `cadence ref measure` (colours, WCAG contrast, boxes, OCR, font size), `cadence ref check` (the inventory schema, `templates/inventaire.json`), `cadence ref eval` (recall, precision, false « read », tokens per page against a known inventory), `cadence ref corpus` (the synthetic corpus with a known truth, rendered by Playwright) `cadence ref diff` (reconciles two readings of a document by program) and `cadence ref analyse` (one reading by the `reference-analyst` agent; `--double` for two independent readings, `ref diff`, a third look by `reference-verifier` and the reconciled inventory, an option whose measurement showed no gain; 20 pages at most per reading) |
|
|
70
|
+
| [docs/stabilite.md](stabilite.md) | what is guaranteed from 1.0.0 and what is not |
|
|
71
|
+
|
|
72
|
+
Also: [news](news.md) (changelog, UX, code and QA reviews, documentation that follows the code), [session](session.md), [corrections](corrections.md), [perf](perf.md), [skills and agents](skills.md), [cadence-hud](cadence-hud.md), [releasing](releasing.md), [overview](overview.md), [how it all works](fonctionnement.md) (four figures).
|
|
73
|
+
|
|
74
|
+
<details>
|
|
75
|
+
<summary>The four tools in short</summary>
|
|
76
|
+
|
|
77
|
+
- **raf** (French *reste à faire*, "what is left to do"): the plan, linked to your commits by the lot id in the message, computed from `git log` and never stored.
|
|
78
|
+
- **news**: a user-facing changelog with screenshots, tied to the plan; a lot is not done until its entry is written.
|
|
79
|
+
- **session**: `session start` and `session close` gather the facts of a work session (each lot in progress shows its commit count, `0 commit` included, next to its last activity).
|
|
80
|
+
- **deliver**: wait for the CI of the pushed commit, run the deploy commands, then **verify the effect**; a delivery is done only when its checks pass. A failed delivery is logged as a *red delivery* with its class (refusal, CI, deploy, check) and the last lines of the failing command's output. While a wave runs, `cadence deliver --sha <sha>` is accepted when no commit of the sha belongs to a lot that is not ready, so a ready lot ships while the wave runs.
|
|
81
|
+
- **auto-verification** (L236): before it reports, an `implement` or `fix` session re-reads the complete diff of its lot (of its fix commits, in `fix`) against the grid of the `code-reviewer` (code bug, architecture drift, architecture documentation, edge cases, one test per changed behaviour), fixes what it finds and writes an « auto-vérification » paragraph in its report; the measure is `cadence corrections --classify` and the share right the first time of `cadence perf`.
|
|
82
|
+
- **perf**: `cadence perf` is the dashboard of the workflow (speed next to quality, `⚠` on a trend), including the red deliveries and the blocking QA findings; `cadence lead tour` opens with its *perf header*, one line per `⚠`, nothing when stable; a rise of the tokens per lot names the step that carries it, and when the share right the first time rose too it reads as quality paid for, not a wrong estimate ([docs/perf.md](perf.md)).
|
|
83
|
+
- **doc-keeper** (L306): a generic agent that reads the diff of a lot and the project's map of documents (`docs.sync`, README, `docs/`, CHANGELOG, news), says which documents must change and updates them (or only lists them, read-only); `cadence orchestrate` calls it before the review when `docs.sync` reports a document behind the lot (a lot never ends ready with that gap open: the documentation is written, or the gap is acknowledged with `raf docs-ok <lot> --reason`, L317), the lead by hand; a document outside the repository only through the `docs.articles` key of the project's `cadence.yaml` (it then proposes a lot, never writes it) ([docs/news.md](news.md#the-librarian-doc-keeper)).
|
|
84
|
+
- **reference-analyst** (L265): an agent that reads one reference document (mock-ups, a spec with figures, a norm) page by page and zone by zone, measures with `cadence ref measure`, and writes a reading (`lectures/a.json`) in the format of `templates/inventaire.json`; `cadence ref analyse` launches it for one reading and has the program check the file and count the tokens (once by default; with `--double` (L266) twice, readings A and B in `lectures/a.json` and `b.json`, which must not see each other; [docs/ref.md](ref.md#cadence-ref-analyse-documentpdf---sections-3-59---slug-name---model-sonnetopushaiku---model-b-sonnetopushaiku---seuil-30---double)).
|
|
85
|
+
Reviews are opt-in gates: `ux-reviewer` (no user-facing change is done before its usability review), `code-reviewer` (no lot with commits is done before its code review) and `qa-reviewer`, which walks the delivered app in a real browser.
|
|
86
|
+
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
## What's new
|
|
90
|
+
|
|
91
|
+
Two lines per version, then one paragraph per version, in [whats-new.md](whats-new.md); the full history, with dates, is in [CHANGELOG.md](../CHANGELOG.md).
|
package/docs/perf.md
CHANGED
|
@@ -34,6 +34,7 @@ current window, with at least 10 classified passes) is flagged by a `⚠` even w
|
|
|
34
34
|
- **Sessions that see the application.** `sessions ux par lot visible` counts, per visible lot rendered in the window, its `ux` steps that ended `ok` **and** the `review-small` steps that ended `ok` of a small visible lot (journal `small: true`): the single pass of a small visible lot is its only ergonomics review, run with the application launched (L283; measured on 24-10, 139 of 140 small lots were visible and the line showed 0.0, −97 %, a false signal). The `review-small` of a lot that is not small is only a code review and is not counted; a journal written before the `small` field counts none.
|
|
35
35
|
`part des lots visibles revus par une étape ux` is the share of the visible lots rendered in the window with at least one `ux` step that ended `ok` (so a small visible lot, reviewed in `review-small`, is not in it). It is informational: no `⚠`, no quality twin (`interventions.dedicatedUx` in `--json`), `n/d` without a visible lot.
|
|
36
36
|
- **Pre-check share of `oui`** (L288). `part de « oui » du précheck` is the share of the lots rendered in the window whose journal records a pre-check verdict (`precheckVerdict`) that is `oui` (the deliverable was already there; an unproven `oui` is recorded `partiel`). Compare the current window with the previous one before and after a change of `orchestrate.precheck` or of the pre-check criterion (the title rule of L288): a share that stays low means the pre-check costs more than it saves. A journal older than L288 has no `precheckVerdict`: its verdict is read back from the lot (`livrable déjà présent` → `oui`, pre-check finding in the brief → `partiel`, otherwise `non`), so the window before the change is measurable. A journal written since L288 whose pre-check report was unreadable (it keeps the `contrôle préalable illisible` warning, no `precheckVerdict`) has no verdict and is not counted, not read back as `non` (L293). Lots with no successful `precheck` step are not counted; informational, no `⚠`, no quality twin (`precheck.yes` in `--json`), `n/d` without a verdict.
|
|
37
|
+
- **Pre-check measured per lot** (L322). Under the section « Contrôle préalable », for the current window: the split `oui` / `partiel` / `non` of the rendered lots (`précheck : oui 1 · partiel 0 · non 1`); the local sessions (`précheck local : n sessions, k échecs, m min`: how many, how many ended without a usable report, wall time; a local session that ended `ok` but whose report was unreadable, so that the lot fell back on Sonnet, counts as a failure, and its line says `local échec`, `--json` `local.status: "illisible"`); the Sonnet sessions of the pre-check, which are the fallback of a local failure and the confirmation of a local `oui` (`précheck Sonnet : n sessions, t k tokens, m min`; the local model counts no token); the gain of the `oui` (`gain des « oui » : n sessions d'implémentation évitées (≈ t k tokens)`: the lots handed back without implementation, times the median of the `implement` steps of the window; without any, the tokens read `(gain en tokens : n/d)` and are `null` in `--json`); then one line per lot (`projet:lot · verdict · local échec 10 min · Sonnet 2 min, 20 k tokens`). Nothing is printed for a window without pre-check session or verdict. In `--json`: `precheck.verdicts`, `precheck.cost` (`local`, `sonnet`, `avoided`, each `{ current, previous }` as a whole) and `precheck.lots` (current window). Read it before changing `orchestrate.precheck` or the lots it applies to: the cost of the pre-check is what it spends (`local` minutes, `sonnet` tokens), its worth is `avoided`.
|
|
37
38
|
- **`--since date`** is an ISO date that ignores everything before that day in both windows (a wave created before that day is not read, even if its lots end after it); a window starting before it is *truncated* (its per-day rates divide by the days covered, no `⚠`).
|
|
38
39
|
It is **not** the instant of `cadence corrections --since` (waves created strictly after).
|
|
39
40
|
- **`--json`** prints one object `{ since, today, windows: { current, previous }, throughput, corrections, interventions, precheck, tokens, delays, durations, cost, orphans, warnings }`; every indicator carries
|
|
@@ -46,7 +47,7 @@ quality twin (the share of lots right the first time) on the same line. A rise i
|
|
|
46
47
|
|
|
47
48
|
| Indicator | Reading | Decision it informs |
|
|
48
49
|
|---|---|---|
|
|
49
|
-
| tokens per lot, by estimate | median of the counted tokens (sum of `tokens.counted` of every step) of the rendered journals of the window whose lot joins the plan, one class per exact estimate (`0.5`, `1`, `2`…), a lot that went through the spec chain (a `spec`, `fiche`, `maquette` or `accept` step; 450 k is the gap of their *budget* (`SPEC_STAGE_ALLOCATION`), the measured gap is smaller, about 150 k at estimate 0.5 and 30 k at estimate 1 on 12–18-10, `docs/perf/goulot-2026-10-18.md`; on 17–23-10 the `⚠` on estimates 0.25 and 0.5 (+27 %) came with a share right the first time up from 37 to 59 %, so the reading is « heavier first write, fewer corrections », `docs/perf/recalage-2026-10-24.md`, L280) in its own class `0.5+spec`, printed after `0.5`, so that a change in the mix of lots is not read as a heavier ordinary lot (L240); a failed lot counts, a journal with no readable tokens or without plan lot does not | is the estimate calibrated? redo the split or the budgets |
|
|
50
|
+
| tokens per lot, by estimate | median of the counted tokens (sum of `tokens.counted` of every step) of the rendered journals of the window whose lot joins the plan, one class per exact estimate (`0.5`, `1`, `2`…), a lot that went through the spec chain (a `spec`, `fiche`, `maquette` or `accept` step; 450 k is the gap of their *budget* (`SPEC_STAGE_ALLOCATION`), the measured gap is smaller, about 150 k at estimate 0.5 and 30 k at estimate 1 on 12–18-10, `docs/perf/goulot-2026-10-18.md`; on 17–23-10 the `⚠` on estimates 0.25 and 0.5 (+27 %) came with a share right the first time up from 37 to 59 %, so the reading is « heavier first write, fewer corrections », `docs/perf/recalage-2026-10-24.md`, L280) in its own class `0.5+spec`, printed after `0.5`, so that a change in the mix of lots is not read as a heavier ordinary lot (L240); a failed lot counts, a journal with no readable tokens or without plan lot does not. Each class also carries `carrier` in the JSON (L312): the kind of step whose tokens per lot, averaged over the lots of the class, rise most from one window to the other (`review` and `review-small` count as one step, the size of the lot picks one or the other; `{ kind, delta }`; `null` when the total per lot does not rise on average) | is the estimate calibrated? redo the split or the budgets. **The advice names the step** (L312): « la hausse est portée par l'étape `implement` (+20 k par lot) », and when the share right the first time rose in the same window the line reads « de la qualité payée, pas une estimate fausse ; juger cette étape (modèle, effort, passes) avant de recaler l'estimate » instead of « l'estimate est mal calibrée » (17–24-10 against 10–17-10: `implement` carries the rise at estimates 0.25 and 0.5, `docs/perf/recalage-2026-10-24-soir.md`) |
|
|
50
51
|
| share of review tokens | counted tokens of the steps whose kind starts with `review` or is `ux`, over the counted tokens of all steps of the rendered journals; a ratio, `n/d` without journal | does the review weigh too much against the writing? lighten it (`orchestrate.review.threshold`) |
|
|
51
52
|
| delay created → done | median of `finished − created` (calendar days) of the plan lots `done` with `finished` in the window and both dates readable | is the queue growing? |
|
|
52
53
|
| delay done → delivery | median of the days between `finished` and the first line of the repository's `deliveries.log` on or after it (`<git common dir>/cadence/`, one repository per project, worktrees included; read without creating anything); `n/d` for a project that is not a git repository, has no log or no later line | deliver in smaller batches? |
|
|
@@ -84,6 +85,6 @@ No indicator of produced volume (lines of code, commits, total tokens or euros o
|
|
|
84
85
|
|
|
85
86
|
Both are judged like the other indicators (`⚠` above +25 %). For red deliveries the sample is the deliveries of the window, green and red (at least 5 per window); for blocking QA findings it is the plan lots `done` in the window (at least 5 per window). `--json` adds `quality: { redDeliveries, qaBlocking }` (Indicator format, `twin` `throughput.delivered` and `throughput.escaped`); every other key is unchanged, and `throughput.delivered.twin` stays `throughput.escaped`.
|
|
86
87
|
|
|
87
|
-
**Red deliveries by class and project** (L282): when the current window holds at least one red delivery, `cadence perf` prints under the « Débit » section two lines, `livraisons rouges par classe (fenêtre courante)` and `livraisons rouges par projet`, each sorted by count (ties: refus, CI, déploiement, vérification, then `non classée` for lines
|
|
88
|
+
**Red deliveries by class and project** (L282): when the current window holds at least one red delivery, `cadence perf` prints under the « Débit » section two lines, `livraisons rouges par classe (fenêtre courante)` and `livraisons rouges par projet`, each sorted by count (ties: refus, CI, déploiement, vérification, then `non classée` for lines whose class is unknown). `--json` adds `quality.reds: { current, previous }`, each `{ count, byClass: { refus, ci, deploiement, verification, inconnue }, byProject: { <project>: { <class>: n } } }`; the other keys are unchanged. **Lines written before the class** (L331): a line with no `[class]` is classed from its wording, the one `deliver` writes itself — `script de livraison en échec (code 2)` → refus, any other code (or a timeout) → déploiement, `déploiement en échec` → déploiement, `CADENCE_DEPLOYED_FILE …` (declared sha invalid or unreadable) → déploiement, `CI en échec` / `CI non terminée après N s` / `gh run list en échec` / `aucun run CI pour ce sha après N min` → ci, `vérification en échec` → verification; the wording must start the reason. Any other line stays `non classée`. The deduction happens at read time: `deliveries-failed.log` is never rewritten. The reason itself (the last lines of the failing command's output) stays in `deliveries-failed.log`: read it there.
|
|
88
89
|
|
|
89
90
|
`cadence lead tour` prints the `⚠` lines of `perf` as its **perf header** (see [lead tour](lead.md#lead-tour)).
|
package/docs/raf.md
CHANGED
|
@@ -64,9 +64,10 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
64
64
|
| `raf commits <id>` | the commits counted for a lot (the set the code review gate uses), one `<sha> <subject>` per line, oldest first; a lot that declares `repos:` ([neighbouring repositories](orchestrate.md#a-lot-whose-work-is-in-a-neighbouring-repository)) gets, after them, one `dépôt <path> :` section per neighbour with the commits there that cite the lot and fall within the lot's period (`started` to `finished`) and, when the lot declares a `cite:` for that neighbour, contain that string (`aucun commit du lot`, or `introuvable` for a path that is not there) |
|
|
65
65
|
| `raf now` | what to do next |
|
|
66
66
|
| `raf list [--status s]` | flat list |
|
|
67
|
+
| `raf docs-ok <lot> --reason text` | acknowledge the `docs.sync` gap of a lot (a document behind its commits, `raf check` red) when the documentation really stays true: a `docs_ok: { date, reason }` key on the lot, **the reason is required** (refused without it, for an unknown lot or for a sub-task); `raf check` and `cadence lead tour` no longer count the lot, `raf show` prints it, and `cadence orchestrate` neither calls the librarian nor holds the lot back for it. Commit the plan with it |
|
|
67
68
|
| `raf ignore <sha> \| "exact subject" [--reason text]` | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) without rewriting history: a dated, reasoned line in the plan's `acknowledged:` section; a sha is exact, a subject covers every commit carrying it; exactly one commit argument, the reason goes through `--reason "text"` (`raf ignore 1a2b3c4 "tooling"` is refused with « un seul commit attendu ; la raison se passe par --reason "texte" », exit 2) |
|
|
68
69
|
| `raf check --ignored` | list the acknowledged commits with their date and reason |
|
|
69
|
-
| `raf check [--since date] [--idle 7]` | since the plan's adoption date by default (a visible lot without a public title is only a `⚠` warning: it never changes the exit code): commits without a lot (commits touching only plan files, or only version fields, `CHANGELOG.md`, the README's « What's new » section and `docs/whats-new.md` are exempt), unknown ids, `todo` lots that already have commits, idle lots, `done` lots with open sub-tasks, bad or circular dependencies, and an open lot titled « Revue UX — <screen> » without `visible: true` (`ux-review-not-visible`, an error: such a lot goes through precheck → implement → review with no UX step, so the screen is never reviewed — finance L32 on 2026-10-23; L271; the message names the fix, `raf visible <id> on`, L273) |
|
|
70
|
+
| `raf check [--since date] [--idle 7]` | since the plan's adoption date by default (a visible lot without a public title is only a `⚠` warning: it never changes the exit code): commits without a lot (commits touching only plan files, or only version fields, `CHANGELOG.md`, the README's « What's new » section and `docs/whats-new.md` are exempt), unknown ids, `todo` lots that already have commits, idle lots (an open lot whose sub-lots, entries carrying `parent:` in a plan kept by another tool, have a recent commit or are in progress is not idle: L321), `done` lots with open sub-tasks, bad or circular dependencies, and an open lot titled « Revue UX — <screen> » without `visible: true` (`ux-review-not-visible`, an error: such a lot goes through precheck → implement → review with no UX step, so the screen is never reviewed — finance L32 on 2026-10-23; L271; the message names the fix, `raf visible <id> on`, L273; on a read-only plan, plan kept by another tool, it never names a `raf` command, which would refuse: with `plan.fields.visible` mapped it says to set `visible:` with the project's tool, without it it says the plan carries no `visible`, to map the field in `cadence.yaml` (`plan.fields.visible`) if the project's tool writes it, and that otherwise the UX review of that screen happens outside orchestrate, L321) |
|
|
70
71
|
| `raf gantt [-o file]` | standalone Gantt page |
|
|
71
72
|
| `raf hook install` | add the post-commit hook (read-only, never blocks) and the pre-commit hook (does nothing unless `hook.autostart` says so, see below) |
|
|
72
73
|
|