@sylad/cadence 1.41.0 → 1.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/dist/archive.js +103 -6
- package/dist/audit.js +2 -2
- package/dist/clean.js +0 -4
- package/dist/cli/context.js +129 -0
- package/dist/cli/deliver-commands.js +114 -0
- package/dist/cli/gates.js +234 -0
- package/dist/cli/lead-commands.js +161 -0
- package/dist/cli/lot-commands.js +257 -0
- package/dist/cli/read-commands.js +227 -0
- package/dist/cli/session-commands.js +152 -0
- package/dist/cli/setup-commands.js +72 -0
- package/dist/cli/spec-commands.js +298 -0
- package/dist/cli/stats-commands.js +138 -0
- package/dist/cli-add.js +37 -0
- package/dist/cli.js +82 -1651
- package/dist/config.js +16 -103
- package/dist/corrections.js +28 -42
- package/dist/docsync.js +15 -3
- package/dist/lead.js +6 -24
- package/dist/orchestrate/attente.js +82 -0
- package/dist/orchestrate/command.js +18 -17
- package/dist/orchestrate/continue.js +2 -12
- package/dist/orchestrate/cycle.js +5 -5
- package/dist/orchestrate/integration-vivante.js +138 -0
- package/dist/orchestrate/launch.js +6 -18
- package/dist/orchestrate/lock.js +3 -20
- package/dist/orchestrate/registry.js +8 -1
- package/dist/orchestrate/schemas.js +5 -1
- package/dist/orchestrate/snapshot.js +13 -0
- package/dist/orchestrate/state.js +17 -0
- package/dist/plan-extras.js +3 -2
- package/dist/plan-ids.js +95 -0
- package/dist/plan-read.js +2 -0
- package/dist/plan.js +29 -34
- package/dist/ref/command.js +2 -3
- package/dist/resources.js +3 -9
- package/dist/state.js +8 -8
- package/dist/verify-gate.js +33 -2
- package/dist/yaml-keys.js +19 -0
- package/docs/cadence-hud.md +1 -1
- package/docs/deliver.md +1 -1
- package/docs/lead.md +2 -2
- package/docs/news.md +2 -0
- package/docs/orchestrate.md +6 -2
- package/docs/qualite.md +1 -1
- package/docs/raf.md +22 -4
- package/docs/whats-new.md +6 -0
- package/package.json +1 -1
- package/skills/lead/SKILL.md +1 -1
package/dist/ref/command.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
3
|
import { join, resolve } from 'node:path';
|
|
5
4
|
import { RafError } from '../plan.js';
|
|
6
5
|
import { AGENTS_DIR } from '../skills.js';
|
|
7
|
-
import { realClaude, readAgents } from '../orchestrate/launch.js';
|
|
6
|
+
import { claudeConfigDir, realClaude, readAgents } from '../orchestrate/launch.js';
|
|
8
7
|
import { add } from './add.js';
|
|
9
8
|
import { analyse } from './analyse.js';
|
|
10
9
|
import { checkInventaire, readInventaire, summaryInventaire } from './check.js';
|
|
@@ -33,7 +32,7 @@ export function refCommand(rest, values, root, io) {
|
|
|
33
32
|
}
|
|
34
33
|
if (verb === 'analyse' && args.length === 1) {
|
|
35
34
|
const claude = realClaude(io.env.CADENCE_CLAUDE_BIN || 'claude', io.env);
|
|
36
|
-
return analyse({ document: args[0], slug: values.slug, sections: values.sections, model: values.model, modelB: values['model-b'], double: values.double === true, seuil: values.seuil, env: io.env, cwd: io.cwd, refsDir, configPath, claudeHome: io.env
|
|
35
|
+
return analyse({ document: args[0], slug: values.slug, sections: values.sections, model: values.model, modelB: values['model-b'], double: values.double === true, seuil: values.seuil, env: io.env, cwd: io.cwd, refsDir, configPath, claudeHome: claudeConfigDir(io.env), now: io.now(), out: io.out, err: io.err, claude, agents: readAgents(AGENTS_DIR) });
|
|
37
36
|
}
|
|
38
37
|
if (verb === 'measure' && args.length === 3) {
|
|
39
38
|
measure({ slug: args[0], page: args[1], zone: args[2], quoi: values.quoi, largeur: values.largeur, env: io.env, refsDir, json: values.json === true, out: io.out });
|
package/dist/resources.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { readYamlFile } from './yaml-keys.js';
|
|
4
4
|
import { isOpen, RafError, RESOURCE_NAME, RESOURCES_BAD_LIST } from './plan.js';
|
|
5
5
|
/**
|
|
6
6
|
* Clé `resources:` du cadence.yaml le plus proche en remontant depuis le dossier parent de `root` : le premier fichier qui porte la clé
|
|
@@ -12,13 +12,7 @@ export function readResources(root) {
|
|
|
12
12
|
for (let dir = dirname(root);; dir = dirname(dir)) {
|
|
13
13
|
const file = join(dir, 'cadence.yaml');
|
|
14
14
|
if (existsSync(file) && statSync(file).isFile()) {
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
raw = parse(readFileSync(file, 'utf8'));
|
|
18
|
-
}
|
|
19
|
-
catch (e) {
|
|
20
|
-
throw new RafError(`${file} illisible : ${e.message.split('\n')[0]}`);
|
|
21
|
-
}
|
|
15
|
+
const raw = readYamlFile(file);
|
|
22
16
|
if (raw != null && typeof raw === 'object' && Object.hasOwn(raw, 'resources')) {
|
|
23
17
|
const list = raw.resources ?? [];
|
|
24
18
|
if (!Array.isArray(list) || !list.every((n) => typeof n === 'string' && RESOURCE_NAME.test(n)))
|
package/dist/state.js
CHANGED
|
@@ -52,7 +52,11 @@ export function writeSessionMark(dir, mark) {
|
|
|
52
52
|
export function lockPath(dir) {
|
|
53
53
|
return join(dir, 'deliver.lock');
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Lit un fichier de verrou : null s'il est absent ou illisible du système de fichiers ; sinon son contenu mis en forme par `fromRaw` et l'âge du fichier,
|
|
57
|
+
* ou `broken` (marqué `unreadable`) quand ce n'est pas du JSON. Commun au verrou de livraison et à celui de l'orchestration.
|
|
58
|
+
*/
|
|
59
|
+
export function readLockJson(file, fromRaw, broken) {
|
|
56
60
|
let text;
|
|
57
61
|
let ageMs;
|
|
58
62
|
try {
|
|
@@ -63,13 +67,13 @@ function readLockFile(file) {
|
|
|
63
67
|
return null;
|
|
64
68
|
}
|
|
65
69
|
try {
|
|
66
|
-
|
|
67
|
-
return { pid: Number(raw.pid), sha: String(raw.sha), started: String(raw.started), ageMs };
|
|
70
|
+
return { ...fromRaw(JSON.parse(text)), ageMs };
|
|
68
71
|
}
|
|
69
72
|
catch {
|
|
70
|
-
return {
|
|
73
|
+
return { ...broken, unreadable: true, ageMs };
|
|
71
74
|
}
|
|
72
75
|
}
|
|
76
|
+
const readLockFile = (file) => readLockJson(file, (raw) => ({ pid: Number(raw.pid), sha: String(raw.sha), started: String(raw.started) }), { pid: 0, sha: '?', started: '?' });
|
|
73
77
|
export function readLock(dir) {
|
|
74
78
|
return readLockFile(lockPath(dir));
|
|
75
79
|
}
|
|
@@ -282,7 +286,3 @@ export function readFailedEntries(dir) {
|
|
|
282
286
|
return [];
|
|
283
287
|
}
|
|
284
288
|
}
|
|
285
|
-
/** Les jours de `deliveries-failed.log` (une ligne par livraison rouge), dans l'ordre du fichier. */
|
|
286
|
-
export function readFailedDeliveries(dir) {
|
|
287
|
-
return readFailedEntries(dir).map((e) => e.day);
|
|
288
|
-
}
|
package/dist/verify-gate.js
CHANGED
|
@@ -53,6 +53,37 @@ function sh(cmd, cwd, timeoutMs, group, env) {
|
|
|
53
53
|
});
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Les fichiers suivis au sha, hors documentation (`docs/`, `.md`, `.txt`), journal `CHANGELOG` (nom exact), plan et `cadence.yaml`, qui citent le chemin du plan (L482) ; vide si aucun ou si git échoue.
|
|
58
|
+
* Chaîne cherchée telle quelle (`-F`), pas comme un motif.
|
|
59
|
+
*/
|
|
60
|
+
function planReaders(repo, sha, planFile) {
|
|
61
|
+
if (!planFile)
|
|
62
|
+
return [];
|
|
63
|
+
try {
|
|
64
|
+
const out = git(repo, 'grep', '-l', '-z', '-I', '-F', '-e', planFile, sha, '--', '.', ':(exclude)docs', ':(exclude)*.md', ':(exclude)*.txt', ':(exclude).cadence', ':(exclude)cadence.yaml', ':(exclude)CHANGELOG');
|
|
65
|
+
return out.split('\0').map((l) => l.slice(sha.length + 1)).filter(Boolean);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return []; // git grep rend 1 sans correspondance
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Le plan dont les commits seuls sont traversés : aucun si un test le lit, déclaré (L450) ou deviné d'après le code qui cite son chemin sans que `gate.readsPlan` le dise (L482). */
|
|
72
|
+
function crossablePlan(repo, sha, planFile, declared) {
|
|
73
|
+
return declared || planReaders(repo, sha, planFile).length ? undefined : planFile;
|
|
74
|
+
}
|
|
75
|
+
/** La fin de la raison du rejeu quand les commits de plan seuls ne sont pas traversés : déclaré (L450) ou deviné d'après le code (L482) ; vide sinon. */
|
|
76
|
+
function planNote(repo, sha, planFile, declared) {
|
|
77
|
+
const crossing = ' ; les commits de plan seuls ne reprennent pas le verdict d\'un sha dessous';
|
|
78
|
+
if (!planFile)
|
|
79
|
+
return '';
|
|
80
|
+
if (declared)
|
|
81
|
+
return ` (orchestrate.gate.readsPlan : un test lit le plan${crossing.replace(' ;', ',')})`;
|
|
82
|
+
const readers = planReaders(repo, sha, planFile);
|
|
83
|
+
if (!readers.length)
|
|
84
|
+
return '';
|
|
85
|
+
return ` (le code lit le plan, gate.readsPlan absent : ${readers.slice(0, 3).join(', ')}${readers.length > 3 ? ', …' : ''} cite ${planFile}${crossing} — déclarer orchestrate.gate.readsPlan)`;
|
|
86
|
+
}
|
|
56
87
|
/** Pourquoi l'arbre principal ne peut pas porter la porte au sha : non propre ou à un autre commit ; null s'il le peut. */
|
|
57
88
|
function mainTreeObstacle(repo, sha) {
|
|
58
89
|
try {
|
|
@@ -142,14 +173,14 @@ async function playGate(ctx) {
|
|
|
142
173
|
let recorded = { reason: 'aucune porte de vague enregistrée pour ce sha' };
|
|
143
174
|
try {
|
|
144
175
|
// Un test qui lit le plan (orchestrate.gate.readsPlan, L450) voit un autre contenu après un `raf done` : les commits de plan seuls ne sont pas traversés.
|
|
145
|
-
recorded = recordedGate(ctx.launchDirs ?? [], repo, sha, steps, config.gate?.readsPlan
|
|
176
|
+
recorded = recordedGate(ctx.launchDirs ?? [], repo, sha, steps, crossablePlan(repo, sha, ctx.planFile, config.gate?.readsPlan));
|
|
146
177
|
}
|
|
147
178
|
catch (e) {
|
|
148
179
|
recorded = { reason: `état des vagues illisible (${gitFailure(e)})` };
|
|
149
180
|
}
|
|
150
181
|
if ('found' in recorded)
|
|
151
182
|
return reuseRecorded(recorded.found, steps, sha, out);
|
|
152
|
-
const replayReason = `verdict rejoué : ${recorded.reason}${
|
|
183
|
+
const replayReason = `verdict rejoué : ${recorded.reason}${planNote(repo, sha, ctx.planFile, config.gate?.readsPlan)}`;
|
|
153
184
|
const obstacle = mainTreeObstacle(repo, sha);
|
|
154
185
|
const mode = ctx.verify?.inPlace;
|
|
155
186
|
const remote = await tryRemote(ctx, short, replayReason, (exec, emit) => runSteps(steps, exec, emit, config, short));
|
package/dist/yaml-keys.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { parse } from 'yaml';
|
|
3
|
+
import { RafError } from './plan.js';
|
|
1
4
|
/** Une clé de cadence.yaml qui vaut `true` ou `false` : toute autre valeur lève l'erreur donnée. */
|
|
2
5
|
export function booleanKey(value, refusal) {
|
|
3
6
|
if (typeof value !== 'boolean')
|
|
@@ -6,3 +9,19 @@ export function booleanKey(value, refusal) {
|
|
|
6
9
|
}
|
|
7
10
|
export const isObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
8
11
|
export const list = (v) => (v == null ? [] : Array.isArray(v) ? v.map(String) : [String(v)]);
|
|
12
|
+
/** Le document yaml de `file` : `undefined` si le fichier manque, `RafError` « illisible » (première ligne de l'erreur du parseur) s'il ne se lit pas. */
|
|
13
|
+
export function readYamlFile(file) {
|
|
14
|
+
if (!existsSync(file))
|
|
15
|
+
return undefined;
|
|
16
|
+
try {
|
|
17
|
+
return parse(readFileSync(file, 'utf8'));
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
throw new RafError(`${file} illisible : ${e.message.split('\n')[0]}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** La clé `key` du document yaml de `file` : `undefined` si le fichier manque ou si le document n'est pas une table qui la porte. */
|
|
24
|
+
export function readYamlKey(file, key) {
|
|
25
|
+
const doc = readYamlFile(file);
|
|
26
|
+
return isObject(doc) ? doc[key] : undefined;
|
|
27
|
+
}
|
package/docs/cadence-hud.md
CHANGED
|
@@ -83,7 +83,7 @@ A project shows at most 5 lots (L411), the first ones in the order above (so tho
|
|
|
83
83
|
|
|
84
84
|
**Reordering the projects (L449).** Above the tree, `ordre des projets (priority:) — glisser, ou Alt+↑ / Alt+↓` and one row per project, in the order of `priority:`: the name (in a `Box` of fixed width, the longest name's: the desktop app's font is proportional, so padding with spaces would align nothing; L495), the progress bar of the band — closed lots over total, filled cells coloured by the share done (red under 33 %, orange under 66 %, green above), the lots in progress with their ids and `+N cette semaine` (L481; `cadence ▮▮▮▯▯▯▯▯▯▯ 3/10 · 1 en cours : L401 · +2 cette semaine`) — so the progress reads without scrolling the band. A project with no `progress` in the tour (an older `cadence`) keeps `7 lots non clos`; `illisible` for a project the tour could not read. It is drawn only where the lots can be reordered (a `Client` region: terminal and desktop app), when the folder has two projects or more, and when the panel is tall enough to keep a row for each project and one lot under it (otherwise the tree has the room). Drag a project onto another (`●` the row you hold, `⇡` / `⇣` where it will land) or click it and press **Alt**+↑ / **Alt**+↓ (↑ / ↓ alone select): the drop runs `cadence lead priority <project> --before|--after <project> <folder>` ([lead priority](lead.md#lead-priority)), which rewrites the `priority:` list of the `cadence.yaml` of the parent folder, comments kept; the panel then reads the plans again and says under the title `✔ ol placé avant cadence — priority: du cadence.yaml est modifiée, à commiter`. This order is the one of the tour, of `cadence orchestrate --continue` and of the panel itself. The panel never commits. A refusal is written under the title with its reason (`✖ /dev/cadence.yaml illisible : …`, a `priority:` that is not a list) and nothing is written; `cadence` missing from the `PATH` says so; a project already at that place says `• … est déjà avant …`. The error row `cadence.yaml` that the tour adds for an unreadable file is not a project and cannot be moved or targeted.
|
|
85
85
|
|
|
86
|
-
States: `▶ en cours` (doing), `○ libre` (todo, no open `after:`), `⊘ bloqué` (todo, an `after:` is still open); a lot in a wave takes the wave's state and words, as the band writes them (`… en attente`, `⟳ implémente`, `⟳ relit`, `⟳ corrige`, `? QUESTION`, `✔ prêt`, `↩ rendu`, `✖ échec`, `⏸ suspendu`). In a native plan the sub-tasks of an open lot (`L401/2`) are its sub-lots (`├`/`└`); they only read `▶ en cours` or `○ à faire`, and are not counted in the project's `N lots non clos`.
|
|
86
|
+
States: `▶ en cours` (doing), `○ libre` (todo, no open `after:`), `⊘ bloqué` (todo, an `after:` is still open); a lot in a wave takes the wave's state and words, as the band writes them (`… en attente`, `⟳ implémente`, `⟳ relit`, `⟳ corrige`, `? QUESTION`, `✔ prêt`, `↩ rendu`, `✖ échec`, `⏸ suspendu`). In a native plan the sub-tasks of an open lot (`L401/2`) are its sub-lots (`├`/`└`); they only read `▶ en cours` or `○ à faire`, and are not counted in the project's `N lots non clos`. A lot carrying `parent:` (a sub-lot laid flat by `raf add --under`, L465, or the field of a plan kept by another tool) is shown under its parent and counted in `N lots non clos`: it is a lot of its own, not a sub-task. An `after:` id that is closed or unknown is struck through and blocks nothing. A project the tour could not read shows `illisible : <error>`; the others stay.
|
|
87
87
|
|
|
88
88
|
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.
|
|
89
89
|
|
package/docs/deliver.md
CHANGED
|
@@ -359,7 +359,7 @@ Nothing runs: no worktree, no copy of the dependencies, exit 0. Otherwise the ga
|
|
|
359
359
|
|
|
360
360
|
**On another machine (L403).** With `orchestrate.gate.host` in `cadence.yaml` the gate is played by ssh on that host, at the sha pushed there, with a dependency cache; it falls back to the local gate when the host does not answer, and replays locally a command whose connection was lost: [docs/remote-gate.md](remote-gate.md).
|
|
361
361
|
|
|
362
|
-
**A test that reads the plan (L450).** Crossing the plan-only commits assumes that no test reads the plan. On 30-10 avatar-pandora's `plan-page.e2e`, which reads `.cadence/raf.yaml`, went red after the `raf done` of L8 while the head gate, taking over the wave's verdict under that commit, saw nothing. A project whose gate has such a test declares it: `orchestrate.gate: { readsPlan: true }` (or `readsPlan: [e2e/plan-page.e2e.ts]`, the paths of the tests that read it; a non-empty list of relative paths without `..`, anything else is refused when `cadence.yaml` is read). The verdict of a sha *below* plan-only commits is then never taken over: the gate is replayed at the sha asked, and the reason line ends with `(orchestrate.gate.readsPlan : un test lit le plan, …)`. A verdict recorded at the very sha asked is still taken over. Absent or `false`: unchanged. The pre-push hook keeps crossing plan-only commits (its rule, L432, is not changed here).
|
|
362
|
+
**A test that reads the plan (L450).** Crossing the plan-only commits assumes that no test reads the plan. On 30-10 avatar-pandora's `plan-page.e2e`, which reads `.cadence/raf.yaml`, went red after the `raf done` of L8 while the head gate, taking over the wave's verdict under that commit, saw nothing. A project whose gate has such a test declares it: `orchestrate.gate: { readsPlan: true }` (or `readsPlan: [e2e/plan-page.e2e.ts]`, the paths of the tests that read it; a non-empty list of relative paths without `..`, anything else is refused when `cadence.yaml` is read). The verdict of a sha *below* plan-only commits is then never taken over: the gate is replayed at the sha asked, and the reason line ends with `(orchestrate.gate.readsPlan : un test lit le plan, …)`. A verdict recorded at the very sha asked is still taken over. Absent or `false`: unchanged. The pre-push hook keeps crossing plan-only commits (its rule, L432, is not changed here). **Undeclared (L482).** When `readsPlan` is absent, `cadence verify --gate` also looks, at the sha asked, for tracked files outside `docs/`, `*.md`, `*.txt`, `.cadence/`, `cadence.yaml` and the file named exactly `CHANGELOG` that quote the plan's path (`git grep -F`, the path as a plain string). If one does, it behaves as if `readsPlan: true` were declared (plan-only commits are not crossed) and the reason line says `(le code lit le plan, gate.readsPlan absent : <files> cite .cadence/raf.yaml … — déclarer orchestrate.gate.readsPlan)`. Declare the key to get the L450 message; there is no opt-out yet, so a project whose tests merely quote the path as a fixture replays the gate under a plan-only commit. Only `verify --gate` is covered, not `orchestrate --dry-run` nor `lead tour`.
|
|
363
363
|
|
|
364
364
|
```yaml
|
|
365
365
|
# cadence.yaml
|
package/docs/lead.md
CHANGED
|
@@ -107,7 +107,7 @@ picture, not its source.
|
|
|
107
107
|
| The UX verdict is the human's unless `lead.ux: lead` (parent `cadence.yaml`, default `human`) | then the lead decides from the report and the captures, and brings any doubt to the human |
|
|
108
108
|
| A session cannot ask the human anything | it stops and reports; the lead brings the question back |
|
|
109
109
|
| Never push or deliver HEAD above a handed-back lot | HEAD holds code that was not reviewed |
|
|
110
|
-
| A lot handed back for its budget or a non-compliant review that the lead had corrected outside the wave is integrated by `cadence orchestrate --resume --wave <wave> --integrate <project>:<lot>` (L370, [`--integrate`](orchestrate.md#one-worktree-per-lot-orchestrateworktrees-l279)): the fix is made in the kept worktree, then `raf review <lot> "conforme : …"` is run in that worktree and its plan committed there, after the last work commit of the lot; the program rebases, replays the checks, keeps the verdict or makes a short review, and advances the branch | never by hand (L367, 26-10), and never a review recorded in the main repository's plan: `--integrate` reads the worktree's plan and would keep refusing |
|
|
110
|
+
| A lot handed back for its budget or a non-compliant review that the lead had corrected outside the wave is integrated by `cadence orchestrate --resume --wave <wave> --integrate <project>:<lot>` (L370, [`--integrate`](orchestrate.md#one-worktree-per-lot-orchestrateworktrees-l279)): the fix is made in the kept worktree, then `raf review <lot> "conforme : …"` is run in that worktree and its plan committed there, after the last work commit of the lot; the program rebases, replays the checks, keeps the verdict or makes a short review, and advances the branch; the command is accepted while the wave runs (L532, the wave takes the request between two steps) | never by hand (L367, 26-10), and never a review recorded in the main repository's plan: `--integrate` reads the worktree's plan and would keep refusing |
|
|
111
111
|
| A plan kept by another tool is read-only for `raf` | its own commands record the lot's state |
|
|
112
112
|
|
|
113
113
|
## Exit codes
|
|
@@ -188,7 +188,7 @@ The commit count of a lot in progress is the one of `raf commits`: plan commits
|
|
|
188
188
|
| `attend` | only when a lot waits on its `gate`: `attend L190` |
|
|
189
189
|
| `dépôt` | `non commité` (modified or untracked files), `non poussé` (commits ahead of the upstream), `livraison en cours` (live delivery lock); `propre` otherwise |
|
|
190
190
|
|
|
191
|
-
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.
|
|
191
|
+
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. A lot that carries `parent:` itself (a sub-lot laid flat, `raf add --under` in a native plan, L465, or the field of a plan kept by another tool) carries it as `parent`, with its own id (`AB-1.2` under `AB-1`, not `AB-1/2`). 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.
|
|
192
192
|
|
|
193
193
|
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).
|
|
194
194
|
|
package/docs/news.md
CHANGED
|
@@ -150,6 +150,8 @@ to be reported as one **major** finding per line — a stale document is not lef
|
|
|
150
150
|
memory of a sentence. Touching a document is what the program checks; whether its text is true is
|
|
151
151
|
still the review's reading. The commits of neighbouring repositories (`repos:`) are not audited.
|
|
152
152
|
|
|
153
|
+
A commit of type `refactor` (`refactor(L7): …`, no `!`) is left out of that reading (L537): a cleanup or an internal split declared with no change of command, key or output has no document to write, so a lot made only of such commits is no gap, and a lot that also holds a `feat` or `fix` is judged on those commits' files alone. `refactor!:` or a `BREAKING CHANGE:` footer in the body (a breaking change) still counts, and a document written in a refactor commit still satisfies the rule. `cadence orchestrate` reads the same commits, so the librarian is not called for a pure refactor.
|
|
154
|
+
|
|
153
155
|
A gap is closed by writing the document, or **acknowledged** when the documents really stay true: `raf docs-ok <lot> --reason "why"` (L317; the reason is required) writes a dated `docs_ok:` key on the lot, which `raf check`, `cadence lead tour` and `cadence orchestrate` all read. There is no silent third way: a lot with an open gap does not end ready in a wave.
|
|
154
156
|
|
|
155
157
|
### The librarian: `doc-keeper`
|
package/docs/orchestrate.md
CHANGED
|
@@ -64,6 +64,8 @@ refuse the resume, and a refusal writes nothing to `control.log`.
|
|
|
64
64
|
|
|
65
65
|
**Answering on a live wave (L407).** `--answer <project:lot> "…"` (from the folder that launched the wave, `--wave` when several are live) no longer waits for the wave to end: on 28-10 a lot waited about 15 minutes for its answer because another lot of the wave was still running, and `--resume` refuses a wave that is running. The answer is appended to the wave's queue (`answers.log` in `.cadence/runs/<wave>/`, one JSON line per answer and per answer taken), the command prints `<lot> : réponse déposée` and returns at once. The live wave reads the queue every 2 seconds and each time a lot finishes, applies the answer to the lot (the same state as `--resume --answer`: the answer joins the brief of its next session) and replays it as one more line of the pool, as soon as a session slot is free; a lot with no worktree never plays together with another lot of its repository. The documented form `--resume --wave <wave> --answer …` does the same when that wave is still running (it used to be refused with `tourne encore`); combined with another resume option (`--budget`, `--drop`, `--integrate`, `--continue`) it is still refused. Refused (exit 2) before anything is written: a lot that is unknown, that several projects carry, or that is not waiting for an answer (`question`); no live wave in that folder (the message gives the `--resume --wave <wave> --answer` form for a stopped wave); a wave started before L407, whose snapshot does not read the queue. `--answer` alone does not combine with `--drop`, `--stop-after-current`, `--budget`, `--dry-run`, `--continue` or lots to launch. An answer whose lot is no longer waiting when the wave reads it (dropped, or already answered by an earlier line) is discarded and the journal says so (`réponse écartée`), it is never played twice; if the wave finishes between the check and the write, the answer is withdrawn and the command says to use `--resume`. Not covered: a lot that depends (`after`) on the answered lot and was left waiting in the pool is not started by this wave when the answer makes the lot ready; `--resume` plays it.
|
|
66
66
|
|
|
67
|
+
**Integrating a lot on a live wave (L532).** `--resume --wave <wave> --integrate <project:lot>` (from the folder that launched the wave) no longer fails with `la vague … tourne encore` when that wave is still running: on 2 November the lead had a lot (L527, then L435) reviewed in its worktree and could not integrate it until the wave ended, hours later under `--continue`. The request is appended to the wave's queue (`integrates.log` in `.cadence/runs/<wave>/`, one JSON line per request and per request taken), the command prints `<lot> : intégration déposée` and returns at once. The live wave reads the queue every 2 seconds and each time a lot finishes, and replays the lot at the `integrate` step as one more line of the pool, as soon as a session slot is free (`<project>:<lot> → suspended — intégration reprise (--resume --integrate)` in the journal, or `… : revue conforme du lead hors vague, <sha>` for a lot the lead corrected and reviewed, L370). The lot is checked twice, when the command is typed and when the wave takes the request: it must be handed back at the integration step, or handed back elsewhere and reviewed `conforme` by the lead in its worktree at the last commit; a request whose lot no longer qualifies by then (already taken by an earlier line, a commit after the review, tracked file modified in the worktree) is discarded and the journal says so (`intégration écartée (…)`), it is never played twice. Refused (exit 2) before anything is written: a lot that is unknown, that several projects carry, named twice, or not eligible (same message as the resume); a wave started before L532, whose snapshot does not read the queue. Combined with another resume option (`--answer`, `--budget`, `--drop`, `--continue`) or with `--replay`, the command is still refused with `tourne encore`. If the wave finishes between the check and the write, the request is withdrawn and the command says to use `--resume`.
|
|
68
|
+
|
|
67
69
|
**Adding a lot to a live wave (L421).** A second `cadence orchestrate <project:lot>` in a repository that a live wave holds is refused (`une orchestration y est déjà en cours (<wave>, pid …)`), and the lead used to wait for the whole wave to end. The refusal now names the way out when that wave was launched from the same folder: `… ; pour y ajouter ce lot : cadence orchestrate --add <project>:<lot> --wave <wave>`. `--add <project:lot> [--wave <wave>]` (repeatable; from the folder that launched the wave, `--wave` when several are live) checks each lot like a lot given at launch (known, open, dependencies, Node, `repos:`), with three differences: a repository held **by that wave** is accepted (its dirty tree and its push guard are the wave's own), a dependency counts when it is a lot of the wave or an earlier `--add` of the same call, and the remaining budget of the wave (`budget − consumed`) must hold the budget of the lot. Then it appends the lot to the wave's queue (`adds.log` in `.cadence/runs/<wave>/`, one JSON line per request and per request taken), prints `<lot> : ajout déposé` and returns at once. The live wave reads the queue with the answers (every 2 seconds and each time a lot finishes), checks the lot again at that moment, and adds it: the lot is written in the wave state and in `wave.json`, the repository lock and push guard are taken if the wave does not hold the repository (any more), and the journal says `<lot> : ajouté à la vague <wave>`. The lot then plays as one more line of the pool as soon as a session slot is free; in a repository in `orchestrate.worktrees: true` mode with a cap above one it plays in its own worktree at once, in parallel with the lot already playing there (L527, every lot of such a repository has its own); otherwise a lot of a repository where another lot of the wave is playing waits for it (never two sessions in one repository), and plays in its own worktree only when every lot still to play in that repository does. Refused (exit 2) before anything is written, nothing queued for any lot as soon as one is refused: a lot unknown or already finished, a lot already in the wave or already queued, a dependency neither done nor in the wave, a repository held by another wave, a budget that does not fit, no live wave in that folder, several live waves without `--wave`, a wave started before L421 (its snapshot does not read the queue). `--add` combines with nothing but `--wave`. A request that no longer holds when the wave reads it (lot done meanwhile, repository taken by another wave, budget spent, `--stop-after-current` requested, usage limit, incident) is discarded and the journal says so (`ajout écarté (<cause>)`) — a request kept waiting for its repository is checked again at the moment it is taken, so a stop requested or a budget spent while it waited discards it too, and the lot never enters `wave.json`; the same goes for a request still queued when no session is left to play (`la vague a fini de jouer`) — if the wave ends between the check and the write, the command withdraws the request and says to launch the lot in a new wave. A legitimate commit of an added lot on the main branch of its repository is not an incident for a session already running in a sibling repository (L456). With `--continue`, the lots added by hand count as lots of the wave and are not drawn a second time. The case of `--resume --integrate` refused while another wave holds the repository is not part of `--add`: the resume waits for that wave (L443).
|
|
68
70
|
|
|
69
71
|
**`--goal "…"` and `goal:` — the objective of the briefs when the title is stale (L258)**: the first line of the `{{objectif}}` of every
|
|
@@ -848,7 +850,9 @@ on elsewhere, so `cadence deliver` accepts it (a repository with a lot still to
|
|
|
848
850
|
held until the wave ends). `cadence deliver` also reads the wave's own state when a lock is still held: it refuses only if the wave still has a lot running, queued or suspended **in this repository**, and goes ahead when the lots left are elsewhere (an unreadable wave state still refuses). They share a **cap on simultaneous sessions**, counted
|
|
849
851
|
across all live waves: 2 by default, `--max-sessions N` (or `CADENCE_MAX_SESSIONS=N`) to change it — give
|
|
850
852
|
the **lowest cap of the live waves applies to all of them** (L395): each wave counts ALL live sessions, whatever their slot, and waits while that
|
|
851
|
-
count has reached the lowest cap declared by any live wave (re-read at every turn: it rises again when the wave with the low cap ends), so a wave at 4 next to a wave at 3 plays 3 sessions, never « 4/3 en cours » (on 28-10 L51 waited 16 min 28 s behind it). The `en attente d'un créneau` line and the free slots (`créneaux libres`) use that common cap. The sessions the lead started by hand and declared with `cadence lead hold` (L424, [lead.md](lead.md#lead-hold-and-release)) count in that total like wave sessions, until their `release` or their time limit: the wave waits for a slot instead of starting a session over the cap, and the line names them (`2/3 en cours : w1, session du lead correctif-L12`).
|
|
853
|
+
count has reached the lowest cap declared by any live wave (re-read at every turn: it rises again when the wave with the low cap ends), so a wave at 4 next to a wave at 3 plays 3 sessions, never « 4/3 en cours » (on 28-10 L51 waited 16 min 28 s behind it). The `en attente d'un créneau` line and the free slots (`créneaux libres`) use that common cap. The sessions the lead started by hand and declared with `cadence lead hold` (L424, [lead.md](lead.md#lead-hold-and-release)) count in that total like wave sessions, until their `release` or their time limit: the wave waits for a slot instead of starting a session over the cap, and the line names them (`2/3 en cours : w1, session du lead correctif-L12`). **Fairness between waves (L531).** The sessions waiting for a slot, in every live wave, register in `waiters/` of the cadence home and a freed slot goes to the first of them, never to whoever polls first: (1) the next step of a lot already playing (implementation done, review, fix…) before the first step of a lot just drawn; (2) then the project that is highest in `priority:` (`--priority` or the `cadence.yaml` of the launch folder; a project not listed comes last); (3) then the oldest wait. A free slot with no one waiting before is taken at once. A waiter whose process died is ignored and dropped; a wait abandoned (`--drop`, `--stop-after-current`) is unregistered. The order is over the slot only: the pool and the fill (L527) are unchanged, and a lot drawn ahead of its turn simply waits for its first slot behind the steps in progress.
|
|
854
|
+
|
|
855
|
+
A session that finds no free slot **waits** (the wave is not refused; the journal says
|
|
852
856
|
`en attente d'un créneau de session depuis …`, repeated every minute, then `créneau de session obtenu après …`). A free slot is always taken, even when a request is already written (relaunching a cut review is not a wait; the request applies afterwards). Once the wait has really started (a first try found no free slot), it reads the wave's requests at every turn (L332): a `--drop` aimed at that lot ends it (the slot is never taken, no session starts, the lot is *handed back* `retiré de la vague (--drop)` like a lot not started) and a `--stop-after-current` ends it too (the lot is *suspended* `arrêt demandé (--stop-after-current)`, the wave **interrupted** and resumable); a `--drop` aimed at another lot does not. The step announced but never started is removed from the state and the lead's answer it was carrying (`pendingAnswer`, taken out of the lot when the brief was built) is put back: `--resume` replays the step with the answer in its brief, and a lot handed back by `--drop` says « la réponse du lead (« … ») n'a pas été jouée » in its outcome. When it is the relaunch of a session already run in the step (a cut review relaunched in a fresh session, L256; the formatting retry) that was waiting, the step ends `failed` with its cause (« relance en session neuve non faite : … ») and keeps what the first session left: its id, its tokens (step and wave budget), its stderr in `<n>-<kind>.first.err` (and its output in `.first.json` for the formatting retry).
|
|
853
857
|
`cadence orchestrate --waits [--since YYYY-MM-DD]` adds those waits up from the journals of `.cadence/runs/` of the launch folder — per day (cumulated wait, number of waits, waves, quota stops — one per "→ suspended — quota atteint" line of the journals, counted on the day of that line, so a wave resumed and stopped again on another day counts once on each day, not once as a wave, tokens) and per wave that waited (the longest wait, a wait that never got its slot) — to decide the cap from figures; each wait is paired with its lot (the journal line carries `project:lot`), so two lots of one wave waiting at the same time (one per repository) are each counted once, and only a wait that never got its slot is reported as such; journals from before this carry no lot and are paired per wave. it reads only, and refuses any other action in the same call. The figures of 2026-10-12 and the cost of a third slot are in `docs/mesures/attente-creneaux-2026-10-12.md`.
|
|
854
858
|
A slot or a registry entry is owned by a pid **and** its start time: a reused pid is a dead owner. Wave
|
|
@@ -962,7 +966,7 @@ orchestrate:
|
|
|
962
966
|
build: npm run build # run BEFORE the tests (L333); both results go to the reviewer, who does not redo them (optional)
|
|
963
967
|
worktreeDeps: [node_modules, .venv] # untracked dependencies that `cadence verify --gate` copies into its throwaway worktree (L348, L389; the gate runs in the main tree instead when it is clean at the sha, `verify.inPlace`, `docs/deliver.md`); absent: the `node_modules` of every level and the `.venv` are copied (never data or `.env`, L392), the other ignored folders are neither copied nor linked (L455) (optional)
|
|
964
968
|
gate: { host: build-box, install: npm ci } # play the gate (build, tests, baseline, replay alone) on another machine through ssh, with a dependency cache and a local fallback, see docs/remote-gate.md (L403); `host` alone is not `readsPlan` (both optional)
|
|
965
|
-
gate: { readsPlan: true } # a test of the gate reads the plan (`.cadence/raf.yaml`): `cadence verify --gate` no longer takes over a wave's verdict under commits that change only the plan (L444), it replays; `true` or the list of the paths of the tests that read it; absent
|
|
969
|
+
gate: { readsPlan: true } # a test of the gate reads the plan (`.cadence/raf.yaml`): `cadence verify --gate` no longer takes over a wave's verdict under commits that change only the plan (L444), it replays; `true` or the list of the paths of the tests that read it; absent: those commits are crossed unless tracked code quotes the plan's path (then it replays and says `gate.readsPlan absent`, L482); `false` is read as absent (L450, `docs/deliver.md`) (optional)
|
|
966
970
|
precheck: true # default: before the first implementation of a lot with no commit whose title cites a finding or an earlier lot, and whose title or `goal:` cites a finding (`constat`, `revue`, `review`, `audit`) or a plan id (an earlier `L<number>`, also followed by a hyphen as in `L12-L15` or `L12-bis`; any other id such as `Q-CAN5`; `project:L<number>`), a read-only Sonnet session checks whether the deliverable is already in the repository (see below); false skips it, local runs it on claude-local (Ollama) with Sonnet as fallback, for every lot with no commit whatever its title (L296); a lot created after the last code commit of the repository is not pre-checked, local or Sonnet, unless it cites a lot that already has commits (L322)
|
|
967
971
|
accept: npx vitest run -c vitest.acceptation.config.ts # accept step (see « The accept step »): the dedicated command that runs ONLY the acceptance tests of the lot (optional; without it the step is skipped with a warning)
|
|
968
972
|
maquette: true # default: UX review of the mockup between spec and accept (see « The mockup review »); false skips it
|
package/docs/qualite.md
CHANGED
|
@@ -24,7 +24,7 @@ Putting a threshold on existing code turns hundreds of findings red on day one,
|
|
|
24
24
|
threshold until it means nothing, or sprinkle `eslint-disable`. The ratchet keeps the thresholds honest instead:
|
|
25
25
|
|
|
26
26
|
1. `node scripts/qualite.mjs --init` measures once and writes `qualite-baseline.json`, a flat map sorted by key,
|
|
27
|
-
versioned with the code. A key is `tool|file|rule` (`eslint|src/
|
|
27
|
+
versioned with the code. A key is `tool|file|rule` (`eslint|src/plan.ts|max-lines`, `knip|src/audit.ts|exports`,
|
|
28
28
|
`jscpd|src/config.ts`) and its value a count. Two metrics have a `…|total` or `…|lignes` twin that sums the figures:
|
|
29
29
|
for ESLint, every key has a `…|total` (the sum of the figures measured, a file of 1397 lines records 1397, three
|
|
30
30
|
functions of cognitive complexity 463, 20 and 18 record 501), and for jscpd every file has a `…|lignes` (the lines
|
package/docs/raf.md
CHANGED
|
@@ -52,7 +52,8 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
52
52
|
|---|---|
|
|
53
53
|
| `raf --version` · `raf -v` | print the version of the package, as `cadence --version` does (L376); only as the first argument, `raf show --version` stays an unknown option |
|
|
54
54
|
| `raf init [--project name] [--prefix L] [--no-hook]` | create the plan, install the hook, add `.playwright-mcp/` to `.gitignore` |
|
|
55
|
-
| `raf add "title" [--estimate d] [--quickwin] [--visible [--screen page\|component\|journey]] [--spec] [--local] [--public "title"] [--after L2,L4] [--uses banc,passerelle] [--set field=value]… [--parent L3]` | add a lot or a sub-task, print its id (`--set`: the value of an [extension field](#extension-fields-planextras-l464) declared by `plan.extras`, refused on a sub-task; `--public`: the lot's title in the public's words, written as `public:` right after `title:`; `--screen`: the screen a visible lot creates or changes — a new `page`, a new `component` or a modified `journey` — written as `screen:` right after `visible:`, shown by `raf show`; it requires `--visible`, and `raf check` reports a `screen:` without `visible:` or with another value as an error. Only `page` triggers the full spec step of `orchestrate` (L209), never inferred from the title; a visible lot above 0.1 day with `component`, `journey` or no `screen:` plays the light mockup (L459); `--spec`: the lot changes a behaviour (something a user, an API client or another service can observe), written as `spec: true` right after `screen:`, `visible:`, `quickwin:` or `estimate:` (whichever comes last), shown by `raf show`, refused on a sub-task. Also set by the lead at planning time, never inferred from the title); `--local`: the lead edits this repository by hand for this lot, written as `local: true` right after `spec:` (or the last key before it), refused on a sub-task ([a lot of the lead](#a-lot-of-the-lead-local-l519)); `--uses`: the shared resources the lot occupies, written as `uses: [banc, passerelle]` after `after:` and before `created:` ([shared resources](#shared-resources-uses-and-resources), L251), refused on a sub-task) |
|
|
55
|
+
| `raf add "title" [--estimate d] [--quickwin] [--visible [--screen page\|component\|journey]] [--spec] [--local] [--public "title"] [--after L2,L4] [--uses banc,passerelle] [--set field=value]… [--parent L3] [--id AB-1.2 [--under AB-1]]` | add a lot or a sub-task, print its id (`--set`: the value of an [extension field](#extension-fields-planextras-l464) declared by `plan.extras`, refused on a sub-task; `--public`: the lot's title in the public's words, written as `public:` right after `title:`; `--screen`: the screen a visible lot creates or changes — a new `page`, a new `component` or a modified `journey` — written as `screen:` right after `visible:`, shown by `raf show`; it requires `--visible`, and `raf check` reports a `screen:` without `visible:` or with another value as an error. Only `page` triggers the full spec step of `orchestrate` (L209), never inferred from the title; a visible lot above 0.1 day with `component`, `journey` or no `screen:` plays the light mockup (L459); `--spec`: the lot changes a behaviour (something a user, an API client or another service can observe), written as `spec: true` right after `screen:`, `visible:`, `quickwin:` or `estimate:` (whichever comes last), shown by `raf show`, refused on a sub-task. Also set by the lead at planning time, never inferred from the title); `--local`: the lead edits this repository by hand for this lot, written as `local: true` right after `spec:` (or the last key before it), refused on a sub-task ([a lot of the lead](#a-lot-of-the-lead-local-l519)); `--uses`: the shared resources the lot occupies, written as `uses: [banc, passerelle]` after `after:` and before `created:` ([shared resources](#shared-resources-uses-and-resources), L251), refused on a sub-task) |
|
|
56
|
+
| `raf add "title" --id <id> [--under <lot>]` · `raf estimate <id> <days>` | free identifiers and flat sub-lots in a writable plan (L465, `plan.idPattern`, below). `--id` gives the lot an identifier of the project's own (`AB-1.2`) instead of the generated `L<n>`; `--under` makes it a sub-lot (`parent: <lot>` written after `title:`; the parent must exist, archived included: `lot parent inconnu : <id>`). Both are refused with `--parent` (which adds a sub-task) and `--id` without `plan.idPattern`. `raf estimate` replaces `estimate:` of a lot of any status, archived included (days > 0 written in decimal, else `estimation invalide : <value typed>`: `0x10`, `1e1`, `abc`, `.5` are refused, and the message quotes what was typed (L535); a sub-task is refused) |
|
|
56
57
|
| `raf add "title" --every <days>` · `raf did <id> ["text"]` | recurring lot: `every` (days) and `last` (last time done) fields; the lot stays `todo`, `raf now` and `session start` list it under « Récurrent » with « dû depuis N j » / « prochain dans N j »; `raf did` resets the count (spec: `docs/superpowers/specs/2026-10-04-L11-tache-recurrente.md`) |
|
|
57
58
|
| `raf screen <id> page\|component\|journey\|none` | set, replace or remove `screen:` on an existing lot, to adopt the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen) on a plan already written (only `page` adopts the [spec step](orchestrate.md#the-spec-step-of-a-lot-with-screen) in full; `component` and `journey` get the [light mockup](orchestrate.md#the-light-mockup-l459) when the lot is visible and above 0.1 day); refused on a lot without `visible:` (like `raf add --screen`) and on a sub-task; `none` removes the key and leaves `visible:` alone |
|
|
58
59
|
| `raf visible <id> on\|off` | set or remove `visible:` on an existing lot (a user-facing change: a news entry is expected, and `cadence orchestrate` adds its UX step), written right after `quickwin:`, `estimate:` or `status:` (whichever comes last); `on` on a visible lot changes nothing, `off` on a lot without the key neither; refused on a sub-task, and `off` is refused while the lot has a `screen:` (`raf screen <id> none` first: a screen is a visible change). It is the fix `raf check` points to for an open « Revue UX — » lot without `visible:` (L273) |
|
|
@@ -70,7 +71,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
70
71
|
| `raf note <id> "text"` | dated note — keep decisions next to the work |
|
|
71
72
|
| `--verbose` on `raf done`, `ux`, `review`, `note`, `move`, `public` · `raf hook post-commit --verbose` (L453) | those commands print no title of the lot they touch (nothing in the lead's context but `L12 → done` and the `plan non commité` command, which is unchanged); `--verbose` adds one line `L12 doing <full title>` (nothing for a sub-task). The post-commit hook warning `raf: L12 (doing) <title>` cuts the title at 60 characters (59 and `…` beyond; a title of 60 stays whole), `--verbose` on the hook gives it whole. `raf show`, `raf list`, `raf now` and the session tables are not concerned |
|
|
72
73
|
| `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 |
|
|
73
|
-
| `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) |
|
|
74
|
+
| `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; under `plan.idPattern` (free ids such as `AB-1`, `X3.4`: the final number says nothing of the order) every archived lot, whatever its id (`L7` included), is archived with a `place:` key, the id of the lot that preceded it in the plan as presented, archived lots included (empty: it was first), so lots archived in different runs keep the plan's order, and comes back right after that lot (`place:` is removed when the lot is reopened, and the archived lots whose `place:` named it are re-pointed to the lot that preceded it: only the reopened lot moves, to the end of the plan). `place` is thus a key cadence writes on a lot: `plan.extras.place` is refused — 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) |
|
|
74
75
|
| `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 |
|
|
75
76
|
| `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) |
|
|
76
77
|
| `raf now` | what to do next |
|
|
@@ -78,7 +79,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
78
79
|
| `raf docs-ok <lot> --reason text` | acknowledge the `docs.sync` gap of a lot (a document behind its commits, `raf check` red) when the documentation really stays true: a `docs_ok: { date, reason }` key on the lot, **the reason is required** (refused without it, for an unknown lot or for a sub-task); `raf check` and `cadence lead tour` no longer count the lot, `raf show` prints it, and `cadence orchestrate` neither calls the librarian nor holds the lot back for it. Commit the plan with it; on a read-only plan (kept by another tool) the key goes to `.cadence/docs-ok.yaml` instead, and that file is committed (L386) |
|
|
79
80
|
| `raf ignore <sha> \| "exact subject" [--reason text]` | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) or a commit that cites known lots without working on them (a lead's plan commit creating lots, a setting cited in parentheses): it then counts for none of them — not started, not active, not to review (`raf commits` omits it) — without rewriting history: a dated, reasoned line in the plan's `acknowledged:` section; a sha is exact, a subject covers every commit carrying it; exactly one commit argument, the reason goes through `--reason "text"` (`raf ignore 1a2b3c4 "tooling"` is refused with « un seul commit attendu ; la raison se passe par --reason "texte" », exit 2) |
|
|
80
81
|
| `raf check --ignored` | list the acknowledged commits with their date and reason |
|
|
81
|
-
| `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:`
|
|
82
|
+
| `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:` (native plan, `raf add --under`, L465, or 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) |
|
|
82
83
|
| `raf gantt [-o file]` | standalone Gantt page |
|
|
83
84
|
| `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 |
|
|
84
85
|
| `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) |
|
|
@@ -246,7 +247,7 @@ plan:
|
|
|
246
247
|
fiche: {} # any non-empty text
|
|
247
248
|
```
|
|
248
249
|
|
|
249
|
-
- **Declaration.** A name is lowercase letters, digits and `_`, starting with a letter, and is not a key cadence uses on a lot (`status`, `estimate`, `after`, `notes`, `tasks`, `uses`, `repos`…). `enum` is a non-empty list of distinct texts; `default` is a non-empty text, one of the `enum` values when there is one. Anything else is refused, naming the key (`cadence.yaml : plan.extras.theme.color inconnu`). `plan.extras` is refused together with a plan kept by another tool (`plan.lots`, `plan.fields`…, read-only): cadence writes no field there.
|
|
250
|
+
- **Declaration.** A name is lowercase letters, digits and `_`, starting with a letter, and is not a key cadence uses on a lot (`status`, `estimate`, `after`, `notes`, `tasks`, `uses`, `repos`, `place`…). `enum` is a non-empty list of distinct texts; `default` is a non-empty text, one of the `enum` values when there is one. Anything else is refused, naming the key (`cadence.yaml : plan.extras.theme.color inconnu`). `plan.extras` is refused together with a plan kept by another tool (`plan.lots`, `plan.fields`…, read-only): cadence writes no field there.
|
|
250
251
|
- **On the lot.** The field is a plain key of the lot, a text (`theme: ui`). A key that is **not** declared is neither read nor checked, and stays in the file as written, as do the declared ones: every `raf` write (start, note, done, title, archive…) edits the lot in place.
|
|
251
252
|
- **`raf check`.** A declared field whose value is not a non-empty text or is outside `enum` is a `bad-field`: `L12 : theme « zzz » hors de enum (infra, ui, data)`. A lot without the key is fine; with a `default` it is read with the default (nothing is written to the file, so changing the `default` changes every lot that does not carry the key).
|
|
252
253
|
- **`raf add "title" --set theme=ui`** writes the key on the new lot (before `created:`); `--set` can be repeated, the value is cut at the first `=` (`--set fiche=docs/a=b.md`). Refused, with nothing written: a field that is not declared (the message lists the declared ones; without `plan.extras`, it says so), a value outside `enum`, `--set theme` or `--set theme=` without a value, the same field twice, and `--set` with `--parent` (a sub-task has no field).
|
|
@@ -298,6 +299,23 @@ plan:
|
|
|
298
299
|
files: [frontend/public/plan-data/plan.json]
|
|
299
300
|
```
|
|
300
301
|
|
|
302
|
+
### Free identifiers and flat sub-lots (`plan.idPattern`, L465)
|
|
303
|
+
|
|
304
|
+
A plan that keeps raf's format can use identifiers of its own (`AB-1.2`, `X3.4`, `E-GL5`) next to the generated `L<n>`.
|
|
305
|
+
The project declares their shape in `cadence.yaml`, as a regular expression **without anchors** (raf anchors it):
|
|
306
|
+
|
|
307
|
+
```yaml
|
|
308
|
+
plan:
|
|
309
|
+
idPattern: '[A-Z]{1,3}-?\d+(?:\.\d+)*'
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
- The key is refused at read time when it is not a text, is empty, starts with `^` or ends with `$`, accepts the empty identifier, is not a valid expression, or uses the reserved group names `id` and `sub`; the error names `plan.idPattern`.
|
|
313
|
+
- `raf add "title" --id AB-1.2` writes that identifier. It must match the pattern in full, contain no `/` (reserved to sub-tasks) and not exist yet, archived lots included. Without `--id`, ids stay `<prefix><n+1>`, whatever the free ids in the plan.
|
|
314
|
+
- `raf add "title" --id AB-1.2 --under AB-1` writes `parent: AB-1` on the lot: a sub-lot **laid flat** in `lots:`, whose id does not start with its parent's. It keeps its own dates, notes, estimate and status; `raf check` counts its commits for the idleness of a parent in progress.
|
|
315
|
+
- Commit messages: with the key, the ids cited in a message are `<prefix>\d+` **or** the pattern, point and digits included (`AB-1.2`, not `AB-1`), with an optional sub-id after a slash: `X7/t2` is the sub-task `t2` of `X7`; any other sub-id (`X7/t1-fusion`) cites the lot `X7`. An id is not read inside a word, after a `/`, or when followed by a dot and a word character (`X3.x`); a closing dot (« see X3. ») is fine. A `<prefix>\d+` id keeps the guard it has without the key (`feat(L3-L5)` cites L3 and L5, `release-L3` cites L3); an id that comes from the pattern is stricter and is not read after a `-` or a `.`, nor before a `-` (`AB-1-x`). So `raf commits`, the review gate and `raf check` see the work of lots with free ids. Without the key nothing changes: only `<prefix>\d+` and `/t<n>` are recognised.
|
|
316
|
+
- `raf estimate <id> <days>` is the command that changes the estimate of an existing lot.
|
|
317
|
+
- A `parent:` written by hand that is not a text is ignored and reported among the lot's problems; an unknown parent is not checked.
|
|
318
|
+
|
|
301
319
|
### Regenerate the published copy on every write (`plan.onWrite`, L346)
|
|
302
320
|
|
|
303
321
|
Declaring the published file is not enough when it is generated: a hand-regenerated
|
package/docs/whats-new.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
## In two lines
|
|
6
6
|
|
|
7
|
+
- **1.42.1**: republishes 1.42.0, whose npm publication failed on two tests of the CI clone (no `main` branch, a draw-order probe that depended on timing); same features.
|
|
8
|
+
- **1.42.0**: free plan identifiers (`plan.idPattern`, `raf add --id`, `raf estimate`); `--integrate` accepted while a wave runs; fair slot sharing between waves; the gate notices code that reads the plan.
|
|
7
9
|
- **1.41.0**: richer plans (detailed states, extension fields); a light mockup step for visible lots that are not pages; `verify --sha` clears a red delivery; the quality ratchet watches its tools' config files.
|
|
8
10
|
- **1.40.0**: `docs/concepts.md`, the notions of cadence for a human; a proposed sub-task maybe already done is flagged; a warning when the memory index loaded every turn grows too big.
|
|
9
11
|
- **1.39.0**: `orchestrate --continue` fills free slots by priority, one worktree per lot when the cap allows (issue #21); waves show their cost in euros before and after.
|
|
@@ -70,6 +72,10 @@ Each is a paragraph in [docs/whats-new.md](whats-new.md) and a full section in [
|
|
|
70
72
|
|
|
71
73
|
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).
|
|
72
74
|
|
|
75
|
+
**1.42.1**: republishes 1.42.0, whose npm publication failed on two tests only (the ratchet test assumed a local `main` branch, the `--priority` probe read a timing-dependent order); no behaviour changes.
|
|
76
|
+
|
|
77
|
+
**1.42.0**: plans can use your own identifiers. `plan.idPattern` accepts free ids (`AB-1.2`) in a writable plan, with `raf add --id`, flat sub-lots under a parent with `raf add --under`, commit references that follow the pattern, and `raf estimate <id> <days>` (L465). `cadence orchestrate --resume --wave <wave> --integrate <project:lot>` is now accepted while the wave still runs: a lot reviewed `conforme` is integrated without waiting for the end of a long `--continue` wave (L532, `docs/orchestrate.md`). Waves sharing the session slots take a freed slot in a common, fair order instead of letting one `--continue` wave starve a review (L531). `cadence verify --gate` replays the gate when code quotes the plan's path while `gate.readsPlan` is absent (L482, `docs/deliver.md`). `src/cli.ts` is split into one module per family of commands, with no behaviour change (L488, L485), and `raf estimate` rejects a non-number cleanly (L535).
|
|
78
|
+
|
|
73
79
|
**1.41.0**: plans can say more without leaving cadence. `plan.statuses` declares the detailed states of a project with their allowed transitions, and `raf status <lot> <state>` writes them; `plan.extras` declares extension fields, validated and kept on every write, set with `raf add --set key=value`; both live together in one plan (L463, L464, `docs/raf.md`). A visible lot that is not a whole page (a component, a touch-up, a UX review of a screen) now gets a light mockup — the current screen and the target, the states it touches — reviewed by the `ux-reviewer` before the code, then checked for conformity after; a lot already entered with a sealed acceptance keeps its full chain (L459, `docs/spec.md`). `cadence verify --sha` green after a red delivery of the same sha records it in the deliveries journal, so the red line goes away without delivering again; it no longer stops on a local task of the lead or an unreadable plan (L435, `docs/deliver.md`). The quality ratchet watches every file its tools read (nested `.gitignore`, `vitest.config.*`, `package.json` entries, `tsconfig.json`), so a configuration change cannot loosen it unnoticed (L479, `docs/qualite.md`).
|
|
74
80
|
|
|
75
81
|
**1.40.0**: a guide for humans, `docs/concepts.md`, linked at the top of the README, explains each notion of cadence in a few sentences, in the order you meet them: plan and lot, verdicts, session and wave, the steps of a lot and who plays each, the test gate, worktree and integration, « livrable jusqu'à » (L460). In the final table and `--status --detail`, a proposed sub-task whose cited file was changed by a commit of the lot after the review that proposed it is marked `— peut-être déjà traitée (<sha>)`, so a review no longer feeds the next one with work already done (L422). `cadence session close` and `cadence lead tour` warn when the Claude Code `MEMORY.md` index, loaded at every turn, outgrows a size threshold (`memory.indexMaxBytes`), and propose to group its dated entries behind one line; `cadence config set` reads that key back like the others, so a wrong value stops with nothing written (L442). A measure of the earlier change to the passes, read on two windows of `cadence perf`, is recorded (L430).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylad/cadence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.1",
|
|
4
4
|
"description": "A small, repo-native working method: a versioned plan linked to your commits, a changelog with screenshots, session rituals and deliveries proven by their effect.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sylvain Ladoire",
|
package/skills/lead/SKILL.md
CHANGED
|
@@ -155,7 +155,7 @@ of the plan itself (section 2b), and the limits of section « Limits that always
|
|
|
155
155
|
handed back ready (refused, review not compliant after the correction passes, red delivery, blocking QA finding) is
|
|
156
156
|
not chained over: it is reported, and the chain goes on with the lots that do not depend on it.
|
|
157
157
|
|
|
158
|
-
**A lot handed back despite the escalation** (L337). When the project declares `orchestrate.escalation: { model }`, the wave itself plays one correction and one fresh review on that model before handing a lot back for a non-compliant review or red tests: do not take such a lot up by hand on a model of your own choice first, and do not replay the escalation yourself. The table says `escalade <model> : …` under the lot. A lot handed back **despite** the escalation (`rendu malgré l'escalade`) is for the human or for an arbitration (the sheet, the brief or the lot is at fault, not the model): report it with the remaining findings and go on with the other lots. `escalade … non jouée` means the lot's budget did not cover it: the lot is yours, with its findings. **Taking such a lot up** (handed back for its budget or a non-compliant review, L370): fix it in its kept worktree (the final table gives the path; a fix subagent there, under `cadence lead hold`), then run `raf review <lot> "conforme : …"` **in that worktree** and commit its plan there — the review is read from the worktree's plan, not from the main repository's, and it must come after the last work commit of the lot —, then `cadence orchestrate --resume --wave <wave> --integrate <project>:<lot>`: the program rebases, replays the checks, keeps your verdict (or makes a short review of the rebased diff) and advances the repository's branch. Do not integrate it by hand any more (L367, 26-10), and do not record that review in the main repository's plan: `--integrate` would not see it and would keep refusing (« pas rendu à l'étape d'intégration »). `escalade … session escaladée sans résultat` means the escalated session itself failed (often a model the account does not have): the model never judged the lot, fix the key or take the lot up, do not read it as the lot's fault. A line `en cours` is a lot still running; `engagée, lot rendu avant le verdict du modèle` is a lot withdrawn, failed by an error or stopped by an incident during the escalation: the model did not judge it, read the lot line for the reason. Without the key, nothing changes; the model has no default, as the account may not have the strongest one.
|
|
158
|
+
**A lot handed back despite the escalation** (L337). When the project declares `orchestrate.escalation: { model }`, the wave itself plays one correction and one fresh review on that model before handing a lot back for a non-compliant review or red tests: do not take such a lot up by hand on a model of your own choice first, and do not replay the escalation yourself. The table says `escalade <model> : …` under the lot. A lot handed back **despite** the escalation (`rendu malgré l'escalade`) is for the human or for an arbitration (the sheet, the brief or the lot is at fault, not the model): report it with the remaining findings and go on with the other lots. `escalade … non jouée` means the lot's budget did not cover it: the lot is yours, with its findings. **Taking such a lot up** (handed back for its budget or a non-compliant review, L370): fix it in its kept worktree (the final table gives the path; a fix subagent there, under `cadence lead hold`), then run `raf review <lot> "conforme : …"` **in that worktree** and commit its plan there — the review is read from the worktree's plan, not from the main repository's, and it must come after the last work commit of the lot —, then `cadence orchestrate --resume --wave <wave> --integrate <project>:<lot>`: the program rebases, replays the checks, keeps your verdict (or makes a short review of the rebased diff) and advances the repository's branch. The same command works while the wave is still running (L532): it drops the request in the wave's queue (`intégration déposée`), which takes it between two steps, so a long `--continue` wave does not hold the lot back. Do not integrate it by hand any more (L367, 26-10), and do not record that review in the main repository's plan: `--integrate` would not see it and would keep refusing (« pas rendu à l'étape d'intégration »). `escalade … session escaladée sans résultat` means the escalated session itself failed (often a model the account does not have): the model never judged the lot, fix the key or take the lot up, do not read it as the lot's fault. A line `en cours` is a lot still running; `engagée, lot rendu avant le verdict du modèle` is a lot withdrawn, failed by an error or stopped by an incident during the escalation: the model did not judge it, read the lot line for the reason. Without the key, nothing changes; the model has no default, as the account may not have the strongest one.
|
|
159
159
|
|
|
160
160
|
**The threshold.** Before every new wave, read the context of the lead session with `cadence session context`: the
|
|
161
161
|
`cadence-hud` mod publishes the context of each session, one file per session named after its id, and the command reads the
|