@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
package/docs/orchestrate.md
CHANGED
|
@@ -12,6 +12,7 @@ exception is a **formatting retry** (below).
|
|
|
12
12
|
cadence orchestrate finance-tracker:L41 ol-companion:L22 cadence:L18@haiku
|
|
13
13
|
cadence orchestrate L18 # from inside a project
|
|
14
14
|
cadence orchestrate maritime-atlas:Q4/accueil-4-ux12@haiku # a lot id may contain "/" (sub-tasks of read-only plans)
|
|
15
|
+
cadence orchestrate maritime:B28.4 --goal "…" # the goal replaces the stale title as the objective of the briefs (below)
|
|
15
16
|
cadence orchestrate … --budget 1.5M # 1500000, 1.5M, 800k; default 2M
|
|
16
17
|
cadence orchestrate … --max-sessions 3 # sessions running at the same moment, all waves together; default 2
|
|
17
18
|
cadence orchestrate … --dry-run # preconditions + the plan of the wave; nothing is started
|
|
@@ -21,6 +22,9 @@ cadence orchestrate --status [<wave>] # the live waves and the repositori
|
|
|
21
22
|
cadence orchestrate --status [<wave>] --watch [--interval 10] # the same, redrawn every 10 s (--interval in seconds); stops by itself when no wave is running (with an explicit `<wave>`: when that wave is no longer running)
|
|
22
23
|
cadence orchestrate --resume [<wave>] [--budget 1M] [--answer ol-companion:L22 "reply"] [--drop ccc:L29]
|
|
23
24
|
cadence orchestrate --resume <wave> --budget maritime:B26.4=+300k # replay a lot handed back for its budget, with the budget raised (below)
|
|
25
|
+
cadence orchestrate --resume <wave> --integrate ccc:L29 # replay a lot handed back at the integration step of a worktree wave (« One worktree per lot », experimental)
|
|
26
|
+
cadence orchestrate --resume <wave> --replay ccc:L29 # same, and also a lot handed back on a refused plan write (fiche notes) at the step it had reached
|
|
27
|
+
cadence orchestrate --remove-worktree ccc:L29 [--abandon-commits] # remove the kept worktree and branch of a lot (« One worktree per lot », experimental)
|
|
24
28
|
cadence orchestrate --resume … --dry-run # refused: a resume cannot be simulated, it writes the wave state and relaunches sessions
|
|
25
29
|
cadence orchestrate --drop ccc:L29 [--wave <wave>] # from another terminal: take a lot out of the running wave
|
|
26
30
|
cadence orchestrate --stop-after-current [--wave <wave>] # from another terminal: let the running sessions finish, start nothing else
|
|
@@ -43,6 +47,24 @@ this way is left out of the resume checks (repository lock, `.nvmrc` Node, dirty
|
|
|
43
47
|
refuse the resume, and a refusal writes nothing to `control.log`.
|
|
44
48
|
`--resume --stop-after-current` is refused (exit 2): resuming contradicts stopping.
|
|
45
49
|
|
|
50
|
+
**`--goal "…"` and `goal:` — the objective of the briefs when the title is stale (L258)**: the first line of the `{{objectif}}` of every
|
|
51
|
+
brief of a lot (and the title quoted in their first sentence) is the lot's title; when a change of scope made it stale, the lead no longer
|
|
52
|
+
has to delegate by hand. Three ways to give the goal, in this order of precedence (`--goal`, then the plan's key, then the title):
|
|
53
|
+
- `--goal "…"` on the command line: `cadence orchestrate maritime:B28.4 --goal "…"`. It takes **exactly one launched lot** (two lots,
|
|
54
|
+
`--continue`, `--resume`, `--status` are refused, exit 2, with nothing written); an empty text, or one starting with `--` (a forgotten
|
|
55
|
+
text: `--goal --dry-run` would otherwise swallow the option and start a real wave), is refused.
|
|
56
|
+
- the `goal:` key of the lot in a **native plan** (`.cadence/raf.yaml`): `goal: Dedup on merge, bank line kept`.
|
|
57
|
+
- on a plan **kept by another tool** (read-only, mapped by `plan.fields` in `cadence.yaml`, e.g. maritime's `docs/plan/taches.yaml`): map the
|
|
58
|
+
key that holds the goal, `plan.fields: { goal: objectif }` (a list takes the first key present, like the other fields); without that
|
|
59
|
+
mapping only a key literally named `goal` is read (a field left out is read under its own name), any other key is ignored without a message.
|
|
60
|
+
The key is read for every lot of a launch, so it is the way to give a goal to several lots at once, on both plan kinds (cadence only reads the plan,
|
|
61
|
+
it never writes a goal into it). The notes and sub-tasks of the lot stay in the objective. The goal read at launch is kept in the state
|
|
62
|
+
of the lot and is the only source afterwards (briefs, `--dry-run`, table): a `--resume` replays the sessions with the goal of the launch, and
|
|
63
|
+
a `goal:` added or changed in the plan since is not read (to change it: `--resume --drop` the lot and launch it again). `--dry-run` prints
|
|
64
|
+
`objectif donné : …` under the lot, and the table of the wave repeats it (`<project>:<lot> — objectif donné : …`) so the lead sees which
|
|
65
|
+
objective the sessions worked from.
|
|
66
|
+
The review and UX briefs carry it too, on a line `Goal of the lot (it takes precedence over the title in the plan): …`, and `code-reviewer` / `qa-reviewer` measure the commits against it, not against the stale title still in a read-only plan.
|
|
67
|
+
|
|
46
68
|
**`--resume --budget <project>:<lot>=+N` — replaying a lot handed back for its budget (L223)**: a lot handed back with
|
|
47
69
|
`correction non jouée (budget)` or `budget du lot atteint` kept its findings but not its budget, and a plain `--resume` does not replay a
|
|
48
70
|
finished lot. `--budget maritime:B26.4=+300k` (the project may be left out when only one project carries the lot; the option is
|
|
@@ -77,10 +99,10 @@ second one would leave a session idle — and is completed with the same reposit
|
|
|
77
99
|
alphabetical order. Launched from inside a project, only that project is drawn. A `priority:` key that cannot be read is refused before anything
|
|
78
100
|
starts, lots given or not. A candidate that preflight refuses (dirty tree, Node missing…) is skipped with its cause in the
|
|
79
101
|
journal (`continue : lot sauté — <cause>`; for the first draw also on the error output). Drawing stops — `continue : arrêt — <reason>` — at: the budget, the
|
|
80
|
-
time window (`--until HH:MM`, today's clock: nothing new is drawn from then on, a running lot is not cut), the usage limit,
|
|
102
|
+
time window (`--until HH:MM`, today's clock: nothing new is drawn from then on, a running lot is not cut), the usage limit (« usage limit reached », « rate limit », « You've hit your weekly limit »),
|
|
81
103
|
a question asked (the wave stays resumable with `--answer`), two lots handed back in a row, an interrupted wave, a `--stop-after-current` request (`arrêt demandé`, even when the last running
|
|
82
104
|
lots finished cleanly), or no ready
|
|
83
|
-
lot whose estimate fits the budget left. `--resume … --continue` draws again after the resumed lots. `--dry-run --continue` replays the
|
|
105
|
+
lot whose estimate fits the budget left. `--resume … --continue` draws again after the resumed lots. When the wave starts, `--continue` (and its `--dry-run`) prints the line `prêts, ressources libres : <project>:<lot> <title> (<resources>) · …` that `cadence lead tour` prints (L252): the ready lots, from every project of the folder, whose `uses:` resources (`raf uses`, `resources:` of the parent `cadence.yaml`) are held neither by a lot `doing` (of the folder's projects, and of every project that shares their resources: those under the folder whose `cadence.yaml` declares `resources:`, walked with its `depth:`, one level by default, so launched from a group or from inside one project it still sees the others) nor by an unfinished lot of a live wave nor by the lots of the first draw of the wave that starts (a lot of a wave is recognised by its repository and its id, not by the project name; those lots, and the ready lots that compete with them for a resource, are not listed); at most 3 lots per project, then `<project> +K`; nothing is held back by it, the draw below is unchanged. `--dry-run --continue` replays the
|
|
84
106
|
draws round by round with the same function as the wave (one lot per repository per round, pre-check, budget counted on the estimates) and
|
|
85
107
|
names the candidates skipped with their cause: `tour 1 : a:L2, b:L2 · tour 2 : a:L3, a:L4`. Drawing happens between rounds: a round of up to `--max-sessions` lots must finish before the next draw.
|
|
86
108
|
|
|
@@ -112,7 +134,7 @@ keeps its `/` (`maritime-atlas:Q4/accueil-4-ux12@haiku`); this holds for `--dry-
|
|
|
112
134
|
same for the lot folder and the dry-run briefs); ids without `/` are unchanged.
|
|
113
135
|
|
|
114
136
|
You choose the lots; the order is the order given (one queue per repository, as many repositories at the
|
|
115
|
-
same time as the session cap allows — 2 by default). `@haiku|@sonnet|@opus` sets the model of the implementation and corrections of that
|
|
137
|
+
same time as the session cap allows — 2 by default; the experimental `orchestrate.worktrees: true` mode, refused without `CADENCE_EXPERIMENTAL_WORKTREES=1` (lots L299–L301 to do before activation), would play the lots of one repository in parallel, each in its own worktree — « One worktree per lot »). `@haiku|@sonnet|@opus` sets the model of the implementation and corrections of that
|
|
116
138
|
lot (default Sonnet; reviews are Opus, except the light review below; Haiku only when you write it, for a mechanical lot). Run it
|
|
117
139
|
in the background and read `--status`: it prints one line per transition and the final table.
|
|
118
140
|
|
|
@@ -152,6 +174,41 @@ orchestrate:
|
|
|
152
174
|
|
|
153
175
|
`full` is also the model of the UX review and of the single pass of a small lot that is not light.
|
|
154
176
|
|
|
177
|
+
**Code changed without a test (L287)**: before any review session, the program checks the diff of the lot (every commit that
|
|
178
|
+
cites it, all sessions included). If it touches code (`testGate.code`, minus `testGate.except`) without touching a single test
|
|
179
|
+
file (`testGate.tests`), the lot goes straight to a correction pass — no review session is spent finding what a file list
|
|
180
|
+
already shows (a missing test was 32 % of the major findings of the 24-10 audit, one pass ≈ 190 k tokens). The pass counts
|
|
181
|
+
as one of the two correction passes, its brief names the files, and the gate plays once per lot: if the correction adds no
|
|
182
|
+
test, the review goes ahead and judges it. It does not play during the minors pass, nor when a correction pass is already used (the gate's pass would take the last one and the review would arrive with none left to correct its majors),
|
|
183
|
+
nor when the lot budget no longer pays the correction and the review after it (a warning says so each time; the review goes ahead). Absent key: no gate.
|
|
184
|
+
|
|
185
|
+
The diff read is that of the lot's repository **and** of its neighbour repositories (`repos:`, same as the « work already present » check; patterns apply to each repository's own paths, so a test in a neighbour lifts the gate). Files a commit **deletes** do not count as code without a test (`git diff-tree --diff-filter=d`): a lot that removes dead code used to trigger a pass of about 65 k tokens for nothing. `docs.sync` still sees deleted files.
|
|
186
|
+
|
|
187
|
+
On a light lot (see « Review sized to the lot »), the gate's correction pass is pass 1: the review that follows is therefore the full one (Opus), not the light one (Sonnet). A light lot that touches code without a test pays the correction **and** an Opus review.
|
|
188
|
+
|
|
189
|
+
```yaml
|
|
190
|
+
orchestrate:
|
|
191
|
+
testGate:
|
|
192
|
+
code: [src/**, bin/*.js] # required: patterns of the code files (same patterns as docs.sync, `!…` excludes)
|
|
193
|
+
tests: ['**/*.test.*', '**/*.spec.*', 'test/**', 'tests/**', '**/__tests__/**'] # default: one touched test file lifts the gate
|
|
194
|
+
except: [src/**/*.d.ts] # code files that need no test (types, configuration, migrations…); default none
|
|
195
|
+
threshold: 1 # default 1: how many code files (after exceptions) before the gate plays
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`testGate: false` is the same as no key.
|
|
199
|
+
|
|
200
|
+
**Librarian before the review** (L306). When `docs.sync` (see [news](news.md#documentation-that-follows-the-code)) reports that
|
|
201
|
+
the commits of the lot touch code without touching the document that describes it, the lot goes to a documentation pass
|
|
202
|
+
before its review: a `fix` session launched with the `doc-keeper` agent (`agents/doc-keeper.md`) and its own brief
|
|
203
|
+
(`fix-docs`), which lists the lines the program computed and asks for the documents to be brought up to date, nothing else. The
|
|
204
|
+
pass counts as one of the two correction passes and plays once per lot: if the librarian touches no document, the review goes
|
|
205
|
+
ahead and carries the gap as before (one major finding per line). It does not play during the minors pass, nor when a
|
|
206
|
+
correction pass is already used (its pass would take the last one), nor when the lot budget no longer pays it and the review after
|
|
207
|
+
it (a warning `bibliothécaire non appelé` says so each time; the review goes ahead). Without `docs.sync`, or with no gap: no pass.
|
|
208
|
+
When the project's `cadence.yaml` declares `docs.articles` (the same key as the article refreshed after a delivery, see [news](news.md#an-article-elsewhere-that-follows-the-delivery)), the brief names those
|
|
209
|
+
documents with the absolute path of their repository (`repo` relative to the project, or absolute), opens that repository to the pass for reading (`--add-dir`), forbids editing them and asks for a proposed lot in the report; the proposal (the `resume` of the librarian's report, when it starts with « lot proposé ») becomes a proposal of the lot, printed in the
|
|
210
|
+
table at the end of the wave, and is the lead's to take into the plan of the other repository.
|
|
211
|
+
|
|
155
212
|
**Effort level per pass** (L137). Each session is launched with `claude -p --effort <level>` (Claude Code 2.1.284; an
|
|
156
213
|
older `claude` is refused at launch unless every pass is `default`). Defaults: `precheck` low (it only reads),
|
|
157
214
|
`implement` and `fix` medium, `review` high (`review-small` follows `review`), `ux` high. A project overrides any of them
|
|
@@ -175,7 +232,7 @@ what the lot asks for (another lot, or a correction, may have done it already).
|
|
|
175
232
|
with proofs), `partiel` or `non`: on `oui` no implementation session is opened and the lot is handed back
|
|
176
233
|
(`livrable déjà présent : <résumé> (<preuves>)`) for the lead to drop or close it; on `partiel` the finding goes into the
|
|
177
234
|
implementation brief and a warning; on `non` — or an unreadable report, which only adds a warning — the wave goes on. A
|
|
178
|
-
lot that already has commits is never pre-checked (resuming it is legitimate). `orchestrate.precheck: false` turns it off.
|
|
235
|
+
lot that already has commits is never pre-checked (resuming it is legitimate), and neither is a lot **whose title and `goal:` cite neither a finding nor another lot** (L288, L292): the pre-check runs only when the title, the `goal:` of the lot (or the goal given at launch) contains the word `constat` (any ending), `revue`, `review` or `audit` (any case, plural allowed; whole words, so `qa-reviewer` and `review-small` do not count), or a whole plan lot id: an `L<number>` earlier than the lot (`L12` in the title of `L30`; `L30` itself, a later lot or `L120` for `L12` do not count), any other id of the plan, whatever the lot (`Q-CAN5`, `B28.1`, `Q4/qa-expectations`; `B28.1` is not `B28.10`), or a lot of another project written `project:L<number>` (`cadence:L68`). Measured on 24-10: 13.4 M tokens (7.1 %) of pre-check for 10 `oui` and 20 `partiel` out of the sessions read, hence the restriction. The restriction costs real `oui`: 7 of the 17 measured (L171, L68, Q-LBL3, Q-NOW5, `Q4/dashboard-21-mesure-prod`, `Q4/qa-expectations`, `Q4/qa-expectations-nouveautes`) have a title that cites nothing, against about a third of the pre-checks avoided (about 20 k tokens each). With `orchestrate.precheck: local` (below) the criterion is therefore dropped (L296): every lot with no commit is pre-checked, on the local model, off the quota. The local/Sonnet agreement on the 37 `oui` and `partiel` verdicts could not be replayed (the repositories now hold every deliverable, so any replay answers `oui`): the safety nets of `local` stay (a local `oui` is confirmed by Sonnet) and the key stays opt-in. The verdict the pre-check reached (`oui`, `partiel`, `non`; an unproven `oui` is recorded `partiel`) is kept in the lot's journal (`precheckVerdict`; for a journal older than L288, `cadence perf` reads it back from the journal: `oui` for a lot handed back `livrable déjà présent`, `partiel` when the lot carries the pre-check finding, `non` for any other lot whose `precheck` step succeeded), and `cadence perf` prints the share of `oui` per window ([perf](perf.md)): change the key or the criterion, then compare the current window with the previous one. `orchestrate.precheck: false` turns it off.
|
|
179
236
|
|
|
180
237
|
## The spec step of a lot with `screen:`
|
|
181
238
|
|
|
@@ -195,7 +252,7 @@ the first implementation: `precheck → spec → maquette → accept → impleme
|
|
|
195
252
|
`#etat-<état>` section per state and a `data-testid` per field; every state has its capture at both widths; everything is committed and the tree is clean. Anything missing is listed back to a fresh `spec` session whose brief names the points, within the lot's session budget: `orchestrate.maquetteReviews` sessions (3 by default) when the mockup review is on (see [The mockup review](#the-mockup-review-before-any-code)), 2 otherwise; if the spec is still not valid on the last one the lot is handed back (`spec non conforme après 3 passes : …`, `après 2 passes` with `orchestrate.maquette: false`).
|
|
196
253
|
- **A costly choice is a question, not a hypothesis.** A rule marked `hypothese` whose `si_faux` involves a data model or storage, a public or shared API contract or an already published route,
|
|
197
254
|
a destructive or irreversible action, or a contradiction with a sourced rule, puts the lot in `question` (the program recognises these by keywords as a safety net behind the agent's own
|
|
198
|
-
judgement; a keyword right after a negation — « rien d'irréversible », « sans suppression », « pas de paiement » — does not count); `--resume --answer <lot> "…"` gives the answer to the resumed `spec` session. Every other hypothesis (wording, order of fields, error state, spacing) stays written in the sheet.
|
|
255
|
+
judgement; a keyword right after a negation — « rien d'irréversible », « sans suppression », « pas de paiement » — does not count); `--resume --answer <lot> "…"` gives the answer to the resumed `spec` session, **and the program itself turns the rules it asked about into `statut: sourcee`** (L297) — `source: réponse du lead, <date>, vague <id>`, no `si_faux`, the sheet committed alone as `spec(<lot>): R3 sourcée par la réponse du lead (orchestrate <wave>)` — before that session starts, so a session that is lost or writes nothing no longer leaves the rule an `hypothese` for the sheet review (seen on 24-10: L279 handed back « fiche non conforme » for R27 although the lead had answered). Only the `statut`, `source` and `si_faux` lines of the rule are edited, the rest of the sheet is kept byte for byte (L302: `[R2]` stays `[R2]`, comments and folded lines stay; a rule written in flow style `{id: R1, …}` hands the lot back instead of being edited). An answer that deletes or changes the rule (« non, supprime R3 », « change en X »: a word of refusal or correction such as `non`, `pas`, `supprime`, `change`, `plutôt`, `remove`, `instead`) sources nothing: the rule stays an `hypothese` for the resumed `spec` session, which applies the answer, and if it does not, the question goes back to the lead. The session only rewrites a rule the lead corrected; only the rules named by the program's question are touched, and an answer to a question of the session itself leaves the sheet alone. If the commit is refused (a hook), the lot is handed back saying so, with no `spec` session played. Every other hypothesis (wording, order of fields, error state, spacing) stays written in the sheet.
|
|
199
256
|
The agent can also ask a question itself in its report.
|
|
200
257
|
- **Under the threshold**: if the session finds that the lot changes no screen, it gives the reason in one line (`sousSeuil`), writes no mockup, and the lot goes on to `implement` as before; the reason is
|
|
201
258
|
kept as a warning of the lot. On resume the spec step is skipped only when a sheet of the lot (`ecran.lot` is the lot) already passes the program's checks (`cadence spec check`, mockup, captures) and carries no costly hypothesis; otherwise the spec is played again. The sheet path in the report is relative to the repository (an absolute or `./` path is normalised, a path outside the repository is a finding).
|
|
@@ -275,7 +332,7 @@ session, `accept` (L173): `precheck → spec → maquette → accept → impleme
|
|
|
275
332
|
- **The lock, for every session of the repository.** At the end of each `implement` and `fix` the cycle compares, for the acceptation files of **all** lots found under `orchestrate.spec.dir` (a repository has one queue: the next lot runs on the same branch), the listed test files with the red commit
|
|
276
333
|
(`git diff <red commit> -- <files>`, empty expected, working tree included, and the fingerprints), and each `acceptation.json` with its version before the session. The only difference tolerated on `acceptation.json` is the `vert` key, and only **the cycle** sets it: a session that changes it, rewrites the
|
|
277
334
|
file, deletes it, creates one outside the accept step, or modifies a locked test hands the lot back (`verrou des tests d'acceptation : …`) — for a modified test the message ends with `; premier commit fautif : <sha7> <subject>`, the oldest commit after the reference (red commit, or the head before the lot) that touched the sealed path (a deletion or a rename included, renames not followed), and names no commit when the deviation is only in the working tree (L231) — a useful change is a question to the lead or a review finding, never a silent gesture. The lock does not depend on `orchestrate.accept` being declared. **Exception — a delivered lot (`vert: true`):** a later lot that makes the rule evolve may modify the listed tests of that acceptation when **a commit that cites the lot in progress amends the sheet of the screen and comes after the red commit** (a strict descendant of it; the commit may have been made before the wave, and once one exists the exception covers the whole acceptation: the tests may change in the same commit as the sheet, in a later one or in a following session, a `fix` after the `implement` for instance, L231): the sheet is what evolves the rules, and tests and sheet change together. A sheet amended by another lot, by a commit that cites no lot, by a commit that is not after the red commit, or only in the working tree (not committed) opens nothing. The exception never covers the acceptation of the lot in progress (its `vert: true` is set right after the implement): that one keeps the whole lock. Without a sheet change the difference is still a deviation — except one narrow case (L184): a listed file whose content is identical to what it was at the head before the lot's first **work** commit (commits found by the plan's refs) is not a deviation, the lot having taken its own addition back (a lot handed back for writing in a locked file and resumed in a new wave, which then removes that addition). This applies to every acceptation, `vert: false` included, but only to a file that **exists** at that head and now: a file absent at that head (e.g. created by the lot's own red commit) or deleted since is never exempted, so deleting a locked test stays a deviation; nor when the sheet of the screen has changed since that head (an evolution admitted for another lot: going back to the old content would cancel it, not the lot's addition); apart from that, with `vert: false` the whole lock applies, sheet change or not; `acceptation.json` itself stays locked (only its `vert` key moves).
|
|
278
|
-
- **A sealed test that contradicts the sheet: `cadence spec reseal` (L200).** The lock is total for the lot in progress (the delivered-lot exception above never covers it; for a delivered lot, see the end of this item), so a test that is wrong against its sheet — e.g. the regex `/\d{1,2} /` of an acceptance test rejecting « T3 2027 annoncée », which the sheet allows — would leave the lot handed back with no way out. The way out is the lead's, outside any wave: correct the test in the working tree (the sheet is left as it is: unchanged since the red commit, or amended by commits of the lot and committed, see « A sheet amended in the same lot » below), then `cadence spec reseal <lot> <test> --reason "why the test contradicts the sheet"`. The program checks that the lot is in progress (`doing`; for a delivered lot, see the end of this item), that the test is listed in the lot's sealed `acceptation-<lot>.json`, that the sheet is unchanged since the red commit or amended by commits of the lot (below), that **no other listed test** changed (the lock keeps everything else) and that the test did change; it then redoes the fingerprint of that one test, appends `{ test, raison, date, avant, apres }` (plus `ficheAmendee` for an amended sheet, below) to the `resceaux` key of `acceptation.json`, commits the test and `acceptation.json` (`test(L35): acceptation rescellée, <test> : <reason>`, the new red commit) and the lot's note with the reason (`plan: …`). It is refused during an orchestrated wave (`CADENCE_ORCHESTRATED`, or the repository's wave lock while one of its lots still works, like `cadence deliver`), with no `--reason`, and on a lot that is neither in progress nor delivered with a green acceptation. Then, depending on how the lot stands: a lot waiting on a `question` is
|
|
335
|
+
- **A sealed test that contradicts the sheet: `cadence spec reseal` (L200).** The lock is total for the lot in progress (the delivered-lot exception above never covers it; for a delivered lot, see the end of this item), so a test that is wrong against its sheet — e.g. the regex `/\d{1,2} /` of an acceptance test rejecting « T3 2027 annoncée », which the sheet allows — would leave the lot handed back with no way out. The way out is the lead's, outside any wave: correct the test in the working tree (the sheet is left as it is: unchanged since the red commit, or amended by commits of the lot and committed, see « A sheet amended in the same lot » below), then `cadence spec reseal <lot> <test> --reason "why the test contradicts the sheet"`. The program checks that the lot is in progress (`doing`; for a delivered lot, see the end of this item), that the test is listed in the lot's sealed `acceptation-<lot>.json`, that the sheet is unchanged since the red commit or amended by commits of the lot (below), that **no other listed test** changed (the lock keeps everything else) and that the test did change; it then redoes the fingerprint of that one test, appends `{ test, raison, date, avant, apres }` (plus `ficheAmendee` for an amended sheet, below) to the `resceaux` key of `acceptation.json`, commits the test and `acceptation.json` (`test(L35): acceptation rescellée, <test> : <reason>`, the new red commit) and the lot's note with the reason (`plan: …`). It is refused during an orchestrated wave (`CADENCE_ORCHESTRATED`, or the repository's wave lock while one of its lots still works, like `cadence deliver`), **while a wave that is not `done` (interrupted, suspended, or still alive with a lot waiting on a question) keeps open any lot of this repository that is still `doing` in this repository's plan (L238)** — the resealed lot, the `--porteur` or any other lot of the repository: the lock hands back every open lot of the repository, so a reseal committed in the meantime was handed back to any of them as the « first offending commit » of the lock (ol-companion L172, then L173). Open means any non-final state in the wave (a question waiting for its `--answer`, a lot suspended by the quota or the budget, a queued lot, a session running): a stopped wave holds no lock, but its `--resume` replays those lots. The filter is by repository (the repository of the lot's project, not its neighbour repositories: lot ids belong to each plan, so a finance-tracker L172 never blocks an ol-companion L172) and by plan: a lot that is `done` or absent in the plan never refuses, so an old interrupted wave whose lots are long delivered blocks nothing, and neither does a `done` wave. The error is a single message that names **every** wave concerned, with its launch folder and its open lots, the wave that holds the repository lock included (alive: while it has an open lot the lock refuses first, so the same message lists it together with the stopped waves of the repository, not the bare « orchestration en cours » line `cadence deliver` gives), and gives for each stopped wave ONE command, runnable as it stands, with a `--drop` per open lot of the repository in that wave, the queued ones that are not `doing` in the plan included (L239: `--resume` would relaunch them, and the lot would take the lock back and find the tree dirty with the corrected test): `cd <launch folder> && cadence orchestrate --resume <wave> --drop <project>:<lot> --drop <project>:<lot>` (`--resume` replays the whole wave except the dropped lots, so one command per lot would start sessions on the others; it looks for the wave in the folder it is run from only); a wave that is still alive is not called suspended, the error says it is running and is to be waited for. A repository reached by another path (a worktree, a symbolic link) is the same repository: the `.git` directories are compared by their real path. The waves read are those of `.cadence/runs/` of the repository root, of the plan's folder and of every folder above them (a wave keeps its state where `cadence orchestrate` was launched, usually the parent folder of the projects), with no `--reason`, and on a lot that is neither in progress nor delivered with a green acceptation. Then, depending on how the lot stands: a lot waiting on a `question` sits in a wave that is not `done`, which refuses the reseal (L238): answer it first (`--resume --answer`, the lot then goes on or is handed back) or close it (`--resume --drop`), reseal afterwards if the lot is handed back (a rewrite of `acceptation.json` without a `resceaux` entry stays a violation of the lock); a lot handed back with `verrou des tests d'acceptation` is finished for `--resume`, so it is replayed by a new wave `cadence orchestrate <project>:<lot>`, which starts again from the red commit. A session never does it, three guards: (1) `cadence spec reseal` is in the sessions' `--disallowedTools` (like `raf done`/`review`/`ux`); (2) a resealed commit is adopted as the new red commit only if it belongs to **no session of the wave** (commits listed in each `steps[].commits`, or in `headBefore..headAfter` of any session of any lot of the wave) — otherwise it stays a violation of the lock; (3) the lock is checked at the end of **every** work session, a session that ends with a question included, so a session that replaces the test, forges the fingerprint, commits and asks a question is handed back with `verrou des tests d'acceptation` without waiting for the lead's `--answer`. The reseal is atomic: if the commit fails (a `pre-commit` hook exiting 1, say) `acceptation.json` and the test are put back as they were before the command (the lead's correction in the working tree is kept, nothing staged) and a readable error says nothing was resealed. With a plan kept read-only by another tool, the reseal itself succeeds but the plan is not touched: the note of the lot is left to the lead (the reason stays in `acceptation.json` and in the commit message; the command only says a note is to be added). With a plan outside the repository or untracked, the note **is** written in the plan file but not committed (the command says `plan à commiter`); do not add it a second time. If the commit of the note is refused (a hook), the reseal stays done: the error gives its commit and says the note, already written, is to be committed by hand. **A sheet amended in the same lot (L217):** a sheet amended since the red commit is accepted, for a lot in progress as for a delivered one (the sheet is then not put back to its red state to be restored afterwards). When its net difference with the red commit (`git diff <red> -- <sheet>`) is not empty, the command reads `git log <red>..HEAD -- <sheet>`: every commit that touched it must cite the lot (read like `raf commits`, i.e. `citedRefs`: the scope of the subject first, the body otherwise; for a delivered lot, the lot, the `--porteur` or any delivered lot of the plan, status `done`, L229: the living sheet is the reference; one cited delivered lot among several is enough), and the sheet must be committed. The fingerprint of the test is then redone against the current sheet and the `resceaux` entry gains the optional key `ficheAmendee` (short sha of the last commit that touched the sheet); the lot's note and the commit message say `fiche amendée en <sha>` instead of `fiche inchangée`. Still refused, with nothing written: a sheet modified by a commit that cites neither the lot nor (delivered lot) the `--porteur` or a delivered lot (`la fiche … a été modifiée depuis le commit rouge … par un commit qui ne cite pas <lot> : <sha>`; for a delivered lot `… ne cite ni <lot>, ni le porteur <porteur>, ni un lot livré : <sha> (cite : <lots ou aucun lot>) — faire citer le porteur ou un lot livré par ce commit (le réécrire avant qu'il soit poussé) ou, sinon, rescelle à la main avec le lead`), a sheet deleted or renamed since the red commit, a sheet with uncommitted changes (working tree or index: `a des modifications non commitées : la commiter (en citant <lot>) avant le rescellé`), as before any other listed test touched, a test left unchanged, an orchestrated wave and a missing `--reason`; the atomicity is unchanged. A sheet amended then brought back to the identical content is treated as unchanged, whoever committed in between. After the reseal the new red commit is the reseal commit, so a second reseal sees the sheet as unchanged. **A delivered lot (L212):** a lot whose acceptation is green (`vert: true`) and whose sealed test turns out unstable while the contract does not change (seen: a tab read as « blank » in CI) had no way out but the « sheet modified in the same lot » exception. `cadence spec reseal <lot> <test> --reason "why" --porteur <lot in progress>` now covers it, with the same guards (the sheet unchanged since the red commit, or amended as above by commits that cite the delivered lot, the carrier or another delivered lot; no other listed test touched, no wave, `--reason`) and three differences: `--porteur` is required and names the lot in progress that carries the fix (refused if it is the delivered lot itself, unknown or not `doing`); the commit cites the **carrier** (`test(L113): acceptation rescellée du lot livré L47, <test> : <reason>`) and so does the plan note and its commit, the delivered lot being closed; the dated `resceaux` entry also records `porteur`, and `vert` stays `true`. `--porteur` on a lot in progress, or `spec reseal` on a delivered lot without `--porteur`, is refused.
|
|
279
336
|
- **Back to green, set by the cycle.** At the end of each `implement`/`fix`, once the session reports green, the cycle runs the dedicated command. Red: the findings go to a `fix` pass (the tests are made green without editing the locked files). Green: the cycle sets `vert: true` and commits it (`chore(L7): acceptation verte …`;
|
|
280
337
|
`commitPlan` may commit the `acceptation.json` files, otherwise the dirty tree would stop the lot). The files then join `orchestrate.test` and the CI, which keep the lot green from then on (an `"e2e": true` acceptation excepted: its files stay in the e2e suite after the build); the following lot, running with `vert: true`, is compared with the head before its own work, not with the red commit, only once the sheet has changed between the red commit and that head: after an admitted evolution (tests changed along with the sheet) it sees no difference either; only a test changed *during* that lot without the sheet is a deviation.
|
|
281
338
|
- **When the lot is handed back** (question, budget, passes exhausted) its tests stay red but excluded from the project's runner and CI: the next lots and `cadence deliver` do not see them; `--resume` continues with the same lock. On resume, the step is skipped when the lot already has a valid committed **and sealed** `acceptation.json` (its `empreintes` set by the program); a conforming file without them proves nothing and `accept` runs again. A sealed green one (`vert: true`) also skips the step when its seal no longer holds (L233); a green one without `empreintes`, with `empreintes: {}`, or whose `empreintes` lack one of the listed files (copied from another file or lot) proves nothing and `accept` runs again (the contents are not compared here, only the presence of one fingerprint per listed file). The fingerprints are keyed by normalised path, so a listed `./test/x.ts` is recognised like `test/x.ts`, on resume and by the lock (which compares it to its fingerprint).
|
|
@@ -285,7 +342,8 @@ session, `accept` (L173): `precheck → spec → maquette → accept → impleme
|
|
|
285
342
|
Once the sheet exists, the steps after it all read it (L174, `src/orchestrate/aval.ts`, the `{{fiche}}` marker of the templates `implement.md`, `fix.md`, `fix-minors.md`, `review.md`, `review-small.md` and `ux.md`). A lot without a sheet (no `screen:`, or under the threshold) keeps exactly the briefs it had.
|
|
286
343
|
|
|
287
344
|
- **`implement` and `fix`** get the path of the sheet and of the mockup, and **one line per rule** (`- R3 [erreur, hypothese] statement. Message: « … ». Tests: « R3 — … »`). The session makes the acceptance tests green **without modifying them** (they are named when the `accept` step ran; the lock above hands the lot back otherwise), **the tests it writes in addition cite the rule** in their name (`R2 — …`), and it **does not invent a rule**: a case the sheet does not cover is written in the report (`nonVerifie` or `choix`), not coded. In a `fix` the sheet stays the reference: a finding that contradicts it is contested under `choix`. Both briefs also say that a conflict with the sealed test of **another** lot is a question to the lead naming both tests (file and `Rn — …` name), never a modification or a choice (L231). The `accept` and `accept-back` briefs and the `acceptance-writer` agent require each assertion to cite, in a comment, the rule and the sentence of its statement that it verifies, to assert nothing beyond that sentence, and to go through a self-check before the commit (an assertion with no sentence behind it is removed or listed under `choix`); the `review` and `review-small` briefs and the `code-reviewer` agent re-read each sealed assertion against the sentence it cites, and one that asserts more than the sheet is a *major* finding (« sealed test stronger than the sheet », worded « than its rule » in the briefs), corrected by the lead with `cadence spec reseal` (L231).
|
|
288
|
-
- **A self-check list before handing back (L222).** Both `implement.md` and `fix.md` carry the same fixed list (plain text, no marker, no new variable), taken from the recurring findings of the reviews of 2026-10-15: the usage documentation (README and architecture documentation) updated in the same commits as the behaviour; one test named `Rn — …` per rule of the sheet (one per business rule of the lot when there is no sheet); no dead code and no test that cannot fail (an assertion always true, or a test that does not exercise its rule); for a modified screen, the capture due of each state at 1440 and 390 px with a
|
|
345
|
+
- **A self-check list before handing back (L222).** Both `implement.md` and `fix.md` carry the same fixed list (plain text, no marker, no new variable), taken from the recurring findings of the reviews of 2026-10-15: the usage documentation (README and architecture documentation) updated in the same commits as the behaviour; one test named `Rn — …` per rule of the sheet (one per business rule of the lot when there is no sheet); no dead code and no test that cannot fail (an assertion always true, or a test that does not exercise its rule); for a modified screen, the capture due of each state at 1440 and 390 px with a file name (where it goes is the Playwright paragraph's rule) (a lot with no modified screen owes none and says so); no false comment, docstring or message. The session goes through it point by point on its own commits before it reports, and a point it cannot hold is written under `choix` or `nonVerifie` of its report: the report schema has no new field. In `fix` the list covers only what the session changed, and "fix them, and nothing else" stays. The other briefs (`fix-minors`, `spec`, `spec-back`, `accept`, `review-small`…) are unchanged. The before/after measure — correction passes per lot, replayed with `cadence corrections` (`--until <bound>` for the "before", `--since <bound>` for the "after", the bound being the time of the `npm run service` that puts the list in service) — is in [docs/mesures/autocontrole-avant-revue-2026-10-15.md](mesures/autocontrole-avant-revue-2026-10-15.md).
|
|
346
|
+
- **Auto-verification once the tests are green (L236).** After the self-check list, `implement.md` and `fix.md` carry a second fixed block: when the full suite and the build pass, the session re-reads the complete diff of its lot (of its fix commits, in `fix`) against the grid of the `code-reviewer` agent — code bug, architecture drift, `docs/architecture.md` (or the project's architecture documentation) up to date, edge cases, one test per changed behaviour — fixes what it finds at once (test first, a `fix(<lot>): …` commit, suite and build replayed), then starts `resume` with an « auto-vérification » paragraph: what it re-read, found and fixed, could not hold. No new report field. The cost bound is written in the brief: one reading of the diff, not a second review (no subagent, no files the diff does not touch). Measure before the block (`cadence corrections --classify`, 10 to 17-10): 356 passes, 53 % code bug, 19 % architecture drift, 14 % unclear spec; `cadence perf` of 17-10: 37 % right the first time. Target: above 50 % on the next window with tokens per lot stable (`cadence perf`).
|
|
289
347
|
- **The code review (`review`, `review-small`) checks rule → test → green** (`règle → test → vert`). For each `Rn` of the sheet the reviewer checks that its test exists (named after the rule), that it is green, and that the behaviour is in the diff; when an acceptance file is named, that each listed test is unchanged since the red commit (`git diff <red commit> -- <file>` empty; the brief gives the red commit). A rule without a test, an acceptance test modified since the red commit, a rule coded that is not in the sheet, and a test that is green without exercising its rule are **major** findings. The reviewer no longer rebuilds the cases from the code.
|
|
290
348
|
- **The UX pass becomes a conformity pass.** For a lot with a sheet, the `ux` session (the `ux-reviewer` agent, **conformity mode**) no longer audits the whole screen: it compares the running app to the mockup and to its captures `maquette-<état>-<largeur>.png`, state by state, at 1440 and 390 px, and reports the differences of rendering and ergonomics the tests do not cover, each tied to a rule or a measure. The rules of the sheet are the tests' job. Its verdict feeds `raf ux`, which stays the lead's, decided on the captures. A small visible lot (single `review-small` pass) gets the conformity instruction in that pass.
|
|
291
349
|
- **The `## Hypothèses` section of the news entry.** The `implement` brief asks for an entry that ends with `## Hypothèses`: one line `- Rn — …` per rule whose status is `hypothese`, with what changes if it is wrong (its `si_faux`) — it is what the lead shows for the `raf ux` decision, the choices nobody validated.
|
|
@@ -374,6 +432,15 @@ The sessions of the wave also get `.cadence/runs/<wave>/tool/bin` (the `raf` and
|
|
|
374
432
|
`PATH`**, ahead of the per-project Node below and of the usual `PATH`: the `raf` a reviewer runs (`raf commits`…) is the
|
|
375
433
|
snapshot's, not the installed one. A wave without a snapshot leaves the `PATH` as it is.
|
|
376
434
|
|
|
435
|
+
**A short temporary folder per lot (L242, L243)**: each session gets a `TMPDIR` of its own lot, `<os.tmpdir()>/cadence-<8 hex of the sha1 of the lot's state folder>`
|
|
436
|
+
(created before the session, exported in the environment of `claude -p` and given to it as an `--add-dir`, so that a write there is not refused outside the working directory; outside the repository, hence never tracked by git),
|
|
437
|
+
and every brief names it: test logs, dumps and exports go there, not in the repository's `tmp/` (sessions left `tmp/suite-l149.txt`, `tmp/vt-*.json`,
|
|
438
|
+
`tmp/*.log` four times on 2026-10-18, one lot handed back for a « dirty repository »). **It is short on purpose**: Chrome (the Playwright MCP) puts its Unix
|
|
439
|
+
sockets under `TMPDIR`, and a socket path is limited to 107 characters. L242 put it under `.cadence/runs/<wave>/<project>--<lot>/tmp` (about 95 characters), Playwright
|
|
440
|
+
refused to start (« Socket path too long ») and the UX reviews of the finance wave 2026-10-19-fin-prev had no capture, 4 lots out of 4. The path leaves 60 characters free
|
|
441
|
+
(a test checks `TMPDIR` + 60 < 108); when `os.tmpdir()` is too long for that, `/tmp` is used. The wave's folder of the lot keeps a `tmp` **symbolic link** to it, to find it from there.
|
|
442
|
+
The folder is shared by the sessions of the lot (implement, review, fix…) and **removed, link included, at the end of each pass of the lot, and when a signal interrupts the wave** (lot finished, suspended or waiting for an answer: nothing is left in `/tmp`; the next session recreates it, empty). A `--dry-run` brief or one delegated by hand has no wave folder, hence no such line.
|
|
443
|
+
|
|
377
444
|
**The code in service (L195)**: on cadence's own repository, `bin/raf.js` and `bin/cadence.js` load `dist/`, so the `raf` and `cadence` of your
|
|
378
445
|
`PATH` used to be whatever the working repository last built. The snapshot protects the wave running, not that installed tool, and a wave's
|
|
379
446
|
sessions run `npm run build`, `npm install`… The code in service now lives in a **separate copy**: a detached git worktree built there,
|
|
@@ -393,6 +460,119 @@ cite a lot whose status is not `done` (`plan:` commits are ignored); a lot `drop
|
|
|
393
460
|
rollback, the older ones are removed only once they are more than 7 days old, counted from the day they left service (a wave's snapshot links its `node_modules` to the copy that launched it). The first run is also what installs the links in `~/.local/bin`. It prints the fingerprint of the copy
|
|
394
461
|
(bin, dist, package.json).
|
|
395
462
|
|
|
463
|
+
## One worktree per lot (`orchestrate.worktrees`, L279)
|
|
464
|
+
|
|
465
|
+
> **Experimental, not enabled.** This mode is infrastructure that two code reviews found defective at the core (lots L299, L300 and L301 of the plan
|
|
466
|
+
> carry the fixes). Until they are done, `orchestrate.worktrees: true` is **refused** by `cadence orchestrate` (exit 2, before acting, `--dry-run`
|
|
467
|
+
> included) with a message naming those lots — on start **and on resume** (`--resume`, `--integrate`, `--replay`, `--continue`: a resumed lot whose configuration asks for worktrees, or whose `cadence/<wave>/…` branch is to be found again, is refused before anything is written: wave state, registry, git) — unless the environment variable `CADENCE_EXPERIMENTAL_WORKTREES=1` is set — which only the tests of the
|
|
468
|
+
> mode do. The default mode (key absent or `false`) is unchanged. What follows describes the mode as built.
|
|
469
|
+
|
|
470
|
+
By default a wave plays the lots of one repository one after the other, in the repository itself: a lot with four correction passes
|
|
471
|
+
holds the whole queue. With `orchestrate: { worktrees: true }` in the project's `cadence.yaml`, two independent lots of the same repository
|
|
472
|
+
play **at the same time**, each in its own `git worktree` on its own branch, within the session cap (`--max-sessions`). Absent or `false`,
|
|
473
|
+
nothing below applies and the wave behaves as before; any other value is refused when the configuration is read
|
|
474
|
+
(`orchestrate.worktrees doit être true ou false`).
|
|
475
|
+
|
|
476
|
+
- **Which lots.** The lots of one repository (as project; two lots that share a neighbour `repos:` are one group) go to worktrees when the
|
|
477
|
+
group has at least two lots, the repository is on a branch (not a detached `HEAD`) and no lot of the group declares `repos:`. Otherwise they
|
|
478
|
+
stay one after the other in the repository, with `orchestrate : <repository> : worktrees demandés mais <reason> : lots joués à la suite` in the
|
|
479
|
+
journal. A lot alone in its repository plays in it. A lot that already has its branch (a resume) keeps its worktree whatever the group.
|
|
480
|
+
- **When.** At the moment the lot takes its place under the cap, after its `raf start` — not at launch: a lot waiting for a place has neither a
|
|
481
|
+
worktree nor a branch. A lot that depends on another lot of the repository (`after`) waits until that one is `ready`, which in this mode only
|
|
482
|
+
happens **after** its integration, then starts from the tip of the repository's branch, which contains it. If the worktree or the branch cannot be
|
|
483
|
+
created (a branch of that name that is not cadence's, a full disk, an `index.lock`) only that lot is handed back, with git's cause
|
|
484
|
+
(`orchestrate : <project>:<lot> : worktree impossible : <cause>`); the other lots go on. Dependency folders ignored by git (`node_modules`…)
|
|
485
|
+
are linked into the worktree like in the baseline worktree.
|
|
486
|
+
- **Names, nothing stored.** The worktree is `.cadence/runs/<wave>/worktrees/<project>--<lot>` — or, when the wave was launched from the repository's own root (its run folder then lies inside the repository), `<CADENCE_HOME>/worktrees/<wave>-<hash of the run folder>/<project>--<lot>`, outside the repository (L301); the branch `cadence/<wave>/<project>--<lot>`; the
|
|
487
|
+
branch the lots started from is noted in the git config of the lot's branch (`branch.<name>.cadence-base`), with the lot and the creation time.
|
|
488
|
+
A resume finds them by their names. `LotState` gains no field: the integration step is the value `integrate` of the existing `next`.
|
|
489
|
+
- **The plan is never written in a worktree.** Everything the program writes to the plan for a lot — `raf start`, the notes of the sheet's
|
|
490
|
+
hypotheses and of the review's minors, the closing of the covered sub-tasks, the review verdict — is written **by the program, in the
|
|
491
|
+
project's own checkout**, one after the other under the repository's integration lock, each as a plan-only commit with explicit paths. A
|
|
492
|
+
read-only plan (`plan:` in `cadence.yaml`) gets `orchestrate.start` and `orchestrate.verdict` run in that checkout, never in the worktree; a plan
|
|
493
|
+
outside the repository is read and written in place, without a commit. The lots' branches carry work commits only (the acceptance seal and
|
|
494
|
+
`vert: true` are work of the lot), so a plan conflict at the rebase cannot happen. A session that modifies the plan file, a file generated with it
|
|
495
|
+
(`plan.files`, the Gantt page) in its worktree — committed or not — gets its lot handed back, naming the file and the commit.
|
|
496
|
+
Before each of these writes the program checks that the repository is still on the branch the lots started from, that no tracked file other than the
|
|
497
|
+
plan is modified in the project's checkout, and takes the lock; a refusal hands the lot back with the cause
|
|
498
|
+
(`écriture du plan refusée : arbre du dépôt modifié (<files>)`, `dépôt sur <branch> et non <start branch>`, `verrou tenu par <wave> (pid <pid>)`), writes
|
|
499
|
+
and commits nothing, and keeps the step the lot reached.
|
|
500
|
+
- **Integration, before `ready`.** At the end of its cycle (compliant review, UX included) the lot is integrated into the repository's branch, and only
|
|
501
|
+
then does it become `ready`; the repository's branch gets the lots one by one, on a linear history. Under the repository's **integration lock**
|
|
502
|
+
(a file next to the wave lock, same format, pid and start time, stale when the holder is dead, plus an in-memory queue because the lots of a wave share
|
|
503
|
+
a pid; the wait is bounded to 10 minutes and holds no session slot):
|
|
504
|
+
1. the repository must still be on its start branch (`le dépôt est sur <branch> et non sur <start branch>`) and have no modified tracked file
|
|
505
|
+
(`arbre du dépôt modifié (<files>)`): the program never changes the lead's branch or tree. The check is made again right before the
|
|
506
|
+
fast-forward (the replayed checks and the short review can last) and right before the plan is written: a lead moved to another branch, or whose
|
|
507
|
+
tree got dirty, in the meantime gets neither the fast-forward nor a plan commit, and the lot is handed back with `next: integrate` (L299);
|
|
508
|
+
2. the lot's branch is rebased on the tip of the repository's branch (`git rebase`); a conflict is **aborted** (`git rebase --abort`), the lot
|
|
509
|
+
is handed back with the files in conflict, its worktree and branch untouched, and no session tries to resolve it;
|
|
510
|
+
3. if the rebase changed the commits, the tests and the build (`orchestrate.test`, `orchestrate.build`) are run again at the rebased tip, a red that
|
|
511
|
+
was not already red at the lot's base sha (L213) hands the lot back and leaves the repository's branch alone;
|
|
512
|
+
4. the patch-id of the lot's diff (`git patch-id --stable`, work commits only) is compared before and after the rebase. **Same patch-id**: the
|
|
513
|
+
reviewed diff is the integrated diff, the verdict is **carried over** to the integrated sha, with a `reporte: { de, vers, patchId }` trace in the plan
|
|
514
|
+
(and in the journal: `<project>:<lot> verdict reporté : <old sha> → <new sha> (patch-id identique)`); no new review. **Different patch-id** (context
|
|
515
|
+
changed): a short review (`review-small`) of the rebased diff is played before the branch moves, a blocking or major finding hands the lot back. A lot handed back after that short review (late refusal) and resumed with
|
|
516
|
+
`--resume --integrate` plays it again, and the new verdict replaces the old trace in the lot's verdict instead of being added to it (L303);
|
|
517
|
+
5. the repository's branch advances by `git merge --ff-only` (`<project>:<lot> intégré : <from>..<to>`), then the program writes the verdict and closes the
|
|
518
|
+
covered sub-tasks in a plan-only commit, on the tip. The verdict's `commit` is the last work commit integrated (`vers`), so the `raf done` gate,
|
|
519
|
+
**unchanged**, finds no work commit after the reviewed one; the `reporte` trace is information, the gate does not read it. A read-only plan gets the
|
|
520
|
+
trace in the text passed to `orchestrate.verdict`;
|
|
521
|
+
6. the worktree and the branch, clean and merged, are removed; the lot is `ready`.
|
|
522
|
+
A stop between the advance and the plan write is resumed at the `integrate` step: the branch being already contained in the repository's, the rebase
|
|
523
|
+
and the advance are skipped and the verdict is written once.
|
|
524
|
+
- **A lot handed back at the integration step** (lock held for more than 10 minutes, repository's branch changed, tracked file modified, rebase conflict,
|
|
525
|
+
red tests, blocking finding of the short review, plan write refused) keeps `next: integrate`, its worktree and its branch. **Only
|
|
526
|
+
`--resume <wave> --integrate <project:lot>` replays it** (repeatable): a plain `--resume`, `--drop`, `--answer` or `--budget` never does. The lot
|
|
527
|
+
restarts at `suspended`, step `integrate` (`<project>:<lot> → suspended — intégration reprise (--resume --integrate)`), and the rebase and the checks run
|
|
528
|
+
again — after a conflict the lead resolves it by hand in the worktree and commits, otherwise the lot is handed back again.
|
|
529
|
+
**A lot handed back on a refused plan write outside the integration** (the fiche's hypothesis notes, or the minor findings of the fiche review: the repository
|
|
530
|
+
is not on its start branch, holds a modified tracked file or the lock stayed held) keeps the step it had reached (`next`, e.g. `implement` or `fiche`) and its
|
|
531
|
+
worktree, and is replayed the same way by the general option `--resume <wave> --replay <project:lot>` (lead's decision of 24 October: the integration option,
|
|
532
|
+
generalised and renamed; `--integrate` keeps meaning the integration step only). The lot restarts at `suspended` on that step
|
|
533
|
+
(`<project>:<lot> → suspended — écriture du plan reprise à l'étape <step> (--resume --replay)`): the notes are written again without duplicates (a review of the
|
|
534
|
+
fiche is replayed and counted once). `--replay` is refused for a lot handed back in any other way, with the same exit code and rules as `--integrate`. Refused (exit 2, before
|
|
535
|
+
anything is written, like the other resume refusals): a lot unknown or named twice, also named by `--drop`, `--answer` or `--budget`, a lot that is not
|
|
536
|
+
handed back at the integration step (`le lot n'est pas rendu à l'étape d'intégration (<status>, étape <step or aucune> : <cause>)`: a lot handed back for
|
|
537
|
+
review, tests or budget has `next` empty and takes the recovery path below), and, like any resume, a repository with a modified tracked file.
|
|
538
|
+
- **A lot handed back outside the integration step** (non-compliant review, red tests, `HEAD` moved, plan touched, acceptance lock, quota…) keeps its worktree and its
|
|
539
|
+
branch `cadence/<wave>/<project>--<lot>` but is not replayed by `--resume`. Its recovery path (L301) is **a new wave of the same lot** (`cadence orchestrate <project>:<lot>`):
|
|
540
|
+
with the worktree mode on (`CADENCE_EXPERIMENTAL_WORKTREES=1`), the new wave looks for the **most recent** (creation time noted by cadence) recognised branch `cadence/<other wave>/<project>--<lot>`
|
|
541
|
+
of a past wave that carries unintegrated commits and started from the repository's current branch, renames it `cadence/<this wave>/<project>--<lot>` (git keeps the cadence config
|
|
542
|
+
with it), moves its worktree to this wave's folder and plays the lot there, so the sessions start from the work already done. The journal says it
|
|
543
|
+
(`orchestrate : <project>:<lot> : branche cadence/<wave>/… d'une vague précédente reprise (N commit(s) non intégré(s))`). Nothing is recovered when the branch is integrated,
|
|
544
|
+
started from another branch, is not cadence's, or belongs to a live wave; the lot then starts afresh as before. A branch whose worktree holds a modified tracked file (the same
|
|
545
|
+
guard as `--resume`), or one of whose commits modifies the plan or a file generated with it, is not recovered either (it would end in an integration conflict): the journal says why
|
|
546
|
+
(`… d'une vague précédente non reprise : <cause>`), the lot starts afresh and the kept branch stays untouched for the lead to clean or integrate by hand. The same refusal, with git's own message as the cause,
|
|
547
|
+
answers a `git worktree move` or `git branch -m` that fails (a locked worktree for instance): the move is undone and nothing is renamed. A lot handed back **at the integration step** (`next: integrate`) never takes this path: its
|
|
548
|
+
branch keeps its own wave's name and is replayed by `--resume --integrate`, so a kept branch of another wave is not renamed in its place. The recovery path needs the worktree mode on (`CADENCE_EXPERIMENTAL_WORKTREES=1`): without the
|
|
549
|
+
variable a new wave leaves every kept branch where it is. The branch of a lot of a wave started at the repository root (`.` as project) is `cadence/<wave>/_--<lot>` (the leading dot of the project becomes `_`).
|
|
550
|
+
**Integrating by hand** (a refused branch, or a lead who prefers to): in the kept worktree (`cadence orchestrate --status` lists it), (1) `git rebase <repository's branch>` and settle the conflicts; (2) run the project's tests and
|
|
551
|
+
build there; (3) from the repository, `git merge --ff-only <branch>` (the branch of the repository must not have moved since the rebase: otherwise rebase again); (4) write the verdict of the lot in the plan (by
|
|
552
|
+
the lead, never by a session); (5) `cadence orchestrate --remove-worktree <project>:<lot> [--wave <wave>]`, which removes the worktree and the branch now that all its commits are integrated.
|
|
553
|
+
- **Nothing is deleted with unintegrated commits.** A worktree whose commits are all in the repository's branch is removed after the integration, by a guarded
|
|
554
|
+
branch deletion (never `-D`). A lot handed back, dropped (`--drop`), failed or in conflict keeps its worktree and branch, and the journal gives the path
|
|
555
|
+
(`orchestrate : <project>:<lot> : worktree conservé : <path> (branche <branch>)`). The final table, `cadence orchestrate --status` and `cadence lead tour`
|
|
556
|
+
list each kept worktree: repository, lot, age, number of unintegrated commits, path (the worktree of a lot a live wave still plays is not « kept »: it
|
|
557
|
+
is in use and not listed). Only the lead removes one:
|
|
558
|
+
`cadence orchestrate --remove-worktree <project>:<lot> [--wave <wave>]` refuses (exit 2, nothing removed) while a commit is neither integrated nor
|
|
559
|
+
declared abandoned with `--abandon-commits`, while the worktree holds a modified tracked file or an untracked file that is not ignored (the removal would delete it),
|
|
560
|
+
and, with or without `--abandon-commits`, while a live wave (the waves registry) still plays the lot; a fully integrated, clean worktree is removed without the option
|
|
561
|
+
(`orchestrate : <project>:<lot> : worktree retiré (<n> commit(s) abandonné(s))`).
|
|
562
|
+
- **Resume of a lot whose branch is gone.** A lot handed back at the integration step whose branch was removed with `--remove-worktree --abandon-commits` has no
|
|
563
|
+
commit left to integrate: `--resume --integrate` creates no worktree for it at the repository's top (also when other lots of its group are resumed with it) and hands
|
|
564
|
+
it back again, `next: integrate`, with the cause « commits abandonnés ». The review verdict is never written against a null `commit`: an integration that finds
|
|
565
|
+
no commit of the lot in the repository's branch is handed back, nothing written in the plan.
|
|
566
|
+
A branch that is still there but lost its `branch.<name>.cadence-base` key (deleted by hand) is not a removed branch: the lot is handed back with a cause that
|
|
567
|
+
names the branch and the missing key, never « commits abandonnés », and nothing is integrated or removed (L303).
|
|
568
|
+
- **Resume.** The worktree of each resumed lot is found again by its name (a worktree git still lists whose folder is gone is pruned and recreated on the
|
|
569
|
+
existing branch) and is subject to the guards of a repository: a modified tracked file refuses the resume (exit 2, nothing written). The wave's lock
|
|
570
|
+
stays one per repository, taken once and released when all the lots of the repository are finished and integrated or handed back; a second wave aiming at
|
|
571
|
+
that repository is refused as before, without creating a worktree.
|
|
572
|
+
- **The application of a UX step.** A step that starts the application (`orchestrate.ux`) waits, in the program, for the application another lot of the
|
|
573
|
+
same repository started, without holding a session slot; steps that do not start the application never wait for it.
|
|
574
|
+
- **Slots.** `cadence orchestrate --status` counts a wave's parallel lots in its held slots, so the free slots are not overestimated.
|
|
575
|
+
|
|
396
576
|
## A lot whose work is in a neighbouring repository
|
|
397
577
|
|
|
398
578
|
Some lots are done in another repository that is not a cadence project (B64 of `maritime-atlas` lives in
|
|
@@ -436,7 +616,7 @@ the name `repos`). The effect, for `cadence orchestrate`:
|
|
|
436
616
|
- **Lock and guard**: the neighbour gets the repository lock (`orchestrate.lock`, so `cadence deliver` refuses it too, and no
|
|
437
617
|
other wave can hold it), the temporary `pre-push` hook, and the same after-session checks (push, tampered hook, a review
|
|
438
618
|
that changed it). Two lots that share a repository, as project or as neighbour, run one after the other, never two
|
|
439
|
-
sessions in one repository; `--status` lists the neighbours among the repositories a wave still holds (a repository released once all its lots are finished no longer appears).
|
|
619
|
+
sessions in one repository (a lot that declares `repos:` is never put in a worktree, so the rule holds for it even with `orchestrate.worktrees: true`); `--status` lists the neighbours among the repositories a wave still holds (a repository released once all its lots are finished no longer appears).
|
|
440
620
|
- **Sessions**: every session gets each neighbour as `--add-dir`, and the briefs name them: the implementation and the
|
|
441
621
|
corrections must commit there too, with a message that cites the lot (`feat(B64): …`), and never push; the reviewers read
|
|
442
622
|
the commits that cite the lot in the project **and** in each neighbour (`raf commits <lot>` lists them all; in a neighbour only the commits that cite the lot and are dated within the lot's period count, and also contain the `cite:` string when the lot declares one for it — a `fix(B64): …` outside the period, or one without the `cite:` string, is not listed.) A lot whose
|
|
@@ -469,10 +649,10 @@ A slot or a registry entry is owned by a pid **and** its start time: a reused pi
|
|
|
469
649
|
identifiers are reserved atomically (`-2`, `-3` suffix when two waves start in the same minute; an existing
|
|
470
650
|
`--wave` is refused). The registry of live waves and the slots live under `~/.cadence/orchestrate/` (`CADENCE_HOME`
|
|
471
651
|
to move it): `cadence orchestrate --status` lists, from any folder, the live waves, the repositories each
|
|
472
|
-
still holds (those not yet released), the cap of each wave, the slots in use and the **free slots** (`créneaux libres : N sur M`: M — the cap set with `--max-sessions` or `CADENCE_MAX_SESSIONS`, 2 by default — minus, for each live wave, the smaller of its cap and the repositories it still holds — the figure the `lead` skill reads before starting a subagent; the lead never raises the cap itself, that is the human's decision, L208). Without an identifier it then prints the table of the wave launched most recently from this folder (by launch time, not by alphabetical order of the identifier).
|
|
652
|
+
still holds (those not yet released), the cap of each wave, the slots in use and the **free slots** (`créneaux libres : N sur M`: M — the cap set with `--max-sessions` or `CADENCE_MAX_SESSIONS`, 2 by default — minus, for each live wave, the smaller of its cap and the repositories it still holds (default mode: a lot that declares neighbouring repositories holds each of them; with `orchestrate.worktrees` and lots playing in worktrees, the registry carries `lanes` instead: one per lot playing in a worktree, one per other group of lots) — minus the sessions the lead started by hand and declared with `cadence lead hold <label>` (L255, [lead.md](lead.md#lead-hold-and-release): the line then reads `… · sessions du lead : N (labels) · créneaux libres : …`, and prints even with no live wave) — the figure the `lead` skill reads before starting a subagent; the lead never raises the cap itself, that is the human's decision, L208). Without an identifier it then prints the table of the wave launched most recently from this folder (by launch time, not by alphabetical order of the identifier).
|
|
473
653
|
|
|
474
654
|
**Guards, imposed by the code**: a global cap of simultaneous sessions, one wave per repository at
|
|
475
|
-
a time (above); `Agent`, `git push`, `cadence deliver`, `raf done|review|ux` are denied to the sessions; a
|
|
655
|
+
a time (above); `Agent`, `Monitor`, `git push`, `cadence deliver`, `raf done|review|ux` are denied to the sessions, and they run with `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` (no `run_in_background` on Bash or sub-agents: `claude -p` waits for any background task before exiting, up to 10 min of inactivity, so a finished session kept its slot; L285 — the session is **not** killed at its `StructuredOutput`, the real cost is only in the final `result` message); a
|
|
476
656
|
temporary `pre-push` hook, installed for the duration of the wave and removed at its end (or, for a repository, as soon as all the lots that touch it are finished), refuses any push
|
|
477
657
|
from a session (`CADENCE_ORCHESTRATED` is in their environment; a repository that already has another
|
|
478
658
|
`pre-push` hook is refused before anything starts — `pushurl` is never touched); after every session the
|
|
@@ -480,7 +660,7 @@ upstream ref and `git ls-remote` are compared with the "before" (a `git checkout
|
|
|
480
660
|
`CADENCE_ORCHESTRATED` is set.
|
|
481
661
|
|
|
482
662
|
**Budget**: the wave counts input + cache writes + output tokens (default 2 M); cache reads are kept and
|
|
483
|
-
shown apart. Each lot also has its own budget, the 90th percentile of the *total* measured per lot on `.cadence/runs` (all the waves a lot went through, 250 lots) for its category (L194): an ordinary lot of up to 0.5 day gets
|
|
663
|
+
shown apart. Each lot also has its own budget, the 90th percentile of the *total* measured per lot on `.cadence/runs` (all the waves a lot went through, 250 lots) for its category (L194): an ordinary lot of up to 0.5 day gets 600 k (floor: a write pass, its review, one fix pass and the short review after it, 155 k being reserved for those last two; measured p90 374 k on 11-10, 451 k on 16–22-10: 3 of 145 lots over 540 k, all maritime-atlas, hence 600 k, L247; remeasured on 17–23-10: 0 of 309 lots over budget, p90 452 k, so unchanged, L280), 0.5 to 1 day 750 k (p90 732 k), then 570 k per day of estimate, 750 k staying the floor until that exceeds it, i.e. up to 1.3 day (p90 1.14 M for 2 days); a lot with `screen: page` gets 1.3 M whatever its estimate (`component` and `journey` keep the ordinary budget; p90 1.28 M: spec, mockup, accept, conformity UX and passes), or its ordinary budget plus 450 k beyond that; a lot with `spec: true` gets its ordinary budget plus 450 k (the back chain measured on L120: 388 k); shown by
|
|
484
664
|
`--dry-run` with its basis): it bounds the *writing* passes, never the reviews. A lot that spent it gets no further implementation or
|
|
485
665
|
fix and is handed back to the lead, but its review (and its single UX review, once the code review is compliant) is played first; a fix pass starts only if
|
|
486
666
|
the remaining budget can also pay the fix and the short review that follows it (155 k tokens reserved: the p90 of a fix
|
|
@@ -491,7 +671,7 @@ Exception: when the tests are red after a write pass, the lot goes to a fix pass
|
|
|
491
671
|
refused for lack of budget the lot is handed back to the lead without a review, with its findings, and the cause names
|
|
492
672
|
the red tests: the pass it names is the one whose tests were red, counted by its number (« la passe fix 2 », « l'implémentation »), and « la passe des mineurs » appears only when that pass was the one played. The wave budget stays for the others. When the budget (or the usage limit) is reached no new session starts, the running ones
|
|
493
673
|
finish, the wave is *suspended* (exit code 3) and `--resume --budget …` continues. A session that returns
|
|
494
|
-
nothing readable, times out (
|
|
674
|
+
nothing readable, times out (a work step: see « Time limit of a write step »; 25 min for a review) or fails is not retried; the lot is handed
|
|
495
675
|
back with the cause. Exit codes: 0 every lot ready · 1 at least one lot handed back (question, failure,
|
|
496
676
|
review still not compliant after two passes, or three when it converges) · 2 refused before acting · 3 wave suspended.
|
|
497
677
|
|
|
@@ -504,6 +684,10 @@ on `main`). A lot's commits are read from git (the commits that cite it, whateve
|
|
|
504
684
|
version commits do not count), not only from the sessions' reports, so a failed or timed-out session that committed still
|
|
505
685
|
bounds the line. Nothing is printed when every lot is ready or when the ready lots sit above the unfinished one.
|
|
506
686
|
|
|
687
|
+
**Delivering while the wave runs** (L277): the same sha can be delivered before the wave ends. `cadence deliver --sha <sha>` is accepted
|
|
688
|
+
during a wave of the same repository when no commit in the history of `<sha>` belongs to a lot of the wave that is not ready; the
|
|
689
|
+
refusal names the lots that do. A lot's commits are read from git (in the repository and in the neighbour repositories of its `repos:` key) as well as from the sessions' reports, so a session still running or cut by the quota counts; a lot whose commits cannot be read refuses the sha. Without `--sha`, `cadence deliver` is still refused until every lot of the repository is ready or handed back.
|
|
690
|
+
|
|
507
691
|
**Formatting retry** (the only `--resume` of a session): when a session ends successfully but in plain text,
|
|
508
692
|
without the `structured_output` the schema asks for (the verdict is there, not in the required shape), the
|
|
509
693
|
orchestrator resumes **that same session once** (`claude -p --resume <session-id> --json-schema <same schema>`,
|
|
@@ -512,9 +696,20 @@ format. It applies to every step that has a schema (implementation, correction,
|
|
|
512
696
|
retry count in the wave budget and in the step (`formatRetry: true`); if the output is still missing after it,
|
|
513
697
|
the step fails as usual — never a second retry.
|
|
514
698
|
|
|
699
|
+
**Cut review, relaunched once (L256)**: a read-only step (review, small review, UX, precheck, mockup review, sheet) whose session is
|
|
700
|
+
killed by a signal without writing anything (exit code above 128, such as 143 for SIGTERM, and an empty output) is *cut*. The
|
|
701
|
+
orchestrator launches the same step **once more in a NEW session** (a fresh `--session-id`, the same brief; never `--resume`: a cut
|
|
702
|
+
session has nothing to resume and the formatting retry above does not apply to it) before handing the lot back. The cut session's
|
|
703
|
+
tokens are read from its journal and counted in the wave budget **and in the step** (so the lot's own budget and the table's token column see them), its `stderr` is kept in `<n>-<kind>.first.err`, and the table shows
|
|
704
|
+
`<kind> relancée en session neuve après une coupure (code 143, sortie vide)` among the warnings. Cut again, the lot fails with `coupure : …`.
|
|
705
|
+
A session killed by the orchestrator itself (Ctrl-C or SIGTERM of the wave) is never relaunched: the new one would outlive it. A signal that arrives WHILE the new session runs kills it like any other: it is recorded as the step's session (`sessionId`) the moment it is launched, so the wave's stop kills it and counts its tokens, and so does the resume of a dead orchestrator (`countInterrupted`). Write steps (implementation, correction, spec, acceptance) are never relaunched, nor are the local model, a timeout, a non-empty output or
|
|
706
|
+
an ordinary exit code. The table tells the three stops apart in the line under it: `quota atteint` (the lot is suspended, the wave
|
|
707
|
+
resumes with `--resume`), `coupure : <kind> coupée (…), relancée une fois en session neuve, coupée de nouveau` (the session itself was
|
|
708
|
+
killed twice: look at the machine, not the lot), `échec : <kind> sans résultat : <cause>` (the session ended without a usable report).
|
|
709
|
+
|
|
515
710
|
State is in `.cadence/runs/<wave>/` of the folder where the command is run (added to `.git/info/exclude`
|
|
516
|
-
when that folder is in a repository
|
|
517
|
-
output, never count as a dirty repository, even when `.gitignore` does not list them): `wave.json`, one `<project>--<lot>.json` per lot (steps, tokens
|
|
711
|
+
when that folder is in a repository, together with `.cadence/corrections.json`; the rest of `.cadence/`, the plan and its notes, is versioned. `.cadence/runs/`, `.cadence/corrections.json` and `.playwright-mcp/`, where the Playwright MCP writes its
|
|
712
|
+
output, never count as a dirty repository, even when `.gitignore` does not list them, while a change to `.cadence/raf.yaml` does): `wave.json`, one `<project>--<lot>.json` per lot (steps, tokens
|
|
518
713
|
kept apart, session ids, commits, verdicts), the JSON output of every session and a `journal.log`. After a
|
|
519
714
|
cut (Ctrl-C, WSL closed) `--resume` replays an interrupted step entirely in a new session whose brief
|
|
520
715
|
lists the commits already present; finished steps are never replayed.
|
|
@@ -545,18 +740,20 @@ News instruction (`cadence news new <lot>`, factual user-side text, a screenshot
|
|
|
545
740
|
orchestrate:
|
|
546
741
|
test: npm test # run by the orchestrator after a work step (optional)
|
|
547
742
|
build: npm run build # run after the tests; both results go to the reviewer, who does not redo them (optional)
|
|
548
|
-
precheck: true # default: before the first implementation of a lot with no commit, 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
|
|
743
|
+
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)
|
|
549
744
|
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)
|
|
550
745
|
maquette: true # default: UX review of the mockup between spec and accept (see « The mockup review »); false skips it
|
|
551
746
|
maquetteReviews: 3 # default 3: how many reviews of the mockup per lot (integer ≥ 1); also the `spec` session budget of a lot with a screen
|
|
552
747
|
fiche: true # default: review of the back-end sheet of a lot with spec: true between spec and accept (see « The spec step of a lot with spec: true »); false skips it
|
|
553
748
|
spec: { dir: docs/specs } # spec step (see « The spec step of a lot with screen: »): folder of the screen specs, relative to the repository (default docs/specs); render: the command the spec session runs to render the mockups at 1440 and 390 px (optional, Playwright otherwise)
|
|
749
|
+
testGate: { code: [src/**] } # code changed without a test goes to a correction pass before the review (see « Code changed without a test »); absent: no gate
|
|
554
750
|
prodAccess: false # default: wave sessions get no access to production (see « Production access of the sessions »); true lifts it for this project
|
|
751
|
+
worktrees: false # EXPERIMENTAL, true is refused until L299–L301 are done (CADENCE_EXPERIMENTAL_WORKTREES=1 lifts it, tests only); default: one lot at a time per repository, in the repository itself; true plays independent lots of one repository in parallel, each in its own git worktree (see « One worktree per lot »)
|
|
555
752
|
effort: { review: xhigh } # effort level per pass (see « Effort level per pass »): precheck, spec, maquette, fiche, accept, implement, fix, review, ux
|
|
556
753
|
ux: http://localhost:4200 # a URL, a launch command, or { command, url, timeout? } — for the UX review (see below)
|
|
557
754
|
permissionMode: auto # default
|
|
558
755
|
addDirs: [/home/me/projects/tmp] # extra directories the sessions may use
|
|
559
|
-
timeouts: { implement: 45, review: 25 } # minutes
|
|
756
|
+
timeouts: { implement: 45, review: 25 } # minutes; `implement` is the base for a 0.25-day lot (see « Time limit of a write step »)
|
|
560
757
|
# a plan kept by the project's own tool is read-only for raf: the orchestrator calls these instead
|
|
561
758
|
start: python3 scripts/raf.py start {lot}
|
|
562
759
|
verdict: python3 scripts/raf.py note {lot} "revue de code : {verdict}"
|
|
@@ -629,11 +826,25 @@ the review stay with the wave, outside the repository, where the lead can look a
|
|
|
629
826
|
folder whenever Playwright was loaded for the lot, even if no `ux` step ran
|
|
630
827
|
(`<project>:<lot> — captures Playwright : <dir>`; `uxCaptures` in the wave state). For `maquette` and `spec` the server also gets
|
|
631
828
|
`--allow-unrestricted-file-access`: the mockup is a local file, and by default the server blocks every `file://` URL. The `implement`, `fix`,
|
|
632
|
-
`fix-minors`, `review-small` and `
|
|
633
|
-
otherwise to keep any Playwright CLI output outside the repository.
|
|
829
|
+
`fix-minors`, `review-small`, `ux`, `maquette` and `spec` briefs carry a `{{shots}}` paragraph (L249) when the step loads the server: the absolute path of the wave's capture directory (`<lot folder>/playwright`), to put in front of every screenshot, snapshot and trace name. Measured on `@playwright/mcp` 0.0.83, `--output-dir` only covers the files the server names itself; a name given by the session is resolved against the server's workspace root, the repository, which is why « a relative file name only » left `savings-1440px.png`… at the root of 5 lots out of 5 on 2026-10-23 (finance L119 L120 L122 L124, warhammer L175). The server allows the absolute path (it lies in its output directory). A brief delegated by hand (`--dry-run`) has no wave folder, so no `{{shots}}` (a test reads the briefs `--dry-run` writes to check it); the `ux-reviewer` and `qa-reviewer` agents, which can also be run by hand with the Playwright plugin, give a path under `.playwright-mcp/` there (`.playwright-mcp/page-home.png`), never a bare name, which the server would put at the repository root. The brief otherwise tells the session to keep any Playwright CLI output outside the repository; `stray/` stays the safety net. The `review-small` brief also forbids a capture at the root of the reviewed repository (`romans-1440px.png`…, four times on 2026-10-17: finance L82 and L84, warhammer L138 and L139): it says that a capture reaches the wave's run directory only through the absolute path of `{{shots}}`, and that a bare file name is resolved against the repository (L237, L249).
|
|
634
830
|
`--dry-run` prints, under each step, `mcp : aucun` or `mcp : playwright (…)`. There is no per-project override in
|
|
635
831
|
`cadence.yaml` yet.
|
|
636
832
|
|
|
637
833
|
Without `start`, a read-only plan's `todo` lot is refused (start it with the project's tool); without
|
|
638
834
|
`verdict`, the review verdict stays in the wave's state and you report it. Only the plan's files
|
|
639
|
-
(`plan.path`, `plan.files`, the QA expectations file) are committed from those commands; anything else dirty stops the lot (`.cadence
|
|
835
|
+
(`plan.path`, `plan.files`, the QA expectations file) are committed from those commands; anything else dirty stops the lot (`.cadence/runs/`, `.cadence/corrections.json` and `.playwright-mcp/` excepted, as above).
|
|
836
|
+
|
|
837
|
+
### Time limit of a write step (L298)
|
|
838
|
+
|
|
839
|
+
`timeouts.implement` (45 min by default) is the limit of a lot estimated at 0.25 day. The limit of a write step
|
|
840
|
+
(implementation, fix, spec, acceptance) is **proportioned to the lot**: multiplied by `estimate / 0.25` (never below 1: a
|
|
841
|
+
0.5-day lot gets 90 min), by 1.5 more when the lot has a sheet (`screen:` or `spec: true`), and never above 4 times the
|
|
842
|
+
base. The estimate is kept in the lot's state; a state written before L298 has none and keeps the base. A review keeps
|
|
843
|
+
`timeouts.review`.
|
|
844
|
+
|
|
845
|
+
The limit is also **extended while the session commits**: at each deadline, if HEAD moved since the previous deadline (or since
|
|
846
|
+
the start), the session gets 15 more minutes, 8 times at most (2 h at the most after the proportioned limit); with no commit since,
|
|
847
|
+
it is cut. A cut write step is no longer a bare « failure »: the table gives the commits kept, the files left **uncommitted**
|
|
848
|
+
(with those that are tests, and the lines added) and says the work stays in the repository. A cut that left commits or files
|
|
849
|
+
hands the lot back to the lead; a cut that left nothing is a failure. For a lot with neighbouring repositories (`repos:`, L303),
|
|
850
|
+
the report counts the commits and the uncommitted files of each neighbour too, prefixed with its name (`../gitops: chart.yaml`).
|