@sylad/cadence 1.23.0 → 1.25.1
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/agents/code-reviewer.md +17 -0
- package/agents/qa-reviewer.md +17 -0
- package/dist/cli.js +69 -19
- package/dist/gate-record.js +34 -8
- package/dist/gate-verdict.js +46 -0
- package/dist/git.js +31 -1
- package/dist/lead.js +4 -1
- package/dist/orchestrate/command.js +224 -131
- package/dist/orchestrate/guard.js +34 -2
- package/dist/orchestrate/integrate.js +6 -9
- package/dist/orchestrate/registry.js +6 -4
- package/dist/orchestrate/table.js +60 -28
- package/dist/perf.js +24 -2
- package/dist/plan.js +25 -2
- package/dist/push-guard.js +50 -1
- package/dist/release.js +11 -0
- package/dist/verify-gate.js +32 -5
- package/docs/cadence-hud.md +8 -0
- package/docs/corrections.md +1 -0
- package/docs/deliver.md +12 -3
- package/docs/lead.md +11 -4
- package/docs/orchestrate.md +10 -8
- package/docs/perf.md +3 -1
- package/docs/raf.md +4 -2
- package/docs/releasing.md +9 -3
- package/docs/skills.md +6 -0
- package/docs/whats-new.md +9 -0
- package/package.json +1 -1
- package/skills/deliver/SKILL.md +8 -0
- package/skills/lead/SKILL.md +26 -1
- package/templates/orchestrate/implement.md +6 -1
package/docs/orchestrate.md
CHANGED
|
@@ -20,6 +20,7 @@ cadence orchestrate … --dry-run # preconditions + the plan of the
|
|
|
20
20
|
cadence orchestrate --continue [--until 18:00] [--priority cadence,maritime] # the wave draws the next ready lot itself (below)
|
|
21
21
|
cadence orchestrate --waits [--since 2026-10-12] # time spent waiting for a session slot, per day and per wave (below)
|
|
22
22
|
cadence orchestrate --status [<wave> | --wave <wave>] # the live waves and the repositories they hold, then the table (default: the last wave of this folder)
|
|
23
|
+
cadence orchestrate --status [<wave>] --detail <project:lot> # the detail of one lot that the compact table leaves out: given goal, UX note and captures, tests already red at the start, choices made, hypotheses of the sheet, proposals, UX verdict (read from the wave's state; the bare `<lot>` is enough when unique in the wave; not with `--watch`)
|
|
23
24
|
cadence orchestrate --status [<wave>] --watch [--interval 10] # the same, redrawn every 10 s (--interval in seconds); stops by itself when no wave is running (with an explicit `<wave>`: when that wave is no longer running)
|
|
24
25
|
cadence orchestrate --resume [--wave <wave> | <wave>] [--budget 1M] [--answer ol-companion:L22 "reply"] [--drop ccc:L29]
|
|
25
26
|
cadence orchestrate --resume --wave <wave> --budget maritime:B26.4=+300k # replay a lot handed back for its budget, with the budget raised (below)
|
|
@@ -394,9 +395,10 @@ When the title, the goal or a note of a lot quotes ids of a reference inventory
|
|
|
394
395
|
|
|
395
396
|
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.
|
|
396
397
|
|
|
397
|
-
- **`implement` and `fix`** get the path of the sheet and of the mockup, and **one line per rule** (`- R3 [erreur, hypothese] statement. Message: « … ». Tests: « R3 — … »`). The session makes the acceptance tests green **without modifying them** (they are named when the `accept` step ran; the lock above hands the lot back otherwise), **the tests it writes in addition cite the rule** in their name (`R2 — …`), and it **does not invent a rule**: a case the sheet does not cover is written in the report (`nonVerifie` or `choix`), not coded. In a `fix` the sheet stays the reference: a finding that contradicts it is contested under `choix`. Both briefs also say that a conflict with the sealed test of **another** lot is a question to the lead naming both tests (file and `Rn — …` name), never a modification or a choice (L231). The `accept` and `accept-back` briefs and the `acceptance-writer` agent require each assertion to cite, in a comment, the rule and the sentence of its statement that it verifies, to assert nothing beyond that sentence, and to go through a self-check before the commit (an assertion with no sentence behind it is removed or listed under `choix`); the `review` and `review-small` briefs and the `code-reviewer` agent re-read each sealed assertion against the sentence it cites, and one that asserts more than the sheet is a *major* finding (« sealed test stronger than the sheet », worded « than its rule » in the briefs), corrected by the lead with `cadence spec reseal` (L231).
|
|
398
|
+
- **`implement` and `fix`** get the path of the sheet and of the mockup, and **one line per rule** (`- R3 [erreur, hypothese] statement. Message: « … ». Tests: « R3 — … »`). The session makes the acceptance tests green **without modifying them** (they are named when the `accept` step ran; the lock above hands the lot back otherwise), **the tests it writes in addition cite the rule** in their name (`R2 — …`), and it **does not invent a rule**: a case the sheet does not cover is written in the report (`nonVerifie` or `choix`), not coded nor tested (the « edge cases » point of the L428 block below says it explicitly). In a `fix` the sheet stays the reference: a finding that contradicts it is contested under `choix`. Both briefs also say that a conflict with the sealed test of **another** lot is a question to the lead naming both tests (file and `Rn — …` name), never a modification or a choice (L231). The `accept` and `accept-back` briefs and the `acceptance-writer` agent require each assertion to cite, in a comment, the rule and the sentence of its statement that it verifies, to assert nothing beyond that sentence, and to go through a self-check before the commit (an assertion with no sentence behind it is removed or listed under `choix`); the `review` and `review-small` briefs and the `code-reviewer` agent re-read each sealed assertion against the sentence it cites, and one that asserts more than the sheet is a *major* finding (« sealed test stronger than the sheet », worded « than its rule » in the briefs), corrected by the lead with `cadence spec reseal` (L231).
|
|
398
399
|
- **A self-check list before handing back (L222).** Both `implement.md` and `fix.md` carry the same fixed list (plain text, no marker, no new variable), taken from the recurring findings of the reviews of 2026-10-15: the usage documentation (README and architecture documentation) updated in the same commits as the behaviour; one test named `Rn — …` per rule of the sheet (one per business rule of the lot when there is no sheet); no dead code and no test that cannot fail (an assertion always true, or a test that does not exercise its rule); for a modified screen, the capture due of each state at 1440 and 390 px with a file name (where it goes is the Playwright paragraph's rule) (a lot with no modified screen owes none and says so); no false comment, docstring or message. The session goes through it point by point on its own commits before it reports, and a point it cannot hold is written under `choix` or `nonVerifie` of its report: the report schema has no new field. In `fix` the list covers only what the session changed, and "fix them, and nothing else" stays. The other briefs (`fix-minors`, `spec`, `spec-back`, `accept`, `review-small`…) are unchanged. The before/after measure — correction passes per lot, replayed with `cadence corrections` (`--until <bound>` for the "before", `--since <bound>` for the "after", the bound being the time of the `npm run service` that puts the list in service) — is in [docs/mesures/autocontrole-avant-revue-2026-10-15.md](mesures/autocontrole-avant-revue-2026-10-15.md).
|
|
399
400
|
- **Auto-verification once the tests are green (L236).** After the self-check list, `implement.md` and `fix.md` carry a second fixed block: when the full suite and the build pass, the session re-reads the complete diff of its lot (of its fix commits, in `fix`) against the grid of the `code-reviewer` agent — code bug, architecture drift, `docs/architecture.md` (or the project's architecture documentation) up to date, edge cases, one test per changed behaviour — fixes what it finds at once (test first, a `fix(<lot>): …` commit, suite and build replayed), then starts `resume` with an « auto-vérification » paragraph: what it re-read, found and fixed, could not hold. No new report field. The cost bound is written in the brief: one reading of the diff, not a second review (no subagent, no files the diff does not touch). Measure before the block (`cadence corrections --classify`, 10 to 17-10): 356 passes, 53 % code bug, 19 % architecture drift, 14 % unclear spec; `cadence perf` of 17-10: 37 % right the first time. Target: above 50 % on the next window with tokens per lot stable (`cadence perf`).
|
|
401
|
+
- **Limits and rules before the walkthrough (L428).** `implement.md` (not `fix.md`) carries a third fixed block between the self-check list and the real walkthrough, aimed at the first cause of correction passes: a code bug the review finds in the diff (100 of the 143 classified passes of the 23–29-10 window, `cadence perf`). The session re-reads its diff against the fiche and the lot and, **per rule** (`Rn`), names the test that fails if the code breaks it and the line that holds it; **per function, parser, pattern, threshold or command it adds or changes**, lists the inputs at the edge (empty, absent, twice, wrong order, wrong type, refused, the real shape of the data, the other callers) and gives each a test that fails without its code, except a case the fiche does not cover: with a fiche, the session does not code a rule the fiche does not state, so that case goes under `"nonVerifie"` or `"choix"`, neither coded nor tested (a lot without a fiche has no such exception: every listed case is due a test, and one left untested goes under `"nonVerifie"`); **per figure, « all », « never » or « always »** written in README, docs, comments or tests, checks it against the code or by running it. A listed case left untested goes under `"nonVerifie"`. The reading of the 23–29-10 passes that led to it, and the way to measure its effect on two windows, are in [mesures/bug-code-2026-10-29.md](mesures/bug-code-2026-10-29.md). Test: `test/limites-et-regles-l428.test.ts`.
|
|
400
402
|
- **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.
|
|
401
403
|
- **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.
|
|
402
404
|
- **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.
|
|
@@ -478,7 +480,7 @@ suspended, except for such a visible lot, where the UX session needs budget and
|
|
|
478
480
|
|
|
479
481
|
**Sub-tasks covered by commits (L82)**: when the review concludes compliant, the open sub-tasks of the lot that a work commit of the lot cites (`feat(L3/t2): …`, in the project or in one of the lot's neighbouring repositories; a commit that cites only the lot covers none) are closed in the same plan commit as the verdict, with the warning `sous-tâches closes : L3/t2 (sha)`, so `raf done` does not refuse the lot over finished work. A read-only plan is never written: each such sub-task is returned as a proposal `[sous-tâche clore] L3/t2 — couverte par <sha>`, for you to close with the project's tool. On a plan in a foreign format (read-only, `cadence.yaml` `plan:`), sub-task ids are read as the plan reads them: `B53/t4-ux1` covers `t4-ux1` only, not `t4`, and a list such as `feat(B53/t4-ux1,t5)` covers each of its sub-tasks. The lot id is not read inside another id (`fix(E-A2/t3,t4, A2/t1)` covers `A2/t1` only for lot `A2`), and a list may repeat the lot id (`fix(L1/t1, L1/t2,t3)` covers `t1`, `t2` and `t3`). A non-compliant review closes nothing.
|
|
480
482
|
|
|
481
|
-
**Choices, not questions**: the author brief tells the session to decide minor interpretation questions itself and to list them under `choix` in its report; the reviewer receives that list to re-read, and the final table prints each one (`choix fait : …`). A session stops with a question only on a real blocker: a decision that changes the scope or the architecture or is costly to undo, AND that the plan, its notes and CLAUDE.md do not settle; everything else is a choice.
|
|
483
|
+
**Choices, not questions**: the author brief tells the session to decide minor interpretation questions itself and to list them under `choix` in its report; the reviewer receives that list to re-read, and the final table prints each one (`choix fait : …`); `--status` does not (L441: its table is compact, one line per lot, and ends with one line naming the lots that have a detail) — `--status --detail <project:lot>` prints them. A session stops with a question only on a real blocker: a decision that changes the scope or the architecture or is costly to undo, AND that the plan, its notes and CLAUDE.md do not settle; everything else is a choice.
|
|
482
484
|
|
|
483
485
|
**Frozen copy of the tool (L61)**: at the real start of a wave (not with `--dry-run` or `--status`), cadence copies the
|
|
484
486
|
parts of its package that run the wave (`bin/`, `dist/`, `templates/`, `agents/`, `skills/`, `package.json`; `node_modules`
|
|
@@ -581,7 +583,7 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
581
583
|
- **Integration, before `ready`.** At the end of its cycle (compliant review, UX included) the lot is integrated into the repository's branch, and only
|
|
582
584
|
then does it become `ready`; the repository's branch gets the lots one by one, on a linear history. Under the repository's **integration lock**
|
|
583
585
|
(a file next to the wave lock, same format, pid and start time, stale when the holder is dead, plus an in-memory queue because the lots of a wave share
|
|
584
|
-
a pid; the wait is bounded to 10 minutes and holds
|
|
586
|
+
a pid; a lot queues behind the other lots of its wave without limit (L425), the wait for another process's lock is bounded to 10 minutes, and no wait holds a session slot):
|
|
585
587
|
1. the repository must still be on its start branch (`le dépôt est sur <branch> et non sur <start branch>`) and have no modified tracked file
|
|
586
588
|
(`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
|
|
587
589
|
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
|
|
@@ -635,7 +637,7 @@ nothing below applies and the wave behaves as before; any other value is refused
|
|
|
635
637
|
6. the worktree and the branch, clean and merged, are removed; the lot is `ready`.
|
|
636
638
|
A stop between the advance and the plan write is resumed at the `integrate` step: the branch being already contained in the repository's, the rebase
|
|
637
639
|
and the advance are skipped and the verdict is written once.
|
|
638
|
-
- **A lot handed back at the integration step** (lock held for more than 10 minutes, repository's branch changed, tracked file modified, rebase conflict,
|
|
640
|
+
- **A lot handed back at the integration step** (lock held by another process for more than 10 minutes, repository's branch changed, tracked file modified, rebase conflict,
|
|
639
641
|
red tests, blocking finding of the short review, plan write refused) keeps `next: integrate`, its worktree and its branch. **Only
|
|
640
642
|
`--resume --wave <wave> --integrate <project:lot>` replays it** (repeatable): a plain `--resume`, `--drop`, `--answer` or `--budget` never does. The lot
|
|
641
643
|
restarts at `suspended`, step `integrate` (`<project>:<lot> → suspended — intégration reprise (--resume --integrate)`), and the rebase and the checks run
|
|
@@ -749,28 +751,28 @@ verdict and screenshots, it does not record it), the push and the deliveries, on
|
|
|
749
751
|
**Several waves at once**: the lock is per **repository** (a lock in the repository's shared state,
|
|
750
752
|
`orchestrate.lock`, which also makes `cadence deliver` refuse that repository), not per folder. A wave is
|
|
751
753
|
refused only when one of its repositories is held by a live wave (`<repo> : une orchestration y est déjà en
|
|
752
|
-
cours`; the lock of a dead process is detected and cleared);
|
|
754
|
+
cours`; the lock of a dead process is detected and cleared), except `--resume --integrate|--replay` (L443): it **waits** for the live wave to end (the line `<repo> : tenu par la vague <wave> (pid <pid>) : attente de sa fin avant de reprendre l'intégration` is printed once per holder, the lock polled every 5 s; the waiting resume is registered among the live waves, so a second `--resume` of the same wave is refused at once instead of waiting too; and since the lock carries the wave id, a lock held by a live process of the same wave, between the first resume's lock and its registration by the run, is refused the same way, `la vague <wave> tourne encore (pid <pid>, verrou de <repo>)`, instead of waiting on itself and replaying a state read before the wait) and gives the usual refusal, code 2, only after 3 hours; the waiting resume takes the lock the moment the holder is gone and keeps it until it plays (released if it fails first), so a second resume waiting on the same repository sees it held and waits on, instead of passing the check too and exiting 2 with its lots already written; a plain `--resume` refuses at once. A resume already refused for another reason (claude not found, a session still alive, an unknown lot) exits 2 at once without waiting, and a `--stop-after-current` asked while the resume waits is kept: the integration does not start. Two waves on different repositories run side by
|
|
753
755
|
side, even when started from the same parent folder. A repository is released **as soon as every lot of the wave that
|
|
754
756
|
touches it is finished** (ready, handed back or failed): its lock and its `pre-push` guard are lifted while the wave goes
|
|
755
757
|
on elsewhere, so `cadence deliver` accepts it (a repository with a lot still to play, a question or a suspended lot stays
|
|
756
758
|
held until the wave ends). `cadence deliver` also reads the wave's own state when a lock is still held: it refuses only if the wave still has a lot running, queued or suspended **in this repository**, and goes ahead when the lots left are elsewhere (an unreadable wave state still refuses). They share a **cap on simultaneous sessions**, counted
|
|
757
759
|
across all live waves: 2 by default, `--max-sessions N` (or `CADENCE_MAX_SESSIONS=N`) to change it — give
|
|
758
760
|
the **lowest cap of the live waves applies to all of them** (L395): each wave counts ALL live sessions, whatever their slot, and waits while that
|
|
759
|
-
count has reached the lowest cap declared by any live wave (re-read at every turn: it rises again when the wave with the low cap ends), so a wave at 4 next to a wave at 3 plays 3 sessions, never « 4/3 en cours » (on 28-10 L51 waited 16 min 28 s behind it). The `en attente d'un créneau` line and the free slots (`créneaux libres`) use that common cap. A session that finds no free slot **waits** (the wave is not refused; the journal says
|
|
761
|
+
count has reached the lowest cap declared by any live wave (re-read at every turn: it rises again when the wave with the low cap ends), so a wave at 4 next to a wave at 3 plays 3 sessions, never « 4/3 en cours » (on 28-10 L51 waited 16 min 28 s behind it). The `en attente d'un créneau` line and the free slots (`créneaux libres`) use that common cap. The sessions the lead started by hand and declared with `cadence lead hold` (L424, [lead.md](lead.md#lead-hold-and-release)) count in that total like wave sessions, until their `release` or their time limit: the wave waits for a slot instead of starting a session over the cap, and the line names them (`2/3 en cours : w1, session du lead correctif-L12`). A session that finds no free slot **waits** (the wave is not refused; the journal says
|
|
760
762
|
`en attente d'un créneau de session depuis …`, repeated every minute, then `créneau de session obtenu après …`). A free slot is always taken, even when a request is already written (relaunching a cut review is not a wait; the request applies afterwards). Once the wait has really started (a first try found no free slot), it reads the wave's requests at every turn (L332): a `--drop` aimed at that lot ends it (the slot is never taken, no session starts, the lot is *handed back* `retiré de la vague (--drop)` like a lot not started) and a `--stop-after-current` ends it too (the lot is *suspended* `arrêt demandé (--stop-after-current)`, the wave **interrupted** and resumable); a `--drop` aimed at another lot does not. The step announced but never started is removed from the state and the lead's answer it was carrying (`pendingAnswer`, taken out of the lot when the brief was built) is put back: `--resume` replays the step with the answer in its brief, and a lot handed back by `--drop` says « la réponse du lead (« … ») n'a pas été jouée » in its outcome. When it is the relaunch of a session already run in the step (a cut review relaunched in a fresh session, L256; the formatting retry) that was waiting, the step ends `failed` with its cause (« relance en session neuve non faite : … ») and keeps what the first session left: its id, its tokens (step and wave budget), its stderr in `<n>-<kind>.first.err` (and its output in `.first.json` for the formatting retry).
|
|
761
763
|
`cadence orchestrate --waits [--since YYYY-MM-DD]` adds those waits up from the journals of `.cadence/runs/` of the launch folder — per day (cumulated wait, number of waits, waves, quota stops — one per "→ suspended — quota atteint" line of the journals, counted on the day of that line, so a wave resumed and stopped again on another day counts once on each day, not once as a wave, tokens) and per wave that waited (the longest wait, a wait that never got its slot) — to decide the cap from figures; each wait is paired with its lot (the journal line carries `project:lot`), so two lots of one wave waiting at the same time (one per repository) are each counted once, and only a wait that never got its slot is reported as such; journals from before this carry no lot and are paired per wave. it reads only, and refuses any other action in the same call. The figures of 2026-10-12 and the cost of a third slot are in `docs/mesures/attente-creneaux-2026-10-12.md`.
|
|
762
764
|
A slot or a registry entry is owned by a pid **and** its start time: a reused pid is a dead owner. Wave
|
|
763
765
|
identifiers are reserved atomically (`-2`, `-3` suffix when two waves start in the same minute; an existing
|
|
764
766
|
`--wave` is refused). The registry of live waves and the slots live under `~/.cadence/orchestrate/` (`CADENCE_HOME`
|
|
765
767
|
to move it): `cadence orchestrate --status` lists, from any folder, the live waves, the repositories each
|
|
766
|
-
still holds (those not yet released), the cap of each wave, the slots in use and the **free slots** (`créneaux libres : N sur M`: M — the cap set with `--max-sessions` or `CADENCE_MAX_SESSIONS`, 2 by default — minus, for each live wave, the smaller of its cap and the repositories it still holds (default mode: a lot that declares neighbouring repositories holds each of them; with `orchestrate.worktrees` and lots playing in worktrees, the registry carries `lanes` instead: one per lot
|
|
768
|
+
still holds (those not yet released), the cap of each wave, the slots in use and the **free slots** (`créneaux libres : N sur M`: M — the cap set with `--max-sessions` or `CADENCE_MAX_SESSIONS`, 2 by default — minus, for each live wave, the smaller of its cap and the repositories it still holds (default mode: a lot that declares neighbouring repositories holds each of them; with `orchestrate.worktrees` and lots playing in worktrees, the registry carries `lanes` instead: one per lot still to play in a worktree — running or waiting, a lot settled (ready, handed back, failed, suspended) no longer counts, updated at each settling (L433) —, one per other group of lots) — minus the sessions the lead started by hand and declared with `cadence lead hold <label>` (L255, [lead.md](lead.md#lead-hold-and-release): the line then reads `… · sessions du lead : N (labels) · créneaux libres : …`, and prints even with no live wave) — the figure the `lead` skill reads before starting a subagent; the lead never raises the cap itself, that is the human's decision, L208). Without an identifier it then prints the table of the wave launched most recently from this folder (by launch time, not by alphabetical order of the identifier).
|
|
767
769
|
|
|
768
770
|
**Context of a session (L398)**: a session starts with `--settings '{"enabledPlugins":{"<id>":false,…}}'` naming the plugins enabled (`enabledPlugins: { "<id>": true }`) in the user's `$CLAUDE_CONFIG_DIR/settings.json` (fallback `~/.claude/settings.json`) and the project's `.claude/settings.json` and `.claude/settings.local.json`: a disabled plugin registers neither its hooks (superpowers' start hook alone injected 3.4 kB) nor its skills, which a session that only reads, edits and runs Bash never calls. The first turn of an `implement` session drops from 42 541 to 38 330 tokens (−4.2 k, −9.9 %, [measure](mesures/contexte-session-2026-10-28.md), which also records the real check that `enabledPlugins: false` stops the plugin's start hook and removes its skills). Nothing else is touched: the user's and the project's settings — `permissions`, `autoMode`, `language`, `env`, hooks, provider and authentication —, the user's skills, agents and global `CLAUDE.md` all apply with their normal precedence; the JSON holds plugin ids only, never a secret, and is passed inline (1 000 ids weigh 50 kB, under Linux's 128 kB argument limit). Without any enabled plugin, no flag is added. The same flag is on the formatting relaunch (`--resume`) and on the `cadence ref analyse` sessions; `claude-local` already runs `--bare`. A step keeps its tools: the `ux` step its Playwright MCP (`--strict-mcp-config --mcp-config`), the reviewer agents their `--agents` / `--agent` and `tools` list, and the sandbox (`KUBECONFIG`, `PROD_VARS`) is unchanged.
|
|
769
771
|
|
|
770
772
|
**Guards, imposed by the code**: a global cap of simultaneous sessions, one wave per repository at
|
|
771
773
|
a time (above); `Agent`, `Monitor`, `git push`, `cadence deliver`, `raf done|review|ux` are denied to the sessions, and they run with `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` (no `run_in_background` on Bash or sub-agents: `claude -p` waits for any background task before exiting, up to 10 min of inactivity, so a finished session kept its slot; L285 — the session is **not** killed at its `StructuredOutput`, the real cost is only in the final `result` message); a
|
|
772
774
|
temporary `pre-push` hook, installed for the duration of the wave and removed at its end (or, for a repository, as soon as all the lots that touch it are finished), refuses any push
|
|
773
|
-
from a session (`CADENCE_ORCHESTRATED` is in their environment; L405: the same hook also calls `raf hook pre-push` for every other push, the lead's or a session started by hand: a push whose local sha has in its history — all of it, not only the range `remote..local`: a commit of a not-ready lot already on the remote still blocks, the wave would stop on « push détecté » —, a deleted branch carrying nothing, a commit of a lot of the live wave that is not ready (running, queued, in question, handed back, failed) is refused with exit 3, the message naming the lot, its state and the safe command `git push <remote> <last deliverable sha>:<remote branch>` (the newest commit of the range `remote..local`, first-parent, that holds no commit of a lot that is not ready; « aucun commit livrable » when there is none); a not-ready lot whose commits cannot be read (a plan that no longer knows the lot, a neighbour that is not a git repository) refuses the push as well, naming the lot (« commits illisibles … rétablir le plan ou attendre la fin de ces lots »), with no safe command since no sha passes the guard while it stays unreadable; a ref pushed outside `refs/heads/` (a tag) is refused the same way with no safe command at all, since pushing another sha under that name would publish a tag that does not name its own commit (wait for the lots to be ready, or tag a deliverable commit): the wave would not tolerate that push either and would stop on « push détecté »; a wave state that cannot be read only warns, an absent or broken `raf` lets the push through, `--no-verify` overrides — the same check as the guard of `cadence deliver --sha`; a repository that already has another
|
|
775
|
+
from a session (`CADENCE_ORCHESTRATED` is in their environment; L405: the same hook also calls `raf hook pre-push` for every other push, the lead's or a session started by hand: a push whose local sha has in its history — all of it, not only the range `remote..local`: a commit of a not-ready lot already on the remote still blocks, the wave would stop on « push détecté » —, a deleted branch carrying nothing, a commit of a lot of the live wave that is not ready (running, queued, in question, handed back, failed) is refused with exit 3, the message naming the lot, its state and the safe command `git push <remote> <last deliverable sha>:<remote branch>` (the newest commit of the range `remote..local`, first-parent, that holds no commit of a lot that is not ready; « aucun commit livrable » when there is none); a not-ready lot whose commits cannot be read (a plan that no longer knows the lot, a neighbour that is not a git repository) refuses the push as well, naming the lot (« commits illisibles … rétablir le plan ou attendre la fin de ces lots »), with no safe command since no sha passes the guard while it stays unreadable; a ref pushed outside `refs/heads/` (a tag) is refused the same way with no safe command at all, since pushing another sha under that name would publish a tag that does not name its own commit (wait for the lots to be ready, or tag a deliverable commit): the wave would not tolerate that push either and would stop on « push détecté »; a wave state that cannot be read only warns, an absent or broken `raf` lets the push through, `--no-verify` overrides — the same check as the guard of `cadence deliver --sha`; then (L426), when the project declares a gate, a push of a branch whose local sha has no green verdict of `cadence verify --gate` is refused too (a commit that changes only the plan takes the verdict of its parent, L432; `docs/deliver.md`; `CADENCE_PUSH_UNGATED=1` overrides it; when the hook is removed and the project declares a gate, a gate-only `pre-push` hook stays so that the delivery push, which comes after the last lot of the repository, is still checked against the verdict); a repository that already has another
|
|
774
776
|
`pre-push` hook is refused before anything starts — `pushurl` is never touched); after every session the
|
|
775
777
|
upstream ref and `git ls-remote` are compared with the "before" (a `git checkout <sha>` or `git switch` that only moves `HEAD` — detached `HEAD`, another branch, no upstream any more — is not a push: the upstream ref is compared only when it is the same branch on both sides, `git ls-remote` stays the judge; the write briefs forbid `git checkout` and `git switch` in the repository and point to a throwaway `git worktree add --detach <tmp> <sha>`; should a write session still end off its starting branch, a clean tree with nothing outside the branch gets the branch put back (`git checkout <branch>`, a warning in the log, the lot carries on from the right `HEAD`), anything else — including a put-back that fails (`index.lock`, a conflicting file), cause « HEAD déplacé — remise de la branche <b> impossible : <git message> » — hands the lot back with the cause « HEAD déplacé » and holds the queue of that repository, the other repositories carrying on; a push or a removed guard hook in ANY watched repository, project or neighbour, is checked first and wins over a moved `HEAD`, L214), and a read-only step that left only UNTRACKED files (screenshots written at the root by a script with a relative path) is not an incident: the files are moved to `.cadence/runs/<wave>/<project>--<lot>/stray/` (kept in their tree; for a neighbour repository, under `stray/<its relative path, `/` replaced by `_`>/`, e.g. `stray/.._gitops/`), a warning naming them goes to the lot's warnings and the wave log, the verdict is kept and the lot continues (L162; a move that is impossible, or an untracked file that vanished during the step, stays an incident); a review that changed `HEAD` or a tracked file (a commit, a modified file) is an incident of that lot only: the lot is handed back to the lead, the lots of the other repositories carry on while the lots still queued behind it in the same repository are suspended (resumable, nothing started on a dirty tree), and a verdict the review had already produced is reported in the outcome and the lot's warnings (never recorded in the plan). **The lead's own push is the one exception (L352)**: `git push origin <sha>:main` to deliver a ready lot while the wave runs (the « livrable jusqu'à <sha> » path of the lead skill) is tolerated, because the pushed state is one the lead has re-verified. The check is the guard of `cadence deliver --sha`: every remote branch that moved advanced fast-forward (or was created) to a sha that is an ancestor of the repository's `HEAD` and holds no commit of a lot of the wave that is not ready (running, queued, in question, handed back, failed, or whose commits cannot be read), so no commit of a lot of the wave that is not ready (a commit that cites no lot, such as one of the plan, passes); the journal says `push du lead (livraison de <lot>) toléré` (`livraison du plan` when no ready lot is in the range) and the session carries on. Any other push — a deleted branch, a forced push, a sha outside the history of `HEAD`, a commit of a lot still running — and a removed guard hook still stop the wave. `raf done|ux|review` and `cadence deliver` refuse when
|
|
776
778
|
`CADENCE_ORCHESTRATED` is set.
|
package/docs/perf.md
CHANGED
|
@@ -41,7 +41,7 @@ current window, with at least 10 classified passes) is flagged by a `⚠` even w
|
|
|
41
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.
|
|
42
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 `⚠`).
|
|
43
43
|
It is **not** the instant of `cadence corrections --since` (waves created strictly after).
|
|
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
|
|
44
|
+
- **`--json`** prints one object `{ since, today, windows: { current, previous }, throughput, corrections, interventions, precheck, escalations, instables, tokens, lotMinutes, delays, durations, cost, quality, orphans, warnings }`; every indicator carries
|
|
45
45
|
`{ current, previous, change, warn, twin, lots, note }` (`twin`: the path of its quality twin, e.g. `throughput.escaped`). Same values as the text.
|
|
46
46
|
|
|
47
47
|
## Tokens, delays, wall time and cost (L226)
|
|
@@ -76,6 +76,8 @@ cache writes are billed at the input price (the grid has no write price, so the
|
|
|
76
76
|
a step without any readable token costs 0 € (the lot stays in the population); a `local` step costs 0 € whatever its counters (outside quota and budget) and needs no price.
|
|
77
77
|
A lot with a step of a model that has no price in the grid (or no readable `model`, named `inconnu`) is left out of the cost and a warning on the error output names the model and the number of lots; delivered lots without any journal are counted aside (`N lot(s) livré(s) sans journal`). No `⚠` on a cost that is `n/d`.
|
|
78
78
|
|
|
79
|
+
**Usual wall time of a lot** (L414). `--json` only: `lotMinutes` is the median wall time of a rendered lot, in minutes (start of its first step to end of its last, journals without a readable start or end left out), per size class under the same keys as `tokens.byEstimate` (`0.5`, `1`, `0.5+spec`…), in the Indicator format, with `warn` always false and `twin` null (a duration has no direction of its own and no quality twin). It is printed in no line and raises no `⚠`: the lots panel of the HUD (`/hud lots`, `docs/cadence-hud.md`) reads it as « the usual time of a lot of this size » and compares it with the elapsed time of each lot in progress.
|
|
80
|
+
|
|
79
81
|
No indicator of produced volume (lines of code, commits, total tokens or euros of a window) is printed or exposed: only per-lot medians, median durations and shares come out. Tokens and euros compare lots of the same size; they never set a spending target.
|
|
80
82
|
|
|
81
83
|
## Delivery quality and the tour header (L227)
|
package/docs/raf.md
CHANGED
|
@@ -59,6 +59,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
59
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` (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`) |
|
|
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
|
+
| `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 |
|
|
62
63
|
| `raf public <id> "title"` · `raf public <id> --clear` | set, replace or remove the public title of a lot |
|
|
63
64
|
| `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) |
|
|
64
65
|
| `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) |
|
|
@@ -76,7 +77,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
76
77
|
| `raf check --ignored` | list the acknowledged commits with their date and reason |
|
|
77
78
|
| `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) |
|
|
78
79
|
| `raf gantt [-o file]` | standalone Gantt page |
|
|
79
|
-
| `raf hook pre-push` | called by the temporary `pre-push` hook that `cadence orchestrate` installs (not by `raf hook install`): reads git's lines on the standard input and exits 3 when the push carries a commit of a lot of the live wave that is not ready, naming the lot and the safe `git push <remote> <sha>:<remote branch>`, or when the commits of a not-ready lot cannot be read (the guard of `deliver --sha`: the push is then unverifiable, the lot is named); exits
|
|
80
|
+
| `raf hook pre-push` | called by the temporary `pre-push` hook that `cadence orchestrate` installs (not by `raf hook install`): reads git's lines on the standard input and exits 3 when the push carries a commit of a lot of the live wave that is not ready, naming the lot and the safe `git push <remote> <sha>:<remote branch>`, or when the commits of a not-ready lot cannot be read (the guard of `deliver --sha`: the push is then unverifiable, the lot is named); exits 3 as well, with or without a live wave, when the project declares a gate (`orchestrate.test` or `orchestrate.build`) and the local sha of a pushed branch has no green verdict of `cadence verify --gate` (L426; a commit that changes only the plan takes the verdict of its parent, L432: « aucun verdict de la porte » or red, naming `cadence verify --gate --sha <sha>`; `CADENCE_PUSH_UNGATED=1` in the environment of the `git push` lets it through and says so); exits 0 when nothing is to refuse |
|
|
80
81
|
| `raf hook install` | add the post-commit hook (read-only, never blocks) and the pre-commit hook (does nothing unless `hook.autostart` says so, see below) |
|
|
81
82
|
|
|
82
83
|
### A commit on a lot that is still `todo`
|
|
@@ -267,9 +268,10 @@ plan:
|
|
|
267
268
|
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
|
|
268
269
|
`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.
|
|
269
270
|
- 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
|
|
270
|
-
`plan.onWrite en échec (<command>, code 7) : <
|
|
271
|
+
`plan.onWrite en échec (<command>, code 7) : <reason> — écriture refusée, plan inchangé`, where the reason is one line: the first `Error: …` line of the command's error output (a Node script that throws prints the file, the faulty line, then the reason, then the stack: the reason is what shows, e.g. `titre public … non conforme (93 caractères > 80)`, L429), else the first line of its error output, else of its standard output, 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).
|
|
271
272
|
`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.
|
|
272
273
|
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.
|
|
274
|
+
A command that writes a file **not listed** in `files` (an incomplete declaration, L436) does not escape the commit either: `raf` compares the files git sees as modified before and after the command, so the `git add` / `git commit` it prints and the pre-commit hook's staging include what the command really changed, and stderr says `raf: plan.onWrite a écrit <path>, absent de plan.onWrite.files — l'ajouter dans cadence.yaml`. A file the lead had already modified and the command did not touch is not cited; a failed command cites nothing (the write is refused). The list lives for the process that wrote the plan, so declare the file to have it cited everywhere (deliver, the wave).
|
|
273
275
|
- A read-only plan (kept by another tool) never runs the command: the project's tool owns the write.
|
|
274
276
|
|
|
275
277
|
Until the file is declared, such a commit is reported as a "commit without a
|
package/docs/releasing.md
CHANGED
|
@@ -22,7 +22,13 @@ A version exists in three places and is published in two; a release does all of
|
|
|
22
22
|
billed evaluation and before the tag. Red: fix it in a commit of its own and redo the version commit. A test that looks
|
|
23
23
|
for the entry of a lot in the CHANGELOG reads the section that carries it (`[Unreleased]` or the published version) through
|
|
24
24
|
`changelogSectionOf` (`test/changelog-section.ts`); `test/changelog-garde.test.ts` refuses a test that selects `## [Unreleased]`
|
|
25
|
-
of the repository's CHANGELOG itself.
|
|
25
|
+
of the repository's CHANGELOG itself. The gate runs in the lead's tree or in a copy of it, both full of files git does not track
|
|
26
|
+
(the `plugins/cadence-hud/.claude-plugin/types` that Claude Code generates, `node_modules`): the 1.25.0 publish run broke on three
|
|
27
|
+
`hud-*` tests that read those types, present in every local tree and absent from the CI checkout (L446). So the same commit is
|
|
28
|
+
also replayed **in a fresh `git clone`** — the next step the command prints: `git clone --no-hardlinks` of the repository
|
|
29
|
+
(only what is committed), `npm ci`, then `npm run typecheck` and `npm test` (those the package declares; a package with neither gets no
|
|
30
|
+
such step), the clone removed whatever the result. Red: no evaluation and no tag. `.github/workflows/tests.yml` does the same on every
|
|
31
|
+
push to `main`, from a fresh checkout.
|
|
26
32
|
3. Run the plugin evaluation, `npm run eval:plugin` (billed, see [Evaluating the plugin](#evaluating-the-plugin-before-a-release)),
|
|
27
33
|
on that version commit; a case below the threshold stops the release until you have read it in the report.
|
|
28
34
|
4. `git tag v<version> && git push origin main v<version>` — the tag starts `.github/workflows/publish.yml`,
|
|
@@ -37,7 +43,7 @@ A version exists in three places and is published in two; a release does all of
|
|
|
37
43
|
while an existing one is left alone. If the package is on npm but the release is still missing, re-run the job;
|
|
38
44
|
the by-hand fallback is `gh release create v<version> --title v<version> --notes-file <the section of CHANGELOG.md> --verify-tag`.
|
|
39
45
|
|
|
40
|
-
`.github/workflows/tests.yml` runs the type-check and the test suite on the same runner image and `npm ci` as the publish job, in a matrix of Node 22 and 24 (the minimum `engines.node` allows, and the Node the publish job uses), and never publishes:
|
|
46
|
+
`.github/workflows/tests.yml` runs the type-check and the test suite on the same runner image and `npm ci` as the publish job, in a matrix of Node 22 and 24 (the minimum `engines.node` allows, and the Node the publish job uses), and never publishes: it starts on every push to `main` (L446: each run is a fresh checkout, which no local tree is), with `gh workflow run tests.yml --ref <branch>` or by pushing a `diag/**` branch, to see on the runner what a red publish run saw.
|
|
41
47
|
|
|
42
48
|
The suite runs the same way on your machine as on that runner, **without `raf` or `cadence` installed** (L384): `test/global-setup.ts` replaces, in the `PATH` of the tests, every folder outside the repository that carries a `raf` or a `cadence` (`~/.local/bin`, the global npm folder, the plugin's `bin/`) by a mirror of that folder without those two names — the other tools stay, `node` included. A test that relies on the installed tool turns red on the lead's machine and in the gate, not only in the publish run: the 1.16.0 publication failed because the post-commit hook of `test/raf-init-commit.test.ts` (R2) found `raf` in the developer's `PATH` and not on the runner. A test that needs `raf` (a hook, a script) builds its own from the sources (`CADENCE_TEST_PACKAGE`).
|
|
43
49
|
|
|
@@ -79,7 +85,7 @@ introduction (the newest version first, as the file is read). Exit code 1 when a
|
|
|
79
85
|
version files are written, the commit is not ready. `--dry-run` reads the forms the same way (nothing is written) and exits 1 on the same findings, so the forms can be checked before the real run.
|
|
80
86
|
|
|
81
87
|
It prints the lead's steps, in order, and runs none: the `git add` of exactly the files of the version commit and the commit,
|
|
82
|
-
`cadence verify --gate --sha HEAD` (always printed, before the evaluation and the tag),
|
|
88
|
+
`cadence verify --gate --sha HEAD` (always printed, before the evaluation and the tag), the replay in a fresh clone (L446: `git clone --no-hardlinks`, `npm ci`, `npm run typecheck` and `npm test` for the scripts `package.json` declares, none printed when it declares neither),
|
|
83
89
|
`npm run eval:plugin -- --trust-plugin` (only when `package.json` has that script), `git tag v<x.y.z>`, `git push origin <branch> v<x.y.z>`,
|
|
84
90
|
`gh run watch`, `npm view <package> version` (not for a private package) and, only with a `.claude-plugin/plugin.json`,
|
|
85
91
|
the plugin update: `claude plugin marketplace update <marketplace>` first (without it the installed plugin stays at the old
|
package/docs/skills.md
CHANGED
|
@@ -59,6 +59,9 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
|
|
|
59
59
|
verdict for `raf review`. It takes the lot's commits from `raf commits`, never
|
|
60
60
|
runs a build whose output is used live, and never edits code. A README or usage
|
|
61
61
|
documentation that does not follow the lot's change is a *major* finding.
|
|
62
|
+
**Short reply (L440):** both reviewers answer in 5 lines at most (`Verdict:`, `Blocking:`, `Follow-ups:`, `Report:`) when the brief
|
|
63
|
+
carries a `Report file: <absolute path>` line, and write the full report to that file (outside the repository, verdict last);
|
|
64
|
+
the lead reads it on demand. With no such line (waves, hand use) the report is complete, as before.
|
|
62
65
|
- **doc-keeper** (agent, L306): any stack; given a repository and a lot id, it reads the diff of the lot and the
|
|
63
66
|
project's map of documents (`docs.sync` of `cadence.yaml`, README, `docs/`, CHANGELOG, news entries), says which
|
|
64
67
|
documents must change and where, then updates them in a writing session (explicit paths, `docs(<lot>): …`) or
|
|
@@ -95,3 +98,6 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
|
|
|
95
98
|
the route, what was expected, what was measured and the evidence; pages checked
|
|
96
99
|
N/N, follow-ups as `raf add` lines, what it could not verify, a one-line
|
|
97
100
|
verdict. Read-only: GET only, no login, nothing submitted; it stops at a PIN.
|
|
101
|
+
**Short reply (L440):** both reviewers answer in 5 lines at most (`Verdict:`, `Blocking:`, `Follow-ups:`, `Report:`) when the brief
|
|
102
|
+
carries a `Report file: <absolute path>` line, and write the full report to that file (outside the repository, verdict last);
|
|
103
|
+
the lead reads it on demand. With no such line (waves, hand use) the report is complete, as before.
|
package/docs/whats-new.md
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## In two lines
|
|
6
6
|
|
|
7
|
+
- **1.25.1**: publishes 1.25.0 (tagged, never published); tests now pass in a fresh clone and `cadence release` replays them in one; the panel of lots shows each project's last delivery.
|
|
8
|
+
- **1.25.0**: the panel of lots shows pending decisions, each lot's cycle and its budget and time overrun; `orchestrate --status` is compact; reviewers answer in 5 lines; `--resume --integrate` waits for a live wave.
|
|
9
|
+
- **1.24.0**: a push whose test gate is not green is refused; a wave counts the sessions the lead declared; a ready lot waits for its wave's integration; the `implement` brief gains « Limits and rules »; `plan.onWrite` errors read clearly.
|
|
7
10
|
- **1.23.0**: the panel of lots shows five lots per project, in progress first, and reorders a lot by dragging it inside its group; `cadence lead tour` always shows the free slots.
|
|
8
11
|
- **1.22.0**: the panel of lots folds a project with a click on its name; a project of more than 20 open lots starts folded.
|
|
9
12
|
- **1.21.1**: `/hud lots` opens on the desktop app whatever the width; a correction session that makes no commit must say why, or the lot is handed back.
|
|
@@ -49,6 +52,12 @@ Each is a paragraph in [docs/whats-new.md](whats-new.md) and a full section in [
|
|
|
49
52
|
|
|
50
53
|
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).
|
|
51
54
|
|
|
55
|
+
**1.25.1**: publishes 1.25.0 and closes the hole that kept it from reaching npm. The `v1.25.0` tag was pushed but its `publish` workflow failed in `test/hud-cycle`, `test/hud-deliveries` and `test/hud-depense-l414`: vitest read `plugins/cadence-hud/tsconfig.json`, which extends `.claude-plugin/types/tsconfig.json`, a file Claude Code writes when it loads the mod and git does not track, so no local tree could see it fail (L445); the tag is not rewritten, everything 1.25.0 brings (see below) ships in this version. The test suite now passes in a fresh `git clone` (the mod's own typecheck still extends that file). `cadence release` prints, right after the gate replay, a step that replays the version commit in a fresh `git clone --no-hardlinks` (`npm ci`, `npm run typecheck`, `npm test`, the clone removed afterwards), and `.github/workflows/tests.yml` also runs on every push to `main` (L446, `docs/releasing.md`). The panel of lots (`/hud lots`, `docs/cadence-hud.md`) shows, under the lots in progress, the last delivery of each project: its short sha, its age in days, green or red (read from the journal `cadence deliver` writes), and the lots closed since the last green delivery that were not delivered; `cadence lead tour --json` gains `closed`, the closed lots of a project with their closing day (L415).
|
|
56
|
+
|
|
57
|
+
**1.25.0**: the lead reads less and waits less. The panel of lots (`/hud lots`, `docs/cadence-hud.md`) opens with « ce qui attend une décision » — the questions of the sessions with their `--answer` command, the lots handed back with their cause, the UX verdicts to record and the failed deliveries, each with a command that runs as pasted (L412) — and draws under each lot a wave carries the cycle of the lot as a strip, from `fiche` to `prêt` then `revue posée → clos → livré`, the current stage in bold with its duration and passes (L413). For each lot in progress it shows the tokens consumed over the lot's budget and the elapsed time next to the usual time of a lot of that size, with a `⚠` on an overrun (budget exceeded, or more than 1.5 times the usual time); `cadence perf --json` gains `lotMinutes`, the median wall time per size class (L414). `cadence orchestrate --status` prints a compact table, one line per lot, and `--status [<wave>] --detail <project:lot>` prints the detail of one lot, which cost the lead 5 to 10 k tokens per read (L441); `code-reviewer` and `qa-reviewer` answer in 5 lines at most and write the full report to the file named by a `Report file: <path>` line of the brief (L440). A wave no longer leaves slots idle while it has nothing left to play (L433), `cadence orchestrate --resume --integrate|--replay` waits, for 3 hours at most, for a live wave that holds the repository instead of exiting 2 (L443), and `cadence verify --gate --sha <sha>` reads the gate the wave played when only plan commits sit above the checked commit, instead of replaying it for about 5 minutes (L444); the `pre-push` hook takes the same shortcut for a commit that changes only the plan (L432), and `cadence verify --gate` plays in a throwaway copy while a wave is live on the repository (L431). `raf done` now cites the files `plan.onWrite` really wrote, so the proposed `git add` no longer leaves one out (L436).
|
|
58
|
+
|
|
59
|
+
**1.24.0**: the push becomes a gate, and the wave counts its sessions better. `cadence verify --gate` keeps its verdict per sha (`.git/cadence/gate-verdicts.json`), and the `pre-push` hook refuses (exit 3) a push of a branch whose sha has no green verdict when the project declares a gate (L426, `docs/deliver.md`): the message says « aucun verdict » or « rouge » and gives `cadence verify --gate --sha <sha>`, and `CADENCE_PUSH_UNGATED=1` overrides it and says so; the guard outlives the wave, a gate-only `pre-push` hook being left behind when the wave frees the repository, so the delivery push after the last lot is still checked. The plan commit at the head of `main` has no verdict and gets a full replay with `cadence verify --gate --sha HEAD`. A wave counts the sessions the lead declared with `cadence lead hold` before taking a session slot (L424): with a cap of 3, one held session and two wave sessions are 3/3 and the next one waits. In worktree mode a ready lot queues behind the other lots of its own wave on the integration lock instead of being handed back after 10 minutes, the bound applying now only to a lock held by another process (L425), and `raf add`, `raf note` and `raf done` warn on stderr while an integration of the repository is in progress. The `implement` brief carries a block « Limits and rules, before the walkthrough » against the code bug found by the review: per rule the test that fails and the line that holds it, per changed code the edge inputs each tested, per figure or « all / never / always » a check against the code (L428, `docs/orchestrate.md`). `plan.onWrite en échec` shows the first `Error: …` line of the script instead of the tail of the Node stack (L429, `docs/raf.md`).
|
|
60
|
+
|
|
52
61
|
**1.23.0**: the panel of lots (`/hud lots`, `docs/cadence-hud.md`) becomes a place to steer from. In each project the lots in progress come first, then the free ones, then the blocked ones, plan order kept in each group (L410); at most five lots per project are shown, with a `+ N lots` / `moins` button for the rest, and a lot that has sub-lots folds and unfolds with a click (L411); `… +N lots de plus` counts every lot the height cuts, sub-lots included. A lot is reordered by dragging it onto another lot of its group, or with Alt+↑ / Alt+↓, through `raf move` (L402): dropping it on a lot of another group (a free lot on a lot in progress) is refused with its reason, a drop that changes nothing says so, and the panel never commits the plan. For the lead, `cadence lead tour` prints `créneaux libres : N sur M` even when nothing runs, and `cadence lead release` prints the free slots and the ready lots, with a ⚠ when a slot stays free (L416, `docs/lead.md`); cadence's own test gate also plays the tests and the type-check of the `cadence-hud` mod (L417); and `npm run service` also updates the local copy of the mod (L419).
|
|
53
62
|
|
|
54
63
|
**1.22.0**: the panel of lots no longer lets one big project hide the others (L409, cadence-hud 0.7.0, `docs/cadence-hud.md`): each project folds and unfolds with a click on its name (Enter under the focus, or `/hud lots <project>`), a project of more than 20 open lots starts folded on a single line (name, open lots, in progress), and the folded state is kept between two openings.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylad/cadence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.1",
|
|
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",
|
package/skills/deliver/SKILL.md
CHANGED
|
@@ -59,6 +59,14 @@ The push targets the branch being delivered (`main` here). Two cases where it do
|
|
|
59
59
|
has moved past it): **never force it**, `--force` would rewind the remote branch. The sha cannot be delivered
|
|
60
60
|
that way: stop and ask the human.
|
|
61
61
|
|
|
62
|
+
**The sha pushed needs a green verdict of the gate (L426).** Where the project declares a gate (`orchestrate.test` or
|
|
63
|
+
`orchestrate.build`), the `pre-push` hook a wave installs, and the gate-only hook it leaves behind, refuse with exit 3 a push
|
|
64
|
+
of a branch whose local sha has no green verdict of `cadence verify --gate`; the message names the sha and the command. The
|
|
65
|
+
lot's sha the lead re-checked has one, and a commit above it that changes only the plan takes it (L432); a release commit or a plan commit that also touches another file has none, and the wave's record does not cover it:
|
|
66
|
+
run `cadence verify --gate --sha <sha>` on the very sha you push (a full replay for such a commit, ~5 minutes) before the
|
|
67
|
+
push, HEAD included when you push `main`. `CADENCE_PUSH_UNGATED=1 git push …` lets a push through with no verdict and says so
|
|
68
|
+
on stderr: only when the human asks for it, never the routine.
|
|
69
|
+
|
|
62
70
|
## Where to run
|
|
63
71
|
|
|
64
72
|
Every `cadence` / `raf` command works on the git repository of the current directory. When the
|
package/skills/lead/SKILL.md
CHANGED
|
@@ -30,7 +30,7 @@ commands (its CLAUDE.md names them), never with `raf start|done|note|ux|review`.
|
|
|
30
30
|
nothing stops the session: `lead tour` and `orchestrate --status` print `⚠ session du lead échue : <label> (…)` until you
|
|
31
31
|
release it, and the band shows the same ⚠ row. **At each ⚠, decide at once**: either it is still worth the slot, so renew it
|
|
32
32
|
with `cadence lead hold <label> --ttl <minutes>` and say why in one sentence, or stop it (`TaskStop` or the agent's end) and
|
|
33
|
-
`cadence lead release <label>`. A fix that ran 5 h on a 3 h slot (23-10) is the case this catches.
|
|
33
|
+
`cadence lead release <label>`. A fix that ran 5 h on a 3 h slot (23-10) is the case this catches. A hold blocks no session of the lead, but orchestrated waves wait for it (they take no slot while held and running sessions reach the cap); it never raises the cap.
|
|
34
34
|
Never raise `--max-sessions` or `CADENCE_MAX_SESSIONS` yourself to speed a wave up: raising the cap is a decision of
|
|
35
35
|
the human (a trial at 3 was decided on 2026-10-12); if the slots are the bottleneck, say so and let them decide.
|
|
36
36
|
- **Never two subagents in the same repository at the same time**, and the lead does not commit in a
|
|
@@ -190,6 +190,11 @@ is always recorded by the lead's own `raf ux`, never by a subagent.
|
|
|
190
190
|
the absolute path of the project and the lot id, **not** the author's report: it reads the diff
|
|
191
191
|
itself from the commits that cite the lot, runs the checks, and returns real defects only,
|
|
192
192
|
ranked, with what it could not verify and a one-line verdict.
|
|
193
|
+
Add a line `Report file: <absolute path>` in a temporary folder outside the repository (one file per
|
|
194
|
+
lot and pass): the agent writes its full report there and answers you in 5 lines (verdict, blocking
|
|
195
|
+
findings, follow-ups, the path). Read the file only when you need it: to act on a blocking finding,
|
|
196
|
+
to hand a finding to a fix, or to bring the verdict line up to date for `raf review`. A short reply with `Blocking: 0`
|
|
197
|
+
needs no read.
|
|
193
198
|
2. **The lead re-verifies itself**: `git log` shows the commits, the test suite and build pass when
|
|
194
199
|
run by the lead, `raf check` is clean. During a wave, or in any tree that is not the lead's own, replay the gate at the lot's last sha with
|
|
195
200
|
`cadence verify --gate --sha <sha> [--project <dir>]` (exit 0 green, 1 red with the failing tests, 2 nothing to replay): a bare
|
|
@@ -215,6 +220,22 @@ is always recorded by the lead's own `raf ux`, never by a subagent.
|
|
|
215
220
|
One project at a time, by the lead: push, then the `deliver` skill (`cadence deliver --dry-run`, then
|
|
216
221
|
`cadence deliver`). Follow the human's standing instructions about confirmation before production.
|
|
217
222
|
|
|
223
|
+
**The sha pushed needs a green verdict of the gate (L426).** Where the project declares a gate (`orchestrate.test` or
|
|
224
|
+
`orchestrate.build`), the `pre-push` hook (the wave's, or the gate-only hook it leaves behind once the repository is freed)
|
|
225
|
+
refuses with exit 3 a push of a branch whose local sha has no green verdict of `cadence verify --gate`, naming the sha and
|
|
226
|
+
the command to run. The verdict of the re-check (section 3, point 2) belongs to the lot's sha, and the head of `main` is
|
|
227
|
+
almost never that sha: `raf done` does not commit, so the plan commit that follows it (« plan: Lx terminé »), a release
|
|
228
|
+
commit or the plan commit of the next lot sits above. A commit that changes only the plan file (L432) takes the verdict of
|
|
229
|
+
its parent, stacked ones included: the hook lets it through, no replay. A commit that also touches another file (a release
|
|
230
|
+
commit, a plan commit with code) has no verdict, and `verify --gate --sha <head>` cannot reuse the wave's record (made at the
|
|
231
|
+
lot's sha, `checks.head`): it replays the whole gate, ~5 minutes. Count that replay in: after such a commit,
|
|
232
|
+
`cadence verify --gate --sha HEAD`, then push `main`. Delivering while the wave runs
|
|
233
|
+
(section 2b) pushes the lot's sha alone (`git push origin <sha>:main`): its verdict is the one the re-check recorded, and the
|
|
234
|
+
plan commit is pushed later, after its own replay. `CADENCE_PUSH_UNGATED=1 git push …` lets a push through with no verdict
|
|
235
|
+
and says so on stderr: it is for the push the human explicitly asks ungated (a plan-only or docs-only commit above a verified
|
|
236
|
+
sha, when the human says so), never the routine: the incident the hook answers (29-10, a project pushed with a red gate whose
|
|
237
|
+
exit code a `| tail -1` had hidden) was exactly a push nobody had checked.
|
|
238
|
+
|
|
218
239
|
When the wave's final table gives a line « livrable jusqu'à <sha> » for the repository (a ready lot stacked under a
|
|
219
240
|
handed-back or failed one), HEAD holds the code of the lot above, which was not reviewed: never push or deliver HEAD.
|
|
220
241
|
Push that sha alone first (`git push origin <sha>:main`), then deliver it (`cadence deliver --dry-run --sha <sha>`, then
|
|
@@ -234,6 +255,10 @@ without an expectations file gets a draft back: show it to the human, who correc
|
|
|
234
255
|
whether it is committed. It is not a gate: `raf done` does not wait for it, and a finding becomes a
|
|
235
256
|
new lot, not a reopened one.
|
|
236
257
|
|
|
258
|
+
Give the `qa-reviewer` the same line `Report file: <absolute path>` (outside the repository): it answers in 5 lines
|
|
259
|
+
(verdict, blocking findings, follow-ups, the path) and keeps the full report, with its measurements, in that file; read the
|
|
260
|
+
file only to show the human a blocking finding or to commit the draft of an expectations file.
|
|
261
|
+
|
|
237
262
|
## 5. Close
|
|
238
263
|
|
|
239
264
|
At the end, the `session-close` routine in each project touched, and `cadence session next` lines in
|
|
@@ -19,9 +19,14 @@ Self-check before handing back (the recurring findings of the reviews). Go throu
|
|
|
19
19
|
- no false comment: a comment, a docstring or a message never describes a behaviour the code no longer has or does not have.
|
|
20
20
|
A point you cannot hold is written under "choix" or "nonVerifie" of your report, not left unsaid.
|
|
21
21
|
|
|
22
|
+
Limits and rules, before the walkthrough (a code bug found by the review is the first cause of correction passes: 100 of the 143 classified passes of the 23–29-10 window). Re-read your diff against the fiche (if there is one) and the lot, then:
|
|
23
|
+
- rules: for each rule of the fiche (`Rn`), or of the lot when it has no fiche, name the test that fails if the code breaks it and the line of code that holds it; a rule with no such pair is not done;
|
|
24
|
+
- edge cases: for every function, parser, pattern, threshold or command you add or change, list the inputs at its edge — empty, absent, twice, wrong order, wrong type, refused, the real shape of the data (not the one of your fixture), the other callers of what you changed — and give each one a test that fails without your code, before handing back, except a case the fiche does not cover: with a fiche, you do not code a rule it does not state, so that case is written under "nonVerifie" or "choix", neither coded nor tested; a case you listed and did not test goes under "nonVerifie";
|
|
25
|
+
- claims: each figure, « all », « never » or « always » that your README, docs, comments or tests state is checked against the code or by running it, not from memory.
|
|
26
|
+
|
|
22
27
|
Real walkthrough once the tests are green, before the auto-verification below: play what you delivered as a user would, not by re-reading the diff. A command: run it in a throwaway repository (`git init` in a fresh folder of the session's temporary folder, never inside the repository), following step by step the documentation the lot wrote (README, `docs/`), every command exactly as written there; a command the documentation cannot run, or whose documented path is impossible from the state the previous steps leave, is broken. A screen: open it in each of its states, failure included, the failure shown where the action takes place (in view from the control the user just used, not above or below the fold), and take a capture of each. What breaks, fix in this same session (test first, a `fix({{lot}}): …` commit) and play it again. Then write in "resume" a paragraph that starts with « parcours réel »: what you played, what broke and was fixed, what you could not play (also under "nonVerifie"); a lot with nothing to play (no command, no screen) says so in that paragraph.
|
|
23
28
|
|
|
24
|
-
Auto-verification once the tests are green, before the review: when the full test suite and the build pass, re-read the complete diff of the lot, commit by commit (the commits `raf commits {{lot}}` lists, each with `git show <sha>`; never a `git diff` range, which would take in the commits of other lots) against the grid of the code-reviewer agent: code bug (wrong result, unhandled error, input trusted); architecture drift (a responsibility put in the wrong module, a duplicated block, a layer skipped); docs/architecture.md (or the project's architecture documentation) up to date; edge cases (
|
|
29
|
+
Auto-verification once the tests are green, before the review: when the full test suite and the build pass, re-read the complete diff of the lot, commit by commit (the commits `raf commits {{lot}}` lists, each with `git show <sha>`; never a `git diff` range, which would take in the commits of other lots) against the grid of the code-reviewer agent: code bug (wrong result, unhandled error, input trusted); architecture drift (a responsibility put in the wrong module, a duplicated block, a layer skipped); docs/architecture.md (or the project's architecture documentation) up to date; edge cases (the list of the edge-cases point above, each with its test); one test per changed behaviour. What you find, fix it at once (test first, a `fix({{lot}}): …` commit) and rerun the suite and the build. Then write in "resume" a paragraph that starts with « auto-vérification »: what you re-read, what you found and fixed, what you could not hold. It is one reading of the diff, not a second review: no subagent, no new exploration, no re-reading of files the diff does not touch.
|
|
25
30
|
|
|
26
31
|
Decide minor interpretation questions yourself (the wording of a message, a name, a default, the
|
|
27
32
|
reading of an ambiguous line of the lot) and list each one under "choix" in your report, with the
|