@sylad/cadence 1.39.0 → 1.41.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 -3
- package/agents/spec-writer.md +1 -0
- package/agents/ux-reviewer.md +2 -0
- package/dist/cli.js +49 -62
- package/dist/config-set.js +2 -0
- package/dist/config.js +8 -8
- package/dist/deliver-lots.js +37 -0
- package/dist/deliver.js +15 -39
- package/dist/docs-ok-file.js +25 -0
- package/dist/lot-lines.js +51 -0
- package/dist/memory-index.js +113 -0
- package/dist/orchestrate/command.js +8 -8
- package/dist/orchestrate/continue.js +4 -2
- package/dist/orchestrate/cycle.js +23 -29
- package/dist/orchestrate/deja-traitee.js +76 -0
- package/dist/orchestrate/integrate.js +7 -2
- package/dist/orchestrate/lot-budget.js +2 -2
- package/dist/orchestrate/maquette.js +3 -0
- package/dist/orchestrate/spec.js +51 -6
- package/dist/orchestrate/table.js +29 -11
- package/dist/plan-extras.js +118 -0
- package/dist/plan-read.js +144 -0
- package/dist/plan-states.js +119 -0
- package/dist/plan.js +91 -157
- package/dist/raf-close-checks.js +25 -0
- package/dist/raf-status.js +28 -0
- package/dist/session.js +8 -0
- package/dist/state.js +23 -0
- package/dist/verify.js +20 -3
- package/dist/yaml-keys.js +2 -0
- package/docs/concepts-livraison-dark.svg +32 -0
- package/docs/concepts-livraison-light.svg +32 -0
- package/docs/concepts-lot-dark.svg +52 -0
- package/docs/concepts-lot-light.svg +52 -0
- package/docs/concepts-vague-dark.svg +39 -0
- package/docs/concepts-vague-light.svg +39 -0
- package/docs/concepts-worktrees-dark.svg +45 -0
- package/docs/concepts-worktrees-light.svg +45 -0
- package/docs/concepts.md +159 -0
- package/docs/deliver.md +24 -2
- package/docs/fonctionnement.md +11 -5
- package/docs/lead.md +10 -6
- package/docs/orchestrate.md +22 -5
- package/docs/overview.md +1 -1
- package/docs/perf.md +1 -0
- package/docs/qualite.md +13 -3
- package/docs/raf.md +42 -5
- package/docs/session.md +32 -0
- package/docs/spec.md +5 -2
- package/docs/whats-new.md +6 -0
- package/package.json +10 -1
- package/skills/lead/SKILL.md +6 -0
- package/skills/session-close/SKILL.md +6 -0
- package/templates/orchestrate/spec.md +2 -2
package/docs/orchestrate.md
CHANGED
|
@@ -108,7 +108,7 @@ command warns `ne lit pas les demandes de contrôle` on the error output.
|
|
|
108
108
|
plan too); then, each time the lots in play are finished, the wave **draws the next ready lots of the plan** — up to the
|
|
109
109
|
session cap — and plays them in the same wave (same budget, same state folder, journal line `continue : tire …`). A lot
|
|
110
110
|
is drawn when it is `todo`, its `after` is lifted, it is not recurring, neither its title nor a note says « à décider »
|
|
111
|
-
(« à décider avec … », « (à décider) »: a lot waiting for a human decision), it is not already in the wave, the project's draw setting does not hold it back (L390, below), and its own budget (derived from its estimate; a lot with `screen: page` is worth 1.3 M whatever its estimate
|
|
111
|
+
(« à décider avec … », « (à décider) »: a lot waiting for a human decision), it is not already in the wave, the project's draw setting does not hold it back (L390, below), and its own budget (derived from its estimate; a lot with `screen: page` is worth 1.3 M whatever its estimate, or its ordinary budget plus the 450 k spec-stage allocation if that is more, a visible lot with another `screen:` (or none) above 0.1 day has its ordinary budget plus that allocation (light mockup, L459), and a lot with `spec: true` gets that allocation on top of its ordinary budget)
|
|
112
112
|
fits in the budget left — otherwise the next one that fits is taken (a lot that does not fit in what is left of the current
|
|
113
113
|
round is not dropped: it is tried again at the next round, once the budget allows it). A round takes at most **one lot per
|
|
114
114
|
repository** while other repositories have ready lots — the pool plays the lots of one repository one after the other, so a
|
|
@@ -310,10 +310,10 @@ lot that already has commits is never pre-checked (resuming it is legitimate), a
|
|
|
310
310
|
## The spec step of a lot with `screen:`
|
|
311
311
|
|
|
312
312
|
A lot that declares `screen:` (`raf add … --visible --screen page|component|journey`, or `raf screen <id> page|component|journey` on a lot that already exists, see [raf](raf.md#raf)) gets, when it is `page`, one more session, `spec`, **after** the pre-check and **before**
|
|
313
|
-
the first implementation: `precheck → spec → maquette → accept → implement → review → ux` (`maquette` unless `orchestrate.maquette: false`, see [The mockup review](#the-mockup-review-before-any-code); `accept` only when the project declares `orchestrate.accept`, see below). A lot without `screen:` or `spec: true` follows the usual path, unchanged (a lot with `spec: true` and no screen has its own chain, see [The spec step of a lot with `spec: true`](#the-spec-step-of-a-lot-with-spec-true)). `--dry-run` lists the step
|
|
313
|
+
the first implementation: `precheck → spec → maquette → accept → implement → review → ux` (`maquette` unless `orchestrate.maquette: false`, see [The mockup review](#the-mockup-review-before-any-code); `accept` only when the project declares `orchestrate.accept`, see below). A lot without `screen:` or `spec: true` follows the usual path, unchanged unless it is visible and above 0.1 day (light mockup, below) (a lot with `spec: true` and no screen has its own chain, see [The spec step of a lot with `spec: true`](#the-spec-step-of-a-lot-with-spec-true)). `--dry-run` lists the step
|
|
314
314
|
(`spec (sonnet)`), its command line and its brief `templates/orchestrate/spec.md`.
|
|
315
315
|
|
|
316
|
-
- **Threshold** (L209): only `screen: page` enters this chain. The measure of 2026-10-12 (no row of the success table reached, about 570 k tokens per lot, 4.5 correction passes per journey lot) narrowed it: `component` and `journey` stay valid values of `screen:` (no existing plan breaks, `raf screen` still sets them) but a lot that carries one follows the ordinary path and gets the ordinary budget, without the 1.3 M flat rate. A lot already in the chain (its sheet, or its `acceptation-<lot>.json`, exists) keeps its sheet and its lock. The `spec: true` step and the back part of a mixed lot do not change. The next measure covers 8 `page` lots.
|
|
316
|
+
- **Threshold** (L209): only `screen: page` enters this chain. The measure of 2026-10-12 (no row of the success table reached, about 570 k tokens per lot, 4.5 correction passes per journey lot) narrowed it: `component` and `journey` stay valid values of `screen:` (no existing plan breaks, `raf screen` still sets them) but a lot that carries one follows the ordinary path and gets the ordinary budget, without the 1.3 M flat rate. A lot already in the chain (its sheet, or its `acceptation-<lot>.json`, exists) keeps its sheet and its lock; since L459, for a visible lot without `spec: true`, only the sealed acceptation keeps the full chain (a sheet alone is also what the light mockup writes). The `spec: true` step and the back part of a mixed lot do not change. The next measure covers 8 `page` lots. Since L459 a *visible* lot above 0.1 day with `component`, `journey` or no `screen:` no longer takes the ordinary path: it plays the light chain of [The light mockup](#the-light-mockup-l459) (no `accept`, ordinary budget plus 450 k); a non-visible one, or one with `orchestrate.maquette: false`, still does.
|
|
317
317
|
- **The session** (L171): Sonnet, the `spec-writer` agent (it writes only the spec folder, never application code), Playwright loaded like for `implement`. It reads the lot,
|
|
318
318
|
the project's CLAUDE.md and its charter (CSS, neighbouring components, `docs/qa/expectations.md`, existing routes and API contracts), then writes in
|
|
319
319
|
`<orchestrate.spec.dir>/<écran>/` (`docs/specs` by default): `maquette.html` (static HTML + CSS to the project's own style; one section `#etat-vide`, `#etat-chargement`,
|
|
@@ -331,6 +331,19 @@ the first implementation: `precheck → spec → maquette → accept → impleme
|
|
|
331
331
|
- **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
|
|
332
332
|
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).
|
|
333
333
|
|
|
334
|
+
## The light mockup (L459)
|
|
335
|
+
|
|
336
|
+
(L459, spec [docs/specs/maquette-legere-lots-visibles/fiche.yaml](specs/maquette-legere-lots-visibles/fiche.yaml)) A mockup validated by the `ux-reviewer` **before** the code was only for the lots of a whole page; the retouches and screen reviews (a visible lot with `screen: component` or `journey`, or none) were coded first and discovered their UX defects in the `ux` pass (1.6 `ux` sessions per visible lot on 30-10, target 1.2). Those lots now get a light mockup:
|
|
337
|
+
|
|
338
|
+
- **Which lots**: visible, without `spec: true`, with an estimate above 0.1 day (the express lane, below, stays one session), and whose `screen:` in the plan is not `page` (`component`, `journey` or absent). It is read from the plan at every call, with no field in the lot's state: it survives a resume and the writing of the sheet. `orchestrate.maquette: false` takes the lot back to the ordinary path (no `spec`, no `maquette`). A lot with `screen: page` keeps its full chain, unchanged. A lot of `spec: true` keeps its back chain. A lot that entered the full chain before L459 with an `acceptation-<lot>.json` next to a screen sheet keeps it too (`accept` already played, its sealed tests checked, budget of 1.3 M), even if it now matches these rules.
|
|
339
|
+
- **The chain**: `precheck → spec → maquette → implement → review → ux` (`review-small` alone for a small lot, as for any small visible lot). There is **no `accept` step**, even when `orchestrate.accept` is declared: a retouche carries no behaviour rules to lock under red tests.
|
|
340
|
+
- **The session `spec`** writes a sheet limited to the states, fields and rules the lot touches, plus `maquette.html` with one section `#etat-<état>` per touched state, and, per touched state and per width (1440 and 390), two captures: `actuel-<état>-<largeur>.png`, the current screen, and `maquette-<état>-<largeur>.png`, the target. If the current screen cannot be captured (the application does not start, the page is protected), the agent says so under `nonVerifie` and bases the mockup on the code; it never invents a capture. Only a lot that changes nothing the user can see (no screen, component, state, text or style: an internal rename, a refactoring with the same rendering) answers `sousSeuil` and goes to `implement`, as for a page; the brief says that a change to a component of an existing screen is not under the threshold, even though it adds no page.
|
|
341
|
+
- **What the program checks** is what it checks for a page: `cadence spec check`, a section and a `data-testid` per state and field, the target captures at both widths, commit, clean tree, and the costly-choice-is-a-question rule. It does **not** check the `actuel-*` captures: their name is a convention of the brief, a free file like `contraste.md`.
|
|
342
|
+
- **The review** is the loop of [The mockup review](#the-mockup-review-before-any-code): up to `orchestrate.maquetteReviews` reviews (3 by default), blocking and major findings sent back to `spec`, the lot handed back with `maquette non conforme après N revue(s) : …` if the last one is still not compliant. The brief asks the reviewer to sweep the touched states only and to compare each target with its `actuel-*` capture.
|
|
343
|
+
- **After the code**, the `ux` pass (or the single `review-small` pass of a small lot) is the conformity pass of the sheet lots (see « The UX pass becomes a conformity pass »): the running app against the mockup and its captures, state by state. Without `orchestrate.ux`, or with an application that does not start, no `ux` session is played and the note « UX à faire par le lead » is written.
|
|
344
|
+
- **Budget**: the ordinary budget of the estimate plus the 450 k spec-stage allocation (`--dry-run` says « plus 450000 pour les étapes spec et maquette »), not the 1.3 M of a page. The 450 k is the allocation of a `spec: true` lot, taken as the first figure (hypothesis, to measure again over two windows).
|
|
345
|
+
- **Measure**: `cadence perf` is unchanged: « sessions ux par lot visible » (see [perf](perf.md)) is judged against 1.2 over two windows after the change; an empty window stays empty.
|
|
346
|
+
|
|
334
347
|
## The spec step of a lot with `spec: true`
|
|
335
348
|
|
|
336
349
|
A lot that changes a behaviour but has no screen (`raf add … --spec`, or `raf spec <id> on`, see [raf](raf.md#raf); L188, design: [docs/superpowers/specs/2026-10-11-etape-spec-back-design.md](superpowers/specs/2026-10-11-etape-spec-back-design.md)) is specified **before** it is coded, on a sheet, like a screen:
|
|
@@ -348,7 +361,7 @@ A lot that changes a behaviour but has no screen (`raf add … --spec`, or `raf
|
|
|
348
361
|
Findings are ranked blocking / major / minor, each with its measurement. A blocking or major one sends the sheet back for a **second `spec` session** (its brief carries the findings), then the sheet is reviewed again; still not conforming, the lot is handed back with what remains (`fiche non conforme après 2 revues : …`).
|
|
349
362
|
The `spec` session has **one budget of two sessions**, shared by the program's check and the reviews: if the check already used the second one, a non-conforming review hands the lot back at once (`fiche non conforme et la spec a déjà joué 2 sessions`), with no second review. A question to the lead (the session's own, or a costly hypothesis) and its answer open a new budget.
|
|
350
363
|
Minor findings become notes of the lot (`Revue de la fiche — mineur : …`, one per finding, committed with the plan; on a read-only plan they become `[note à poser]` proposals), not lines of the final table. This verdict is **not** `raf review`: the review of the code keeps that role. `orchestrate.fiche: false` skips the step.
|
|
351
|
-
- **A lot with a screen and a behaviour**: it is a lot with `screen:` whose **sheet** has a back part (a `sujet` block next to `ecran`, same folder), never a `spec: true` beside `screen: page` (`raf spec <id> on` only says so on such a lot and changes nothing; on `component` or `journey`,
|
|
364
|
+
- **A lot with a screen and a behaviour**: it is a lot with `screen:` whose **sheet** has a back part (a `sujet` block next to `ecran`, same folder), never a `spec: true` beside `screen: page` (`raf spec <id> on` only says so on such a lot and changes nothing; on `component` or `journey`, `spec: true` makes a back-end spec lot, ordinary budget plus 450 k, instead of the [light mockup](#the-light-mockup-l459) such a visible lot plays without it). The program reads the sheet, not the plan: as soon as the sheet has a `sujet` block (the contracts), the `code-reviewer` also reviews it,
|
|
352
365
|
the chain is `spec → fiche → maquette → accept`, and the findings of the two reviews go together to a single `spec` session, which counts for one in the budget shared by both (`orchestrate.maquetteReviews` sessions, 3 by default, see the mockup review below). The brief of the spec of a lot with a screen offers the back part (and joins its template) without making it compulsory; a lot whose sheet has no `sujet` keeps its chain, without `fiche`. `--dry-run` cannot list `fiche` for a lot with a screen (the sheet does not exist yet).
|
|
353
366
|
- **Acceptance and code**: `accept` is the screen's step, with a back brief (`accept-back.md`: cases call the contracts of `contrats[]` as close as possible, external sources simulated from `entree`; no `data-testid`). `implement`, `fix` and `review` receive the sheet (cases, rules, contracts) as for a screen, without mockup; the review also checks that each contract is held (output, exact codes and messages of the errors).
|
|
354
367
|
- **Hypotheses**: a lot without a screen has no News entry. Once the sheet is final — after its review and the second `spec` session if there was one, just before `accept` or `implement`, and also for a sheet already present on a resume — the program writes **one note of the lot per hypothesis** (`Hypothèses (fiche <sujet>) — R3 : … ; si faux : …`, not duplicated on a resume) and commits it with the plan; on a plan kept by another tool the lines become proposals (`[note à poser] …`). The end-of-wave table lists them under each lot (`hypothèse de la fiche : …`) for the lead to relay.
|
|
@@ -561,6 +574,10 @@ except a copy that is still needed, which stays and is said: a copy that a live
|
|
|
561
574
|
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 same run updates the **local copy of the `cadence-hud` mod** (L419), the one `CLAUDE_CODE_PLUGIN_DIRS` makes Claude Code load (`~/.claude/mods/cadence-hud`, `--mod-dir` for another folder): `plugins/cadence-hud` of the copy just put in service (not the working repository) is synchronised into it with `rsync -a --delete`, which leaves out `.claude-plugin/types` (generated by Claude Code in the copy). The output says `mod cadence-hud : copie locale mise à jour (<dir>)`. A machine without that folder gets no copy created (`copie locale absente … non mise à jour`), and a failed `rsync` is a warning on the error output (`avertissement : mod cadence-hud non mis à jour …`), exit code 0 like the purge: the version is in service. Before L419 the copy was updated by hand at each release and a forgotten one left the old mod loaded. The first run is also what installs the links in `~/.local/bin`. It prints the fingerprint of the copy
|
|
562
575
|
(bin, dist, package.json).
|
|
563
576
|
|
|
577
|
+
## Proposals the lot's own commits may have treated (L422)
|
|
578
|
+
|
|
579
|
+
The sub-tasks a review proposes (`[sous-tâche code] …`, `ux`, `maquette`) are returned to the lead in the final table and in `--status --detail <lot>`. When a fix pass runs after the review, it often treats them; 28-10, L230 was opened on proposals already corrected, 5 passes lost. The program now keeps, for each sub-task, the HEAD the review that issued it had read (`proposalHeads` in the lot's state; a later review that repeats the same line does not renew it) and, when it prints the line, looks for the files the text cites (`src/a.ts`, `src/a.ts:42`, `` `docs/z.md` ``; not web addresses nor numbers). If a commit of the lot (those the steps noted, or by default the ones git attributes to it: the HEAD of the review and these shas come from the same state of the branch, whereas the repository's HEAD does not see a lot not yet integrated and only finds rewritten commits once it was rebased) that is not in the history of that HEAD modified one of them (whole path or end of path), the line ends with `— peut-être déjà traitée (<sha>)`, the oldest such commit. It is a hint to check before opening a lot, not a verdict: nothing is removed from the proposals. Left unmarked: minors (already a precise file line), sub-tasks citing no file, states written before this change (no recorded HEAD), a HEAD the repository does not know, a git read that fails, `--status` (compact: the lines are not printed, no git read), and commits made in a neighbouring repository (`repos:`), which are not read.
|
|
580
|
+
|
|
564
581
|
## Express lane (L519)
|
|
565
582
|
|
|
566
583
|
A lot of **at most 0.1 day** (`estimate: 0.1` or less, no `screen:`, no `spec: true`) takes the express lane, with no key to set. The L517 study ([docs/mesures/temps-d-un-lot.md](mesures/temps-d-un-lot.md), lever 2) measures 15.5 min and 116 k counted tokens for such a lot, 44 % of them in review and preparation: the lane cuts about 4.9 min and 51 k tokens per lot. What changes:
|
|
@@ -851,7 +868,7 @@ upstream ref and `git ls-remote` are compared with the "before" (a `git checkout
|
|
|
851
868
|
`CADENCE_ORCHESTRATED` is set.
|
|
852
869
|
|
|
853
870
|
**Budget**: the wave counts input + cache writes + output tokens (default 2 M); cache reads are kept and
|
|
854
|
-
shown apart. Each lot also has its own budget, the 90th percentile of the *total* measured per lot on `.cadence/runs` (all the waves a lot went through, 250 lots) for its category (L194): an ordinary lot of up to 0.5 day gets 600 k (floor: a write pass, its review, one fix pass and the short review after it, 155 k being reserved for those last two; measured p90 374 k on 11-10, 451 k on 16–22-10: 3 of 145 lots over 540 k, all maritime-atlas, hence 600 k, L247; remeasured on 17–23-10: 0 of 309 lots over budget, p90 452 k, so unchanged, L280; and on 18–24-10: 0 of 322 over, p90 460 k at 600 k, 557 k at 750 k, 744 k at 1.3 M, unchanged again although the tokens per lot rose by a third at estimates 0.25 and 0.5: the rise is the write pass, which the budget bounds with room, L312), 0.5 to 1 day 750 k (p90 732 k), then 570 k per day of estimate, 750 k staying the floor until that exceeds it, i.e. up to 1.3 day (p90 1.14 M for 2 days); a lot with `screen: page` gets 1.3 M whatever its estimate (
|
|
871
|
+
shown apart. Each lot also has its own budget, the 90th percentile of the *total* measured per lot on `.cadence/runs` (all the waves a lot went through, 250 lots) for its category (L194): an ordinary lot of up to 0.5 day gets 600 k (floor: a write pass, its review, one fix pass and the short review after it, 155 k being reserved for those last two; measured p90 374 k on 11-10, 451 k on 16–22-10: 3 of 145 lots over 540 k, all maritime-atlas, hence 600 k, L247; remeasured on 17–23-10: 0 of 309 lots over budget, p90 452 k, so unchanged, L280; and on 18–24-10: 0 of 322 over, p90 460 k at 600 k, 557 k at 750 k, 744 k at 1.3 M, unchanged again although the tokens per lot rose by a third at estimates 0.25 and 0.5: the rise is the write pass, which the budget bounds with room, L312), 0.5 to 1 day 750 k (p90 732 k), then 570 k per day of estimate, 750 k staying the floor until that exceeds it, i.e. up to 1.3 day (p90 1.14 M for 2 days); a lot with `screen: page` gets 1.3 M whatever its estimate (p90 1.28 M: spec, mockup, accept, conformity UX and passes), or its ordinary budget plus 450 k beyond that; a visible lot with `component`, `journey` or no `screen:` above 0.1 day gets its ordinary budget plus 450 k (light mockup, L459; figure of the `spec: true` chain, to measure again); a lot with `spec: true` gets its ordinary budget plus 450 k (the back chain measured on L120: 388 k); shown by
|
|
855
872
|
`--dry-run` with its basis): it bounds the *writing* passes, never the reviews. A lot that spent it gets no further implementation or
|
|
856
873
|
fix and is handed back to the lead, but its review (and its single UX review, once the code review is compliant) is played first; a fix pass starts only if
|
|
857
874
|
the remaining budget can also pay the fix and the short review that follows it (155 k tokens reserved: the p90 of a fix
|
package/docs/overview.md
CHANGED
|
@@ -74,7 +74,7 @@ The two plugins are independent. `cadence@cadence` carries the rituals, which ca
|
|
|
74
74
|
| [docs/stabilite.md](stabilite.md) | what is guaranteed from 1.0.0 and what is not |
|
|
75
75
|
| [docs/qualite.md](qualite.md) | code-quality guard rails: ESLint (typescript-eslint, sonarjs), knip and jscpd compared to a versioned baseline by a ratchet (`npm run qualite`, `--init`, `--ecrire`, `--migrer`, `--contre`), in the wave's gate and the CI; the recipe for TypeScript, Python, Java and shell |
|
|
76
76
|
|
|
77
|
-
Also: [news](news.md) (changelog, UX, code and QA reviews, documentation that follows the code), [quality](qualite.md), [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).
|
|
77
|
+
Also: [news](news.md) (changelog, UX, code and QA reviews, documentation that follows the code), [quality](qualite.md), [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), [the notions, for a human](concepts.md), [how it all works](fonctionnement.md) (four figures).
|
|
78
78
|
|
|
79
79
|
<details>
|
|
80
80
|
<summary>The four tools in short</summary>
|
package/docs/perf.md
CHANGED
|
@@ -32,6 +32,7 @@ current window, with at least 10 classified passes) is flagged by a `⚠` even w
|
|
|
32
32
|
A journal joins its plan by the project name (the path of the project relative to the folder) and the `lot` field of the journal; an id `Q4/accueil-4-ux12` joins the plan lot `Q4`. Journals with no plan lot are counted aside (`N lot(s) de journal sans plan`) and never counted as finished outside the wave.
|
|
33
33
|
An unreadable plan skips its project with a warning on the error output and the other projects are read; the indicators that read the plans are `n/d` only when no plan is readable.
|
|
34
34
|
- **Sessions that see the application.** `sessions ux par lot visible` counts, per visible lot rendered in the window, its `ux` steps that ended `ok` **and** the `review-small` steps that ended `ok` of a small visible lot (journal `small: true`): the single pass of a small visible lot is its only ergonomics review, run with the application launched (L283; measured on 24-10, 139 of 140 small lots were visible and the line showed 0.0, −97 %, a false signal). The `review-small` of a lot that is not small is only a code review and is not counted; a journal written before the `small` field counts none.
|
|
35
|
+
The formula is unchanged by the light mockup (L459, [docs/orchestrate.md](orchestrate.md#the-light-mockup-l459)): the lots that now play `spec` and `maquette` before the code are expected to lower this value from 1.6 (30-10) towards 1.2, to be judged over two windows after the change; a window with no visible lot rendered leaves the line empty (`n/d`).
|
|
35
36
|
`part des lots visibles revus par une étape ux` is the share of the visible lots rendered in the window with at least one `ux` step that ended `ok` (so a small visible lot, reviewed in `review-small`, is not in it). It is informational: no `⚠`, no quality twin (`interventions.dedicatedUx` in `--json`), `n/d` without a visible lot.
|
|
36
37
|
- **Pre-check share of `oui`** (L288). `part de « oui » du précheck` is the share of the lots rendered in the window whose journal records a pre-check verdict (`precheckVerdict`) that is `oui` (the deliverable was already there; an unproven `oui` is recorded `partiel`). Compare the current window with the previous one before and after a change of `orchestrate.precheck` or of the pre-check criterion (the title rule of L288): a share that stays low means the pre-check costs more than it saves. A journal older than L288 has no `precheckVerdict`: its verdict is read back from the lot (`livrable déjà présent` → `oui`, pre-check finding in the brief → `partiel`, otherwise `non`), so the window before the change is measurable. A journal written since L288 whose pre-check report was unreadable (it keeps the `contrôle préalable illisible` warning, no `precheckVerdict`) has no verdict and is not counted, not read back as `non` (L293). Lots with no successful `precheck` step are not counted; informational, no `⚠`, no quality twin (`precheck.yes` in `--json`), `n/d` without a verdict.
|
|
37
38
|
- **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`.
|
package/docs/qualite.md
CHANGED
|
@@ -63,9 +63,19 @@ anything, and the ratchet closes them:
|
|
|
63
63
|
`modifié`). That is not only the three files this repository uses: ESLint 9 loads `eslint.config.js` **before**
|
|
64
64
|
`eslint.config.mjs` (then `.cjs`, `.ts`, `.mts`, `.cts`), knip reads `knip.json`, `knip.jsonc`, `.knip.json`,
|
|
65
65
|
`.knip.jsonc`, `knip.ts`, `knip.js`, `knip.config.ts`, `knip.config.js` and `package.json#knip`, jscpd reads
|
|
66
|
-
`.jscpd.json` and `package.json#jscpd`, and knip as jscpd respect `.gitignore`.
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
`.jscpd.json` and `package.json#jscpd`, and knip as jscpd respect `.gitignore`. Changing a threshold is a decision of
|
|
67
|
+
the plan, delivered alone; a lot judged by those thresholds cannot move them. The lot that adopts the tools (no baseline at its start) introduces its configs freely.
|
|
68
|
+
The comparison also covers what knip and vitest read: `vitest.config.*`, `vitest.workspace.*`, `vite.config.*` (all of
|
|
69
|
+
`ts`, `mts`, `cts`, `js`, `mjs`, `cjs`), `tsconfig.json`, every nested `.gitignore` (at the start of the lot or now;
|
|
70
|
+
`node_modules` and `.git` are not walked; an untracked one that another `.gitignore` rule already ignores, such as the `types/.gitignore` Claude Code generates, is not counted, while one that ignores itself with `*` is, and so is one that a
|
|
71
|
+
negation pattern such as `!.gitignore` keeps un-ignored), and in `package.json` the keys `scripts`, `main`, `bin` and `exports` next to
|
|
72
|
+
`knip` and `jscpd` (knip finds its entry points there).
|
|
73
|
+
- **A hidden path**: `.git/info/exclude` is not versioned, so it cannot be compared to the start of the lot. When it holds
|
|
74
|
+
a pattern (a line that is neither blank nor a comment) the command adds a warning to its output, success or refusal,
|
|
75
|
+
naming the first three patterns. It does not fail: that file legitimately holds local paths.
|
|
76
|
+
|
|
77
|
+
A tool missing from `node_modules` (a fresh clone without `npm install`) exits 2 with « eslint est absent de node_modules :
|
|
78
|
+
lancer « npm install » », instead of the JSON error the empty output of a node that could not start used to give.
|
|
69
79
|
|
|
70
80
|
**When the start of the lot has no baseline yet** (the lot that introduces the ratchet, while `main` moves under it):
|
|
71
81
|
the baseline measured at the start no longer matches the code once `main` is merged or the lot rebased, since `main`'s
|
package/docs/raf.md
CHANGED
|
@@ -52,11 +52,11 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
52
52
|
|---|---|
|
|
53
53
|
| `raf --version` · `raf -v` | print the version of the package, as `cadence --version` does (L376); only as the first argument, `raf show --version` stays an unknown option |
|
|
54
54
|
| `raf init [--project name] [--prefix L] [--no-hook]` | create the plan, install the hook, add `.playwright-mcp/` to `.gitignore` |
|
|
55
|
-
| `raf add "title" [--estimate d] [--quickwin] [--visible [--screen page\|component\|journey]] [--spec] [--local] [--public "title"] [--after L2,L4] [--uses banc,passerelle] [--parent L3]` | add a lot or a sub-task, print its id (`--public`: the lot's title in the public's words, written as `public:` right after `title:`; `--screen`: the screen a visible lot creates or changes — a new `page`, a new `component` or a modified `journey` — written as `screen:` right after `visible:`, shown by `raf show`; it requires `--visible`, and `raf check` reports a `screen:` without `visible:` or with another value as an error. Only `page` triggers the spec step of `orchestrate` (L209), never inferred from the title; `--spec`: the lot changes a behaviour (something a user, an API client or another service can observe), written as `spec: true` right after `screen:`, `visible:`, `quickwin:` or `estimate:` (whichever comes last), shown by `raf show`, refused on a sub-task. Also set by the lead at planning time, never inferred from the title); `--local`: the lead edits this repository by hand for this lot, written as `local: true` right after `spec:` (or the last key before it), refused on a sub-task ([a lot of the lead](#a-lot-of-the-lead-local-l519)); `--uses`: the shared resources the lot occupies, written as `uses: [banc, passerelle]` after `after:` and before `created:` ([shared resources](#shared-resources-uses-and-resources), L251), refused on a sub-task) |
|
|
55
|
+
| `raf add "title" [--estimate d] [--quickwin] [--visible [--screen page\|component\|journey]] [--spec] [--local] [--public "title"] [--after L2,L4] [--uses banc,passerelle] [--set field=value]… [--parent L3]` | add a lot or a sub-task, print its id (`--set`: the value of an [extension field](#extension-fields-planextras-l464) declared by `plan.extras`, refused on a sub-task; `--public`: the lot's title in the public's words, written as `public:` right after `title:`; `--screen`: the screen a visible lot creates or changes — a new `page`, a new `component` or a modified `journey` — written as `screen:` right after `visible:`, shown by `raf show`; it requires `--visible`, and `raf check` reports a `screen:` without `visible:` or with another value as an error. Only `page` triggers the full spec step of `orchestrate` (L209), never inferred from the title; a visible lot above 0.1 day with `component`, `journey` or no `screen:` plays the light mockup (L459); `--spec`: the lot changes a behaviour (something a user, an API client or another service can observe), written as `spec: true` right after `screen:`, `visible:`, `quickwin:` or `estimate:` (whichever comes last), shown by `raf show`, refused on a sub-task. Also set by the lead at planning time, never inferred from the title); `--local`: the lead edits this repository by hand for this lot, written as `local: true` right after `spec:` (or the last key before it), refused on a sub-task ([a lot of the lead](#a-lot-of-the-lead-local-l519)); `--uses`: the shared resources the lot occupies, written as `uses: [banc, passerelle]` after `after:` and before `created:` ([shared resources](#shared-resources-uses-and-resources), L251), refused on a sub-task) |
|
|
56
56
|
| `raf add "title" --every <days>` · `raf did <id> ["text"]` | recurring lot: `every` (days) and `last` (last time done) fields; the lot stays `todo`, `raf now` and `session start` list it under « Récurrent » with « dû depuis N j » / « prochain dans N j »; `raf did` resets the count (spec: `docs/superpowers/specs/2026-10-04-L11-tache-recurrente.md`) |
|
|
57
|
-
| `raf screen <id> page\|component\|journey\|none` | set, replace or remove `screen:` on an existing lot, to adopt the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen) on a plan already written (only `page` adopts the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen); `component` and `journey`
|
|
57
|
+
| `raf screen <id> page\|component\|journey\|none` | set, replace or remove `screen:` on an existing lot, to adopt the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen) on a plan already written (only `page` adopts the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen) in full; `component` and `journey` get the [light mockup](orchestrate.md#the-light-mockup-l459) when the lot is visible and above 0.1 day); refused on a lot without `visible:` (like `raf add --screen`) and on a sub-task; `none` removes the key and leaves `visible:` alone |
|
|
58
58
|
| `raf visible <id> on\|off` | set or remove `visible:` on an existing lot (a user-facing change: a news entry is expected, and `cadence orchestrate` adds its UX step), written right after `quickwin:`, `estimate:` or `status:` (whichever comes last); `on` on a visible lot changes nothing, `off` on a lot without the key neither; refused on a sub-task, and `off` is refused while the lot has a `screen:` (`raf screen <id> none` first: a screen is a visible change). It is the fix `raf check` points to for an open « Revue UX — » lot without `visible:` (L273) |
|
|
59
|
-
| `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`
|
|
59
|
+
| `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` both work and set or remove the back-end `spec:` (with `spec: true` the lot plays the back-end spec chain, without it a visible lot above 0.1 day plays the [light mockup](orchestrate.md#the-light-mockup-l459), L459). `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`) |
|
|
60
60
|
| `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) |
|
|
61
61
|
| `raf move <id> --before\|--after <id>` | reorder a lot in the plan file (L400): `<id>` is placed right before or right after the other lot, the others keep their order, and the lot travels with all its keys, its sub-tasks and its comments. Only among siblings: a lot among lots, a sub-task (`L1/t1`) among the sub-tasks of its own lot (`move : L2 et L1/t1 n'ont pas le même parent …`, plan unchanged). Exactly one of `--before` / `--after`, one id (else `usage : raf move <id> --before <id> \| raf move <id> --after <id>`); a lot placed where it already is, or an unknown id (`lot inconnu : <id>`), changes nothing. **Refused** on a read-only plan, plan kept by another tool (`plan en lecture seule : … — le modifier avec l'outil du projet`: reorder it with the project's own tool), on an archived lot (`move : L1 est archivé …`: the archive is presented in its former order and the order of the plan file never moves, see `raf archive`) and while a wave holds the repository (`move : orchestration en cours (vague …, pid …) — attendre …`: the lots of the wave rely on the order they read; the lock falls as soon as all of them are ready or handed back). The order is the order of the file, so it is the order `raf list`, `raf now`, the Gantt page and the draw of `cadence orchestrate` follow; the command only writes the plan, it does not commit it (`plan.commit` prints the command, as for every plan write) |
|
|
62
62
|
| `raf add`, `raf note`, `raf done` during an integration (L425) | while a wave integrates a lot of the repository (the integration lock is held by a live process), these three commands write the plan as usual and print on stderr `avertissement : intégration en cours (vague …, pid …) — …`: the plan is a tracked file of the main tree, and an integration that sees it modified refuses its lot (`arbre du dépôt modifié`, 2026-10-29: a `raf add` of the lead during the integration of L416). Commit the plan right away. Exit code 0; nothing is printed in a lot's worktree (its plan is not the tree the integration checks) nor when the lock belongs to a dead process |
|
|
@@ -64,6 +64,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
64
64
|
| `raf public <id> "title"` · `raf public <id> --clear` | set, replace or remove the public title of a lot |
|
|
65
65
|
| `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`, with the code-review gate active (`raf review enable`) a lot whose commits its review does not cover, **and a lot whose recorded review verdict says `non conforme` / `not compliant` even when it covers every commit** (L476: `non conforme : 1 bloquant` at the last commit no longer closes; `conforme après 1 correction` is compliant; redo the review with `raf review <id> "verdict"` after the fix, or `--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`, `ux <lot>` and `review <lot>` print the `git add` / `git commit` command that takes them, the subject from the `plan.commit` template, L371) |
|
|
66
66
|
| `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. A lot already `vert: true` whose tests lack fingerprints (L526) is replayed and, green, sealed (subject `chore(<lot>): empreintes de l'acceptation posées par cadence spec green`). Refused for a `todo` lot, during a wave, and while an unfinished wave keeps a lot of the repository open; see [orchestrate](orchestrate.md) |
|
|
67
|
+
| `raf status [<id> <state> ["message"] \| <id> --back ["message"]]` | the project's **detailed states** (L463, `plan.states`, [below](#detailed-states-planstates-l463)): sets the `state:` of a lot under the guard of `plan.transitions` and notes the message (`état bloque : attend la passerelle`); the base status is derived from the state — a `todo` lot moved to a `doing` state starts (`started:` set), `doing` → `todo` is done here too (so a blocked state of base `todo`, or the return `--back` to a `todo` state, works; `started:` is removed) under the gate of `raf todo`: refused as soon as work commits cite the lot (`L1 a 1 commit(s) qui le citent : le remettre en attente perdrait leur lien — un état doing (raf status L1 <état>), ou raf drop L1`), so a lot with work never goes back to `todo`, where `raf check` would refuse it and a wave could take it again; any other change of base status goes through its own command and gates (`raf done`, `drop`, `todo`; refused with the command to use). Entering the `plan.blockedState` remembers the state before in `state_before:`, `--back` returns to it; with no argument it lists the states with their base status, their number of lots and their transitions. Refused without `plan.states`, on a sub-task, on an unknown state, on the state the lot already has |
|
|
67
68
|
| `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`), or on a sub-task; sub-tasks and other keys are left as they are. **Reopening** (L396): on a `done` or `dropped` lot, `raf todo` is THE command — never the YAML — that reopens it (`→ todo`, `finished:` removed, `started:` kept so its commits stay its own, dated note « rouvert (était done) : reason »); `raf start`/`raf done`/`raf drop` on a closed lot say so (`L3 est done ; le rouvrir si c'est voulu : raf todo L3`). An archived lot reopened this way comes back into the plan file at that write |
|
|
68
69
|
| `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) |
|
|
69
70
|
| `raf note <id> "text"` | dated note — keep decisions next to the work |
|
|
@@ -73,7 +74,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
73
74
|
| `raf show <id> [--notes]` | one lot: status, dates, `goal : …` right after the title when the lot has a `goal:` (native key or `plan.fields.goal`, L272: the objective that replaces the title in the `orchestrate` briefs), `after`, public title, then for a visible lot two stable lines — `visible : oui` (followed by `screen : …` when the lot has one) and `ux : <verdict>` or `ux : absent` (the `raf ux` verdict, L439: what the `livreur` agent reads before closing a visible lot; a lot that is not visible prints neither line), `spec`, `uses : banc, passerelle` when the lot has `uses:`, dated notes in order, then the counted commits (as `raf commits`, neighbouring repositories included); `--notes` prints the notes alone |
|
|
74
75
|
| `raf commits <id>` | the commits counted for a lot (the set the code review gate uses), one `<sha> <subject>` per line, oldest first; a lot that declares `repos:` ([neighbouring repositories](orchestrate.md#a-lot-whose-work-is-in-a-neighbouring-repository)) gets, after them, one `dépôt <path> :` section per neighbour with the commits there that cite the lot and fall within the lot's period (`started` to `finished`) and, when the lot declares a `cite:` for that neighbour, contain that string (`aucun commit du lot`, or `introuvable` for a path that is not there) |
|
|
75
76
|
| `raf now` | what to do next |
|
|
76
|
-
| `raf list [--status s]` | flat list |
|
|
77
|
+
| `raf list [--status s] [--json]` | flat list; `--json` prints the lots as a JSON array (L464, [extension fields](#extension-fields-planextras-l464)) |
|
|
77
78
|
| `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; on a read-only plan (kept by another tool) the key goes to `.cadence/docs-ok.yaml` instead, and that file is committed (L386) |
|
|
78
79
|
| `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) |
|
|
79
80
|
| `raf check --ignored` | list the acknowledged commits with their date and reason |
|
|
@@ -215,6 +216,42 @@ migrate:
|
|
|
215
216
|
keep: [docs/specs/**, test/legacy-plan.test.ts] # cite docs/plan/raf.yaml without reading the plan
|
|
216
217
|
```
|
|
217
218
|
|
|
219
|
+
## Detailed states: `plan.states` (L463)
|
|
220
|
+
|
|
221
|
+
A project that tracks more than `todo`, `doing`, `done` and `dropped` (« tested », « delivered », « blocked »…) declares its own states in `cadence.yaml`. The base status stays the four above — every gate of cadence (`raf done`, reviews, UX, `raf now`, the Gantt) reads only it — and each state names the base status it belongs to:
|
|
222
|
+
|
|
223
|
+
```yaml
|
|
224
|
+
plan:
|
|
225
|
+
states: { a_faire: todo, en_cours: doing, bloque: doing, termine: done, livre: done, abandonne: dropped }
|
|
226
|
+
transitions: # optional: without it nothing is guarded
|
|
227
|
+
a_faire: [en_cours, bloque, abandonne]
|
|
228
|
+
en_cours: [bloque, termine, abandonne]
|
|
229
|
+
bloque: [en_cours, abandonne]
|
|
230
|
+
termine: [livre] # a state with no entry is terminal once `transitions` is declared
|
|
231
|
+
doneState: termine # what `raf done` sets (default: the first done state)
|
|
232
|
+
blockedState: bloque # `raf status <lot> --back` returns from it
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`states` is an ordered object, state → base status, and must hold at least one `todo`, one `doing` and one `done` state. The state is written as `state:` right after `status:` (and `state_before:` while blocked). `raf start`, `raf done`, `raf drop` and `raf todo` set the state of their status themselves (`doing` → the first `doing` state, `done` → `doneState`, …) and obey `plan.transitions` too — refused with the allowed states, `--force` passes (`raf todo`, the reopening, is not guarded). A lot with no `state:` (written before the declaration) is read in the default state of its status. `raf check` reports a `state:` that is not declared or whose base status contradicts `status:`. `raf show` prints `état :`. `plan.states` does not go with a plan held by another tool (`lots`, `fields`, `statuses`, `estimates`): its states are its own.
|
|
236
|
+
|
|
237
|
+
## Extension fields: `plan.extras` (L464)
|
|
238
|
+
|
|
239
|
+
A project that sorts its lots by something cadence does not know (a theme, an owner, the path of a sheet) declares the field in `cadence.yaml`, and cadence then keeps it, checks it and shows it:
|
|
240
|
+
|
|
241
|
+
```yaml
|
|
242
|
+
plan:
|
|
243
|
+
extras:
|
|
244
|
+
theme: { enum: [infra, ui, data] } # only these values
|
|
245
|
+
owner: { default: claude } # what a lot without the key is read as
|
|
246
|
+
fiche: {} # any non-empty text
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- **Declaration.** A name is lowercase letters, digits and `_`, starting with a letter, and is not a key cadence uses on a lot (`status`, `estimate`, `after`, `notes`, `tasks`, `uses`, `repos`…). `enum` is a non-empty list of distinct texts; `default` is a non-empty text, one of the `enum` values when there is one. Anything else is refused, naming the key (`cadence.yaml : plan.extras.theme.color inconnu`). `plan.extras` is refused together with a plan kept by another tool (`plan.lots`, `plan.fields`…, read-only): cadence writes no field there.
|
|
250
|
+
- **On the lot.** The field is a plain key of the lot, a text (`theme: ui`). A key that is **not** declared is neither read nor checked, and stays in the file as written, as do the declared ones: every `raf` write (start, note, done, title, archive…) edits the lot in place.
|
|
251
|
+
- **`raf check`.** A declared field whose value is not a non-empty text or is outside `enum` is a `bad-field`: `L12 : theme « zzz » hors de enum (infra, ui, data)`. A lot without the key is fine; with a `default` it is read with the default (nothing is written to the file, so changing the `default` changes every lot that does not carry the key).
|
|
252
|
+
- **`raf add "title" --set theme=ui`** writes the key on the new lot (before `created:`); `--set` can be repeated, the value is cut at the first `=` (`--set fiche=docs/a=b.md`). Refused, with nothing written: a field that is not declared (the message lists the declared ones; without `plan.extras`, it says so), a value outside `enum`, `--set theme` or `--set theme=` without a value, the same field twice, and `--set` with `--parent` (a sub-task has no field).
|
|
253
|
+
- **`raf show`** prints one `field : value` line per field the lot carries or receives by default. **`raf list --json`** prints the lots (filtered by `--status`) as one JSON array with the fields of the lot under the names of the plan file (`docs_ok`, not `docsOk`; `problems`, the diagnostics of `raf check`, is not exported), the extension fields under `extras` (absent when the plan declares none or the lot has none). A field is set on an existing lot by editing the file; there is no `raf` command for it yet.
|
|
254
|
+
|
|
218
255
|
## Plan file
|
|
219
256
|
|
|
220
257
|
```yaml
|
|
@@ -312,7 +349,7 @@ plan:
|
|
|
312
349
|
fields: # raf field: key in the file (a list = first one present)
|
|
313
350
|
title: titre
|
|
314
351
|
public: titre_public # the lot's title for the public; read when it is text, else ignored
|
|
315
|
-
screen: ecran # page, component or journey; only on a visible lot (only `page` enters the spec step)
|
|
352
|
+
screen: ecran # page, component or journey; only on a visible lot (only `page` enters the full spec step; the others, if visible and above 0.1 day, the light mockup)
|
|
316
353
|
spec: fiche # true or false: the lot changes a behaviour
|
|
317
354
|
status: etat
|
|
318
355
|
estimate: effort
|
package/docs/session.md
CHANGED
|
@@ -123,3 +123,35 @@ The command deletes nothing, never fails on the cleanup and does not change the
|
|
|
123
123
|
The commands get `CADENCE_SINCE` and `CADENCE_TODAY`. `CADENCE_SINCE` is the `--since` as given; on `session close` without `--since` it is the instant of the last `session start` as an ISO 8601 UTC string (`2026-09-28T07:12:00.000Z`), or `<today> 00:00` when no mark exists.
|
|
124
124
|
The commands add facts and decide nothing: a failing command is reported and changes neither
|
|
125
125
|
the exit code nor the verdict.
|
|
126
|
+
|
|
127
|
+
## Memory index size (L442)
|
|
128
|
+
|
|
129
|
+
Claude Code loads the `MEMORY.md` index of the folder it was launched in at every turn: a long index is a
|
|
130
|
+
fixed cost of every session. `cadence session close` weighs it and, above a threshold, prints a "Mémoire"
|
|
131
|
+
section:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
Mémoire : l'index MEMORY.md pèse 15700 octets (seuil 12000)
|
|
135
|
+
14 entrée(s) datée(s) (9100 octets) à regrouper sous une ligne vers un index secondaire — le skill propose, l'humain décide
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- **Where it looks.** `<CLAUDE_CONFIG_DIR or ~/.claude>/projects/<folder>/memory/MEMORY.md`, where `<folder>` is the
|
|
139
|
+
working directory of the command (never the git root: Claude Code indexes the folder it was launched in) with every
|
|
140
|
+
character that is not a letter or a digit replaced by `-`. A symbolic link is followed. With `--all`, each project
|
|
141
|
+
section weighs its own memory.
|
|
142
|
+
- **When it speaks.** Only when the file is strictly larger than the threshold, in bytes: 12000 by default, or
|
|
143
|
+
`memory.indexMaxBytes` (an integer ≥ 1) in the `cadence.yaml` the command reads. An absent index, an empty one or
|
|
144
|
+
one at the threshold prints nothing. A value that is not an integer ≥ 1 stops the close (exit code 2, no report), as
|
|
145
|
+
`session.cleanDays` does.
|
|
146
|
+
- **Dated entries.** A list line (`- `) whose text starts with a day `dd-mm`, after at most `**`, `[` and `⚠`
|
|
147
|
+
(`- **[31-10 : …`). The name of the linked file does not count, so the line that points to the secondary index is
|
|
148
|
+
never counted. With none, the section says `aucune entrée datée à regrouper`.
|
|
149
|
+
- **What it does not do.** It is a proposal, not a condition: the exit code and the « prêt à fermer » line are those of
|
|
150
|
+
a close without the warning, and cadence never writes the memory. The [session-close skill](skills.md) proposes the
|
|
151
|
+
grouping and moves the entries only after the human agrees.
|
|
152
|
+
- **Unreadable index** (a folder, no read permission): one line `Mémoire : l'index MEMORY.md est illisible (<cause>), sa taille n'est pas contrôlée`; the rest of the report is printed.
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
memory:
|
|
156
|
+
indexMaxBytes: 12000 # bytes; the default
|
|
157
|
+
```
|
package/docs/spec.md
CHANGED
|
@@ -47,13 +47,16 @@ error output, nothing on the standard one, and exits 1. The derivation is built
|
|
|
47
47
|
|
|
48
48
|
## Spec step (design, first steps delivered)
|
|
49
49
|
|
|
50
|
-
For a visible lot that adds a page (`screen: page
|
|
50
|
+
For a visible lot that adds a page (`screen: page`), `orchestrate` gets a step
|
|
51
51
|
before the code: a static HTML mockup in the project's own style (1440 and 390 px; empty, loading and
|
|
52
52
|
error states) and a machine-readable screen sheet (fields, numbered rules `R1…` each marked *sourced*
|
|
53
53
|
or *hypothesis*, actions → API calls). The `ux-reviewer` agent reads the mockup before any code; the
|
|
54
54
|
acceptance tests are written from the sheet, red, by a session other than the implementer's; the
|
|
55
55
|
implementation gets the sheet, the code review checks rule → test → green, the hypotheses go into the
|
|
56
|
-
lot's news entry, and a human is asked only about a choice that is costly to undo.
|
|
56
|
+
lot's news entry, and a human is asked only about a choice that is costly to undo. A visible lot above 0.1 day
|
|
57
|
+
with `screen: component`, `journey` or no `screen:` (and no `spec: true`) plays a lighter version since L459: spec → mockup
|
|
58
|
+
of the touched states only (current screen and target), mockup review, then the code, with no `accept` and a budget of
|
|
59
|
+
ordinary plus 450 k (1.05 M for a lot up to half a day), see [The light mockup](orchestrate.md#the-light-mockup-l459). The design is in
|
|
57
60
|
`docs/superpowers/specs/2026-10-10-etape-spec-design.md` (with an example sheet and the plan of the
|
|
58
61
|
lots that deliver it). Delivered so far: the sheet templates `templates/fiche.yaml` (screen) and `templates/fiche-back.yaml` (a lot without a screen: `sujet`, `cas`, `regles`, `contrats`, `donnees`), `cadence spec check`
|
|
59
62
|
(the sheet's invariants) and `cadence spec qa` (the screen's `docs/qa/expectations.md` section, derived
|
package/docs/whats-new.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
## In two lines
|
|
6
6
|
|
|
7
|
+
- **1.41.0**: richer plans (detailed states, extension fields); a light mockup step for visible lots that are not pages; `verify --sha` clears a red delivery; the quality ratchet watches its tools' config files.
|
|
8
|
+
- **1.40.0**: `docs/concepts.md`, the notions of cadence for a human; a proposed sub-task maybe already done is flagged; a warning when the memory index loaded every turn grows too big.
|
|
7
9
|
- **1.39.0**: `orchestrate --continue` fills free slots by priority, one worktree per lot when the cap allows (issue #21); waves show their cost in euros before and after.
|
|
8
10
|
- **1.38.1**: `spec green` seals a lot closed at `vert: true` without fingerprints (issue #20); `deliver`, `raf move`, `spec reseal` and `spec green` refuse cleanly during a local task.
|
|
9
11
|
- **1.38.0**: an express lane plays a touch-up of at most 0.1 day in one session; `raf add --local` and `cadence local start|done|abort` lock a repository while the lead edits it by hand.
|
|
@@ -68,6 +70,10 @@ Each is a paragraph in [docs/whats-new.md](whats-new.md) and a full section in [
|
|
|
68
70
|
|
|
69
71
|
The long form of « In two lines » above (the README no longer carries a « What's new » section since L326: it links here). The full history, with the date of each version, is in [CHANGELOG.md](../CHANGELOG.md).
|
|
70
72
|
|
|
73
|
+
**1.41.0**: plans can say more without leaving cadence. `plan.statuses` declares the detailed states of a project with their allowed transitions, and `raf status <lot> <state>` writes them; `plan.extras` declares extension fields, validated and kept on every write, set with `raf add --set key=value`; both live together in one plan (L463, L464, `docs/raf.md`). A visible lot that is not a whole page (a component, a touch-up, a UX review of a screen) now gets a light mockup — the current screen and the target, the states it touches — reviewed by the `ux-reviewer` before the code, then checked for conformity after; a lot already entered with a sealed acceptance keeps its full chain (L459, `docs/spec.md`). `cadence verify --sha` green after a red delivery of the same sha records it in the deliveries journal, so the red line goes away without delivering again; it no longer stops on a local task of the lead or an unreadable plan (L435, `docs/deliver.md`). The quality ratchet watches every file its tools read (nested `.gitignore`, `vitest.config.*`, `package.json` entries, `tsconfig.json`), so a configuration change cannot loosen it unnoticed (L479, `docs/qualite.md`).
|
|
74
|
+
|
|
75
|
+
**1.40.0**: a guide for humans, `docs/concepts.md`, linked at the top of the README, explains each notion of cadence in a few sentences, in the order you meet them: plan and lot, verdicts, session and wave, the steps of a lot and who plays each, the test gate, worktree and integration, « livrable jusqu'à » (L460). In the final table and `--status --detail`, a proposed sub-task whose cited file was changed by a commit of the lot after the review that proposed it is marked `— peut-être déjà traitée (<sha>)`, so a review no longer feeds the next one with work already done (L422). `cadence session close` and `cadence lead tour` warn when the Claude Code `MEMORY.md` index, loaded at every turn, outgrows a size threshold (`memory.indexMaxBytes`), and propose to group its dated entries behind one line; `cadence config set` reads that key back like the others, so a wrong value stops with nothing written (L442). A measure of the earlier change to the passes, read on two windows of `cadence perf`, is recorded (L430).
|
|
76
|
+
|
|
71
77
|
**1.39.0**: free slots no longer stay empty (GitHub issue #21, L527, `docs/orchestrate.md`). With `--continue`, once the order is agreed (`--priority` or `priority:` of the parent `cadence.yaml`), the wave starts the next ready lot in each slot as soon as one frees up — a lot handed back at its pre-check frees its slot in seconds —, in priority order, within the session cap, the shared resources and one session per repository or one worktree per lot, and the journal says so. In a repository with `orchestrate.worktrees: true` and a cap above 1, every lot gets its own worktree, a lot launched alone or brought by `--add` included, so a second lot of the same repository plays in parallel instead of waiting; `--dry-run --continue` now simulates the draws one by one, as the wave plays them. `cadence orchestrate --dry-run` also announces the cost in euros of each lot and of the wave beside its token budget, and the final table and `--status` show the real cost (L529, price grid `perf.prices`). `cadence release` reads the « In two lines » section of `docs/whats-new.md` the way the test does, so a line too long or a duplicate is caught before the tag (L418), and a timing test that turned red under load in a fresh clone has a wide margin (L528).
|
|
72
78
|
|
|
73
79
|
**1.38.1**: `cadence spec green <lot>` now handles a lot closed at `vert: true` with no fingerprints (closed before the seal, GitHub issue #20): it replays the lot's acceptance command and, green, writes the fingerprints in a commit that cites the lot, so a later lot that amends one of its tests no longer has to be finished outside a wave (L526, `docs/orchestrate.md`). During a local task of the lead (1.38.0), `cadence deliver`, `raf move`, `cadence spec reseal` and `cadence spec green` refuse with a message that names the task and how to end it, instead of « état illisible » or an impossible `--sha` hint (L525, `docs/raf.md`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylad/cadence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.41.0",
|
|
4
4
|
"description": "A small, repo-native working method: a versioned plan linked to your commits, a changelog with screenshots, session rituals and deliveries proven by their effect.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sylvain Ladoire",
|
|
@@ -37,6 +37,15 @@
|
|
|
37
37
|
"docs/qualite.md",
|
|
38
38
|
"docs/overview.md",
|
|
39
39
|
"docs/whats-new.md",
|
|
40
|
+
"docs/concepts.md",
|
|
41
|
+
"docs/concepts-lot-light.svg",
|
|
42
|
+
"docs/concepts-lot-dark.svg",
|
|
43
|
+
"docs/concepts-vague-light.svg",
|
|
44
|
+
"docs/concepts-vague-dark.svg",
|
|
45
|
+
"docs/concepts-worktrees-light.svg",
|
|
46
|
+
"docs/concepts-worktrees-dark.svg",
|
|
47
|
+
"docs/concepts-livraison-light.svg",
|
|
48
|
+
"docs/concepts-livraison-dark.svg",
|
|
40
49
|
"docs/fonctionnement.md",
|
|
41
50
|
"docs/fonctionnement-lot-light.svg",
|
|
42
51
|
"docs/fonctionnement-lot-dark.svg",
|
package/skills/lead/SKILL.md
CHANGED
|
@@ -64,6 +64,12 @@ not truncated) or on a net bottleneck (`⚠ goulot`): at most one improvement lo
|
|
|
64
64
|
their place, and the human decides. Never open a lot for a micro-optimisation or for the variation of a single window.
|
|
65
65
|
Without a `⚠`, the header is empty and you say nothing about perf.
|
|
66
66
|
|
|
67
|
+
A line `mémoire · MEMORY.md <n> octets > seuil <s> · …` (after the worktree lines) says the `MEMORY.md` index of the parent
|
|
68
|
+
folder, loaded at every turn of your session, weighs more than `memory.indexMaxBytes` (12000 bytes by default): relay it in one
|
|
69
|
+
line and propose to group its dated entries under one line that points to a secondary index. Do it only after the human agrees;
|
|
70
|
+
the tour only measures, it never writes the memory. A line `mémoire · MEMORY.md illisible (…)` or the error line
|
|
71
|
+
`cadence.yaml · ✗ erreur : memory.indexMaxBytes …` is relayed as it is. `--json` never carries it.
|
|
72
|
+
|
|
67
73
|
Then report to the human in a compact table — one row per project — and propose **three** items
|
|
68
74
|
across projects, each with project, lot id and one sentence of justification: finish what is in
|
|
69
75
|
progress, then drift that blocks a delivery, then ready quick wins. **Stop and wait for the priority.**
|
|
@@ -59,6 +59,12 @@ With no project named, run `cadence session close --all` from the parent folder,
|
|
|
59
59
|
4. **Memory, filtered** (only if you keep a persistent memory): write down what the repository does
|
|
60
60
|
NOT already say — a trap and its cause, a decision or correction from the human, a collaboration
|
|
61
61
|
rule. Test: "do `git log` or the docs already say it?" → then no memory.
|
|
62
|
+
When the report has a section "Mémoire" (the `MEMORY.md` index of this folder weighs more than
|
|
63
|
+
`memory.indexMaxBytes` of `cadence.yaml`, 12000 bytes by default), propose to group the dated entries it
|
|
64
|
+
counts under one line that points to a secondary index (a memory file holding them): the index is loaded
|
|
65
|
+
at every turn, so its weight is paid by every session. Move the entries only after the human agrees, then
|
|
66
|
+
run `cadence session close` again — the section is gone or the size has dropped. Without the agreement
|
|
67
|
+
nothing moves and the warning comes back at the next close. The command never writes the memory.
|
|
62
68
|
5. **Skills and agents, on threshold, PROPOSED**: a skill when the same chain of commands was done by
|
|
63
69
|
hand at least twice today; an agent update when an agent got something wrong or its domain moved.
|
|
64
70
|
List them with the benefit; the human decides. Never create them here.
|
|
@@ -5,7 +5,7 @@ Read the lot, its notes and sub-tasks in the plan (`.cadence/raf.yaml`, the lega
|
|
|
5
5
|
{{spec}}
|
|
6
6
|
|
|
7
7
|
What you produce, in `<dir>/<écran>/`:
|
|
8
|
-
1. `maquette.html`: static HTML + CSS, to the project's charter (reuse its variables, fonts and components; invent no style when an equivalent exists), no business JavaScript. Every state is an addressable section: `<section id="etat-vide">`, `etat-chargement`, `etat-erreur`, and `etat-charge` when the screen has data. Every field of the sheet carries `data-testid="<id of the field>"`.
|
|
8
|
+
1. `maquette.html`: static HTML + CSS, to the project's charter (reuse its variables, fonts and components; invent no style when an equivalent exists), no business JavaScript. Every state the sheet covers is an addressable section: `<section id="etat-vide">`, `etat-chargement`, `etat-erreur`, and `etat-charge` when the screen has data (a light mockup covers only the states the lot touches, as said above). Every field of the sheet carries `data-testid="<id of the field>"`.
|
|
9
9
|
2. The captures described above, one per state and per width, copied from the Playwright output directory of this wave into the folder (give a capture the absolute path of that directory, as the Playwright paragraph says, then copy it).
|
|
10
10
|
3. `fiche.yaml`, in the format of the template below. Check it with `cadence spec check <fiche>` until it is valid, and derive the QA expectations with `cadence spec qa <fiche>` only if the lot asks for it.
|
|
11
11
|
|
|
@@ -18,7 +18,7 @@ Sheet rules:
|
|
|
18
18
|
- Check every date in the sample data of the mockup before keeping it: compute its weekday (a command such as `date -d 2026-09-03 +%A`, or a script), never write it from memory — a « Wed 03/09 » that is a Thursday sends the mockup back for nothing. The same goes for any figure that must add up (a score and its result, a total and its parts).
|
|
19
19
|
- Expose the messages exactly as the screen shows them; the message of a rule is visible in the state its `etat` names.
|
|
20
20
|
- A rule you invent is not a free choice: **a hypothesis whose `si_faux` involves an expensive-to-undo choice is a question, not a hypothesis.** The four expensive categories: a data model or storage (key, migration, user data created once shipped); a public or shared API contract, or an already published route / URL; a destructive or irreversible action (deletion, sending, payment); a rule that contradicts an existing sourced rule. In those cases do not write the hypothesis: stop and put the question under "questions" (the lead answers and the step is resumed). Everything else (wording, order of the fields, error state, spacing) is a written hypothesis, cheap to correct later.
|
|
21
|
-
-
|
|
21
|
+
- `sousSeuil` is only for a lot that changes nothing the user can see: if, after reading the lot and the code, you find that it changes no screen, no component, no state, no text and no style (an internal rename, a refactoring with the same rendering), write one line with the reason in `sousSeuil`, create no mockup and no sheet, and finish: the lot then follows the usual path. A change to a component, a state, a text or a style of an existing screen is NOT under the threshold, even when it adds no page: write its mockup (the light one when this brief asks for it).
|
|
22
22
|
|
|
23
23
|
Commit the spec with explicit paths (never `git add -A` or `commit -a`), a message that cites the lot (`spec({{lot}}): …`), the sheet, the mockup, the captures and `contraste.md` together, and leave the working tree clean. Do not implement the screen, do not write application code or tests, do not push, deliver, run `raf done`, `raf ux` or `raf review`.
|
|
24
24
|
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|