@sylad/cadence 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +26 -8
  4. package/agents/code-reviewer.md +2 -2
  5. package/agents/doc-keeper.md +19 -0
  6. package/agents/qa-reviewer.md +2 -2
  7. package/agents/spec-writer.md +1 -1
  8. package/agents/ux-reviewer.md +1 -1
  9. package/bin/cadence.js +12 -1
  10. package/dist/audit.js +14 -4
  11. package/dist/check.js +6 -0
  12. package/dist/cli.js +295 -29
  13. package/dist/config.js +147 -5
  14. package/dist/corrections.js +78 -22
  15. package/dist/deliver.js +85 -21
  16. package/dist/docsync.js +19 -3
  17. package/dist/git.js +16 -3
  18. package/dist/lead.js +37 -12
  19. package/dist/migrate.js +166 -0
  20. package/dist/orchestrate/briefs.js +28 -6
  21. package/dist/orchestrate/command.js +303 -34
  22. package/dist/orchestrate/cycle.js +720 -273
  23. package/dist/orchestrate/guard.js +2 -2
  24. package/dist/orchestrate/integrate.js +85 -0
  25. package/dist/orchestrate/launch.js +37 -5
  26. package/dist/orchestrate/mutex.js +30 -0
  27. package/dist/orchestrate/pool.js +74 -36
  28. package/dist/orchestrate/registry.js +123 -14
  29. package/dist/orchestrate/result.js +1 -1
  30. package/dist/orchestrate/snapshot.js +25 -0
  31. package/dist/orchestrate/spec.js +89 -5
  32. package/dist/orchestrate/state.js +42 -6
  33. package/dist/orchestrate/table.js +61 -2
  34. package/dist/orchestrate/timeouts.js +40 -0
  35. package/dist/orchestrate/worktree.js +368 -0
  36. package/dist/perf.js +620 -0
  37. package/dist/plan.js +92 -6
  38. package/dist/ready-free.js +109 -0
  39. package/dist/ref/add.js +116 -0
  40. package/dist/ref/check.js +338 -0
  41. package/dist/ref/command.js +78 -0
  42. package/dist/ref/corpus-render.js +83 -0
  43. package/dist/ref/corpus.js +201 -0
  44. package/dist/ref/evaluate.js +241 -0
  45. package/dist/ref/figures.js +179 -0
  46. package/dist/ref/measure.js +330 -0
  47. package/dist/ref/pdf.js +104 -0
  48. package/dist/ref/png.js +125 -0
  49. package/dist/ref/store.js +40 -0
  50. package/dist/ref/tools.js +51 -0
  51. package/dist/resources.js +51 -0
  52. package/dist/session.js +5 -2
  53. package/dist/state.js +82 -0
  54. package/dist/testgate.js +15 -0
  55. package/docs/cadence-hud.md +12 -0
  56. package/docs/corrections.md +1 -1
  57. package/docs/cycle-vague-dark.svg +1 -1
  58. package/docs/cycle-vague-light.svg +1 -1
  59. package/docs/deliver.md +58 -3
  60. package/docs/fonctionnement-wave-dark.svg +1 -1
  61. package/docs/fonctionnement-wave-light.svg +1 -1
  62. package/docs/fonctionnement.md +5 -5
  63. package/docs/lead.md +37 -12
  64. package/docs/news.md +24 -1
  65. package/docs/orchestrate.md +230 -19
  66. package/docs/perf.md +89 -0
  67. package/docs/raf.md +89 -11
  68. package/docs/ref.md +316 -0
  69. package/docs/session.md +2 -2
  70. package/docs/skills.md +9 -2
  71. package/docs/spec.md +1 -1
  72. package/docs/stabilite.md +4 -4
  73. package/docs/whats-new.md +4 -0
  74. package/package.json +3 -1
  75. package/skills/deliver/SKILL.md +6 -1
  76. package/skills/lead/SKILL.md +31 -6
  77. package/skills/session-close/SKILL.md +1 -1
  78. package/skills/session-start/SKILL.md +2 -2
  79. package/templates/inventaire.json +96 -0
  80. package/templates/orchestrate/accept-back.md +1 -0
  81. package/templates/orchestrate/accept.md +1 -0
  82. package/templates/orchestrate/fiche.md +1 -0
  83. package/templates/orchestrate/fix-docs.md +18 -0
  84. package/templates/orchestrate/fix-minors.md +2 -1
  85. package/templates/orchestrate/fix.md +5 -2
  86. package/templates/orchestrate/implement.md +6 -3
  87. package/templates/orchestrate/maquette.md +2 -1
  88. package/templates/orchestrate/precheck.md +2 -1
  89. package/templates/orchestrate/review-recheck.md +2 -0
  90. package/templates/orchestrate/review-small.md +5 -1
  91. package/templates/orchestrate/review.md +2 -0
  92. package/templates/orchestrate/spec-back.md +2 -1
  93. package/templates/orchestrate/spec.md +4 -3
  94. package/templates/orchestrate/ux.md +3 -1
