@sylad/cadence 1.8.0 → 1.10.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 (105) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +27 -54
  4. package/agents/code-reviewer.md +2 -2
  5. package/agents/doc-keeper.md +19 -0
  6. package/agents/precheck-reader.md +1 -1
  7. package/agents/qa-reviewer.md +2 -2
  8. package/agents/reference-analyst.md +25 -0
  9. package/agents/reference-verifier.md +19 -0
  10. package/agents/spec-writer.md +1 -1
  11. package/agents/ux-reviewer.md +1 -1
  12. package/bin/cadence.js +9 -1
  13. package/dist/audit.js +16 -4
  14. package/dist/check.js +24 -2
  15. package/dist/cli.js +258 -30
  16. package/dist/config.js +101 -5
  17. package/dist/corrections.js +24 -2
  18. package/dist/deliver.js +125 -37
  19. package/dist/docsync.js +19 -3
  20. package/dist/git.js +16 -3
  21. package/dist/lead.js +45 -12
  22. package/dist/migrate.js +166 -0
  23. package/dist/orchestrate/briefs.js +46 -6
  24. package/dist/orchestrate/changelog-merge.js +94 -0
  25. package/dist/orchestrate/command.js +358 -41
  26. package/dist/orchestrate/cycle.js +922 -285
  27. package/dist/orchestrate/guard.js +2 -2
  28. package/dist/orchestrate/integrate.js +129 -0
  29. package/dist/orchestrate/launch.js +48 -6
  30. package/dist/orchestrate/mutex.js +30 -0
  31. package/dist/orchestrate/no-test.js +41 -0
  32. package/dist/orchestrate/pool.js +74 -36
  33. package/dist/orchestrate/registry.js +146 -14
  34. package/dist/orchestrate/result.js +1 -1
  35. package/dist/orchestrate/snapshot.js +54 -1
  36. package/dist/orchestrate/spec.js +89 -5
  37. package/dist/orchestrate/state.js +56 -15
  38. package/dist/orchestrate/table.js +74 -3
  39. package/dist/orchestrate/timeouts.js +40 -0
  40. package/dist/orchestrate/worktree.js +368 -0
  41. package/dist/perf.js +188 -25
  42. package/dist/plan.js +112 -7
  43. package/dist/proc.js +6 -0
  44. package/dist/ready-free.js +109 -0
  45. package/dist/ref/add.js +129 -0
  46. package/dist/ref/analyse.js +220 -0
  47. package/dist/ref/check.js +338 -0
  48. package/dist/ref/command.js +92 -0
  49. package/dist/ref/corpus-render.js +83 -0
  50. package/dist/ref/corpus.js +201 -0
  51. package/dist/ref/diff.js +262 -0
  52. package/dist/ref/evaluate.js +241 -0
  53. package/dist/ref/figures.js +252 -0
  54. package/dist/ref/measure.js +330 -0
  55. package/dist/ref/pdf.js +116 -0
  56. package/dist/ref/png.js +125 -0
  57. package/dist/ref/regard.js +102 -0
  58. package/dist/ref/session.js +34 -0
  59. package/dist/ref/store.js +40 -0
  60. package/dist/ref/tools.js +51 -0
  61. package/dist/resources.js +51 -0
  62. package/dist/session.js +5 -2
  63. package/dist/state.js +75 -7
  64. package/dist/testgate.js +15 -0
  65. package/docs/cadence-hud.md +12 -0
  66. package/docs/cycle-vague-dark.svg +1 -1
  67. package/docs/cycle-vague-light.svg +1 -1
  68. package/docs/deliver.md +76 -5
  69. package/docs/fonctionnement-wave-dark.svg +1 -1
  70. package/docs/fonctionnement-wave-light.svg +1 -1
  71. package/docs/fonctionnement.md +6 -6
  72. package/docs/lead.md +40 -13
  73. package/docs/news.md +30 -1
  74. package/docs/orchestrate.md +275 -33
  75. package/docs/overview.md +65 -1
  76. package/docs/perf.md +14 -8
  77. package/docs/raf.md +90 -11
  78. package/docs/ref.md +415 -0
  79. package/docs/releasing.md +3 -3
  80. package/docs/session.md +2 -2
  81. package/docs/skills.md +21 -2
  82. package/docs/spec.md +1 -1
  83. package/docs/stabilite.md +4 -4
  84. package/docs/whats-new.md +30 -1
  85. package/package.json +2 -1
  86. package/skills/deliver/SKILL.md +6 -1
  87. package/skills/lead/SKILL.md +28 -10
  88. package/skills/session-close/SKILL.md +1 -1
  89. package/skills/session-start/SKILL.md +2 -2
  90. package/templates/inventaire.json +96 -0
  91. package/templates/orchestrate/accept-back.md +1 -0
  92. package/templates/orchestrate/accept.md +1 -0
  93. package/templates/orchestrate/fiche.md +1 -0
  94. package/templates/orchestrate/fix-docs.md +18 -0
  95. package/templates/orchestrate/fix-minors.md +2 -1
  96. package/templates/orchestrate/fix.md +5 -2
  97. package/templates/orchestrate/implement.md +7 -4
  98. package/templates/orchestrate/maquette.md +2 -1
  99. package/templates/orchestrate/precheck.md +2 -1
  100. package/templates/orchestrate/review-recheck.md +2 -0
  101. package/templates/orchestrate/review-small.md +5 -1
  102. package/templates/orchestrate/review.md +2 -0
  103. package/templates/orchestrate/spec-back.md +2 -1
  104. package/templates/orchestrate/spec.md +4 -3
  105. package/templates/orchestrate/ux.md +3 -1
