@sylad/cadence 1.13.0 → 1.15.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 +3 -0
- package/agents/reference-planner.md +26 -0
- package/dist/audit.js +5 -2
- package/dist/cli.js +195 -21
- package/dist/config-set.js +6 -3
- package/dist/config.js +47 -9
- package/dist/corrections.js +1 -0
- package/dist/git.js +11 -1
- package/dist/lead.js +5 -1
- package/dist/orchestrate/accept.js +63 -1
- package/dist/orchestrate/baseline.js +39 -3
- package/dist/orchestrate/changelog-merge.js +98 -5
- package/dist/orchestrate/command.js +118 -12
- package/dist/orchestrate/cycle.js +255 -63
- package/dist/orchestrate/decisions.js +2 -0
- package/dist/orchestrate/integrate.js +97 -5
- package/dist/orchestrate/launch.js +2 -0
- package/dist/orchestrate/state.js +1 -1
- package/dist/orchestrate/workers.js +51 -0
- package/dist/orchestrate/worktree.js +15 -4
- package/dist/perf.js +9 -1
- package/dist/plan-commit.js +90 -0
- package/dist/plan.js +41 -3
- package/dist/ref/analyse.js +34 -4
- package/dist/ref/command.js +2 -1
- package/dist/ref/coverage.js +136 -0
- package/dist/ref/diff.js +99 -15
- package/dist/release.js +5 -1
- package/dist/session.js +7 -1
- package/dist/stale-notes.js +22 -0
- package/dist/verify-gate.js +138 -0
- package/docs/deliver.md +37 -0
- package/docs/fonctionnement.md +2 -1
- package/docs/lead.md +6 -4
- package/docs/orchestrate.md +67 -13
- package/docs/overview.md +3 -2
- package/docs/perf.md +3 -1
- package/docs/raf.md +50 -2
- package/docs/ref.md +61 -3
- package/docs/releasing.md +5 -3
- package/docs/session.md +8 -0
- package/docs/skills.md +1 -0
- package/docs/spec.md +2 -0
- package/docs/stabilite.md +2 -0
- package/docs/whats-new.md +9 -0
- package/package.json +3 -3
- package/skills/lead/SKILL.md +8 -6
- package/skills/orchestrate/SKILL.md +1 -1
- package/skills/session-close/SKILL.md +2 -0
- package/skills/session-start/SKILL.md +2 -1
package/docs/orchestrate.md
CHANGED
|
@@ -139,8 +139,8 @@ keeps its `/` (`maritime-atlas:Q4/accueil-4-ux12@haiku`); this holds for `--dry-
|
|
|
139
139
|
same for the lot folder and the dry-run briefs); ids without `/` are unchanged.
|
|
140
140
|
|
|
141
141
|
You choose the lots; the order is the order given (one queue per repository, as many repositories at 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 »).
|
|
143
|
-
lot (default Sonnet; reviews are Opus, except the light review below; Haiku only when you write it, for a mechanical lot). Run it
|
|
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 »). `@<model>` sets the model of the implementation and corrections of that
|
|
143
|
+
lot (`@haiku`, `@sonnet`, `@opus`, or any other name, passed as it stands to `claude --model` like `orchestrate.escalation.model`: `@fable`, `@claude-fable-5-1` — L345; the dry-run warns, without refusing, for a name outside the three aliases; default Sonnet; reviews are Opus, except the light review below; Haiku only when you write it, for a mechanical lot). Run it
|
|
144
144
|
in the background and read `--status`: it prints one line per transition and the final table.
|
|
145
145
|
|
|
146
146
|
**Following a wave (L49)**: `--status --watch` clears the screen and redraws the live waves and the table every
|
|
@@ -166,11 +166,13 @@ usability (code only when the lot is not `visible`). Failing tests (reported red
|
|
|
166
166
|
|
|
167
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
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`.
|
|
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. With `orchestrate.test` declared (L361), the same structured report is honoured when the program's own gate is green (`orchestrate.test` exits 0): `vert: false` with a non-empty `echecsDeBase` and an empty `nouveauxEchecs` goes straight to the review, with a warning, without a correction pass and without the « tests rouges » escalation; without those two fields the claim of red tests still triggers 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
170
|
|
|
171
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.
|
|
172
172
|
|
|
173
|
-
**
|
|
173
|
+
**A red test is replayed alone before it counts, only on a declared command (L354)**: on 2026-10-26, with 32 cores and a cap of 4 sessions, every suite ran vitest at 31 workers, and four correction passes (L344 11 min, L342 9, L349 7, B28.12 4) found nothing to fix — each time « a test that passes alone ». cadence does not guess how a file is replayed on your runner (a first version appended the files to `orchestrate.test` and, on a composed gate, excused a real regression), so the replay is **off unless the project declares its command**: `orchestrate.testReplay`, where `{files}` is replaced by the files of the red tests, e.g. `testReplay: "npx vitest run --maxWorkers=$CADENCE_TEST_WORKERS --minWorkers=1 {files}"` (a value without `{files}` is refused when the file is read). **Without it nothing is replayed and nothing is held for unstable**: a red test is counted as before. With it, a red test of the gate (`orchestrate.test`, played after every writing session and at the rebased tip) that is absent from the baseline is **replayed alone, twice at most**, by that command, in the lot's worktree (vitest names `file > suite > test`, pytest `file::test`, jest `FAIL file` and `● Suite › test`, the latter tied to the `FAIL` line that precedes it in the same output and replayed with that file; the file is the part before the first `::` or ` > `, so either of them inside a test title is not part of the path). Green at a replay, the test is **« instable sous charge »**: it is not counted, it triggers no correction pass, even when the session reported its tests red (`vert: false`: a session that plays the suite under the same load sees the same red; the announcement is not held against the lot when every red of the gate is unstable, as it is not when every red was already red at the start), the lot goes on to its review, and the trace is a lot warning (so a line of the final table), a journal line `<project>:<lot> · N test(s) instable(s) sous charge …`, the lot's `instables` list in its journal, and the `cadence perf` line `tests instables sous charge` (`instables` in `--json`). Red at both replays it is counted as before. A red name that designates no file (a plain `FAIL name`) cannot be replayed alone and is counted at once. **A replay in which the test did not run counts red**: only a replay that exits 0 after running tests proves a test green, and only for the tests whose file appears in the replay's output, as a whole path on a line of the runner — never in the launcher's echo of the command: npm 12 writes `npm notice run 'vitest' run … <files>` on stderr for `npx …` as for `npm test -- …`, npm and pnpm write `> vitest run … <files>`, yarn `$ …`, and those lines name every file asked for, played or not (a runner that silently skips a file it cannot find, such as vitest given a path of another workspace, exits 0 without playing it: that test stays red; with a reporter that does not list files, nothing is ever excused; `a.test.ts` is not found in `src/a.test.ts`); a replay that fails, or that runs no test (the runner's closing line `No test files found, exiting with code`, `No tests found, exiting with code` or `no tests ran in`, even with exit code 0; the same words inside a test title or a test's output do not count), leaves every test it was asked about red. In a vitest workspace the names read `|package| src/a.test.ts > case`: the `|package|` prefix is dropped to find the file. The reviewer is told which tests were red under load (`instables` of the check, also when the baseline holds red tests of its own), so it does not report them, and the correction finding lists only the tests still red after the replays. The baseline run is included: with `testReplay`, a red test of the L213 baseline (the gate played at the start sha, in its throwaway worktree) is replayed alone there, twice at most, before it is retained as « already red at the start »; green at a replay it is dropped from the baseline (a journal line `… · ligne de base <sha> : N test(s) rouge(s) sous charge, vert(s) rejoué(s) seul(s), non retenu(s) …`), so a test the lot really breaks is not excused by a load failure of the start sha. The baseline replays are not the lot's « instables »: they say nothing about the lot's code. **A composed gate is not supported (decision of 26-10)**: `orchestrate.testReplay` is refused when `cadence.yaml` is read if `orchestrate.test` has `&&` or `||` outside quotes, or a parenthesis (sub-shell `( … )`, `$( … )`, function): a first runner that goes red cuts the next one, so a green replay of its tests says nothing of the rest, and a replay command written for one runner of the gate cannot replay the tests of the other. Write the replay for a gate that launches a single runner; without `testReplay` a composed gate is played as before, nothing is replayed. A jest `FAIL` line may carry a duration (`FAIL src/a.test.ts (6.21 s)`) or a project name (`FAIL backend src/a.test.ts`): the file is its last word, and any other `PASS`/`FAIL` line ties no file to the `●` lines that follow. The `FAIL project file` line is itself a red name (`project file`) and is replayed with that file, like the `●` tests under it.
|
|
174
|
+
|
|
175
|
+
**Each session gets its share of the machine (L350)**: the wave computes `CADENCE_TEST_WORKERS = max(1, cores ÷ session cap)` (`--max-sessions` or `CADENCE_MAX_SESSIONS`; 32 cores and a cap of 4 give 8; a `CADENCE_TEST_WORKERS` already set in the environment that launches the wave wins when it is a positive integer). It is put in the environment of every session and of every command of the gate (tests, build, baseline, replays), recorded as `testWorkers` in the wave state, and the `implement`, `fix`, minors and librarian briefs tell the session to respect it: `vitest run --minWorkers=1 --maxWorkers=$CADENCE_TEST_WORKERS` (vitest 2 refuses `--maxWorkers` alone, its default minimum being the core count: « options.minThreads and options.maxThreads must not conflict »), `jest --maxWorkers=$CADENCE_TEST_WORKERS`, `pytest -n $CADENCE_TEST_WORKERS`. The gate command itself must honour it for the share to bind: cadence's own `vitest.config.ts` reads it (`poolOptions.forks.maxForks`, `threads.maxThreads`); in another project write `orchestrate.test: npx vitest run --minWorkers=1 --maxWorkers=$CADENCE_TEST_WORKERS` (or the equivalent of its runner).
|
|
174
176
|
|
|
175
177
|
**Review sized to the lot (L108)**: a lot whose `estimate` is ≤ `orchestrate.review.threshold` (0.25 day by default)
|
|
176
178
|
is *light*: one single review (Sonnet by default, `code-reviewer` agent, same criteria), no minors pass — the minors are
|
|
@@ -180,7 +182,7 @@ chain described above (Opus, two corrections, minors pass). `--dry-run` shows «
|
|
|
180
182
|
|
|
181
183
|
```yaml
|
|
182
184
|
orchestrate:
|
|
183
|
-
review: { threshold: 0.25, light: sonnet, full: opus } # defaults; threshold in days (0 = no light lot), models haiku|sonnet|opus
|
|
185
|
+
review: { threshold: 0.25, light: sonnet, full: opus } # defaults; threshold in days (0 = no light lot), models: haiku|sonnet|opus or any name passed as it stands to claude --model (L345)
|
|
184
186
|
```
|
|
185
187
|
|
|
186
188
|
`full` is also the model of the UX review and of the single pass of a small lot that is not light.
|
|
@@ -192,7 +194,7 @@ orchestrate:
|
|
|
192
194
|
escalation: { model: fable } # no default: absent = nothing changes
|
|
193
195
|
```
|
|
194
196
|
|
|
195
|
-
The name is passed as it stands to `claude --model` — no closed list `haiku|sonnet|opus`, and no default value, because the account may not have the model. When the review is still not compliant after the planned passes (`MAX_PASSES`, or `MAX_PASSES_CONVERGING` when it converges), or when the lot would be handed back « tests rouges » after them, the wave plays **one** correction and then **one** fresh code review, both on that model, before handing the lot back. The lot is handed back only if that escalated review is not compliant (`… ; rendu malgré l'escalade <model>`); the escalated correction that leaves the tests red hands it back without a review. A compliant escalated review ends the lot as any compliant review does (minors pass, UX review, `ready`). The UX review keeps its own rule: a lot handed back for a non-compliant UX review is not escalated. The escalation counts as a correction pass (`passes` of the table) and is played at most once per lot, a resume included (`escalation` in the lot's journal).
|
|
197
|
+
The name is passed as it stands to `claude --model` — no closed list `haiku|sonnet|opus` (the same holds for `@<model>` of a lot and for `orchestrate.review.light` / `full`, L345: the three aliases keep their meaning, any other name goes through unchanged, and `--dry-run` prints `⚠ modèle « <name> » hors des alias …` for each such name, without refusing), and no default value, because the account may not have the model. When the review is still not compliant after the planned passes (`MAX_PASSES`, or `MAX_PASSES_CONVERGING` when it converges), or when the lot would be handed back « tests rouges » after them, the wave plays **one** correction and then **one** fresh code review, both on that model, before handing the lot back. The lot is handed back only if that escalated review is not compliant (`… ; rendu malgré l'escalade <model>`); the escalated correction that leaves the tests red hands it back without a review. A compliant escalated review ends the lot as any compliant review does (minors pass, UX review, `ready`). The UX review keeps its own rule: a lot handed back for a non-compliant UX review is not escalated. The escalation counts as a correction pass (`passes` of the table) and is played at most once per lot, a resume included (`escalation` in the lot's journal).
|
|
196
198
|
|
|
197
199
|
The lot's budget is respected: the escalation needs what a correction and its review need (`REVIEW_RESERVE`, as for any correction pass), and the budget of a lot in a project that declares `escalation` reserves its share for it (`ESCALATION_RESERVE`, see **Budget**). When the budget does not cover it, it is **not played** and the outcome says so (`escalade <model> non jouée (budget du lot …)`); the lot is handed back to the lead with its findings. The final table prints one line per escalated lot, `<project>:<lot> — escalade <model> : <outcome>` (`en cours` only while the lot still runs — the table also serves `--status` and a suspended wave —; a lot withdrawn (`--drop`), failed by an error or stopped by an incident of the repository before the model's verdict says `engagée, lot rendu avant le verdict du modèle`: the model did not judge it and it is not counted against the model —, compliant review, lot handed back despite the escalation — for the human or an arbitration of the lead —, not played, or an escalated session that returned nothing (empty output, launch error, cut, timeout; the only case said `session escaladée sans résultat`) — then the model never judged the lot and the lead takes it up, `rendu malgré l'escalade` is said only for a lot handed back on a verdict). A lot whose escalated review was compliant but is handed back later (non-compliant UX review, refused integration) keeps `revue neuve conforme` and says its lot line is the reason, the wave log traces the three moments, and `cadence perf` counts the escalated lots of each window and their outcome (`docs/perf.md`). The price grid of `cadence perf` knows `sonnet`, `opus` and `haiku`: the cost of an escalated step on another model reads `n/d`.
|
|
198
200
|
|
|
@@ -273,7 +275,7 @@ the first implementation: `precheck → spec → maquette → accept → impleme
|
|
|
273
275
|
`#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`).
|
|
274
276
|
- **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,
|
|
275
277
|
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
|
|
276
|
-
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 `
|
|
278
|
+
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 `docs(<lot>): règle R3 sourcée par la réponse du lead (orchestrate <wave>)` (the `plan.commit` template when set, L343) — 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.
|
|
277
279
|
The agent can also ask a question itself in its report.
|
|
278
280
|
- **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
|
|
279
281
|
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).
|
|
@@ -354,10 +356,18 @@ session, `accept` (L173): `precheck → spec → maquette → accept → impleme
|
|
|
354
356
|
(`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
|
|
355
357
|
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).
|
|
356
358
|
- **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.
|
|
359
|
+
- **A closed lot left at `vert: false`: `cadence spec green` (L366).** The cycle sets `vert: true` itself; a lot driven and closed by hand (`raf done`) never gets it, and its `acceptation-<lot>.json` then keeps the lock total on its tests: every later wave of the repository is handed back with `verrou des tests d'acceptation … premier commit fautif` on lots that never touched them. Three guards:
|
|
360
|
+
- `cadence spec green <lot>` runs the dedicated command (`orchestrate.accept`, same rules as the cycle: the commands whose root holds a listed file, `{acceptation}` and `CADENCE_ACCEPTATION` set; a listed file under no command's root is refused) and, when it is green, sets `vert: true` and commits the file alone, citing the lot (`chore(L12): acceptation verte, vert: true posé par cadence spec green`, or the `plan.commit` template of the project when it has one). The subject stays short and fixed: the resealed tests are listed in the commit body, one `- <path>` line each, so a `commit-msg` hook bounding the header (commitlint `header-max-length` 100) accepts the commit. Tests refined by the lot after the red commit (the case of issue #18: its own fix commits changed them) would still be reported by the lock, whose reference is the red commit: the command therefore reseals every listed test whose fingerprint changed (the `empreintes` are redone, one `resceaux` entry each) in that same commit, which becomes the red commit; a listed test changed but not committed is refused before anything runs. Red: the tail of the output on stderr, exit 1, nothing changed. Already green: nothing to do. Refused for a `todo` lot, with `CADENCE_ORCHESTRATED` set, while a wave holds the repository lock and while an unfinished wave keeps a lot of the repository open (the same refusals as `spec reseal`, worded for `spec green`: « spec green : refusé … puis relancer spec green »).
|
|
361
|
+
- `raf done <lot>` refuses a lot whose acceptance is `vert: false` (the message names the file and the command above); `--force` closes it anyway and warns on stderr.
|
|
362
|
+
- `cadence orchestrate … --dry-run` prints once per repository `⚠ <repo> · acceptation rouge d'un lot clos : L12 (<file>, vert: false)` for each `done` lot left that way, before the wave starts.
|
|
357
363
|
- **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 …`;
|
|
358
364
|
`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.
|
|
359
365
|
- **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).
|
|
360
366
|
|
|
367
|
+
## Elements of a reference document in the briefs (L367)
|
|
368
|
+
|
|
369
|
+
When the title, the goal or a note of a lot quotes ids of a reference inventory (`<slug>#<type>-<n>`, see [ref](ref.md#cadence-ref-coverage-inventairejson)), the `spec` brief lists them and asks the spec writer to take each into the sheet (its `source` quotes the id) or to justify it (`<id> : <reason>` under « choix » and in `perimetre.hors_perimetre`), and the `ux` brief asks the ux-reviewer to check each on the finished screen (a small visible lot has no `ux` pass: its single `review-small` pass gets the same instruction and checks the ids on the finished screen). The ids are read from the plan at launch (`lotIds`, `src/ref/coverage.ts`); a lot without id has unchanged briefs.
|
|
370
|
+
|
|
361
371
|
## The downstream briefs: sheet, rule → test → green, conformity, Hypothèses
|
|
362
372
|
|
|
363
373
|
Once the sheet exists, the steps after it all read it (L174, `src/orchestrate/aval.ts`, the `{{fiche}}` marker of the templates `implement.md`, `fix.md`, `fix-minors.md`, `review.md`, `review-small.md` and `ux.md`). A lot without a sheet (no `screen:`, or under the threshold) keeps exactly the briefs it had.
|
|
@@ -368,7 +378,7 @@ Once the sheet exists, the steps after it all read it (L174, `src/orchestrate/av
|
|
|
368
378
|
- **Real walkthrough before the auto-verification (L344).** Reading a diff did not show three major review findings of 26-10 (a command impossible in its own documented path, a « champs exclus » read against a decision of 22-10, a failure banner out of view). `implement.md` therefore asks, once the tests are green, to play what was delivered as a user would: a command in a throwaway repository (`git init` in a fresh temporary folder), following the documentation the lot wrote, command by command; a screen in each of its states, failure included and shown where the action takes place, with a capture of each. What breaks is fixed in the same session (test first) and played again; the report says it in a paragraph of `"resume"` that starts with « parcours réel » (no new field; a lot with nothing to play says so). `fix.md` does not carry it: a correction pass fixes the findings it is given.
|
|
369
379
|
- **Decisions to respect (L344).** The program (not the pre-check session, which many lots skip) reads the notes of the lot and of its parent lot (`parent:`) and keeps those that name who decided (Sylvain, the human, the lead, the user) and say that they decided, validated, asked or refused (`src/orchestrate/decisions.ts`). The notes the program writes itself are never taken for one, whatever they quote: a hypothesis of a fiche (« Hypothèses (fiche …) — », a choice nobody validated), a minor of a review (« Revue de … — mineur : »), a replaced title (« titre remplacé : », the stale one), « remis en attente » and « abandonné : ». Each is given with its date and its text as written (cut at 1500 characters) under « Decisions to respect » in the `implement` and `fix` briefs (and in the `--dry-run` briefs), with the rule: an interpretation that departs from one of them is a question under `"questions"`, never a choice under `"choix"`. Nothing is written when no note qualifies. The effect is measured by the share right the first time of `cadence perf` over two windows.
|
|
370
380
|
- **The code review (`review`, `review-small`) checks rule → test → green** (`règle → test → vert`). For each `Rn` of the sheet the reviewer checks that its test exists (named after the rule), that it is green, and that the behaviour is in the diff; when an acceptance file is named, that each listed test is unchanged since the red commit (`git diff <red commit> -- <file>` empty; the brief gives the red commit). A rule without a test, an acceptance test modified since the red commit, a rule coded that is not in the sheet, and a test that is green without exercising its rule are **major** findings. The reviewer no longer rebuilds the cases from the code.
|
|
371
|
-
- **The UX pass becomes a conformity pass.** For a lot with a sheet, the `ux` session (the `ux-reviewer` agent, **conformity mode**) no longer audits the whole screen: it compares the running app to the mockup and to its captures `maquette-<état>-<largeur>.png`, state by state, at 1440 and 390 px, and reports the differences of rendering and ergonomics the tests do not cover, each tied to a rule or a measure. The rules of the sheet are the tests' job. Its verdict feeds `raf ux`, which stays the lead's, decided on the captures. A small visible lot (single `review-small` pass) gets the conformity instruction in that pass.
|
|
381
|
+
- **The UX pass becomes a conformity pass.** For a lot with a sheet, the `ux` session (the `ux-reviewer` agent, **conformity mode**) no longer audits the whole screen: it compares the running app to the mockup and to its captures `maquette-<état>-<largeur>.png`, state by state, at 1440 and 390 px, and reports the differences of rendering and ergonomics the tests do not cover, each tied to a rule or a measure. The rules of the sheet are the tests' job. Its verdict feeds `raf ux`, which stays the lead's, decided on the captures. A small visible lot (single `review-small` pass) gets the conformity instruction in that pass. It gets the verification of the reference ids the lot quotes there too (L367, see above).
|
|
372
382
|
- **The `## Hypothèses` section of the news entry.** The `implement` brief asks for an entry that ends with `## Hypothèses`: one line `- Rn — …` per rule whose status is `hypothese`, with what changes if it is wrong (its `si_faux`) — it is what the lead shows for the `raf ux` decision, the choices nobody validated.
|
|
373
383
|
|
|
374
384
|
```markdown
|
|
@@ -490,6 +500,21 @@ except a copy that is still needed, which stays and is said: a copy that a live
|
|
|
490
500
|
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
|
|
491
501
|
(bin, dist, package.json).
|
|
492
502
|
|
|
503
|
+
## The subject of the plan commits (`plan.commit`, L343)
|
|
504
|
+
|
|
505
|
+
The commits the program writes for the plan in a wave (`plan: <lot> démarré (orchestrate <wave>)`, `… revue de code enregistrée`, `… revue de code notée`, `… hypothèses de la fiche <sujet> notées`, `… mineurs de la revue de <revue> notés`) take the template of the `plan.commit` key of `cadence.yaml`
|
|
506
|
+
(`{lot}`, `{action}`, `{vague}`; the same key sets the plan commit of `cadence spec reseal`; absent, the subject is unchanged — keys and rules in [docs/raf.md](raf.md#the-subject-of-the-plan-commits-plancommit-l343)).
|
|
507
|
+
A project whose `commit-msg` hook enforces Conventional Commits sets for instance `plan: { commit: 'chore({lot}): {action} (orchestrate {vague})' }`.
|
|
508
|
+
|
|
509
|
+
`--dry-run` plays the subjects the lot would write, for each lot, through the repository's **real** `commit-msg` hook, as git would run it (`git rev-parse --git-path hooks/commit-msg`: `core.hooksPath` and husky included; a hook that is not executable is skipped by git and here). Every lot writes `démarré`, `revue de code enregistrée` and `revue de code notée`; a lot with a spec stage (`spec: true` or `screen:`) also writes `mineurs de la revue de la fiche notés`, the sheet sourced by the lead's answer (action `règles R1, R2 sourcées par la réponse du lead`, through the same `plan.commit` template; absent, `docs(<lot>): règles R1, R2 sourcées par la réponse du lead (orchestrate <wave>)`, lower-case after the colon because `subject-case` refuses a capital) and, for a `spec: true` lot without a screen, `hypothèses de la fiche <sujet> notées`. Those sheet subjects are measured with a subject of **real length**, since `header-max-length` (100) depends on it: the lot's sheet when it is already written (its `sujet.id` for the hypotheses, its **costly** hypotheses for the lead's answer, since the answer only sources the rules the wave asked about — with none, no answer is committed and none is played), otherwise the longest subject of the sheets folder and as many rules as the sheet of the folder that has the most costly hypotheses (one at least); with no sheet in the repository at all, `<sujet>` and one rule, with a warning that the measure is short. Measuring the answer with every hypothesis of a sheet (13) declared refused an answer that cites five. It prints under the lot:
|
|
510
|
+
|
|
511
|
+
- `messages de plan : 3 message(s) acceptés par le hook commit-msg du dépôt (<hook>)` (5 or 6 for a lot with a spec stage: the hypotheses subject only exists for a `spec: true` lot without a screen) when none is refused;
|
|
512
|
+
- `✗ message de plan refusé par le hook commit-msg : « <subject> » — <what the hook said>` for each one refused, then the advice to set `plan.commit` (it governs every one of them, the lead's answer included): without it the lot would fail on its first refused plan commit, before or during its sessions (the hypotheses are noted after the spec session). The dry-run still exits 0 (it simulates, it does not gate);
|
|
513
|
+
- for a lot with a spec stage, what the measure used: `fiche du lot : docs/specs/<sujet>/fiche.yaml (hypothèses R1, R2)`, or `fiche du lot pas encore écrite : mesure avec le sujet le plus long de docs/specs (<sujet>) et <n> règle(s)`;
|
|
514
|
+
- nothing when the repository has no `commit-msg` hook and no `plan.commit` is set.
|
|
515
|
+
|
|
516
|
+
When a plan commit is refused all the same (a hook of the project, a header too long), the plan files are unstaged and what cadence wrote is undone before the lot fails, so the next lot of the wave does not find a dirty repository and is not handed back. What the lead had changed in the plan without committing before cadence wrote (a `raf add` or `raf note` in the project repository during a session: the plan files, the files maintained with them, the `acceptation.json`) is put back as it was, not erased: those files stay modified, as before the commit, and only the content cadence added (the hypotheses notes, the review record) is gone.
|
|
517
|
+
|
|
493
518
|
## One worktree per lot (`orchestrate.worktrees`, L279)
|
|
494
519
|
|
|
495
520
|
> **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
|
|
@@ -522,7 +547,7 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
522
547
|
read-only plan (`plan:` in `cadence.yaml`) gets `orchestrate.start` and `orchestrate.verdict` run in that checkout, never in the worktree; a plan
|
|
523
548
|
outside the repository is read and written in place, without a commit. The lots' branches carry work commits only (the acceptance seal and
|
|
524
549
|
`vert: true` are work of the lot), so a plan conflict at the rebase cannot happen. A session that modifies the plan file, a file generated with it
|
|
525
|
-
(`plan.files`, the Gantt page) in its worktree — committed or not — gets its lot handed back, naming the file and the commit.
|
|
550
|
+
(`plan.files`, the files of `plan.onWrite`, the Gantt page) in its worktree — committed or not — gets its lot handed back, naming the file and the commit.
|
|
526
551
|
Before each of these writes the program checks that the repository is still on the branch the lots started from, that no tracked file other than the
|
|
527
552
|
plan is modified in the project's checkout, and takes the lock; a refusal hands the lot back with the cause
|
|
528
553
|
(`écriture du plan refusée : arbre du dépôt modifié (<files>)`, `dépôt sur <branch> et non <start branch>`, `verrou tenu par <wave> (pid <pid>)`), writes
|
|
@@ -535,6 +560,11 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
535
560
|
(`arbre du dépôt modifié (<files>)`): the program never changes the lead's branch or tree. The check is made again right before the
|
|
536
561
|
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
|
|
537
562
|
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);
|
|
563
|
+
**When the repository's branch itself moved** during that time (a plan commit of the lead, another lot closed by hand), the fast-forward is no longer
|
|
564
|
+
possible: instead of handing the lot back, the program replays steps 2 to 5 at the new tip (rebase, replayed checks, verdict carry-over or short
|
|
565
|
+
review, fast-forward), at most **twice**, and logs `<project>:<lot> main a avancé pendant l'intégration, rebase rejoué (n/2)`. A branch that moves again
|
|
566
|
+
after the second replay hands the lot back with `intégration refusée : main a encore avancé après 2 rebases rejoués`, `next: integrate`, nothing of
|
|
567
|
+
the lot in the repository's branch (L364);
|
|
538
568
|
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
|
|
539
569
|
is handed back with the files in conflict, its worktree and branch untouched, and no session tries to resolve it. **One exception** (L314): two
|
|
540
570
|
parallel lots that each add an entry under `## [Unreleased]` of `CHANGELOG.md` (what `docs.sync` imposes on every lot) always collide, so a commit
|
|
@@ -544,7 +574,19 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
544
574
|
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
|
|
545
575
|
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
|
|
546
576
|
from the repository's side — while an entry that was already there at the starting point, reworded, stays a conflict by hand. The
|
|
547
|
-
journal says `<project>:<lot> CHANGELOG.md : conflit d'ajout pur résolu par union sous [Unreleased] (<n> commit(s))`.
|
|
577
|
+
journal says `<project>:<lot> CHANGELOG.md : conflit d'ajout pur résolu par union sous [Unreleased] (<n> commit(s))`. **A release during the branch's life** (L362):
|
|
578
|
+
when the repository's branch was released after the lot's branch was cut, its `## [Unreleased]` became
|
|
579
|
+
`## [x.y.z]` and git, merging without conflict, files the lot's entries under that published version. After the rebase the program moves the entries of
|
|
580
|
+
the lot (those in its `[Unreleased]` and not in the `CHANGELOG.md` of the starting point) that landed in the published part, and were not already
|
|
581
|
+
there on the repository's branch, back under the repository's `## [Unreleased]`, in a commit of its own (`docs: CHANGELOG — les entrées du lot replacées
|
|
582
|
+
sous [Unreleased] …`); the journal says `<project>:<lot> CHANGELOG.md : <n> entrée(s) du lot replacée(s) sous [Unreleased], …`. When the release and the
|
|
583
|
+
lot's addition touch the same lines, the conflict is resolved too (L369): an entry of the commit's parent that the repository's branch now keeps under a
|
|
584
|
+
published version was **moved by the release, not removed**, so it does not count as a removal and is not written again under `[Unreleased]`. Only an entry of the parent, whole, counts as moved: a new entry of the lot whose text is a fragment of a published one (« Fix typo » when the release carries « Fix typo in README ») is not that entry, it stays under `[Unreleased]`, and the same whole-entry match applies when the program looks for a lot's entry in the published part.
|
|
585
|
+
**One final entry per lot** (L369): the lot's commits may write then reword their entry several times (L360 on 2026-10-26: four contradictory entries
|
|
586
|
+
stacked). After the rebase the program lists the entries the lot's commits wrote under `[Unreleased]` that are neither at the starting point nor at
|
|
587
|
+
the lot's tip (nor under a version the tip itself carries: an entry the lot published itself is final), drops them from the result (under `[Unreleased]` and under a version), and puts back an entry of the tip that is missing, in a commit of
|
|
588
|
+
its own (`docs: CHANGELOG — une entrée finale par lot …`); an entry the repository's branch also carries is left alone. The union of an earlier replayed commit files the lot's entry at the end of its section; where the lot first put it (top of the section included) does not matter for the next replayed commit. The journal says
|
|
589
|
+
`<project>:<lot> CHANGELOG.md : une entrée finale par lot, les versions intermédiaires du lot écartées`. Anything else — an entry that
|
|
548
590
|
was already there removed or reworded, free text under `[Unreleased]`, the rest of the
|
|
549
591
|
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
|
|
550
592
|
steps 3 and 4 apply: tests replayed, and a short review since the patch-id changed;
|
|
@@ -580,7 +622,8 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
580
622
|
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
|
|
581
623
|
anything is written, like the other resume refusals): a lot unknown or named twice, also named by `--drop`, `--answer` or `--budget`, a lot that is not
|
|
582
624
|
handed back at the integration step (`le lot n'est pas rendu à l'étape d'intégration (<status>, étape <step or aucune> : <cause>)`: a lot handed back for
|
|
583
|
-
review, tests or budget has `next` empty and takes the recovery path below), and, like any resume, a repository with a modified tracked file.
|
|
625
|
+
review, tests or budget has `next` empty and takes the recovery path below, unless the lead had it corrected and reviewed off the wave: next paragraph), and, like any resume, a repository with a modified tracked file.
|
|
626
|
+
**A lot handed back for its budget or a non-compliant review, that the lead had corrected and reviewed outside the wave (L370)** is accepted by `--resume --wave <wave> --integrate <project:lot>` too: the lead fixed it in the kept worktree, then recorded a compliant review with `raf review <lot> "…"` run in that worktree and committed the plan there (the review is read from the worktree's plan, not from the main repository's), and the recorded `commit` of that review is the **last work commit of the lot in its worktree** (no commit after the review; a review that predates a commit, or none, keeps the refusal above). The lot restarts at the integration step (`<project>:<lot> → suspended — intégration reprise (--resume --integrate) : revue conforme du lead hors vague, <sha>`) with the lead's verdict in place of the wave's review: rebase on the repository's branch, checks replayed, the verdict carried over when the diff is the same (same patch-id) or a short review of the rebased diff (the plan commit made in the worktree is part of that diff, so a short review is the usual case; if the lead also wrote on that lot in the repository's plan meanwhile — `raf note`, `raf add --parent` — the plan conflict of the rebase is settled by the program, which keeps the repository's plan and writes the lead's review back on it, provided the commit only adds lot reviews — nothing else on a lot, no lot added or removed, and nothing outside `lots` such as an `acknowledged:` entry from `raf ignore` or a head setting, which a kept repository plan would silently lose — and the repository did not change that review itself; any other plan conflict hands the lot back as before), then the advance. Handed back for another cause (acceptance lock, `HEAD` moved…), a lot that meets the condition is accepted the same way; `--replay` never takes this path. The lead no longer integrates by hand for it.
|
|
584
627
|
- **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
|
|
585
628
|
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>`):
|
|
586
629
|
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>`
|
|
@@ -793,7 +836,9 @@ News instruction (`cadence news new <lot>`, factual user-side text, a screenshot
|
|
|
793
836
|
```yaml
|
|
794
837
|
orchestrate:
|
|
795
838
|
test: npm test # run by the orchestrator after a work step (optional)
|
|
839
|
+
testReplay: npx vitest run --minWorkers=1 --maxWorkers=$CADENCE_TEST_WORKERS {files} # replay alone of a red test before it counts (L354, see « A red test is replayed alone »); `{files}` required; absent: no replay, nothing held for unstable (optional)
|
|
796
840
|
build: npm run build # run BEFORE the tests (L333); both results go to the reviewer, who does not redo them (optional)
|
|
841
|
+
worktreeDeps: [node_modules, .venv] # untracked dependencies that `cadence verify --gate` links into its throwaway worktree (L348, `docs/deliver.md`); absent: the ignored folders of every level, as the worktrees of a wave (optional)
|
|
797
842
|
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)
|
|
798
843
|
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)
|
|
799
844
|
maquette: true # default: UX review of the mockup between spec and accept (see « The mockup review »); false skips it
|
|
@@ -860,7 +905,12 @@ the `.nvmrc` again for every live lot and refuses it like at start if it can no
|
|
|
860
905
|
The file is read trimmed (`22`, `v22`, `22.22`, `v22.22.3`); the highest matching version installed under
|
|
861
906
|
`$NVM_DIR/versions/node` (default `~/.nvm/versions/node`) **that has an executable `bin/node`** is used: a higher
|
|
862
907
|
version with an empty or broken `bin` is skipped for a lower valid one (and `--dry-run` says so); if none is valid
|
|
863
|
-
the lot is refused.
|
|
908
|
+
the lot is refused. The same `PATH` goes to every command the program
|
|
909
|
+
launches itself — `orchestrate.test`, `orchestrate.build` (the baseline run at the start sha included), `start`, the acceptance
|
|
910
|
+
runs and `verdict` — so the gate judges the lot under the Node the session wrote it for, not under the one the wave was
|
|
911
|
+
started with. `--dry-run` adds `⚠ .nvmrc 24 ≠ node du PATH v20.20.2 : …` under the lot when `orchestrate.test` or
|
|
912
|
+
`orchestrate.build` is declared and the `node` the `PATH` resolves does not match the `.nvmrc` (nothing is printed without a
|
|
913
|
+
gate, without a `node` on the `PATH`, or when they match). Only the environment of the sessions and of these commands changes,
|
|
864
914
|
never the orchestrator's own. No `.nvmrc` → nothing changes. A `.nvmrc` that cannot be resolved (`lts/*`, an
|
|
865
915
|
alias, a version not installed) refuses the lot before anything is started (exit 2), naming the requested
|
|
866
916
|
version and the folder searched — there is no silent fallback to the default Node. `--dry-run` prints
|
|
@@ -886,7 +936,11 @@ folder whenever Playwright was loaded for the lot, even if no `ux` step ran
|
|
|
886
936
|
|
|
887
937
|
Without `start`, a read-only plan's `todo` lot is refused (start it with the project's tool); without
|
|
888
938
|
`verdict`, the review verdict stays in the wave's state and you report it. Only the plan's files
|
|
889
|
-
(`plan.path`, `plan.files`, the QA expectations file) are committed from those commands; anything else dirty stops the lot (`.cadence/runs/`, `.cadence/corrections.json` and `.playwright-mcp/` excepted, as above).
|
|
939
|
+
(`plan.path`, `plan.files`, the files of `plan.onWrite`, the QA expectations file) are committed from those commands; anything else dirty stops the lot (`.cadence/runs/`, `.cadence/corrections.json` and `.playwright-mcp/` excepted, as above).
|
|
940
|
+
|
|
941
|
+
### `plan.onWrite`: the published plan follows every write (L346)
|
|
942
|
+
|
|
943
|
+
When `cadence.yaml` declares `plan.onWrite: { command, files }` ([raf](raf.md#regenerate-the-published-copy-on-every-write-planonwrite-l346)), the wave runs `command` at the repository root after each write of the plan it makes (start of the lot, notes of the sheet and of the review, review recorded), and the produced `files` go into the same `plan: …` commit as the plan, even when they were not tracked yet. If the command fails the write is refused: the plan is put back, nothing is committed, and the lot is handed back naming `plan.onWrite en échec`.
|
|
890
944
|
|
|
891
945
|
### Time limit of a write step (L298)
|
|
892
946
|
|
package/docs/overview.md
CHANGED
|
@@ -65,9 +65,9 @@ The two plugins are independent. `cadence@cadence` carries the rituals, which ca
|
|
|
65
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
66
|
| [docs/deliver.md](deliver.md) | delivery: wait for CI, deploy, verify the effect |
|
|
67
67
|
| [docs/releasing.md](releasing.md) | releasing: `cadence release patch\|minor\|major` prepares the version commit (version fields, CHANGELOG, `docs/whats-new.md` check) and prints the lead's steps; nothing is published by it |
|
|
68
|
-
| [docs/spec.md](spec.md) | the screen and back-end sheets, `cadence spec check\|qa\|reseal` |
|
|
68
|
+
| [docs/spec.md](spec.md) | the screen and back-end sheets, `cadence spec check\|qa\|reseal\|green` |
|
|
69
69
|
| [docs/lead.md](lead.md) | the lead workflow and `cadence lead tour` |
|
|
70
|
-
| [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/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 coverage` (which elements of the inventory no done lot covers, nor a written exclusion), `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) |
|
|
71
71
|
| [docs/stabilite.md](stabilite.md) | what is guaranteed from 1.0.0 and what is not |
|
|
72
72
|
|
|
73
73
|
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).
|
|
@@ -83,6 +83,7 @@ Also: [news](news.md) (changelog, UX, code and QA reviews, documentation that fo
|
|
|
83
83
|
- **real walkthrough and decisions** (L344): once the tests are green an `implement` session plays what it delivered as a user (the command in a throwaway repository following its own documentation, the screen in each state with the failure in view) and reports it under « parcours réel »; the notes of the lot and of its parent that record a decision of the human reach the `implement` and `fix` briefs under « Decisions to respect », a departure being a question, never a choice (`docs/orchestrate.md`).
|
|
84
84
|
- **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)).
|
|
85
85
|
- **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)).
|
|
86
|
+
- **reference-planner** (L367): an agent that reads an inventory and the plan and proposes lots that cite the ids they cover, feasible or blocked, with their dependencies; it creates nothing (`cadence ref coverage` lists what is left).
|
|
86
87
|
- **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)).
|
|
87
88
|
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.
|
|
88
89
|
|
package/docs/perf.md
CHANGED
|
@@ -37,9 +37,11 @@ current window, with at least 10 classified passes) is flagged by a `⚠` even w
|
|
|
37
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`.
|
|
38
38
|
- **Share within budget** (L349). `part des rendus sur budget` counts a lot against its budget **minus the share kept for the escalation** (`escalationReserve` of the journal, `docs/orchestrate.md`) as long as its escalation was not engaged: the ordinary passes stop that share earlier, so counting them against the whole budget would call a lot that hit its limit « within budget ». Once the escalation is engaged (a journal `escalation` whose issue is not `non jouée`) the share is released at run time, so all the steps of the lot, whatever their model, are counted against the whole budget. An escalation `non jouée` engaged nothing: the ordinary passes ate the share, so the lot stays counted against its budget minus the share. A journal with no `escalationReserve` (a wave before L349, or no `orchestrate.escalation`) is counted against its whole budget, as before.
|
|
39
39
|
- **Model escalations** (L337). Under the section « Escalade de modèle », for the current window: `escalades de modèle : n lots · k conforme · r rendu malgré l'escalade · b non jouée · e en échec` — the finished lots whose journal carries an `escalation` (`orchestrate.escalation`, `docs/orchestrate.md`) and how it ended: the fresh review on the escalation model was compliant (`conforme`), the lot was handed back despite it (`rendu`), the lot's budget did not cover it (`non jouée`), or an escalated session returned nothing — model unavailable to the account, cut, timeout (`en échec`, the model never judged the lot); a lot interrupted during its escalation, withdrawn from the wave (`--drop`) or stopped by an incident of the repository is counted among the lots but in no outcome, never as `en échec`. Nothing is printed without an escalation. In `--json`: `escalations` (`{ current, previous }`, each `{ lots, conforme, rendu, nonJouee, echec }`). Read it to see whether the escalation model earns its cost: many `rendu` means the stronger model does not settle what the passes could not, so the brief or the sheet is at fault.
|
|
40
|
+
|
|
41
|
+
- **Tests unstable under load** (L354). Under the section « Porte de tests sous charge », for the current window: `tests instables sous charge : n tests dans k lots (rouges dans la suite, verts rejoués seuls ; précédente m)` — the tests of the rendered lots that were red in the gate's whole suite and green when the gate replayed them alone (`instables` of the lot journal, only for a project that declares `orchestrate.testReplay`, `docs/orchestrate.md`): they were neither counted nor corrected. Nothing is printed without one. In `--json`: `instables` (`{ current, previous }`, each `{ lots, tests }`). A rise says the machine is too loaded for the suite: lower the session cap or the `CADENCE_TEST_WORKERS` share before reading it as flaky tests.
|
|
40
42
|
- **`--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 `⚠`).
|
|
41
43
|
It is **not** the instant of `cadence corrections --since` (waves created strictly after).
|
|
42
|
-
- **`--json`** prints one object `{ since, today, windows: { current, previous }, throughput, corrections, interventions, precheck, escalations, tokens, delays, durations, cost, quality, orphans, warnings }`; every indicator carries
|
|
44
|
+
- **`--json`** prints one object `{ since, today, windows: { current, previous }, throughput, corrections, interventions, precheck, escalations, instables, tokens, delays, durations, cost, quality, orphans, warnings }`; every indicator carries
|
|
43
45
|
`{ current, previous, change, warn, twin, lots, note }` (`twin`: the path of its quality twin, e.g. `throughput.escaped`). Same values as the text.
|
|
44
46
|
|
|
45
47
|
## Tokens, delays, wall time and cost (L226)
|
package/docs/raf.md
CHANGED
|
@@ -55,7 +55,8 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
55
55
|
| `raf spec <id> on\|off` | set or remove `spec:` on an existing lot (a lot that changes a behaviour, for the back-end spec step of `cadence orchestrate`, see [The spec step of a lot with `spec: true`](orchestrate.md#the-spec-step-of-a-lot-with-spec-true)); refused on a sub-task; `off` is refused on a lot that has `screen: page` (the page implies the spec: remove `screen:` first) and `on` there only says so and changes nothing; on `component` and `journey` (ordinary path since L209) both work and set or remove the back-end `spec:`. `raf spec` only takes `on`/`off`: `cadence spec check\|qa <fiche.yaml>` keeps its own subcommands. `raf check` reports a `spec:` that is neither `true` nor `false` (`spec : true ou false attendu`) |
|
|
56
56
|
| `raf uses <lot> [<resource>…]` | replace the `uses:` list of a lot of any status (`done` and `dropped` included) by the names given (duplicates removed, order kept); with no name, remove the key. Prints nothing. A name is a word of lowercase letters, digits, `-` and `_` (else `ressource invalide : <name> (lettres minuscules, chiffres, - et _)`, plan unchanged); a sub-task is refused (`uses se pose sur un lot, pas sur une sous-tâche : L1/t1`); an unknown lot is `lot inconnu : <id>`. A name not yet declared is accepted: `raf check` refuses it (L251) |
|
|
57
57
|
| `raf public <id> "title"` · `raf public <id> --clear` | set, replace or remove the public title of a lot |
|
|
58
|
-
| `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`) |
|
|
58
|
+
| `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`, and a lot whose `acceptation-<lot>.json` is still `vert: false`, L366: closed like that it would lock every later wave of the repository, see `cadence spec green`; with `--force` it only warns on stderr. Like every `raf` command it only writes the plan file and **never commits** it: when the plan or a file `plan.onWrite` produces is left with changes, `done` prints the `git add` / `git commit` command that takes them, the subject from the `plan.commit` template, L371) |
|
|
59
|
+
| `cadence spec green <lot>` | L366: run the dedicated acceptance command (`orchestrate.accept`) on a lot driven or closed by hand; green, set `vert: true` in its `acceptation-<lot>.json` and commit that file alone (subject `chore(<lot>): acceptation verte, vert: true posé par cadence spec green` or the `plan.commit` template; the resealed tests are listed in the commit body, never in the subject), resealing the fingerprints of the listed tests refined since the red commit (journal `resceaux`) so the lock goes quiet; a listed test changed but not committed is refused. Red: exit 1, nothing changed. Refused for a `todo` lot, during a wave, and while an unfinished wave keeps a lot of the repository open; see [orchestrate](orchestrate.md) |
|
|
59
60
|
| `raf todo <id> [--reason text]` | put a started lot back to `todo` (`doing` → `todo`, `started:` removed, dated note « remis en attente : reason »): refused when work commits cite the lot (plan-only and version commits do not count, as `raf commits`), when the lot is not `doing` (a `done` or `dropped` lot is reopened by hand), or on a sub-task; sub-tasks and other keys are left as they are |
|
|
60
61
|
| `raf migrate [--dry-run]` | move `docs/plan/raf.yaml` to `.cadence/raf.yaml` and rewrite the readers of the old path in the repository, see [Moving the plan: `raf migrate`](#moving-the-plan-raf-migrate) |
|
|
61
62
|
| `raf note <id> "text"` | dated note — keep decisions next to the work |
|
|
@@ -65,7 +66,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
65
66
|
| `raf now` | what to do next |
|
|
66
67
|
| `raf list [--status s]` | flat list |
|
|
67
68
|
| `raf docs-ok <lot> --reason text` | acknowledge the `docs.sync` gap of a lot (a document behind its commits, `raf check` red) when the documentation really stays true: a `docs_ok: { date, reason }` key on the lot, **the reason is required** (refused without it, for an unknown lot or for a sub-task); `raf check` and `cadence lead tour` no longer count the lot, `raf show` prints it, and `cadence orchestrate` neither calls the librarian nor holds the lot back for it. Commit the plan with it |
|
|
68
|
-
| `raf ignore <sha> \| "exact subject" [--reason text]` | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) without rewriting history: a dated, reasoned line in the plan's `acknowledged:` section; a sha is exact, a subject covers every commit carrying it; exactly one commit argument, the reason goes through `--reason "text"` (`raf ignore 1a2b3c4 "tooling"` is refused with « un seul commit attendu ; la raison se passe par --reason "texte" », exit 2) |
|
|
69
|
+
| `raf ignore <sha> \| "exact subject" [--reason text]` | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) or a commit that cites known lots without working on them (a lead's plan commit creating lots, a setting cited in parentheses): it then counts for none of them — not started, not active, not to review (`raf commits` omits it) — 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) |
|
|
69
70
|
| `raf check --ignored` | list the acknowledged commits with their date and reason |
|
|
70
71
|
| `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) |
|
|
71
72
|
| `raf gantt [-o file]` | standalone Gantt page |
|
|
@@ -142,6 +143,27 @@ then the legacy `docs/plan/raf.yaml`.
|
|
|
142
143
|
- `cadence lead tour`, `session --all` and `cadence orchestrate` recognise a project by `.cadence/raf.yaml`, `docs/plan/raf.yaml` or a
|
|
143
144
|
`cadence.yaml` with `plan:`.
|
|
144
145
|
|
|
146
|
+
## The subject of the plan commits: `plan.commit` (L343)
|
|
147
|
+
|
|
148
|
+
`cadence orchestrate` (the lot started, the code review recorded or noted, the hypotheses and minors of a lot noted) and `cadence spec reseal` (the lot's note) commit
|
|
149
|
+
the plan on their own. Their subject has always been `plan: <lot> <action> (orchestrate <wave>)` (`plan: <lot> <action>` outside a wave), which a `commit-msg` hook
|
|
150
|
+
that enforces Conventional Commits (commitlint with `config-conventional`, husky) refuses: `plan` is not one of its types, the lot failed before any session and the next one
|
|
151
|
+
was handed back with the plan left staged (GitHub issue #15). `plan.commit` in `cadence.yaml` sets the subject of **all** of them:
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
plan:
|
|
155
|
+
commit: 'chore({lot}): {action} (orchestrate {vague})'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- `raf done <lot>` does not commit the plan (it never does, see below) but prints the command that would, with this template: `{action}` is `terminé` and, outside a wave, the group holding `{vague}` is dropped (`chore(L12): terminé`). Without a template: `plan: L12 terminé`.
|
|
159
|
+
- `{lot}` is required (a commit must cite its lot); `{action}` is what was done (`démarré`, `revue de code enregistrée`, `revue de code notée`, `hypothèses de la fiche <sujet> notées`, `mineurs de la revue de <revue> notés`, `règles R1, R2 sourcées par la réponse du lead`, `test d'acceptation rescellé (<test>)`);
|
|
160
|
+
`{vague}` is the wave id. A word between braces that is none of these, an empty template or one on several lines is refused when the config is read.
|
|
161
|
+
- Outside a wave (`spec reseal`) there is no wave: the parenthesised group that holds `{vague}` is dropped, so the template above gives `chore(L17): test d'acceptation rescellé (…)`.
|
|
162
|
+
- Absent, the subject is the one above, unchanged. The key needs `plan:` to be an object (`plan: .cadence/raf.yaml` as a plain path has nowhere to put it: write `plan: { path: …, commit: … }`).
|
|
163
|
+
- `cadence spec green` (L366) commits its `acceptation-<lot>.json` with the template too when there is one (`{action}` = `acceptation verte, vert: true posé par cadence spec green`); without one its subject stays `chore(<lot>): …`. The tests it reseals are listed in the commit body, never in the subject, which therefore does not grow with them.
|
|
164
|
+
- The commit of the fiche sourced by the lead's answer takes the template too (its default, `docs(<lot>): règle R3 sourcée par la réponse du lead (orchestrate <wave>)`, is already Conventional but can exceed `header-max-length` with several rules and a wave id). The `test(<lot>): …` and `chore(<lot>): …` commits of the cycle (acceptation sealed, `vert: true` set) and the work commits are already Conventional and are not templated.
|
|
165
|
+
- `cadence orchestrate … --dry-run` runs every plan subject each lot would write through the repository's real `commit-msg` hook (found as git finds it: `core.hooksPath` and husky included), the sheet subjects measured with a subject of real length (the lot's sheet, or the longest of the folder) and the lead's answer with the costly hypotheses it would source, and names the one it would refuse — see [docs/orchestrate.md](orchestrate.md#the-subject-of-the-plan-commits-plancommit-l343).
|
|
166
|
+
|
|
145
167
|
## Moving the plan: `raf migrate`
|
|
146
168
|
|
|
147
169
|
`raf migrate [--dry-run]` moves a legacy `docs/plan/raf.yaml` to `.cadence/raf.yaml` and updates what reads the old path, so the
|
|
@@ -213,6 +235,31 @@ plan:
|
|
|
213
235
|
files: [frontend/public/plan-data/plan.json]
|
|
214
236
|
```
|
|
215
237
|
|
|
238
|
+
### Regenerate the published copy on every write (`plan.onWrite`, L346)
|
|
239
|
+
|
|
240
|
+
Declaring the published file is not enough when it is generated: a hand-regenerated
|
|
241
|
+
`plan.json` that drifts from the plan (a flag forgotten) turns the project's
|
|
242
|
+
« published plan up to date » check red. `plan.onWrite` makes the program run the
|
|
243
|
+
generator itself, after **every** write of the plan:
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
plan:
|
|
247
|
+
onWrite: { command: npm run plan, files: [frontend/public/plan-data/plan.json] }
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
- `command` runs through `sh -c` at the repository root (the folder of `cadence.yaml`, whatever folder `raf` is run from), with a
|
|
251
|
+
2-minute limit. It runs after `raf add|start|done|drop|todo|note|did|title|uses|public|screen|visible|spec|ux|review|ignore|docs-ok`, after the writes of `cadence orchestrate`
|
|
252
|
+
(start of the lot, notes of the review and of the sheet, review recorded), after the plan note of `cadence spec reseal`, after `cadence deliver` opens an article lot, and after the config lot
|
|
253
|
+
of `cadence config set --all`. There is one code path for all of them, so a new command that writes the plan gets it for free.
|
|
254
|
+
- `files` (optional list of files, relative to the repository, never outside it; a folder is refused with `plan.onWrite.files « <path> » est un dossier`) are the files the command produces. They are **kept with the plan**, like `plan.files`: a commit that touches only
|
|
255
|
+
them and the plan is plan upkeep, and they go **in the same commit as the plan** everywhere the program commits the plan (`cadence orchestrate`, `cadence config set`, `cadence spec reseal`, the
|
|
256
|
+
`hook.autostart: start` pre-commit). A produced file that is not tracked yet is added. `raf` itself never commits: for its commands, `git add` the plan and the produced files together.
|
|
257
|
+
- A command that fails (non-zero code, cannot start, time limit) **refuses the write**: the plan and the declared files are put back as they were, the command stops with
|
|
258
|
+
`plan.onWrite en échec (<command>, code 7) : <last lines of its output> — écriture refusée, plan inchangé`, and in a wave the lot is handed back with that message. With `hook.autostart: start`, the pre-commit hook says `démarrage automatique refusé : plan.onWrite en échec …`, the lot stays todo and the commit goes through without the plan (never the « raf start fait par le hook » line).
|
|
259
|
+
`cadence spec reseal` commits the reseal before writing its note: if `plan.onWrite` then fails, the error says `le rescellé a eu lieu (commit <sha>) mais la note du lot n’a pas été écrite` (exit 2, plan unchanged); running the command again answers « rien à resceller », so add the note by hand with `raf note` once the generator works.
|
|
260
|
+
A command that succeeds without producing a declared file (a wrong path in `files`) does not refuse the write, but says `raf: plan.onWrite a réussi mais le fichier déclaré est absent : <path>` on stderr — the file it really wrote would otherwise stay out of the plan's commit.
|
|
261
|
+
- A read-only plan (kept by another tool) never runs the command: the project's tool owns the write.
|
|
262
|
+
|
|
216
263
|
Until the file is declared, such a commit is reported as a "commit without a
|
|
217
264
|
lot" when it cites none, and counts as work on the lots it cites.
|
|
218
265
|
|
|
@@ -244,6 +291,7 @@ plan:
|
|
|
244
291
|
repos: depots # the lot's neighbouring repositories (a list of paths and/or { path, cite }, or one of them)
|
|
245
292
|
parent: parent
|
|
246
293
|
add: 'mytool add {title}' # optional: the command that creates a lot here (L342: `cadence config set --all` uses it; {title} is replaced by the quoted title)
|
|
294
|
+
commit: 'chore({lot}): {action}' # optional (L343): the subject of the plan commits cadence writes — see « The subject of the plan commits » below
|
|
247
295
|
statuses: # raf status: their states
|
|
248
296
|
todo: [prevu, specifie]
|
|
249
297
|
doing: [en_cours, teste]
|
package/docs/ref.md
CHANGED
|
@@ -7,7 +7,7 @@ tables drawn as pictures, message templates, figures of a norm (WMO, ICAO…). `
|
|
|
7
7
|
turns such a document into files a session can read without losing the pictures. The whole
|
|
8
8
|
design is in
|
|
9
9
|
[`docs/superpowers/specs/2026-10-23-ref-documents-de-reference-design.md`](superpowers/specs/2026-10-23-ref-documents-de-reference-design.md)
|
|
10
|
-
(issue #14); this page describes what is delivered so far: **`cadence ref add`** (lot 1 of 10), **`cadence ref measure`** (lot 2 of 10) **`cadence ref check`** with the inventory schema (lot 3 of 10), **`cadence ref eval`** (lot 4 of 10, with the synthetic corpus `cadence ref corpus`) **`cadence ref analyse`** (lot 5 of 10: the `reference-analyst` agent, one single reading) and **`cadence ref diff`** with the double reading (lot 6 of 10). **The series stops at lot 6**: the stopping point of the double reading (hypothesis H1) was not met, so lots 7 to 10 are dropped and the double reading stays an option, `--double` ([measurement and conclusion](mesures/ref-double-lecture-2026-10-25.md)).
|
|
10
|
+
(issue #14); this page describes what is delivered so far: **`cadence ref add`** (lot 1 of 10), **`cadence ref measure`** (lot 2 of 10) **`cadence ref check`** with the inventory schema (lot 3 of 10), **`cadence ref coverage`** (the coverage of the document by the lots of the plan), **`cadence ref eval`** (lot 4 of 10, with the synthetic corpus `cadence ref corpus`) **`cadence ref analyse`** (lot 5 of 10: the `reference-analyst` agent, one single reading) and **`cadence ref diff`** with the double reading (lot 6 of 10). **The series stops at lot 6**: the stopping point of the double reading (hypothesis H1) was not met, so lots 7 to 10 are dropped and the double reading stays an option, `--double` ([measurement and conclusion](mesures/ref-double-lecture-2026-10-25.md)).
|
|
11
11
|
|
|
12
12
|
## `cadence ref add <document.pdf> [--sections 3-5,9] [--slug name]`
|
|
13
13
|
|
|
@@ -290,7 +290,7 @@ text of a `texte`, role and content of a `composant`, figure of a `lien`…), si
|
|
|
290
290
|
| **silence** | a question, in either reading | gathered (one question once), never sent to a third look and not counted below: a silence is not a reading that someone else contradicts |
|
|
291
291
|
|
|
292
292
|
More than **30 %** of the paired-or-single elements unpaired (silences left out, `--seuil` changes it, in percent) means the two readings do not describe the same document: no reconciliation, the question goes to the lead (exit 1, nothing written).
|
|
293
|
-
The ids of the result are renumbered `<slug>#<type>-<n>` (reading A in its order, then what B alone carries) and the references (`composant.ecran`, `etat.cible`, `statut.couvre`) follow; an element that referred to a discarded one is discarded with it, **except a paired element** (both readings carry it): it holds while one of its two versions points to a kept element, and is then `incertain` with the cause when only one version does; it is discarded only when both versions point to discarded elements (see `ref analyse`, « Reconciliation »).
|
|
293
|
+
The ids of the result are renumbered `<slug>#<type>-<n>` (reading A in its order, then what B alone carries), except that an element read again keeps the id it had in the inventory `--out` replaces (see `ref coverage`) and the references (`composant.ecran`, `etat.cible`, `statut.couvre`) follow; an element that referred to a discarded one is discarded with it, **except a paired element** (both readings carry it): it holds while one of its two versions points to a kept element, and is then `incertain` with the cause when only one version does; it is discarded only when both versions point to discarded elements (see `ref analyse`, « Reconciliation »).
|
|
294
294
|
|
|
295
295
|
```
|
|
296
296
|
$ cadence ref diff a.json b.json --out inventaire.json
|
|
@@ -334,7 +334,7 @@ and output tokens of the session (the count of the orchestrate budget; cache rea
|
|
|
334
334
|
in the list counts as **no answer**: the element stays `incertain` (`troisième regard sans réponse`), never a `lu` by default. No element carried by one reading only: no third look, no session;
|
|
335
335
|
- the **silences** of both readings are gathered, once per question, and never sent to a third look.
|
|
336
336
|
|
|
337
|
-
The result is **`<references.dir>/<slug>/inventaire.json`**: `genre: "inventaire"`, valid for `ref check`, ids renumbered, `usage` = `{ tokens, lectures: { a, b, regard } }` (the total of the three sessions and each one's tokens and model, what `ref eval` reads for the tokens per page)
|
|
337
|
+
The result is **`<references.dir>/<slug>/inventaire.json`**: `genre: "inventaire"`, valid for `ref check`, ids renumbered (an element already in the previous `inventaire.json` keeps its id), `usage` = `{ tokens, lectures: { a, b, regard } }` (the total of the three sessions and each one's tokens and model, what `ref eval` reads for the tokens per page)
|
|
338
338
|
and `reconciliation` = the counts, the unpaired ratio and, for each element the third look ruled on, `{ lecture, id (in its reading), verdict, raison }`.
|
|
339
339
|
|
|
340
340
|
A session cut by a signal with nothing written is started again once in a fresh session (like a review); cut twice, it is a failure (exit 2). Without `--double` the single reading is written to `lectures/a.json` and checked as above; `--model-b` and `--seuil` are refused then (exit 2).
|
|
@@ -372,6 +372,64 @@ the same number for a corpus case read whole. Without `--double` the output is t
|
|
|
372
372
|
`docs/mesures/ref-lecture-unique-2026-10-24.md` states what the single reading is measured on and with which commands (recall, false « read », tokens per page against the corpus of `ref corpus`, the declared ceiling), and what was measured.
|
|
373
373
|
The double reading is measured the same way, `ref eval` on `inventaire.json` instead of `lectures/a.json`, and compared with that baseline: `docs/mesures/ref-double-lecture-2026-10-25.md` (hypothesis H1, the stopping point of lot 6: not met, which is why `--double` is an option).
|
|
374
374
|
|
|
375
|
+
## `cadence ref coverage <inventaire.json>`
|
|
376
|
+
|
|
377
|
+
Answers one question without a model: **what of the reference document is covered by a lot?** (L367, GitHub issue #19). It takes the
|
|
378
|
+
inventory of `ref analyse --double` / `ref diff --out`, or the reading `lectures/a.json` that `ref analyse` writes by default (a reading carries
|
|
379
|
+
the same elements and ids). Each element has an **id** `<slug>#<type>-<n>`, and the id **stays the element's address from one reading of the
|
|
380
|
+
document to the next**: `ref diff --out` reads the inventory it replaces, and every `ref analyse` (single reading or `--double`) reads what it replaces
|
|
381
|
+
(the `inventaire.json` of the document, else its `lectures/a.json`); they give an element read again (same type, same page, same identity, the zone that overlaps most)
|
|
382
|
+
its old id, the references (`ecran`, `cible`, `couvre`) following; a new element takes a number above the highest of its type, a number of a vanished
|
|
383
|
+
element is never reused, so a lot that quotes `doc#texte-3` keeps pointing at the same element when one is inserted before it. The written `exclusions`
|
|
384
|
+
come along. (`ref diff` on two readings that were never reconciled before, or a `ref analyse` of a document with no valid previous file, numbers by order.) `ref check` refuses two elements with the same id, and
|
|
385
|
+
`source` says the page and zone of the element. A lot **covers** an element by quoting its id in its title, its goal (`goal:`) or a note
|
|
386
|
+
the lead wrote on it; the sub-tasks' notes and the notes the program writes itself (title replaced, minors of a review, hypotheses, put back on hold) do not count.
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
$ cadence ref coverage inventaire.json
|
|
390
|
+
✗ exemple#texte-3 (texte p.1 zone 120,600,240,64 « Valider ») : aucun lot, aucune exclusion écrite
|
|
391
|
+
✗ exemple#composant-1 (composant p.2 zone 80,40,300,60 « Layers ») : lot non fait (L12 doing)
|
|
392
|
+
✗ exemple#texte-5 (texte p.1 zone 10,10,90,20 « Aide ») : justifié non pris par la fiche de L12 : service absent, sans exclusion écrite
|
|
393
|
+
3 élément(s) sans lot fait ni exclusion écrite sur 11
|
|
394
|
+
$ echo $?
|
|
395
|
+
1
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
An element is **covered** when a lot **done** quotes its id and its sheet does not justify it as not taken, or when the file carries a **written exclusion** with its reason:
|
|
399
|
+
|
|
400
|
+
```json
|
|
401
|
+
"exclusions": [{ "id": "exemple#texte-7", "raison": "décor du document, pas du produit" }]
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
A line `<id> : <reason>` in `sujet.perimetre.hors_perimetre` of the sheet of a lot (`docs/specs/<sujet>/fiche.yaml`, `sujet.lot`) is how the spec-writer justifies an element it
|
|
405
|
+
does not take: that lot does not cover it, it stays a gap until the lead writes its exclusion or another done lot quotes it. (A justification written
|
|
406
|
+
only in the report, or in a sheet with no back part, is not read: the lead removes the id from the lot or writes the exclusion.)
|
|
407
|
+
|
|
408
|
+
`exclusions` is a key of the root of the file (`ref check` ignores it, as it ignores any unknown key). An exclusion without a
|
|
409
|
+
reason, or of an id that is not in the file, excludes nothing and is listed (`✗ exclusions[0].raison : …`). A lot `dropped` covers
|
|
410
|
+
nothing, a lot `todo` or `doing` is named in the line of the element but does not cover it. **The exclusions are written in a file that `ref analyse`
|
|
411
|
+
replaces** (`references.dir` is outside the repository): before it removes or rewrites `inventaire.json`, `lectures/a.json` or `lectures/b.json`, `ref analyse` copies the
|
|
412
|
+
`exclusions` they carry into `exclusions-<date>.json` next to them and says so (`attention : …`); `ref analyse --double` and `ref diff --out` carry those of the inventory they replace into the new one (an id that no
|
|
413
|
+
longer exists is then listed by this command).
|
|
414
|
+
|
|
415
|
+
Exit codes: **0** every element is covered (`couverture complète : N éléments (n par un lot fait, k exclus avec leur raison)`), **1** at
|
|
416
|
+
least one is not (the `✗` lines, then the count, **on the standard output**), **2** wrong call, unreadable file, a file that does not pass `ref check` or no plan. It launches no model and no system tool, reads the plan like `raf` does (`cadence.yaml` `plan:`) and the sheets under `orchestrate.spec.dir`, and writes nothing.
|
|
417
|
+
|
|
418
|
+
### Planning the coverage: the `reference-planner` agent
|
|
419
|
+
|
|
420
|
+
The lead runs `cadence ref coverage` to see the gaps, then has the [`reference-planner`](../agents/reference-planner.md) agent read the inventory
|
|
421
|
+
and the plan. It **only proposes**: lots that cite the ids they cover, each **feasible** or **blocked** (`bloque_par` names the missing datum or
|
|
422
|
+
service, never « missing information »), what each depends on, the elements it would exclude and the silences that are questions for a human. It creates
|
|
423
|
+
no lot and writes no file; the lead creates the lots (`raf add "… exemple#texte-3 …"`) and writes the exclusions. Run
|
|
424
|
+
`cadence ref coverage` again until it exits 0.
|
|
425
|
+
|
|
426
|
+
### Briefs of a wave
|
|
427
|
+
|
|
428
|
+
When the title, the goal or a note of a lot quotes ids of an inventory, `cadence orchestrate` puts them in two briefs (and in `--dry-run`): the
|
|
429
|
+
**spec-writer** takes each into its sheet (the field, rule or state that shows it quotes the id in its `source`) and **justifies each element it does not
|
|
430
|
+
take** (`<id> : <reason>` under « choix » of its report, and in `perimetre.hors_perimetre` when the sheet has a back part); the **ux-reviewer**
|
|
431
|
+
checks each of them on the finished screen and reports a missing or different one as a finding naming its id. A small visible lot has no `ux` pass: its single `review-small` pass carries the same instruction and checks the ids on the finished screen. A lot that quotes no id has the same briefs as before.
|
|
432
|
+
|
|
375
433
|
## `cadence ref corpus <dossier> [--sans-rendu]`
|
|
376
434
|
|
|
377
435
|
Writes the **synthetic corpus with a known truth** that `ref eval` scores against (design §« Évaluation »): seven small documents,
|