@sylad/cadence 1.33.0 → 1.35.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/NOTICE +28 -0
- package/README.md +2 -2
- package/agents/qa-reviewer.md +5 -4
- package/agents/spec-writer.md +2 -0
- package/agents/ux-reviewer.md +28 -0
- package/dist/cli.js +13 -29
- package/dist/config.js +16 -30
- package/dist/corrections-sans-code.js +12 -0
- package/dist/corrections.js +4 -5
- package/dist/deliver.js +4 -4
- package/dist/gate-config.js +47 -0
- package/dist/orchestrate/accept.js +5 -2
- package/dist/orchestrate/baseline.js +57 -0
- package/dist/orchestrate/briefs.js +5 -1
- package/dist/orchestrate/command.js +6 -6
- package/dist/orchestrate/cycle.js +119 -140
- package/dist/orchestrate/gate-notes.js +27 -0
- package/dist/orchestrate/gate-parts.js +135 -0
- package/dist/orchestrate/gate-place.js +66 -0
- package/dist/orchestrate/precheck.js +79 -0
- package/dist/orchestrate/probe.js +124 -0
- package/dist/orchestrate/schemas.js +10 -1
- package/dist/orchestrate/state.js +1 -1
- package/dist/orchestrate/workers.js +31 -4
- package/dist/perf.js +1 -1
- package/dist/plan-commit.js +4 -2
- package/dist/plan-uncommitted.js +26 -0
- package/dist/release.js +9 -7
- package/dist/remote-gate.js +278 -0
- package/dist/verify-gate-remote.js +55 -0
- package/dist/verify-gate.js +37 -28
- package/dist/yaml-keys.js +6 -0
- package/docs/corrections.md +4 -0
- package/docs/deliver.md +4 -2
- package/docs/news.md +2 -4
- package/docs/orchestrate.md +40 -2
- package/docs/overview.md +1 -0
- package/docs/raf.md +3 -3
- package/docs/releasing.md +16 -7
- package/docs/remote-gate.md +89 -0
- package/docs/skills.md +5 -2
- package/docs/whats-new.md +9 -0
- package/package.json +3 -1
- package/skills/lead/SKILL.md +1 -1
- package/templates/orchestrate/probe.md +20 -0
- package/templates/orchestrate/spec.md +3 -1
package/docs/orchestrate.md
CHANGED
|
@@ -198,6 +198,19 @@ usability (code only when the lot is not `visible`). Failing tests (reported red
|
|
|
198
198
|
|
|
199
199
|
**A red test is replayed alone before it counts, only on a declared command (L354)**: on 2026-10-26, with 32 cores and a cap of 4 sessions, every suite ran vitest at 31 workers, and four correction passes (L344 11 min, L342 9, L349 7, B28.12 4) found nothing to fix — each time « a test that passes alone ». cadence does not guess how a file is replayed on your runner (a first version appended the files to `orchestrate.test` and, on a composed gate, excused a real regression), so the replay is **off unless the project declares its command**: `orchestrate.testReplay`, where `{files}` is replaced by the files of the red tests, e.g. `testReplay: "npx vitest run --maxWorkers=$CADENCE_TEST_WORKERS --minWorkers=1 {files}"` (a value without `{files}` is refused when the file is read). **Without it nothing is replayed and nothing is held for unstable**: a red test is counted as before. With it, a red test of the gate (`orchestrate.test`, played after every writing session and at the rebased tip) that is absent from the baseline is **replayed alone, twice at most**, by that command, in the lot's worktree (vitest names `file > suite > test`, pytest `file::test`, jest `FAIL file` and `● Suite › test`, the latter tied to the `FAIL` line that precedes it in the same output and replayed with that file; the file is the part before the first `::` or ` > `, so either of them inside a test title is not part of the path). Green at a replay, the test is **« instable sous charge »**: it is not counted, it triggers no correction pass, even when the session reported its tests red (`vert: false`: a session that plays the suite under the same load sees the same red; the announcement is not held against the lot when every red of the gate is unstable, as it is not when every red was already red at the start), the lot goes on to its review, and the trace is a lot warning (so a line of the final table), a journal line `<project>:<lot> · N test(s) instable(s) sous charge …`, the lot's `instables` list in its journal, and the `cadence perf` line `tests instables sous charge` (`instables` in `--json`). Red at both replays it is counted as before. A red name that designates no file (a plain `FAIL name`) cannot be replayed alone and is counted at once. **A replay in which the test did not run counts red**: only a replay that exits 0 after running tests proves a test green, and only for the tests whose file appears in the replay's output, as a whole path on a line of the runner — never in the launcher's echo of the command: npm 12 writes `npm notice run 'vitest' run … <files>` on stderr for `npx …` as for `npm test -- …`, npm and pnpm write `> vitest run … <files>`, yarn `$ …`, and those lines name every file asked for, played or not (a runner that silently skips a file it cannot find, such as vitest given a path of another workspace, exits 0 without playing it: that test stays red; with a reporter that does not list files, nothing is ever excused; `a.test.ts` is not found in `src/a.test.ts`); a replay that fails, or that runs no test (the runner's closing line `No test files found, exiting with code`, `No tests found, exiting with code` or `no tests ran in`, even with exit code 0; the same words inside a test title or a test's output do not count), leaves every test it was asked about red. In a vitest workspace the names read `|package| src/a.test.ts > case`: the `|package|` prefix is dropped to find the file. The reviewer is told which tests were red under load (`instables` of the check, also when the baseline holds red tests of its own), so it does not report them, and the correction finding lists only the tests still red after the replays. The baseline run is included: with `testReplay`, a red test of the L213 baseline (the gate played at the start sha, in its throwaway worktree) is replayed alone there, twice at most, before it is retained as « already red at the start »; green at a replay it is dropped from the baseline (a journal line `… · ligne de base <sha> : N test(s) rouge(s) sous charge, vert(s) rejoué(s) seul(s), non retenu(s) …`), so a test the lot really breaks is not excused by a load failure of the start sha. The baseline replays are not the lot's « instables »: they say nothing about the lot's code. **A composed gate is not supported (decision of 26-10)**: `orchestrate.testReplay` is refused when `cadence.yaml` is read if `orchestrate.test` has `&&` or `||` outside quotes, or a parenthesis (sub-shell `( … )`, `$( … )`, function): a first runner that goes red cuts the next one, so a green replay of its tests says nothing of the rest, and a replay command written for one runner of the gate cannot replay the tests of the other. Write the replay for a gate that launches a single runner; without `testReplay` a composed gate is played as before, nothing is replayed. A jest `FAIL` line may carry a duration (`FAIL src/a.test.ts (6.21 s)`) or a project name (`FAIL backend src/a.test.ts`): the file is its last word, and any other `PASS`/`FAIL` line ties no file to the `●` lines that follow. The `FAIL project file` line is itself a red name (`project file`) and is replayed with that file, like the `●` tests under it.
|
|
200
200
|
|
|
201
|
+
**A test that expires close to its limit even alone is a « délai trop juste », not a bug (L510)**: on 2026-10-31 a test of warhammer (L61) took 25 s alone for a 30 s delay: it passed alone, expired under the slightest load, and the gate sent four correction passes and the escalation for a defect that was not one. A composed gate declares its parts (`orchestrate.test` as a list, L512, below) to get the same. The replay alone now also reads the **duration** of the test (its own line `✓ file > test 25012ms`, else the `✓ file (3 tests) 24s` line of its file, an upper bound) and the **delay** it expired at in the whole-suite output (vitest `Test timed out in 30000ms`, jest `Exceeded timeout of 5000 ms for a test`). Green alone at **80 %** of that delay or more, the test is no longer filed with the unstable-under-load ones: no correction pass, the lot's `delaisJustes` (test, duration, delay) and a warning that names it (`délai trop juste, pas un bug … : test/a.test.ts > plan-data (25 s seul pour 30 s de délai) — relever le délai du test`), shown in the end-of-wave table. A test still red alone is a bug whatever its delay; a test with no delay in the suite output or no readable duration stays an ordinary unstable test.
|
|
202
|
+
|
|
203
|
+
**A red already red at the baseline triggers neither a correction nor the escalation (L510)**: on 2026-10-31 evatosorus L45 got a Fable correction on the drawer test of L23, a test that was red before the lot. A composed gate declares its parts (`orchestrate.test` as a list, L512, below); `node --test` (evatosorus) names no file and waits for L514. The baseline (L213) is one run of the whole suite, replayed alone for what it sees red (L354); a test that run did not see red (order, load, state left by another test) but that is red **alone** at the start sha was not excused. With `orchestrate.testReplay`, a test still red after its replays alone in the lot is now replayed alone at the start sha too (a throwaway worktree, the build first): named as failing by two replays that exit in failure, it is already red: `dejaRouges` on the gate run, a warning that names it, no correction pass and no escalation for it. A replay at the start sha that fails without naming the test (another test red, a build that does not run) excuses nothing; a test with no file to replay alone is never excused; without `testReplay` nothing is replayed at the start and the red counts as before. When other tests of the same run are new reds, only those are sent to the correction.
|
|
204
|
+
|
|
205
|
+
**A gate declared in parts: each part runs in its folder, each red is replayed by the part that printed it (L512)**: on 2026-10-31 the gate of warhammer was `(cd backend && npm test) && (cd frontend && npx vitest run …)`, a composed gate for which `testReplay` is refused, so neither the replay alone (L354) nor the « délai trop juste » and « déjà rouge » of L510 applied: four correction passes and an escalation for false reds (L61). `orchestrate.test` now also takes a **list of parts**, `[{ dir, run, replay? }]`: `dir` is the folder the part runs in (relative to the repository, no `..`, unique), `run` its command, `replay` (optional) its replay-alone command, with `{files}`. cadence runs **every part separately, in its folder** (`cd <dir> && <run>`, on the host of `gate.host` too), all of them even when one is red (a `&&` stops at the first red and hides the next parts), and **attributes each red test to the part whose output printed it**: no reading of a shell chain, no path prefix, no disk access. A red of a part that declares `replay` is replayed alone by that command, in that folder, with the files that part printed (twice at most; the L354 and L510 rules are unchanged: exit 0 with the file on a line of the runner, duration against the delay, replay alone at the start sha, baseline included); a part without `replay` keeps its reds counted as before, and so does a name that designates no file. **A name printed red by several parts is replayed in each of them and is green only if every one of those replays is green** (a part without `replay` can never clear it): never a false green. The string form of `orchestrate.test` is accepted unchanged (one block; `testReplay` still refused on a composed chain); `testReplay` next to a list is refused (declare `replay` in each part); a part whose folder does not exist makes the gate red (`can't cd to <dir>`) and nothing is replayed — more generally **a red part that names no test** (compile error, crashed runner, `cd` failure) **or whose output carries an unnamed failure** (coverage threshold, a file that does not load) blocks every replay: the gate stays red even when the named reds of another part pass alone, as a string gate does on an unnamed failure. At the baseline (start sha), where the replay alone *removes* excuses, each part is still judged on its own: a part red without a named test there withholds only its own reds (none), and the load reds of the other parts are replayed by their part and dropped, so a real regression of the lot is never « déjà rouge ». Wherever the gate is shown, a list reads `dir : run ; dir : run` — the lot's check run and the cache key of its baseline (which `cadence verify --gate` compares to take over a wave's verdict), the `tests` step of `verify --gate` (which also names the red parts), the accept session's prompt, the green-gate warning — and the implement, fix, probe, minors and librarian briefs give one `tests (run in \`dir/\`)` line per part. `node --test` (evatosorus) names a test by its title, never by its file: a lot of its own (L514), on top of the parts. warhammer declares:
|
|
206
|
+
|
|
207
|
+
```yaml
|
|
208
|
+
orchestrate:
|
|
209
|
+
test:
|
|
210
|
+
- { dir: backend, run: npm test }
|
|
211
|
+
- { dir: frontend, run: "npx vitest run --exclude '**/*.e2e.spec.ts'", replay: "npx vitest run --minWorkers=1 --maxWorkers=$CADENCE_TEST_WORKERS {files}" }
|
|
212
|
+
```
|
|
213
|
+
|
|
201
214
|
**Each session gets its share of the machine (L350)**: the wave computes `CADENCE_TEST_WORKERS = max(1, cores ÷ session cap)` (`--max-sessions` or `CADENCE_MAX_SESSIONS`; 32 cores and a cap of 4 give 8; a `CADENCE_TEST_WORKERS` already set in the environment that launches the wave wins when it is a positive integer). It is put in the environment of every session and of every command of the gate (tests, build, baseline, replays), recorded as `testWorkers` in the wave state, and the `implement`, `fix`, minors and librarian briefs tell the session to respect it: `vitest run --minWorkers=1 --maxWorkers=$CADENCE_TEST_WORKERS` (vitest 2 refuses `--maxWorkers` alone, its default minimum being the core count: « options.minThreads and options.maxThreads must not conflict »), `jest --maxWorkers=$CADENCE_TEST_WORKERS`, `pytest -n $CADENCE_TEST_WORKERS`. The gate command itself must honour it for the share to bind: cadence's own `vitest.config.ts` reads it (`poolOptions.forks.maxForks`, `threads.maxThreads`); in another project write `orchestrate.test: npx vitest run --minWorkers=1 --maxWorkers=$CADENCE_TEST_WORKERS` (or the equivalent of its runner).
|
|
202
215
|
|
|
203
216
|
**The session plays the gate's own commands (L399)**: the briefs of the writing sessions (implement, fix, minors pass, librarian pass) name the exact `orchestrate.test` and `orchestrate.build` of the project (`The program's gate, played after every writing session, is exactly: …`) and ask to run them as written, not a narrower equivalent. Measured on 22–28-10 (`docs/mesures/portes-rouges-2026-10-28.md`): sessions that played `npm run build` (`src` only) announced a green lot that `npx tsc --noEmit` (tests included) sent back to a correction pass. Without `orchestrate.test` and `orchestrate.build` there is no line.
|
|
@@ -262,7 +275,7 @@ table at the end of the wave, and is the lead's to take into the plan of the oth
|
|
|
262
275
|
|
|
263
276
|
**Effort level per pass** (L137). Each session is launched with `claude -p --effort <level>` (Claude Code 2.1.284; an
|
|
264
277
|
older `claude` is refused at launch unless every pass is `default`). Defaults: `precheck` low (it only reads),
|
|
265
|
-
`implement` and `fix` medium, `review` high (`review-small` follows `review`), `ux` high. A project overrides any of them
|
|
278
|
+
`implement`, `probe` and `fix` medium, `review` high (`review-small` follows `review`), `ux` high. A project overrides any of them
|
|
266
279
|
in `cadence.yaml`; the levels are `low`, `medium`, `high`, `xhigh`, `max`, and `default` sends no `--effort` (the
|
|
267
280
|
level of the session, as before this key). The format-retry session (`--resume`) keeps the level of its pass, and
|
|
268
281
|
`--dry-run` shows the flag in each command line, except for a `precheck: local` session: `claude-local` is launched
|
|
@@ -270,7 +283,7 @@ without `--effort` (see below), so `effort.precheck` has no effect on it and the
|
|
|
270
283
|
|
|
271
284
|
```yaml
|
|
272
285
|
orchestrate:
|
|
273
|
-
effort: { precheck: low, spec: medium, maquette: high, fiche: medium, accept: medium, implement: medium, fix: medium, review: high, ux: high } # defaults
|
|
286
|
+
effort: { precheck: low, spec: medium, maquette: high, fiche: medium, accept: medium, implement: medium, probe: medium, fix: medium, review: high, ux: high } # defaults
|
|
274
287
|
```
|
|
275
288
|
|
|
276
289
|
To measure what a level costs, run the same kind of wave before and after changing the key: each step of
|
|
@@ -299,6 +312,7 @@ the first implementation: `precheck → spec → maquette → accept → impleme
|
|
|
299
312
|
`maquette-<état>-<largeur>.png` (1440 and 390), and `fiche.yaml` (the template `templates/fiche.yaml` is part of its brief). The mockup is rendered by
|
|
300
313
|
`orchestrate.spec.render` when the project declares it (the session runs that command), otherwise with Playwright; the captures land in the wave's output directory and the session copies the
|
|
301
314
|
kept ones into the folder. `effort.spec` (default `medium`) sets its effort level. It commits the whole spec in one commit that cites the lot (`spec(L7): …`).
|
|
315
|
+
- **Colours the screen adds (L513).** The `spec-writer` names each one as a semantic token by its role with its text token `on-<role>` (a colour without its `on-*` is a defect of the spec), takes the values from a ramp drawn from the charter's brand colour (same hue, stepped lightness; no external palette) and creates no duplicate of a token the charter already has (the existing pair is only measured). It writes `contraste.md` in the spec folder, committed with the sheet: two lists (tokens created, existing pairs measured), one line per token / on-token pair with both values, the WCAG 2.2 ratio and `ok` or `non`; a pair under 4.5:1 is fixed by another step of the ramp, never by the text size. `contraste.md` is a free file: the program does not read it, and the sheet format is unchanged. With no CSS variable and no brand colour it invents no ramp, measures the existing values and writes the absence of a coloured charter in the sheet. On a product with a charter, neither the `spec-writer` nor the `ux-reviewer` proposes, draws or blames a generic catalogue style.
|
|
302
316
|
- **What the program checks** afterwards, with no model: the sheet passes `cadence spec check`, is the lot's (`ecran.lot`) and sits in the folder named by `ecran.id`; `maquette.html` exists with a
|
|
303
317
|
`#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`).
|
|
304
318
|
- **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,
|
|
@@ -410,6 +424,7 @@ Once the sheet exists, the steps after it all read it (L174, `src/orchestrate/av
|
|
|
410
424
|
- **Decisions to respect (L344).** The program (not the pre-check session, which many lots skip) reads the notes of the lot and of its parent lot (`parent:`) and keeps those that name who decided (Sylvain, the human, the lead, the user) and say that they decided, validated, asked or refused (`src/orchestrate/decisions.ts`). The notes the program writes itself are never taken for one, whatever they quote: a hypothesis of a fiche (« Hypothèses (fiche …) — », a choice nobody validated), a minor of a review (« Revue de … — mineur : »), a replaced title (« titre remplacé : », the stale one), « remis en attente » and « abandonné : ». Each is given with its date and its text as written (cut at 1500 characters) under « Decisions to respect » in the `implement` and `fix` briefs (and in the `--dry-run` briefs), with the rule: an interpretation that departs from one of them is a question under `"questions"`, never a choice under `"choix"`. Nothing is written when no note qualifies. The effect is measured by the share right the first time of `cadence perf` over two windows.
|
|
411
425
|
- **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.
|
|
412
426
|
- **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. It gets the verification of the reference ids the lot quotes there too (L367, see above).
|
|
427
|
+
- **Delivery grid and AI tics (L513).** A last section of `agents/ux-reviewer.md`, `## Delivery grid`, applies to the `ux` step of a visible lot and to the review of an existing screen, not to mockup or conformity mode (their sections and `templates/orchestrate/ux.md` are unchanged). Five points, each a line of the checklist of the report file when the brief names one (the 15-line short reply does not repeat them); without a `Report file:` line, which is the case of the orchestrated `ux` step (structured report, no checklist, schema unchanged), a point that could not be checked goes in `nonVerifie` and the `verdict` line ends with the tally of the grid and the tics line (`grille 5/5 — tics d’IA : 0 constat`): visible focus, `prefers-reduced-motion`, `cursor: pointer`, no emoji as an icon, no horizontal scroll at 375, 768, 1024 and 1440 px; a width that cannot be rendered goes in `nonVerifie`, a failing point is a finding on the usual blocking / major / minor scale. The same section carries a static AI-tic check: the reviewer applies seven named rules (of the 16 of `detect.mjs`; the nine others are not kept, with their reasons in the agent) while reading the diff (it never runs `detect.mjs`; the tools line is unchanged), each tic being a numbered finding T1, T2… (file, line, rule, severity), minor when it has no effect on the task or legibility, `tics d’IA : 0 constat` or `non applicable` otherwise; a tic that is a choice of the product's charter is not a finding. The rules are reworded from `detect.mjs` (Apache-2.0 for the rules ported from Impeccable, as its header limits it; MIT for `purple-triplet` and `unmodified-shadcn-tokens`, its own rules; file and commit read are in `NOTICE`); the attribution is in the `NOTICE` file of the package, which stays under MIT.
|
|
413
428
|
- **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.
|
|
414
429
|
|
|
415
430
|
```markdown
|
|
@@ -535,6 +550,25 @@ except a copy that is still needed, which stays and is said: a copy that a live
|
|
|
535
550
|
The waves that count are those of the launch folders the script knows (`<folder>/.cadence/runs/<wave>/wave.json`): the repository itself and the working directory of every wave the orchestrator's registry still lists (`~/.cadence/orchestrate/waves/<pid>.json`; the registry drops a dead wave when another registers, so it does not know suspended waves by itself). There is no search over the whole disk: a suspended wave launched from another folder is not protected here, and `--resume` repairs its snapshot instead (above). A wave never marked `done` keeps its copy until it is, or until its folder is removed. A failure of the purge (unreadable copy, busy file) once `service` and the links have switched is a warning on the error output (`avertissement : purge des anciennes copies interrompue …`): the new version is in service, the fingerprint is printed, the exit code is 0, the next put in service tries again. The same run updates the **local copy of the `cadence-hud` mod** (L419), the one `CLAUDE_CODE_PLUGIN_DIRS` makes Claude Code load (`~/.claude/mods/cadence-hud`, `--mod-dir` for another folder): `plugins/cadence-hud` of the copy just put in service (not the working repository) is synchronised into it with `rsync -a --delete`, which leaves out `.claude-plugin/types` (generated by Claude Code in the copy). The output says `mod cadence-hud : copie locale mise à jour (<dir>)`. A machine without that folder gets no copy created (`copie locale absente … non mise à jour`), and a failed `rsync` is a warning on the error output (`avertissement : mod cadence-hud non mis à jour …`), exit code 0 like the purge: the version is in service. Before L419 the copy was updated by hand at each release and a forgotten one left the old mod loaded. The first run is also what installs the links in `~/.local/bin`. It prints the fingerprint of the copy
|
|
536
551
|
(bin, dist, package.json).
|
|
537
552
|
|
|
553
|
+
## The edge-case probe (`orchestrate.probe`, L497)
|
|
554
|
+
|
|
555
|
+
Code bugs are the first cause of correction passes, and an edge case is the first sub-cause of those (64 % of the `bug-code` passes since the instruction of L447, 69 % before: writing it in the brief changed nothing). `orchestrate.probe: true` adds a step between the writing and the review: a **fresh Sonnet session** (`probe`, brief `templates/orchestrate/probe.md`, `effort.probe` medium, the delay of a write step `timeouts.work`) that has not written the code, reads the lot's diff, and tries to break it with tests at the edges: empty, absent, twice, wrong type, refused, the real shape of the data, the other callers. The diff is given as a command (`git diff <start sha>..HEAD`), never pasted; the sheet of the lot is named when there is one. Like `implement` and `fix`, the brief gives the program's exact gate (L399: the probe commits tests the gate replays, `tsc` and the quality script included) and the session's machine share (L350). The session writes its tests in new files and commits them all, the passing and the failing, with `probe(L<n>): …`; it does not touch the application code nor the sealed acceptance tests.
|
|
556
|
+
|
|
557
|
+
```yaml
|
|
558
|
+
orchestrate:
|
|
559
|
+
probe: true # true or false; absent = nothing changes (no session, no field in the journal, no line in the output)
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
**When it plays.** Once per lot, after a writing session whose gates are green (tests, build, acceptance) and after the « code sans test » gate, before the librarian and the review; never during the minors pass, never for a lot with no commit. It is recorded in the lot's state (`probe`): a resume does not replay it, except after a quota, where it is the step that was interrupted. `--dry-run` lists the `probe (sonnet)` step between `implement` and `review`.
|
|
563
|
+
|
|
564
|
+
**What a red test does.** After the session the program plays its own gate (`orchestrate.build`, `orchestrate.test`) as after any writing. A test red **now and not at the start sha** (never a failure already there, L213) sends the lot to a correction pass (`fix`) before the review, with a finding of severity *major* whose text starts with `sonde de cas limites : ` and names the test; the probe's own report (`echecs`, « test — edge — expected ») is added to it so the fix session sees the edge. Without `orchestrate.test` there is no gate to read: the failures the session announces are taken as they are, with the warning `sonde de cas limites : échecs annoncés par la session, non vérifié par le programme (orchestrate.test absent)`. All the probe's tests stay in the lot. A test outside `orchestrate.testGate.tests` committed by the probe is a warning that names the files (`sonde de cas limites : la session a modifié du code hors tests (…)`); nothing is removed.
|
|
565
|
+
|
|
566
|
+
**Cost and limits.** The probe's session counts in the lot's tokens and budget. The correction it triggers is one of the two ordinary passes (`MAX_PASSES`): after it, one pass is left for the review, so the librarian is not called (the `bibliothécaire non appelé` warning says so, and the review carries the documentation gap). The probe does not play when a correction pass is already done, or when the budget does not pay the probe (estimated at 65 k tokens, `PROBE_RESERVE`, no measure yet), the correction and the review that follows: the warning `sonde de cas limites non jouée : <n> passe(s) de correction déjà faite(s) ou budget du lot (<count>) insuffisant` says so and the review goes ahead. If the red tests cannot be corrected for lack of budget after the probe, the lot goes back to the lead without a review, with its findings, as for any red tests after a writing (L145). Without a new commit from the probe, the review keeps the results of the gate played after the writing (it does not rerun the suite and the build).
|
|
567
|
+
|
|
568
|
+
**It never blocks the lot.** A failed, cut or timed-out session, or an unreadable report, is the warning `sonde de cas limites non jouée : <cause>` and the review goes ahead; the questions of the report become warnings, nobody waits. A quota suspends the wave like any session and the resume replays the probe. What a cut session committed before the cut goes through the same checks as an answered one (commits in the journal, lock on sealed tests, warning for files outside the tests). What a failed or cut session leaves uncommitted, staged or not (a cut between `git add` and the commit is ordinary), is put back (index unstaged, tracked files restored, new files removed) with a warning that names the files, so a timed-out probe never hands the lot back for a dirty tree. Only a violation of the lock on sealed acceptance tests, or a dirty repository after a session that did answer, hands the lot back, as after any writing session.
|
|
569
|
+
|
|
570
|
+
**Measuring it.** The key is set in cadence's own `cadence.yaml` only, as a trial, and the lead sets it to `true` after the release is in service. Keep or drop it on figures: `cadence corrections <folder> --since <date>` (share of `bug-code` and of `cas limite` among the correction passes, lots with 2 passes or more, L430) and `cadence perf` (the `probe` step has its own row; tokens per lot). The `probe` step keeps the texts of the findings it sent to correction (`constats`, each starting with `sonde de cas limites : `): `cadence corrections` reads them as the findings of the `fix` pass that follows, in place of a review's. The pass is also told apart by the `probe` step that precedes the `fix` step and by the lot's warning `sonde de cas limites : <n> constat(s), passe de correction avant la revue`; the finding given to the fix session starts with `sonde de cas limites : `.
|
|
571
|
+
|
|
538
572
|
## The subject of the plan commits (`plan.commit`, L343)
|
|
539
573
|
|
|
540
574
|
The commits the program writes for the plan in a wave (`plan: <lot> démarré (orchestrate <wave>)`, `… revue de code enregistrée`, `… revue de code notée`, `… hypothèses de la fiche <sujet> notées`, `… mineurs de la revue de <revue> notés`) take the template of the `plan.commit` key of `cadence.yaml`
|
|
@@ -872,9 +906,13 @@ News instruction (`cadence news new <lot>`, factual user-side text, a screenshot
|
|
|
872
906
|
```yaml
|
|
873
907
|
orchestrate:
|
|
874
908
|
test: npm test # run by the orchestrator after a work step (optional)
|
|
909
|
+
test: # or a LIST of parts (L512, see « A gate declared in parts »): each `run` is played in its `dir`, all parts even when one is red, each red test attributed to the part that printed it and replayed alone by that part's `replay` (`{files}` required; a part without it keeps its reds counted); `testReplay` is then refused
|
|
910
|
+
- { dir: backend, run: npm test }
|
|
911
|
+
- { dir: frontend, run: npx vitest run, replay: "npx vitest run {files}" }
|
|
875
912
|
testReplay: npx vitest run --minWorkers=1 --maxWorkers=$CADENCE_TEST_WORKERS {files} # replay alone of a red test before it counts (L354, see « A red test is replayed alone »; `cadence verify --gate` applies it too, L387, `docs/deliver.md`); `{files}` required; absent: no replay, nothing held for unstable (optional)
|
|
876
913
|
build: npm run build # run BEFORE the tests (L333); both results go to the reviewer, who does not redo them (optional)
|
|
877
914
|
worktreeDeps: [node_modules, .venv] # untracked dependencies that `cadence verify --gate` copies into its throwaway worktree (L348, L389; the gate runs in the main tree instead when it is clean at the sha, `verify.inPlace`, `docs/deliver.md`); absent: the `node_modules` of every level and the `.venv` are copied (never data or `.env`, L392), the other ignored folders are neither copied nor linked (L455) (optional)
|
|
915
|
+
gate: { host: build-box, install: npm ci } # play the gate (build, tests, baseline, replay alone) on another machine through ssh, with a dependency cache and a local fallback, see docs/remote-gate.md (L403); `host` alone is not `readsPlan` (both optional)
|
|
878
916
|
gate: { readsPlan: true } # a test of the gate reads the plan (`.cadence/raf.yaml`): `cadence verify --gate` no longer takes over a wave's verdict under commits that change only the plan (L444), it replays; `true` or the list of the paths of the tests that read it; absent or `false`: those commits are crossed (L450, `docs/deliver.md`) (optional)
|
|
879
917
|
precheck: true # default: before the first implementation of a lot with no commit whose title cites a finding or an earlier lot, and whose title or `goal:` cites a finding (`constat`, `revue`, `review`, `audit`) or a plan id (an earlier `L<number>`, also followed by a hyphen as in `L12-L15` or `L12-bis`; any other id such as `Q-CAN5`; `project:L<number>`), a read-only Sonnet session checks whether the deliverable is already in the repository (see below); false skips it, local runs it on claude-local (Ollama) with Sonnet as fallback, for every lot with no commit whatever its title (L296); a lot created after the last code commit of the repository is not pre-checked, local or Sonnet, unless it cites a lot that already has commits (L322)
|
|
880
918
|
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)
|
package/docs/overview.md
CHANGED
|
@@ -70,6 +70,7 @@ The two plugins are independent. `cadence@cadence` carries the rituals, which ca
|
|
|
70
70
|
| [docs/spec.md](spec.md) | the screen and back-end sheets, `cadence spec check\|qa\|reseal\|green` |
|
|
71
71
|
| [docs/lead.md](lead.md) | the lead workflow and `cadence lead tour` |
|
|
72
72
|
| [docs/ref.md](ref.md) | reference documents: `cadence ref add` (rendered pages, positioned text, images, figure index), `cadence ref measure` (colours, WCAG contrast, boxes, OCR, font size), `cadence ref check` (the inventory schema, `templates/inventaire.json`), `cadence ref eval` (recall, precision, false « read », tokens per page against a known inventory), `cadence ref corpus` (the synthetic corpus with a known truth, rendered by Playwright) `cadence ref coverage` (which elements of the inventory no done lot covers, nor a written exclusion), `cadence ref diff` (reconciles two readings of a document by program) and `cadence ref analyse` (one reading by the `reference-analyst` agent; `--double` for two independent readings, `ref diff`, a third look by `reference-verifier` and the reconciled inventory, an option whose measurement showed no gain; 20 pages at most per reading) |
|
|
73
|
+
| [docs/remote-gate.md](remote-gate.md) | `orchestrate.gate.host` and `gate.install`: the gate (`verify --gate`, a wave's lot gate, baseline and replay alone) played by ssh on another machine, dependency cache by lockfile fingerprint, local fallback |
|
|
73
74
|
| [docs/stabilite.md](stabilite.md) | what is guaranteed from 1.0.0 and what is not |
|
|
74
75
|
| [docs/qualite.md](qualite.md) | code-quality guard rails: ESLint (typescript-eslint, sonarjs), knip and jscpd compared to a versioned baseline by a ratchet (`npm run qualite`, `--init`, `--ecrire`, `--migrer`, `--contre`), in the wave's gate and the CI; the recipe for TypeScript, Python, Java and shell |
|
|
75
76
|
|
package/docs/raf.md
CHANGED
|
@@ -62,7 +62,7 @@ raf gantt # .cadence/gantt.html (next to the plan)
|
|
|
62
62
|
| `raf add`, `raf note`, `raf done` during an integration (L425) | while a wave integrates a lot of the repository (the integration lock is held by a live process), these three commands write the plan as usual and print on stderr `avertissement : intégration en cours (vague …, pid …) — …`: the plan is a tracked file of the main tree, and an integration that sees it modified refuses its lot (`arbre du dépôt modifié`, 2026-10-29: a `raf add` of the lead during the integration of L416). Commit the plan right away. Exit code 0; nothing is printed in a lot's worktree (its plan is not the tree the integration checks) nor when the lock belongs to a dead process |
|
|
63
63
|
| `raf add`, `raf note`, `raf done` while a wave works in the main tree (L475) | when the repository's orchestration lock is held by a live wave and one of its lots is in `implementing`, `reviewing` or `fixing` **without a worktree** (a wave without worktrees, or a lot whose worktree folder does not exist), these commands write the plan as usual and print on stderr `avertissement : la vague … travaille dans l'arbre principal de ce dépôt (projet:lot) — …`: a plan commit of the lead then hands the lot back at review (2026-10-30: cadence L458 handed back by a plan commit during its review). Write the plan without committing it, or commit once the step ends. Exit code 0; nothing is printed for lots that are queued, in question, ready, handed back, failed or suspended, for lots playing in their worktree, from a worktree of the repository, from a session of the wave itself (`CADENCE_ORCHESTRATED` set), when the lock belongs to a dead process, or when the wave's state is unreadable |
|
|
64
64
|
| `raf public <id> "title"` · `raf public <id> --clear` | set, replace or remove the public title of a lot |
|
|
65
|
-
| `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`, with the code-review gate active (`raf review enable`) a lot whose commits its review does not cover, **and a lot whose recorded review verdict says `non conforme` / `not compliant` even when it covers every commit** (L476: `non conforme : 1 bloquant` at the last commit no longer closes; `conforme après 1 correction` is compliant; redo the review with `raf review <id> "verdict"` after the fix, or `--force`), and a lot whose `acceptation-<lot>.json` is still `vert: false`, L366: closed like that it would lock every later wave of the repository, see `cadence spec green`; with `--force` it only warns on stderr. Like every `raf` command it only writes the plan file and **never commits** it: when the plan or a file `plan.onWrite` produces is left with changes, `done`
|
|
65
|
+
| `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`, with the code-review gate active (`raf review enable`) a lot whose commits its review does not cover, **and a lot whose recorded review verdict says `non conforme` / `not compliant` even when it covers every commit** (L476: `non conforme : 1 bloquant` at the last commit no longer closes; `conforme après 1 correction` is compliant; redo the review with `raf review <id> "verdict"` after the fix, or `--force`), and a lot whose `acceptation-<lot>.json` is still `vert: false`, L366: closed like that it would lock every later wave of the repository, see `cadence spec green`; with `--force` it only warns on stderr. Like every `raf` command it only writes the plan file and **never commits** it: when the plan or a file `plan.onWrite` produces is left with changes, `done`, `ux <lot>` and `review <lot>` print the `git add` / `git commit` command that takes them, the subject from the `plan.commit` template, L371) |
|
|
66
66
|
| `cadence spec green <lot>` | L366: run the dedicated acceptance command (`orchestrate.accept`) on a lot driven or closed by hand; green, set `vert: true` in its `acceptation-<lot>.json` and commit that file alone (subject `chore(<lot>): acceptation verte, vert: true posé par cadence spec green` or the `plan.commit` template; the resealed tests are listed in the commit body, never in the subject), resealing the fingerprints of the listed tests refined since the red commit (journal `resceaux`) so the lock goes quiet; a listed test changed but not committed is refused. Red: exit 1, nothing changed. Refused for a `todo` lot, during a wave, and while an unfinished wave keeps a lot of the repository open; see [orchestrate](orchestrate.md) |
|
|
67
67
|
| `raf todo <id> [--reason text]` | put a started lot back to `todo` (`doing` → `todo`, `started:` removed, dated note « remis en attente : reason »): refused when work commits cite the lot (plan-only and version commits do not count, as `raf commits`), or on a sub-task; sub-tasks and other keys are left as they are. **Reopening** (L396): on a `done` or `dropped` lot, `raf todo` is THE command — never the YAML — that reopens it (`→ todo`, `finished:` removed, `started:` kept so its commits stay its own, dated note « rouvert (était done) : reason »); `raf start`/`raf done`/`raf drop` on a closed lot say so (`L3 est done ; le rouvrir si c'est voulu : raf todo L3`). An archived lot reopened this way comes back into the plan file at that write |
|
|
68
68
|
| `raf migrate [--dry-run]` | move `docs/plan/raf.yaml` to `.cadence/raf.yaml` and rewrite the readers of the old path in the repository, see [Moving the plan: `raf migrate`](#moving-the-plan-raf-migrate) |
|
|
@@ -169,10 +169,10 @@ plan:
|
|
|
169
169
|
commit: 'chore({lot}): {action} (orchestrate {vague})'
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
- `raf done <lot
|
|
172
|
+
- `raf done <lot>`, `raf ux <lot> "verdict"` and `raf review <lot> "verdict"` (L509) do not commit the plan (raf never does, see below) but print the command that would — the plan and the files `plan.onWrite` regenerated, so a published `plan.json` is never left behind — with this template: `{action}` is `terminé` (`verdict UX enregistré` for `ux`, `revue de code enregistrée` for `review`) and, outside a wave, the parenthesised or bracketed group holding `{vague}` is dropped (`chore(L12): terminé`). Without a template: `plan: L12 terminé`.
|
|
173
173
|
- `{lot}` is required (a commit must cite its lot); `{action}` is what was done (`démarré`, `revue de code enregistrée`, `revue de code notée`, `hypothèses de la fiche <sujet> notées`, `mineurs de la revue de <revue> notés`, `règles R1, R2 sourcées par la réponse du lead`, `test d'acceptation rescellé (<test>)`);
|
|
174
174
|
`{vague}` is the wave id. A word between braces that is none of these, an empty template or one on several lines is refused when the config is read.
|
|
175
|
-
- Outside a wave (`spec reseal`) there is no wave: the parenthesised group that holds `{vague}` is dropped, so the template above gives `chore(L17): test d'acceptation rescellé (…)`.
|
|
175
|
+
- Outside a wave (`spec reseal`) there is no wave: the parenthesised or square-bracketed group that holds `{vague}` is dropped, so the template above gives `chore(L17): test d'acceptation rescellé (…)`.
|
|
176
176
|
- Absent, the subject is the one above, unchanged. The key needs `plan:` to be an object (`plan: .cadence/raf.yaml` as a plain path has nowhere to put it: write `plan: { path: …, commit: … }`).
|
|
177
177
|
- `cadence spec green` (L366) commits its `acceptation-<lot>.json` with the template too when there is one (`{action}` = `acceptation verte, vert: true posé par cadence spec green`); without one its subject stays `chore(<lot>): …`. The tests it reseals are listed in the commit body, never in the subject, which therefore does not grow with them.
|
|
178
178
|
- The commit of the fiche sourced by the lead's answer takes the template too (its default, `docs(<lot>): règle R3 sourcée par la réponse du lead (orchestrate <wave>)`, is already Conventional but can exceed `header-max-length` with several rules and a wave id). The `test(<lot>): …` and `chore(<lot>): …` commits of the cycle (acceptation sealed, `vert: true` set) and the work commits are already Conventional and are not templated.
|
package/docs/releasing.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
A version exists in three places and is published in two; a release does all of it, in this order.
|
|
6
6
|
`cadence release patch|minor|major [--dry-run]` (L341) prepares step 1 and prints steps 2 to 6 in order; it publishes nothing.
|
|
7
|
+
Between the version commit and the gate it also prints a **provisional local tag** `v<version>` (L508), see step 2.
|
|
7
8
|
|
|
8
9
|
1. Bump `version` in `package.json` (then `npm install` to refresh `package-lock.json`),
|
|
9
10
|
`.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`, and write the version's section of
|
|
@@ -16,10 +17,14 @@ A version exists in three places and is published in two; a release does all of
|
|
|
16
17
|
in that commit, not before the review. A commit that touches any other file
|
|
17
18
|
— or another field of a manifest, or a dependency in `package-lock.json` — is work like any other and has
|
|
18
19
|
to be reviewed.
|
|
19
|
-
2.
|
|
20
|
+
2. Put the provisional local tag on that commit, then replay the project's gate on it (L508). Some tests read the latest tag
|
|
21
|
+
(`test/changelog.test.ts` checks that a lot cited under the latest tagged version has a commit in that tag); run before the tag
|
|
22
|
+
exists, they still see the previous version, which is how 1.34.0 was tagged green and failed its publication. The tag is made first, local only, and the gate,
|
|
23
|
+
the fresh-clone replay and the evaluation below each remove it (`git tag -d`, the step stays red) when they fail; nothing is pushed
|
|
24
|
+
until step 4. The gate: `cadence verify --gate --sha HEAD` (L380). The version commit empties
|
|
20
25
|
`## [Unreleased]` of `CHANGELOG.md`, and a test that read only that section would pass before it and fail after — the red
|
|
21
26
|
publish run of 1.14.0 (L368) and the stopped 1.15.2 (L380) — so the suite is run on the commit that will be tagged, before the
|
|
22
|
-
billed evaluation and before the
|
|
27
|
+
billed evaluation and before the push. Red: fix it in a commit of its own and redo the version commit. A test that looks
|
|
23
28
|
for the entry of a lot in the CHANGELOG reads the section that carries it (`[Unreleased]` or the published version) through
|
|
24
29
|
`changelogSectionOf` (`test/changelog-section.ts`); `test/changelog-garde.test.ts` refuses a test that selects `## [Unreleased]`
|
|
25
30
|
of the repository's CHANGELOG itself. The gate runs in the lead's tree or in a copy of it, both full of files git does not track
|
|
@@ -27,11 +32,14 @@ A version exists in three places and is published in two; a release does all of
|
|
|
27
32
|
`hud-*` tests that read those types, present in every local tree and absent from the CI checkout (L446). So the same commit is
|
|
28
33
|
also replayed **in a fresh `git clone`** — the next step the command prints: `git clone --no-hardlinks` of the repository
|
|
29
34
|
(only what is committed), `npm ci`, then `npm run typecheck` and `npm test` (those the package declares; a package with neither gets no
|
|
30
|
-
such step), the clone removed whatever the result. Red: no evaluation
|
|
35
|
+
such step), the clone removed whatever the result. Red: no evaluation, tag removed. `.github/workflows/tests.yml` does the same on every
|
|
31
36
|
push to `main`, from a fresh checkout.
|
|
37
|
+
With `orchestrate.gate.host` ([remote-gate.md](remote-gate.md)) the gate is played on another machine that receives only the commit
|
|
38
|
+
(`refs/cadence-gate/<sha>`), so that host does not see the provisional tag and a test reading the latest tag passes there without checking
|
|
39
|
+
anything; only the local fresh-clone replay, which does receive the tag, catches it, and it is printed only when the package declares `test` or `typecheck`.
|
|
32
40
|
3. Run the plugin evaluation, `npm run eval:plugin` (billed, see [Evaluating the plugin](#evaluating-the-plugin-before-a-release)),
|
|
33
|
-
on that version commit; a case below the threshold stops the release until you have read it in the report.
|
|
34
|
-
4. `git
|
|
41
|
+
on that version commit; a case below the threshold stops the release and removes the tag (step 2) until you have read it in the report; if you judge the case a false negative, run `git tag v<version>` again on the version commit before step 4.
|
|
42
|
+
4. `git push origin main v<version>` (the tag already exists, step 2; `git tag v<version>` is not run twice) — the tag starts `.github/workflows/publish.yml`,
|
|
35
43
|
which publishes to npm through Trusted Publishing (OIDC, no token stored anywhere): it checks the tag
|
|
36
44
|
matches `package.json`, `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json` and that `CHANGELOG.md` has a `## [x.y.z]` section for it (no section, no publication), then `npm publish --provenance`, where `prepublishOnly` runs the type-check, the quality ratchet (`npm run qualite`, [qualite.md](qualite.md): exit 1 when a measure got worse, 2 when a tool crashed or the baseline is missing) and the
|
|
37
45
|
tests and `prepare` builds `dist/`; a red suite or a measure that got worse stops the publication (fix the code, then `npm run qualite:cliquet` locks the gain); then it creates the GitHub release with that CHANGELOG section as its text. The trusted publisher is declared
|
|
@@ -85,8 +93,9 @@ introduction (the newest version first, as the file is read). Exit code 1 when a
|
|
|
85
93
|
version files are written, the commit is not ready. `--dry-run` reads the forms the same way (nothing is written) and exits 1 on the same findings, so the forms can be checked before the real run.
|
|
86
94
|
|
|
87
95
|
It prints the lead's steps, in order, and runs none: the `git add` of exactly the files of the version commit and the commit,
|
|
88
|
-
|
|
89
|
-
`
|
|
96
|
+
the provisional tag `git tag v<x.y.z>` (L508: made before the gate, removed by `git tag -d` when the gate, the clone replay or the evaluation is red),
|
|
97
|
+
`cadence verify --gate --sha HEAD` (always printed, before the evaluation), the replay in a fresh clone (L446: `git clone --no-hardlinks`, `npm ci`, `npm run typecheck` and `npm test` for the scripts `package.json` declares, none printed when it declares neither),
|
|
98
|
+
`npm run eval:plugin -- --trust-plugin` (only when `package.json` has that script), `git push origin <branch> v<x.y.z>`,
|
|
90
99
|
`gh run watch`, `npm view <package> version` (not for a private package) and, only with a `.claude-plugin/plugin.json`,
|
|
91
100
|
the plugin update: `claude plugin marketplace update <marketplace>` first (without it the installed plugin stays at the old
|
|
92
101
|
version; skipped when the marketplace has no name), then `claude plugin update <plugin>@<marketplace>`, then `/reload-plugins`
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Remote gate: build and tests played on another machine (L403)
|
|
2
|
+
|
|
3
|
+
A project whose build and tests are heavy can play its gate on a second machine through ssh, at the sha
|
|
4
|
+
it pushes there, with a cache of dependencies. It is one optional key of `cadence.yaml`; absent, nothing changes
|
|
5
|
+
(no ssh call, the same output lines as before).
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
# cadence.yaml
|
|
9
|
+
orchestrate:
|
|
10
|
+
build: npm run build
|
|
11
|
+
test: npx vitest run
|
|
12
|
+
gate:
|
|
13
|
+
host: build-box # an ssh destination: `machine`, `user@machine`
|
|
14
|
+
install: npm ci # optional: installs the dependencies on the host to fill a cache
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`host` is a non-empty string without space, not starting with `-` (ssh would read it as an option);
|
|
18
|
+
anything else refuses `cadence.yaml` with `orchestrate.gate.host : destination ssh attendue …`. `install` is a non-empty command.
|
|
19
|
+
`install` without `host` does nothing (the gate stays local). Any other key of `gate` is refused as unknown (`readsPlan`, `host`, `install` are the three). Declaring `host` alone is **not** `readsPlan`:
|
|
20
|
+
the plan-only commits are still crossed (L444) and the `readsPlan` message does not appear.
|
|
21
|
+
Nothing in cadence names a machine: the host comes from this key, and the work folder on it is derived.
|
|
22
|
+
|
|
23
|
+
## What runs where
|
|
24
|
+
|
|
25
|
+
Played on the host, at the sha, in a throwaway worktree: `orchestrate.build` then `orchestrate.test` of `cadence verify --gate`
|
|
26
|
+
(`docs/deliver.md`), and, in a wave (`docs/orchestrate.md`), the program's gate of a lot after a work step, the baseline of the starting sha
|
|
27
|
+
and the replay alone of the red tests (`orchestrate.testReplay`, or the `replay` of each part of a gate declared as a list, L512: each part and its replay run in their `dir` of the host's worktree). The commands are those of `cadence.yaml`, unchanged; the verdict is read as a local one
|
|
28
|
+
(0 green, 1 red with the tests named, baseline, recording of the checks), and the verdict of a sha is noted in the local shared state as before.
|
|
29
|
+
The other commands of a wave (claude sessions, acceptance, start, verdict) stay local.
|
|
30
|
+
|
|
31
|
+
The first line of the gate says where it ran:
|
|
32
|
+
`verify --gate 6b0d9aa : porte jouée sur l'hôte build-box (sha 6b0d9aa, cache utilisé (empreinte 1a2b3c4d))`. In a wave the same line goes to the journal
|
|
33
|
+
(`porte du lot L12 : porte jouée sur l'hôte …`) and the place is kept in the lot's checks (`lieu`).
|
|
34
|
+
|
|
35
|
+
`verify.inPlace: true` wins over the host: the gate runs in the main tree and the output says `l'hôte … est ignoré`.
|
|
36
|
+
`inPlace: false` or absent lets the host come before the main tree and the throwaway copy.
|
|
37
|
+
|
|
38
|
+
A wave plays the gate on the host only when the lot's worktree is **clean and at the commit pushed**: the host would not see uncommitted
|
|
39
|
+
changes. Otherwise the gate is local and the journal says `worktree non validé, jouée en local`.
|
|
40
|
+
|
|
41
|
+
## The work folder on the host
|
|
42
|
+
|
|
43
|
+
Relative to the ssh login directory: `.cadence-gate/<folder name of the local project>/` with `repo.git` (bare repository where the sha is pushed,
|
|
44
|
+
`git push --no-verify`, created at first use), `deps/<fingerprint>` (the dependency caches) and `wt/<unique id>` (the throwaway worktrees).
|
|
45
|
+
The host needs `ssh` access without password (`BatchMode`), `git`, `tar` and the tools of the project. Installing the machine is the lead's job.
|
|
46
|
+
|
|
47
|
+
## The dependency cache
|
|
48
|
+
|
|
49
|
+
The key is the sha256 of the lockfiles of the sha, at any level of the repository: `package-lock.json`, `npm-shrinkwrap.json`, `pnpm-lock.yaml`,
|
|
50
|
+
`yarn.lock`, `poetry.lock`, `uv.lock`, `Pipfile.lock`, `Cargo.lock`, `go.sum`. No lockfile at the sha: no cache, the gate plays without prepared dependencies.
|
|
51
|
+
A known fingerprint is reused without installing; the worktree receives a **copy** of the cache, never a link. An absent cache is filled once:
|
|
52
|
+
|
|
53
|
+
- with `gate.install`: played **on the host**, in a worktree at the sha; the cache folder carries the platform of the host in its key (`<fingerprint>-linux-x64`);
|
|
54
|
+
- without `gate.install`: by copying the local dependencies (`node_modules`, `.venv`, or `orchestrate.worktreeDeps`), but only if the lockfiles of the local tree
|
|
55
|
+
have the fingerprint of the sha **and** the local system and architecture equal the host's (read by the probe). Otherwise the gate falls back to local, with the cause.
|
|
56
|
+
|
|
57
|
+
A cache is written in a unique temporary folder, then published by a rename to `deps/<fingerprint>`; if it already exists (a wave in parallel, a `verify --gate`), the temporary is
|
|
58
|
+
thrown and the existing cache is used. A failed install publishes nothing. cadence **never purges** the caches or the bare repository: freeing the host's disk is up to the lead
|
|
59
|
+
(deleting `.cadence-gate/<project>` only forces a push and a reinstall). Temporary folders and worktrees left more than 6 hours ago are removed at the next call.
|
|
60
|
+
|
|
61
|
+
## When the host does not answer
|
|
62
|
+
|
|
63
|
+
The gate first probes the host (`ssh -o BatchMode=yes`, short connection delay: never a password prompt). It falls back to the **local gate**, never to a red, and says so:
|
|
64
|
+
|
|
65
|
+
- `verify --gate : porte déportée indisponible sur <host> (<cause>) — repli sur la porte locale`: the probe, the work folder or the push failed;
|
|
66
|
+
- `verify --gate : cache de dépendances impossible sur <host> (<cause>) — repli sur la porte locale`: the cache could not be filled or read;
|
|
67
|
+
- in a wave: `porte du lot <lot> : porte déportée indisponible sur <host> (<cause>) — jouée en local` and `… cache de dépendances impossible sur <host> (<cause>) — jouée en local`.
|
|
68
|
+
|
|
69
|
+
**A connection lost during a command (ssh exit code 255).** The command did not give a verdict, so it is never counted red:
|
|
70
|
+
`verify --gate : connexion perdue avec <host> pendant la porte — rejouée en local` (the whole gate is replayed locally), and in a wave
|
|
71
|
+
`porte du lot <lot> : connexion perdue avec <host> pendant la porte — rejouée en local` (the command is replayed locally — the lot's gate, its baseline `(ligne de base <sha>)`
|
|
72
|
+
or the replay alone — and the next commands of that gate stay local), written to the output **and** to the journal of the wave, with no correction pass and no finding. Any other remote exit code
|
|
73
|
+
is the verdict of the command: 1 is a red, as locally.
|
|
74
|
+
|
|
75
|
+
## Timeout, Ctrl-C, signals
|
|
76
|
+
|
|
77
|
+
Remote commands run with a terminal (`ssh -tt`) and their process group id is kept on the host. At the timeout (30 minutes per command), on Ctrl-C or on a signal, cadence kills the local
|
|
78
|
+
ssh and then sends the host a separate command that kills the group and removes the worktree. If that command fails the output says
|
|
79
|
+
`nettoyage de l'hôte <host> en échec (<cause>) — le worktree orphelin y sera retiré au prochain appel`.
|
|
80
|
+
|
|
81
|
+
## Quoting and the sha
|
|
82
|
+
|
|
83
|
+
The sha, the host and the paths put in an ssh command are single-quoted with escaped quotes. A sha that is not hexadecimal (4 to 64 characters) is refused before any ssh command:
|
|
84
|
+
`verify --gate : sha invalide pour la porte déportée (hexadécimal attendu)`, exit 2.
|
|
85
|
+
|
|
86
|
+
## `CADENCE_TEST_WORKERS`
|
|
87
|
+
|
|
88
|
+
The commands receive `CADENCE_TEST_WORKERS` computed with the **host's** cores (`nproc`) divided by the session cap (`CADENCE_MAX_SESSIONS`, 2 by default), unless the caller's environment already
|
|
89
|
+
holds a positive integer (same rule as the local gate, L350).
|
package/docs/skills.md
CHANGED
|
@@ -13,7 +13,9 @@ gives `/cadence:session-start`, `/cadence:session-close`, `/cadence:deliver`,
|
|
|
13
13
|
`/cadence:lead`, `/cadence:orchestrate`, the `ux-reviewer`, `code-reviewer` and `qa-reviewer` agents and the `livreur` delivery agent. Or copy them into the
|
|
14
14
|
repository with `cadence skills install` (to `.claude/skills/cadence-*` and
|
|
15
15
|
`.claude/agents/cadence-*.md`; `--dir` for another `.claude` folder,
|
|
16
|
-
`--force` to overwrite local
|
|
16
|
+
`--force` to overwrite a copy that differs from the package's text, a local edit or an
|
|
17
|
+
earlier version never edited: the command compares the copy to the text it installs, not to the
|
|
18
|
+
version that installed it).
|
|
17
19
|
|
|
18
20
|
- **session-start**: reports the facts briefly, proposes three lots from the
|
|
19
21
|
plan, then waits for your priority — nothing starts before your answer.
|
|
@@ -50,6 +52,7 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
|
|
|
50
52
|
edits code. Read-only for real: its `tools:` list is `Read, Grep, Glob, Bash` and the
|
|
51
53
|
Playwright tools (`mcp__playwright`, and `mcp__plugin_playwright_playwright` when the
|
|
52
54
|
browser comes from the Playwright plugin) — no `Edit`, no `Write`. In a wave it gives its captures the absolute path of the capture directory its brief names (a bare name would land in the repository); outside a wave, a path under `.playwright-mcp/` (e.g. `.playwright-mcp/page-home.png`; the Playwright MCP resolves a bare file name against the repository, where it would land at the root); `qa-reviewer` follows it too.
|
|
55
|
+
**Delivery grid (L513):** on the `ux` step and when reviewing an existing screen (not in mockup or conformity mode) its checklist carries a five-point grid — visible focus, `prefers-reduced-motion`, `cursor: pointer` on clickable elements, no emoji used as an icon, no horizontal scroll at 375, 768, 1024 and 1440 px (a width it could not render goes to `nonVerifie`; a failing point follows the blocking / major / minor scale) — and a static check of AI tics, seven of the 16 rules of `detect.mjs` (Apache-2.0, attribution in `NOTICE`; the other nine are not kept, reasons in the agent) applied by reading the diff without running it. Each tic is a finding `T1`, `T2`… (file, line, named rule, severity); none gives `tics d’IA : 0 constat`, no front file `non applicable`; a tic that is a choice of the product's charter is not a finding. The grid is not repeated in the short reply. Without a `Report file:` line (the orchestrated `ux` step, whose structured report has no checklist and whose schema is unchanged), a point that could not be checked goes to `nonVerifie` and the `verdict` line ends with the tally of the grid and the tics line (`grille 5/5 — tics d’IA : 0 constat`).
|
|
53
56
|
**Short reply (L452):** with a `Report file: <absolute path>` line in the brief it answers in 15 lines at most (`Verdict:`, `Findings:` with
|
|
54
57
|
the rule or measure of each, `Not verified:`, `Sub-tasks:`, `Report:`, `Captures:`) and writes the full report to that file (outside the repository, verdict last).
|
|
55
58
|
- **code-reviewer** (agent): any stack; given a repository and a lot id, it reads
|
|
@@ -95,7 +98,7 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
|
|
|
95
98
|
the project's expectations file in a real browser at 1440 and 390 px and
|
|
96
99
|
measures: expected content present and non-empty, no error or missing-data
|
|
97
100
|
message, every API call answered 2xx with a non-empty body, no console error,
|
|
98
|
-
no broken content image. It reads the page as text first and takes a capture only for a gap. Findings are defects (a line of the expectations
|
|
101
|
+
no broken content image. It reads the page as text first and takes a capture only for a gap. It works in its own browser context (its own profile, named after the app, a suffixed one when that profile is already open elsewhere), launched by a Node script with `launchPersistentContext` rather than through the shared MCP browser tools — the script loads `playwright` from a package already on the machine (`CADENCE_PLAYWRIGHT`, the app's `node_modules`, the package of the wave's Playwright MCP server, else the newest npx cache), falls back on the installed Chrome (`channel: 'chrome'`) when the bundled Chromium is missing, never installs anything, and without a loadable Playwright the pass is reported under « Not verified » rather than done with the shared tools: several QA passes can run together without sharing a browser or reading each other's tabs (L511). Findings are defects (a line of the expectations
|
|
99
102
|
broken, or a universal check failing with a visible effect, with or without an
|
|
100
103
|
expectations file), suspects (it looks like missing or wrong data and no
|
|
101
104
|
expectation settles it) or noise (a console error or a failed request with no
|
package/docs/whats-new.md
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## In two lines
|
|
6
6
|
|
|
7
|
+
- **1.35.0**: a test already red before a lot, or that only needs more time, no longer costs correction passes, including on a gate declared in parts; QA passes run in browsers of their own; design agents check contrast and AI tics.
|
|
8
|
+
- **1.34.1**: republishes 1.34.0, whose npm publication failed after the tag; same features (edge-case probe, steadier `mise-en-service` tests).
|
|
9
|
+
- **1.34.0**: an optional edge-case probe (`orchestrate.probe: true`) tries to break a lot's diff with tests at the edges before the code review; flaky tests of `mise-en-service` made deterministic.
|
|
7
10
|
- **1.33.0**: the lots panel shows each project's progress bar in its « ordre des projets » section, aligned in the desktop app; a simplification audit of the code, and dead exports removed.
|
|
8
11
|
- **1.32.0**: code-quality guard rails in a ratchet (ESLint, sonarjs, knip, jscpd vs a versioned baseline), a recipe for TS, Python, Java, shell; `raf done` refuses a lot whose recorded review is `non conforme`.
|
|
9
12
|
- **1.31.0**: `deliver --sha` refuses, outside a wave too, a sha that carries commits of a lot neither done nor ready (a `non conforme` verdict is not ready); `raf add/note/done` warn when a wave session writes in the main tree.
|
|
@@ -60,6 +63,12 @@ Each is a paragraph in [docs/whats-new.md](whats-new.md) and a full section in [
|
|
|
60
63
|
|
|
61
64
|
The long form of « In two lines » above (the README no longer carries a « What's new » section since L326: it links here). The full history, with the date of each version, is in [CHANGELOG.md](../CHANGELOG.md).
|
|
62
65
|
|
|
66
|
+
**1.35.0**: the wave's gate stops paying for reds that need no fix. With `orchestrate.testReplay`, a red that stays red alone in the lot is replayed alone at the start sha, and one already failing there is « déjà rouge »: no correction pass, no escalation; a test that expired in the whole suite and passes alone at 80 % or more of its delay is a « délai trop juste », a warning naming the test with its duration and its delay (L510, `docs/orchestrate.md`). On a gate played in several folders, `orchestrate.test` takes a list of parts `[{ dir, run, replay? }]` and each part plays, replays and attributes its own reds, so a composed gate gets the same two verdicts (L512, `docs/orchestrate.md`, « A gate declared in parts »; evatosorus `node --test` is not covered yet). `cadence corrections` now counts the passes launched on tests that ended without a code change (L510, `docs/corrections.md`). The gate can also run on another machine through `orchestrate.gate.host`, by ssh at the pushed sha, with a local fallback and a lost connection replayed locally, never counted red (L403, `docs/remote-gate.md`). The `qa-reviewer` opens a browser context and a profile of its own, so two QA passes started together no longer share a tab (L511, `docs/skills.md`). The `spec-writer` joins a `contraste.md` (WCAG 2.2 ratios, 4.5:1 at least) to each sheet, and the `ux-reviewer` gets a delivery grid and a static check of AI tics (L513, `docs/orchestrate.md`). Small fixes: `cadence release` prints its local tag before the gate and removes it when red (L508, `docs/releasing.md`), `raf ux` and `raf review` print the commit command for the regenerated plan (L509), and `cadence deliver` outside a wave commits the plan it wrote for the « article à rafraîchir » lot (L420, `docs/news.md`).
|
|
67
|
+
|
|
68
|
+
**1.34.1**: 1.34.0 was tagged but its publication to npm failed (a CHANGELOG entry cited a lot with no commit in the tag); 1.34.1 publishes the same content, with that entry reworded. Install 1.34.1 to get the edge-case probe and the steadier tests of 1.34.0.
|
|
69
|
+
|
|
70
|
+
**1.34.0**: `cadence orchestrate` can play an edge-case probe between the implementation and the code review (L497, `orchestrate.probe: true`, `docs/orchestrate.md`): a fresh Sonnet session reads the lot's diff, and the sheet when there is one, and writes tests at the edges — empty, absent, twice, wrong type, refused, the real shape of the data, the other callers; a red test sends the lot back to the implementation as a finding before the Opus review, which costs more. The probe's findings never block the lot (a failed or timed-out probe is a warning and the review goes on), while the checks common to every writing session — a dirty repository, the acceptance-test lock — apply to it too. Absent, nothing changes. It answers the « bug de code » bottleneck: edge cases were 64 % of the code-bug correction passes after two written instructions in the implementation brief. The test suite also stops blocking a Vitest worker for more than 60 s: `mise-en-service` compiles once per test file instead of five synchronous `tsc` runs (L480).
|
|
71
|
+
|
|
63
72
|
**1.33.0**: the lots panel (`/hud lots`) shows, on each row of its « ordre des projets » section, the progress bar of the band — closed lots over total, coloured by the share done, the lots in progress and `+N cette semaine` (L481, `docs/cadence-hud.md`); the bars all start at the same column, the project name sitting in a box as wide as the longest name, which a proportional font aligns where spaces did not (L494, L495). Dragging and Alt+↑ / Alt+↓ are unchanged. A read-only simplification audit of the code (L461, `docs/mesures/audit-simplification-l461.md`) ranks dead code, copies and split plans by gain and risk; its first cleanups remove exports nothing imports and a dead type (L483, L484), with no change of behaviour.
|
|
64
73
|
|
|
65
74
|
**1.32.0**: code-quality guard rails for cadence itself and a reference recipe for any project (L462, `docs/qualite.md`): ESLint with typescript-eslint and sonarjs (file and function length, depth, parameters, cyclomatic and cognitive complexity), knip (dead code, unused exports and dependencies) and jscpd (duplicates), compared by `npm run qualite` to a versioned baseline — existing code passes, nothing may get worse, a file a lot touches can only improve; `--migrer` is the admitted path to move or split a file of the baseline, and a changed tool configuration or a new suppression directive is refused. The recipe gives the same ratchet for Python, Java and shell. `raf done` refuses, without `--force`, a lot whose recorded code-review verdict is non-compliant, even when the review covers all its commits (L476, `docs/raf.md`), the predicate `cadence deliver --sha` uses since 1.31.0.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylad/cadence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.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",
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
"templates",
|
|
18
18
|
".claude-plugin",
|
|
19
19
|
"README.md",
|
|
20
|
+
"NOTICE",
|
|
20
21
|
"docs/raf.md",
|
|
21
22
|
"docs/orchestrate.md",
|
|
22
23
|
"docs/deliver.md",
|
|
24
|
+
"docs/remote-gate.md",
|
|
23
25
|
"docs/spec.md",
|
|
24
26
|
"docs/lead.md",
|
|
25
27
|
"docs/ref.md",
|
package/skills/lead/SKILL.md
CHANGED
|
@@ -263,7 +263,7 @@ user must find there) and returns measured findings; it reads only, and never lo
|
|
|
263
263
|
blocking findings back to the human — a page whose main content is missing, or that shows an error,
|
|
264
264
|
is a defect even when the delivery checks are green — with its proposed follow-up lines. A project
|
|
265
265
|
without an expectations file gets a draft back: show it to the human, who corrects it and decides
|
|
266
|
-
whether it is committed. It is not a gate: `raf done` does not wait for it, and a finding becomes a
|
|
266
|
+
whether it is committed. Several `qa-reviewer` passes may run together: each opens its own browser context (its own profile), so two apps are walked without sharing a browser. It is not a gate: `raf done` does not wait for it, and a finding becomes a
|
|
267
267
|
new lot, not a reopened one.
|
|
268
268
|
|
|
269
269
|
Give the `qa-reviewer` the same line `Report file: <absolute path>` (outside the repository): it answers in 5 lines
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Edge-case probe for lot `{{lot}}` — "{{titre}}" — of the repository `{{chemin}}`. You are a fresh session: you did not write this code, and the review that follows reads it as it stands. Your job is to try to break it, with tests at its edges, before that review.
|
|
2
|
+
Goal of the lot: {{objectif}}
|
|
3
|
+
{{repos}}
|
|
4
|
+
{{fiche}}
|
|
5
|
+
Commits of the lot:
|
|
6
|
+
{{commits}}
|
|
7
|
+
The diff of the lot is not pasted here: read it with `{{diff}}` (add `--stat` first for a large lot, then read it file by file), and open the code around each change.
|
|
8
|
+
For every function, parser, pattern, threshold or command the diff adds or changes, try the inputs at its edge:
|
|
9
|
+
- empty (empty string, list, object, file, zero);
|
|
10
|
+
- absent (missing key, null, undefined, a file that does not exist);
|
|
11
|
+
- twice (the same item or the same call twice, a duplicate, a repeat);
|
|
12
|
+
- wrong type (a string where a number is expected, an object for a list);
|
|
13
|
+
- refused (a rejected input, a failing dependency, the error path: the exact message and code the lot or its fiche states);
|
|
14
|
+
- the real shape of the data (a real file or a real call, never only a fixture you built to fit the code);
|
|
15
|
+
- other callers of what the diff changed (find them with `grep`; each must still work).
|
|
16
|
+
Rules: write the tests in NEW test files only (never edit an existing test), named after the rule they attack when the lot has a fiche. Run them, and commit them all, the ones that pass and the ones that fail, with explicit paths (never `git add -A` or `commit -a`) and a message that cites the lot (`probe({{lot}}): …`). Do not modify the application code: you find defects, you do not fix them. Do not modify any existing test, and never an `acceptation-<lot>.json` or the tests it seals. Do not push, deliver, run `raf done`, `raf ux` or `raf review`. Read the project's CLAUDE.md first.
|
|
17
|
+
Report in `echecs` one entry per test of yours that fails, written « <test name> — <the edge tried> — <what was expected> »; a test that passes is not listed. Leave `echecs` empty when none fails. What you could not try goes under `nonVerifie`. A question is only for a real blocker: the program writes it down and does not wait for an answer.
|
|
18
|
+
{{gate}}
|
|
19
|
+
{{workers}}
|
|
20
|
+
{{tmp}}
|
|
@@ -11,6 +11,8 @@ What you produce, in `<dir>/<écran>/`:
|
|
|
11
11
|
|
|
12
12
|
Write the texts before the review, with the structure the UX reviewer will check: an error message says what, why, how to fix; an empty or loading state says what it is, why it is empty, how to start; an action label is a verb that names the result, never "OK" or "Validate" alone; the confirmation of a destructive action names the object and the consequence; one word per notion, in the sheet and in the mockup. A word or a message already in the product is kept as it is.
|
|
13
13
|
|
|
14
|
+
Colours the screen adds: for each one, a semantic token named by its role and its text token `on-<role>` (a colour without its `on-*` is a defect of the spec), with values from a ramp drawn from the brand colour of the charter, never an external palette; a role the charter already covers keeps its token (no duplicate, the existing pair is only measured). Write `contraste.md` in the same folder: one line per token / on-token pair, the two values, the ratio (WCAG 2.2, relative luminance) and `ok` or `non`, every pair at least 4.5:1 (fix by changing the step of the ramp, not the text size). A product with no CSS variable and no brand colour: invent no ramp, measure the existing values and write the absence of a coloured charter in the sheet. Never propose a generic catalogue style on a product that has a charter.
|
|
15
|
+
|
|
14
16
|
Sheet rules:
|
|
15
17
|
- Every rule is `sourcee` (it says where it comes from: a line of the lot, a note, `expectations.md`, a rule already coded, an API contract) or `hypothese` (you chose it for lack of a source; `si_faux` says what would change if it were wrong). Never present a guess as sourced.
|
|
16
18
|
- 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).
|
|
@@ -18,7 +20,7 @@ Sheet rules:
|
|
|
18
20
|
- 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.
|
|
19
21
|
- 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.
|
|
20
22
|
|
|
21
|
-
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
|
|
23
|
+
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, the captures and `contraste.md` 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`.
|
|
22
24
|
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.
|
|
23
25
|
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".
|
|
24
26
|
{{commits}}
|