package/docs/ref.md ADDED
@@ -0,0 +1,316 @@
1
+ [← cadence](../README.md)
2
+
3
+ # `cadence ref`: reference documents (specs, norms, mock-ups)
4
+
5
+ A spec delivered as a PDF often keeps part of its information in images: screen mock-ups,
6
+ tables drawn as pictures, message templates, figures of a norm (WMO, ICAO…). `cadence ref`
7
+ turns such a document into files a session can read without losing the pictures. The whole
8
+ design is in
9
+ [`docs/superpowers/specs/2026-10-23-ref-documents-de-reference-design.md`](superpowers/specs/2026-10-23-ref-documents-de-reference-design.md)
10
+ (issue #14); this page describes what is delivered so far: **`cadence ref add`** (lot 1 of 10), **`cadence ref measure`** (lot 2 of 10) **`cadence ref check`** with the inventory schema (lot 3 of 10), and **`cadence ref eval`** (lot 4 of 10, with the synthetic corpus `cadence ref corpus`).
11
+
12
+ ## `cadence ref add <document.pdf> [--sections 3-5,9] [--slug name]`
13
+
14
+ Registers a PDF and builds, for it:
15
+
16
+ - **pages rendered at 200 dpi** (`pages/page-<n>.png`, `pdftoppm`);
17
+ - **positioned text** of every page (`pdftotext -bbox-layout`): each line with its box
18
+ `x, y, l, h` in pixels of the rendered page (200 dpi);
19
+ - **embedded images** (`pdfimages`, `images/p<page>-<n>.png`) with their region in the same
20
+ pixels (`pdftohtml -xml`);
21
+ - **the figure index**: one entry per caption at the start of a line (`Figure n`, `Fig. n`,
22
+ `Table n`, `Tableau n`, `Planche n`). After a separator (`—`, `–`, `:`, `.`, `-`) the line is a
23
+ caption whatever the case of the title (`Figure 1 — map of stations`); without one,
24
+ `Figure 3 shows…`, `Figure 5, which…` and `Figure 5 (page 4) and…` cite the figure. One exception:
25
+ a line made only of the number and one closed parenthesis (`Table IV.3 (in accordance with
26
+ 4.3.11)`, WMO-No. 49 vol. II) is a caption when no other line looks like the caption of that
27
+ number; with another one, or open (`Table 11 (see Example…`), it cites. A separator
28
+ followed by nothing (`figure 24.` ending a sentence) is not a caption either. A
29
+ table-of-contents entry (dot leaders on its line or on the next one, or any line of a contents
30
+ page: one where at least two lines, and more than half, have dot leaders or end with a possible
31
+ page number) is neither a caption nor a citation, so a figure that only the contents list names
32
+ comes out as « nothing cites it ». A trailing number alone never makes an entry:
33
+ `Table 4.1. … presented in Chapter 4` among prose is a caption. When several lines look like the
34
+ caption of one figure, those with a title are kept (`Figure 5 Jet speed` rather than a bare `Figure 5`), if any; among them
35
+ the first on a page that holds an image wins, then the first with a separator, then the first. Each entry has its page, line, box, its images, and **the passages that cite it** (page,
36
+ line, exact line; `nommee` when the text names the figure, `designee` for « as shown
37
+ below » / « ci-dessous », attached to the nearest caption of the page). Plurals count one citation per figure
38
+ (`Figures 2 and 3`, `Tables 4 to 6`, `Figs. 2.1 et 2.2`); a number keeps the letters of an annex numbering (`Figure 1.B.1`, `Table 12.B.3`). Listed under **`unresolved`**
39
+ (« lien non résolu »): a citation of a figure with no caption, a figure that nothing cites,
40
+ and an image on a page that has no caption (`image sans légende`).
41
+
42
+ - **thumbnails of every page** (`thumbs/page-<n>.png`, 40 dpi) and the **pages without text**:
43
+ a page with fewer than 20 characters of text is taken for scanned and reported
44
+ (`pages sans texte : 30 (1-4, 6-31)`), so a scanned norm does not look indexed while its index
45
+ is empty; the thumbnails tell which pages to render with `--sections`. (Both recipes come from
46
+ the `norme-etude` skill of maritime-atlas: `refdoc.py index`.)
47
+
48
+ Without `--sections`, a document of 30 pages or fewer is rendered whole; a larger one only gets
49
+ its index (text and figures), no page is rendered. With `--sections`, the pages asked for are
50
+ rendered **plus the pages of the figures they name**, wherever they are in the document. Running
51
+ `add` again on the same document (same sha256) keeps the pages rendered before and adds the new
52
+ ones; a slug taken by another document is refused (`--slug` picks another name).
53
+
54
+ A PDF that asks for a password, forbids copying, is damaged, is not a PDF or has no page is refused with the reason. Image
55
+ documents (PNG, JPG) are not read yet.
56
+
57
+ ## `cadence ref measure <slug> <page> <zone> [--quoi couleurs|contraste|boites|texte] [--largeur 1440] [--json]`
58
+
59
+ Measures a zone of a **rendered** page of a document registered by `add` (a page `add` did not render
60
+ is refused with the command that renders it). The zone is `page` (the whole page), `x,y` (one
61
+ point) or `x,y,l,h` (a rectangle), in pixels of the rendered page (200 dpi: the unit of the boxes in
62
+ `document.json`). Every length in the answer is brought back to a **reference width** of 1440 px
63
+ (`--largeur`), the width of a screen, whatever the resolution of the page: the first line says the
64
+ factor. Without `--quoi`, the four measures; a point only has colours.
65
+
66
+ - **`couleurs`**: the exact colour of a point (`#c81e28`, `rgb(200, 30, 40)`); for a rectangle the
67
+ mean (rounded) and the **dominant colours** with their share of the zone (grouped by 8 levels per
68
+ channel, at most 5, none under 1 %).
69
+ - **`contraste`**: WCAG 2.2 contrast ratio between the zone's **background** (its dominant colour) and
70
+ its **text**: every colour carried by at least 10 % of the non-background pixels is a text colour
71
+ (an anti-aliased edge is spread over many shades and stays under that share), and the verdict is the
72
+ one of the **least contrasted** text colour, never a mean of several (a red and a blue text do not
73
+ make a purple that is not in the image). The one exception is a zone where no colour reaches that share (a gradient, text over a photo): the text is then the mean of the pixels furthest from the background, a colour that may not exist in the image, and the answer says so (`texte.moyenne: true` in JSON, a note on the `contraste` line). The three thresholds are read on the unrounded ratio: AA (4.5), AA large text and interface
74
+ components (3), AAA (7). `#767676` on white gives 4.54, `#777777` 4.48. A zone of one colour has no
75
+ contrast to measure.
76
+ - **`boites`**: the connected shapes that differ from the background (8 neighbours), the 20 largest first, and the
77
+ bounding box of all the content; coordinates are those of the page, not of the zone. A pixel belongs to a shape from a distance of 24 (RGB, Euclidean) to the background, the threshold under which a zone is flat: a pale card (`#eeeeee` on white) is a shape.
78
+ Shapes under 16 pixels are dust.
79
+ - **`texte`**: the words tesseract reads (English model, `--psm 6`) with their box and confidence, and the
80
+ **estimated font size**: the median height of the words that have no descender and a letter as tall
81
+ as a capital (`Hello`, `2026`, not `gap`) read with confidence ≥ 60, divided by 0.72 (the cap height
82
+ of Helvetica / Arial in em). The size is the one of the reference width.
83
+
84
+ **Declared tolerance** (last line, and `tolerance` in `--json`): colours ±1/255 per channel (mean
85
+ rounded), boxes ±1 pixel of the page, i.e. ±⌈factor⌉ px at the reference width, font size ±5 % (the cap
86
+ height of a face is 0.70 to 0.73 em) plus one pixel of cap height, given with the measure
87
+ (`police : ≈ 28.2 px (±10 %, 4 mot(s) …)`). The image is read as it is: an alpha channel is composed over white.
88
+ Only 8-bit, non-interlaced PNG (what `pdftoppm` writes) is read; another kind is refused with the reason.
89
+
90
+ ```
91
+ $ cadence ref measure norme 2 page --quoi contraste
92
+ norme p.2 · zone 0,0,1700,2200 px de la page · ramené à 1440 px (×0.85)
93
+ contraste : 4.54:1 (texte #767676 sur fond #ffffff) · AA ✓ · AA grand texte et composants ✓ · AAA ✗
94
+ tolérance : couleurs ±1/255 par canal, boîtes ±1 px, police ±5 % plus un pixel de hauteur de capitale
95
+ ```
96
+
97
+ `--json` prints one object (`document`, `page`, `zone`, `echelle`, `tolerance`, then `couleurs`,
98
+ `contraste`, `boites`, `texte` as asked). Exit codes: 0 done, 2 refusal (tool, unknown document,
99
+ page absent or not rendered, invalid zone or option).
100
+
101
+ ## `cadence ref check <inventaire.json | lecture.json>`
102
+
103
+ Validates an **inventory** (`genre: "inventaire"`) or a **reading** (`genre: "lecture"`): the JSON file in which the
104
+ reference analysis lists what the document shows. The model to copy is
105
+ [`templates/inventaire.json`](../templates/inventaire.json) (one element of each of the eleven types, slug `exemple`).
106
+ `check` validates the **form**, never the truth of a measure; it calls no system tool (it works with an empty `PATH`), does not read
107
+ `references.dir` and writes no file.
108
+
109
+ ```
110
+ $ cadence ref check inventaire.json
111
+ ✗ elements[3].libelle : champ du type texte manquant ou invalide
112
+ ✗ elements[5].ecran : exemple#texte-1 ne désigne aucun élément de type ecran
113
+ 2 écart(s)
114
+ $ cadence ref check templates/inventaire.json
115
+ inventaire valide : 11 éléments (ecran: 1, composant: 1, etat: 1, texte: 1, jeton: 1, table: 1, gabarit: 1, schema: 1, lien: 1, statut: 1, silence: 1)
116
+ ```
117
+
118
+ Exit codes: **0** valid (summary on the standard output; « lecture valide » for a reading), **1** deviations (one
119
+ `✗ <path> : <reason>` line per deviation then `N écart(s)`, **on the standard output**), **2** wrong call (not exactly one
120
+ argument) or unreadable file (absent, a folder, empty, not JSON, UTF-8 BOM included): the reason goes to the error output. A
121
+ duplicated JSON key is not detected (the last one wins, as with `JSON.parse`). An unknown key, at the root or in an element, is ignored.
122
+
123
+ **Root**: `version` (1), `genre`, `document` `{ slug (kebab-case), sha256 (64 lowercase hex) }`, `elements` (non-empty array), and, in an
124
+ expected inventory, `pages` (the number of pages of the document, an integer ≥ 1; not checked here, read by `ref eval` for the tokens per page).
125
+
126
+ **Every element**: `type` (`ecran`, `composant`, `etat`, `texte`, `jeton`, `table`, `gabarit`, `schema`, `lien`, `statut`, `silence`);
127
+ `id` = `<slug of document>#<type>-<n>` (n ≥ 1, no leading zero; the slug is compared as it is, even invalid, and two elements never share an id);
128
+ `source` `{ page ≥ 1, zone { x, y, l, h } }` in pixels of the page rendered at 200 dpi (x, y ≥ 0, l, h ≥ 1; the zone is optional only for a `silence`);
129
+ `citations` (optional) `[{ page, ligne, extrait }]`; `confiance` (`mesure`, `lu`, `incertain`); `cause` (non-empty, **only** on an
130
+ `incertain` element); `ocr` (optional boolean; in a reading an `ocr: true` element is never `lu`).
131
+
132
+ | Type | Own keys |
133
+ |---|---|
134
+ | `ecran` | `largeur_reference` (integer > 0, 1440 in the model), `zones` (non-empty) `[{ nom, x, y, l, h, pct { x, y, l, h } }]`: pixels at that width, `pct` numbers from 0 to 100 |
135
+ | `composant` | `role` (from the list of roles below), `position { x, y }` (≥ 0), `taille { l, h }` (≥ 1), `contenu` (text, may be empty), `ecran` (id of an `ecran`) |
136
+ | `etat` | `nom` (non-empty, open list), `cible` (id of a `composant` or an `ecran`) |
137
+ | `texte` | `libelle` (non-empty, no leading or trailing blank), `role` (from the list of roles below) |
138
+ | `jeton` | `categorie` (`couleur`, `taille_police`, `espacement`, `rayon`, `opacite`), `valeur` (non-empty string or finite number), `point` (`{ x, y }` or a zone `{ x, y, l, h }`; required when `confiance` is `mesure`), `contraste` (optional) `{ texte, fond, ratio 1 to 21 }` |
139
+ | `table` | `colonnes` (non-empty array of texts), `lignes` (non-empty, each row has as many text cells as columns) |
140
+ | `gabarit` | `nom`, `champs` (non-empty, ordered) `[{ nom, obligatoire (boolean), format, min?, max? }]`, `min ≤ max` |
141
+ | `schema` | `noeuds` (non-empty) `[{ id, libelle }]` (unique ids), `flux` `[{ de, vers, libelle, direction }]` (`de`, `vers` = node ids; `direction` = `de_vers` or `bidirectionnel`) |
142
+ | `lien` | `figure`, `passage { page, ligne, extrait }`, `constat` (`confirme`, `precise`, `contredit`); `precise` adds `lecture_ambigue`, `interpretation`, `raison`; `contredit` adds `enonces` (exactly two texts) and carries no `interpretation` |
143
+ | `statut` | `libelle`, `couvre` (non-empty array of ids of any type, itself included) |
144
+ | `silence` | `question` (non-empty, ends with `?`) |
145
+
146
+ **Roles** (`role` of a `texte` and of a `composant`): a **closed list**, because the role takes part in the identity of a component and
147
+ in the verdict on a text in `ref eval`. A role outside the list is a deviation of the schema (`« label » n'est pas un rôle de la
148
+ liste : …`), never a reading error counted later. The list is in `ROLES` of `src/ref/check.ts` and in the `roles` key of
149
+ [`templates/inventaire.json`](../templates/inventaire.json) (one line of definition per role; a test keeps both identical):
150
+
151
+ | Role | Definition |
152
+ |---|---|
153
+ | `titre` | title of a document, a page or a section |
154
+ | `titre-panneau` | title of a panel or a window of an interface |
155
+ | `paragraphe` | sentence or block of running text |
156
+ | `legende` | text that names a figure, a table or a colour scale (« Figure 1 — … ») |
157
+ | `etiquette-figure` | word or value written inside a figure (arrow, axis, curve, threshold) |
158
+ | `exigence` | statement of one requirement in a list of requirements |
159
+ | `ligne` | line of a record or a form, with no other function |
160
+ | `message_erreur` | message that reports an error or a refusal to the user |
161
+ | `unite` | unit attached to a value (kt, hPa, m) |
162
+ | `info_bulle` | short text shown on hover or focus of an element |
163
+ | `bouton` | control one acts on (`composant`), or its label (`texte`) |
164
+ | `champ` | area where a value is typed (`composant`), or its label (`texte`) |
165
+ | `case_a_cocher` | two-state control, ticked or not, independent of the others |
166
+ | `selecteur` | control that picks one value in a closed list (drop-down, radio buttons) |
167
+ | `onglet` | entry of a bar that switches between views of the same level |
168
+ | `menu` | list of actions or destinations that opens from a control |
169
+ | `liste` | sequence of items of the same kind shown one under the other |
170
+ | `panneau` | interface area that groups controls (`composant`) |
171
+ | `carte` | map or chart display area (`composant`) |
172
+ | `icone` | pictogram with no text that stands for an action or an object |
173
+ | `badge` | pill or mark that qualifies an element (status, counter) |
174
+
175
+ References (`ecran`, `cible`, `couvre`, flux nodes) are resolved on the whole list of ids, wherever the element is. Every slug and id
176
+ deviation (R6/R9) is listed, none masks another; elsewhere a deviation can hide others (a non-object element only reports its
177
+ `type`, an invalid `confiance` skips the `cause` check, an invalid `elements` validates no element). The key names of `ecran` and the values of `direction` are a format shared with the later lots
178
+ (they are hypotheses of the sheet `docs/specs/ref-inventaire-schema/fiche.yaml`).
179
+
180
+ ### Where the files go
181
+
182
+ Outside the repository: `references.dir` of `cadence.yaml` (relative to that file), by default
183
+ `$XDG_DATA_HOME/cadence/references` (`~/.local/share/cadence/references`). A `references.dir`
184
+ inside a Git repository is refused: the documents may be private. One folder per document:
185
+
186
+ ```
187
+ <references.dir>/<slug>/document.json source, sha256, pages (lines, boxes), images, figures, unresolved
188
+ <references.dir>/<slug>/pages/ page-<n>.png, 200 dpi
189
+ <references.dir>/<slug>/images/ p<page>-<n>.png
190
+ <references.dir>/<slug>/thumbs/ page-<n>.png, 40 dpi, every page
191
+ ```
192
+
193
+ ### System tools
194
+
195
+ `cadence ref` calls poppler (`pdftoppm`, `pdftotext`, `pdfimages`, `pdfinfo`, `pdftohtml`),
196
+ ImageMagick (`magick`, or `convert` for version 6) and tesseract. They are looked up in the
197
+ `PATH` before the document is read; each one missing is named with its install command and the
198
+ command exits **2**, writing nothing and installing nothing:
199
+
200
+ ```
201
+ ref : outil absent du PATH, à installer (rien n'est installé d'office) :
202
+ magick ou convert : sudo apt install imagemagick
203
+ ```
204
+
205
+ (`poppler-utils`, `imagemagick`, `tesseract-ocr`.) `add` does not call ImageMagick or tesseract, `measure` calls tesseract (and only for the text); both check all the tools at the first call, as the design asks.
206
+ ImageMagick is not called by either: `measure` decodes the rendered PNG pages itself. The rest of cadence depends on none of them.
207
+
208
+ Exit codes: 0 done, 2 refusal (tool, file, option, slug, folder).
209
+
210
+ ## `cadence ref eval <attendu> <lecture> [--json]`
211
+
212
+ Scores a reading against the inventory that is known to be true (the evaluation of the design, §« Mesure »). `attendu` is an
213
+ inventory (`genre: "inventaire"`), `lecture` a reading or an inventory of the same document; both go through the checks of
214
+ `ref check` first. Two folders work too (a file against a folder, or the reverse, is refused with exit 2 and the accepted combinations: two files, or two folders): `attendu` holds `<cas>/attendu.json` (what `ref corpus` writes) and `lecture`
215
+ `<cas>/lecture.json`; a case without a reading counts as read with nothing (recall 0) and is named under `sans lecture`.
216
+
217
+ An element read is matched to an expected one **by type and identity**, never by id or zone (two independent readings number
218
+ their elements their own way; blanks inside a text do not count, and the case does not either, except for a `texte`, which is verbatim: `120 KT` is not `120 kt`): `texte` and `statut` by `libelle`, `composant` by
219
+ `role` and `contenu`, `etat` by `nom` and the identity of its target, `jeton` by `categorie` and `valeur` (a colour is compared as
220
+ written), `table` by `colonnes`, `gabarit` by `nom`, `schema` by the labels of its nodes, `lien` by `figure`, `silence` by
221
+ `question`, `ecran` (one per document). An expected element is matched once. The matched element is **juste** if it also says the
222
+ same thing: the `role` of a `texte`, the `lignes` of a `table`, the `champs` of a `gabarit` (name, mandatory, format, bounds, in
223
+ order), the `flux` of a `schema`, the `constat` of a `lien` (the interpretation of a `precise` is not compared), the elements
224
+ covered by a `statut`, the `largeur_reference` of an `ecran`.
225
+
226
+ - **rappel** = juste / expected, **précision** = juste / read, both by type and overall;
227
+ - **faux lu** = read elements marked `lu` or `mesure` (« sûrs ») that are not juste, over the sûrs: an invented element, a wrong
228
+ cell, a `lien` that confirms what the figure contradicts. An `incertain` element is never a faux lu (it lowers the precision);
229
+ - **pièges**: an expected element carries `piege: "<name>"`; a piège is found when all its elements are juste (`✓ nom : 1/1`);
230
+ - **tokens par page**: the `usage: { tokens }` of the reading divided by the `pages` of the expected inventory (a root key of the
231
+ expected file, see the model), for the cases that have both; without `usage`, « non mesurés » (`null` in `--json`), never a zero.
232
+ A case whose expected inventory has **no `pages`** is left out of the figure, tokens and pages alike, and the output says so:
233
+ `tokens par page : N cas sans `pages` exclus (names)` (`tokens.exclus` in `--json`).
234
+
235
+ The output is the totals, then one line per type present (in the order of the eleven types), then the pièges (`✓` all found, `✗`
236
+ some missing) and, last, the cases without a reading. This one is a reading of the corpus that confirms a link the figure makes
237
+ precise, misreads a table cell, an opacity and a state, drops one line of the scan and counts 4,200 tokens for the one case that
238
+ reports `usage`:
239
+
240
+ ```
241
+ $ cadence ref eval tmp/corpus-ref lectures/
242
+ 7 cas · 47 éléments attendus · 46 lus
243
+ rappel : 89.4 % (42/47) · précision : 91.3 % (42/46)
244
+ faux lu : 9.3 % (4/43 sûrs)
245
+ tokens par page : 4200 (4200 sur 1 pages)
246
+ par type :
247
+ ecran rappel 100 % (2/2) · précision 100 % (2/2) · faux lu 0/2
248
+ composant rappel 100 % (5/5) · précision 100 % (5/5) · faux lu 0/5
249
+ etat rappel 50 % (1/2) · précision 50 % (1/2) · faux lu 1/2
250
+ texte rappel 96.7 % (29/30) · précision 100 % (29/29) · faux lu 0/26
251
+ jeton rappel 50 % (1/2) · précision 50 % (1/2) · faux lu 1/2
252
+ table rappel 0 % (0/1) · précision 0 % (0/1) · faux lu 1/1
253
+ gabarit rappel 100 % (1/1) · précision 100 % (1/1) · faux lu 0/1
254
+ lien rappel 66.7 % (2/3) · précision 66.7 % (2/3) · faux lu 1/3
255
+ statut rappel 100 % (1/1) · précision 100 % (1/1) · faux lu 0/1
256
+ pièges :
257
+ ✗ controle-translucide : 0/1
258
+ ✓ figure-contredit-texte : 1/1
259
+ ✓ gabarit-type-bufr : 1/1
260
+ ✓ page-proposal : 1/1
261
+ ✗ scan-bruite : 3/4
262
+ ✗ table-en-image : 0/1
263
+ ✗ texte-ambigu-leve-par-figure : 0/1
264
+ ```
265
+
266
+ Reading it: the `etat` line is the recall of the states (the threshold of the design is 95 % of the components **and** states);
267
+ in `faux lu 1/2` the second number is the number of sûrs read of that type (an `incertain` one, like the OCR texts of the scan,
268
+ is not in it). A misread affirmation (the table cell, the confirmed link) is an element found but not juste; a misread part of the identity (the opacity, the name of a state) is a missing element and an invented one; both cost recall and add a faux lu, while a dropped element (the scan line) costs recall only. The `✗` pièges name what a reader must work on: here the confirmed link, the cell and the
269
+ opacity are exactly the traps of those cases.
270
+
271
+
272
+ A reading whose `document.slug` or `document.sha256` differs from the expected one is refused (never a measure over two versions
273
+ mixed). Exit codes: **0** done, **2** refusal (wrong call, file absent or unreadable, invalid reading, other document, folder with
274
+ no `attendu.json`). `--json` prints one object: `total`, `types`, `pieges`, `tokens`, `cas`.
275
+
276
+ ## `cadence ref corpus <dossier> [--sans-rendu]`
277
+
278
+ Writes the **synthetic corpus with a known truth** that `ref eval` scores against (design §« Évaluation »): seven small documents,
279
+ one per trap taken from a real defect, each with the inventory that is true of what is drawn.
280
+
281
+ | Case (= trap) | What it draws | Expected `piege` element |
282
+ |---|---|---|
283
+ | `texte-ambigu-leve-par-figure` | « written beside the arrow » (head or tail?), the figure puts `120 KT` over the tail | `lien` `precise` |
284
+ | `figure-contredit-texte` | text says 80 kt, the figure draws the threshold at 100 kt | `lien` `contredit` |
285
+ | `table-en-image` | a code table rasterised at render time: its cells are not in the PDF text layer | `table` |
286
+ | `gabarit-type-bufr` | BUFR section 1: ordered fields, mandatory or optional, format, bounds | `gabarit` |
287
+ | `controle-translucide` | a white button at 25 % opacity on a striped gradient, next to an opaque one | `jeton` `opacite` |
288
+ | `page-proposal` | page 1 requirements, page 2 a panel mock-up carrying a « Proposal » badge; two rows of the panel are drawn selected and greyed (`etat` `selectionne`, `desactive`), and the caption confirms the requirement (`lien` `confirme`) | `statut` |
289
+ | `scan-bruite` | a station sheet rasterised, grainy and blurred: the PDF has no text layer, expected texts are `ocr` | `texte` (`ocr`) |
290
+
291
+ Each case folder holds `document.html` (one 8.5×11 in `section.page` per page, deterministic), `attendu.json` (an inventory that
292
+ passes `ref check`, slug `corpus-<case>`, `sha256` of the `document.pdf` once rendered (of the HTML with `--sans-rendu`, which writes no PDF), `pages`, zones in pixels at 200 dpi, `piege` on the trap elements), and,
293
+ unless `--sans-rendu`, `document.pdf` and `page-<n>.png` (1700×2200, 200 dpi) written by **Playwright** (Chromium). Texts of a table,
294
+ a template or a schema belong to it and are not listed as `texte`; labels of a figure are.
295
+
296
+ **One convention for the labels** (what a faithful reading must find, so that `ref eval` does not count it as a faux lu): every
297
+ visible text outside a table or a template is an expected `texte`, with its role, **including the label of a component**: the
298
+ button `Layers` is a `composant` (`bouton`, `contenu: "Layers"`) **and** a `texte` (`bouton`, `libelle: "Layers"`), the rows `Wind`
299
+ and `Waves` of the panel are `case_a_cocher` components and `case_a_cocher` texts, the `Proposal` badge is a `statut` and a `badge`
300
+ `texte`. The tests check it both ways: every label of the expected inventory is drawn, and every drawn text (the HTML, and the
301
+ words of `pdftotext` once rendered) has an expected `texte`. Playwright is not a dependency of cadence:
302
+ install it (`npm i -D playwright`, `npx playwright install chromium`) or point `CADENCE_PLAYWRIGHT` to the folder of the package;
303
+ without it, or when Chromium does not launch (`npx playwright install chromium` missing), the command exits **2** and writes nothing (`--sans-rendu` still writes HTML and expected files). An existing folder is
304
+ refused (exit 2). The corpus is written where you say, not in `evals/`, which `claude plugin eval` reads as case folders.
305
+
306
+ The tests of the real render (PDF and PNG sizes, page counts, hash, zones of the texts against `pdftotext -bbox`) run only when
307
+ `CADENCE_PLAYWRIGHT` points to the package; `.github/workflows/tests.yml` installs Playwright and Chromium and sets it. Locally:
308
+ `CADENCE_PLAYWRIGHT=<folder of the playwright package> npx vitest run test/ref-corpus.test.ts`.
309
+
310
+ The PDF changes at every render, so the expected `sha256` is the one of the PDF just written: register it with the slug of the
311
+ expected file (`cadence ref add tmp/corpus-ref/<case>/document.pdf --slug corpus-<case>`), or `ref eval` refuses the reading.
312
+
313
+ ```
314
+ $ cadence ref corpus tmp/corpus-ref
315
+ $ cadence ref eval tmp/corpus-ref lectures/ # lectures/<case>/lecture.json
316
+ ```
package/docs/session.md CHANGED
@@ -3,7 +3,7 @@
3
3
  # session
4
4
 
5
5
  ```sh
6
- cadence session start # notes from the last close, lots in progress (silent ones flagged),
6
+ cadence session start # notes from the last close, lots in progress (commit count, `0 commit` included, and last activity; silent ones flagged),
7
7
  # work done since yesterday by lot, drift, repo state, 3 proposals
8
8
  cadence session start --since "3 days ago" --idle 2
9
9
  cadence session close # commits by lot since the last `session start`,
@@ -35,7 +35,7 @@ ouverture`, then the lines as written, blank ones dropped), so `session-close` c
35
35
  them without running `session start` again.
