@sylad/cadence 1.21.0 → 1.22.1

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.
@@ -8,7 +8,7 @@
8
8
  {
9
9
  "name": "cadence",
10
10
  "description": "Session start and close rituals driven by a versioned plan (raf), and deliveries proven by their effect. Needs the cadence CLI (npm i -g @sylad/cadence).",
11
- "version": "1.21.0",
11
+ "version": "1.22.1",
12
12
  "source": "./",
13
13
  "author": {
14
14
  "name": "Sylvain Ladoire"
@@ -17,7 +17,7 @@
17
17
  {
18
18
  "name": "cadence-hud",
19
19
  "description": "A status band above the Claude Code prompt: context, 5 h / 7 d quota windows, cost per model, agents of the session and the running cadence orchestrate waves, plus a read-only lots panel (`/hud lots`). Works without the cadence CLI, except the per-project plan progress and the lots panel, which need `cadence` on the PATH.",
20
- "version": "0.6.0",
20
+ "version": "0.7.0",
21
21
  "source": "./plugins/cadence-hud",
22
22
  "author": {
23
23
  "name": "Sylvain Ladoire"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cadence",
3
3
  "description": "A repo-native working method: session start and close rituals driven by a versioned plan (raf), deliveries proven by their effect, and three reviewer agents (UX, code, QA).",
4
- "version": "1.21.0",
4
+ "version": "1.22.1",
5
5
  "author": { "name": "Sylvain Ladoire" },
6
6
  "homepage": "https://github.com/Sylad/cadence",
7
7
  "repository": "https://github.com/Sylad/cadence",
@@ -704,6 +704,8 @@ async function session(c, kind, local = false) {
704
704
  if (playwright)
705
705
  l.uxCaptures = join(dirname(mcpConfig), 'playwright');
706
706
  const answerInBrief = l.pendingAnswer; // gardée pour la rendre si la session ne part jamais (créneau abandonné, L332)
707
+ if (answerInBrief && kind === 'fix')
708
+ step.answered = true;
707
709
  l.pendingAnswer = null; // la réponse est dans le brief : elle ne repart pas avec la session suivante
708
710
  const tmpDir = w.store.tmpDir(l.project, l.lot); // TMPDIR de la session, aussi en --add-dir : hors du cwd, un Write y serait refusé en `claude -p`
709
711
  // Délai (L298) : l'étape d'écriture est proportionnée à l'estimate et à la fiche du lot, puis prolongée à chaque échéance où elle a commité.
@@ -2199,12 +2201,19 @@ async function work(c, kind) {
2199
2201
  return;
2200
2202
  }
2201
2203
  // La session peut n'avoir plus rien à commiter (réponse du lead, lot commité avant la vague) : le travail du lot est déjà dans git.
2202
- // Même règle côté fix (L56) : la passe n'avait rien à ajouter, le lot a déjà ses commits de travail → revue neuve ; la passe reste consommée, MAX_PASSES borne la boucle.
2204
+ // Même règle côté fix (L56) : la passe n'avait rien à ajouter, le lot a déjà ses commits de travail → revue neuve, à condition que la session donne sa raison (L406, plus bas) ; la passe reste consommée, MAX_PASSES borne la boucle.
2203
2205
  const alreadyDone = hasWork(plan, wd(c), l);
2204
2206
  if (made === 0 && !alreadyDone && !headMoved) {
2205
2207
  stop(c, 'handed-back', `${kind} sans commit`);
2206
2208
  return;
2207
2209
  }
2210
+ // L406 : une correction qui ne commite rien doit dire pourquoi (« choix », lu par la revue). Sans raison, la revue relirait les mêmes commits et rendrait les mêmes constats : la passe ne compte pas (toFix l'avait comptée), le lot est rendu.
2211
+ const reason = (rep.choix ?? []).some((x) => x.trim()) || !!done.step.answered; // la réponse du lead, que la session a lue dans son brief, vaut raison
2212
+ if (kind === 'fix' && made === 0 && !headMoved && !docsPass && !reason) {
2213
+ l.pass = Math.max(0, l.pass - 1);
2214
+ stop(c, 'handed-back', 'fix sans commit ni raison (choix vide) : la revue aurait relu les mêmes commits');
2215
+ return;
2216
+ }
2208
2217
  // L317 : la passe du bibliothécaire qui laisse l'écart ouvert (rien commité, ou autre chose que la documentation) ne passe pas à la revue : le lot ne finirait pas prêt, `raf check` resterait rouge.
2209
2218
  if (docsPass) {
2210
2219
  const open = openDocGap(c);
@@ -2216,7 +2225,7 @@ async function work(c, kind) {
2216
2225
  if (headMoved)
2217
2226
  l.warnings.push(`${minorsPass ? 'passe des mineurs' : 'correction'} reprise sans nouveau commit : un commit d'une session précédente est relu par la revue`);
2218
2227
  else if (made === 0)
2219
- l.warnings.push(kind === 'fix' ? 'fix sans nouveau commit : revue lancée sur les commits du lot' : `${kind} sans nouveau commit : revue lancée sur les commits déjà faits du lot`);
2228
+ l.warnings.push(kind === 'fix' ? 'fix sans nouveau commit : revue lancée sur les commits du lot, avec la raison de la session (choix)' : `${kind} sans nouveau commit : revue lancée sur les commits déjà faits du lot`);
2220
2229
  const red = [];
2221
2230
  // Tests annoncés rouges (L213) : avec orchestrate.test, le programme rejoue et compare à la ligne de base, la session n'est crue que si les tests y sont rouges autrement que par des échecs de départ ; sans, la liste des échecs de base de la session, non vérifiée.
2222
2231
  const claimed = { source: 'tests', gravite: 'bloquant', texte: `tests annoncés rouges par la session : ${rep.tests.commande} — ${rep.tests.resultat}` };
@@ -59,20 +59,22 @@ answers `argument inconnu : … (attendu : cmd, projets, lots)` and leaves the b
59
59
 
60
60
  ### The lots panel (`/hud lots`, L401)
61
61
 
62
- `/hud lots` opens a read-only **panel** beside the conversation (inline above the prompt on the main screen); the same command closes it. It is never opened on its own, and a terminal under 110 columns answers `terminal trop étroit pour le panneau (110 colonnes au moins)` and opens nothing. For each project of the folder (the order of `priority:`) it draws the open lots as a tree, with their state and their `after:` links:
62
+ `/hud lots` opens a **panel**, read-only for the lots themselves beside the conversation (inline above the prompt on the main screen); the same command closes it. It is never opened on its own, and the command opens it whatever the width of the window, Claude placing it; when Claude cannot place it, the command answers `panneau des lots non placé : <reason>` and nothing opens. For each project of the folder (the order of `priority:`) it draws the open lots as a tree, with their state and their `after:` links:
63
63
 
64
64
  ```
65
65
  Lots lecture seule lu à 15:52
66
- cadence 7 lots non clos
66
+ ▾ cadence 7 lots non clos
67
67
  L401 Panneau des lots (mod cadence-hud, Pane) ▶ en cours
68
68
  ├ L401/2 Écrire le panneau et son rafraîchissement ▶ en cours
69
69
  └ L401/3 Test du rendu (claude plugin test) et doc ○ à faire
70
70
  L402 Réordonner par glisser-déposer after: L400 L401 ⊘ bloqué
71
- ol 1 lot non clos
71
+ ▾ ol 1 lot non clos
72
72
  L80 Calendrier des matchs ⟳ relit
73
73
  /hud lots pour fermer
74
74
  ```
75
75
 
76
+ Each project name is a button (L409): a click, or Enter once it has the focus (click in the panel, or ctrl+x then tab), folds the project to one line (`▸ maritime 159 lots non clos · 3 en cours`, the name, the open lots and those in progress) or opens it again (`▾`). `/hud lots <project>` does the same from the keyboard (`projet inconnu : … (projets : …)` for a name that is not in the panel). A project with more than 20 open lots (native sub-tasks not counted) is folded until you choose; your choice wins over that default either way, and it is kept between two openings of the panel and between sessions (the plugin's `$.store`, key `lotsFolds`). A folded project that is shown is not counted in `… +N lots de plus`, and when the panel is short it keeps its line even after the cut (the open lots before it give way); if there are more folded projects than the panel has room for, the ones left out are counted there with their open lots. A project the tour could not read has no button.
77
+
76
78
  States: `▶ en cours` (doing), `○ libre` (todo, no open `after:`), `⊘ bloqué` (todo, an `after:` is still open); a lot in a wave takes the wave's state and words, as the band writes them (`… en attente`, `⟳ implémente`, `⟳ relit`, `⟳ corrige`, `? QUESTION`, `✔ prêt`, `↩ rendu`, `✖ échec`, `⏸ suspendu`). In a native plan the sub-tasks of an open lot (`L401/2`) are its sub-lots (`├`/`└`); they only read `▶ en cours` or `○ à faire`, and are not counted in the project's `N lots non clos`. In a plan kept by another tool the sub-lots come from `parent:`. An `after:` id that is closed or unknown is struck through and blocks nothing. A project the tour could not read shows `illisible : <error>`; the others stay.
77
79
 
78
80
  Before the first reading the panel says `Lecture des plans…`; if `cadence lead tour` does not answer it says `Lots illisibles : cadence lead tour n'a pas répondu. Nouvelle lecture dans une minute.` (a failed re-reading keeps the last table); a `cadence` too old to give the `lots` field says `Lots indisponibles : cadence est trop ancien, mettez-le à jour.`; with no open lot, `Aucun lot non clos dans les projets de ce dossier.`. A tree taller than the panel ends with `… +N lots de plus`. Under 60 columns the `after:` list moves to its own line and the title is cut with `…`, never the id or the state.
@@ -454,9 +454,13 @@ review) still starts with the implementation session: its brief tells it to read
454
454
  sub-tasks, which carry the findings of any earlier review. If that session ends without a new commit on a lot that
455
455
  already has commits, the wave goes on to the review (warning `implement sans nouveau commit`) instead of handing the
456
456
  lot back; with no commit on the lot at all it is handed back as `implement sans commit`. The same goes for a
457
- correction pass: a `fix` session that adds no commit to a lot that already has work commits goes on to a fresh review
458
- (warning `fix sans nouveau commit : revue lancée sur les commits du lot`), the pass still counted — the cap of two
459
- correction passes stays the guard against a loop; with no commit on the lot it is handed back as `fix sans commit`.
457
+ correction pass: a `fix` session that adds no commit to a lot that already has work commits must give its reason under
458
+ `choix` (the brief says so; a blank entry is no reason). With the reason, the wave goes on to a fresh review, which reads
459
+ it (warning `fix sans nouveau commit : revue lancée sur les commits du lot, avec la raison de la session (choix)`), the
460
+ pass counted — the cap of two correction passes stays the guard against a loop. Without it (L406), a review would reread
461
+ the same commits and return the same findings, so the lot is handed back as `fix sans commit ni raison (choix vide) :
462
+ la revue aurait relu les mêmes commits` and the pass is not counted (measured over the wave states of 2026-10-28 and before: 68 `fix` sessions without commit, 5 with an empty `choix` — about 7 %; the two L401 passes of 2026-10-28 gave a reason and stay counted, the test gate relaunched them, not a review). With no commit on the lot at all it is handed back as
463
+ `fix sans commit`.
460
464
 
461
465
  **Compliant with minor findings**: the minors are not left for a follow-up lot. One **minors pass** runs
462
466
  before concluding: a new Sonnet session with its own brief (`fix-minors.md`) fixes the minors that are right
package/docs/whats-new.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  ## In two lines
6
6
 
7
+ - **1.22.0**: the panel of lots folds a project with a click on its name; a project of more than 20 open lots starts folded.
8
+ - **1.21.1**: `/hud lots` opens on the desktop app whatever the width; a correction session that makes no commit must say why, or the lot is handed back.
7
9
  - **1.21.0**: `/hud lots` opens a panel beside the conversation with the open lots of every project; the pre-push hook of a wave refuses a push that carries a lot not ready.
8
10
  - **1.20.0**: `raf move <id> --before|--after <id>` reorders a lot or a sub-task in the plan, refused during a wave of the repository and on a plan kept by another tool.
9
11
  - **1.19.1**: `cadence verify --gate` in the main tree reads `HEAD` and `git status` again after the steps, says what moved and prints no green or red verdict that is not the sha's.
@@ -46,6 +48,10 @@ Each is a paragraph in [docs/whats-new.md](whats-new.md) and a full section in [
46
48
 
47
49
  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).
48
50
 
51
+ **1.22.0**: the panel of lots no longer lets one big project hide the others (L409, cadence-hud 0.7.0, `docs/cadence-hud.md`): each project folds and unfolds with a click on its name (Enter under the focus, or `/hud lots <project>`), a project of more than 20 open lots starts folded on a single line (name, open lots, in progress), and the folded state is kept between two openings.
52
+
53
+ **1.21.1**: `/hud lots` opened nothing on the desktop app, even on a very wide screen (« terminal trop étroit », L408): the mod checked 110 columns itself, while a panel the person asks for is placed at any width; the engine now decides, and says « non placé » with its reason otherwise (cadence-hud 0.6.1). In a wave, a correction session that makes no commit must give its reason under `choix`; without one the lot is handed back instead of replaying the same review (L406, `docs/orchestrate.md`).
54
+
49
55
  **1.21.0**: what is in progress, project by project, is now visible beside the conversation. `/hud lots` (cadence-hud 0.6.0, L401, `docs/cadence-hud.md`) opens a read-only panel with, for each project of the parent folder, the tree of its open lots and native sub-tasks, their state (in progress, ready, blocked, in a wave, handed back, in question) and their `after:` links, crossed out once satisfied; it reads `cadence lead tour --json`, which now carries a `lots` field (measured 7.0–8.4 s on ten projects, as before). The temporary `pre-push` hook of a wave also refuses a push of the lead or of a hand-started session that carries a commit of a lot not ready, names the lot and gives the safe command `git push <remote> <sha>:<branch>`; for a tag it refuses without a command, since a tag must name its own commit (L405, `docs/orchestrate.md`).
50
56
 
51
57
  **1.20.0**: the order of the plan is no longer edited by hand. `raf move <id> --before|--after <id>` (L400, `docs/raf.md`) moves a lot among the lots, or a sub-task among the sub-tasks of its lot; it is refused across different parents, on an archived lot, during a live wave of the repository (the same detection as `cadence deliver`) and on a plan kept by another tool (with that tool's command); a lot already in place leaves the file untouched, and like every plan write it does not commit. A test that read the real plan file directly, red since `raf archive`, now goes through the plan loader, and a guard test refuses any test that reads the real plan without it (L404).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylad/cadence",
3
- "version": "1.21.0",
3
+ "version": "1.22.1",
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",
@@ -25,7 +25,7 @@ A point you cannot hold is written under "choix" or "nonVerifie" of your report,
25
25
 
26
26
  Auto-verification once the tests are green, before the review: when the full test suite and the build pass, re-read the complete diff of your fix commits (`git diff <HEAD before your first fix>..HEAD`, run in each repository where you committed a fix: the project and any neighbouring repository above) against the grid of the code-reviewer agent: code bug; architecture drift; docs/architecture.md (or the project's architecture documentation) up to date; edge cases; one test per changed behaviour. What you find in your own fix, fix it at once (a `fix({{lot}}): …` commit) and rerun the suite and the build; touch nothing else. Then write in "resume" a paragraph that starts with « auto-vérification »: what you re-read, what you found and fixed, what you could not hold. It is one reading of the diff, not a second review: no subagent, no new exploration, no re-reading of files the diff does not touch.
27
27
 
28
- If a finding is wrong, say so under "choix" with the reason. Ask (under "questions") only when a fix needs a decision that passes the rule below.
28
+ If a finding is wrong, say so under "choix" with the reason. If you make no commit at all, "choix" MUST say why: a session with no commit and no reason is handed back to the lead without a new review (the review would reread the same commits). Ask (under "questions") only when a fix needs a decision that passes the rule below.
29
29
  A question is legitimate only if it names what it would change: the scope of the lot, the architecture, or a costly rollback (data, production, public API);
30
30
  otherwise it is a choice: decide, write the alternative you did not take, go on. Minor choices to settle yourself:
31
31
  a spacing, colour or label value when a measurement or a rule justifies it; the URL of a link (the most general official page if a precise one is not certain);