@sylad/cadence 0.28.0 → 1.0.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +62 -7
- package/dist/orchestrate/cycle.js +79 -8
- package/package.json +2 -1
|
@@ -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": "0.
|
|
11
|
+
"version": "1.0.0",
|
|
12
12
|
"source": "./",
|
|
13
13
|
"author": {
|
|
14
14
|
"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": "0.
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"author": { "name": "Sylvain Ladoire" },
|
|
6
6
|
"homepage": "https://github.com/Sylad/cadence",
|
|
7
7
|
"repository": "https://github.com/Sylad/cadence",
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Code prompt that shows the session's context, quota, cost and the orchestrate wa
|
|
|
24
24
|
|
|
25
25
|
## What's new
|
|
26
26
|
|
|
27
|
-
**0.28.0**: `orchestrate.precheck: local` runs the « deliverable already present? » pre-check of `cadence orchestrate` on the local model (`claude-local`, Ollama), off the Anthropic quota, Sonnet staying the default and the fallback (L146); the `qa-reviewer` agent walks only the pages a lot touched, with a **Scope** line in its report (L123); the sub-tasks a review closes are also read in a lot's neighbouring repositories (L156) and a commit's sub-task list is read with the plan's left guard (L155); an unstable test is fixed (L90). **0.27.1**: `cadence orchestrate` no longer leaves a session's child processes alive: a dev server started in its own process group, or orphaned by a shell that exits (`nohup srv &`), is killed with the session through its process tree and its `CADENCE_SESSION` mark, a tmux server, `screen`, `gpg-agent` or `dirmngr` started on demand being spared; a session that exits with code 0 while a descendant holds its output open is no longer reported as « délai dépassé » (L83); the `ux-reviewer` and `qa-reviewer` agents carry the briefs' Playwright rule outside `cadence orchestrate` (L84). **0.27.0**: a compliant review of `cadence orchestrate` settles the open sub-tasks that commits of the lot cite (`feat(L3/t2): …`, comma lists included), closed in the same plan commit as the verdict or, on a read-only plan, returned as `[sous-tâche clore]` proposals, so `raf done` no longer refuses a lot over sub-tasks whose work is done; on a foreign-format plan the ids are read as the plan reads them (L82); the test suite stays green with two full suites running in parallel (L150). **0.26.0**: `cadence orchestrate` raises the floor of a lot's budget from 200 k to 450 k tokens, enough to pay a write pass, its review, one fix pass and the short review that follows, so a lot of 0.5 or 1 day is no longer handed back at its second or third pass; a `--continue` wave draws fewer lots as a result (L128). **0.25.0**: the project line of the plans' progress in the `cadence-hud` band names the lots in progress after their count, three at most then `…` (L153, `cadence-hud` 0.4.2); the progress follows the folder the session was launched from, so a `cd` into a sub-project no longer shrinks the band, and the session context keeps the `session.start` folder (L154). **0.24.0**: `cadence orchestrate --drop <project:lot>` and `--stop-after-current` steer a live wave from another terminal, `--resume --drop` removes a lot before replaying a stopped wave (L79); the final table of a wave says « livrable jusqu'à <sha> » for a ready lot stacked under a lot handed back, with `git push origin <sha>:main && cadence deliver --sha <sha>` (L76); a version commit (version fields, `CHANGELOG.md`, the README's « What's new ») no longer asks for a new code review (L72). **0.23.0**: the progress bar of each project in the `cadence-hud` band is coloured by the share of lots done, red below 33 %, orange below 66 %, green beyond (L152, `cadence-hud` 0.4.1); `cadence deliver` under a wave lock refuses only when a lot of the wave still works in that very repository, a linked worktree included (L127). **0.22.0**: `cadence orchestrate --continue` draws the next ready lot of the plan itself, in the declared priority, until the budget, the time window or a question stops it (L147); `cadence session context` prints the context of the session, which the `cadence-hud` band publishes, and the `lead` skill chains lots without the human while it stays under 60 % (L148); the band shows the progress of each project's plan, and `cadence lead tour --json` gains `progress` (L149, `cadence-hud` 0.4.0); the final review of a lot is always played, the lot's own budget bounding the writing passes only, with 65 k tokens reserved for it (L145); the cause « tests rouges après … » names the pass whose tests were red (L151). **0.21.0**: `cadence orchestrate --status` and `cadence lead tour` print the free session slots, which the `lead` skill reads (L141); `orchestrate.effort` sets the effort level of each pass of a wave (L137); `docs.sync` makes `raf check`, `lead tour` and the review brief report a document that does not follow the code (L143); `docs.articles` opens, after a green `cadence deliver`, a lot `Article <project> à rafraîchir` in the neighbouring repository (L144). **0.20.0**: `cadence-hud` no longer misleads once the work is over: `/hud cmd` lists the background commands it counts (id, tool, origin, age, name), any other argument answers `argument inconnu` instead of hiding the band, commands with no end seen for an hour or no record go to a grey `N cmd sans fin vue`, a finished subagent takes its commands with it, two races that left a `1 cmd` counted forever are closed, and a finished wave shows grey without its budget then disappears after 30 minutes (L142); the `qa-reviewer` and `ux-reviewer` agents are read-only for real, their frontmatter listing Read, Grep, Glob, Bash and the Playwright tools (L140). **0.19.1**: `cadence-hud`'s `N cmd` counter goes back down when a Monitor expires, when the background commands a subagent started end with it, and at session start or plugin reload (L135); its `tool.call` and `prompt.submit` guard hooks carry a `.catch` on registration, which clears the `claude plugin validate` warning (L136). **0.19.0**: `cadence orchestrate` lifts a repository's lock and `pre-push` guard as soon as every lot of the wave that touches it is finished, so `cadence deliver` works there while the wave continues elsewhere (L132); a review that leaves a repository dirty hands back that lot only, suspends the lots still queued in that repository and keeps the other repositories running, its verdict reported instead of lost (L133); `cadence session close` re-run without a `session start` keeps the window since the last opening (L131); `cadence-hud` draws the lot rows of a wave in aligned columns on every surface (L134). **0.18.0**: `cadence session next` prints what it records (issue #6); `cadence session close` covers the period since the last `session start` (or the last close) and says so in its title, `--since` still wins (issue #7). **0.17.0**: `cadence-hud` shows an `N cmd` segment next to the agents, the background commands of the session (Bash run in the background and Monitor, subagents' included), tracked from the tool results and ended by the task's notification or `TaskStop`. **0.16.0**: a lot can declare **neighbouring repositories** (`repos:` key of the lot, `{ path, cite }` for a repository shared between projects): `cadence orchestrate` refuses, locks and guards them, gives them to every session as `--add-dir`, and `raf commits`, `raf show` and the review verdict read the commits that cite the lot there too. **0.15.0**: `hook.autostart` in `cadence.yaml` (`warn` by default, `refuse` or `start`): a pre-commit hook that refuses, or starts in the same commit, a lot still todo that the commit cites; `cadence session start|close --all [--depth n]`, one section per project under a folder. **0.14.0**: `cadence lead tour`, the lead's morning table of every project in a folder, printed by a program (one line per project: lots in progress, gaps, last notes, next ready lot, repository) in place of one subagent per project; the `lead` skill calls it. **0.13.1**: `cadence-hud` takes back the segments that fit once a big one has dropped (reset times, then the per-model consumption, now short: `opus 1.2M sonnet 800k`); `CADENCE_HUD_AMBIGUOUS=1` for a terminal that draws `▰ ▱ ⚙ │ ↻` in one cell. **0.13.0**: `cadence orchestrate --status --watch` (a wave's table redrawn in the terminal, which
|
|
27
|
+
**1.0.0**: the first major release, a stable contract: the commands and options, the keys of `cadence.yaml`, the plan file and the names of the skills and agents follow semantic versioning, and the README's *Stability* section lists what is guaranteed and what is not (L164). **0.28.0**: `orchestrate.precheck: local` runs the « deliverable already present? » pre-check of `cadence orchestrate` on the local model (`claude-local`, Ollama), off the Anthropic quota, Sonnet staying the default and the fallback (L146); the `qa-reviewer` agent walks only the pages a lot touched, with a **Scope** line in its report (L123); the sub-tasks a review closes are also read in a lot's neighbouring repositories (L156) and a commit's sub-task list is read with the plan's left guard (L155); an unstable test is fixed (L90). **0.27.1**: `cadence orchestrate` no longer leaves a session's child processes alive: a dev server started in its own process group, or orphaned by a shell that exits (`nohup srv &`), is killed with the session through its process tree and its `CADENCE_SESSION` mark, a tmux server, `screen`, `gpg-agent` or `dirmngr` started on demand being spared; a session that exits with code 0 while a descendant holds its output open is no longer reported as « délai dépassé » (L83); the `ux-reviewer` and `qa-reviewer` agents carry the briefs' Playwright rule outside `cadence orchestrate` (L84). **0.27.0**: a compliant review of `cadence orchestrate` settles the open sub-tasks that commits of the lot cite (`feat(L3/t2): …`, comma lists included), closed in the same plan commit as the verdict or, on a read-only plan, returned as `[sous-tâche clore]` proposals, so `raf done` no longer refuses a lot over sub-tasks whose work is done; on a foreign-format plan the ids are read as the plan reads them (L82); the test suite stays green with two full suites running in parallel (L150). **0.26.0**: `cadence orchestrate` raises the floor of a lot's budget from 200 k to 450 k tokens, enough to pay a write pass, its review, one fix pass and the short review that follows, so a lot of 0.5 or 1 day is no longer handed back at its second or third pass; a `--continue` wave draws fewer lots as a result (L128). **0.25.0**: the project line of the plans' progress in the `cadence-hud` band names the lots in progress after their count, three at most then `…` (L153, `cadence-hud` 0.4.2); the progress follows the folder the session was launched from, so a `cd` into a sub-project no longer shrinks the band, and the session context keeps the `session.start` folder (L154). **0.24.0**: `cadence orchestrate --drop <project:lot>` and `--stop-after-current` steer a live wave from another terminal, `--resume --drop` removes a lot before replaying a stopped wave (L79); the final table of a wave says « livrable jusqu'à <sha> » for a ready lot stacked under a lot handed back, with `git push origin <sha>:main && cadence deliver --sha <sha>` (L76); a version commit (version fields, `CHANGELOG.md`, the README's « What's new ») no longer asks for a new code review (L72). **0.23.0**: the progress bar of each project in the `cadence-hud` band is coloured by the share of lots done, red below 33 %, orange below 66 %, green beyond (L152, `cadence-hud` 0.4.1); `cadence deliver` under a wave lock refuses only when a lot of the wave still works in that very repository, a linked worktree included (L127). **0.22.0**: `cadence orchestrate --continue` draws the next ready lot of the plan itself, in the declared priority, until the budget, the time window or a question stops it (L147); `cadence session context` prints the context of the session, which the `cadence-hud` band publishes, and the `lead` skill chains lots without the human while it stays under 60 % (L148); the band shows the progress of each project's plan, and `cadence lead tour --json` gains `progress` (L149, `cadence-hud` 0.4.0); the final review of a lot is always played, the lot's own budget bounding the writing passes only, with 65 k tokens reserved for it (L145); the cause « tests rouges après … » names the pass whose tests were red (L151). **0.21.0**: `cadence orchestrate --status` and `cadence lead tour` print the free session slots, which the `lead` skill reads (L141); `orchestrate.effort` sets the effort level of each pass of a wave (L137); `docs.sync` makes `raf check`, `lead tour` and the review brief report a document that does not follow the code (L143); `docs.articles` opens, after a green `cadence deliver`, a lot `Article <project> à rafraîchir` in the neighbouring repository (L144). **0.20.0**: `cadence-hud` no longer misleads once the work is over: `/hud cmd` lists the background commands it counts (id, tool, origin, age, name), any other argument answers `argument inconnu` instead of hiding the band, commands with no end seen for an hour or no record go to a grey `N cmd sans fin vue`, a finished subagent takes its commands with it, two races that left a `1 cmd` counted forever are closed, and a finished wave shows grey without its budget then disappears after 30 minutes (L142); the `qa-reviewer` and `ux-reviewer` agents are read-only for real, their frontmatter listing Read, Grep, Glob, Bash and the Playwright tools (L140). **0.19.1**: `cadence-hud`'s `N cmd` counter goes back down when a Monitor expires, when the background commands a subagent started end with it, and at session start or plugin reload (L135); its `tool.call` and `prompt.submit` guard hooks carry a `.catch` on registration, which clears the `claude plugin validate` warning (L136). **0.19.0**: `cadence orchestrate` lifts a repository's lock and `pre-push` guard as soon as every lot of the wave that touches it is finished, so `cadence deliver` works there while the wave continues elsewhere (L132); a review that leaves a repository dirty hands back that lot only, suspends the lots still queued in that repository and keeps the other repositories running, its verdict reported instead of lost (L133); `cadence session close` re-run without a `session start` keeps the window since the last opening (L131); `cadence-hud` draws the lot rows of a wave in aligned columns on every surface (L134). **0.18.0**: `cadence session next` prints what it records (issue #6); `cadence session close` covers the period since the last `session start` (or the last close) and says so in its title, `--since` still wins (issue #7). **0.17.0**: `cadence-hud` shows an `N cmd` segment next to the agents, the background commands of the session (Bash run in the background and Monitor, subagents' included), tracked from the tool results and ended by the task's notification or `TaskStop`. **0.16.0**: a lot can declare **neighbouring repositories** (`repos:` key of the lot, `{ path, cite }` for a repository shared between projects): `cadence orchestrate` refuses, locks and guards them, gives them to every session as `--add-dir`, and `raf commits`, `raf show` and the review verdict read the commits that cite the lot there too. **0.15.0**: `hook.autostart` in `cadence.yaml` (`warn` by default, `refuse` or `start`): a pre-commit hook that refuses, or starts in the same commit, a lot still todo that the commit cites; `cadence session start|close --all [--depth n]`, one section per project under a folder. **0.14.0**: `cadence lead tour`, the lead's morning table of every project in a folder, printed by a program (one line per project: lots in progress, gaps, last notes, next ready lot, repository) in place of one subagent per project; the `lead` skill calls it. **0.13.1**: `cadence-hud` takes back the segments that fit once a big one has dropped (reset times, then the per-model consumption, now short: `opus 1.2M sonnet 800k`); `CADENCE_HUD_AMBIGUOUS=1` for a terminal that draws `▰ ▱ ⚙ │ ↻` in one cell. **0.13.0**: `cadence orchestrate --status --watch` (a wave's table redrawn in the terminal, which
|
|
28
28
|
stops with the wave) and the lead skill that names the wave and follows its journal; lot ids with `/`
|
|
29
29
|
(sub-tasks of a read-only plan) accepted by `orchestrate`; `cadence-hud`'s first line measured in
|
|
30
30
|
terminal cells and kept to ASCII; `publish.yml` on the v5 actions. **0.12.0**: the `cadence-hud` plugin (a status band above the Claude Code prompt),
|
|
@@ -1083,8 +1083,7 @@ a time (above); `Agent`, `git push`, `cadence deliver`, `raf done|review|ux` are
|
|
|
1083
1083
|
temporary `pre-push` hook, installed for the duration of the wave and removed at its end (or, for a repository, as soon as all the lots that touch it are finished), refuses any push
|
|
1084
1084
|
from a session (`CADENCE_ORCHESTRATED` is in their environment; a repository that already has another
|
|
1085
1085
|
`pre-push` hook is refused before anything starts — `pushurl` is never touched); after every session the
|
|
1086
|
-
upstream ref and `git ls-remote` are compared with the "before", and a review that changed `HEAD` or the
|
|
1087
|
-
tree (screenshots left at the root, a commit) is an incident of that lot only: the lot is handed back to the lead, the lots of the other repositories carry on while the lots still queued behind it in the same repository are suspended (resumable, nothing started on a dirty tree), and a verdict the review had already produced is reported in the outcome and the lot's warnings (never recorded in the plan). A push or a removed guard hook still stops the wave. `raf done|ux|review` and `cadence deliver` refuse when
|
|
1086
|
+
upstream ref and `git ls-remote` are compared with the "before", and a read-only step that left only UNTRACKED files (screenshots written at the root by a script with a relative path) is not an incident: the files are moved to `.cadence/runs/<wave>/<project>--<lot>/stray/` (kept in their tree; for a neighbour repository, under `stray/<its relative path, `/` replaced by `_`>/`, e.g. `stray/.._gitops/`), a warning naming them goes to the lot's warnings and the wave log, the verdict is kept and the lot continues (L162; a move that is impossible, or an untracked file that vanished during the step, stays an incident); a review that changed `HEAD` or a tracked file (a commit, a modified file) is an incident of that lot only: the lot is handed back to the lead, the lots of the other repositories carry on while the lots still queued behind it in the same repository are suspended (resumable, nothing started on a dirty tree), and a verdict the review had already produced is reported in the outcome and the lot's warnings (never recorded in the plan). A push or a removed guard hook still stops the wave. `raf done|ux|review` and `cadence deliver` refuse when
|
|
1088
1087
|
`CADENCE_ORCHESTRATED` is set.
|
|
1089
1088
|
|
|
1090
1089
|
**Budget**: the wave counts input + cache writes + output tokens (default 2 M); cache reads are kept and
|
|
@@ -1375,13 +1374,15 @@ A version exists in three places and is published in two; a release does all of
|
|
|
1375
1374
|
in that commit, not before the review. A commit that touches any other file
|
|
1376
1375
|
— or another field of a manifest, or a dependency in `package-lock.json` — is work like any other and has
|
|
1377
1376
|
to be reviewed.
|
|
1378
|
-
2.
|
|
1377
|
+
2. Run the plugin evaluation, `npm run eval:plugin` (billed, see [Evaluating the plugin](#evaluating-the-plugin-before-a-release)),
|
|
1378
|
+
on that version commit; a case below the threshold stops the release until you have read it in the report.
|
|
1379
|
+
3. `git tag v<version> && git push origin main v<version>` — the tag starts `.github/workflows/publish.yml`,
|
|
1379
1380
|
which publishes to npm through Trusted Publishing (OIDC, no token stored anywhere): it checks the tag
|
|
1380
1381
|
matches `package.json`, `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json` and that `CHANGELOG.md` has a `## [x.y.z]` section for it (no section, no publication), then `npm publish --provenance`, where `prepublishOnly` runs the type-check and the
|
|
1381
1382
|
tests and `prepare` builds `dist/`; a red suite stops the publication; then it creates the GitHub release with that CHANGELOG section as its text. The trusted publisher is declared
|
|
1382
1383
|
once on npmjs.com (package settings → Trusted Publisher → GitHub Actions, `Sylad/cadence`, `publish.yml`).
|
|
1383
|
-
|
|
1384
|
-
|
|
1384
|
+
4. Watch the run: `gh run watch` (or `gh run list --workflow publish.yml`).
|
|
1385
|
+
5. Check the effect: `npm view @sylad/cadence version` answers the new version.
|
|
1385
1386
|
A run is safe to re-run, and two runs for one tag queue instead of racing (`concurrency` per ref, never cancelling the one that publishes):
|
|
1386
1387
|
a version already on npm skips `npm publish`, and a GitHub release that is missing is created (`--verify-tag`)
|
|
1387
1388
|
while an existing one is left alone. If the package is on npm but the release is still missing, re-run the job;
|
|
@@ -1390,7 +1391,61 @@ A version exists in three places and is published in two; a release does all of
|
|
|
1390
1391
|
The Claude Code plugin is read from the repository, so pushing `main` is what updates it; npm is what
|
|
1391
1392
|
`npx @sylad/cadence` and a global install read, and only the tag publishes there. A missing tag, or a red
|
|
1392
1393
|
publish run, leaves npm behind without any other error — 0.3.0 and 0.4.0 were never published — hence
|
|
1393
|
-
step
|
|
1394
|
+
step 5.
|
|
1395
|
+
|
|
1396
|
+
### Evaluating the plugin (before a release)
|
|
1397
|
+
|
|
1398
|
+
`evals/` holds an evaluation suite for the plugin itself, run by `claude plugin eval` (Claude Code 2.1.263 and later). Each case is
|
|
1399
|
+
a folder with a `prompt.md` and `graders/*.md`, and replays a trap found by hand: `raf-done-sans-revue` (`raf done` refused on a lot whose
|
|
1400
|
+
commits have no code review, with no `--force` to get round it), `deliver-sha` (`git push origin <sha>:main` then
|
|
1401
|
+
`cadence deliver --sha <sha>` for a pushed commit that is not `HEAD`), `orchestrate-id-avec-slash` (`maritime-atlas:Q4/accueil-4-ux12@haiku`
|
|
1402
|
+
keeps its `/`) and `livrer-sh-sans-bloc` (a `./livrer.sh` without a `deliver:` block in `cadence.yaml` is not
|
|
1403
|
+
run by `cadence deliver`). Every case also runs a **baseline without the plugin**, so the report gives the score of each arm and the
|
|
1404
|
+
difference: a trap the baseline already avoids proves nothing about the plugin. The cases are read-only questions (tools `Read`, `Glob`, `Grep`,
|
|
1405
|
+
`Skill`, no scaffold script), each graded by a regular expression and by a model-judged criterion.
|
|
1406
|
+
|
|
1407
|
+
```sh
|
|
1408
|
+
npm run eval:plugin # claude plugin eval . --max-cost-usd 5 --no-publish; 4 cases × 3 runs × 2 arms = 24 runs
|
|
1409
|
+
claude plugin eval . --case deliver-sha --runs 1 --trust-plugin --no-publish --max-cost-usd 1 # one case, one run (the first run asks to trust the plugin)
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
**Every run is billed** (a full `claude` session on your own credential, plus the judge): the suite is run at the release, once the
|
|
1413
|
+
version commit is ready, never in `npm test`, in `prepublishOnly` or in CI, and `--max-cost-usd 5` aborts a runaway. A case below
|
|
1414
|
+
the threshold (1.0 by default, `--threshold`) makes the command exit 1: read the case in the report before deciding it is the plugin
|
|
1415
|
+
and not the wording of the case. `npm test` only checks that the
|
|
1416
|
+
files are well formed (`test/plugin-evals.test.ts`); `evals/` is not part of the npm package.
|
|
1417
|
+
|
|
1418
|
+
## Stability
|
|
1419
|
+
|
|
1420
|
+
From 1.0.0 cadence follows [semantic versioning](https://semver.org): what is listed under
|
|
1421
|
+
*Guaranteed* only changes incompatibly in a major version; a minor or a patch never breaks it.
|
|
1422
|
+
|
|
1423
|
+
**Guaranteed**
|
|
1424
|
+
|
|
1425
|
+
- **Commands and options** of `raf` and `cadence` (`cadence raf …` is `raf …`): `init`, `add`, `start`, `done`, `drop`,
|
|
1426
|
+
`note`, `did`, `public`, `ux`, `review`, `commits`, `show`, `list`, `now`, `ignore`, `check`, `gantt`, `hook`;
|
|
1427
|
+
`cadence news`, `session` (`start`, `close`, `next`, `context`), `lead tour`, `deliver`, `verify`, `orchestrate`,
|
|
1428
|
+
`skills`; their options and exit codes as this README documents them. A new command or option is a minor change.
|
|
1429
|
+
- **The keys of `cadence.yaml`**: `plan`, `news`, `hook`, `session`, `deliver`, `orchestrate`, `docs`, `qa`, `priority`
|
|
1430
|
+
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.
|
|
1431
|
+
- **The plan file** (`docs/plan/raf.yaml`): `project`, `prefix`, `since`, `ignore`, `lots` (with their
|
|
1432
|
+
fields, `tasks`, `notes`, `repos`) and `acknowledged`, and the plan formats `plan:` in `cadence.yaml` can read.
|
|
1433
|
+
The `version` key is kept in the file but ignored (`src/plan.ts` does not read it): it is not a schema version.
|
|
1434
|
+
- **The names of the skills** (`session-start`, `session-close`, `deliver`, `lead`) **and of the agents**
|
|
1435
|
+
(`ux-reviewer`, `code-reviewer`, `qa-reviewer`).
|
|
1436
|
+
|
|
1437
|
+
**Not guaranteed** (may change in any release)
|
|
1438
|
+
|
|
1439
|
+
- The **text of the outputs**: wording, layout, order of the lines and columns of every report, table and message
|
|
1440
|
+
(the `--json` outputs and the exit codes are part of the commands above). In a `--json` output a new field is a
|
|
1441
|
+
minor change, so consumers must tolerate unknown fields; removing or renaming a field is a major change.
|
|
1442
|
+
- The **`precheck-reader`** agent: internal to `cadence orchestrate`, not meant to be called by hand.
|
|
1443
|
+
- The **`cadence-hud` band**: its segments, colours and layout; it is versioned on its own.
|
|
1444
|
+
- The **wave journals** under `.cadence/runs` and the files `cadence orchestrate` keeps there.
|
|
1445
|
+
- The wording of the briefs and of the instructions inside the skills and the agents.
|
|
1446
|
+
|
|
1447
|
+
There is no schema version and no migration: a file written by an earlier release keeps working, and a key a
|
|
1448
|
+
release adds is optional.
|
|
1394
1449
|
|
|
1395
1450
|
## License
|
|
1396
1451
|
|
|
@@ -2,7 +2,7 @@ import { execFileSync, spawn } from 'node:child_process';
|
|
|
2
2
|
import { toolBinOf, withoutLaunchVars } from './snapshot.js';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { dirname, join, relative } from 'node:path';
|
|
5
|
-
import { writeFileSync } from 'node:fs';
|
|
5
|
+
import { copyFileSync, existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
6
|
import { coveredTasks, lotCommits, lotWork } from '../audit.js';
|
|
7
7
|
import { readDocsConfig } from '../config.js';
|
|
8
8
|
import { docSyncBrief, docSyncGaps, filesOf } from '../docsync.js';
|
|
@@ -447,20 +447,91 @@ async function session(c, kind, local = false) {
|
|
|
447
447
|
w.saveWave();
|
|
448
448
|
return stop(c, 'failed', `incident : ${w.incident}`);
|
|
449
449
|
}
|
|
450
|
+
// Étape en lecture seule qui n'a laissé que des fichiers NON SUIVIS (captures d'un script en chemin relatif, L162) : déplacés dans le dossier du lot, avertissement, le verdict est gardé.
|
|
451
|
+
if (!write && b.head === a.head && b.tracked.join() === a.tracked.join() && b.untracked.join() !== a.untracked.join() && b.untracked.every((f) => a.untracked.includes(f))) {
|
|
452
|
+
const fresh = a.untracked.filter((f) => !b.untracked.includes(f)).map(unquotePath);
|
|
453
|
+
const stray = join(dir, 'stray');
|
|
454
|
+
const to = where ? join(stray, relative(l.repo, path).replace(/[\\/]/g, '_')) : stray;
|
|
455
|
+
const moved = moveStray(path, fresh, to);
|
|
456
|
+
if (moved.length) {
|
|
457
|
+
const text = `${kind} : ${moved.length} fichier(s) non suivi(s) laissé(s)${where} par une étape en lecture seule, déplacé(s) dans ${to} : ${moved.join(', ')}`;
|
|
458
|
+
c.lot.warnings.push(text);
|
|
459
|
+
w.log(`${lotKey(l.project, l.lot)} · ${text}`);
|
|
460
|
+
}
|
|
461
|
+
if (moved.length === fresh.length)
|
|
462
|
+
continue;
|
|
463
|
+
// Déplacement partiel : l'incident ne nomme que ce qui reste dans le dépôt.
|
|
464
|
+
return handBack(c, kind, res.structured, step, where, path, b, a, fresh.filter((f) => !moved.includes(f)));
|
|
465
|
+
}
|
|
450
466
|
if (!write && (b.head !== a.head || b.tracked.join() !== a.tracked.join() || b.untracked.join() !== a.untracked.join())) {
|
|
451
|
-
|
|
452
|
-
step.status = 'failed';
|
|
453
|
-
step.cause = `le dépôt${where} a changé pendant une revue`;
|
|
454
|
-
const traces = [...trackedPaths(a).filter((f) => !trackedPaths(b).includes(f)), ...a.untracked.filter((f) => !b.untracked.includes(f))];
|
|
455
|
-
(w.dirtyRepos ??= new Map()).set(path, lotKey(l.project, l.lot));
|
|
456
|
-
const what = traces.length ? ` (${traces.join(', ')})` : b.head !== a.head ? ' (commit)' : '';
|
|
457
|
-
return stop(c, 'handed-back', `incident : ${kind} de ${lotKey(l.project, l.lot)} a modifié le dépôt${where}${what}${lostVerdict(c, kind, res.structured)}`);
|
|
467
|
+
return handBack(c, kind, res.structured, step, where, path, b, a);
|
|
458
468
|
}
|
|
459
469
|
}
|
|
460
470
|
step.status = 'ok';
|
|
461
471
|
save(c);
|
|
462
472
|
return { step, report: res.structured, before, after, others };
|
|
463
473
|
}
|
|
474
|
+
/** Incident du LOT (L133) : la revue a laissé des traces (captures, commit), ni push ni garde supprimée — les lots des autres dépôts continuent. */
|
|
475
|
+
function handBack(c, kind, report, step, where, path, b, a, left) {
|
|
476
|
+
const l = c.lot;
|
|
477
|
+
const w = c.wave;
|
|
478
|
+
step.status = 'failed';
|
|
479
|
+
step.cause = `le dépôt${where} a changé pendant une revue`;
|
|
480
|
+
const traces = left ?? [...trackedPaths(a).filter((f) => !trackedPaths(b).includes(f)), ...a.untracked.filter((f) => !b.untracked.includes(f))];
|
|
481
|
+
(w.dirtyRepos ??= new Map()).set(path, lotKey(l.project, l.lot));
|
|
482
|
+
const what = traces.length ? ` (${traces.join(', ')})` : b.head !== a.head ? ' (commit)' : '';
|
|
483
|
+
return stop(c, 'handed-back', `incident : ${kind} de ${lotKey(l.project, l.lot)} a modifié le dépôt${where}${what}${lostVerdict(c, kind, report)}`);
|
|
484
|
+
}
|
|
485
|
+
/** Décode un chemin de `git status --porcelain` : entre guillemets, avec les échappements du C-quoting (`\303\251`, `\"`, `\\`, `\t`…). */
|
|
486
|
+
function unquotePath(f) {
|
|
487
|
+
if (!/^".*"$/s.test(f))
|
|
488
|
+
return f;
|
|
489
|
+
const bytes = [];
|
|
490
|
+
const esc = { a: 7, b: 8, t: 9, n: 10, v: 11, f: 12, r: 13 };
|
|
491
|
+
const body = f.slice(1, -1);
|
|
492
|
+
for (let i = 0; i < body.length; i++) {
|
|
493
|
+
const ch = body[i];
|
|
494
|
+
if (ch !== '\\') {
|
|
495
|
+
bytes.push(...Buffer.from(ch));
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const oct = /^[0-7]{3}/.exec(body.slice(i + 1));
|
|
499
|
+
if (oct) {
|
|
500
|
+
bytes.push(parseInt(oct[0], 8));
|
|
501
|
+
i += 3;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
const n = body[++i];
|
|
505
|
+
bytes.push(...(n in esc ? [esc[n]] : Buffer.from(n)));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return Buffer.from(bytes).toString('utf8');
|
|
509
|
+
}
|
|
510
|
+
/** Déplace des fichiers non suivis (chemins relatifs au dépôt, décodés) sous `to`, arborescence gardée ; renvoie ceux qui l'ont été (s'arrête au premier échec). */
|
|
511
|
+
function moveStray(repo, files, to) {
|
|
512
|
+
const moved = [];
|
|
513
|
+
for (const rel of files) {
|
|
514
|
+
try {
|
|
515
|
+
const from = join(repo, rel);
|
|
516
|
+
let dest = join(to, rel);
|
|
517
|
+
for (let i = 1; existsSync(dest); i++)
|
|
518
|
+
dest = join(to, `${rel}.${i}`);
|
|
519
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
520
|
+
try {
|
|
521
|
+
renameSync(from, dest);
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
copyFileSync(from, dest); // autre volume
|
|
525
|
+
rmSync(from);
|
|
526
|
+
}
|
|
527
|
+
moved.push(rel);
|
|
528
|
+
}
|
|
529
|
+
catch {
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return moved;
|
|
534
|
+
}
|
|
464
535
|
/** Verdict d'une revue interrompue par un incident du lot : rapporté au lead plutôt que perdu (L133). Rien n'est enregistré dans le plan. */
|
|
465
536
|
function lostVerdict(c, kind, report) {
|
|
466
537
|
if (kind === 'precheck')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylad/cadence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.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",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"build": "tsc -p tsconfig.build.json",
|
|
27
27
|
"test": "vitest run",
|
|
28
28
|
"typecheck": "tsc --noEmit",
|
|
29
|
+
"eval:plugin": "claude plugin eval . --max-cost-usd 5 --no-publish",
|
|
29
30
|
"prepare": "npm run build",
|
|
30
31
|
"prepublishOnly": "npm run typecheck && npm test"
|
|
31
32
|
},
|