@sylad/cadence 0.10.0 → 0.10.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.
@@ -6,7 +6,7 @@
6
6
  {
7
7
  "name": "cadence",
8
8
  "description": "Session start and close rituals driven by a versioned plan (raf), and deliveries proven by their effect. Needs the cadence CLI (npm i -g @sylad/cadence).",
9
- "version": "0.10.0",
9
+ "version": "0.10.1",
10
10
  "source": "./",
11
11
  "author": { "name": "Sylvain Ladoire" }
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cadence",
3
3
  "description": "A repo-native working method: session start and close rituals driven by a versioned plan (raf), deliveries proven by their effect, and three reviewer agents (UX, code, QA).",
4
- "version": "0.10.0",
4
+ "version": "0.10.1",
5
5
  "author": { "name": "Sylvain Ladoire" },
6
6
  "homepage": "https://github.com/Sylad/cadence",
7
7
  "repository": "https://github.com/Sylad/cadence",
package/README.md CHANGED
@@ -36,7 +36,8 @@ reports a page left empty or in error.
36
36
  touches is a plan file**: the plan itself, its Gantt page (only at its default place, `gantt.html` next to the plan — written elsewhere with `raf gantt -o`, declare it under `plan.files`), the `plan:` key of the config file in use (`cadence.yaml`, or
37
37
  the `--config` file; a change to `deliver:`/`session:` is work), or a file the
38
38
  project lists under `plan.files` in `cadence.yaml` (a page it generates from
39
- the plan, a journal). Such a commit is never a "commit without a lot", and it
39
+ the plan, a journal), or the QA expectations file (`docs/qa/expectations.md`,
40
+ or the file named by `qa.expectations`; the lead commits the `qa-reviewer`'s draft of it). Such a commit is never a "commit without a lot", and it
40
41
  does not count as work on the lots it cites: it is absent from `raf commits`,
41
42
  does not start a `todo` lot, does not make a code review stale, and
42
43
  `cadence deliver` does not announce the lots it cites as delivered. The files
@@ -274,8 +275,8 @@ The counterpart of the UX review, off by default. With the rule on, `raf done`
274
275
  refuses a lot that has at least one commit citing it and no recorded verdict
275
276
  (`--force` to override), and `raf check` reports such lots finished after the
276
277
  `reviewSince` day. A lot with no commit has nothing to review; neither does a
277
- lot whose only commits touch plan files alone (the plan, or a file listed under
278
- `plan.files`), predate the plan's `since` or match an `ignore:` pattern — `raf commits <id>` prints exactly the counted set.
278
+ lot whose only commits touch plan files alone (the plan, a file listed under
279
+ `plan.files`, or the QA expectations file), predate the plan's `since` or match an `ignore:` pattern — `raf commits <id>` prints exactly the counted set.
279
280
 
280
281
  The verdict is tied to what was reviewed: `raf review` stores it on the lot with
281
282
  the sha of the lot's latest counted commit (`review: { date, verdict, commit }`,
@@ -346,7 +347,7 @@ qa:
346
347
  expectations: docs/quality/pages.md
347
348
  ```
348
349
 
349
- Only the agent reads that key; the CLI does not use it. Without an expectations file the agent walks
350
+ The agent reads that key, and so does `raf check`: a commit touching only that file is plan upkeep (no lot to cite, see above). Without an expectations file the agent walks
350
351
  the routes it discovers and still runs its universal checks: an error shown, a failed API call
351
352
  whose content is missing on screen, a broken or missing content image are defects with or without a
352
353
  file; an empty 2xx body, like whatever else would need an expectation to judge, is suspect at most
@@ -725,7 +726,7 @@ orchestrate:
725
726
 
726
727
  Without `start`, a read-only plan's `todo` lot is refused (start it with the project's tool); without
727
728
  `verdict`, the review verdict stays in the wave's state and you report it. Only the plan's files
728
- (`plan.path`, `plan.files`) are committed from those commands; anything else dirty stops the lot.
729
+ (`plan.path`, `plan.files`, the QA expectations file) are committed from those commands; anything else dirty stops the lot.
729
730
 
730
731
  ## Claude Code skills
731
732
 
package/dist/audit.js CHANGED
@@ -12,7 +12,7 @@ function configRel(plan, root) {
12
12
  }
13
13
  /** Le plan, sa page Gantt, la configuration lue (cadence.yaml) et les fichiers tenus avec lui (plan.files). */
14
14
  function ownFiles(plan, root) {
15
- return new Set([relative(root, plan.path), relative(root, join(dirname(plan.path), 'gantt.html')), configRel(plan, root), ...plan.files]);
15
+ return new Set([relative(root, plan.path), relative(root, join(dirname(plan.path), 'gantt.html')), configRel(plan, root), ...plan.maintained]);
16
16
  }
17
17
  function withoutPlanKey(text) {
18
18
  if (text === null)
@@ -39,7 +39,7 @@ export function planCommits(plan, root, opts = {}) {
39
39
  }
40
40
  /**
41
41
  * Commit d'entretien du plan : TOUS ses fichiers sont des fichiers du plan (le plan, sa page Gantt,
42
- * plan.files — son plan publié par exemple — et la configuration lue, mais celle-ci seulement quand la
42
+ * plan.files — son plan publié par exemple —, les attendus QA et la configuration lue, mais celle-ci seulement quand la
43
43
  * clé `plan:` est la seule à changer : deliver/session/… sont du travail). Les fichiers décident,
44
44
  * jamais le sujet : « chore(plan): … » qui touche un fichier source est un commit comme un autre.
45
45
  */
package/dist/cli.js CHANGED
@@ -47,7 +47,7 @@ Quand la portée du sujet cite des lots — « feat(L3): … », « chore(L31,L3
47
47
  mention en passage (« page équipe (L27) »), une plage (« L28–L31 », « L45 à L48 ») ou le corps n'y comptent pas.
48
48
  Sans portée citant un lot, tout le message est lu : « fix: L3 corrigé », « L3/t1 ».
49
49
  Un commit qui ne touche que le plan, la clé plan: de cadence.yaml (deliver/session sont du travail) (et les fichiers déclarés sous plan.files, un plan
50
- publié par exemple) n'a pas à en citer, et ne compte pas pour les lots qu'il cite ; le sujet n'y change rien.
50
+ publié par exemple, ainsi que le fichier d'attendus QA : docs/qa/expectations.md ou qa.expectations) n'a pas à en citer, et ne compte pas pour les lots qu'il cite ; le sujet n'y change rien.
51
51
  Un lot --visible attend une entrée Nouveautés (docs/nouveautes/, --dir) avec capture ; raf check le vérifie.
52
52
  Un texte qui commence par « - » se passe après « -- » : raf note L1 -- "-5 %".
53
53
  Le plan est docs/plan/raf.yaml, ou celui que nomme « plan: » dans cadence.yaml ; un plan tenu par un
package/dist/config.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, isAbsolute, posix, relative } from 'node:path';
2
3
  import { parse } from 'yaml';
3
4
  import { isDay } from './dates.js';
4
5
  import { FIELDS, RafError, STATUSES } from './plan.js';
@@ -6,6 +7,29 @@ const KEYS = ['path', 'project', 'since', 'ignore', 'files', 'lots', 'fields', '
6
7
  const FORMAT_KEYS = ['lots', 'fields', 'statuses', 'estimates'];
7
8
  const isObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
8
9
  const list = (v) => (v == null ? [] : Array.isArray(v) ? v.map(String) : [String(v)]);
10
+ /**
11
+ * Clé `qa:` : le fichier d'attendus QA, ramené à la forme que git rapporte (relatif à la racine du dépôt,
12
+ * séparateurs posix, sans `./`). Une valeur vide vaut absente ; un type faux ou un chemin hors du dépôt est refusé.
13
+ */
14
+ function readQa(qa, file) {
15
+ if (qa == null)
16
+ return undefined;
17
+ if (!isObject(qa))
18
+ throw new RafError(`${file} : qa doit être un objet`);
19
+ const v = qa.expectations;
20
+ if (v == null)
21
+ return undefined;
22
+ if (typeof v !== 'string')
23
+ throw new RafError(`${file} : qa.expectations doit être un chemin`);
24
+ const raw = v.trim().replace(/\\/g, '/');
25
+ if (raw === '')
26
+ return undefined;
27
+ const root = dirname(file);
28
+ const rel = posix.normalize(isAbsolute(raw) ? relative(root, raw).replace(/\\/g, '/') : raw);
29
+ if (rel === '.' || rel === '..' || rel.startsWith('../') || isAbsolute(rel))
30
+ throw new RafError(`${file} : qa.expectations « ${v.trim()} » est hors du dépôt`);
31
+ return rel;
32
+ }
9
33
  /**
10
34
  * Clé `plan:` de cadence.yaml : où est le plan et, s'il est tenu par un autre outil, comment le lire.
11
35
  * Null quand le fichier ou la clé manque — le plan est alors docs/plan/raf.yaml au format de raf.
@@ -20,18 +44,22 @@ export function readPlanConfig(file) {
20
44
  catch (e) {
21
45
  throw new RafError(`${file} illisible : ${e.message.split('\n')[0]}`);
22
46
  }
23
- const p = raw?.plan;
47
+ const doc = raw;
48
+ const qa = readQa(doc?.qa, file);
49
+ const p = doc?.plan;
24
50
  if (p == null)
25
- return null;
51
+ return qa ? { settings: { qaExpectations: qa } } : null;
26
52
  const bad = (what) => new RafError(`${file} : plan.${what}`);
27
53
  if (typeof p === 'string' && p.trim() !== '')
28
- return { path: p, settings: {} };
54
+ return { path: p, settings: { ...(qa ? { qaExpectations: qa } : {}) } };
29
55
  if (!isObject(p))
30
56
  throw new RafError(`${file} : plan doit être un chemin ou un objet`);
31
57
  for (const k of Object.keys(p))
32
58
  if (!KEYS.includes(k))
33
59
  throw bad(`${k} inconnu (attendu : ${KEYS.join(', ')})`);
34
60
  const settings = {};
61
+ if (qa)
62
+ settings.qaExpectations = qa;
35
63
  if (p.project != null)
36
64
  settings.project = String(p.project);
37
65
  if (p.since != null) {
@@ -72,7 +72,7 @@ function trackedPaths(s) {
72
72
  async function commitPlan(c, message) {
73
73
  const repo = c.lot.repo;
74
74
  const plan = c.loadPlan();
75
- const own = new Set([relative(repo, plan.path), ...plan.files]);
75
+ const own = new Set([relative(repo, plan.path), ...plan.maintained]);
76
76
  const dirty = trackedPaths(await snapshot(repo, { remote: false }));
77
77
  if (dirty.length === 0)
78
78
  return null;
package/dist/plan.js CHANGED
@@ -130,6 +130,14 @@ export class Plan {
130
130
  get files() {
131
131
  return this.settings.files ?? [];
132
132
  }
133
+ /** Fichier d'attendus QA du `qa-reviewer`, relatif à la racine du dépôt. */
134
+ get qaExpectations() {
135
+ return this.settings.qaExpectations ?? 'docs/qa/expectations.md';
136
+ }
137
+ /** Tout ce qui est tenu avec le plan sans être du travail : plan.files et le fichier d'attendus QA. Source unique de l'audit et de l'orchestrateur. */
138
+ get maintained() {
139
+ return [...this.files, this.qaExpectations];
140
+ }
133
141
  /**
134
142
  * Références citées par un texte. Format de raf : préfixe et numéro (`L3`, `L3/t1`). Autre format :
135
143
  * les identifiants du plan eux-mêmes, quelle que soit leur forme (`E-A2`, `NC2.4`, `B33/t1-fusion`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylad/cadence",
3
- "version": "0.10.0",
3
+ "version": "0.10.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",