@sylad/cadence 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +23 -7
- package/agents/code-reviewer.md +2 -2
- package/agents/doc-keeper.md +19 -0
- package/agents/qa-reviewer.md +2 -2
- package/agents/spec-writer.md +1 -1
- package/agents/ux-reviewer.md +1 -1
- package/bin/cadence.js +9 -1
- package/dist/audit.js +14 -4
- package/dist/check.js +6 -0
- package/dist/cli.js +235 -30
- package/dist/config.js +101 -5
- package/dist/corrections.js +23 -2
- package/dist/deliver.js +78 -21
- package/dist/docsync.js +19 -3
- package/dist/git.js +16 -3
- package/dist/lead.js +37 -12
- package/dist/migrate.js +166 -0
- package/dist/orchestrate/briefs.js +28 -6
- package/dist/orchestrate/command.js +303 -34
- package/dist/orchestrate/cycle.js +720 -273
- package/dist/orchestrate/guard.js +2 -2
- package/dist/orchestrate/integrate.js +85 -0
- package/dist/orchestrate/launch.js +37 -5
- package/dist/orchestrate/mutex.js +30 -0
- package/dist/orchestrate/pool.js +74 -36
- package/dist/orchestrate/registry.js +123 -14
- package/dist/orchestrate/result.js +1 -1
- package/dist/orchestrate/snapshot.js +25 -0
- package/dist/orchestrate/spec.js +89 -5
- package/dist/orchestrate/state.js +42 -6
- package/dist/orchestrate/table.js +61 -2
- package/dist/orchestrate/timeouts.js +40 -0
- package/dist/orchestrate/worktree.js +368 -0
- package/dist/perf.js +80 -19
- package/dist/plan.js +92 -6
- package/dist/ready-free.js +109 -0
- package/dist/ref/add.js +116 -0
- package/dist/ref/check.js +338 -0
- package/dist/ref/command.js +78 -0
- package/dist/ref/corpus-render.js +83 -0
- package/dist/ref/corpus.js +201 -0
- package/dist/ref/evaluate.js +241 -0
- package/dist/ref/figures.js +179 -0
- package/dist/ref/measure.js +330 -0
- package/dist/ref/pdf.js +104 -0
- package/dist/ref/png.js +125 -0
- package/dist/ref/store.js +40 -0
- package/dist/ref/tools.js +51 -0
- package/dist/resources.js +51 -0
- package/dist/session.js +5 -2
- package/dist/state.js +60 -7
- package/dist/testgate.js +15 -0
- package/docs/cadence-hud.md +12 -0
- package/docs/cycle-vague-dark.svg +1 -1
- package/docs/cycle-vague-light.svg +1 -1
- package/docs/deliver.md +58 -3
- package/docs/fonctionnement-wave-dark.svg +1 -1
- package/docs/fonctionnement-wave-light.svg +1 -1
- package/docs/fonctionnement.md +5 -5
- package/docs/lead.md +35 -12
- package/docs/news.md +24 -1
- package/docs/orchestrate.md +230 -19
- package/docs/perf.md +13 -8
- package/docs/raf.md +89 -11
- package/docs/ref.md +316 -0
- package/docs/session.md +2 -2
- package/docs/skills.md +9 -2
- package/docs/spec.md +1 -1
- package/docs/stabilite.md +4 -4
- package/docs/whats-new.md +2 -0
- package/package.json +2 -1
- package/skills/deliver/SKILL.md +6 -1
- package/skills/lead/SKILL.md +24 -6
- package/skills/session-close/SKILL.md +1 -1
- package/skills/session-start/SKILL.md +2 -2
- package/templates/inventaire.json +96 -0
- package/templates/orchestrate/accept-back.md +1 -0
- package/templates/orchestrate/accept.md +1 -0
- package/templates/orchestrate/fiche.md +1 -0
- package/templates/orchestrate/fix-docs.md +18 -0
- package/templates/orchestrate/fix-minors.md +2 -1
- package/templates/orchestrate/fix.md +5 -2
- package/templates/orchestrate/implement.md +6 -3
- package/templates/orchestrate/maquette.md +2 -1
- package/templates/orchestrate/precheck.md +2 -1
- package/templates/orchestrate/review-recheck.md +2 -0
- package/templates/orchestrate/review-small.md +5 -1
- package/templates/orchestrate/review.md +2 -0
- package/templates/orchestrate/spec-back.md +2 -1
- package/templates/orchestrate/spec.md +4 -3
- package/templates/orchestrate/ux.md +3 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { parse } from 'yaml';
|
|
4
|
+
import { isOpen, RafError, RESOURCE_NAME, RESOURCES_BAD_LIST } from './plan.js';
|
|
5
|
+
/**
|
|
6
|
+
* Clé `resources:` du cadence.yaml le plus proche en remontant depuis le dossier parent de `root` : le premier fichier qui porte la clé
|
|
7
|
+
* (même vide) l'emporte. Aucun ne la porte : liste vide, attribuée au cadence.yaml du parent direct. Un fichier illisible ou une clé
|
|
8
|
+
* qui n'est pas une liste de noms valides est une `RafError` nommant le fichier, jamais une liste vide silencieuse.
|
|
9
|
+
*/
|
|
10
|
+
export function readResources(root) {
|
|
11
|
+
const fallback = join(dirname(root), 'cadence.yaml');
|
|
12
|
+
for (let dir = dirname(root);; dir = dirname(dir)) {
|
|
13
|
+
const file = join(dir, 'cadence.yaml');
|
|
14
|
+
if (existsSync(file) && statSync(file).isFile()) {
|
|
15
|
+
let raw;
|
|
16
|
+
try {
|
|
17
|
+
raw = parse(readFileSync(file, 'utf8'));
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
throw new RafError(`${file} illisible : ${e.message.split('\n')[0]}`);
|
|
21
|
+
}
|
|
22
|
+
if (raw != null && typeof raw === 'object' && Object.hasOwn(raw, 'resources')) {
|
|
23
|
+
const list = raw.resources ?? [];
|
|
24
|
+
if (!Array.isArray(list) || !list.every((n) => typeof n === 'string' && RESOURCE_NAME.test(n)))
|
|
25
|
+
throw new RafError(`${file} : resources ${RESOURCES_BAD_LIST}`);
|
|
26
|
+
return { file, names: [...new Set(list)] };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (dirname(dir) === dir)
|
|
30
|
+
return { file: fallback, names: [] };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Un écart par ressource d'un lot ouvert que le cadence.yaml des dossiers parents ne déclare pas. Le fichier n'est lu que si un lot ouvert
|
|
35
|
+
* porte `uses:` : un plan qui n'en porte pas donne le contrôle d'avant, quel que soit l'état du dossier parent.
|
|
36
|
+
*/
|
|
37
|
+
export function resourceIssues(lots, root) {
|
|
38
|
+
const using = lots.filter((l) => isOpen(l.status) && l.uses?.length);
|
|
39
|
+
if (using.length === 0)
|
|
40
|
+
return [];
|
|
41
|
+
let declared;
|
|
42
|
+
try {
|
|
43
|
+
declared = readResources(root);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
if (e instanceof RafError)
|
|
47
|
+
return [{ message: e.message }];
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
return using.flatMap((l) => l.uses.filter((n) => !declared.names.includes(n)).map((n) => ({ message: `${l.id} utilise la ressource « ${n} », non déclarée dans ${declared.file} (resources:)` })));
|
|
51
|
+
}
|
package/dist/session.js
CHANGED
|
@@ -81,6 +81,8 @@ export function lastActivity(lot, commits) {
|
|
|
81
81
|
const days = [commits[0]?.day, lot.started, ...lot.notes.map((n) => n.date)].filter((d) => !!d);
|
|
82
82
|
return days.length ? days.reduce(maxDay) : undefined;
|
|
83
83
|
}
|
|
84
|
+
/** « 0 commit », « 1 commit », « 3 commits » : le zéro se dit, une note de clôture sur un lot sans commit ne se lit pas comme « fini ». */
|
|
85
|
+
export const commitCount = (n) => `${n} commit${n > 1 ? 's' : ''}`;
|
|
84
86
|
const hhmm = (d) => `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
85
87
|
/**
|
|
86
88
|
* Fenêtre de la clôture : --since s'il est donné ; sinon depuis la dernière ouverture du
|
|
@@ -114,10 +116,11 @@ export function sessionStart(ctx, opts) {
|
|
|
114
116
|
if (doing.length === 0)
|
|
115
117
|
out(' (rien)');
|
|
116
118
|
for (const l of doing) {
|
|
117
|
-
const
|
|
119
|
+
const commits = all.byLot.get(l.id) ?? [];
|
|
120
|
+
const last = lastActivity(l, commits);
|
|
118
121
|
const idle = last ? diffDays(last, today) : 0;
|
|
119
122
|
const state = last ? `dernière activité ${last}${idle > opts.idle ? `, silencieux depuis ${idle} j` : ''}` : 'aucune activité';
|
|
120
|
-
out(` ${l.id} ${l.title} (${state})`);
|
|
123
|
+
out(` ${l.id} ${l.title} (${commitCount(commits.length)}, ${state})`);
|
|
121
124
|
}
|
|
122
125
|
section(out, 'En attente', gateWaiting(lots, blocked).map(({ lot, text }) => `${lot.id} ${lot.title} (${text})`));
|
|
123
126
|
const recent = period(ctx, opts.since);
|
package/dist/state.js
CHANGED
|
@@ -156,9 +156,48 @@ export function pidAlive(pid) {
|
|
|
156
156
|
export function appendDelivery(dir, day, sha) {
|
|
157
157
|
appendFileSync(join(dir, 'deliveries.log'), `${day} ${sha}\n`);
|
|
158
158
|
}
|
|
159
|
-
/**
|
|
160
|
-
export
|
|
161
|
-
|
|
159
|
+
/** Classe d'une livraison rouge : où elle a cassé. `refus` : le script a refusé avant d'agir (code 2). */
|
|
160
|
+
export const FAIL_CLASSES = ['refus', 'ci', 'deploiement', 'verification'];
|
|
161
|
+
const TAIL_SEP = ' ⏎ ';
|
|
162
|
+
const TAIL_LINES = 5;
|
|
163
|
+
const LINE_MAX = 200;
|
|
164
|
+
const REASON_MAX = 300;
|
|
165
|
+
// eslint-disable-next-line no-control-regex
|
|
166
|
+
const ANSI = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
167
|
+
// eslint-disable-next-line no-control-regex
|
|
168
|
+
const CONTROL = /[\u0000-\u001f\u007f]/g;
|
|
169
|
+
const MASK = '***';
|
|
170
|
+
const SECRET_WORDS = new Set(['token', 'secret', 'password', 'passwd', 'pwd', 'pass', 'key', 'credential', 'credentials', 'auth', 'authorization', 'signature', 'sig', 'jwt', 'cookie']);
|
|
171
|
+
// Mots sensibles qui, collés à un autre (APIKEY, ACCESSTOKEN, GITHUBTOKEN), terminent encore un nom : pas « pass », « sig » ou « auth » (BYPASS, ...).
|
|
172
|
+
const SECRET_SUFFIXES = ['token', 'secret', 'password', 'passwd', 'key', 'credential', 'credentials', 'signature', 'pwd'];
|
|
173
|
+
// Un nom de variable ou de paramètre d'URL est sensible quand l'un de ses mots (séparés par _ - . ou une majuscule) en est un, ou finit par l'un des suffixes.
|
|
174
|
+
const sensitiveName = (name) => name
|
|
175
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
176
|
+
.split(/[_.-]/)
|
|
177
|
+
.some((w) => SECRET_WORDS.has(w.toLowerCase()) || SECRET_SUFFIXES.some((x) => w.toLowerCase().endsWith(x)));
|
|
178
|
+
const TOKEN_SHAPES = /\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{16,}|xox[abprs]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16}|npm_[A-Za-z0-9]{30,})\b/g;
|
|
179
|
+
const BEARER = /\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{8,}/gi;
|
|
180
|
+
const URL_USERINFO = /(\b[a-z][a-z0-9+.-]*:\/\/)[^\s/@]+@/gi;
|
|
181
|
+
const ASSIGNMENT = /([A-Za-z][A-Za-z0-9_.-]*)=("[^"]*"|'[^']*'|[^\s&"']+)/g;
|
|
182
|
+
/** Masque ce qui ressemble à un secret (jeton connu, Bearer, identifiants d'URL, VAR=valeur ou paramètre d'URL au nom sensible) : motifs probables, pas une garantie. */
|
|
183
|
+
export const maskSecrets = (line) => line
|
|
184
|
+
.replace(BEARER, `$1 ${MASK}`)
|
|
185
|
+
.replace(URL_USERINFO, `$1${MASK}@`)
|
|
186
|
+
.replace(TOKEN_SHAPES, MASK)
|
|
187
|
+
.replace(ASSIGNMENT, (m, name) => (sensitiveName(name) ? `${name}=${MASK}` : m));
|
|
188
|
+
const clean = (line, max) => {
|
|
189
|
+
const t = maskSecrets(line.replace(ANSI, '').replace(CONTROL, ' ')).replaceAll('⏎', ' ').trim();
|
|
190
|
+
return t.length > max ? `${t.slice(0, max - 1)}…` : t;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Ajoute une livraison rouge à `deliveries-failed.log` : une ligne « jour sha [classe] raison ⏎ ligne ⏎ ligne ».
|
|
194
|
+
* La raison est la première ligne du message d'échec ; `tail` (sortie de la commande en échec) est borné à ses
|
|
195
|
+
* 5 dernières lignes non vides de 200 caractères, sans séquence ANSI, secrets probables masqués (raison comprise).
|
|
196
|
+
*/
|
|
197
|
+
export function appendFailedDelivery(dir, day, sha, reason, cls, tail = []) {
|
|
198
|
+
const kept = tail.flatMap((l) => clean(l, LINE_MAX) || []).slice(-TAIL_LINES);
|
|
199
|
+
const head = `${day} ${sha} ${cls ? `[${cls}] ` : ''}${clean(reason.split('\n')[0], REASON_MAX)}`;
|
|
200
|
+
appendFileSync(join(dir, 'deliveries-failed.log'), `${[head, ...kept].join(TAIL_SEP)}\n`);
|
|
162
201
|
}
|
|
163
202
|
export function lastDelivery(dir) {
|
|
164
203
|
const file = join(dir, 'deliveries.log');
|
|
@@ -179,15 +218,29 @@ export function readDeliveries(dir) {
|
|
|
179
218
|
return null;
|
|
180
219
|
}
|
|
181
220
|
}
|
|
182
|
-
/** Les
|
|
183
|
-
export function
|
|
221
|
+
/** Les livraisons rouges de `deliveries-failed.log`, dans l'ordre du fichier ; une ligne illisible est ignorée, un fichier absent ou illisible donne aucune livraison rouge. Lecture seule : ne crée rien. */
|
|
222
|
+
export function readFailedEntries(dir) {
|
|
184
223
|
try {
|
|
185
224
|
return readFileSync(join(dir, 'deliveries-failed.log'), 'utf8')
|
|
186
225
|
.split('\n')
|
|
187
|
-
.
|
|
188
|
-
.
|
|
226
|
+
.flatMap((line) => {
|
|
227
|
+
const [head = '', ...tail] = line.split(TAIL_SEP);
|
|
228
|
+
const m = /^(\S+)(?: (\S+))?(?: (.*))?$/.exec(head);
|
|
229
|
+
if (!m || !isDay(m[1]))
|
|
230
|
+
return [];
|
|
231
|
+
let reason = m[3] ?? '';
|
|
232
|
+
const c = /^\[(\w+)\] ?/.exec(reason);
|
|
233
|
+
const cls = c && FAIL_CLASSES.includes(c[1]) ? c[1] : null;
|
|
234
|
+
if (cls)
|
|
235
|
+
reason = reason.slice(c[0].length);
|
|
236
|
+
return [{ day: m[1], sha: m[2] ?? '', cls, reason, tail }];
|
|
237
|
+
});
|
|
189
238
|
}
|
|
190
239
|
catch {
|
|
191
240
|
return [];
|
|
192
241
|
}
|
|
193
242
|
}
|
|
243
|
+
/** Les jours de `deliveries-failed.log` (une ligne par livraison rouge), dans l'ordre du fichier. */
|
|
244
|
+
export function readFailedDeliveries(dir) {
|
|
245
|
+
return readFailedEntries(dir).map((e) => e.day);
|
|
246
|
+
}
|
package/dist/testgate.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { docSyncSet } from './docsync.js';
|
|
2
|
+
export const DEFAULT_TEST_PATTERNS = ['**/*.test.*', '**/*.spec.*', 'test/**', 'tests/**', '**/__tests__/**'];
|
|
3
|
+
/**
|
|
4
|
+
* Les fichiers de code que le lot a modifiés sans toucher le moindre fichier de test (triés), ou rien : un test touché, ou
|
|
5
|
+
* moins de `threshold` fichiers de code. Un fichier de test n'est jamais du code, même quand il correspond à `code`.
|
|
6
|
+
*/
|
|
7
|
+
export function testGateMissing(gate, files) {
|
|
8
|
+
const isTest = docSyncSet(gate.tests);
|
|
9
|
+
if ([...files].some(isTest))
|
|
10
|
+
return [];
|
|
11
|
+
const isCode = docSyncSet(gate.code);
|
|
12
|
+
const isException = gate.except.length ? docSyncSet(gate.except) : () => false;
|
|
13
|
+
const code = [...files].filter((f) => isCode(f) && !isException(f)).sort();
|
|
14
|
+
return code.length >= gate.threshold ? code : [];
|
|
15
|
+
}
|
package/docs/cadence-hud.md
CHANGED
|
@@ -27,6 +27,18 @@ ol ▮▮▮▮▮▯▯▯▯▯ 39/78 · 0 en cours · +3 cette semaine
|
|
|
27
27
|
cadence ▮▮▮▮▮▯▯▯▯▯ 10/20 · 2 en cours : L149 L153
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
The **sessions of the lead** (L276) — the ones started by hand and declared with `cadence lead hold <label>` — are drawn
|
|
31
|
+
under the waves, even when no wave is running: the slots line of `cadence orchestrate --status` and one line per session
|
|
32
|
+
(label, age, time left):
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
sessions du lead : 1 · créneaux libres : 2 sur 3
|
|
36
|
+
correctif-L12 depuis 4 min · reste 56 min
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
With a live wave and no held session the line reads `créneaux libres : 1 sur 2`; with neither, nothing. A session whose
|
|
40
|
+
time limit has run out stops counting in the slots but is not dropped (L278): a row `⚠ session du lead échue : correctif-B28 échue depuis 2 h 10` stays under the others (in red) until `cadence lead release <label>` or a renewed `lead hold`, since nothing stops the session itself. They are read from disk (`holds/` of the cadence home), like the waves.
|
|
41
|
+
|
|
30
42
|
The bar is lots done over lots done + in progress + to do (dropped and recurring lots left out); the count of lots in progress is followed by their ids (`2 en cours : L149 L153`), cut to three then `…` (`5 en cours : L1 L2 L3 …`), the count alone at zero;
|
|
31
43
|
`+N cette semaine` counts the lots created in the last 7 days (today and the six before) and is left out at zero. The source is
|
|
32
44
|
`cadence lead tour <folder> --json` (the folder a wave was launched from, otherwise the session's: a folder of projects gives one line per project, a project with no sub-project gives its own single line),
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
<line x1="981" y1="127" x2="981" y2="68" stroke="#58a6ff" stroke-width="1.2" stroke-dasharray="3 3" marker-end="url(#a)"/>
|
|
39
39
|
<text x="73" y="216" font-size="11.5" fill="#8b949e">dashed: only for a lot with a page or a behaviour to pin down (screen: page, spec: true)</text>
|
|
40
40
|
<text x="661" y="216" font-size="11.5" fill="#8b949e">each step in a fresh session, driven by cadence orchestrate</text>
|
|
41
|
-
<text x="20" y="236" font-size="11.5" fill="#8b949e">Every verdict is written to the plan (
|
|
41
|
+
<text x="20" y="236" font-size="11.5" fill="#8b949e">Every verdict is written to the plan (.cadence/raf.yaml); a delivery counts only when its checks pass.</text>
|
|
42
42
|
</svg>
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
<line x1="981" y1="127" x2="981" y2="68" stroke="#0969da" stroke-width="1.2" stroke-dasharray="3 3" marker-end="url(#a)"/>
|
|
39
39
|
<text x="73" y="216" font-size="11.5" fill="#57606a">dashed: only for a lot with a page or a behaviour to pin down (screen: page, spec: true)</text>
|
|
40
40
|
<text x="661" y="216" font-size="11.5" fill="#57606a">each step in a fresh session, driven by cadence orchestrate</text>
|
|
41
|
-
<text x="20" y="236" font-size="11.5" fill="#57606a">Every verdict is written to the plan (
|
|
41
|
+
<text x="20" y="236" font-size="11.5" fill="#57606a">Every verdict is written to the plan (.cadence/raf.yaml); a delivery counts only when its checks pass.</text>
|
|
42
42
|
</svg>
|
package/docs/deliver.md
CHANGED
|
@@ -43,6 +43,33 @@ cadence deliver # 0 delivered and verified · 1 a step failed · 2
|
|
|
43
43
|
CI can only build what was pushed); no other delivery running (a lock whose
|
|
44
44
|
process died is removed with a warning); with `ci: github`, `gh` installed and
|
|
45
45
|
logged in.
|
|
46
|
+
- **Red delivery log** (L282): a failure after the lock is taken appends one line to
|
|
47
|
+
`<git common dir>/cadence/deliveries-failed.log`: `day sha [class] reason ⏎ line ⏎ line`.
|
|
48
|
+
The class says where it broke: `refus` (the project script exited with code 2 — the
|
|
49
|
+
convention of `deliver` itself for « refused before acting »), `ci`, `deploiement`
|
|
50
|
+
(the project script with any other code, a deploy command, an unusable
|
|
51
|
+
`CADENCE_DEPLOYED_FILE`) or `verification`. The reason is the first line of the failure
|
|
52
|
+
message, followed by the **last 5 non-empty lines** (200 characters each, ANSI sequences
|
|
53
|
+
removed) of the output of the command that failed: the project script, the CI command, the
|
|
54
|
+
deploy command or the check command. A GitHub CI run or an URL check has no output of its
|
|
55
|
+
own, so no line follows. cadence now relays the output of those commands through itself
|
|
56
|
+
(same bytes on its own stdout and stderr, but the command no longer sees a terminal there,
|
|
57
|
+
so it may drop colours or progress bars). A line rewritten with `\r` (a progress bar that
|
|
58
|
+
did not drop) keeps only its last non-empty frame, not 5 frames of the bar. **Probable
|
|
59
|
+
secrets are masked** (`***`) in the reason and in those lines before the file is written:
|
|
60
|
+
known token shapes (GitHub, GitLab, npm, Slack, AWS key id, `sk-…`), `Bearer`/`Basic`
|
|
61
|
+
values, `user:password@` in a URL, and any `NAME=value` (shell variable or URL query
|
|
62
|
+
parameter) whose name has a word such as token, secret, password, key, auth, signature, or
|
|
63
|
+
ends with one of token, secret, password, pwd, key, credential, signature (`APIKEY`,
|
|
64
|
+
`ACCESSTOKEN`, `GITHUBTOKEN`, `MYSQLPWD`) (L294). The 8192 characters kept are the END of the
|
|
65
|
+
output (`OUTPUT_KEPT`, `src/deliver.ts`), so the bound cannot cut the end of a value; it can cut
|
|
66
|
+
its beginning (the name and the opening quote gone), and that case is **not handled** — the log
|
|
67
|
+
stays local. A quoted value never closed is not treated either (L294 tried, and each fix opened a
|
|
68
|
+
new leak): only closed values and unquoted values are matched.
|
|
69
|
+
This is a list of likely patterns, not a guarantee: a secret of another shape printed by a
|
|
70
|
+
command can still land in the log, which stays local (`<git common dir>/cadence/`). `cadence perf` reads classes and projects from it
|
|
71
|
+
([perf](perf.md#delivery-quality-and-the-tour-header-l227)); a line written before L282
|
|
72
|
+
has no class and counts as « non classée ».
|
|
46
73
|
- Every command is killed when it exceeds its budget (CI, `deployTimeout`, what is
|
|
47
74
|
left of `verifyTimeout`) and reported as "délai dépassé". Killed means the
|
|
48
75
|
whole tree, followed while the command runs: every 200 ms cadence lists the
|
|
@@ -90,7 +117,7 @@ cadence deliver # 0 delivered and verified · 1 a step failed · 2
|
|
|
90
117
|
5 minutes is a failure (you probably pushed another commit than the one you
|
|
91
118
|
deliver); every run must end `success`, `skipped` or `neutral`. `gh` errors
|
|
92
119
|
are retried, and reported with their cause after 5 minutes.
|
|
93
|
-
- Commands get `CADENCE_SHA`, `CADENCE_SHORT` (7 characters) and `CADENCE_BRANCH
|
|
120
|
+
- Commands get `CADENCE_SHA`, `CADENCE_SHORT` (7 characters) and `CADENCE_BRANCH` (a `deliver.script` also gets `CADENCE_DEPLOYED_FILE`, see below);
|
|
94
121
|
`${SHA}` and `${SHORT}` are replaced in `url` and `contains`.
|
|
95
122
|
- On success the lots cited by the commits since the previous delivery are
|
|
96
123
|
listed, so you can `raf done` those whose effect you have seen. With a
|
|
@@ -124,13 +151,41 @@ cadence deliver --sha 6b0d9aa -- api # an earli
|
|
|
124
151
|
`--sha` (any mode) delivers a pushed commit other than `HEAD` — for a CI that
|
|
125
152
|
builds each service only on the commit that touched it. `allowDirty` suits a
|
|
126
153
|
working tree shared by several sessions when the delivery starts from a pushed
|
|
127
|
-
sha and never from local files; without it a modified tree is refused.
|
|
154
|
+
sha and never from local files; without it a modified tree is refused. `--sha`
|
|
155
|
+
itself never reads the working tree: a tracked file modified outside the delivered
|
|
156
|
+
sha (the plan after `raf done`, say) neither refuses nor is reported, with or
|
|
157
|
+
without `allowDirty`.
|
|
158
|
+
|
|
159
|
+
**While a wave runs.** `cadence orchestrate` holds the repository: a plain `cadence deliver` is refused until every lot of the
|
|
160
|
+
repository in the wave is ready or handed back. `--sha` is the exception (L277): it is accepted while the wave runs when no
|
|
161
|
+
commit in the history of `<sha>` belongs to a lot of the wave that is not ready (running, queued, in question, suspended,
|
|
162
|
+
handed back, failed), that is when `<sha>` is « livrable jusqu'à » (the line the wave's final table prints). A ready lot is
|
|
163
|
+
then delivered while the wave runs, without waiting for the slowest lot. The refusal names the lots that hold the sha back. A lot's commits
|
|
164
|
+
are what git attributes to it in the repository (the commits that cite it) **and** what its sessions' reports noted, for the project's own
|
|
165
|
+
repository as for a neighbour one (the lot's `repos:` key): git sees at once the commit of a session still running, cut by the quota or failed,
|
|
166
|
+
which the reports only note when the session ends validly. A lot of the wave with no commit yet blocks nothing. What cannot be verified is
|
|
167
|
+
refused: an unreadable wave state, an unknown sha, or a lot that is not ready whose commits cannot be read (its project's plan is unreadable or
|
|
168
|
+
does not know the lot) while none of the commits already known holds the sha back. Push the sha alone first (`git push origin <sha>:main`).
|
|
169
|
+
|
|
170
|
+
**What is logged as delivered.** cadence no longer guesses which commit a project script deployed. Before running `deliver.script` it creates an
|
|
171
|
+
empty file and hands its path to the script in `CADENCE_DEPLOYED_FILE` (next to `CADENCE_SHA`, `CADENCE_SHORT`, `CADENCE_BRANCH`). A script
|
|
172
|
+
that deploys a commit other than `CADENCE_SHA` (it committed a `--news` timestamp itself, say) writes that sha into the file. After the script:
|
|
173
|
+
|
|
174
|
+
- the file holds a hexadecimal sha (7 to 40 characters) that resolves to a commit: that is the sha logged, and `✓ livré :` names it (« sha déclaré par le script ») — it wins over
|
|
175
|
+
HEAD, during a wave or not;
|
|
176
|
+
- the file holds anything else (not a commit, several lines, `HEAD` or a branch name — a revision would be resolved after the script, possibly at another lot's commit, so only a sha is accepted): the delivery fails with a message that says so, nothing is logged;
|
|
177
|
+
- the file is empty and the delivery runs **while a wave runs**: the log keeps `<sha>`, and if HEAD moved during the delivery a warning says
|
|
178
|
+
so (« la tête a avancé pendant la livraison ; sans sha déclaré par le script (CADENCE_DEPLOYED_FILE), le journal garde <sha> ») — the new
|
|
179
|
+
commit may be the script's, a running lot's, or a plan commit, and cadence cannot tell them apart;
|
|
180
|
+
- the file is empty and **no wave runs**: the historical rule stays, a script that committed and pushed during the delivery has delivered the new
|
|
181
|
+
HEAD, which is logged (« head moved by the script »).
|
|
128
182
|
|
|
129
183
|
cadence keeps what the script usually lacks: the preconditions (clean tree, pushed
|
|
130
184
|
`HEAD`), the lock (never two deliveries at once), the delivery log and the lots
|
|
131
185
|
delivered. Arguments are quoted for `sh`, so spaces and quotes reach the script
|
|
132
186
|
intact. If the script commits and pushes during the delivery (stamping a
|
|
133
|
-
changelog entry, say), the new `HEAD` is the sha recorded as delivered
|
|
187
|
+
changelog entry, say), outside a wave the new `HEAD` is the sha recorded as delivered; during a wave, or to be exact
|
|
188
|
+
about it anywhere, the script declares it in `CADENCE_DEPLOYED_FILE` (above). Exit code
|
|
134
189
|
0 of the script means delivered; `verify` checks, if any, run after it.
|
|
135
190
|
|
|
136
191
|
## verify: replay the effect checks, outside a delivery
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1320 350" width="1320" height="350" role="img" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif" font-size="13" fill="#e6edf3">
|
|
2
2
|
<title>A lot inside an orchestrate wave</title>
|
|
3
|
-
<desc>A lot without commits starts with a precheck (unless orchestrate.precheck is false), then by type through spec, mockup or sheet review and sealed acceptance tests, or straight to the implementation; then the code review, corrections (two passes, a third if the review converges), the UX pass if visible, once, after the compliant code review, and an automatic raf review.</desc>
|
|
3
|
+
<desc>A lot without commits whose title cites a finding or an earlier lot starts with a precheck (unless orchestrate.precheck is false), then by type through spec, mockup or sheet review and sealed acceptance tests, or straight to the implementation; then the code review, corrections (two passes, a third if the review converges), the UX pass if visible, once, after the compliant code review, and an automatic raf review.</desc>
|
|
4
4
|
<defs><marker id="arr2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#8b949e"/></marker></defs>
|
|
5
5
|
<rect width="1320" height="350" fill="#0d1117"/>
|
|
6
6
|
<rect x="20" y="140" width="120" height="52" rx="6" fill="#0d1117" stroke="#8b949e" stroke-width="1.5" stroke-dasharray="6 4"/>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1320 350" width="1320" height="350" role="img" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif" font-size="13" fill="#1f2328">
|
|
2
2
|
<title>A lot inside an orchestrate wave</title>
|
|
3
|
-
<desc>A lot without commits starts with a precheck (unless orchestrate.precheck is false), then by type through spec, mockup or sheet review and sealed acceptance tests, or straight to the implementation; then the code review, corrections (two passes, a third if the review converges), the UX pass if visible, once, after the compliant code review, and an automatic raf review.</desc>
|
|
3
|
+
<desc>A lot without commits whose title cites a finding or an earlier lot starts with a precheck (unless orchestrate.precheck is false), then by type through spec, mockup or sheet review and sealed acceptance tests, or straight to the implementation; then the code review, corrections (two passes, a third if the review converges), the UX pass if visible, once, after the compliant code review, and an automatic raf review.</desc>
|
|
4
4
|
<defs><marker id="arr2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#57606a"/></marker></defs>
|
|
5
5
|
<rect width="1320" height="350" fill="#ffffff"/>
|
|
6
6
|
<rect x="20" y="140" width="120" height="52" rx="6" fill="#ffffff" stroke="#57606a" stroke-width="1.5" stroke-dasharray="6 4"/>
|
package/docs/fonctionnement.md
CHANGED
|
@@ -12,7 +12,7 @@ box with a blue outline is where the work is kept or checked.
|
|
|
12
12
|
<img alt="A lot goes from todo to doing, receives commits that cite it, gets its review and UX verdicts, becomes done, then is delivered by cadence deliver" src="fonctionnement-lot-light.svg">
|
|
13
13
|
</picture>
|
|
14
14
|
|
|
15
|
-
The plan (`docs/plan/raf.yaml`, or the file `cadence.yaml` names) is the single source of truth:
|
|
15
|
+
The plan (`.cadence/raf.yaml`, the legacy `docs/plan/raf.yaml`, or the file `cadence.yaml` names) is the single source of truth:
|
|
16
16
|
`raf check` reports every commit that cites no lot (unless a motivated `raf ignore` covers it).
|
|
17
17
|
`hook.autostart` in `cadence.yaml` sets what the pre-commit hook does with a commit that cites a lot
|
|
18
18
|
still `todo`: `warn` (the default), `refuse`, or `start` it in the same commit. `raf done` is refused
|
|
@@ -22,10 +22,10 @@ while a sub-task is open. A delivery is finished only when its effect checks pas
|
|
|
22
22
|
|
|
23
23
|
<picture>
|
|
24
24
|
<source media="(prefers-color-scheme: dark)" srcset="fonctionnement-wave-dark.svg">
|
|
25
|
-
<img alt="A lot without commits starts with a precheck (unless disabled), then by type through spec, mockup or sheet review and sealed acceptance tests, or straight to the implementation; then the UX pass if visible, the code review, corrections and an automatic raf review" src="fonctionnement-wave-light.svg">
|
|
25
|
+
<img alt="A lot without commits whose title cites a finding or another lot starts with a precheck (unless disabled), then by type through spec, mockup or sheet review and sealed acceptance tests, or straight to the implementation; then the UX pass if visible, the code review, corrections and an automatic raf review" src="fonctionnement-wave-light.svg">
|
|
26
26
|
</picture>
|
|
27
27
|
|
|
28
|
-
A lot without commits yet starts with the **precheck** (unless `orchestrate.precheck: false`; a lot that already has commits is never pre-checked): is the deliverable already there? « Yes » hands the lot back
|
|
28
|
+
A lot without commits yet whose title cites a finding (`constat`, `revue`, `review`, `audit`) or another lot (its `goal:` counts like its title) starts with the **precheck** (unless `orchestrate.precheck: false`; a lot that already has commits, or whose title (or `goal:`) cites neither, is never pre-checked — L288): is the deliverable already there? « Yes » hands the lot back
|
|
29
29
|
to the lead; « partial » passes what exists to the implementation. Then:
|
|
30
30
|
|
|
31
31
|
- `screen: page` only: **spec** (mockup and screen sheet), **mockup review** (`ux-reviewer`, two
|
|
@@ -81,7 +81,7 @@ The budget a wave gives a lot is derived from its estimate, measured on the runs
|
|
|
81
81
|
|
|
82
82
|
| Lot | Budget |
|
|
83
83
|
|---|---|
|
|
84
|
-
| up to 0.5 day |
|
|
84
|
+
| up to 0.5 day | 600 k tokens |
|
|
85
85
|
| 0.5 to 1 day | 750 k |
|
|
86
86
|
| beyond | 570 k per day, 750 k at least |
|
|
87
87
|
| `screen: page` | 1.3 M at least, or the ordinary budget + 450 k if that is more |
|
|
@@ -96,7 +96,7 @@ The final review is always played; the budget bounds the writing passes. A `scre
|
|
|
96
96
|
|
|
97
97
|
- **Session cap.** As many sessions or subagents running at once as the cap (2 by default, set by the
|
|
98
98
|
human with `CADENCE_MAX_SESSIONS` or `--max-sessions`). The lead never raises it itself.
|
|
99
|
-
- Never two sessions in the same repository at the same time.
|
|
99
|
+
- Never two sessions in the same repository at the same time. (An experimental worktree mode, `orchestrate.worktrees: true`, would let two independent lots of one repository play at once; it is refused without `CADENCE_EXPERIMENTAL_WORKTREES=1`, lots L299–L301 to do before it is enabled — [docs/orchestrate.md](orchestrate.md#one-worktree-per-lot-orchestrateworktrees-l279).)
|
|
100
100
|
- A session cannot ask the human anything: it stops and reports, and the lead brings the question back.
|
|
101
101
|
- A sealed acceptance test that contradicts its sheet is corrected by the lead with
|
|
102
102
|
`cadence spec reseal <lot> <test> --reason "…"`, never by editing `acceptation.json`.
|
package/docs/lead.md
CHANGED
|
@@ -10,7 +10,7 @@ summaries, decides with the human, and leaves the work to fresh short sessions r
|
|
|
10
10
|
<img alt="The lead workflow in three lanes (human, lead session, orchestrate program and agents), from the tour of the projects to the close of the session" src="lead-workflow-light.svg">
|
|
11
11
|
</picture>
|
|
12
12
|
|
|
13
|
-
Dashed boxes are conditional steps: the precheck runs only for a lot without a commit yet (unless `orchestrate.precheck: false`), and the spec, sheet review and accept steps run only for a `screen: page` lot or a `spec: true` lot. The skill itself is `skills/lead/SKILL.md`; this page is its
|
|
13
|
+
Dashed boxes are conditional steps: the precheck runs only for a lot without a commit yet whose title cites a finding (`constat`, `revue`, `review`, `audit`) or another lot (unless `orchestrate.precheck: false`), and the spec, sheet review and accept steps run only for a `screen: page` lot or a `spec: true` lot. The skill itself is `skills/lead/SKILL.md`; this page is its
|
|
14
14
|
picture, not its source.
|
|
15
15
|
|
|
16
16
|
## The steps
|
|
@@ -20,9 +20,10 @@ picture, not its source.
|
|
|
20
20
|
quick wins. It stops and waits: the human picks the lots and the order (`--priority`, or the
|
|
21
21
|
`priority:` list of the parent folder's `cadence.yaml`).
|
|
22
22
|
2. **Wave.** The lead starts `cadence orchestrate <project>:<lot> … --wave <id>` in the background.
|
|
23
|
+
When a lot's title is stale, the lead does not delegate by hand: it passes `--goal "…"` (one lot per launch), or writes a `goal:` key on the lots in the plan (several lots at once) — in the native plan as `goal:`, in a plan kept by another tool (read-only) by mapping its key with `plan.fields: { goal: <their key> }` in `cadence.yaml` — which replaces the title as the objective of the briefs (L258, [orchestrate](orchestrate.md)).
|
|
23
24
|
For each lot the program runs, in fresh sessions and in this order: `precheck → spec → maquette →
|
|
24
|
-
accept → implement → review → ux`. The **precheck** applies to
|
|
25
|
-
`orchestrate.precheck: false
|
|
25
|
+
accept → implement → review → ux`. The **precheck** applies to a lot without a commit yet whose title cites a finding (`constat`, `revue`, `review`, `audit`) or another lot (unless
|
|
26
|
+
`orchestrate.precheck: false`; L288). The spec steps exist only for a lot that declares
|
|
26
27
|
a `screen:` of `page` (a visible lot that creates or changes a whole screen; `screen: component` and `screen: journey`
|
|
27
28
|
lots take the ordinary path, straight to the implementation, unless they already have a sheet) or `spec: true` (a lot without a screen
|
|
28
29
|
that still has a behaviour to pin down: `precheck → spec → fiche → accept → implement → review`);
|
|
@@ -62,7 +63,7 @@ picture, not its source.
|
|
|
62
63
|
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
|
|
63
64
|
`raf done`.
|
|
64
65
|
6. **Delivery.** Push (for a « livrable jusqu'à <sha> », that sha alone, never HEAD), then
|
|
65
|
-
`cadence deliver`. After a green delivery that changes what a page shows or what it is served, the
|
|
66
|
+
`cadence deliver`. 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
|
|
66
67
|
`qa-reviewer` agent walks the delivered pages in a real browser, reads only, and brings its blocking
|
|
67
68
|
findings to the human. It is not a gate: a finding becomes a new lot.
|
|
68
69
|
7. **Threshold and chaining.** Before every new wave the lead reads `cadence session context`. Under
|
|
@@ -79,9 +80,9 @@ picture, not its source.
|
|
|
79
80
|
|
|
80
81
|
| Rule | Why |
|
|
81
82
|
|---|---|
|
|
82
|
-
| At most **as many subagents as the session cap** running at once (2 by default; the human sets it with `CADENCE_MAX_SESSIONS`), orchestrate sessions included | `cadence orchestrate --status` and `cadence lead tour` print `créneaux libres : N sur M`, M being the configured cap; the lead never raises `--max-sessions` or `CADENCE_MAX_SESSIONS` itself |
|
|
83
|
-
| Never two sessions in the same repository at the same time; the lead does not commit where a wave runs | a wave's lock refuses `cadence deliver` while a lot still works there |
|
|
84
|
-
| A locked acceptance test that contradicts the sheet is corrected by the lead with `cadence spec reseal <lot> <test> --reason "…"` (lot in progress, outside a wave; an amended sheet is accepted once committed, every commit that touched it citing the lot, L217, never put it back to the red state; a delivered lot with an unstable test: add `--porteur <lot in progress>`, L212), then
|
|
83
|
+
| At most **as many subagents as the session cap** running at once (2 by default; the human sets it with `CADENCE_MAX_SESSIONS`), orchestrate sessions included | `cadence orchestrate --status` and `cadence lead tour` print `créneaux libres : N sur M`, M being the configured cap and the sessions the lead started by hand (`cadence lead hold`, L255) being subtracted; the lead never raises `--max-sessions` or `CADENCE_MAX_SESSIONS` itself |
|
|
84
|
+
| Never two sessions in the same repository at the same time; the lead does not commit where a wave runs (the experimental worktree mode, `orchestrate.worktrees: true`, is refused without `CADENCE_EXPERIMENTAL_WORKTREES=1` until L299–L301 are done: not a rule for the lead) | a wave's lock refuses `cadence deliver` while a lot still works there |
|
|
85
|
+
| A locked acceptance test that contradicts the sheet is corrected by the lead with `cadence spec reseal <lot> <test> --reason "…"` (lot in progress, outside a wave and with no wave that is not `done` keeping open a lot of the repository still `doing` in its plan, the resealed lot or another, e.g. a question waiting: `--resume --answer` or `--drop` first, the one error lists every wave concerned, the live one that holds the lock (to wait for) and the stopped ones, and gives for each stopped wave the single `cd <launch folder> && cadence orchestrate --resume <wave> --drop <project>:<lot> [--drop …]` to run, a `--drop` per open lot of the repository in that wave, the queued ones that are not `doing` in the plan included, since `--resume` would relaunch them and they would take the lock back, L238 L239; an amended sheet is accepted once committed, every commit that touched it citing the lot, L217, never put it back to the red state; a delivered lot with an unstable test: add `--porteur <lot in progress>`, L212), then a new `cadence orchestrate <project>:<lot>` for a lot handed back (`--resume` skips it) | never by a session, never by editing `acceptation.json` |
|
|
85
86
|
| Deliveries and the `raf ux` / `raf review` verdicts belong to the lead, one project at a time | never to a subagent, never two deliveries in parallel |
|
|
86
87
|
| 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 |
|
|
87
88
|
| A session cannot ask the human anything | it stops and reports; the lead brings the question back |
|
|
@@ -107,33 +108,55 @@ The mechanics around it (the life of a lot in the plan, a lot inside a wave, put
|
|
|
107
108
|
[fonctionnement.md](fonctionnement.md). See also the [lead tour](lead.md#lead-tour) section below and the [orchestrate](orchestrate.md#orchestrate)
|
|
108
109
|
page.
|
|
109
110
|
|
|
111
|
+
## lead hold and release
|
|
112
|
+
|
|
113
|
+
`cadence lead hold <label> [--ttl minutes]` and `cadence lead release <label>` (L255) declare the sessions the lead starts by hand — a fix subagent, a review, a QA pass launched outside an orchestrated wave. No wave carries such a session, so the slot registry could not see it: on 2026-10-23 the lead ran 4 sessions under a cap of 3 while `créneaux libres` said one slot was free. `hold` registers the session under its label for `--ttl` minutes (an integer ≥ 1, 60 by default; holding the same label again renews it instead of counting twice); `release` hands it back (releasing a label that is not held answers `aucune session « <label> » inscrite` and exits 0). A hold whose time has run out stops counting, but is not forgotten: nothing stops the session (L278), so `cadence lead tour` and `cadence orchestrate --status` print `⚠ session du lead échue : <label> (inscrite depuis 5 h 10 min, échue depuis 2 h 10 min) — la reprendre (cadence lead hold <label> --ttl <minutes>) ou l'arrêter (cadence lead release <label>)` after the slots line, and the `cadence-hud` band shows the same ⚠ row, until the lead renews the hold or releases it (an expired hold nobody touched is dropped 24 h after its expiry). The `--ttl` is therefore the maximum duration declared for the session; the `lead` skill says to renew or stop it at each ⚠. The line is not part of `--json`.
|
|
114
|
+
|
|
115
|
+
The held sessions are kept in the `holds/` folder of the cadence home (`CADENCE_HOME`, shared by every folder and every wave). While at least one is held, the line of the live waves of `cadence orchestrate --status` and of `cadence lead tour` gains a segment and subtracts them: `vagues en cours : 1 · sessions en cours : 0 · sessions du lead : 1 (correctif-L12) · créneaux libres : 0 sur 3`. A held session alone is enough to print the line (`vagues en cours : 0`). It is a display, like the rest of the slots line: a hold blocks no session and never raises the cap (a wave that is already running keeps its own slot counting), and the `lead` skill declares every hand-started session with it.
|
|
116
|
+
|
|
117
|
+
## The librarian by hand
|
|
118
|
+
|
|
119
|
+
For a lot the lead delegated by hand, `raf check` or the `dérive` column of `cadence lead tour` reporting `documentation en retard` is
|
|
120
|
+
handled before the review, as an orchestrated wave does it: the lead hands the project (absolute path) and the lot id to the
|
|
121
|
+
`doc-keeper` agent ([skills](skills.md), [news](news.md#the-librarian-doc-keeper)), in a writing session, or read-only when it
|
|
122
|
+
only wants the list of the documents to change (L306). A document outside the repository is named by the `docs.articles` key
|
|
123
|
+
of the project's `cadence.yaml`, never guessed: the lead reads that key itself and puts its entries in the brief with the absolute path of each repository (the subagent is given read access to it), because the agent examines no outside document the brief does not name. The agent proposes a lot for the plan of that repository, in the `resume` of its
|
|
124
|
+
report, and the lead decides whether to add it (`raf add` in that repository). The agent never writes to that plan.
|
|
125
|
+
|
|
110
126
|
## lead tour
|
|
111
127
|
|
|
112
128
|
`cadence lead tour [folder] [--idle 3] [--depth n] [--json]` is the lead's morning table, with no model in
|
|
113
129
|
the loop. From a parent folder (default: the current one) it takes every direct sub-folder that
|
|
114
|
-
holds `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key (a plan kept by another tool
|
|
115
|
-
included), and for each one reads the same facts as `cadence session start` — in-process, no
|
|
130
|
+
holds `.cadence/raf.yaml` or `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key (a plan kept by another tool
|
|
131
|
+
included; hidden folders, `node_modules` and `tmp/` — throwaway working copies — are skipped), and for each one reads the same facts as `cadence session start` — in-process, no
|
|
116
132
|
sub-process, no call to `claude` — then prints **one line per project**:
|
|
117
133
|
|
|
118
134
|
```
|
|
119
|
-
alpha · en cours L1 (silencieux 4j) · dérive 1 (✗ L2 a 1 commit(s) mais est encore todo) · notes : finir L1 puis livrer · prochain L3 Typo · dépôt non commité, non poussé
|
|
135
|
+
alpha · en cours L1 (1 commit, silencieux 4j) · dérive 1 (✗ L2 a 1 commit(s) mais est encore todo) · notes : finir L1 puis livrer · prochain L3 Typo · dépôt non commité, non poussé
|
|
120
136
|
beta · en cours rien · dérive aucune · notes : aucune · prochain T2 Second · dépôt propre
|
|
121
137
|
```
|
|
122
138
|
|
|
123
139
|
| Column | Content |
|
|
124
140
|
|---|---|
|
|
125
|
-
| `en cours` | ids of the lots in progress
|
|
141
|
+
| `en cours` | ids of the lots in progress, each with its number of commits (`0 commit` included: a close note on a lot without commit is not « done, `raf done` forgotten »); then `aucune activité` when it has no activity at all, `silencieux Nj` when the lot's last activity (commit, note or start) is more than `--idle` days old (default 3) |
|
|
126
142
|
| `dérive` | the gaps `raf check` reports (a lot whose commits leave a document behind, [`docs.sync`](news.md#documentation-that-follows-the-code), included), counted and the first two shown |
|
|
127
143
|
| `notes` | the notes left by the last `session close`, joined with ` / ` and cut at 120 characters |
|
|
128
144
|
| `prochain` | the first ready lot (quick wins first) — id and title cut at 60 characters; a lot whose [`gate`](raf.md#plan-file) is not met is not ready |
|
|
129
145
|
| `attend` | only when a lot waits on its `gate`: `attend L190` |
|
|
130
146
|
| `dépôt` | `non commité` (modified or untracked files), `non poussé` (commits ahead of the upstream), `livraison en cours` (live delivery lock); `propre` otherwise |
|
|
131
147
|
|
|
148
|
+
After the project lines the tour lists the **worktrees kept by waves** (L279, experimental mode, not enabled until L299–L301 are done; [`orchestrate.worktrees`](orchestrate.md#one-worktree-per-lot-orchestrateworktrees-l279)): a title, then one line per kept worktree — repository, lot, age, number of unintegrated commits, path — so that a lot handed back with unintegrated commits is never forgotten; nothing is printed when there is none (`--json`: `worktrees` on the project's row). `cadence orchestrate --status` and the final table of a wave print the same lines; the lead removes one with `cadence orchestrate --remove-worktree <project>:<lot> [--abandon-commits]`.
|
|
149
|
+
|
|
132
150
|
Before the project lines the tour prints the **perf header** (L227): one line per `⚠` of `cadence perf` for the folder, in the order of `perf`, each ending with its decision and, for a trend, the value of its quality twin (speed and quality side by side); the net-bottleneck `⚠ goulot` line is relayed as is. Nothing is printed when no `⚠` stands, and the output is then the one of a tour without header. The header is read with `perf`'s own reading of the folder (projects up to two levels, whatever `--depth`) and only in the text output: `--json` is unchanged. When `perf` cannot read the folder (no `.cadence/runs`, malformed price grid, unreadable journals or plans) the tour prints no header, reports no perf error and exits 0. The [lead skill](skills.md) reads the header before proposing the three lots and opens an improvement lot only on a trend confirmed on two windows or a net bottleneck.
|
|
133
151
|
|
|
134
|
-
When an orchestrated wave is live, one more line closes the tour (not part of `--json`): `vagues en cours : 1 · sessions en cours : 0 · créneaux libres : 1 sur 2` — the lead reads the free slots there instead of computing them (L141). The `sur M` is the cap the human set (`CADENCE_MAX_SESSIONS`, 2 by default), not a fixed 2 (L208); an invalid value falls back to 2 in this display.
|
|
152
|
+
When an orchestrated wave is live, one more line closes the tour (not part of `--json`): `vagues en cours : 1 · sessions en cours : 0 · créneaux libres : 1 sur 2` — the lead reads the free slots there instead of computing them (L141). The `sur M` is the cap the human set (`CADENCE_MAX_SESSIONS`, 2 by default), not a fixed 2 (L208); an invalid value falls back to 2 in this display. The sessions held with [`lead hold`](#lead-hold-and-release) are subtracted from the free slots, and one alone is enough to print the line (L255).
|
|
153
|
+
|
|
154
|
+
Next to it, when at least one ready lot is free, a line `prêts, ressources libres : alpha:L3 <title> · beta:L2 <title> (banc)` (L252, GitHub issue #13, 2/3) lists, for every project of the tour, the ready lots (same rule as `next`, minus the ones marked « à décider ») that can start now: a lot is free when none of the resources in its `uses:` (shown in parentheses) is held by a lot `doing` in any project of the tour or in any project that shares its resources (the projects under the folder whose `cadence.yaml` declares `resources:`, walked with that file's `depth:`, one level by default, so `P/x` and `P/grp/alpha` share the `banc` of `P/cadence.yaml` with `depth: 2` wherever the tour is launched from, even from inside one project; with no declared resource, the projects of the parent folder), nor by an unfinished lot of a live wave (a wave's lot is recognised by its repository and its id, not by the project name: a nested project `grp/alpha` matches the wave launched from `grp/` that cites it as `alpha`, and a wave launched from another folder only hides the lots of its own repositories); a lot with no `uses:` holds none and is always free. The line shows at most 3 lots per project, then `alpha +K` for the others. Like the slots line it prints while a wave is live, or as soon as a session is held with `lead hold` (L255); with neither, it stays silent. It is a display: nothing locks, `raf start` and `orchestrate` still start a lot whose resource is taken. `--json` gives the same lots, always, as a `free` array on each project (`id`, `title`, and `uses` when the lot has any; absent when the project has none).
|
|
155
|
+
|
|
156
|
+
The `lead` skill (L253, GitHub issue #13, 3/3) tells the lead to read that line again each time an agent comes back and each time a lot is blocked, before waiting: a lot it lists can start at once, with the free slots line saying a slot is free, instead of waiting behind the blocked one. The line never raises the slot cap.
|
|
135
157
|
|
|
136
158
|
`--json` prints the same content as an array of objects (`project`, `doing`, `drift`, `notes`, `waiting` (only when a lot waits on its `gate`),
|
|
159
|
+
`free` (L252, above),
|
|
137
160
|
`next`, `repo`, and `error` when the project could not be read), plus `progress` — `{ done, doing, todo, added7 }`,
|
|
138
161
|
the lots done / in progress / to do (dropped and recurring lots left out) and those created in the last 7 days (L149), which
|
|
139
162
|
[`cadence-hud`](../plugins/cadence-hud/README.md) draws as one progress bar per project. Projects come in the order of the
|
package/docs/news.md
CHANGED
|
@@ -150,10 +150,33 @@ 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
|
+
### The librarian: `doc-keeper`
|
|
154
|
+
|
|
155
|
+
`docs.sync` says *that* a document is behind; the `doc-keeper` agent (L306, `agents/doc-keeper.md`, generic: it knows no
|
|
156
|
+
folder and no project) says *which* passage and writes it. Given a repository and a lot, it reads the diff of the lot
|
|
157
|
+
(the commits that cite it), draws the map of the project's documents (the `docs.sync` rules, the README, the pages of
|
|
158
|
+
`docs/`, the CHANGELOG, the news entries, the architecture document), lists the documents the change makes false or leaves
|
|
159
|
+
out, then:
|
|
160
|
+
|
|
161
|
+
- **writing session**: updates them and commits with explicit paths and `docs(<lot>): …`;
|
|
162
|
+
- **read-only session** (no write tool, or a brief that says so): changes nothing and lists, document by document, the
|
|
163
|
+
passage to write.
|
|
164
|
+
|
|
165
|
+
`cadence orchestrate` calls it by itself when `docs.sync` reports a document behind the commits of the lot, **before the
|
|
166
|
+
review** (see « Librarian before the review » in [orchestrate](orchestrate.md)); the lead calls it by hand otherwise
|
|
167
|
+
([lead](lead.md)).
|
|
168
|
+
|
|
169
|
+
A document **outside the repository** is the librarian's only when the configuration names it, with the key that already
|
|
170
|
+
names the project's article elsewhere, `docs.articles` (see below; absent by default): « this project is also described
|
|
171
|
+
by this file of that repository ». The librarian reads that file (its repository is opened to the session for reading, and
|
|
172
|
+
the brief gives its absolute path) and, when the change makes it false, **proposes a lot** for the plan of that repository
|
|
173
|
+
(a title, then the file, the passage and the sentence to change) in the `resume` of its report, starting with
|
|
174
|
+
« lot proposé ». It never edits that file and never writes to that plan: the lead decides whether the lot enters it.
|
|
175
|
+
|
|
153
176
|
### An article elsewhere that follows the delivery
|
|
154
177
|
|
|
155
178
|
A case study that lives in a neighbouring repository (the project's article on a showcase site) goes stale
|
|
156
|
-
the same way. `docs.articles` names it:
|
|
179
|
+
the same way. `docs.articles` names it (`repo` absolute, or relative to the project):
|
|
157
180
|
|
|
158
181
|
```yaml
|
|
159
182
|
docs:
|