@sylad/cadence 1.24.0 → 1.26.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.
@@ -1,5 +1,5 @@
1
1
  import { verdictFor } from './gate-verdict.js';
2
- import { tryGit } from './git.js';
2
+ import { changedFiles, tryGit } from './git.js';
3
3
  import { blockingLots, LABEL } from './orchestrate/table.js';
4
4
  const ZERO = /^0+$/;
5
5
  /** Combien de commits de la plage on essaie, du plus récent au plus ancien, pour trouver le dernier livrable. */
@@ -43,13 +43,35 @@ export function pushRefusal(root, wave, lots, input, remoteName = 'origin') {
43
43
  }
44
44
  /** La variable qui passe outre le verdict de la porte au push (L426) ; la garde des lots de la vague (`pushRefusal`) n'en dépend pas. */
45
45
  export const UNGATED_ENV = 'CADENCE_PUSH_UNGATED';
46
+ /** Combien de commits de plan empilés on remonte au plus ; au-delà, le sha n'a pas de verdict (la porte se rejoue). */
47
+ const PLAN_HOPS = 50;
48
+ /**
49
+ * Le verdict de la porte pour `sha`, ou, quand `sha` ne change que le plan (`planFile`) et n'a qu'un parent, celui de son parent, de proche en proche
50
+ * (L432 : un commit de `raf done` au-dessus d'un sha à porte verte ne réclame pas une suite complète rejouée). Son propre verdict, s'il en a un, prime.
51
+ * Une fusion (deux parents), un commit racine, un commit qui touche autre chose que le plan ou un commit sans fichier n'héritent de rien.
52
+ */
53
+ function planVerdict(root, sha, planFile) {
54
+ let at = sha;
55
+ for (let hop = 0; hop <= PLAN_HOPS; hop++) {
56
+ const verdict = verdictFor(root, at);
57
+ if (verdict)
58
+ return verdict;
59
+ const parents = (tryGit(root, ['rev-list', '--parents', '-n1', at]) ?? '').trim().split(/\s+/).slice(1);
60
+ const files = parents.length === 1 ? changedFiles(root, at) : [];
61
+ if (files.length !== 1 || files[0] !== planFile)
62
+ return null;
63
+ at = parents[0];
64
+ }
65
+ return null;
66
+ }
46
67
  /**
47
68
  * Le refus d'un push dont le sha de tête n'a pas de verdict vert de `cadence verify --gate` (L426) : pour chaque ligne de l'entrée standard du hook pre-push,
48
69
  * le sha local d'une branche (`refs/heads/`) doit avoir son dernier verdict à 0 dans l'état partagé du dépôt. Absent ou rouge, le refus nomme le sha, dit lequel des deux
49
70
  * et donne la commande qui rend le verdict (`cadence verify --gate --sha <sha>`) ; il rappelle la variable qui passe outre. Une suppression de branche, un sha illisible
50
71
  * et une réf hors `refs/heads/` (un tag : son sha est celui de l'objet tag, pas d'un commit à vérifier) passent. Sans porte déclarée (`gate` faux) rien n'est exigé.
72
+ * Un commit qui ne change que le plan (`planFile`, relatif à la racine du dépôt) reprend le verdict de son parent (L432, `planVerdict`).
51
73
  */
