@sylad/cadence 0.10.1 → 0.12.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.
Files changed (41) hide show
  1. package/.claude-plugin/marketplace.json +16 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +219 -21
  4. package/agents/code-reviewer.md +8 -3
  5. package/agents/precheck-reader.md +14 -0
  6. package/agents/qa-reviewer.md +19 -2
  7. package/agents/ux-reviewer.md +1 -1
  8. package/bin/cadence.js +3 -2
  9. package/dist/audit.js +34 -7
  10. package/dist/cli.js +113 -13
  11. package/dist/config.js +104 -15
  12. package/dist/deliver.js +10 -3
  13. package/dist/news.js +17 -1
  14. package/dist/orchestrate/app.js +189 -0
  15. package/dist/orchestrate/briefs.js +16 -0
  16. package/dist/orchestrate/command.js +224 -45
  17. package/dist/orchestrate/cycle.js +208 -26
  18. package/dist/orchestrate/guard.js +148 -3
  19. package/dist/orchestrate/launch.js +43 -5
  20. package/dist/orchestrate/lock.js +19 -5
  21. package/dist/orchestrate/node-env.js +77 -0
  22. package/dist/orchestrate/pool.js +4 -3
  23. package/dist/orchestrate/registry.js +120 -0
  24. package/dist/orchestrate/schemas.js +7 -1
  25. package/dist/orchestrate/snapshot.js +99 -0
  26. package/dist/orchestrate/state.js +22 -0
  27. package/dist/orchestrate/table.js +11 -1
  28. package/dist/plan.js +29 -0
  29. package/dist/proc.js +19 -0
  30. package/dist/session.js +6 -3
  31. package/dist/state.js +34 -14
  32. package/package.json +1 -1
  33. package/skills/lead/SKILL.md +17 -8
  34. package/templates/orchestrate/fix-minors.md +3 -0
  35. package/templates/orchestrate/fix.md +4 -0
  36. package/templates/orchestrate/implement.md +5 -1
  37. package/templates/orchestrate/precheck.md +10 -0
  38. package/templates/orchestrate/review-recheck.md +2 -0
  39. package/templates/orchestrate/review-small.md +3 -0
  40. package/templates/orchestrate/review.md +2 -0
  41. package/templates/orchestrate/ux.md +2 -0