36
36
 
37
37
  `--all` runs the same command inside each project found under the current folder —
38
- `docs/plan/raf.yaml`, or a `cadence.yaml` with `plan:` — and never enters a project
38
+ `.cadence/raf.yaml`, `docs/plan/raf.yaml`, or a `cadence.yaml` with `plan:` — and never enters a project
39
39
  to look for another. `--since` and `--idle` go to every project; an error in one
40
40
  project is printed in its section and the others still run. It replaces the shell
41
41
  loop over the sub-folders. A project that is not the root of its own git repository gets a `✗` line
package/docs/skills.md CHANGED
@@ -40,7 +40,7 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
40
40
  one project at a time; a delivery that changes what a page shows or what it is
41
41
  served is then checked in the running app by the `qa-reviewer` agent, whose
42
42
  blocking findings come back to you. As many
43
- subagents as the session cap at most (2 by default, `CADENCE_MAX_SESSIONS`), never two in the same repository. The whole flow is drawn in
43
+ subagents as the session cap at most (2 by default, `CADENCE_MAX_SESSIONS`), the ones it starts by hand declared with `cadence lead hold`, never two in the same repository. The whole flow is drawn in
44
44
  [docs/lead.md](lead.md); how a lot, a wave, the lead and the service of cadence itself fit together, in four figures, is in
