@sylad/cadence 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +40 -32
- package/agents/acceptance-writer.md +2 -1
- package/bin/cadence.js +4 -2
- package/dist/cli.js +33 -18
- package/dist/corrections.js +10 -1
- package/dist/orchestrate/accept.js +26 -9
- package/dist/orchestrate/baseline.js +66 -0
- package/dist/orchestrate/command.js +49 -18
- package/dist/orchestrate/continue.js +10 -2
- package/dist/orchestrate/cycle.js +272 -27
- package/dist/orchestrate/guard.js +13 -3
- package/dist/orchestrate/launch.js +1 -1
- package/dist/orchestrate/schemas.js +2 -0
- package/dist/orchestrate/spec.js +14 -0
- package/dist/orchestrate/table.js +11 -0
- package/dist/orchestrate/waits.js +120 -0
- package/dist/plan.js +3 -3
- package/package.json +1 -1
- package/skills/lead/SKILL.md +12 -9
- package/templates/acceptation-runner.mjs +15 -3
- package/templates/orchestrate/accept-back.md +1 -0
- package/templates/orchestrate/accept.md +1 -0
- package/templates/orchestrate/fix-minors.md +1 -0
- package/templates/orchestrate/fix.md +2 -0
- package/templates/orchestrate/implement.md +2 -0
- package/templates/orchestrate/spec-back.md +1 -0
- package/templates/orchestrate/spec.md +2 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { isDay, toDay } from '../dates.js';
|
|
4
|
+
import { RunStore } from './state.js';
|
|
5
|
+
/** « 4 min 27 s », « 45 s », « 1 h 02 min 03 s » (la forme de `duration` et des lignes du journal) en millisecondes ; null si illisible. */
|
|
6
|
+
export function parseWaitMs(text) {
|
|
7
|
+
const m = /^(?:(\d+) h )?(?:(\d+) min )?(\d+) s$/.exec(text.trim());
|
|
8
|
+
if (!m)
|
|
9
|
+
return null;
|
|
10
|
+
return ((Number(m[1] ?? 0) * 60 + Number(m[2] ?? 0)) * 60 + Number(m[3])) * 1000;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Les attentes d'un créneau d'un journal : comptées à l'obtention (« obtenu après … »), au jour de cette ligne ; `unfinished` = attentes commencées sans créneau obtenu.
|
|
14
|
+
* Chaque attente est appariée à son lot (« <projet>:<lot> · … ») : deux lots d'une même vague (un par dépôt, runPool) attendent en même temps. Une ligne sans lot (journal d'avant) est celle de la vague entière.
|
|
15
|
+
*/
|
|
16
|
+
export function parseWaits(journal) {
|
|
17
|
+
const waits = [];
|
|
18
|
+
let unfinished = 0;
|
|
19
|
+
const state = new Map(); // « depuis » de la dernière ligne d'attente : une ligne dont il ne progresse pas (recule, ou « depuis 0 s » de nouveau) est celle d'une nouvelle attente
|
|
20
|
+
for (const line of journal.split('\n')) {
|
|
21
|
+
const m = /^(\S+) (?:(\S+) · )?(?:en attente d'un créneau de session depuis (.+?) \(|créneau de session obtenu après (.+?) d'attente)/.exec(line);
|
|
22
|
+
if (!m)
|
|
23
|
+
continue;
|
|
24
|
+
const key = m[2] ?? '';
|
|
25
|
+
let s = state.get(key);
|
|
26
|
+
if (!s)
|
|
27
|
+
state.set(key, (s = { open: false, since: 0 }));
|
|
28
|
+
if (m[4] === undefined) {
|
|
29
|
+
const ms = parseWaitMs(m[3]) ?? 0;
|
|
30
|
+
if (s.open && ms <= s.since)
|
|
31
|
+
unfinished++;
|
|
32
|
+
s.open = true;
|
|
33
|
+
s.since = ms;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const ms = parseWaitMs(m[4]);
|
|
37
|
+
const at = new Date(m[1]);
|
|
38
|
+
if (ms !== null && !Number.isNaN(at.getTime()))
|
|
39
|
+
waits.push({ day: toDay(at), ms });
|
|
40
|
+
s.open = false;
|
|
41
|
+
}
|
|
42
|
+
for (const s of state.values())
|
|
43
|
+
if (s.open)
|
|
44
|
+
unfinished++;
|
|
45
|
+
return { waits, unfinished };
|
|
46
|
+
}
|
|
47
|
+
/** Les arrêts par le quota d'un journal (« … → suspended — quota atteint », cycle.ts), au jour de la ligne : le statut final de wave.json ne les garde pas, `--resume` le remet à `running` puis `done`. */
|
|
48
|
+
export function parseQuotaStops(journal) {
|
|
49
|
+
const days = [];
|
|
50
|
+
for (const line of journal.split('\n')) {
|
|
51
|
+
const m = /^(\S+) \S+ → suspended — quota atteint$/.exec(line);
|
|
52
|
+
const at = m ? new Date(m[1]) : null;
|
|
53
|
+
if (at && !Number.isNaN(at.getTime()))
|
|
54
|
+
days.push(toDay(at));
|
|
55
|
+
}
|
|
56
|
+
return days;
|
|
57
|
+
}
|
|
58
|
+
/** Attente de créneau par vague et par jour, lue dans les `journal.log` et `wave.json` de `.cadence/runs/<vague>/` du dossier de lancement ; `since` écarte les vagues d'avant ce jour (début de l'identifiant). */
|
|
59
|
+
export function readWaits(launchDir, since) {
|
|
60
|
+
const base = RunStore.runsDir(launchDir);
|
|
61
|
+
const ids = existsSync(base) ? readdirSync(base).filter((n) => existsSync(join(base, n, 'wave.json'))).sort() : [];
|
|
62
|
+
const waves = [];
|
|
63
|
+
const days = new Map();
|
|
64
|
+
const dayOf = (day) => {
|
|
65
|
+
let d = days.get(day);
|
|
66
|
+
if (!d)
|
|
67
|
+
days.set(day, (d = { day, totalMs: 0, count: 0, waves: 0, quotaHits: 0, consumed: 0 }));
|
|
68
|
+
return d;
|
|
69
|
+
};
|
|
70
|
+
for (const id of ids) {
|
|
71
|
+
const idDay = id.slice(0, 10);
|
|
72
|
+
if (!isDay(idDay) || (since && idDay < since))
|
|
73
|
+
continue; // un dossier qui n'est pas une vague datée (essai à la main) n'a pas de jour
|
|
74
|
+
let meta = {};
|
|
75
|
+
try {
|
|
76
|
+
meta = JSON.parse(readFileSync(join(base, id, 'wave.json'), 'utf8'));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// wave.json illisible : la vague compte sans jetons
|
|
80
|
+
}
|
|
81
|
+
const journalFile = join(base, id, 'journal.log');
|
|
82
|
+
const journal = existsSync(journalFile) ? readFileSync(journalFile, 'utf8') : '';
|
|
83
|
+
const { waits, unfinished } = parseWaits(journal);
|
|
84
|
+
waves.push({ id, totalMs: waits.reduce((s, w) => s + w.ms, 0), count: waits.length, maxMs: Math.max(0, ...waits.map((w) => w.ms)), unfinished, status: meta.status ?? '?', consumed: meta.consumed ?? 0 });
|
|
85
|
+
const d = dayOf(idDay);
|
|
86
|
+
d.waves++;
|
|
87
|
+
d.consumed += meta.consumed ?? 0;
|
|
88
|
+
for (const day of parseQuotaStops(journal))
|
|
89
|
+
dayOf(day).quotaHits++;
|
|
90
|
+
for (const w of waits) {
|
|
91
|
+
const dw = dayOf(w.day);
|
|
92
|
+
dw.totalMs += w.ms;
|
|
93
|
+
dw.count++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { waves, days: [...days.values()].sort((a, b) => a.day.localeCompare(b.day)) };
|
|
97
|
+
}
|
|
98
|
+
function duration(ms) {
|
|
99
|
+
const s = Math.round(ms / 1000);
|
|
100
|
+
if (s < 60)
|
|
101
|
+
return `${s} s`;
|
|
102
|
+
const min = Math.floor(s / 60);
|
|
103
|
+
return min < 60 ? `${min} min ${String(s % 60).padStart(2, '0')} s` : `${Math.floor(min / 60)} h ${String(min % 60).padStart(2, '0')} min ${String(s % 60).padStart(2, '0')} s`;
|
|
104
|
+
}
|
|
105
|
+
/** Deux tableaux : par jour, puis par vague (celles qui ont attendu). */
|
|
106
|
+
export function renderWaits(r) {
|
|
107
|
+
if (r.waves.length === 0)
|
|
108
|
+
return ['aucune vague dans ce dossier'];
|
|
109
|
+
const lines = ['attente de créneau de session, par jour', ' jour attente cumulée attentes vagues quota atteint jetons'];
|
|
110
|
+
for (const d of r.days)
|
|
111
|
+
lines.push(` ${d.day} ${duration(d.totalMs).padEnd(16)} ${String(d.count).padStart(8)} ${String(d.waves).padStart(6)} ${String(d.quotaHits).padStart(13)} ${d.consumed}`);
|
|
112
|
+
const total = r.days.reduce((s, d) => s + d.totalMs, 0);
|
|
113
|
+
lines.push(` total ${duration(total)}`, '', 'par vague (celles qui ont attendu)');
|
|
114
|
+
const waited = r.waves.filter((w) => w.count > 0 || w.unfinished > 0).sort((a, b) => b.totalMs - a.totalMs);
|
|
115
|
+
if (waited.length === 0)
|
|
116
|
+
lines.push(' aucune');
|
|
117
|
+
for (const w of waited)
|
|
118
|
+
lines.push(` ${w.id} ${duration(w.totalMs).padEnd(16)} ${w.count} attente(s), la plus longue ${duration(w.maxMs)}${w.unfinished ? `, ${w.unfinished} sans créneau obtenu` : ''} [${w.status}]`);
|
|
119
|
+
return lines;
|
|
120
|
+
}
|
package/dist/plan.js
CHANGED
|
@@ -437,15 +437,15 @@ export class Plan {
|
|
|
437
437
|
const keys = node.items.map((p) => String(p.key?.value ?? p.key));
|
|
438
438
|
node.items.splice(keys.indexOf('visible') + 1, 0, this.doc.createPair('screen', screen));
|
|
439
439
|
}
|
|
440
|
-
/** Pose (true) ou retire (false) le `spec:` d'un lot existant ; `screen
|
|
440
|
+
/** Pose (true) ou retire (false) le `spec:` d'un lot existant ; `screen: page` l'implique, donc on ne retire pas la spec d'un lot à page (`component` et `journey` suivent le chemin ordinaire depuis L209 : leur `spec: true` est la spec back). Renvoie false quand `screen: page` rendait la pose sans objet. */
|
|
441
441
|
setSpec(ref, on) {
|
|
442
442
|
this.writable();
|
|
443
443
|
if (ref.includes('/'))
|
|
444
444
|
throw new RafError(`la spec se pose sur un lot, pas une sous-tâche : ${ref}`);
|
|
445
445
|
const node = this.lotNode(ref);
|
|
446
|
-
if (this.lot(ref).screen) {
|
|
446
|
+
if (this.lot(ref).screen === 'page') {
|
|
447
447
|
if (!on)
|
|
448
|
-
throw new RafError(`${ref} a screen: — retirer d'abord screen: (raf screen ${ref} none), l'écran implique la spec`);
|
|
448
|
+
throw new RafError(`${ref} a screen: page — retirer d'abord screen: (raf screen ${ref} none), l'écran implique la spec`);
|
|
449
449
|
return false;
|
|
450
450
|
}
|
|
451
451
|
if (!on) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylad/cadence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "A small, repo-native working method: a versioned plan linked to your commits, a changelog with screenshots, session rituals and deliveries proven by their effect.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sylvain Ladoire",
|
package/skills/lead/SKILL.md
CHANGED
|
@@ -14,13 +14,16 @@ commands (its CLAUDE.md names them), never with `raf start|done|note|ux|review`.
|
|
|
14
14
|
|
|
15
15
|
## Limits that always apply
|
|
16
16
|
|
|
17
|
-
- **At most
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
- **At most as many subagents running at once as the session cap, 2 unless the human set another.** Queue
|
|
18
|
+
the rest. The cap is the human's setting (`CADENCE_MAX_SESSIONS`, or `--max-sessions` on a wave; 2 by
|
|
19
|
+
default, shared by every wave). The sessions of an orchestrated wave (section 2b) count toward this limit:
|
|
20
|
+
orchestrate sessions plus subagents never exceed the cap. A wave already caps itself; before starting a
|
|
21
|
+
subagent, check `cadence orchestrate --status`, and while a wave runs, start none beyond the free slots. Read the cap and the
|
|
22
|
+
free slots, do not compute them: `cadence orchestrate --status` and `cadence lead tour` print
|
|
23
|
+
`créneaux libres : N sur M` (M is the configured cap; the registry subtracts, for each live wave, min(cap,
|
|
24
|
+
repositories it still holds) from the M slots; no line from `lead tour` means no live wave, so all M are free).
|
|
25
|
+
Never raise `--max-sessions` or `CADENCE_MAX_SESSIONS` yourself to speed a wave up: raising the cap is a decision of
|
|
26
|
+
the human (a trial at 3 was decided on 2026-10-12); if the slots are the bottleneck, say so and let them decide.
|
|
24
27
|
- **Never two subagents in the same repository at the same time**, and the lead does not commit in a
|
|
25
28
|
repository where a subagent is working.
|
|
26
29
|
- **Deliveries and `raf ux` / `raf review` verdicts are done by the lead, one project at a time** —
|
|
@@ -92,7 +95,7 @@ and the project declares how to see its app (`orchestrate.ux` in `cadence.yaml`)
|
|
|
92
95
|
(Opus, the `code-reviewer` agent; Sonnet, with no pass on the minor findings, for a lot whose estimate is
|
|
93
96
|
≤ 0.25 d — `orchestrate.review` in `cadence.yaml`; a blocker or major finding still brings a correction and an
|
|
94
97
|
Opus review), a correction in a new session if the review is not compliant (two
|
|
95
|
-
passes at most) — and records `raf review` itself when the code review is compliant. A lot with `spec: true` (no screen) goes `spec → fiche → accept` before the code: the `code-reviewer` reviews its back-end sheet, and the hypotheses of the final sheet come back in the table as « hypothèse de la fiche » lines, which the lead relays to the human. The state is in
|
|
98
|
+
passes at most, a third when the review converges: blocking + major findings down from one review to the next and only one left) — and records `raf review` itself when the code review is compliant. A lot with `spec: true` (no screen) goes `spec → fiche → accept` before the code: the `code-reviewer` reviews its back-end sheet, and the hypotheses of the final sheet come back in the table as « hypothèse de la fiche » lines, which the lead relays to the human. The state is in
|
|
96
99
|
`.cadence/runs/<wave>/`, not in this conversation. `@haiku` only when the human writes it, for a
|
|
97
100
|
mechanical lot. Exit code: 0 all ready · 1 some lots handed back · 2 refused before acting · 3 wave
|
|
98
101
|
suspended (budget or usage limit; `--resume --budget …` continues).
|
|
@@ -100,7 +103,7 @@ suspended (budget or usage limit; `--resume --budget …` continues).
|
|
|
100
103
|
What the orchestrator does **not** do, and stays with the lead: choose the lots (with the human), bring the
|
|
101
104
|
questions back (`--resume --answer <project>:<lot> "…"`), look at the UX reviewer's captures and record
|
|
102
105
|
`raf ux` (the orchestrator reports its verdict, it never records it; who decides it is the `lead.ux` setting, section 2c), re-verify (section 3, point 2),
|
|
103
|
-
`raf done`, push and deliver. **A locked acceptance test that contradicts the sheet** (the lot is handed back with `verrou des tests d'acceptation`, or the session's question says its own test is wrong, e.g. a regex that rejects a sentence the sheet allows) has a path, and it is the lead's, never a session's: correct the test in the working tree, **the sheet
|
|
106
|
+
`raf done`, push and deliver. **A locked acceptance test that contradicts the sheet** (the lot is handed back with `verrou des tests d'acceptation`, or the session's question says its own test is wrong, e.g. a regex that rejects a sentence the sheet allows) has a path, and it is the lead's, never a session's: correct the test in the working tree (the sheet is left as it is; if it was amended, **commit it first, citing the lot**: an amended sheet whose every commit cites the lot is accepted, L217, never put it back to the red state), then `cadence spec reseal <lot> <test> --reason "why it contradicts the sheet"` (outside any wave): the program redoes the fingerprint of that one test, writes the reason in `acceptation.json` (`resceaux`) and as a note of the lot, commits both, and the lock keeps everything else. For a **delivered** lot (`vert: true`) whose contract does not change but whose test is unstable, add `--porteur <the lot in progress that carries the fix>`: the commit and the note cite that lot, `vert` stays true (L212). It refuses a lot that is neither in progress nor delivered, a sheet with uncommitted changes or touched by a commit that does not cite the lot, and any other listed test touched, and during a wave of that repository. It is atomic (a refused commit restores `acceptation.json` and the test), with a plan kept read-only by another tool it leaves the plan untouched and says a note is to be added by hand, with an untracked plan or one outside the repository it writes the note but does not commit it (`plan à commiter`: do not add it again), and if the commit of the note is refused the reseal stays done and the error says to commit the plan by hand. Do it **between two sessions**, never inside one: a resealed commit that belongs to a session of the wave is not adopted (still a lock violation), and sessions are forbidden the command. Then continue the lot: `--resume --answer` when it waits on a `question`; a new wave `cadence orchestrate <project>:<lot>` when it was handed back (`--resume` skips finished lots). Minor findings and proposed sub-tasks come back in the table: adding them to
|
|
104
107
|
the plan is the lead's decision. A minor finding becomes a lot of its own only if it describes an observable bug (a wrong output, a crash, a measured regression); otherwise it stays a note of the originating lot, so that a review never feeds the next one. If an orchestrated wave is running in a repository, do not commit there
|
|
105
108
|
and do not deliver it (`cadence deliver` refuses).
|
|
106
109
|
|
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
// vitest.acceptation.config.ts (commande dédiée, orchestrate.accept)
|
|
7
7
|
// test: { include: acceptationOnly() } (une LISTE de lanceurs : chacun ne voit que les fichiers sous sa racine, cf. acceptationOnly)
|
|
8
8
|
//
|
|
9
|
+
// playwright.config.ts testMatch: acceptationE2e() (suite e2e, lancée APRÈS le build : une acceptation `"e2e": true`, navigateur et dist requis)
|
|
10
|
+
//
|
|
9
11
|
// Un acceptation.json à `vert: false` (tests rouges du lot, pas encore implémenté) exclut ses fichiers du lanceur du projet et de la CI ;
|
|
10
12
|
// le cycle pose `vert: true` quand la commande dédiée passe, et les fichiers rejoignent alors le lanceur ordinaire.
|
|
13
|
+
// Exception (L203) : une acceptation `"e2e": true` ne le rejoint JAMAIS (sans dist ni Chromium elle le rendrait rouge) : elle reste exclue, verte ou non, et va dans la suite e2e.
|
|
11
14
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
12
15
|
import { join, posix } from 'node:path';
|
|
13
16
|
|
|
@@ -22,8 +25,7 @@ const read = (file) => {
|
|
|
22
25
|
|
|
23
26
|
const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
export function acceptationIgnored(root = process.cwd(), dir = 'docs/specs', format = 'glob') {
|
|
28
|
+
function collect(root, dir, format, keep) {
|
|
27
29
|
const base = join(root, dir);
|
|
28
30
|
if (!existsSync(base)) return [];
|
|
29
31
|
const out = [];
|
|
@@ -37,13 +39,23 @@ export function acceptationIgnored(root = process.cwd(), dir = 'docs/specs', for
|
|
|
37
39
|
}
|
|
38
40
|
for (const file of files) {
|
|
39
41
|
const doc = read(join(folder, file));
|
|
40
|
-
if (!doc || doc
|
|
42
|
+
if (!doc || !keep(doc)) continue;
|
|
41
43
|
for (const f of doc.fichiers) if (typeof f === 'string') out.push(format === 'regex' ? `${escape(f)}$` : f);
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
return out;
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
/** Fichiers des acceptation-<lot>.json (ou acceptation.json) à `vert: false`, ou à `e2e: true` (verts ou non), sous `<root>/<dir>/*` (un fichier par lot : plusieurs par écran), relatifs à `root` ; `format: 'regex'` pour testPathIgnorePatterns. */
|
|
50
|
+
export function acceptationIgnored(root = process.cwd(), dir = 'docs/specs', format = 'glob') {
|
|
51
|
+
return collect(root, dir, format, (doc) => doc.vert !== true || doc.e2e === true);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Fichiers des acceptations `e2e: true` (vertes ou rouges) sous `<root>/<dir>/*`, relatifs à `root` : le `testMatch` de la suite e2e, lancée après le build. */
|
|
55
|
+
export function acceptationE2e(root = process.cwd(), dir = 'docs/specs', format = 'glob') {
|
|
56
|
+
return collect(root, dir, format, (doc) => doc.e2e === true);
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
/**
|
|
48
60
|
* Fichiers de l'acceptation.json donné (par défaut celui de `CADENCE_ACCEPTATION`, posé par le cycle) : l'`include` de la config dédiée.
|
|
49
61
|
* Avec une liste de lanceurs (`orchestrate.accept` en liste), chaque lanceur ne prend que les fichiers sous SA racine, le dossier de son
|
|
@@ -7,6 +7,7 @@ Rules:
|
|
|
7
7
|
- Every test must FAIL for now, because the feature is missing (not because of a typo or a broken setup): the program runs the dedicated command and refuses tests that pass.
|
|
8
8
|
- Do not touch the sheet, the plan or any application code. Do not push, deliver, run `raf done`, `raf ux` or `raf review`.
|
|
9
9
|
- Commit with explicit paths (never `git add -A` or `commit -a`), ONE commit citing the lot (`test({{lot}}): acceptation R1–Rn (rouge)`) with the test files, the acceptation file and the runner configuration, and leave the working tree clean. The program seals the acceptation file afterwards (it adds the fingerprint of each test file): never write `empreintes` or `vert: true` yourself.
|
|
10
|
+
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|
|
10
11
|
Decide minor interpretation questions yourself and list each one under "choix" with the alternative you did not take. Leave "questions" empty rather than writing a placeholder such as "no question".
|
|
11
12
|
{{commits}}
|
|
12
13
|
{{reponse}}
|
|
@@ -7,6 +7,7 @@ Rules:
|
|
|
7
7
|
- Every test must FAIL for now, because the feature is missing (not because of a typo or a broken setup): the program runs the dedicated command and refuses tests that pass.
|
|
8
8
|
- Do not touch the sheet, the mockup, the plan or any application code. Do not push, deliver, run `raf done`, `raf ux` or `raf review`.
|
|
9
9
|
- Commit with explicit paths (never `git add -A` or `commit -a`), ONE commit citing the lot (`test({{lot}}): acceptation R1–Rn (rouge)`) with the test files, the acceptation file and the runner configuration, and leave the working tree clean. The program seals the acceptation file afterwards (it adds the fingerprint of each test file): never write `empreintes` or `vert: true` yourself.
|
|
10
|
+
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|
|
10
11
|
Decide minor interpretation questions yourself and list each one under "choix" with the alternative you did not take. Leave "questions" empty rather than writing a placeholder such as "no question".
|
|
11
12
|
{{commits}}
|
|
12
13
|
{{reponse}}
|
|
@@ -12,6 +12,7 @@ Fix each minor finding that is right, and nothing else. Rules: test first; commi
|
|
|
12
12
|
its tests pass, with explicit paths (never `git add -A` or `commit -a`), and a message that cites the
|
|
13
13
|
lot (`fix({{lot}}): …`); run the project's full test suite and build before reporting; do not push,
|
|
14
14
|
deliver, run `raf done`, `raf ux` or `raf review`. Read the project's CLAUDE.md first.
|
|
15
|
+
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|
|
15
16
|
Do not stop to ask. A finding that is wrong, or not worth its change, is not fixed: list it under
|
|
16
17
|
"choix" in your report with the reason — the reviewer re-reads it and the lead sees it. Leave
|
|
17
18
|
"questions" empty. Making no commit at all is a valid outcome when every finding is rejected.
|
|
@@ -12,6 +12,7 @@ Rules: test first; commit each fix as soon as its tests pass, with explicit path
|
|
|
12
12
|
`git add -A` or `commit -a`), and a message that cites the lot (`fix({{lot}}): …`); run the project's
|
|
13
13
|
full test suite and build before reporting; do not push, deliver, run `raf done`, `raf ux` or
|
|
14
14
|
`raf review`. Read the project's CLAUDE.md first.
|
|
15
|
+
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|
|
15
16
|
If a finding is wrong, say so under "choix" with the reason. Ask (under "questions") only when a fix needs a decision that passes the rule below.
|
|
16
17
|
A question is legitimate only if it names what it would change: the scope of the lot, the architecture, or a costly rollback (data, production, public API);
|
|
17
18
|
otherwise it is a choice: decide, write the alternative you did not take, go on. Minor choices to settle yourself:
|
|
@@ -19,6 +20,7 @@ a spacing, colour or label value when a measurement or a rule justifies it; the
|
|
|
19
20
|
a News entry: only if a finding asks for it; a written rule of the project's CLAUDE.md (apply it); who launches the review or UX pass (never the session: the program and the lead do).
|
|
20
21
|
"not my job to touch the plan" is not a question: say it in the report.
|
|
21
22
|
Never stop to ask about how to organise your own work (split or squash a commit, order of the sub-parts, whether to continue, "I have no question"): decide, list it under "choix", go on. Leave "questions" empty rather than writing a placeholder such as "no question".
|
|
23
|
+
Tests already red before the lot: if the suite is red, check whether the same tests are red at the sha the lot started from (the parent of the oldest work commit that cites {{lot}}, plan-only commits left out, or the `HEAD` before this session if the lot has none: a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards). Failures already there are not a defect of this lot: do not try to fix them and do not report your tests as red for them; list their names under "echecsDeBase" in your report (one test per entry, as the runner prints it), list under "nouveauxEchecs" the tests that are red now and were NOT red at that sha (an empty list when there are none: with `vert: false`, a non-empty "echecsDeBase" and an empty "nouveauxEchecs" the lot goes to review without a correction pass; any entry in "nouveauxEchecs" with `vert: false` triggers one), and say under "nonVerifie" if you could not check. The program replays the suite itself and compares to the same baseline.
|
|
22
24
|
A finding you contest (it describes a state older than the code, or you think it wrong) is not a question and not a reason to stop: do not ask the lead to arbitrate. Write it under "choix" with the evidence (the command, the line, the commit), fix what is right, go on; the next fresh review re-reads it.
|
|
23
25
|
You are one short session of an orchestrated wave: do not launch subagents (the Agent tool is disabled).
|
|
24
26
|
Give your final report as the structured output (commits, tests, build, what you could not verify, choices, questions).
|
|
@@ -7,6 +7,7 @@ Rules: test first; commit each sub-part as soon as its tests pass, with explicit
|
|
|
7
7
|
`git add -A` or `commit -a`), and a message that cites the lot (`feat({{lot}}): …`); run the project's
|
|
8
8
|
full test suite and build before reporting; do not push, deliver, run `raf done`, `raf ux` or
|
|
9
9
|
`raf review`.
|
|
10
|
+
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|
|
10
11
|
Documentation: the project's README (and its usage documentation) describes the behaviour you deliver, updated in the same commits, like the CHANGELOG — a README that does not follow your change is a major finding of the review.
|
|
11
12
|
Decide minor interpretation questions yourself (the wording of a message, a name, a default, the
|
|
12
13
|
reading of an ambiguous line of the lot) and list each one under "choix" in your report, with the
|
|
@@ -19,6 +20,7 @@ a written rule of the project's CLAUDE.md (apply it); who launches the review or
|
|
|
19
20
|
Never stop to ask about how to organise your own work (split or squash a commit, order of the sub-parts, whether to continue, "I have no question"): decide, list it under "choix", go on. Leave "questions" empty rather than writing a placeholder such as "no question".
|
|
20
21
|
If the lot's commits already cover its open sub-tasks, do not ask whether to continue: make no commit and say so
|
|
21
22
|
in your report (the review that follows re-reads those commits). If they do not cover them all, implement the rest.
|
|
23
|
+
Tests already red before the lot: if the suite is red, check whether the same tests are red at the sha the lot started from (the parent of the oldest work commit that cites {{lot}}, plan-only commits left out, or the `HEAD` before this session if the lot has none: a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards). Failures already there are not a defect of this lot: do not try to fix them and do not report your tests as red for them; list their names under "echecsDeBase" in your report (one test per entry, as the runner prints it), list under "nouveauxEchecs" the tests that are red now and were NOT red at that sha (an empty list when there are none: with `vert: false`, a non-empty "echecsDeBase" and an empty "nouveauxEchecs" the lot goes to review without a correction pass; any entry in "nouveauxEchecs" with `vert: false` triggers one), and say under "nonVerifie" if you could not check. The program replays the suite itself and compares to the same baseline.
|
|
22
24
|
Report: commits (sha + subject), tests and build results with their numbers, what you could not
|
|
23
25
|
verify, choices made, open questions.
|
|
24
26
|
|
|
@@ -16,6 +16,7 @@ Sheet rules:
|
|
|
16
16
|
- If, after reading the lot and the code, you find that the lot changes NO behaviour (nothing a user, an API client or another service can observe: a lint, a rename, a move, a doc, a CI or cache setting without observable effect), write one line with the reason in `sousSeuil`, create no sheet, and finish: the lot then follows the usual path.
|
|
17
17
|
|
|
18
18
|
Commit the sheet with explicit paths (never `git add -A` or `commit -a`), a message that cites the lot (`spec({{lot}}): …`), and leave the working tree clean. Do not implement the behaviour, do not write application code or tests, do not push, deliver, run `raf done`, `raf ux` or `raf review`.
|
|
19
|
+
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|
|
19
20
|
Decide minor interpretation questions yourself and list each one under "choix" with the alternative you did not take. Leave "questions" empty rather than writing a placeholder such as "no question".
|
|
20
21
|
{{commits}}
|
|
21
22
|
{{reponse}}
|
|
@@ -14,9 +14,10 @@ Sheet rules:
|
|
|
14
14
|
- Check every date in the sample data of the mockup before keeping it: compute its weekday (a command such as `date -d 2026-09-03 +%A`, or a script), never write it from memory — a « Wed 03/09 » that is a Thursday sends the mockup back for nothing. The same goes for any figure that must add up (a score and its result, a total and its parts).
|
|
15
15
|
- Expose the messages exactly as the screen shows them; the message of a rule is visible in the state its `etat` names.
|
|
16
16
|
- A rule you invent is not a free choice: **a hypothesis whose `si_faux` involves an expensive-to-undo choice is a question, not a hypothesis.** The four expensive categories: a data model or storage (key, migration, user data created once shipped); a public or shared API contract, or an already published route / URL; a destructive or irreversible action (deletion, sending, payment); a rule that contradicts an existing sourced rule. In those cases do not write the hypothesis: stop and put the question under "questions" (the lead answers and the step is resumed). Everything else (wording, order of the fields, error state, spacing) is a written hypothesis, cheap to correct later.
|
|
17
|
-
- If, after reading the lot and the code, you find that the lot changes NO screen (no new page
|
|
17
|
+
- If, after reading the lot and the code, you find that the lot changes NO screen (no new page), write one line with the reason in `sousSeuil`, create no mockup and no sheet, and finish: the lot then follows the usual path.
|
|
18
18
|
|
|
19
19
|
Commit the spec with explicit paths (never `git add -A` or `commit -a`), a message that cites the lot (`spec({{lot}}): …`), the sheet, the mockup and the captures together, and leave the working tree clean. Do not implement the screen, do not write application code or tests, do not push, deliver, run `raf done`, `raf ux` or `raf review`.
|
|
20
|
+
Never run `git checkout` or `git switch` to another commit or branch in this repository: the orchestrator watches it and a moved HEAD breaks the other sessions of the wave. To look at another sha, use a throwaway `git worktree add --detach <tmp> <sha>`, removed afterwards.
|
|
20
21
|
Decide minor interpretation questions yourself and list each one under "choix" with the alternative you did not take. Leave "questions" empty rather than writing a placeholder such as "no question".
|
|
21
22
|
{{commits}}
|
|
22
23
|
{{reponse}}
|