52
- export function gateRefusal(root, gate, input) {
74
+ export function gateRefusal(root, gate, input, planFile) {
53
75
  if (!gate)
54
76
  return null;
55
77
  for (const entry of input.split('\n')) {
@@ -58,7 +80,7 @@ export function gateRefusal(root, gate, input) {
58
80
  continue;
59
81
  if (!(remoteRef || ref).startsWith('refs/heads/'))
60
82
  continue;
61
- const verdict = verdictFor(root, local);
83
+ const verdict = planVerdict(root, local, planFile);
62
84
  if (verdict?.code === 0)
63
85
  continue;
64
86
  const short = local.slice(0, 7);
package/dist/release.js CHANGED
@@ -164,6 +164,7 @@ function plan(root, bump, today) {
164
164
  packageName: String(pkg.name ?? ''),
165
165
  isPrivate: pkg.private === true,
166
166
  hasEvalScript: typeof pkg.scripts?.['eval:plugin'] === 'string',
167
+ cloneScripts: ['typecheck', 'test'].filter((n) => typeof pkg.scripts?.[n] === 'string'),
167
168
  };
168
169
  }
169
170
  /**
@@ -194,12 +195,22 @@ export function whatsNewIssues(text, version) {
194
195
  check('paragraphe long', 'manquant', 'mal placé', /^\*\*(\d+\.\d+\.\d+)\*\*:/, split, lines.length, 'juste après l’introduction de « Version by version »');
195
196
  return issues;
196
197
  }
198
+ /**
199
+ * Le passage en clone neuf (L446) : la porte voit l'arbre du lead, avec ses fichiers non suivis (la 1.25.0 a cassé à la publication sur des
200
+ * types que Claude Code génère dans plugins/cadence-hud et que seul un clone ne porte pas). `git clone` du dépôt courant ne prend que ce qui est commité,
201
+ * `--no-hardlinks` pour ne rien partager avec lui ; le clone est retiré, vert ou rouge.
202
+ */
203
+ function cloneStep(scripts) {
204
+ const run = scripts.map((n) => (n === 'test' ? 'npm test' : `npm run ${n}`)).join(' && ');
205
+ return `d=$(mktemp -d) && git clone -q --no-hardlinks . "$d/c" && (cd "$d/c" && npm ci && ${run}); r=$?; rm -rf "$d"; test $r -eq 0 (la suite dans un git clone neuf, comme publish.yml ; rouge = pas de tag)`;
206
+ }
197
207
  /** Les étapes que le lead joue, dans l'ordre : la commande n'en fait aucune. */
198
208
  function steps(p, files, branch) {
199
209
  const tag = `v${p.version}`;
200
210
  const out = [
201
211
  `git add ${files.join(' ')} && git commit -m "release: ${p.version}"`,
202
212
  'cadence verify --gate --sha HEAD (la suite et le build rejoués sur le commit de version, où le CHANGELOG n\'a plus de [Unreleased] plein ; rouge = pas d\'éval ni de tag)',
213
+ ...(p.cloneScripts.length ? [cloneStep(p.cloneScripts)] : []),
203
214
  ...(p.hasEvalScript ? ['npm run eval:plugin -- --trust-plugin (facturé ; un cas sous le seuil arrête la release)'] : []),
204
215
  `git tag ${tag}`,
205
216
  `git push origin ${branch} ${tag} (le tag lance le workflow de publication)`,
@@ -5,6 +5,8 @@ import { join } from 'node:path';
5
5
  import { recordedGate } from './gate-record.js';
6
6
  import { recordVerdict } from './gate-verdict.js';
7
7
  import { onTermination } from './proc.js';
8
+ import { sharedStateDir } from './state.js';
9
+ import { activeLock, REPO_LOCK } from './orchestrate/lock.js';
8
10
  import { failedTests, hasUnnamedFailure, unloadedSuites } from './orchestrate/baseline.js';
9
11
  import { configuredCap } from './orchestrate/command.js';
10
12
  import { replayRed, TEST_WORKERS_ENV, testWorkersFrom } from './orchestrate/workers.js';
@@ -86,7 +88,7 @@ function mainTreeMoved(repo, sha) {
86
88
  /**
87
89
  * `cadence verify --gate` (L348) : la porte du projet (`orchestrate.build`, puis `orchestrate.test`, dans l'ordre de la vague : les tests lisent ce que le build produit)
88
90
  * rejouée au `sha` (L389) dans l'arbre principal quand il est propre et à ce sha (les outils qui refusent un `node_modules` venu d'un autre arbre y tournent chez eux) ;
89
- * sinon dans un worktree jetable, hors du dépôt, dont les dépendances non suivies sont copiées depuis le dépôt vivant (`orchestrate.worktreeDeps`,
91
+ * sinon (arbre non propre ou à un autre commit, ou vague d'orchestration vivante sur le dépôt, L431) dans un worktree jetable, hors du dépôt, dont les dépendances non suivies sont copiées depuis le dépôt vivant (`orchestrate.worktreeDeps`,
90
92
  * sinon les `node_modules` de tout niveau et le `.venv` ; les autres dossiers ignorés sont reliés, comme dans les worktrees de vague ; la copie est asynchrone pour que Ctrl-C la coupe). `verify.inPlace` impose l'un (`true`, refus si l'arbre n'est pas propre au sha) ou l'autre (`false`).
91
93
  * La première ligne de la sortie dit où la porte a tourné ; en place, HEAD et `git status` sont relus après les étapes et, si l'arbre a bougé (HEAD avancé, status sale), la sortie dit ce qui a changé, que le verdict n'est pas celui du sha et sort en code 2, rouge ou vert ; la ligne « vert » / « rouge » n'est alors pas écrite. Les deux commandes sont rejouées même si la première est rouge : le compte rendu est entier.
92
94
  * Chaque commande reçoit `CADENCE_TEST_WORKERS` comme dans la porte d'une vague (L350, L387) : la valeur de l'environnement de l'appelant si c'est un entier positif, sinon cœurs ÷ plafond de sessions (`CADENCE_MAX_SESSIONS`, 2 par défaut) ;
@@ -125,14 +127,15 @@ async function playGate(ctx) {
125
127
  }
126
128
  let recorded = { reason: 'aucune porte de vague enregistrée pour ce sha' };
127
129
  try {
128
- recorded = recordedGate(ctx.launchDirs ?? [], repo, sha, steps);
130
+ recorded = recordedGate(ctx.launchDirs ?? [], repo, sha, steps, ctx.planFile);
129
131
  }
130
132
  catch (e) {
131
133
  recorded = { reason: `état des vagues illisible (${gitFailure(e)})` };
132
134
  }
133
135
  if ('found' in recorded) {
134
136
  const f = recorded.found;
135
- out(`verify --gate ${short} : verdict repris de la porte de la vague ${f.wave} (lot ${f.lot} : contrôles du programme au même sha, arbre ${f.tree.slice(0, 7)} identique) — rien rejoué`);
137
+ const where = f.checked === sha ? 'au même sha' : `au sha ${f.checked.slice(0, 7)}, sous les seuls commits de plan de ${short}`;
138
+ out(`verify --gate ${short} : verdict repris de la porte de la vague ${f.wave} (lot ${f.lot} : contrôles du programme ${where}, arbre ${f.tree.slice(0, 7)} identique) — rien rejoué`);
136
139
  for (const step of steps) {
137
140
  const run = f.runs.find((r) => r.label === (step.name === 'build' ? 'build' : 'tests') && r.command === step.cmd);
138
141
  out(`✓ ${step.name} (${step.cmd}) — enregistré par la vague${run.instables?.length ? `, ${run.instables.length} test(s) instable(s) sous charge, verts rejoués seuls` : ''}`);
@@ -147,7 +150,15 @@ async function playGate(ctx) {
147
150
  out(`verify --gate : verify.inPlace demande l'arbre principal, mais ${obstacle} — rien n'est joué`);
148
151
  return 2;
149
152
  }
150
- const inPlace = mode === true || (mode === undefined && obstacle === null);
153
+ // Une vague vivante intègre ses lots (HEAD bouge) et commite son plan : l'arbre principal n'est pas un lieu stable, la porte joue dans une copie (L431) ; `verify.inPlace: true` reste obéi.
154
+ let liveWave = null;
155
+ try {
156
+ liveWave = activeLock(join(sharedStateDir(repo), REPO_LOCK))?.wave ?? null;
157
+ }
158
+ catch {
159
+ // verrou illisible : comme s'il n'y avait pas de vague
160
+ }
161
+ const inPlace = mode === true || (mode === undefined && obstacle === null && liveWave === null);
151
162
  if (inPlace) {
152
163
  out(`verify --gate ${short} : porte jouée dans l'arbre principal ${repo} (propre, au sha)`);
153
164
  out(replayReason);
@@ -211,7 +222,7 @@ async function playGate(ctx) {
211
222
  copied = await copyDeps(repo, dir, ignoredDeps(repo).filter(isCopiedDep));
212
223
  linkIgnored(repo, dir);
213
224
  }
214
- const why = mode === false ? 'verify.inPlace: false' : obstacle;
225
+ const why = mode === false ? 'verify.inPlace: false' : (obstacle ?? `la vague ${liveWave} est vivante sur ce dépôt : HEAD et le plan y bougent`);
215
226
  out(`verify --gate ${short} : porte jouée dans une copie jetable ${dir} (${why}) ; dépendances copiées : ${copied.length ? copied.join(', ') : 'aucune'}`);
216
227
  out(replayReason);
217
228
  if (missing.length)
@@ -85,6 +85,14 @@ States: `▶ en cours` (doing), `○ libre` (todo, no open `after:`), `⊘ bloqu
85
85
 
86
86
  Before the first reading the panel says `Lecture des plans…`; if `cadence lead tour` does not answer it says `Lots illisibles : cadence lead tour n'a pas répondu. Nouvelle lecture dans une minute.` (a failed re-reading keeps the last table); a `cadence` too old to give the `lots` field says `Lots indisponibles : cadence est trop ancien, mettez-le à jour.`; with no open lot, `Aucun lot non clos dans les projets de ce dossier.`. A tree taller than the panel ends with `… +N lots de plus`. Under 60 columns the `after:` list moves to its own line and the title is cut with `…`, never the id or the state.
87
87
 
88
+ **The cycle of a lot as a strip (L413).** Under a lot, one line draws where it stands in its cycle, read from the wave state (`.cadence/runs/<wave>/<project>--<lot>.json`, whose steps the collector returns as `trail`: kind, status, start, end) and from the plan (`reviewed: true` in the lot's tour row when the plan holds its code review). The wave's stages, in order: `fiche` (the `spec` and `fiche` sessions), `maquette`, `accept` — these three only when the lot played them — then `implement`, `revue` (`review`, `review-small`), `correction` (`fix`), `porte` (the program's build or tests gate, which has no session), `UX`, `intégration`, `prêt`; the control `precheck` is not a stage. Each shows a glyph (`✔` done, `▶` current in bold, `○` to come, `✖` failed), its name, its passes — `correction n/3` (the lot's `pass`), `revue ×2` when the review ran more than once, the gate's `build` / `tests` — and its duration (the sum of its sessions, up to now for the one running; none when its dates are unreadable or the stage was not played). The current stage is the running session; else the gate while it runs; else, between two sessions, the lot's `next`; a lot `failed` or `handed-back` shows in `✖` the stage of its `next` (a lot handed back at integration: `intégration`), else its last stage played, a lot in `question` keeps it current. A stage never played that a later one has passed counts done (`porte`, a `correction 0/3`). Then the plan's: `revue posée → clos → livré` — the review is done when the plan holds it; the first undone one is current once the wave is `ready`. A lot with no wave gets the plan's strip only when it is `doing` **and** its review is posed (before, nothing happened that a strip could show); a `todo` lot, a sub-task and a lot of another project's wave get none. The strip takes one more row (it counts in the panel's height, in the `… +N lots de plus` cut and in the pointer region's rows) and is as wide as the body minus the id's indent; too long, it folds the finished stages into `✔ n`, then replaces the arrows with spaces, then cuts its end with `…`. Computed by `hooks/cycle.ts` (`cycleOf`, `cycleSegments`), drawn by `hooks/lot-row.tsx`; tested by `tests/cycle.test.ts` (L413 R1 to R15), `tests/lots-pane.test.ts` (R16, R17), `test/hud-cycle.test.ts` (R13: the real collector) and `test/lead-tour-lots.test.ts` (R12: `reviewed`).
89
+
90
+ **Lots in progress: budget and duration (L414).** Under the decisions block, `Lots en cours : budget et durée (N)` shows, for each lot being worked in a **live** wave (`implementing`, `reviewing`, `fixing`, `question`), one line `▶ project:lot 100k/750k tokens · 10 min (habituel 30 min)`: the tokens counted by the lot's steps over its budget, the time elapsed since its first step started, up to now — a program gate (build, tests) runs with no session while the lot stays in progress, and the usual time counts those gates too —, and the usual time of a lot of the same size. A lot that goes over comes first, in the warning colour, prefixed `⚠`: `⚠ cadence:L64 786k/750k tokens dépassé · 53 min (habituel 30 min, dépassé)`. **Budget over**: tokens above the lot's `budget` minus the share reserved for model escalation (`escalationReserve`) while the escalation is not engaged (ordinary passes do not spend it; once engaged it is released and counts); a lot state without `budget` shows the tokens alone and is never over. **Time over**: elapsed time above 1.5 times the usual time. The usual time is `lotMinutes` of `cadence perf --json` (`docs/perf.md`): the median wall time of the rendered lots of the same size class (the estimate, `0.5+spec` for a lot that went through the spec chain: the keys of `tokens.byEstimate`), the current window, else the previous one, the first with at least 5 rendered lots (`MIN_SAMPLE`, the same bound under which `cadence perf` refuses a trend): a window with fewer lots, even non-empty, gives no usual time. The mod reads it (`cadence perf <folder> --json`) with each re-read of the board **while a wave is live** (none without one, and no block), and keeps the last successful read when `cadence perf` fails or is missing. With no usual time for the lot's class (or no estimate in its state) the line shows the duration alone and never flags the time. The block is capped at a quarter of the body, decisions and lots in progress together at half of it (the tree keeps the rest); past that, the lots over first, then `… +N lots en cours`. **A lot that is over never loses its signal**: as soon as one lot is over, the block keeps at least two lines, the title and the first lot over, taken from the tree, even in a body of 7 lines or less or when the decisions fill their half (the finished waves of the last 7 days can). With no lot over, a block that has no room for two lines is not drawn and the tree keeps the body. Details in the plugin's README.
91
+
92
+ **Last delivery of each project (L415).** Under the lots in progress, `Dernière livraison par projet (N)` shows one line per project that has delivered: `✔ finance 3f9a1c2 · il y a 2 j · verte · 2 lots clos non livrés : L12, L13`, or `✖ … · rouge` in red when the last delivery failed; `rien à livrer` when every closed lot was delivered, and nothing after `verte` when the plan is not known. It reads the journal `cadence deliver` writes (`.git/cadence/deliveries.log`, one `day sha` line per green delivery, and `deliveries-failed.log`) through the `LAST_DELIVERIES` script (python3, the folder and each direct sub-folder whose `.git` is a folder, like the failed-delivery script; run at each 5 s refresh **while the panel is open**): the last delivery is the red one when `deliveries-failed.log` was modified after `deliveries.log` (the files' modification date decides, as for the failed deliveries), the green one otherwise; a repository with no readable line is absent. The age counts days from the journal's day (the journal has no hour): `aujourd’hui`, `hier`, `il y a N j`. **Closed lots not delivered**: `cadence lead tour --json` gives each project row `closed`, the closed lots (`done`, archived ones included, lots with an `every:` left out) as `{ id, finished }`; a lot is delivered when a green delivery happened on the day it was closed or after (the rule of the delivery delay in `cadence perf`), so it is not delivered when closed **after the day of the last green delivery** — a red delivery delivers nothing, and a project that never delivered green counts all its closed lots. A project absent from the tour, in error, or read by a `cadence` too old to give `closed` shows no count (not « rien à livrer »: we do not know). Order: red first, then the most undelivered lots, then the name. Height (`deliveriesHeight`): the title and one line per project, within what the decisions and the lots in progress leave of the half of the body, `… +N projets` when they do not all fit, nothing under two lines; the tree keeps the rest. Calculation: `hooks/deliveries.ts` (`deliveriesOf`, `buildDeliveries`, `parseLastDeliveries`). Tested by `tests/deliveries.test.ts` (L415 R4 to R11), `tests/lots-pane.test.ts` (R12 to R14, panel mounted), `test/hud-livraisons-l415.test.ts` (R2, R3: the real script on real journals) and `test/lead-tour-lots.test.ts` (R1: the `closed` field).
93
+
94
+ **What is waiting for a decision (L412).** Above the tree, a block `Ce qui attend une décision (N)` gathers what the person has to settle; it exists only when there is something to settle. It reads the band's waves (live ones, and the last finished one only when none is live), the finished waves of the folder that the collector returns apart in `ended` (whether a wave is live or not, without the band's 30-minute delay: **all** the finished waves of the last 7 days, with no cap on their number (a cost guard of 500 waves, far above the 162 waves per 7 days measured, is not a rule), newest first by the date of their `wave.json` — a question or a handed-back lot waits as long as the plan keeps the lot open and its wave is less than 7 days old (beyond that, the decision leaves the block even if the lot is still open in the plan), a newer wave finishing without that lot does not erase it; a live wave is not among them: its `wave.json`, always the most recent of the folder since orchestrate rewrites `consumed` in it, is skipped by the collector; the band and the tree keep reading `waves` only, their drawing does not change) and the failed deliveries; a lot counts once, in the first wave carrying it, live waves first then the finished ones newest first (taken over in a newer wave, that wave decides). Four kinds: `? project:lot <questions>` (a lot in the `question` state; the short text is the first line of each question, joined by ` / `) with, below it in grey, the command that answers it (`cd <launch folder of the wave> && cadence orchestrate --resume --wave <wave> --answer project:lot "…"`, the one the end-of-wave table prints, preceded by a `cd` to the wave's `cwd`: orchestrate finds the wave only from there) — while the wave is live, no command but `réponse possible à la fin de la vague`, since orchestrate refuses to resume a wave that is still running; `↩ project:lot <cause>` (a `handed-back` or `failed` lot: the first line of its outcome, the status in words when empty; a `suspended` lot waits for nothing from the person), in red; `◐ project:lot verdict UX : <verdict>` (a `ready`, visible lot whose review returned a verdict) with `cd <repository of the lot> && raf ux <lot> "verdict"` below (the `repo` of the lot state: from the parent folder raf answers « pas de plan », from another project it would record the verdict on that project's lot of the same id) — the commands to copy start with `cd` and run as they are from any folder, a path with a space or a quote is quoted for sh; `✖ project livraison : <short sha> · <reason> (<day>)` in red (7 characters: deliver writes the full 40-character sha in the log). The block never takes more than half of the body (3 lines at least): past that, the first decisions that fit, then `… +N décisions de plus`. Failed deliveries come from the `DELIVERIES` script (collect.ts, python3), run at every 5 s refresh **while the panel is open**: for the folder and each direct sub-folder whose `.git` is a directory, the last line of `.git/cadence/deliveries-failed.log`, unless `deliveries.log` was modified after it (a green delivery clears the failure; file modification times decide, the day on the line cannot order two writes of the same day). The plan decides: the panel's board (read from `cadence lead tour --json`) drops any decision whose lot is no longer open in a project the tour read, so a UX verdict recorded then `raf done`, or a handed-back lot taken over and closed, disappears at the next plan read; a project missing from the board, in error or without lots keeps its decisions. The command to copy is wrapped over several lines at the panel's width, never cut, every line but the last ending with the shell continuation ` \` (`\` glued to the piece when a word longer than the line is cut): pasted as they are into a shell, the lines run one command, the whole one (without it, a first line `cadence orchestrate --resume --wave <wave>` ran alone, without its `--answer`); the wrapped lines count in the block's ceiling. Details in the plugin's README.
95
+
88
96
  The data is the `lots` field of `cadence lead tour --json` ([lead tour](lead.md#lead-tour)), read together with the band's `progress` (same call, same folder). While the panel is open, the modification date of each plan (`.cadence/raf.yaml` and `cadence.yaml` of the folder and of its direct sub-folders) is read every 5 s, one `stat` each: a changed plan, like a wave transition, triggers a new reading before the minute is over. Cost of the `lots` field (R18, measured on 2026-10-28 with `cadence lead tour --json` on the parent folder of ten projects, three passes each): before L401 (cadence 1.19.1, no `lots`) 8.0 / 9.1 / 7.3 s and 86 KB of output; after (the L401 code) 7.5 / 7.0 / 8.4 s and 188 KB (192,503 bytes). The output more than doubles in size but the duration stays in the same order, under the 20 s refresh delay of the band, so the rule holds.
89
97
 
90
98
  The source lives in [`plugins/cadence-hud`](../plugins/cadence-hud/README.md)
package/docs/deliver.md CHANGED
@@ -295,7 +295,7 @@ cadence verify --gate --sha 6b0d9aa --project app # from the parent folder, t
295
295
 
296
296
  **A gate the wave already played is read, not replayed (L397).** A lot that turns ready has just been through the program's gate (`orchestrate.build`, then `orchestrate.test`, at the commit it integrates), and the wave keeps the result in its state (`.cadence/runs/<wave>/<lot>.json`, key `checks`: the commit, its git tree, each command and its code). Replaying the same build and suite on the same commit costs ~5 minutes and says nothing new (L390, 28-10). `cadence verify --gate` therefore looks in the current folder, the repository root and its parent for a wave state of this repository (most recent first) and **reads the verdict** when all of this holds:
297
297
 
298
- - the recorded checks ran **at the requested sha** (the same commit, not a descendant);
298
+ - the recorded checks ran **at the requested sha** (the same commit, not a descendant) — or at the commit under it when the commits between them change **only the plan file** (the review record and `raf done` that the wave and the lead commit above an integrated lot, L444; the walk stops at a commit that touches another file, a merge or a root commit; the first line then says « au sha <checked>, sous les seuls commits de plan »);
299
299
  - the git tree of the sha is **identical** to the recorded tree;
300
300
  - every current step (`orchestrate.build`, `orchestrate.test`) has a recorded run with **the same command and code 0** (an unstable test the wave replayed alone is still green and is said).
301
301
 
@@ -306,7 +306,7 @@ verify --gate 6b0d9aa : verdict repris de la porte de la vague 2026-10-28-0830c
306
306
  verify --gate 6b0d9aa : vert
307
307
  ```
308
308
 
309
- Nothing runs: no worktree, no copy of the dependencies, exit 0. Otherwise the gate is **replayed** exactly as described above, and the line right after the one telling where it ran gives the reason: `verdict rejoué : aucune porte de vague enregistrée pour ce sha` (no wave state, another sha such as the head after an integration that rebased or a plan commit, another repository), `arbre différent de celui contrôlé`, `arbre non enregistré` (state written before L397), or `l'enregistrement (vague …, lot …) ne couvre pas en vert test (…)` (a red, missing or changed step). A recorded red is never repeated as a verdict: it is replayed. The verdict read is the one of the wave's gate (with `orchestrate.testReplay` applied), not a new run: to force a replay, ask for a sha the wave did not check.
309
+ Nothing runs: no worktree, no copy of the dependencies, exit 0. Otherwise the gate is **replayed** exactly as described above, and the line right after the one telling where it ran gives the reason: `verdict rejoué : aucune porte de vague enregistrée pour ce sha` (no wave state, another sha such as the head after an integration that rebased, a commit that also touches another file (a plan-only commit is crossed, L444), another repository), `arbre différent de celui contrôlé`, `arbre non enregistré` (state written before L397), or `l'enregistrement (vague …, lot …) ne couvre pas en vert test (…)` (a red, missing or changed step). A recorded red is never repeated as a verdict: it is replayed. The verdict read is the one of the wave's gate (with `orchestrate.testReplay` applied), not a new run: to force a replay, ask for a sha the wave did not check.
310
310
 
311
311
  ```yaml
312
312
  # cadence.yaml
@@ -315,7 +315,7 @@ verify:
315
315
  # inPlace: true # the main tree or nothing: not clean at the sha → exit 2, nothing is run
316
316
  ```
317
317
 
318
- Absent, `verify.inPlace` chooses as above. Which dependencies are copied into the throwaway worktree:
318
+ Absent, `verify.inPlace` chooses as above, with one more case for the copy (L431): **a live wave on the repository** (the `orchestrate.lock` of the repository is held by a running process). A wave integrates its lots by fast-forward into the main tree and commits its plan there, so HEAD can move during the gate and the verdict would be « non probant » (5 minutes lost, 29-10); while a wave is live the gate therefore always runs in the throwaway copy, and the first line says why (`la vague … est vivante sur ce dépôt`). A stale lock (dead process) does not count. `verify.inPlace: true` is still obeyed, as the project's explicit choice. Which dependencies are copied into the throwaway worktree:
319
319
 
320
320
  - `orchestrate.worktreeDeps: [node_modules, .venv, front/node_modules]` in `cadence.yaml`: those paths, relative to the repository
321
321
  (no absolute path, no `..`); one that does not exist in the main tree is reported and skipped;
@@ -344,4 +344,4 @@ raf: push refusé — 6b0d9aa : aucun verdict de la porte pour ce sha ; la rejou
344
344
  raf: push refusé — 6b0d9aa : sa porte est rouge (verdict du 2026-10-29) ; la rejouer : …
345
345
  ```
346
346
 
347
- This is the exit code of the gate itself, not a pipe's: on 29-10 a project was pushed with a red gate because its exit code had been hidden by `| tail -1` (the same loss as 28-10). `CADENCE_PUSH_UNGATED=1` in the environment of the `git push` lets the push through and says so on stderr (`push passé outre`); `--no-verify` still skips the hook as for any hook. A deleted branch, a tag and an unreadable verdict file (read as « no verdict ») are handled as follows: deletions and tags are not checked, a damaged file refuses the push until the gate is replayed (which rewrites it). A `cadence.yaml` that cannot be read lets the push through, like an unreadable wave state. The check applies where the hook is installed: the hook a wave installs, and, once the wave frees the repository (all its lots finished, so before the delivery push), a gate-only `pre-push` hook it leaves behind when the project declares a gate (`orchestrate.test` or `orchestrate.build`). That hook is not left when another `pre-push` hook is in place (the project's own, or that of another live wave), nor when `core.hooksPath` points inside the tracked tree; the next wave replaces it, and a repository that never saw a wave has none.
347
+ This is the exit code of the gate itself, not a pipe's: on 29-10 a project was pushed with a red gate because its exit code had been hidden by `| tail -1` (the same loss as 28-10). `CADENCE_PUSH_UNGATED=1` in the environment of the `git push` lets the push through and says so on stderr (`push passé outre`); `--no-verify` still skips the hook as for any hook. **A commit that changes only the plan takes its parent's verdict (L432).** The `raf done` commit above a sha with a green gate touches nothing but the plan (`.cadence/raf.yaml`, or the file named by `plan:` in `cadence.yaml`): the hook walks down the commits whose only changed file is the plan, each with a single parent (up to 50 stacked), and uses the first verdict it finds, so no full suite is replayed for it. A verdict of the commit itself takes precedence; a red verdict below stays red; a commit that also touches another file, a merge commit, a root commit and a stack with no verdict below get none (the message is the usual one, naming the pushed sha). A deleted branch, a tag and an unreadable verdict file (read as « no verdict ») are handled as follows: deletions and tags are not checked, a damaged file refuses the push until the gate is replayed (which rewrites it). A `cadence.yaml` that cannot be read lets the push through, like an unreadable wave state. The check applies where the hook is installed: the hook a wave installs, and, once the wave frees the repository (all its lots finished, so before the delivery push), a gate-only `pre-push` hook it leaves behind when the project declares a gate (`orchestrate.test` or `orchestrate.build`). That hook is not left when another `pre-push` hook is in place (the project's own, or that of another live wave), nor when `core.hooksPath` points inside the tracked tree; the next wave replaces it, and a repository that never saw a wave has none.
package/docs/lead.md CHANGED
@@ -64,13 +64,17 @@ picture, not its source.
64
64
  build run by the lead, `raf check` clean. A `visible` lot waits for the human to look at the
65
65
  UX reviewer's captures: the lead records `raf ux` on the human's verdict, never on its own — unless the parent folder's `cadence.yaml` says `lead.ux: lead`: then the lead forms the verdict from the reviewer's report and its own look at the captures, and asks the human only at the slightest doubt. Then
66
66
  `raf done`.
67
- 6. **Delivery.** Push (for a « livrable jusqu'à <sha> », that sha alone, never HEAD), then
67
+ 6. **Delivery.** The lead can hand a ready lot to the `livreur` agent (L439, [`agents/livreur.md`](../agents/livreur.md)): one subagent per lot, one at a time per repository, never during the integration of a lot of the wave (`cadence lead tour` shows it). The brief gives the project folder, the lot, the sha to deliver (« livrable jusqu'à »), the value of `lead.ux`, the base URL of the delivered app and, for a visible lot with `lead.ux: lead`, the `ux-reviewer`'s report and the folder of its captures. The `livreur` checks the brief and the sha first (the sha is an ancestor of HEAD, no commit of a lot that is not done below it, not already in `origin/main`; « already delivered » means in `origin/main` AND in the delivery log `deliveries.log`), then runs `cadence verify --gate --sha`, the UX verdict (`raf ux` only with `lead.ux: lead`, on the captures it opens itself, nothing at the slightest doubt; with `lead.ux: human` it only reads that a verdict is recorded: `raf show <lot>` prints `visible : oui` and `ux : <verdict>` or `ux : absent` for a visible lot), `raf done` and the plan commit that `raf done` prints, `git push origin <sha>:main`, `cadence deliver --dry-run --sha` then `cadence deliver --sha`. It pushes neither the plan commit (the lead pushes it after its own replay) nor `HEAD`, uses neither `--force` nor `CADENCE_PUSH_UNGATED`, and retries nothing after a refusal. It hands back one line, `L84 livré 3d6edf4, QA à lancer <url>` (or `…, pas de QA à lancer` when `git diff --name-only origin/main..<sha>`, read right after `git fetch origin main` and before the gate and the push — after the push the interval is empty — lists docs, the plan or tests only; when it touches a page or an API, a brief without the base URL is refused there, before any action), or one `Question : …` naming the step reached, the sha, the cause and the command to rerun. The lead launches the `qa-reviewer` itself with that URL; `raf review` stays with the lead. Otherwise the lead delivers itself: push (for a « livrable jusqu'à <sha> », that sha alone, never HEAD), then
68
68
  `cadence deliver`. Where the project declares a gate, the `pre-push` hook refuses a sha with no green verdict of
69
69
  `cadence verify --gate` (L426): the lot's sha has the verdict of the re-check, the plan commit above it (the head of `main`
70
- after `raf done` and its commit) has none and gets a full replay with `cadence verify --gate --sha HEAD` before the push;
70
+ after `raf done` and its commit) changes only the plan and takes that verdict (L432); a commit that also touches another file (a release commit, say) has none and gets a full replay with `cadence verify --gate --sha HEAD` before the push;
71
71
  `CADENCE_PUSH_UNGATED=1` is the human's explicit override, never the routine. A lot that turns ready is delivered while the wave runs (`cadence deliver --sha <sha>`, accepted when no commit of the sha belongs to a lot that is not ready), not at the end of the wave. After a green delivery that changes what a page shows or what it is served, the
72
72
  `qa-reviewer` agent walks the delivered pages in a real browser, reads only, and brings its blocking
73
73
  findings to the human. It is not a gate: a finding becomes a new lot.
74
+ The lead asks both reviewers (`code-reviewer`, `qa-reviewer`) for a short reply with a `Report file: <path>` line in
75
+ the brief (L440): the full report goes to that file, outside the repository, and the agent answers in 5 lines at most
76
+ (`Verdict:`, `Blocking:`, `Follow-ups:`, `Report:`). The lead reads the file only when it needs the detail; twenty
77
+ reports of 3 to 5k each had swollen its context on 29-10.
74
78
  7. **Threshold and chaining.** Before every new wave the lead reads `cadence session context`. Under
75
79
  **60 %** it chains: `cadence orchestrate --continue` draws the next ready lot of the plan, in the
76
80
  declared priority, without asking the human « next? ». From 60 % upward, or when the figure cannot
@@ -170,7 +174,7 @@ The commit count of a lot in progress is the one of `raf commits`: plan commits
170
174
  | `attend` | only when a lot waits on its `gate`: `attend L190` |
171
175
  | `dépôt` | `non commité` (modified or untracked files), `non poussé` (commits ahead of the upstream), `livraison en cours` (live delivery lock); `propre` otherwise |
172
176
 
173
- With `--json`, each project row also carries `lots` (L401): the open lots (`todo`, `doing`, recurring ones left out) as `{ id, title, status, after, parent? }`, in plan order, `after` as the plan writes it (a closed or unknown id is kept as is). The open sub-tasks of those lots follow their lot as entries of their own (`id` = `<lot>/<n>`, `parent` = the lot, no `after`); those of a closed, dropped or recurring lot are left out with it. In a plan kept by another tool, `parent` is the lot's own `parent:` field. A row in error has no `lots`. The [lots panel](cadence-hud.md#the-lots-panel-hud-lots-l401) of `cadence-hud` reads this field.
177
+ With `--json`, each project row also carries `lots` (L401): the open lots (`todo`, `doing`, recurring ones left out) as `{ id, title, status, after, parent?, reviewed? }`, in plan order, `after` as the plan writes it (a closed or unknown id is kept as is). `reviewed: true` is set when the plan holds the lot's code review (`review:` posed by `raf review`, L413), absent otherwise: the [cycle strip](cadence-hud.md#the-lots-panel-hud-lots-l401) of the lots panel reads it for its « revue posée » stage. The open sub-tasks of those lots follow their lot as entries of their own (`id` = `<lot>/<n>`, `parent` = the lot, no `after`); those of a closed, dropped or recurring lot are left out with it. In a plan kept by another tool, `parent` is the lot's own `parent:` field. A row in error has no `lots`. With `--json`, a row also carries `closed` (L415): the closed lots (`done`, archived ones included, lots with an `every:` left out, a lot with no `finished:` day left out) as `{ id, finished }` in plan order, absent when there is none; the lots panel compares `finished` with the day of the project's last green delivery to list the closed lots not delivered. The [lots panel](cadence-hud.md#the-lots-panel-hud-lots-l401) of `cadence-hud` reads this field.
174
178
 
175
179
  After them, one line per project that does not declare `orchestrate.test` (L327): `alpha · porte de tests absente : orchestrate.test n'est pas déclaré, la vague ne lancera pas la suite (seule la revue le fera si elle le décide) · commande probable : npm test (à déclarer en orchestrate.test)`. The probable command is read from `package.json` (`scripts.test`), `pyproject.toml` (pytest) or `pom.xml` (`mvn test`) and omitted when none exists; the lead declares `test:` and `build:` in that project's `cadence.yaml` after checking the command is green, one config commit per project. `--json`: `noTest: { probable }` on the project's row, absent when the key is declared. Same line in [`orchestrate --dry-run`](orchestrate.md).
176
180
 
@@ -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)
@@ -479,7 +480,7 @@ suspended, except for such a visible lot, where the UX session needs budget and
479
480
 
480
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.
481
482
 
482
- **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.
483
484
 
484
485
  **Frozen copy of the tool (L61)**: at the real start of a wave (not with `--dry-run` or `--status`), cadence copies the
485
486
  parts of its package that run the wave (`bin/`, `dist/`, `templates/`, `agents/`, `skills/`, `package.json`; `node_modules`
@@ -750,7 +751,7 @@ verdict and screenshots, it does not record it), the push and the deliveries, on
750
751
  **Several waves at once**: the lock is per **repository** (a lock in the repository's shared state,
751
752
  `orchestrate.lock`, which also makes `cadence deliver` refuse that repository), not per folder. A wave is
752
753
  refused only when one of its repositories is held by a live wave (`<repo> : une orchestration y est déjà en
753
- cours`; the lock of a dead process is detected and cleared); two waves on different repositories run side by
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
754
755
  side, even when started from the same parent folder. A repository is released **as soon as every lot of the wave that
755
756
  touches it is finished** (ready, handed back or failed): its lock and its `pre-push` guard are lifted while the wave goes
756
757
  on elsewhere, so `cadence deliver` accepts it (a repository with a lot still to play, a question or a suspended lot stays
@@ -764,14 +765,14 @@ A slot or a registry entry is owned by a pid **and** its start time: a reused pi
764
765
  identifiers are reserved atomically (`-2`, `-3` suffix when two waves start in the same minute; an existing
765
766
  `--wave` is refused). The registry of live waves and the slots live under `~/.cadence/orchestrate/` (`CADENCE_HOME`
766
767
  to move it): `cadence orchestrate --status` lists, from any folder, the live waves, the repositories each
767
- 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 playing in a worktree, 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).
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).
768
769
 
769
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.
770
771
 
771
772
  **Guards, imposed by the code**: a global cap of simultaneous sessions, one wave per repository at
772
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
773
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
774
- 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 (`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
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
775
776
  `pre-push` hook is refused before anything starts — `pushurl` is never touched); after every session the
776
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
777
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
@@ -68,7 +68,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
68
68
  | `raf note <id> "text"` | dated note — keep decisions next to the work |
69
69
  | `raf title <id> "new title"` | replace the title of a lot of any status (`done` included) and add a dated note « titre remplacé : « old title » »; `raf now`, `raf show`, `raf list`, `session start` and the proposals read the new one. Refused on an empty or identical title, an unknown lot or a sub-task |
70
70
  | `raf archive [--before date] [--dry-run]` | move the `done` and `dropped` lots (those finished strictly before `--before`, when given; a lot with no `finished:` date stays then) out of the FILE `.cadence/raf.yaml` into `.cadence/archive/<year>.yaml`, the year of their end (L396). **The archive is a storage detail, invisible to every reader**: the loaded plan presents all the lots, active and archived, **in the order they had before the archiving** — each archived lot takes its place by number among the lots of the plan file (just before the first active lot with a greater number), the order of the plan file itself never moves — so `raf show`, `raf list`, `raf commits`, `raf check`, `raf now`, `raf gantt`, `cadence news`, `cadence perf`, `session start`, `lead tour`, `cadence ref coverage`, `cadence deliver`, the post-commit hook and `cadence orchestrate` print exactly what they printed before the archiving — nothing has to « also look in the archive », a lot id is never reused, and a commit or a News entry citing an archived lot is not « inconnu ». What changes is the file: the plan the sessions open stays short (the cadence plan weighed 559 KB, ≈ 150 k tokens) and the briefs of the implementation, the sheets, the code review and the pre-check tell the sessions never to open `.cadence/archive/`. **Writing**: every lot goes back to the file it came from — `raf note`, `raf review`, `raf ux`, `raf title`… on an archived lot rewrite its year file, the plan file is untouched; a lot reopened (`raf todo <id>`, or `status: doing` set by hand in a year file) comes back to the end of the plan file at the next write, so the archive only ever holds `done` and `dropped` lots. **Lossless, whatever fails**: the year files are written before the plan file (should the plan then fail to write, a moved lot is at worst duplicated — which the next load refuses by name — never lost), and any exception during the write (`EACCES` on a read-only file, a full disk, a `plan.onWrite` command that fails) puts the plan, the year files and the declared files back as they were — a year file born of that write disappears — and the error says so (`écriture du plan refusée (EACCES, …) — plan et archive inchangés`). **Guards**: a closed lot with an open gap (what `raf check` signals — code review, UX review, docs.sync, News entry…) stays in the plan with the gap as its printed reason (`L1 reste dans le plan : écart ouvert — L1 est visible et terminé sans entrée Nouveautés …`); a lot named in `after:` by another, or counted by a `gate:`, is archived like any other — the readers see the archive, so the dependency and the count read the same. A same id found in two files (plan and archive, or two years) is a named error that stops every command (`identifiant en double : L1 est dans .cadence/archive/2025.yaml et dans .cadence/raf.yaml`) until one copy is removed by hand — never a silent choice of one copy. `--dry-run` lists without writing; `--before` takes `YYYY-MM-DD`; refused on a read-only plan (its tool keeps its own history) and while the plan is still at the legacy `docs/plan/raf.yaml` (the archive would land under `docs/`, published, and `raf migrate` would not move it: `raf migrate` first, the refusal says so). For `cadence orchestrate`, the year files are plan files like `.cadence/raf.yaml`: a session that commits one has « touché le plan ». Like every plan write, it is not committed: the command to commit the plan and the archive files together, at explicit paths, is printed (`plan.commit` template, action `N lot(s) archivé(s)`); replaying moves nothing twice. The archive files are kept with the plan: touching them is not working on a lot. **The published plan**: a generator run by `plan.onWrite` (below) that reads only the file `.cadence/raf.yaml` publishes a plan without the archived lots; `raf archive` says so on stderr when `plan.onWrite` is declared, the generator must also read `.cadence/archive/*.yaml` (key `lots`, same lot format as the plan) |
71
- | `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, `screen` and `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 |
71
+ | `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 |
72
72
  | `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) |
73
73
  | `raf now` | what to do next |
74
74
  | `raf list [--status s]` | flat list |
@@ -77,7 +77,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
77
77
  | `raf check --ignored` | list the acknowledged commits with their date and reason |
78
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) |
79
79
  | `raf gantt [-o file]` | standalone Gantt page |
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: « 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
+ | `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 |
81
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) |
82
82
 
83
83
  ### A commit on a lot that is still `todo`
@@ -271,6 +271,7 @@ plan:
271
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).
272
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.
273
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).
274
275
  - A read-only plan (kept by another tool) never runs the command: the project's tool owns the write.
275
276
 
276
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: start it with `gh workflow run tests.yml --ref <branch>` or by pushing a `diag/**` branch, to see on the runner what a red publish run saw.
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
@@ -10,7 +10,7 @@ As a plugin:
10
10
  ```
11
11
 
12
12
  gives `/cadence:session-start`, `/cadence:session-close`, `/cadence:deliver`,
13
- `/cadence:lead`, `/cadence:orchestrate` and the `ux-reviewer`, `code-reviewer` and `qa-reviewer` agents. Or copy them into the
13
+ `/cadence:lead`, `/cadence:orchestrate`, the `ux-reviewer`, `code-reviewer` and `qa-reviewer` agents and the `livreur` delivery agent. Or copy them into the
14
14
  repository with `cadence skills install` (to `.claude/skills/cadence-*` and
15
15
  `.claude/agents/cadence-*.md`; `--dir` for another `.claude` folder,
16
16
  `--force` to overwrite local edits).
@@ -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
@@ -79,6 +82,7 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
79
82
  other did not, each with the rendered page and a crop of its zone, and answers `confirme` or `ecarte` with a reason, measuring with
80
83
  `cadence ref measure` rather than by eye and never guessing (what it cannot tell is `ecarte`). Its tools are `Read` and `Bash`: it writes nothing and never sees the readings
81
84
  ([ref](ref.md#cadence-ref-analyse-documentpdf---sections-3-59---slug-name---model-sonnetopushaiku---model-b-sonnetopushaiku---seuil-30---double)).
85
+ - **livreur** (agent, L439): the lead's delivery agent. Given one ready lot, its sha and the base URL of the app, it checks the sha (ancestor of HEAD, no lot that is not done below it, not already in `origin/main`), then runs `cadence verify --gate --sha`, the UX verdict according to `lead.ux`, `raf done` with the plan commit it prints, `git push origin <sha>:main` and `cadence deliver`, and hands back one line (`L84 livré 3d6edf4, QA à lancer <url>`) or one `Question : …`. Its tools are `Read`, `Grep`, `Glob` and `Bash`: no `Agent`, no `Edit`, no `Write`; it never launches the `qa-reviewer`, `raf review` or a force push ([lead](lead.md)).
82
86
  - **qa-reviewer** (agent): any web app (read-only like `ux-reviewer`: same tool list, no `Edit`, no `Write`); given a repository and a base URL (and
83
87
  optionally a lot id, to walk only the pages it touched — those that call the
84
88
  changed endpoints, those of the screens it changed, the pages that consume the services the lot changed,
@@ -95,3 +99,6 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
95
99
  the route, what was expected, what was measured and the evidence; pages checked
96
100
  N/N, follow-ups as `raf add` lines, what it could not verify, a one-line
97
101
  verdict. Read-only: GET only, no login, nothing submitted; it stops at a PIN.
102
+ **Short reply (L440):** both reviewers answer in 5 lines at most (`Verdict:`, `Blocking:`, `Follow-ups:`, `Report:`) when the brief
103
+ carries a `Report file: <absolute path>` line, and write the full report to that file (outside the repository, verdict last);
104
+ 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.26.0**: a new `livreur` agent delivers a ready lot for the lead (gate, UX verdict, `raf done`, push, deliver) and hands back one line; `raf show` prints `visible` and `ux` for a visible lot.
8
+ - **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.
9
+ - **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.
7
10
  - **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.
8
11
  - **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.
9
12
  - **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.
@@ -50,6 +53,12 @@ Each is a paragraph in [docs/whats-new.md](whats-new.md) and a full section in [
50
53
 
51
54
  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).
52
55
 
56
+ **1.26.0**: the lead hands the delivery of a ready lot to a subagent. The `livreur` agent (L439, installed by `cadence skills install` like the others) takes one ready lot and one sha given by the lead, and runs `cadence verify --gate --sha`, the UX verdict according to `lead.ux`, `raf done` and the plan commit it prints, `git push origin <sha>:main` and `cadence deliver`, then hands back one line (`L84 livré 3d6edf4, QA à lancer <url>`) or one `Question : …`. It first checks that the sha is an ancestor of HEAD, has no lot that is not done below it and is not already in `origin/main` and in the delivery log; it launches no agent, never the `qa-reviewer` (the lead does, after the line), and the lead launches one `livreur` at a time (`docs/lead.md`). A relaunch on a lot already done weighs both proofs (`origin/main` and `deliveries.log`), so « pushed, never delivered » is no longer masked by « déjà clos ». It reads `git diff --name-only origin/main..<sha>` right after `git fetch origin main`, before the gate and the push, to decide whether a QA pass is due and refuses a brief without the base URL before any action. `raf show <lot>` prints two new stable lines for a visible lot, `visible : oui` (then `screen : …` when the lot has one) and `ux : <verdict>` or `ux : absent` (`docs/raf.md`): that is how the `livreur` knows whether the lot is visible and whether the human's UX verdict is recorded (`lead.ux: human`).
57
+
58
+ **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).
59
+
60
+ **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).
61
+
53
62
  **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`).
54
63
 
55
64
  **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).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylad/cadence",
3
- "version": "1.24.0",
3
+ "version": "1.26.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",
@@ -62,7 +62,7 @@ The push targets the branch being delivered (`main` here). Two cases where it do
62
62
  **The sha pushed needs a green verdict of the gate (L426).** Where the project declares a gate (`orchestrate.test` or
63
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
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; a plan or release commit above it has none, and the wave's record does not cover it:
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
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
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
68
  on stderr: only when the human asks for it, never the routine.