@@ -1,14 +1,27 @@
1
1
  {
2
2
  "name": "cadence",
3
3
  "description": "cadence — a repo-native working method for Claude Code sessions",
4
- "owner": { "name": "Sylvain Ladoire" },
4
+ "owner": {
5
+ "name": "Sylvain Ladoire"
6
+ },
5
7
  "plugins": [
6
8
  {
7
9
  "name": "cadence",
8
10
  "description": "Session start and close rituals driven by a versioned plan (raf), and deliveries proven by their effect. Needs the cadence CLI (npm i -g @sylad/cadence).",
9
- "version": "0.10.1",
11
+ "version": "0.12.0",
10
12
  "source": "./",
11
- "author": { "name": "Sylvain Ladoire" }
13
+ "author": {
14
+ "name": "Sylvain Ladoire"
15
+ }
16
+ },
17
+ {
18
+ "name": "cadence-hud",
19
+ "description": "A status band above the Claude Code prompt: context, 5 h / 7 d quota windows, cost per model, agents of the session and the running cadence orchestrate waves. No CLI needed.",
20
+ "version": "0.2.0",
21
+ "source": "./plugins/cadence-hud",
22
+ "author": {
23
+ "name": "Sylvain Ladoire"
24
+ }
12
25
  }
13
26
  ]
14
27
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cadence",
3
3
  "description": "A repo-native working method: session start and close rituals driven by a versioned plan (raf), deliveries proven by their effect, and three reviewer agents (UX, code, QA).",
4
- "version": "0.10.1",
4
+ "version": "0.12.0",
5
5
  "author": { "name": "Sylvain Ladoire" },
6
6
  "homepage": "https://github.com/Sylad/cadence",
7
7
  "repository": "https://github.com/Sylad/cadence",
package/README.md CHANGED
@@ -19,6 +19,23 @@ several projects through subagents — plus three reviewer agents: `ux-reviewer`
19
19
  gate, and `qa-reviewer`, which walks the delivered app in a real browser and
20
20
  reports a page left empty or in error.
21
21
 
22
+ And a second plugin, [`cadence-hud`](#cadence-hud): a status band above the Claude
23
+ Code prompt that shows the session's context, quota, cost and the orchestrate waves running.
24
+
25
+ ## What's new
26
+
27
+ **0.12.0**: the `cadence-hud` plugin (a status band above the Claude Code prompt),
28
+ `raf ignore` to acknowledge a commit without a lot, waves that size the review to the lot,
29
+ hand back a lot whose deliverable is already there and start the app themselves for the UX
30
+ review (`orchestrate.ux`), and briefs that require the README to follow the change. **0.11.0**: waves on different repositories run side by side under a shared cap of
31
+ simultaneous sessions (`--max-sessions`, 2 by default), and every release now has its
32
+ changelog section and GitHub release. **0.10.1**: the QA expectations file (`docs/qa/expectations.md`) is kept with the plan —
33
+ a commit that only touches it no longer has to cite a lot. **0.10.0**: `cadence orchestrate`
34
+ sends an already-committed lot straight to review, and sessions only ask questions that
35
+ name what they would change.
36
+
37
+ Every version, with what it brings and since when: [CHANGELOG.md](CHANGELOG.md).
38
+
22
39
  ## raf
23
40
 
24
41
  - The plan is a YAML file in the repo (`docs/plan/raf.yaml`), edited by the CLI.
@@ -48,7 +65,7 @@ reports a page left empty or in error.
48
65
  ```sh
49
66
  npm install -g @sylad/cadence # or: npx -p @sylad/cadence raf …
50
67
 
51
- raf init # docs/plan/raf.yaml + post-commit hook
68
+ raf init # docs/plan/raf.yaml + post-commit hook + .playwright-mcp/ in .gitignore
52
69
  raf add "Monthly dedup on merge" --estimate 2
53
70
  raf add "Typo in footer" --quickwin
54
71
  raf add "Loan cache" --after L1
@@ -65,16 +82,19 @@ raf gantt # docs/plan/gantt.html
65
82
 
66
83
  | Command | Effect |
67
84
  |---|---|
68
- | `raf init [--project name] [--prefix L] [--no-hook]` | create the plan and install the hook |
85
+ | `raf init [--project name] [--prefix L] [--no-hook]` | create the plan, install the hook, add `.playwright-mcp/` to `.gitignore` |
69
86
  | `raf add "title" [--estimate d] [--quickwin] [--visible] [--public "title"] [--after L2,L4] [--parent L3]` | add a lot or a sub-task, print its id (`--public`: the lot's title in the public's words, written as `public:` right after `title:`) |
70
87
  | `raf add "title" --every <days>` · `raf did <id> ["text"]` | recurring lot: `every` (days) and `last` (last time done) fields; the lot stays `todo`, `raf now` and `session start` list it under « Récurrent » with « dû depuis N j » / « prochain dans N j »; `raf did` resets the count (spec: `docs/superpowers/specs/2026-10-04-L11-tache-recurrente.md`) |
71
88
  | `raf public <id> "title"` · `raf public <id> --clear` | set, replace or remove the public title of a lot |
72
89
  | `raf start <id>` · `raf done <id> [--force]` · `raf drop <id> [--reason text]` | dated transitions (`done` refuses open sub-tasks unless `--force`) |
73
90
  | `raf note <id> "text"` | dated note — keep decisions next to the work |
91
+ | `raf show <id> [--notes]` | one lot: status, dates, `after`, public title, dated notes in order, then the counted commits (as `raf commits`); `--notes` prints the notes alone |
74
92
  | `raf commits <id>` | the commits counted for a lot (the set the code review gate uses), one `<sha> <subject>` per line, oldest first |
75
93
  | `raf now` | what to do next |
76
94
  | `raf list [--status s]` | flat list |
77
- | `raf check [--since date] [--idle 7]` | since the plan's adoption date by default: commits without a lot (commits touching only plan files are exempt), unknown ids, `todo` lots that already have commits, idle lots, `done` lots with open sub-tasks, bad or circular dependencies |
95
+ | `raf ignore <sha> \| "exact subject" [--reason text]` | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) without rewriting history: a dated, reasoned line in the plan's `acknowledged:` section; a sha is exact, a subject covers every commit carrying it |
96
+ | `raf check --ignored` | list the acknowledged commits with their date and reason |
97
+ | `raf check [--since date] [--idle 7]` | since the plan's adoption date by default (a visible lot without a public title is only a `⚠` warning: it never changes the exit code): commits without a lot (commits touching only plan files are exempt), unknown ids, `todo` lots that already have commits, idle lots, `done` lots with open sub-tasks, bad or circular dependencies |
78
98
  | `raf gantt [-o file]` | standalone Gantt page |
79
99
  | `raf hook install` | add the (non-blocking, read-only) post-commit hook |
80
100
 
@@ -190,7 +210,7 @@ What changed *for the user*, one entry per visible lot, each with a screenshot.
190
210
  ```sh
191
211
  raf add "Amounts like 3.000 read as three thousand" --visible # → L8
192
212
  raf start L8 && git commit -m "fix(L8): thousands separator" && raf done L8
193
- cadence news new L8 # docs/nouveautes/2026-09-29-amounts-like-3-000-….md
213
+ cadence news new L8 # docs/nouveautes/2026-09-29-amounts-like-3-000-….md (title: the lot's public title if any)
194
214
  # add docs/nouveautes/captures/l8.png, list it under `captures:`, write the text
195
215
  cadence news check # exit 1 on drift (also part of raf check)
196
216
  cadence news build -o frontend/public/nouveautes
@@ -219,7 +239,7 @@ captures:
219
239
 
220
240
  | Command | Effect |
221
241
  |---|---|
222
- | `cadence news new <lot…> [--title t]` | entry skeleton, dated and timed now (`date`, `created`), titled after the lot |
242
+ | `cadence news new <lot…> [--title t]` | entry skeleton, dated and timed now (`date`, `created`), titled after the lot's public title (`public`), else its title; `--title` wins |
223
243
  | `cadence news list` | entries, newest first (see *Order* below) |
224
244
  | `cadence news check` | visible lots done without entry, unknown lots, missing or undeclared screenshots, entries without creation time, bad headers |
225
245
  | `cadence news stamp` | migration: writes `created:` into entries without one (or with an empty one), from the author date of the commit that added the file under its current name (now if not committed yet) |
@@ -453,6 +473,19 @@ deliver:
453
473
  deployTimeout: 1800 # seconds, per deploy command
454
474
  ```
455
475
 
476
+ A public title (`public:`) longer than the site's Plan page accepts fails its build. Declare the limit
477
+ in `cadence.yaml` and it is caught before the push:
478
+
479
+ ```yaml
480
+ news:
481
+ publicTitleMax: 80 # characters; without the key, `raf check` warns at 80 in a project that has a news directory
482
+ ```
483
+
484
+ `raf check` also warns (`⚠`, not an error: exit code 0 if nothing else is wrong) about every visible lot, dropped ones excepted, that has no public title: the site would show its technical title (a done lot cited by a News entry is spared: the site reuses that entry's title). Fix it with `raf public <id> "…"`.
485
+
486
+ With the key, `raf public`, `raf add --public`, `raf done` (on a lot whose public title is too long) and
487
+ `cadence news new` (its title) refuse above it; without it, only `raf check` warns.
488
+
456
489
  ```sh
457
490
  cadence deliver --dry-run # preconditions, then the resolved steps; nothing runs
458
491
  cadence deliver # 0 delivered and verified · 1 a step failed · 2 refused before acting
@@ -632,14 +665,15 @@ exception is a **formatting retry** (below).
632
665
  cadence orchestrate finance-tracker:L41 ol-companion:L22 cadence:L18@haiku
633
666
  cadence orchestrate L18 # from inside a project
634
667
  cadence orchestrate … --budget 1.5M # 1500000, 1.5M, 800k; default 2M
668
+ cadence orchestrate … --max-sessions 3 # sessions running at the same moment, all waves together; default 2
635
669
  cadence orchestrate … --dry-run # preconditions + the plan of the wave; nothing is started
636
- cadence orchestrate --status [<wave>] # the table, read back from the state (default: the last wave)
670
+ cadence orchestrate --status [<wave>] # the live waves and the repositories they hold, then the table (default: the last wave of this folder)
637
671
  cadence orchestrate --resume [<wave>] [--budget 1M] [--answer ol-companion:L22 "reply"]
638
672
  ```
639
673
 
640
- You choose the lots; the order is the order given (one queue per repository, two repositories at most
641
- at the same time). `@haiku|@sonnet|@opus` sets the model of the implementation and corrections of that
642
- lot (default Sonnet; reviews are always Opus; Haiku only when you write it, for a mechanical lot). Run it
674
+ You choose the lots; the order is the order given (one queue per repository, as many repositories at the
675
+ same time as the session cap allows — 2 by default). `@haiku|@sonnet|@opus` sets the model of the implementation and corrections of that
676
+ lot (default Sonnet; reviews are Opus, except the light review below; Haiku only when you write it, for a mechanical lot). Run it
643
677
  in the background and read `--status`: it prints one line per transition and the final table.
644
678
 
645
679
  **Cycle of a lot**: preconditions (clean tracked files, lot `todo` or `doing`, dependencies met) →
@@ -652,6 +686,27 @@ you with the findings. A small lot (`estimate` ≤ 0.5 or `quickwin`) gets one s
652
686
  usability (code only when the lot is not `visible`). Failing tests (reported red, or red when
653
687
  `orchestrate.test` is run) go straight to a correction.
654
688
 
689
+ **Review sized to the lot (L108)**: a lot whose `estimate` is ≤ `orchestrate.review.threshold` (0.25 day by default)
690
+ is *light*: one single review (Sonnet by default, `code-reviewer` agent, same criteria), no minors pass — the minors are
691
+ returned to the lead as notes (proposals). A blocking or major finding on a light lot still triggers a correction, and
692
+ the review that follows is the full one (Opus); the cap of two correction passes is unchanged. A bigger lot keeps the
693
+ chain described above (Opus, two corrections, minors pass). `--dry-run` shows « revue légère » on a light lot.
694
+
695
+ ```yaml
696
+ orchestrate:
697
+ review: { threshold: 0.25, light: sonnet, full: opus } # defaults; threshold in days (0 = no light lot), models haiku|sonnet|opus
698
+ ```
699
+
700
+ `full` is also the model of the UX review and of the single pass of a small lot that is not light.
701
+
702
+ **Pre-check « deliverable already present? »** (L77). Before the first implementation of a lot that has no commit yet, a
703
+ short read-only session (Sonnet, `precheck` step, brief `templates/orchestrate/precheck.md`) looks in the repository for
704
+ what the lot asks for (another lot, or a correction, may have done it already). It answers `oui` (everything is there,
705
+ with proofs), `partiel` or `non`: on `oui` no implementation session is opened and the lot is handed back
706
+ (`livrable déjà présent : <résumé> (<preuves>)`) for the lead to drop or close it; on `partiel` the finding goes into the
707
+ implementation brief and a warning; on `non` — or an unreadable report, which only adds a warning — the wave goes on. A
708
+ lot that already has commits is never pre-checked (resuming it is legitimate). `orchestrate.precheck: false` turns it off.
709
+
655
710
  **A lot that already has commits** (a spec commit, an interrupted wave, a lot committed by hand, a lot sent back after a
656
711
  review) still starts with the implementation session: its brief tells it to read the lot, its notes and its open
657
712
  sub-tasks, which carry the findings of any earlier review. If that session ends without a new commit on a lot that
@@ -674,13 +729,40 @@ review with minors: it concludes on that review instead of staying suspended.
674
729
 
675
730
  **Choices, not questions**: the author brief tells the session to decide minor interpretation questions itself and to list them under `choix` in its report; the reviewer receives that list to re-read, and the final table prints each one (`choix fait : …`). A session stops with a question only on a real blocker: a decision that changes the scope or the architecture or is costly to undo, AND that the plan, its notes and CLAUDE.md do not settle; everything else is a choice.
676
731
 
732
+ **Frozen copy of the tool (L61)**: at the real start of a wave (not with `--dry-run` or `--status`), cadence copies the
733
+ parts of its package that run the wave (`bin/`, `dist/`, `templates/`, `agents/`, `skills/`, `package.json`; `node_modules`
734
+ is linked, not copied) into `.cadence/runs/<wave>/tool/` and relaunches the orchestrate process from that copy (the
735
+ parent process waits, relays the signals and exits with the same code or signal). Code, brief templates and agents
736
+ therefore come from the snapshot: a wave can orchestrate cadence itself, and a session of the wave may edit
737
+ `templates/` or rebuild `dist/` without touching the wave running. `--resume` relaunches from the snapshot of the
738
+ resumed wave, never from the current `dist/`; a wave without a snapshot (started before L61) resumes with the current
739
+ package and a warning. The copy is removed with the wave folder. The wave's pid in `--status` is the relaunched process.
740
+ The sessions of the wave also get `.cadence/runs/<wave>/tool/bin` (the `raf` and `cadence` entries of the snapshot) **first in their
741
+ `PATH`**, ahead of the per-project Node below and of the usual `PATH`: the `raf` a reviewer runs (`raf commits`…) is the
742
+ snapshot's, not the installed one. A wave without a snapshot leaves the `PATH` as it is.
743
+
677
744
  **What stays with you**: choosing the lots, the questions raised (`--resume --answer`), re-verifying
678
745
  after the wave (`git log`, tests, `raf check`), `raf done`, **`raf ux`** (the orchestrator reports the UX
679
746
  verdict and screenshots, it does not record it), the push and the deliveries, one project at a time.
680
747
 
681
- **Guards, imposed by the code**: at most two sessions, one per repository (a lock in the repository's
682
- shared state, `orchestrate.lock`, which also makes `cadence deliver` refuse that repository) and one wave
683
- per folder; `Agent`, `git push`, `cadence deliver`, `raf done|review|ux` are denied to the sessions; a
748
+ **Several waves at once**: the lock is per **repository** (a lock in the repository's shared state,
749
+ `orchestrate.lock`, which also makes `cadence deliver` refuse that repository), not per folder. A wave is
750
+ refused only when one of its repositories is held by a live wave (`<repo> : une orchestration y est déjà en
751
+ cours`; the lock of a dead process is detected and cleared); two waves on different repositories run side by
752
+ side, even when started from the same parent folder. They share a **cap on simultaneous sessions**, counted
753
+ across all live waves: 2 by default, `--max-sessions N` (or `CADENCE_MAX_SESSIONS=N`) to change it — give
754
+ every wave the same value: each wave counts ALL live sessions, whatever their slot, and waits while that
755
+ count has reached ITS OWN cap, so with different caps the highest one can push the total past the lowest
756
+ (which then waits). A session that finds no free slot **waits** (the wave is not refused; the journal says
757
+ `en attente d'un créneau de session depuis …`, repeated every minute, then `créneau de session obtenu après …`).
758
+ A slot or a registry entry is owned by a pid **and** its start time: a reused pid is a dead owner. Wave
759
+ identifiers are reserved atomically (`-2`, `-3` suffix when two waves start in the same minute; an existing
760
+ `--wave` is refused). The registry of live waves and the slots live under `~/.cadence/orchestrate/` (`CADENCE_HOME`
761
+ to move it): `cadence orchestrate --status` lists, from any folder, the live waves, the repositories each
762
+ holds, the cap of each wave and the slots in use.
763
+
764
+ **Guards, imposed by the code**: a global cap of simultaneous sessions, one wave per repository at
765
+ a time (above); `Agent`, `git push`, `cadence deliver`, `raf done|review|ux` are denied to the sessions; a
684
766
  temporary `pre-push` hook, installed for the duration of the wave and removed at its end, refuses any push
685
767
  from a session (`CADENCE_ORCHESTRATED` is in their environment; a repository that already has another
686
768
  `pre-push` hook is refused before anything starts — `pushurl` is never touched); after every session the
@@ -689,7 +771,9 @@ tree is an incident that stops the wave. `raf done|ux|review` and `cadence deliv
689
771
  `CADENCE_ORCHESTRATED` is set.
690
772
 
691
773
  **Budget**: the wave counts input + cache writes + output tokens (default 2 M); cache reads are kept and
692
- shown apart. When the budget (or the usage limit) is reached no new session starts, the running ones
774
+ shown apart. Each lot also has its own budget derived from its estimate (400 k tokens per day, floor 200 k, shown by
775
+ `--dry-run`): a lot that spent it gets no further session and is handed back to the lead, the wave budget stays for
776
+ the others. When the budget (or the usage limit) is reached no new session starts, the running ones
693
777
  finish, the wave is *suspended* (exit code 3) and `--resume --budget …` continues. A session that returns
694
778
  nothing readable, times out (45 min for work, 25 for a review) or fails is not retried; the lot is handed
695
779
  back with the cause. Exit codes: 0 every lot ready · 1 at least one lot handed back (question, failure,
@@ -704,18 +788,23 @@ retry count in the wave budget and in the step (`formatRetry: true`); if the out
704
788
  the step fails as usual — never a second retry.
705
789
 
706
790
  State is in `.cadence/runs/<wave>/` of the folder where the command is run (added to `.git/info/exclude`
707
- when that folder is in a repository): `wave.json`, one `<project>--<lot>.json` per lot (steps, tokens
791
+ when that folder is in a repository; `.cadence/` and `.playwright-mcp/`, where the Playwright MCP writes its
792
+ output, never count as a dirty repository, even when `.gitignore` does not list them): `wave.json`, one `<project>--<lot>.json` per lot (steps, tokens
708
793
  kept apart, session ids, commits, verdicts), the JSON output of every session and a `journal.log`. After a
709
794
  cut (Ctrl-C, WSL closed) `--resume` replays an interrupted step entirely in a new session whose brief
710
795
  lists the commits already present; finished steps are never replayed.
711
796
 
712
797
  Briefs are the templates of `templates/orchestrate/` (`implement.md` is the `lead` skill's standard
713
- brief; `--dry-run` writes the rendered ones). A project can declare, in `cadence.yaml`:
798
+ brief; `--dry-run` writes the rendered ones). The `implement` brief of a `visible` lot also carries the
799
+ News instruction (`cadence news new <lot>`, factual user-side text, a screenshot in `docs/nouveautes/captures/` or
800
+ `nocapture:`, `cadence news check` green); the brief gives the absolute path of the wave's Playwright output directory and the `implement` session gets `--add-dir` on it, to copy the capture into the repository; a lot that is not `visible` gets nothing. A project can declare, in `cadence.yaml`:
714
801
 
715
802
  ```yaml
716
803
  orchestrate:
717
804
  test: npm test # run by the orchestrator after a work step (optional)
718
- ux: http://localhost:4200 # a URL, a launch command, or { url, command } — for the UX review
805
+ build: npm run build # run after the tests; both results go to the reviewer, who does not redo them (optional)
806
+ 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
807
+ ux: http://localhost:4200 # a URL, a launch command, or { command, url, timeout? } — for the UX review (see below)
719
808
  permissionMode: auto # default
720
809
  addDirs: [/home/me/projects/tmp] # extra directories the sessions may use
721
810
  timeouts: { implement: 45, review: 25 } # minutes
@@ -724,9 +813,80 @@ orchestrate:
724
813
  verdict: python3 scripts/raf.py note {lot} "revue de code : {verdict}"
725
814
  ```
726
815
 
816
+ **`orchestrate.ux`, who starts the app**: a string is a URL if it starts with `http://` or `https://`, a launch
817
+ command otherwise; both forms behave as before (the `ux` brief gives the URL, or tells the session to start the app
818
+ with the command and stop it). The object form `{ command, url, timeout? }` makes **the program** start the app, not
819
+ the UX session. Before the `ux` step (and before the `review-small` single pass of a small `visible` lot, which follows
820
+ the same rule) the orchestrator:
821
+
822
+ 0. takes a lock on the `host:port` of `url` (a file `cadence-ux-<host>-<port>.lock` in the OS temp folder, created
823
+ exclusively, containing the pid of the orchestrator; a lock whose pid is dead is taken over). Another wave — in
824
+ this process or another — that declares the same URL waits for it, and the wait counts in `timeout`; past it the
825
+ note is « UX non vérifiée : url tenue par une autre vague » and nothing is started. The lock is given back after
826
+ the app is stopped. Declaring **distinct ports per project** is still recommended: the lock serialises two waves
827
+ on one URL, it does not make them fast;
828
+ 1. probes `url`; if it already answers it does **not** start anything, notes « port occupé » (`uxNote`: the UX is not
829
+ verified) and the lot goes on;
830
+ 2. starts `command` with `sh -c` from the repository root, in its own detached process group, its output in
831
+ `<wave>/<project>--<lot>/ux-app.log` (the command carries its own prefixes, e.g. `cd web && PORT=4300 npm start`;
832
+ there is no `env`, `cwd` or account/PIN key);
833
+ 3. probes `url` until an HTTP status below 500, for `timeout` seconds (default 300); the answer only counts while
834
+ the process group it started is still alive. A command that exits early, or
835
+ no answer in time, means « UX not verified » with the end of the log in the note: the UX session is skipped,
836
+ the lot goes on to the code review — it is never a failure of the wave (the single pass of a small lot still runs,
837
+ on the code alone, its brief saying the app could not be verified);
838
+ 4. gives the session « The running app is at `<url>` », with no instruction to start anything;
839
+ 5. at the end of the step — success, error or SIGTERM of the wave — kills the group: SIGTERM, then SIGKILL after 10 s.
840
+
841
+ ```yaml
842
+ orchestrate:
843
+ ux: { command: 'cd web && PORT=4300 npm start', url: 'http://localhost:4300', timeout: 120 }
844
+ ```
845
+
846
+ In the object form `url` must start with `http://` or `https://`, and `timeout` is only accepted together with both
847
+ `command` and `url` (the program only waits when it starts the app). A wave stopped (incident, quota, budget) before
848
+ the step suspends the lot without starting the app.
849
+
850
+ **Node per project (`.nvmrc`)**: when a project has a `.nvmrc` at its root, every session the
851
+ orchestrator launches for it (implementation, UX and code reviews, corrections) runs with the matching Node
852
+ first in its `PATH`, so `node`, `npm` and `npx` resolve to it (Astro needs 22 while the default may be 20) — and so does
853
+ the app the program starts for the UX review (`ux.command`): same links directory first in its `PATH`, no
854
+ `. ~/.nvm/nvm.sh && nvm use` prefix needed (it exits with code 3 under `sh`). Only
855
+ those four names (`node`, `npm`, `npx`, `corepack`) are exposed, through symlinks in
856
+ `.cadence/runs/<wave>/node-bin/<version>/` (recreated at start and at every `--resume`): the rest of that Node's
857
+ `bin` (globally installed `raf`, `cadence`, `claude`…) is never exposed, so it does not shadow the tools in the usual `PATH`;
858
+ only the snapshot's `tool/bin` (above) comes first, ahead of this Node directory. `--resume` resolves
859
+ the `.nvmrc` again for every live lot and refuses it like at start if it can no longer be resolved.
860
+ The file is read trimmed (`22`, `v22`, `22.22`, `v22.22.3`); the highest matching version installed under
861
+ `$NVM_DIR/versions/node` (default `~/.nvm/versions/node`) **that has an executable `bin/node`** is used: a higher
862
+ version with an empty or broken `bin` is skipped for a lower valid one (and `--dry-run` says so); if none is valid
863
+ the lot is refused. Only the sessions' environment changes,
864
+ never the orchestrator's own. No `.nvmrc` → nothing changes. A `.nvmrc` that cannot be resolved (`lts/*`, an
865
+ alias, a version not installed) refuses the lot before anything is started (exit 2), naming the requested
866
+ version and the folder searched — there is no silent fallback to the default Node. `--dry-run` prints
867
+ `node : v22.22.3 (.nvmrc 22)` under each lot that has one, with the skipped versions when it happens
868
+ (`node : v22.9.0 (.nvmrc 22 ; v22.22.3 écartée : pas de node exécutable)`).
869
+
870
+ **Minimal MCP servers per step**: every session the orchestrator launches gets `--strict-mcp-config --mcp-config
871
+ <file>`, with the file written by the orchestrator in the lot's folder of the wave
872
+ (`.cadence/runs/<wave>/<project>--<lot>/mcp-<step>.json`). `--strict-mcp-config` makes `claude` ignore every other
873
+ source (user, project, plugin servers: Serena, context7, Cloudflare…), so a session no longer starts a handful of
874
+ `npx`/`uvx` servers it does not use. `review` and `review-recheck` always get an empty set
875
+ (`{"mcpServers":{}}`); so do `implement`, `fix` and `fix-minors` on a lot without a screen. Playwright, launched as
876
+ `npx -y @playwright/mcp@latest --output-dir <wave>/<project>--<lot>/playwright`, is loaded for the `ux` step and, on a
877
+ `visible` lot, for `implement` and `fix` (`fix-minors` included) and, on a small `visible` lot, for `review-small` (its only
878
+ usability review; `review-small` of a small lot without a screen loads nothing), so the screenshots and snapshots of the work and of
879
+ the review stay with the wave, outside the repository, where the lead can look at them; the final table prints that
880
+ folder whenever Playwright was loaded for the lot, even if no `ux` step ran
881
+ (`<project>:<lot> — captures Playwright : <dir>`; `uxCaptures` in the wave state). The `implement`, `fix`,
882
+ `fix-minors`, `review-small` and `ux` briefs tell the session to give relative file names if a Playwright server is available, and
883
+ otherwise to keep any Playwright CLI output outside the repository.
884
+ `--dry-run` prints, under each step, `mcp : aucun` or `mcp : playwright (…)`. There is no per-project override in
885
+ `cadence.yaml` yet.
886
+
727
887
  Without `start`, a read-only plan's `todo` lot is refused (start it with the project's tool); without