45
45
  [docs/fonctionnement.md](fonctionnement.md).
46
46
  - **ux-reviewer** (agent): captures at 1440 and 390 px, findings grounded in a
@@ -48,7 +48,7 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
48
48
  `raf add --parent` sub-tasks, and a one-line verdict for `raf ux`. It never
49
49
  edits code. Read-only for real: its `tools:` list is `Read, Grep, Glob, Bash` and the
50
50
  Playwright tools (`mcp__playwright`, and `mcp__plugin_playwright_playwright` when the
51
- browser comes from the Playwright plugin) — no `Edit`, no `Write`. Outside a wave it gives its captures a relative file name only, which the Playwright MCP writes under `.playwright-mcp/` (the same rule as the wave's briefs); `qa-reviewer` follows it too.
51
+ 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.
52
52
  - **code-reviewer** (agent): any stack; given a repository and a lot id, it reads
53
53
  the diff itself from the commits that cite the lot — not the author's summary —
54
54
  and the project's CLAUDE.md, when there is one, for its conventions; findings grounded in a
@@ -58,6 +58,13 @@ repository with `cadence skills install` (to `.claude/skills/cadence-*` and
58
58
  verdict for `raf review`. It takes the lot's commits from `raf commits`, never
59
59
  runs a build whose output is used live, and never edits code. A README or usage
60
60
  documentation that does not follow the lot's change is a *major* finding.
61
+ - **doc-keeper** (agent, L306): any stack; given a repository and a lot id, it reads the diff of the lot and the
62
+ project's map of documents (`docs.sync` of `cadence.yaml`, README, `docs/`, CHANGELOG, news entries), says which
63
+ documents must change and where, then updates them in a writing session (explicit paths, `docs(<lot>): …`) or
64
+ only lists them in a read-only one. `cadence orchestrate` calls it before the review when `docs.sync` reports a
65
+ document behind the lot; the lead calls it by hand. A document outside the repository only when the project's
66
+ `cadence.yaml` names it (`docs.articles`): the agent then proposes a lot for that repository's plan and never writes it
67
+ ([news](news.md#the-librarian-doc-keeper)).
61
68
  - **qa-reviewer** (agent): any web app (read-only like `ux-reviewer`: same tool list, no `Edit`, no `Write`); given a repository and a base URL (and
62
69
  optionally a lot id, to walk only the pages it touched — those that call the
63
70
  changed endpoints, those of the screens it changed, the pages that consume the services the lot changed,
package/docs/spec.md CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  `cadence spec check` prints the same summary line for each kind (`N cas, N règles (N hypothèses), N contrats, N données` for a back-end sheet) and keeps its exit codes: 0 valid, 1 violations, 2 usage.
28
28
 
29
- `cadence spec reseal <lot> <test> --reason texte` is the lead's way to correct one sealed acceptance test of the lot in progress that contradicts its sheet (unchanged, or amended by commits of the lot, L217; of a delivered lot, also by commits of another delivered lot, L229) (of a delivered lot: add `--porteur <lot in progress>`), see [The accept step](orchestrate.md#the-accept-step-red-acceptance-tests-and-their-lock).
29
+ `cadence spec reseal <lot> <test> --reason texte` is the lead's way to correct one sealed acceptance test of the lot in progress that contradicts its sheet (unchanged, or amended by commits of the lot, L217; of a delivered lot, also by commits of another delivered lot, L229) (of a delivered lot: add `--porteur <lot in progress>`); refused while a wave of the repository is running, or a wave that is not `done` keeps open (question waiting, suspended, queued) any lot of this repository still `doing` in its plan, the resealed lot or another (L238: one error, also when a live wave holds the lock, names each wave (live ones to wait for), its launch folder and, for each stopped wave, the single `cd <folder> && cadence orchestrate --resume <wave> --drop <project>:<lot> --drop …` that closes all its open lots of the repository, the queued ones that are not `doing` in the plan included (L239); a lot `done` or absent in the plan never refuses), see [The accept step](orchestrate.md#the-accept-step-red-acceptance-tests-and-their-lock).
30
30
 
31
31
  `cadence spec qa <fiche.yaml>` derives from a valid sheet the section of `docs/qa/expectations.md` for its screen and prints it on the standard output
32
32
  (it never writes the file: you paste the section, and the lead commits it like the rest of the plan files). A sheet with violations prints them on the
package/docs/stabilite.md CHANGED
@@ -8,17 +8,17 @@ From 1.0.0 cadence follows [semantic versioning](https://semver.org): what is li
8
8
  **Guaranteed**
9
9
 
10
10
  - **Commands and options** of `raf` and `cadence` (`cadence raf …` is `raf …`): `init`, `add`, `start`, `done`, `drop`,
11
- `note`, `did`, `public`, `screen`, `spec`, `ux`, `review`, `commits`, `show`, `list`, `now`, `ignore`, `check`, `gantt`, `hook`;
11
+ `note`, `did`, `public`, `screen`, `spec`, `visible`, `ux`, `review`, `commits`, `show`, `list`, `now`, `ignore`, `check`, `gantt`, `hook`;
12
12
  `cadence news`, `session` (`start`, `close`, `next`, `context`), `lead tour`, `corrections`, `deliver`, `verify`, `orchestrate`,
13
13
  `skills`, `spec` (`cadence spec <lot> on|off` is `raf spec`; `check`, `qa`: exit **0** when the sheet is valid and the section is printed, **1** with violations, **2** on a missing or unreadable file or a wrong usage);
14
14
  their options and exit codes as the pages [raf](raf.md), [orchestrate](orchestrate.md), [deliver](deliver.md), [spec](spec.md), [news](news.md), [session](session.md), [lead](lead.md), [corrections](corrections.md) and [skills](skills.md) document them. A new command or option is a minor change.
15
15
  - **The keys of `cadence.yaml`**: `plan`, `news`, `hook`, `session`, `deliver`, `orchestrate`, `docs`, `qa`, `priority`, `depth`, `lead` (`lead.ux`)
16
- and the keys each of them takes. A key is not renamed nor removed, nor does its meaning change; a new key is a minor change.
17
- - **The plan file** (`docs/plan/raf.yaml`): `project`, `prefix`, `since`, `ignore`, `lots` (with their
16
+ and the keys each of them takes (`docs`: `sync`, `since`, `articles`; `docs.articles` names both the article refreshed after a delivery and the outside documents the `doc-keeper` agent proposes lots for). A key is not renamed nor removed, nor does its meaning change; a new key is a minor change.
17
+ - **The plan file** (`.cadence/raf.yaml`, or the legacy `docs/plan/raf.yaml`): `project`, `prefix`, `since`, `ignore`, `lots` (with their
18
18
  fields, `tasks`, `notes`, `repos`) and `acknowledged`, and the plan formats `plan:` in `cadence.yaml` can read.
19
19
  The `version` key is kept in the file but ignored (`src/plan.ts` does not read it): it is not a schema version.
20
20
  - **The names of the skills** (`session-start`, `session-close`, `deliver`, `lead`) **and of the agents**
21
- (`ux-reviewer`, `code-reviewer`, `qa-reviewer`).
21
+ (`ux-reviewer`, `code-reviewer`, `qa-reviewer`, `doc-keeper`).
22
22
 
23
23
  **Not guaranteed** (may change in any release)
24
24
 
package/docs/whats-new.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  The long form of the README's « What's new » (the README keeps two lines per version). The full history, with the date of each version, is in [CHANGELOG.md](../CHANGELOG.md).
6
6
 
7
+ **1.9.0**: the default plan is `.cadence/raf.yaml`, a hidden folder git versions and `docs/` never publishes (L246), and `raf migrate [--dry-run]` moves a legacy plan and the readers of the old path (L284, L305); `raf title`, `raf visible`, `uses:` resources shown by `lead tour` and the lot `goal:` (L244, L273, L251–L253, L258). `cadence ref add|measure|check|corpus|eval` registers reference documents outside the repository, measures a rendered zone, validates the inventory schema and scores a reading against a known truth (L261–L264). In `cadence orchestrate`: the `doc-keeper` agent and `docs.articles` (L306, L308), the mechanical `orchestrate.testGate` before the review (L287), `precheck: local` on every lot with no commit (L296), a write-step time limit proportioned to the lot (L298), sessions that no longer hold their slot after their report (L285), the real weekly-limit message recognised (L286), and an experimental worktree mode with `--resume --integrate|--replay` and `--remove-worktree` (L279, L299–L304). `cadence lead hold|release` counts the lead's hand-started sessions, an expired one is flagged by `lead tour`, `--status` and the `cadence-hud` band (0.5.0; L255, L276, L278). `cadence deliver --sha` is accepted while a wave runs (L277), `CADENCE_DEPLOYED_FILE` names the deployed sha (L277, L281) and `deliveries-failed.log` keeps the class and last lines of a red delivery, secrets masked (L282, L289, L294). `cadence perf` breaks red deliveries down and counts the `review-small` UX (L282, L283). Details: [CHANGELOG.md](../CHANGELOG.md).
8
+
9
+ **1.8.0**: `cadence perf [folder] [--since date] [--json]` is the permanent dashboard of the workflow, without a model: the last 7 days against the 7 before, read in `.cadence/runs`, with a `⚠` on a trend or a bottleneck (L225); `perf.prices` prices the lots in euros (L226); `cadence deliver` writes `deliveries-failed.log` and `cadence lead tour` opens with a perf header (L227). The measure of the back-end spec step on 10 lots is in `docs/mesures` (L190). The release was blocked by a test that required an entry under `[Unreleased]` (L235). Details: [docs/perf.md](perf.md).
10
+
7
11
  **1.7.0**: `cadence orchestrate` no longer replays the `accept` step of a lot whose acceptation is green and sealed, e.g. after a `cadence spec reseal`, and the `accept` brief and the `acceptance-writer` agent may no longer rewrite a green oracle (L233); `cadence orchestrate --resume … --dry-run` is refused before anything is written instead of resuming the wave for real (L228); `cadence spec reseal <delivered lot> … --porteur` accepts a sheet amended by commits of another delivered lot, and its refusal names the commit and the way out (L229); the README is a short home page (about 5 kB instead of 208 kB in the package), the rest moved unchanged to `docs/` pages checked by `scripts/check-links.mjs`, and a commit that touches only the version fields, the CHANGELOG, « What's new » and `docs/whats-new.md` is a version commit for `raf check` and `raf done` (L232).
8
12
 
9
13
  **1.6.0**: the mockup review sweeps everything in its first report and runs up to `orchestrate.maquetteReviews` times (new key, default 3 instead of 2), and a review may mark a finding `inchange` (L230); the acceptance lock opens its exception only through a commit of the lot in progress that amends the sheet after the red commit, and names the first offending commit (L231).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylad/cadence",
3
- "version": "1.7.0",
3
+ "version": "1.9.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",
@@ -22,10 +22,12 @@
22
22
  "docs/deliver.md",
23
23
  "docs/spec.md",
24
24
  "docs/lead.md",
25
+ "docs/ref.md",
25
26
  "docs/stabilite.md",
26
27
  "docs/news.md",
27
28
  "docs/session.md",
28
29
  "docs/corrections.md",
30
+ "docs/perf.md",
29
31
  "docs/skills.md",
30
32
  "docs/cadence-hud.md",
31
33
  "docs/releasing.md",
@@ -27,6 +27,11 @@ deliver:
27
27
  script: ./scripts/ship.sh "$CADENCE_SHORT"
28
28
  ```
29
29
 
30
+ A script that deploys a commit other than `$CADENCE_SHA` (it committed a Nouveautés timestamp itself)
31
+ writes that sha into the file named by `$CADENCE_DEPLOYED_FILE`: cadence logs it as delivered and never guesses
32
+ (a content that is not a hexadecimal commit sha — `HEAD` or a branch name included — fails the delivery; empty during a wave, the log keeps `$CADENCE_SHA` and a warning
33
+ says the head moved).
34
+
30
35
  Its arguments go after `--`: `cadence deliver -- api frontend` (`--sha <rev>` to deliver a pushed
31
36
  commit other than HEAD). Ask the human (or the project's own
32
37
  delivery skill) which arguments this delivery needs; never guess them.
@@ -36,7 +41,7 @@ delivery skill) which arguments this delivery needs; never guess them.
36
41
  Every `cadence` / `raf` command works on the git repository of the current directory. When the
37
42
  session runs from a parent folder that holds several projects (not itself a repository), run each
38
43
  command inside the project concerned: `cd <project> && cadence …`. The projects are the sub-folders
39
- that contain `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key.
44
+ that contain `.cadence/raf.yaml` or `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key.
40
45
 
41
46
  Deliver one project at a time: the one the human names, or ask.
42
47
 
@@ -7,7 +7,7 @@ description: Lead several cadence projects from a parent folder without loading
7
7
 
8
8
  The lead session stays small: it reads summaries, decides with the human, delegates, checks and
9
9
  delivers. Project files are changed by subagents, each with its own context; the facts of the tour come from `cadence lead tour`, a program. The projects
10
- are the sub-folders of the current directory that contain `docs/plan/raf.yaml`, or a `cadence.yaml`
10
+ are the sub-folders of the current directory that contain `.cadence/raf.yaml` or `docs/plan/raf.yaml`, or a `cadence.yaml`
11
11
  with a `plan:` key. A project whose `cadence.yaml` maps the fields of a plan kept by its own tool is
12
12
  **read-only** for `raf`: it takes part in the tour, and its plan is changed with the project's own
13
13
  commands (its CLAUDE.md names them), never with `raf start|done|note|ux|review`.
@@ -21,7 +21,16 @@ commands (its CLAUDE.md names them), never with `raf start|done|note|ux|review`.
21
21
  subagent, check `cadence orchestrate --status`, and while a wave runs, start none beyond the free slots. Read the cap and the
22
22
  free slots, do not compute them: `cadence orchestrate --status` and `cadence lead tour` print
23
23
  `créneaux libres : N sur M` (M is the configured cap; the registry subtracts, for each live wave, min(cap,
24
- repositories it still holds) from the M slots; no line from `lead tour` means no live wave, so all M are free).
24
+ repositories it still holds) and one slot per lead session (below) from the M slots; no line from `lead tour` means no live wave and no lead session, so all M are free).
25
+ **A session you start by hand counts too** (a fix subagent, a review, a QA pass launched outside a wave): the registry does not
26
+ see it, so declare it. Right before starting it, `cadence lead hold <label> [--ttl minutes]` (60 minutes by default,
27
+ the label names the session: `correctif-L12`); as soon as it comes back, `cadence lead release <label>`. While it is held, both
28
+ commands print `sessions du lead : N (labels)` and subtract it from `créneaux libres`; the `--ttl` is the maximum duration you
29
+ declare for the session (size it to the slot you have, not to the optimistic case). When it runs out the hold stops counting, but
30
+ nothing stops the session: `lead tour` and `orchestrate --status` print `⚠ session du lead échue : <label> (…)` until you
31
+ release it, and the band shows the same ⚠ row. **At each ⚠, decide at once**: either it is still worth the slot, so renew it
32
+ with `cadence lead hold <label> --ttl <minutes>` and say why in one sentence, or stop it (`TaskStop` or the agent's end) and
33
+ `cadence lead release <label>`. A fix that ran 5 h on a 3 h slot (23-10) is the case this catches. Holding does not block anything and never raises the cap.
25
34
  Never raise `--max-sessions` or `CADENCE_MAX_SESSIONS` yourself to speed a wave up: raising the cap is a decision of
26
35
  the human (a trial at 3 was decided on 2026-10-12); if the slots are the bottleneck, say so and let them decide.
27
36
  - **Never two subagents in the same repository at the same time**, and the lead does not commit in a
@@ -34,12 +43,19 @@ commands (its CLAUDE.md names them), never with `raf start|done|note|ux|review`.
34
43
  ## 1. Tour of the projects
35
44
 
36
45
  Run `cadence lead tour` from the parent folder (one command, no subagent, no model: it reads the same
37
- facts as `cadence session start` for every project). It prints one line per project — lots in progress
38
- (`silencieux Nj` when idle for more than three days), drift, notes from the last close, the next ready lot,
46
+ facts as `cadence session start` for every project). It prints one line per project — lots in progress with their commit count
47
+ (`0 commit` included; `silencieux Nj` when idle for more than three days), drift, notes from the last close, the next ready lot,
39
48
  repository state — and exits 0 even when a project is in error (its line says so). `--json` gives the same
40
49
  content. Read the lines yourself; open a project's own `cadence session start` only for the one you need to
41
50
  look at closer.
42
51
 
52
+ The tour opens with the **perf header**: one line per `⚠` of `cadence perf` (speed and quality side by side), nothing
53
+ when everything is stable. Read it **before proposing the three items** and relay each `⚠` in one line in your report.
54
+ Open an **improvement lot** only on a trend confirmed on two windows (a `⚠` of trend: two windows compared, enough lots, window
55
+ not truncated) or on a net bottleneck (`⚠ goulot`): at most one improvement lot per tour, in addition to the three, never in
56
+ their place, and the human decides. Never open a lot for a micro-optimisation or for the variation of a single window.
57
+ Without a `⚠`, the header is empty and you say nothing about perf.
58
+
43
59
  Then report to the human in a compact table — one row per project — and propose **three** items
44
60
  across projects, each with project, lot id and one sentence of justification: finish what is in
45
61
  progress, then drift that blocks a delivery, then ready quick wins. **Stop and wait for the priority.**
@@ -52,7 +68,7 @@ one fresh short session per step; the wave runs from a frozen copy of the cadenc
52
68
  `cd <project> && raf start <lot>`, then starts from the brief written by `cadence orchestrate --dry-run
53
69
  <project>:<lot>`: it is already rendered (`{{chemin}}`, `{{lot}}`, `{{titre}}`, `{{objectif}}` and `{{news}}`,
54
70
  the News instruction of a `visible` lot, are filled in; it names no wave Playwright directory — a hand delegation has none — so the screenshot goes under the OS temp directory, then into `docs/nouveautes/captures/`). The lead only adjusts the goal to what done looks
55
- like (the human's words) and keeps the rest verbatim.
71
+ like (the human's words) and keeps the rest verbatim. **A stale title is no reason to delegate by hand**: the lot's scope changed since the plan was written, so give the new goal to the program — `--goal "…"` on the launch (one lot per launch), or a `goal:` key on the lots in the plan (every launch, several lots at once): `goal:` in a native plan, and in a plan kept by another tool (read-only, e.g. maritime's `docs/plan/taches.yaml`) the key of theirs that holds it, mapped with `plan.fields: { goal: <their key> }` in `cadence.yaml` (an unmapped key other than `goal` is ignored without a message). It replaces the title as the objective of every brief and shows in the wave table.
56
72
 
57
73
  A lot that adds or changes a screen is `visible`: after the implementation, have the
58
74
  `ux-reviewer` agent review it (give it the URL or the way to run the app) and bring its verdict and
@@ -67,6 +83,8 @@ silent wait, no polling loop — `cadence orchestrate --status` shows where the
67
83
  cadence orchestrate <project>:<lot> <project>:<lot>@haiku … --wave <id> [--budget 2M]
68
84
  ```
69
85
 
86
+ For a single lot whose title is stale, add `--goal "<what done looks like>"` (refused with two lots, `--continue` or `--resume`); for several lots, a `goal:` key per lot in the plan (mapped by `plan.fields.goal` on a plan kept by another tool).
87
+
70
88
  **Make the wave visible to the human.** Name the background task after what it runs — « vague `<id>` :
71
89
  `<project>:<lot>`, … » (its `description`), so the task list says which projects and lots, not just « a task
72
90
  is running » — and pass `--wave <id>` (e.g. `2026-10-08-1030`) so its state folder is known in advance. Right after
@@ -89,6 +107,12 @@ handed back too. `cadence orchestrate --stop-after-current` lets the sessions ru
89
107
  ends `interrupted`, and `--resume` takes it up again. To take a lot out of a wave that is already stopped, use
90
108
  `--resume --drop <project>:<lot>` (the lot is handed back instead of being replayed). Neither kills a session.
91
109
 
110
+ **One worktree per lot is experimental and not enabled** (`orchestrate.worktrees: true`, L279): `cadence orchestrate` refuses it (exit 2, `--dry-run` too) while lots L299, L300 and L301 are not done, unless `CADENCE_EXPERIMENTAL_WORKTREES=1` is set (the mode's tests only) — never set it for a real wave. Its options exist but belong to that mode: `--resume <wave> --integrate <project:lot>` replays a lot handed back at the integration step (the wave's output names that exact command), `--replay <project:lot>` also after a refused plan write, `--remove-worktree <project:lot> [--abandon-commits]` removes a kept worktree and branch, and `cadence orchestrate --status` and `cadence lead tour` list the worktrees kept by waves (nothing to list in the default mode).
111
+
112
+ **Delivering while the wave runs.** A lot that turns **ready** in the journal is not left waiting: once the lead has re-verified it (section 3, point 2) and recorded its verdict (`raf done`), it delivers that lot at once, without waiting for the end of the wave. The re-check runs at that sha in a throwaway `git worktree add --detach <tmp> <sha>` (removed afterwards), never in the shared tree, which holds the lots still running. Take the sha to deliver from `git log`: the last commit of the ready lot such that no commit of a lot that is not ready (running, queued, in question, handed back) sits beneath it — the same « livrable jusqu'à » the final table prints. Push that sha alone (`git push origin <sha>:main`, never HEAD, which holds the code of lots still running), then `cadence deliver --dry-run --sha <sha>` and `cadence deliver --sha <sha>`: `deliver` accepts a `--sha` during a wave of the same repository when none of its commits belongs to a lot that is not ready, and refuses otherwise, naming the lots. Without `--sha` it stays refused until every lot of the repository is ready or handed back. The wave's sessions keep committing in the working tree: `--sha` never reads it. What the log keeps is `<sha>`, unless the delivery script declares another deployed commit in `CADENCE_DEPLOYED_FILE`; if HEAD moved during the delivery without that declaration, `deliver` warns (« la tête a avancé pendant la livraison ») and the log keeps `<sha>`: it is not a failure. This is the measured gain: a ready lot used to wait for the slowest lot of its wave (1,554 lot-minutes between « ready » and the end of the wave over 28 waves).
113
+
114
+ **Free resources.** While a wave is live or a session is held with `cadence lead hold`, `cadence lead tour` and `cadence orchestrate --continue` print, after the free slots line, `prêts, ressources libres : <project>:<lot> <title> (<resources>) · …`: the ready lots whose shared resources (`uses:`) nothing holds. Read it again each time an agent comes back (a session ended, a lot handed back or ready) and each time a lot is blocked (a question, a refusal, a resource taken), **before waiting** for the next transition: a lot listed there can start now, so start it (with the free slots line saying a slot is free) instead of waiting behind the blocked one. The line is a display: nothing is locked, and it never raises the cap — no `créneaux libres`, no new session. No line means no ready lot is free: wait.
115
+
92
116
  `cadence orchestrate --dry-run …` first when a precondition is in doubt. The program, not the lead, runs
93
117
  for each lot a fresh short session per step — implementation (Sonnet), code review
94
118
  (Opus, the `code-reviewer` agent; Sonnet, with no pass on the minor findings, for a lot whose estimate is
@@ -102,7 +126,7 @@ suspended (budget or usage limit; `--resume --budget …` continues).
102
126
  What the orchestrator does **not** do, and stays with the lead: choose the lots (with the human), bring the
103
127
  questions back (`--resume --answer <project>:<lot> "…"`), look at the UX reviewer's captures and record
104
128
  `raf ux` (the orchestrator reports its verdict, it never records it; who decides it is the `lead.ux` setting, section 2c), re-verify (section 3, point 2),
105
- `raf done`, push and deliver. **A locked acceptance test that contradicts the sheet** (the lot is handed back with `verrou des tests d'acceptation`, or the session's question says its own test is wrong, e.g. a regex that rejects a sentence the sheet allows) has a path, and it is the lead's, never a session's: correct the test in the working tree (the sheet is left as it is; if it was amended, **commit it first, citing the lot**: an amended sheet whose every commit cites the lot is accepted, L217, never put it back to the red state), then `cadence spec reseal <lot> <test> --reason "why it contradicts the sheet"` (outside any wave): the program redoes the fingerprint of that one test, writes the reason in `acceptation.json` (`resceaux`) and as a note of the lot, commits both, and the lock keeps everything else. For a **delivered** lot (`vert: true`) whose contract does not change but whose test is unstable, add `--porteur <the lot in progress that carries the fix>`: the commit and the note cite that lot, `vert` stays true (L212). It refuses a lot that is neither in progress nor delivered, a sheet with uncommitted changes or touched by a commit that does not cite the lot, and any other listed test touched, and during a wave of that repository. It is atomic (a refused commit restores `acceptation.json` and the test), with a plan kept read-only by another tool it leaves the plan untouched and says a note is to be added by hand, with an untracked plan or one outside the repository it writes the note but does not commit it (`plan à commiter`: do not add it again), and if the commit of the note is refused the reseal stays done and the error says to commit the plan by hand. Do it **between two sessions**, never inside one: a resealed commit that belongs to a session of the wave is not adopted (still a lock violation), and sessions are forbidden the command. Then continue the lot: `--resume --answer` when it waits on a `question`; a new wave `cadence orchestrate <project>:<lot>` when it was handed back (`--resume` skips finished lots). Minor findings and proposed sub-tasks come back in the table: adding them to
129
+ `raf done`, push and deliver. **A locked acceptance test that contradicts the sheet** (the lot is handed back with `verrou des tests d'acceptation`, or the session's question says its own test is wrong, e.g. a regex that rejects a sentence the sheet allows) has a path, and it is the lead's, never a session's: correct the test in the working tree (the sheet is left as it is; if it was amended, **commit it first, citing the lot**: an amended sheet whose every commit cites the lot is accepted, L217, never put it back to the red state), then `cadence spec reseal <lot> <test> --reason "why it contradicts the sheet"` (outside any wave): the program redoes the fingerprint of that one test, writes the reason in `acceptation.json` (`resceaux`) and as a note of the lot, commits both, and the lock keeps everything else. For a **delivered** lot (`vert: true`) whose contract does not change but whose test is unstable, add `--porteur <the lot in progress that carries the fix>`: the commit and the note cite that lot, `vert` stays true (L212). It refuses a lot that is neither in progress nor delivered, a sheet with uncommitted changes or touched by a commit that does not cite the lot, and any other listed test touched, and during a wave of that repository. It is atomic (a refused commit restores `acceptation.json` and the test), with a plan kept read-only by another tool it leaves the plan untouched and says a note is to be added by hand, with an untracked plan or one outside the repository it writes the note but does not commit it (`plan à commiter`: do not add it again), and if the commit of the note is refused the reseal stays done and the error says to commit the plan by hand. It is also refused while a wave that is not `done` (stopped on a question or the quota, or still alive) keeps open any lot of that repository still `doing` in its plan, **the resealed lot, the porteur or any other lot of the repository** (the lock hands back all of them, L238; a lot `done` or absent in the plan, an old abandoned wave, a lot of another repository with the same id never block): the error is a single message, also when a live wave holds the repository lock: it lists every wave (the live ones and the stopped ones) with its launch folder and its lots and gives, for each stopped wave, ONE command to run as it stands, `cd <launch folder> && cadence orchestrate --resume <wave> --drop <project>:<lot> --drop <project>:<lot>` (a `--drop` per open lot of the repository, queued ones not yet `doing` in the plan included, since `--resume` replays the whole wave; or `--resume <wave> --answer …` to let it go on); a wave still alive is waited for. Close them **first**, reseal afterwards. Do it **between two sessions**, never inside one: a resealed commit that belongs to a session of the wave is not adopted (still a lock violation), and sessions are forbidden the command. Then continue the lot: a new wave `cadence orchestrate <project>:<lot>` when it was handed back (`--resume` skips finished lots). Minor findings and proposed sub-tasks come back in the table: adding them to
106
130
  the plan is the lead's decision. A minor finding becomes a lot of its own only if it describes an observable bug (a wrong output, a crash, a measured regression); otherwise it stays a note of the originating lot, so that a review never feeds the next one. If an orchestrated wave is running in a repository, do not commit there
107
131
  and do not deliver it (`cadence deliver` refuses).
108
132
 
@@ -159,6 +183,7 @@ is always recorded by the lead's own `raf ux`, never by a subagent.
159
183
 
160
184
  0. After an orchestrated wave the review is already done, by a fresh session: read the table, then go to
161
185
  point 2. For a lot delegated by hand:
186
+ 0b. For a lot delegated by hand, `raf check` or the `dérive` column reporting `documentation en retard` is handled **before** the review: hand the lot to the `doc-keeper` agent (a fresh subagent; the absolute path of the project and the lot id; it updates the documents, or only lists them if you say read-only). Read the `docs.articles` key of the project's `cadence.yaml` yourself and put its entries (`repo`, `file`, and the absolute path of that repository, `repo` being relative to the project) in the brief, with read access to that repository for the subagent: the agent examines a document outside the repository only if the brief names it, and with no entry (the key is absent by default) you name none. A proposed lot it returns for a document outside the repository (`docs.articles`) is yours to decide: it never writes to that plan.
162
187
  1. The `code-reviewer` agent, as a fresh subagent (most capable model), reviews the lot. Give it
163
188
  the absolute path of the project and the lot id, **not** the author's report: it reads the diff
164
189
  itself from the commits that cite the lot, runs the checks, and returns real defects only,
@@ -10,7 +10,7 @@ description: Close a work session on a repository that uses cadence — plan hyg
10
10
  Every `cadence` / `raf` command works on the git repository of the current directory. When the
11
11
  session runs from a parent folder that holds several projects (not itself a repository), run
12
12
  `cadence session close --all` there (`--depth 2` for projects one folder deeper): one "## project"
13
- section per sub-folder that contains `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key.
13
+ section per sub-folder that contains `.cadence/raf.yaml` or `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key.
14
14
  Other commands (`raf …`, `cadence deliver`) still run inside the project concerned: `cd <project> && cadence …`.
15
15
 
16
16
  With no project named, run `cadence session close --all` from the parent folder, or `cadence session close` in each project touched during the session
@@ -5,7 +5,7 @@ description: Start a work session on a repository that uses cadence — gather t
5
5
 
6
6
  # Session start — resume without reciting
7
7
 
8
- The repository already carries the state: the plan (`docs/plan/raf.yaml`, or the file named by `plan:` in `cadence.yaml`), the history, the notes left at
8
+ The repository already carries the state: the plan (`.cadence/raf.yaml`, or the legacy `docs/plan/raf.yaml`, or the file named by `plan:` in `cadence.yaml`), the history, the notes left at
9
9
  the last close. The only thing missing is the human's priority for today. This skill gathers the facts,
10
10
  proposes, and stops.
11
11
 
@@ -14,7 +14,7 @@ proposes, and stops.
14
14
  Every `cadence` / `raf` command works on the git repository of the current directory. When the
15
15
  session runs from a parent folder that holds several projects (not itself a repository), run
16
16
  `cadence session start --all` there (`--depth 2` for projects one folder deeper): one "## project"
17
- section per sub-folder that contains `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key.
17
+ section per sub-folder that contains `.cadence/raf.yaml` or `docs/plan/raf.yaml`, or a `cadence.yaml` with a `plan:` key.
18
18
  Other commands (`raf …`, `cadence deliver`) still run inside the project concerned: `cd <project> && cadence …`.
19
19
 
20
20
  With no project named, run `cadence session start --all` from the parent folder (or `cadence session start` in **each** project that has a plan) and report