package/docs/ref.md ADDED
@@ -0,0 +1,415 @@
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), **`cadence ref eval`** (lot 4 of 10, with the synthetic corpus `cadence ref corpus`) **`cadence ref analyse`** (lot 5 of 10: the `reference-analyst` agent, one single reading) and **`cadence ref diff`** with the double reading (lot 6 of 10). **The series stops at lot 6**: the stopping point of the double reading (hypothesis H1) was not met, so lots 7 to 10 are dropped and the double reading stays an option, `--double` ([measurement and conclusion](mesures/ref-double-lecture-2026-10-25.md)).
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`), and ranges are expanded figure by figure (`Figures 2–4` with an en dash glued to its bounds — a dash set off by spaces or an em dash, `Figures 2 and 3 — 10 stations`, `Figures 2 and 3—10 stations`, is prose and not a range —, `Figures 01–03` keeping the leading zeros of its bounds, `Figures 2, 4 to 6`, `Figures 1.1 to 1.3`, `Figures 1.B.1 to 1.B.3`, `Figures 3-1 to 3-3`, `Tables A1 to A3`; a range of which the bounds do not share their prefix, that decreases or that spans 50 numbers or more keeps its two bounds, and a bare second bound after a prefixed one (`Tables 4.8–10`, `A3–1`) is not a figure of its own; the figures in the middle of a range are cited when they have a caption, and are never an unresolved link when they have none, so `figures 11–20` leaves only `Figure 11` and `Figure 20` to resolve, and an explicit `Figure 15` further on the same line stays cited or unresolved on its own); a number keeps the letters of an annex numbering (`Figure 1.B.1`, `Table 12.B.3`). A figure preceded by the word `code` or `indicator` (`code figure 3`, `code figures 2 and 3`, `indicator figures 1 to 6, 8 and 9` — a value of a WMO code —, including when the word ends the previous line, or the last line of the previous page before its footer and the header of the next page, and the reference opens the next one) is not a citation and not an unresolved link; neither is an en-dash range (`figures 20–49`) in a sentence that already has a `code figure` (`code figure 17 shall have precedence over figures 20–49`). A header or footer is a line that, digits apart, opens or ends three pages or more. `code table` is not affected. 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
+ A poppler tool that fails while `add` reads the document (`pdftotext`, `pdftoppm`, `pdfimages`, `pdftohtml`: non-zero exit, output too large, disk full)
209
+ is a refusal too, naming the tool, the document and the reason: `ref : pdftotext a échoué sur doc.pdf (disque plein)`. The folder of a new document is not left behind (an existing document keeps its `document.json`; `pdftohtml` is read before anything is written).
210
+
211
+ Exit codes: 0 done, 2 refusal (tool, file, option, slug, folder, tool failure).
212
+
213
+ ## `cadence ref eval <attendu> <lecture> [--json]`
214
+
215
+ Scores a reading against the inventory that is known to be true (the evaluation of the design, §« Mesure »). `attendu` is an
216
+ inventory (`genre: "inventaire"`), `lecture` a reading or an inventory of the same document; both go through the checks of
217
+ `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`
218
+ `<cas>/lecture.json`; a case without a reading counts as read with nothing (recall 0) and is named under `sans lecture`.
219
+
220
+ An element read is matched to an expected one **by type and identity**, never by id or zone (two independent readings number
221
+ 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
222
+ `role` and `contenu`, `etat` by `nom` and the identity of its target, `jeton` by `categorie` and `valeur` (a colour is compared as
223
+ written), `table` by `colonnes`, `gabarit` by `nom`, `schema` by the labels of its nodes, `lien` by `figure`, `silence` by
224
+ `question`, `ecran` (one per document). An expected element is matched once. The matched element is **juste** if it also says the
225
+ same thing: the `role` of a `texte`, the `lignes` of a `table`, the `champs` of a `gabarit` (name, mandatory, format, bounds, in
226
+ order), the `flux` of a `schema`, the `constat` of a `lien` (the interpretation of a `precise` is not compared), the elements
227
+ covered by a `statut`, the `largeur_reference` of an `ecran`.
228
+
229
+ - **rappel** = juste / expected, **précision** = juste / read, both by type and overall;
230
+ - **faux lu** = read elements marked `lu` or `mesure` (« sûrs ») that are not juste, over the sûrs: an invented element, a wrong
231
+ cell, a `lien` that confirms what the figure contradicts. An `incertain` element is never a faux lu (it lowers the precision);
232
+ - **pièges**: an expected element carries `piege: "<name>"`; a piège is found when all its elements are juste (`✓ nom : 1/1`);
233
+ - **tokens par page**: the `usage: { tokens }` of the reading divided by the `pages` of the expected inventory (a root key of the
234
+ expected file, see the model), for the cases that have both; without `usage`, « non mesurés » (`null` in `--json`), never a zero.
235
+ A case whose expected inventory has **no `pages`** is left out of the figure, tokens and pages alike, and the output says so:
236
+ `tokens par page : N cas sans `pages` exclus (names)` (`tokens.exclus` in `--json`).
237
+
238
+ The output is the totals, then one line per type present (in the order of the eleven types), then the pièges (`✓` all found, `✗`
239
+ some missing) and, last, the cases without a reading. This one is a reading of the corpus that confirms a link the figure makes
240
+ 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
241
+ reports `usage`:
242
+
243
+ ```
244
+ $ cadence ref eval tmp/corpus-ref lectures/
245
+ 7 cas · 47 éléments attendus · 46 lus
246
+ rappel : 89.4 % (42/47) · précision : 91.3 % (42/46)
247
+ faux lu : 9.3 % (4/43 sûrs)
248
+ tokens par page : 4200 (4200 sur 1 pages)
249
+ par type :
250
+ ecran rappel 100 % (2/2) · précision 100 % (2/2) · faux lu 0/2
251
+ composant rappel 100 % (5/5) · précision 100 % (5/5) · faux lu 0/5
252
+ etat rappel 50 % (1/2) · précision 50 % (1/2) · faux lu 1/2
253
+ texte rappel 96.7 % (29/30) · précision 100 % (29/29) · faux lu 0/26
254
+ jeton rappel 50 % (1/2) · précision 50 % (1/2) · faux lu 1/2
255
+ table rappel 0 % (0/1) · précision 0 % (0/1) · faux lu 1/1
256
+ gabarit rappel 100 % (1/1) · précision 100 % (1/1) · faux lu 0/1
257
+ lien rappel 66.7 % (2/3) · précision 66.7 % (2/3) · faux lu 1/3
258
+ statut rappel 100 % (1/1) · précision 100 % (1/1) · faux lu 0/1
259
+ pièges :
260
+ ✗ controle-translucide : 0/1
261
+ ✓ figure-contredit-texte : 1/1
262
+ ✓ gabarit-type-bufr : 1/1
263
+ ✓ page-proposal : 1/1
264
+ ✗ scan-bruite : 3/4
265
+ ✗ table-en-image : 0/1
266
+ ✗ texte-ambigu-leve-par-figure : 0/1
267
+ ```
268
+
269
+ Reading it: the `etat` line is the recall of the states (the threshold of the design is 95 % of the components **and** states);
270
+ 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,
271
+ 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
272
+ opacity are exactly the traps of those cases.
273
+
274
+
275
+ A reading whose `document.slug` or `document.sha256` differs from the expected one is refused (never a measure over two versions
276
+ mixed). Exit codes: **0** done, **2** refusal (wrong call, file absent or unreadable, invalid reading, other document, folder with
277
+ no `attendu.json`). `--json` prints one object: `total`, `types`, `pieges`, `tokens`, `cas`.
278
+
279
+ ## `cadence ref diff <lecture-a.json> <lecture-b.json> [--out inventaire.json] [--seuil 30]`
280
+
281
+ Reconciles **two readings of the same document** (design §3, step 3), by program, without any session. Both files must pass `ref check`, be of genre `lecture`, and carry the same `slug` **and** `sha256` (a reconciliation over two
282
+ versions of a document is refused, exit 2). Elements are paired **by type, page, identity and zone overlap**; the identity is what designates an element whatever its id, zone and confidence (the same notion as `ref eval`: the
283
+ text of a `texte`, role and content of a `composant`, figure of a `lien`…), since two independent readings number their ids their own way:
284
+
285
+ | Paired as | When | Result |
286
+ |---|---|---|
287
+ | **concordant** | same identity and same affirmation (the role of a text, the verdict of a link, the cells of a table…) | one element, `mesure` if either reading measured it, else `lu`; a doubt of one reading (`incertain`) stays a doubt, with its cause |
288
+ | **divergent** | same identity but another affirmation, or another identity on the same zone (overlap ≥ 50 %, same type and page: `120 KT` read `120 kt`) | one `incertain` element whose cause holds both versions: `lectures divergentes — libelle : A « 120 KT » ≠ B « 120 kt »`; neither is chosen |
289
+ | **seul** | carried by one reading only | `incertain` (`lu dans la lecture B seulement, troisième regard non fait`) until the third look rules on it (see `ref analyse`) |
290
+ | **silence** | a question, in either reading | gathered (one question once), never sent to a third look and not counted below: a silence is not a reading that someone else contradicts |
291
+
292
+ More than **30 %** of the paired-or-single elements unpaired (silences left out, `--seuil` changes it, in percent) means the two readings do not describe the same document: no reconciliation, the question goes to the lead (exit 1, nothing written).
293
+ The ids of the result are renumbered `<slug>#<type>-<n>` (reading A in its order, then what B alone carries) and the references (`composant.ecran`, `etat.cible`, `statut.couvre`) follow; an element that referred to a discarded one is discarded with it, **except a paired element** (both readings carry it): it holds while one of its two versions points to a kept element, and is then `incertain` with the cause when only one version does; it is discarded only when both versions point to discarded elements (see `ref analyse`, « Reconciliation »).
294
+
295
+ ```
296
+ $ cadence ref diff a.json b.json --out inventaire.json
297
+ concordants : 31 · divergents : 2 · seuls : 3 (A : 1, B : 2) · silences : 9
298
+ non appariés : 8.3 % (seuil 30 %)
299
+ divergent : texte p.2 : libelle : A « 120 KT » ≠ B « 120 kt »
300
+ seul (B) : composant p.1 : bouton|Layers
301
+ ```
302
+
303
+ `--out` writes the reconciled inventory (genre `inventaire`, valid for `ref check`). Exit codes: **0** reconciled, **1** too many unpaired elements, **2** refusal (wrong call, unreadable or invalid file, not a reading, two documents, bad `--seuil`).
304
+
305
+ ## `cadence ref analyse <document.pdf> [--sections 3-5,9] [--slug name] [--model sonnet|opus|haiku] [--model-b sonnet|opus|haiku] [--seuil 30] [--double]`
306
+
307
+ The chain: `add`, then **one reading** (one session of the [`reference-analyst`](../agents/reference-analyst.md) agent, `lectures/a.json`), checked by the program. `--double` adds the **double reading** of the design (§3): two independent readings, `ref diff` by program,
308
+ a **third look** at what only one reading carries, and the reconciled inventory.
309
+
310
+ **The double reading is an option, not the default, because its measurement did not show a gain.** On the synthetic corpus (7 cases, one draw per case) it gives the same recall as the single reading (83 %), a **higher** rate of false « read » (50.7 % against 37.7 % for reading A alone), the same
311
+ three misses and **2.8 times the tokens** (63 250 per page against 22 375), with errors that are correlated between the two readings (hypothesis H1 not met). The design is explicit: a version that raises the rate of false « read » or lowers the recall of the `precise` links does not ship as the default.
312
+ `--model-b` and `--seuil` are accepted with `--double` only (exit 2 otherwise). The figures and their limits: [`docs/mesures/ref-double-lecture-2026-10-25.md`](mesures/ref-double-lecture-2026-10-25.md) (« Lecture des chiffres », conclusion at the end).
313
+
314
+ **Single reading (default).** One fresh session of `reference-analyst`, on `--model` (default `sonnet`), writes `lectures/a.json`; the program reads the file itself (below). The `lectures/b.json` and `inventaire.json` of an earlier `--double` pass are removed first: they would not match the new `a.json`.
315
+
316
+ **Two readings (`--double`).** Two fresh sessions of the [`reference-analyst`](../agents/reference-analyst.md) agent (`claude -p`, the review effort and the permission mode of `orchestrate` in `cadence.yaml`, a working directory that is the
317
+ document folder) run together: reading **A** on `--model` (default `sonnet`), reading **B** on `--model-b` (default `opus`: another model decorrelates the errors, hypothesis H1). Each agent looks at every rendered page whole,
318
+ then every zone at full resolution, measures with `cadence ref measure`, copies every text verbatim, links every figure to the passages that cite it (`confirme`, `precise`, `contredit`), writes what the document does not show as a `silence`,
319
+ marks `incertain` (with its cause) what it cannot read for sure, and writes **`<references.dir>/<slug>/lectures/a.json`** (resp. `b.json`): a reading (`genre: "lecture"`) in the format of
320
+ [`templates/inventaire.json`](../templates/inventaire.json), validated by `cadence ref check` before it hands back. The two briefs are the same text, the file to write apart; neither names the other reading, an expected inventory or a corpus,
321
+ and the agent is told never to open another file of `lectures/`. The sessions need `claude` on the `PATH` (or `CADENCE_CLAUDE_BIN`) and the system tools of `ref add`.
322
+
323
+ **The program does not trust the reports.** For each reading it reads the file itself: it must exist (the ones of an earlier run, `a.json`, `b.json` and `inventaire.json`, are removed before the sessions start), pass `ref check`, be a `lecture`
324
+ and carry the `slug` and `sha256` of the document that was read, never another (a reading of two versions mixed is refused). It then writes **`usage`** into each reading, `{ tokens, modele, session }`, `tokens` being the input, cache-write
325
+ and output tokens of the session (the count of the orchestrate budget; cache reads are left out), which the agent never writes.
326
+
327
+ **Reconciliation.** [`ref diff`](#cadence-ref-diff-lecture-ajson-lecture-bjson---out-inventairejson---seuil-30) pairs the two readings. More than 30 % of unpaired elements (`--seuil`, in percent): no reconciliation, exit 1, the question to the lead
328
+ (the two readings stay in `lectures/`, `cadence ref diff` compares them again). Otherwise:
329
+
330
+ - **concordant** elements become one `lu` (or `mesure`) element; **divergent** ones are `incertain` with both versions in the cause, **never chosen**;
331
+ - the elements **only one reading carries** go to the **third look**: one session of the [`reference-verifier`](../agents/reference-verifier.md) agent (read-only: `Read` and `Bash` to measure; same model as reading A) that sees **only those elements**, each with the
332
+ rendered page and a **crop** of its zone (`crops/regard-<n>.png`, the zone and 24 px around it; no crop when the page is not rendered, the brief then says so), and answers `confirme` or `ecarte` with a reason. **Confirmed**: the element keeps its confidence
333
+ (`mesure` stays `mesure`, `lu` stays `lu`, a doubt stays a doubt); **discarded**: it disappears, with what only held through it (a state of a discarded component, a badge that covers nothing any more). A paired element (both readings carry it) holds as long as **one** of its two versions points to kept elements: when only one version pointed to a discarded element (a reading that cut a second screen wrongly and attached its components to it), the element stays, attached as the other version has it, and is `incertain` with the cause (`rattachement : la lecture A le rattachait à un élément écarté…`); it disappears only when both versions point to discarded elements. A verdict without a reason, an unknown verdict or a number that is not
334
+ in the list counts as **no answer**: the element stays `incertain` (`troisième regard sans réponse`), never a `lu` by default. No element carried by one reading only: no third look, no session;
335
+ - the **silences** of both readings are gathered, once per question, and never sent to a third look.
336
+
337
+ The result is **`<references.dir>/<slug>/inventaire.json`**: `genre: "inventaire"`, valid for `ref check`, ids renumbered, `usage` = `{ tokens, lectures: { a, b, regard } }` (the total of the three sessions and each one's tokens and model, what `ref eval` reads for the tokens per page)
338
+ and `reconciliation` = the counts, the unpaired ratio and, for each element the third look ruled on, `{ lecture, id (in its reading), verdict, raison }`.
339
+
340
+ A session cut by a signal with nothing written is started again once in a fresh session (like a review); cut twice, it is a failure (exit 2). Without `--double` the single reading is written to `lectures/a.json` and checked as above; `--model-b` and `--seuil` are refused then (exit 2).
341
+
342
+ **Declared ceiling: 20 pages per reading** (`LECTURE_PAGES_MAX`, design §5 « Taille »). With `--sections`, the pages read are those of `--sections` plus the pages of the figures they cite: the pages an
343
+ earlier `add` of the same document rendered stay on disk but are **not** read, so choosing fewer pages with `--sections` works under the same slug. Without `--sections`, every rendered page is read (the whole document when it
344
+ has 30 pages or fewer, else the pages an earlier `add` rendered). More than 20 pages to read is refused before any session (exit 2, the count and the command `cadence ref analyse <document> --slug <slug> --sections 3-12`), and so is a large document with no
345
+ `--sections` and no page rendered. Splitting into several pairs of readings is not done here.
346
+
347
+ A session cut by a signal and started again is counted in full: its tokens (read back from its Claude Code journal, as `orchestrate` does) are added to those of the fresh one, and the half-written reading of the cut session is removed before the fresh one starts.
348
+
349
+ ```
350
+ $ cadence ref analyse norme.pdf --sections 4-9 --double
351
+ norme : norme.pdf
352
+ pages : 120
353
+ rendues : 8 (4, 5, 6, 7, 8, 9, 33, 34)
354
+ …
355
+ norme : double lecture (A sonnet, B opus) — 8 pages lues (plafond d'une lecture : 20)
356
+ lectures : ~/.local/share/cadence/references/norme/lectures/a.json, ~/.local/share/cadence/references/norme/lectures/b.json
357
+ réconciliation : concordants 31 · divergents 2 · seuls 3 · silences 9 — non appariés 8.3 % (seuil 30 %)
358
+ troisième regard (sonnet) : 2 confirmés, 1 écarté, 0 sans réponse
359
+ inventaire : ~/.local/share/cadence/references/norme/inventaire.json
360
+ éléments : 44 (mesure : 6, lu : 27, incertain : 11)
361
+ tokens : 520000 (65000 par page, 8 pages lues) — A : 190000, B : 250000, troisième regard : 80000
362
+ question : Que veut dire « Figure 1 » ?
363
+ ```
364
+
365
+ The last lines are one `question :` per distinct question of the agents (a `contredit`, an `incertain`). Exit codes: **0** done, **1** a refused reading (no file, `ref check` deviations, wrong genre, wrong document: one
366
+ `✗ lecture A : <reason>` line per deviation then `N écart(s)` on the standard output, the file is left as written, no `usage` is added) or too many unpaired elements, or the third look discarding every element (all of them were carried by one reading only: nothing is left to write, no inventory, the readings stay, a question to the lead), **2** refusal (unknown `--model` or `--model-b`, no page or more than 20 to read, tool or file as for `add`, a session that
367
+ failed: `la session de lecture a échoué : <cause>`), **3** quota reached (message on the error output, no inventory; the readings already written stay; run it again later). The tokens per page of the output divide by the pages **read**; `ref eval` divides by the `pages` of the expected inventory,
368
+ the same number for a corpus case read whole. Without `--double` the output is the one of lot 5: `<slug> : lecture unique (<model>) — …`, `lecture : <file>`, `éléments : …`, `tokens : …`.
369
+
370
+ ### Reference measure on the corpus
371
+
372
+ `docs/mesures/ref-lecture-unique-2026-10-24.md` states what the single reading is measured on and with which commands (recall, false « read », tokens per page against the corpus of `ref corpus`, the declared ceiling), and what was measured.
373
+ The double reading is measured the same way, `ref eval` on `inventaire.json` instead of `lectures/a.json`, and compared with that baseline: `docs/mesures/ref-double-lecture-2026-10-25.md` (hypothesis H1, the stopping point of lot 6: not met, which is why `--double` is an option).
374
+
375
+ ## `cadence ref corpus <dossier> [--sans-rendu]`
376
+
377
+ Writes the **synthetic corpus with a known truth** that `ref eval` scores against (design §« Évaluation »): seven small documents,
378
+ one per trap taken from a real defect, each with the inventory that is true of what is drawn.
379
+
380
+ | Case (= trap) | What it draws | Expected `piege` element |
381
+ |---|---|---|
382
+ | `texte-ambigu-leve-par-figure` | « written beside the arrow » (head or tail?), the figure puts `120 KT` over the tail | `lien` `precise` |
383
+ | `figure-contredit-texte` | text says 80 kt, the figure draws the threshold at 100 kt | `lien` `contredit` |
384
+ | `table-en-image` | a code table rasterised at render time: its cells are not in the PDF text layer | `table` |
385
+ | `gabarit-type-bufr` | BUFR section 1: ordered fields, mandatory or optional, format, bounds | `gabarit` |
386
+ | `controle-translucide` | a white button at 25 % opacity on a striped gradient, next to an opaque one | `jeton` `opacite` |
387
+ | `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` |
388
+ | `scan-bruite` | a station sheet rasterised, grainy and blurred: the PDF has no text layer, expected texts are `ocr` | `texte` (`ocr`) |
389
+
390
+ Each case folder holds `document.html` (one 8.5×11 in `section.page` per page, deterministic), `attendu.json` (an inventory that
391
+ 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,
392
+ unless `--sans-rendu`, `document.pdf` and `page-<n>.png` (1700×2200, 200 dpi) written by **Playwright** (Chromium). Texts of a table,
393
+ a template or a schema belong to it and are not listed as `texte`; labels of a figure are.
394
+
395
+ **One convention for the labels** (what a faithful reading must find, so that `ref eval` does not count it as a faux lu): every
396
+ visible text outside a table or a template is an expected `texte`, with its role, **including the label of a component**: the
397
+ button `Layers` is a `composant` (`bouton`, `contenu: "Layers"`) **and** a `texte` (`bouton`, `libelle: "Layers"`), the rows `Wind`
398
+ and `Waves` of the panel are `case_a_cocher` components and `case_a_cocher` texts, the `Proposal` badge is a `statut` and a `badge`
399
+ `texte`. The tests check it both ways: every label of the expected inventory is drawn, and every drawn text (the HTML, and the
400
+ words of `pdftotext` once rendered) has an expected `texte`. Playwright is not a dependency of cadence:
401
+ install it (`npm i -D playwright`, `npx playwright install chromium`) or point `CADENCE_PLAYWRIGHT` to the folder of the package;
402
+ 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
403
+ refused (exit 2). The corpus is written where you say, not in `evals/`, which `claude plugin eval` reads as case folders.
404
+
405
+ The tests of the real render (PDF and PNG sizes, page counts, hash, zones of the texts against `pdftotext -bbox`) run only when
406
+ `CADENCE_PLAYWRIGHT` points to the package; `.github/workflows/tests.yml` installs Playwright and Chromium and sets it. Locally:
407
+ `CADENCE_PLAYWRIGHT=<folder of the playwright package> npx vitest run test/ref-corpus.test.ts`.
408
+
409
+ 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
410
+ expected file (`cadence ref add tmp/corpus-ref/<case>/document.pdf --slug corpus-<case>`), or `ref eval` refuses the reading.
411
+
412
+ ```
413
+ $ cadence ref corpus tmp/corpus-ref
414
+ $ cadence ref eval tmp/corpus-ref lectures/ # lectures/<case>/lecture.json
415
+ ```
package/docs/releasing.md CHANGED
@@ -7,11 +7,11 @@ A version exists in three places and is published in two; a release does all of
7
7
  1. Bump `version` in `package.json` (then `npm install` to refresh `package-lock.json`),
8
8
  `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`, and write the version's section of
9
9
  `CHANGELOG.md` (`## [x.y.z] - date`, what changes for the user, lots cited), in **one commit that holds
10
- only those files** (the version fields and the CHANGELOG, nothing else in the manifests, the « What's new »
11
- section of the README, the one place of it a release rewrites, and `docs/whats-new.md`, its long form: add the version there too). Such a commit is
10
+ only those files** (the version fields and the CHANGELOG, nothing else in the manifests, `docs/whats-new.md`: add the version to its « In two lines » list and its
11
+ long form (the README no longer carries a « What's new » section, L326)). Such a commit is
12
12
  exempt from the code-review gate like a plan commit: it closes the lot after its review, and `raf check`
13
13
  does not ask for a new one (nor does it count it as a commit without a lot). The « What's new »
14
- summary is ordered by version, a number known only at the release and shared by several lots: write it
14
+ summary (`docs/whats-new.md`) is ordered by version, a number known only at the release and shared by several lots: write it
15
15
  in that commit, not before the review. A commit that touches any other file
16
16
  — or another field of a manifest, or a dependency in `package-lock.json` — is work like any other and has
17
17
  to be reviewed.
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,25 @@ 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)).
68
+ - **reference-analyst** (agent, L265): given the folder of a document registered by `cadence ref add` and the file to
69
+ write, it looks at every rendered page whole, then every zone at full resolution, measures with `cadence ref measure`
70
+ (never by eye), copies every text verbatim, links each figure to the passages that cite it and writes one reading in
71
+ the format of `templates/inventaire.json`, validated by `cadence ref check`; what it cannot read for sure is
72
+ `incertain` with its cause, what the document does not show is a `silence`. Its tools are `Read`, `Write`, `Bash`,
73
+ `Grep`, `Glob`: it writes only its reading, never the tokens (the program counts them), never an expected inventory, and it never opens
74
+ another file of `lectures/` (L266: with `--double`, `cadence ref analyse` launches it twice, readings A and B, which must stay independent).
75
+ `cadence ref analyse` launches it ([ref](ref.md#cadence-ref-analyse-documentpdf---sections-3-59---slug-name---model-sonnetopushaiku---model-b-sonnetopushaiku---seuil-30---double)).
76
+ - **reference-verifier** (agent, L266): the third look of `cadence ref analyse`. It receives only the elements that one of the two readings wrote and the
77
+ other did not, each with the rendered page and a crop of its zone, and answers `confirme` or `ecarte` with a reason, measuring with
78
+ `cadence ref measure` rather than by eye and never guessing (what it cannot tell is `ecarte`). Its tools are `Read` and `Bash`: it writes nothing and never sees the readings
79
+ ([ref](ref.md#cadence-ref-analyse-documentpdf---sections-3-59---slug-name---model-sonnetopushaiku---model-b-sonnetopushaiku---seuil-30---double)).
61
80
  - **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
81
  optionally a lot id, to walk only the pages it touched — those that call the
63
82
  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 <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
@@ -2,7 +2,36 @@
2
2
 
3
3
  # What's new, version by version
4
4
 
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).
5
+ ## In two lines
6
+
7
+ - **1.10.0**: the worktree mode of `cadence orchestrate` needs only the `orchestrate.worktrees` key and is on for cadence itself; the gate plays `orchestrate.build` before the tests, and `lead tour` flags a missing `orchestrate.test`.
8
+ A lot waiting for a slot obeys `--drop` and `--stop-after-current`, the pre-check is back on Sonnet, `cadence perf` classes red deliveries, and the README is a short home page again.
9
+ - **1.9.0**: the plan lives at `.cadence/raf.yaml` (`raf migrate`); `cadence ref` reads reference documents; `doc-keeper` agent, `orchestrate.testGate`, `cadence lead hold` shown by `cadence-hud`.
10
+ - **1.8.0**: `cadence perf` shows the speed of the workflow next to its quality (cost in euros per delivered lot, red deliveries, blocking QA findings), and `cadence lead tour` opens with a perf header.
11
+ - **1.7.0**: `cadence orchestrate` no longer replays `accept` on a green sealed acceptation and refuses `--resume --dry-run`; `spec reseal --porteur` accepts a sheet amended by a delivered lot; the README becomes a short home page.
12
+ - **1.6.0**: the mockup review sweeps everything in its first report and runs up to `orchestrate.maquetteReviews` times (3 by default); the acceptance lock names the first offending commit.
13
+ - **1.5.1**: publishes 1.5.0 (tagged, never published): one UX review per lot, replay of a lot handed back for its budget, a self-check list in the briefs, a `tests.yml` workflow.
14
+ - **1.5.0**: `gate: { count: N, … }` makes a lot wait for N done lots of a kind; `raf todo <id>` puts a started lot without work commits back to `todo`.
15
+ - **1.4.0**: `cadence spec reseal` accepts a sheet amended by the lot's commits; wave budgets recalibrated; a `git checkout` in a session no longer stops the wave.
16
+ - **1.3.0**: the spec step narrows to `screen: page`; `cadence spec reseal … --porteur` re-seals an unstable test; `cadence orchestrate --waits`; a third correction pass when the review converges.
17
+ - **1.2.0**: the spec step reaches lots without a screen (`spec: true`), `orchestrate.accept` may be a list of runners, `lead.ux: lead`, and the code in service is a separate worktree.
18
+ - **1.1.0**: the spec step of `cadence orchestrate`: screen sheet, reviewed mockup, red locked acceptance tests, and briefs that carry rule → test → green.
19
+ - **1.0.0**: the first major release, a stable contract that follows semantic versioning; the [stability page](stabilite.md) lists what is guaranteed.
20
+
21
+ <details>
22
+ <summary>Older versions (0.10.0 to 0.28.0)</summary>
23
+
24
+ Each is a paragraph in [docs/whats-new.md](whats-new.md) and a full section in [CHANGELOG.md](../CHANGELOG.md): `cadence-hud` (0.12.0 to 0.25.0), `cadence orchestrate --continue` and `--status --watch`, `cadence lead tour`, `hook.autostart`, `docs.sync` and `docs.articles`, neighbouring repositories, `raf ignore`.
25
+
26
+ </details>
27
+
28
+ ## Version by version
29
+
30
+ The long form of « In two lines » above (the README no longer carries a « What's new » section since L326: it links here). The full history, with the date of each version, is in [CHANGELOG.md](../CHANGELOG.md).
31
+
32
+ **1.10.0**: the worktree mode of `cadence orchestrate` (one git worktree per lot, independent lots of one repository in parallel) is no longer behind `CADENCE_EXPERIMENTAL_WORKTREES=1`: the `orchestrate.worktrees` key alone is the switch, on start and on `--resume`, `--integrate`, `--replay` and `--continue`, and cadence's own `cadence.yaml` turns it on after the trials L310 and L316 (L320); a red at the integrated tip is played once more before the lot is handed back, and the refusal names the red tests (L328); a union of the `CHANGELOG.md` entries of parallel lots, which accepts that a lot rewrites its own entry (L314, L323), and a kept branch recovered without the pre-check (L311). The gate of the wave plays `orchestrate.build` before `orchestrate.test`, a red build handing the lot back as a « build » finding (L333); `cadence lead tour` and `cadence orchestrate --dry-run` flag a project without `orchestrate.test` and name the probable command (L327). The pre-check is back on Sonnet in cadence's own configuration after the local model failed on about one lot in three, skips a lot created after the last code commit of the repository, and `cadence perf` measures it per lot (L322, L328). A lot waiting for a session slot obeys `--drop` and `--stop-after-current` (L332); `--resume` without a wave resumes the only unfinished one and is refused when several are (L309). `cadence perf` classes the red deliveries written before the class from their text (L331) and names the step that carries a rise of the tokens per lot (L312). `npm run service` purges the old copies put in service (L330), `cadence deliver` keeps a Ctrl-C (L325, L329), and `raf docs-ok` acknowledges a `docs.sync` gap (L317); `raf check` no longer reports two false drifts (L321). `cadence ref analyse` reads a reference document, `cadence ref diff` reconciles two readings and `--double` reads twice, an option (L265, L266); `cadence ref add` cites a range of figures and no longer reads « code figure n » as a citation (L274, L313); a failing PDF tool is a refusal, not a stack trace (L275). The README is a short home page again, the detail having moved to `docs/overview.md` (L326). Details: [CHANGELOG.md](../CHANGELOG.md).
33
+
34
+ **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).
6
35
 
7
36
  **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).
8
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylad/cadence",
3
- "version": "1.8.0",
3
+ "version": "1.10.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,6 +22,7 @@
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",