728
888
  `verdict`, the review verdict stays in the wave's state and you report it. Only the plan's files
729
- (`plan.path`, `plan.files`, the QA expectations file) are committed from those commands; anything else dirty stops the lot.
889
+ (`plan.path`, `plan.files`, the QA expectations file) are committed from those commands; anything else dirty stops the lot (`.cadence/` and `.playwright-mcp/` excepted, as above).
730
890
 
731
891
  ## Claude Code skills
732
892
 
@@ -763,7 +923,7 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
763
923
  - **lead**: from a folder holding several projects, one subagent per project
764
924
  gathers the facts, you choose the priorities, the lots are delegated with
765
925
  `cadence orchestrate` (fresh short sessions with a standard brief — test first,
766
- commits citing the lot, no push — reviewed by the `code-reviewer` agent, see
926
+ README and usage documentation updated with the change, commits citing the lot, no push — reviewed by the `code-reviewer` agent, see
767
927
  [orchestrate](#orchestrate)), re-verified by the lead, then delivered
768
928
  one project at a time; a delivery that changes what a page shows or what it is
769
929
  served is then checked in the running app by the `qa-reviewer` agent, whose
@@ -780,7 +940,8 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
780
940
  block, dead code) or a named rule, each with `file:line` and a concrete
781
941
  scenario; real defects only, ranked, what it could not verify, and a one-line
782
942
  verdict for `raf review`. It takes the lot's commits from `raf commits`, never
783
- runs a build whose output is used live, and never edits code.
943
+ runs a build whose output is used live, and never edits code. A README or usage
944
+ documentation that does not follow the lot's change is a *major* finding.
784
945
  - **qa-reviewer** (agent): any web app; given a repository and a base URL (and
785
946
  optionally a lot id, to start with the pages it touched — for a backend-only
786
947
  lot, those that call the changed endpoints), it opens each page of
@@ -796,19 +957,56 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
796
957
  N/N, follow-ups as `raf add` lines, what it could not verify, a one-line
797
958
  verdict. Read-only: GET only, no login, nothing submitted; it stops at a PIN.
798
959
 
960
+ ## cadence-hud
961
+
962
+ A band above the Claude Code prompt (terminal and desktop app), refreshed every 5 s:
963
+
964
+ ```
965
+ ctx ▰▰▰▰▱▱▱▱▱▱ 42 % 84k/200k │ 5h 23 % ↻ 2 h 10 │ 7j 61 % ↻ 6 j 15 h │ $1.23 │ fable 410k $0.95 · sonnet 85k $0.12 │ ⚙ 2 agents
966
+ ⟳ cadence · 2026-10-07-2131 en cours │ budget ▱▱▱▱▱▱▱▱▱▱ 1 % 14k/2M │ 1 session/2
967
+ L112 implémente implement@sonnet 34 s global-setup coupe le cache de compilation de Node…
968
+ ```
969
+
970
+ Context of the session (green / orange / red at 50 and 75 %), the 5-hour and 7-day quota
971
+ windows with the time to their reset, the cost of the session and its split per model, the
972
+ subagents of this session, and every live `cadence orchestrate` wave: budget, sessions, one
973
+ aligned line per active lot (status, step, model, elapsed, start of the title), the lots still
974
+ waiting. When no wave is running, the last finished one stays on a grey line until the next
975
+ starts. Waves are read from disk (`~/.cadence/orchestrate/waves/`, then `.cadence/runs/`); no
976
+ cadence command is run, and the CLI is not required.
977
+
978
+ Install it as a plugin from the same marketplace:
979
+
980
+ ```
981
+ /plugin marketplace add Sylad/cadence
982
+ /plugin install cadence-hud@cadence
983
+ ```
984
+
985
+ `/hud` hides or shows the band. The source lives in [`plugins/cadence-hud`](plugins/cadence-hud/README.md)
986
+ (its own README has the details of every cell); it is not part of the npm package. To work on it:
987
+ `claude plugin validate plugins/cadence-hud`, `claude plugin test plugins/cadence-hud`, and
988
+ `tsc -p plugins/cadence-hud` once Claude Code has loaded the plugin at least once (it generates
989
+ the `.claude-plugin/types` the tsconfig extends).
990
+
799
991
  ## Releasing
800
992
 
801
993
  A version exists in three places and is published in two; a release does all of it, in this order:
802
994
 
803
995
  1. Bump `version` in `package.json` (then `npm install` to refresh `package-lock.json`),
804
996
  `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`, in the commit that closes the lot.
997
+ Write the version's section of `CHANGELOG.md` (`## [x.y.z] - date`, what changes for the user, lots cited)
998
+ and refresh the « What's new » summary of this README in that same commit.
805
999
  2. `git tag v<version> && git push origin main v<version>` — the tag starts `.github/workflows/publish.yml`,
806
1000
  which publishes to npm through Trusted Publishing (OIDC, no token stored anywhere): it checks the tag
807
- matches `package.json`, `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`, then `npm publish --provenance`, where `prepublishOnly` runs the type-check and the
808
- tests and `prepare` builds `dist/`; a red suite stops the publication. The trusted publisher is declared
1001
+ 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 and the
1002
+ tests and `prepare` builds `dist/`; a red suite stops the publication; then it creates the GitHub release with that CHANGELOG section as its text. The trusted publisher is declared
809
1003
  once on npmjs.com (package settings → Trusted Publisher → GitHub Actions, `Sylad/cadence`, `publish.yml`).
810
1004
  3. Watch the run: `gh run watch` (or `gh run list --workflow publish.yml`).
811
1005
  4. Check the effect: `npm view @sylad/cadence version` answers the new version.
1006
+ A run is safe to re-run, and two runs for one tag queue instead of racing (`concurrency` per ref, never cancelling the one that publishes):
1007
+ a version already on npm skips `npm publish`, and a GitHub release that is missing is created (`--verify-tag`)
1008
+ while an existing one is left alone. If the package is on npm but the release is still missing, re-run the job;
1009
+ the by-hand fallback is `gh release create v<version> --title v<version> --notes-file <the section of CHANGELOG.md> --verify-tag`.
812
1010
 
813
1011
  The Claude Code plugin is read from the repository, so pushing `main` is what updates it; npm is what
814
1012
  `npx @sylad/cadence` and a global install read, and only the tag publishes there. A missing tag, or a red
@@ -35,7 +35,8 @@ fact. If the path or the id is missing, or the lot has no commit to review, say
35
35
  and a finding must point at a line that exists today. Read what the changed code calls and what
36
36
  calls it, far enough to know whether a caller is broken.
37
37
  5. **Run what verifies**: the project's tests, type check and linter, with the commands found in
38
- step 1. A linter or coverage tool the project does not have goes under "not verified": it is
38
+ step 1. When the brief says the program already ran the tests and the build, take those results as given: do not rerun them
39
+ (nor rebuild), and run only a check they do not cover. A linter or coverage tool the project does not have goes under "not verified": it is
39
40
  not a finding. Do not run a command that deploys, publishes, pushes, migrates data or reaches a
40
41
  remote system, and never run a build whose output directory is used live — the hint is an
41
42
  output directory that a `bin` entry or a symlink on the PATH points to; list what you did not
@@ -56,14 +57,17 @@ fact. If the path or the id is missing, or the lot has no commit to review, say
56
57
  - errors swallowed, inputs trusted, resources not released, secrets or personal data written
57
58
  to a log or to the repository;
58
59
  - a written convention of the project not followed: quote the line of CLAUDE.md.
60
+ - documentation that does not follow the change: the lot changes a behaviour, a command, an option or a default and the README (or the project's usage documentation) still describes the old one or says nothing: name the file and the stale or missing passage.
59
61
  7. **Rank** each finding: *blocking* (wrong result, lost data, security hole, crash, a command that
60
62
  fails), *major* (breaks in a plausible scenario, behaviour changed without a test, a written
61
- convention broken), *minor* (costs maintenance: duplication, dead code). Untested code that is
63
+ convention broken, a README or usage documentation that does not follow the change), *minor* (costs maintenance: duplication, dead code). Untested code that is
62
64
  practically unreachable, and a rule that holds as written while an edge defeats its purpose, are
63
65
  *minor* — unless they can lose or corrupt data.
64
66
 
65
67
  ## Output
66
68
 
69
+ A proposed sub-task describes an observable bug (a wrong output, a crash, a measured regression); any other minor finding stays a note of this lot, not a sub-task.
70
+
67
71
  A short report:
68
72
 
69
73
  - **Commits reviewed**: sha and subject, and the commands you ran with their result (counts).
@@ -85,5 +89,6 @@ A short report:
85
89
  - Judge on taste: naming, formatting or structure you would have written differently is not a
86
90
  finding unless a written convention or a named practice says so.
87
91
  - Report a finding you have not read in the code or measured, or pad the list: real defects only.
88
- - Take the author's summary, or a green run you did not launch, as proof.
92
+ - Take the author's summary, or a green run you did not launch, as proof — except the results the
93
+ brief says the program ran itself.
89
94
  - Edit code, commit, or record `raf review` yourself: the session that owns the lot does it.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: precheck-reader
3
+ description: Read-only pre-check reader. Before a lot that has no commit is implemented, looks in the repository for a deliverable that another lot or a correction already made, and reports oui, partiel or non with a proof (file:line or commit sha) for each part. Quick look only, it implements nothing, runs no test suite and does not review. Used by the orchestrator, not by hand.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You check, quickly, whether the deliverable of one lot of a plan is already in the repository. You report; you never edit code.
8
+
9
+ Follow the brief you are given: read the lot in the plan, then look for each part of its deliverable in the code, the tests and `git log`. A lot with no commit of its own is the normal case here — do not stop for that, it is the reason for the check.
10
+
11
+ - Do not run the test suite, the type-check, the linter or the build: this is a quick look.
12
+ - Do not modify, commit, push, or run `raf start|done|note|review|ux`. Do not launch subagents.
13
+ - Answer `oui` only when every part is present, each with a proof; `partiel` when some are (say which); `non` otherwise. When in doubt, answer `non`.
14
+ - Give the report in the structured output the brief asks for.
@@ -42,7 +42,7 @@ missing, or the URL does not answer, say so and stop.
42
42
  write it into the repository. Say plainly that without expectations an empty state cannot be told
43
43
  from a normal one.
44
44
  4. **Open each page in a real browser** (Playwright, or the browser tool available), at **1440 px**
45
- and **390 px** wide. Let it settle: after `load`, wait a fixed few seconds, scroll through the
45
+ and **390 px** wide. Walk within the bounded pass below. Let it settle: after `load`, wait a fixed few seconds, scroll through the
46
46
  page (lazy images), wait again — never for network idle, which streams and polling never reach.
47
47
  Then measure:
48
48
  - browser state, once before the first page, in a profile already used (a persistent context, not a fresh one — a `userDataDir` reserved for QA and kept between passes, never the user's own browser profile): compare the bundle the page loaded (its script URL) with the one `index.html` references, re-read without cache — a returning visitor still holds the old one, so a difference is stated in the report — then clear the cache and measure;
@@ -95,6 +95,23 @@ missing, or the URL does not answer, say so and stop.
95
95
  false, or an error is shown to the user), *major* (secondary content missing or wrong, a section
96
96
  silently dropped after a failed or empty API call, a broken content image), *minor* (noise). A broken line of the expectations with no visible loss on the page (the content is on screen by another path) is *minor* too.
97
97
 
98
+ ## Bounded pass
99
+
100
+ The pass has a time budget: the one the caller names, otherwise 15 minutes. You keep the count
101
+ from the first page.
102
+
103
+ - Never wait in silence on your own background work (a scripted walk, a long browser task, a
104
+ listener): every wait has a timeout and is announced in one line (what you wait for, until
105
+ when). A task still running at its deadline is stopped and its pages counted as partial or not
106
+ reached — you do not wait for it again.
107
+ - Write as you go. After each width measured, append its measurements to a results file in the
108
+ temporary directory — one line per page and width, the page and the width named — before
109
+ measuring the next. Never one single file written at the end of the pass: a pass that is
110
+ stopped loses it all, and a page stopped between its two widths keeps the first.
111
+ - When the budget is spent, or the caller asks you to stop, stop walking and write the report from
112
+ the results file: pages measured at both widths are checked, a page measured at one width is
113
+ partial, the pages not reached are named, never dropped.
114
+
98
115
  ## Output
99
116
 
100
117
  A short report:
@@ -102,7 +119,7 @@ A short report:
102
119
  - **Pages checked N/N**, with the base URL and the date and time of the run, and the two widths. The
103
120
  second N is every page of the expectations (or every route discovered): a page you could not open
104
121
  is counted and named, never dropped. A page counts as checked when both widths were measured; a
105
- page checked partially (one width, tabs not opened) is counted and named as partial.
122
+ page checked partially (one width, tabs not opened) is counted and named as partial. If the pass stopped before the end (budget spent, stop requested), say so in the first line, with the pages measured so far.
106
123
  - **Findings**, most severe first, each with: the route, its kind and rank, what was expected —
107
124
  quote the line of the expectations, or name the universal check, or, for a suspect, give the
108
125
  expectation line you propose —, what was measured, and the evidence — status code, response
@@ -38,7 +38,7 @@ A short report:
38
38
  "not compliant: 1 blocking".
39
39
  - **Findings**, most severe first, each with: what (with the capture), the rule or the measure, the
40
40
  proposed change, the effort (S ≤ a session, M ≈ a day).
41
- - **Proposed sub-tasks**: one `raf add --parent <lot> "…"` line per finding worth doing.
41
+ - **Proposed sub-tasks**: one `raf add --parent <lot> "…"` line per finding worth doing. A proposed sub-task describes an observable bug (a wrong output, a crash, a measured regression); any other minor finding stays a note of this lot, not a sub-task.
42
42
  - For any redesign, a **described mockup** (layout, hierarchy, what moves where) to be approved before
43
43
  anyone codes it.
44
44
 
package/bin/cadence.js CHANGED
@@ -34,8 +34,9 @@ if (tool === 'raf') {
34
34
  cadence verify [--retry secondes] [--sha rév]
35
35
  rejoue les vérifications d'effet (deliver.verify) hors livraison, une passe, en parallèle ;
36
36
  code 0 tout vert, 1 un effet rouge, 2 rien à vérifier ; « session start » la lance aussi
37
- cadence orchestrate <projet>:<lot>[@modèle]… [--budget 2M] [--dry-run]
38
- une session claude neuve par étape (implémentation, revues, corrections) ; --status, --resume
37
+ cadence orchestrate <projet>:<lot>[@modèle]… [--budget 2M] [--max-sessions 2] [--wave id] [--dry-run]
38
+ une session claude neuve par étape (implémentation, revues, corrections) ; --status, --resume ;
39
+ --max-sessions : sessions simultanées, toutes vagues confondues (CADENCE_MAX_SESSIONS)
39
40
  cadence skills install [--dir .claude] [--force]
40
41
  installe les skills Claude Code session-start, session-close, deliver et l'agent ux-reviewer`);
41
42
  process.exitCode = !tool || ['help', '--help', '-h'].includes(tool) ? 0 : 2;