npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sylad/cadence

v1.3.0

Published

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.

Readme

cadence

A small working method that lives in your repository. Solo developers and AI-assisted sessions lose track of what is left to do and what each commit was for; cadence keeps both in plain files next to the code.

Four tools:

  • raf (French reste à faire, "what is left to do"): the plan, linked to your commits;
  • news: a user-facing changelog with screenshots, tied to the plan;
  • session: the facts to start and to close a work session;
  • deliver: wait for the CI of the pushed commit, deploy, then verify the effect.

And four Claude Code skills that turn them into rituals — session-start, session-close, deliver, and lead to pilot several projects through subagents — plus three reviewer agents: ux-reviewer (no user-facing change is done before its usability review) and code-reviewer (no lot with commits is done before its code review), each behind an opt-in gate, and qa-reviewer, which walks the delivered app in a real browser and reports a page left empty or in error.

And a second plugin, cadence-hud: a status band above the Claude Code prompt that shows the session's context, quota, cost and the orchestrate waves running.

Spec step (design, first steps delivered)

For a visible lot that adds a page (screen: page; a component or a journey follows the ordinary path since L209), orchestrate gets a step before the code: a static HTML mockup in the project's own style (1440 and 390 px; empty, loading and error states) and a machine-readable screen sheet (fields, numbered rules R1… each marked sourced or hypothesis, actions → API calls). The ux-reviewer agent reads the mockup before any code; the acceptance tests are written from the sheet, red, by a session other than the implementer's; the implementation gets the sheet, the code review checks rule → test → green, the hypotheses go into the lot's news entry, and a human is asked only about a choice that is costly to undo. The design is in docs/superpowers/specs/2026-10-10-etape-spec-design.md (with an example sheet and the plan of the lots that deliver it). Delivered so far: the sheet templates templates/fiche.yaml (screen) and templates/fiche-back.yaml (a lot without a screen: sujet, cas, regles, contrats, donnees), cadence spec check (the sheet's invariants) and cadence spec qa (the screen's docs/qa/expectations.md section, derived from the sheet), see spec below, and the spec step of orchestrate itself (the spec-writer agent, the render at 1440 and 390 px, the commit of the spec), see The spec step in orchestrate; the accept step (red acceptance tests and the lock that protects them), see The accept step; the UX review of the mockup (the ux-reviewer agent in mockup mode), see The mockup review; and the downstream briefs (the sheet given to implement, rule → test → green in the code review, a light UX conformity pass, the ## Hypothèses section of the news entry), see The downstream briefs.

What's new

1.3.0: the spec step's threshold is narrowed to screen: page after the measure of 2026-10-12 (no row of the success table reached, about 570 k tokens per lot): component and journey lots follow the ordinary path again (L176, L209, L210); cadence spec reseal … --porteur <lot> re-seals the unstable sealed test of a delivered lot whose contract does not change (L212); the session cap shown and followed is the human's (CADENCE_MAX_SESSIONS, L208); cadence orchestrate --waits measures slot waits (L206); a third correction pass when the review converges (L205); "e2e": true acceptations stay out of the unit runner (L203); a dropped lot fully reverted no longer blocks npm run service (L207). 1.2.0: the spec step reaches lots without a screen: a lot that declares spec: true (raf add --spec, raf spec) gets a back-end sheet of cases, rules, contracts and data (cadence spec check), reviewed before any code, then acceptance tests locked before the implementation, and raf screen lets a plan already written adopt the step; orchestrate.accept may be a list of runners (front and back), each run under its own root; cadence spec reseal <lot> <test> --reason is the lead's way out when a sealed test contradicts its unchanged sheet; lead.ux: lead lets the lead form the UX verdict itself; cadence lead tour --depth n looks for projects deeper in the folder; the budgets of a wave are recalibrated on the measured runs (750 k for an ordinary lot of 0.5 to 1 day, 1.3 M for a lot with a screen); the code in service on the cadence repository is a separate worktree, updated only by npm run service, so a wave can no longer put unreviewed code in service (L185–L189, L191, L192, L194–L198, L200, L201). 1.1.0: the spec step of cadence orchestrate: a lot that declares screen: gets a machine-readable screen sheet (cadence spec check|qa), a mockup reviewed against it, acceptance tests committed red and locked before the implementation, and briefs that carry the sheet and rule → test → green to implement, fix and the code review; the news entry gains a Hypothèses section (L166, L169–L174, L177–L184). 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'à » 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 stops with the wave) and the lead skill that names the wave and follows its journal; lot ids with / (sub-tasks of a read-only plan) accepted by orchestrate; cadence-hud's first line measured in 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), raf ignore to acknowledge a commit without a lot, waves that size the review to the lot, hand back a lot whose deliverable is already there and start the app themselves for the UX review (orchestrate.ux), and briefs that require the README to follow the change. 0.11.0: waves on different repositories run side by side under a shared cap of simultaneous sessions (--max-sessions, 2 by default), and every release now has its changelog section and GitHub release. 0.10.1: the QA expectations file (docs/qa/expectations.md) is kept with the plan — a commit that only touches it no longer has to cite a lot. 0.10.0: cadence orchestrate sends an already-committed lot straight to review, and sessions only ask questions that name what they would change.

Every version, with what it brings and since when: CHANGELOG.md.

raf

  • The plan is a YAML file in the repo (docs/plan/raf.yaml), edited by the CLI. Your comments and hand edits are preserved.
  • A commit belongs to a lot when its message cites the id: feat(L3): …, fix: L3/t1 …. The link is computed from git log, never stored, so committing never dirties the plan. The id is read as a whole word: XL3, L3x and L3.4 do not cite L3, while L3. at the end of a sentence does. When the subject has a scope that cites lots (feat(L3): …, chore(L31,L32): …), the scope alone decides: a passing mention (page équipe (L27)), a range (L28–L31, L45 à L48) or the body does not count. Without a scope citing a lot, the whole message is read as before.
  • raf check audits drift between the plan and the history.
  • Plan upkeep needs no lot. A commit is plan upkeep when every file it 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 the --config file; a change to deliver:/session: is work), or a file the project lists under plan.files in cadence.yaml (a page it generates from the plan, a journal), or the QA expectations file (docs/qa/expectations.md, 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 does not count as work on the lots it cites: it is absent from raf commits, does not start a todo lot, does not make a code review stale, and cadence deliver does not announce the lots it cites as delivered. The files decide, never the subject: a chore(plan): … commit that touches a source file is a commit like any other.
  • raf gantt writes a single self-contained HTML page (no server, no CDN).
npm install -g @sylad/cadence    # or: npx -p @sylad/cadence raf …

raf init                          # docs/plan/raf.yaml + post-commit hook + .playwright-mcp/ in .gitignore
raf add "Monthly dedup on merge" --estimate 2
raf add "Typo in footer" --quickwin
raf add "Loan cache" --after L1
raf add "write the migration" --parent L1     # → L1/t1
raf start L1
git commit -m "feat(L1): dedup by calendar month"
raf now                           # in progress, next up (quickwins first), recently done
raf done L1/t1 && raf done L1
raf check                         # exit 1 on drift
raf gantt                         # docs/plan/gantt.html

Commands

| Command | Effect | |---|---| | raf init [--project name] [--prefix L] [--no-hook] | create the plan, install the hook, add .playwright-mcp/ to .gitignore | | raf add "title" [--estimate d] [--quickwin] [--visible [--screen page\|component\|journey]] [--spec] [--public "title"] [--after L2,L4] [--parent L3] | add a lot or a sub-task, print its id (--public: the lot's title in the public's words, written as public: right after title:; --screen: the screen a visible lot creates or changes — a new page, a new component or a modified journey — written as screen: right after visible:, shown by raf show; it requires --visible, and raf check reports a screen: without visible: or with another value as an error. Only page triggers the spec step of orchestrate (L209), never inferred from the title; --spec: the lot changes a behaviour (something a user, an API client or another service can observe), written as spec: true right after screen:, visible:, quickwin: or estimate: (whichever comes last), shown by raf show, refused on a sub-task. Also set by the lead at planning time, never inferred from the title) | | raf add "title" --every <days> · raf did <id> ["text"] | recurring lot: every (days) and last (last time done) fields; the lot stays todo, raf now and session start list it under « Récurrent » with « dû depuis N j » / « prochain dans N j »; raf did resets the count (spec: docs/superpowers/specs/2026-10-04-L11-tache-recurrente.md) | | raf screen <id> page\|component\|journey\|none | set, replace or remove screen: on an existing lot, to adopt the spec step on a plan already written (only page adopts the spec step; component and journey do not); refused on a lot without visible: (like raf add --screen) and on a sub-task; none removes the key and leaves visible: alone | | raf spec <id> on\|off | set or remove spec: on an existing lot (a lot that changes a behaviour, for the back-end spec step of cadence orchestrate, see The spec step of a lot with spec: true); refused on a sub-task; off is refused on a lot that has screen: page (the page implies the spec: remove screen: first) and on there only says so and changes nothing; on component and journey (ordinary path since L209) both work and set or remove the back-end spec:. raf spec only takes on/off: cadence spec check\|qa <fiche.yaml> keeps its own subcommands. raf check reports a spec: that is neither true nor false (spec : true ou false attendu) | | raf public <id> "title" · raf public <id> --clear | set, replace or remove the public title of a lot | | raf start <id> · raf done <id> [--force] · raf drop <id> [--reason text] | dated transitions (done refuses open sub-tasks unless --force) | | raf note <id> "text" | dated note — keep decisions next to the work | | raf show <id> [--notes] | one lot: status, dates, after, public title, dated notes in order, then the counted commits (as raf commits, neighbouring repositories included); --notes prints the notes alone | | raf commits <id> | the commits counted for a lot (the set the code review gate uses), one <sha> <subject> per line, oldest first; a lot that declares repos: (neighbouring repositories) gets, after them, one dépôt <path> : section per neighbour with the commits there that cite the lot and fall within the lot's period (started to finished) and, when the lot declares a cite: for that neighbour, contain that string (aucun commit du lot, or introuvable for a path that is not there) | | raf now | what to do next | | raf list [--status s] | flat list | | raf ignore <sha> \| "exact subject" [--reason text] | acknowledge a commit without a lot (tooling chore, a plan commit citing an unknown id) without rewriting history: a dated, reasoned line in the plan's acknowledged: section; a sha is exact, a subject covers every commit carrying it | | raf check --ignored | list the acknowledged commits with their date and reason | | raf check [--since date] [--idle 7] | since the plan's adoption date by default (a visible lot without a public title is only a ⚠ warning: it never changes the exit code): commits without a lot (commits touching only plan files, or only version fields, CHANGELOG.md and the README's « What's new » section, are exempt), unknown ids, todo lots that already have commits, idle lots, done lots with open sub-tasks, bad or circular dependencies | | raf gantt [-o file] | standalone Gantt page | | raf hook install | add the post-commit hook (read-only, never blocks) and the pre-commit hook (does nothing unless hook.autostart says so, see below) |

A commit on a lot that is still todo

By default the post-commit hook only warns (L1 est encore todo — raf start L1). The hook.autostart key of cadence.yaml changes what the pre-commit hook does when the message cites a todo lot (a recurring lot, a lot already doing, a commit without a lot and a plan-upkeep commit are never concerned):

hook:
  autostart: refuse   # warn (default) | refuse | start
  • warn: today's behaviour, the commit goes through, the post-commit hook warns.
  • refuse: the commit fails, loudly, with raf start <id> in the message (--no-verify still bypasses it).
  • start: the hook runs raf start <id> and stages the plan, so the plan change is in the same commit — no amend afterwards, no dirty plan left behind. Only when docs/plan/raf.yaml is identical to HEAD (no uncommitted change to the plan, staged or not): otherwise nothing is started, the hook says why (plan modifié non commité : démarrage automatique sauté, raf start <id> à la main) and behaves like warn.

The pre-commit hook reads the message from the command line of the git commit that runs it (-m, -am, --message, -F; Linux, through /proc) or from CADENCE_COMMIT_MESSAGE. When the message is not known yet (editor, --amend) it does nothing and the post-commit warning stays. Run raf hook install again to add the pre-commit block to an existing repository. A read-only plan is never started by the hook. Only a refusal (exit code 3 of raf hook pre-commit) stops a commit: an older or broken raf lets it through.

Global options: --file path or RAF_FILE; RAF_TODAY=YYYY-MM-DD to freeze the date. cadence raf … is the same as raf ….

Plan file

version: 1
project: my-app
prefix: L
since: 2026-09-28     # commits before this date are not audited
ignore: ['^chore\(batch\):']   # optional: subjects of automated commits, neither audited nor counted for a lot
lots:
  - id: L1
    title: Monthly dedup on merge
    status: doing        # todo | doing | done | dropped
    estimate: 2          # working days
    quickwin: false
    visible: true        # user-facing: a news entry is expected when done
    after: [L0]
    created: 2026-09-28
    started: 2026-09-29
    notes:
      - { date: 2026-09-29, text: "keep the bank line when both sources exist" }
    tasks:
      - { id: t1, title: write the migration, status: done }

A plan elsewhere, or in another format

cadence.yaml, at the repository root, can say where the plan is. The short form keeps raf's own format, and the plan stays writable:

plan: planning/todo.yaml

A project that publishes its plan (a JSON generated from it and committed with it) declares that file, so a commit touching only the plan and its published copy is plan upkeep; the plan keeps raf's format and stays writable:

plan:
  files: [frontend/public/plan-data/plan.json]

Until the file is declared, such a commit is reported as a "commit without a lot" when it cites none, and counts as work on the lots it cites.

A project that already keeps its plan with its own tool is read without migrating it: describe the file, and raf now, raf list, raf commits, raf check, raf gantt and cadence session start|close work on it. Such a plan is read-only — raf add|start|done|note|ux|review refuse and leave the file to the project's tool, and the two review gates do not apply to it (a uxSince or reviewSince written in it is ignored).

plan:
  path: docs/plan/taches.yaml
  project: my-app               # what the file does not say itself: project, since, ignore
  since: 2026-10-02
  ignore: ['^plan: ']
  files: [docs/plan/journal.ndjson]   # kept with the plan: a commit touching only these is a plan commit
  lots: taches                  # root key holding the list (default: lots)
  fields:                       # raf field: key in the file (a list = first one present)
    title: titre
    public: titre_public        # the lot's title for the public; read when it is text, else ignored
    screen: ecran               # page, component or journey; only on a visible lot (only `page` enters the spec step)
    spec: fiche                 # true or false: the lot changes a behaviour
    status: etat
    estimate: effort
    created: cree_le
    started: demarre_le
    finished: [livre_le, ferme_le]
    notes: note
    repos: depots               # the lot's neighbouring repositories (a list of paths and/or { path, cite }, or one of them)
    parent: parent
  statuses:                     # raf status: their states
    todo: [prevu, specifie]
    doing: [en_cours, teste]
    done: [deploye, valide]
    dropped: caduc
  estimates: { S: 0.5, M: 1, L: 3 }   # their effort labels, in working days
  • Fields: id, title, status, estimate, quickwin, visible, screen, spec, public, every, last, after, created, started, finished, notes, repos, parent; one left out is read under its own name. A timestamp counts for its day; a note written as plain text is one note.
  • spec is the exception to « a left-out field is read under its own name »: a read-only plan's spec: is often the path of a spec document, so it is read only when plan.fields.spec maps it, and then only true counts (any other value is neither an error nor a spec step). Natively, spec: stays true or false (L198).
  • parent: an entry B33/t1-fusion whose parent is B33 becomes the sub-task t1-fusion of B33.
  • Ids need no prefix: a commit belongs to a lot when its message cites one of the plan's ids as a whole word (E-A2, NC2.4, B33/t1-fusion). An id that is not in the plan cannot be told from ordinary text: such a commit counts as "without a lot", never as an unknown reference.
  • A state missing from statuses, or an effort label missing from estimates, is reported by raf check.

Gantt scheduling

One lane of work. Finished lots use their real dates (or their commits' dates); lots in progress run until max(start + estimate, today); lots to do follow in file order, after their dependencies, on working days.

news

What changed for the user, one entry per visible lot, each with a screenshot.

  • Mark a lot as user-facing with raf add … --visible (or visible: true in the YAML). raf done and the post-commit hook remind you to write its entry; raf check fails while a visible lot is done without one.
  • An entry is a Markdown file in docs/nouveautes/, with a YAML header. Screenshots are files you take yourself, stored next to the entries.
  • cadence news build writes nouveautes.json for your app to display, a self-contained index.html, and copies the screenshots.
raf add "Amounts like 3.000 read as three thousand" --visible   # → L8
raf start L8 && git commit -m "fix(L8): thousands separator" && raf done L8
cadence news new L8          # docs/nouveautes/2026-09-29-amounts-like-3-000-….md (title: the lot's public title if any)
# add docs/nouveautes/captures/l8.png, list it under `captures:`, write the text
cadence news check           # exit 1 on drift (also part of raf check)
cadence news build -o frontend/public/nouveautes
---
title: Amounts like 3.000 read as three thousand
date: 2026-09-29
created: 2026-09-29T14:32+02:00
lots: [L8]
captures: [captures/l8.png]
# nocapture: reason, when a screenshot makes no sense
---
Imported statements now read **3.000** as three thousand, not three.

A screenshot can say what it shows: write it as { file, alt } instead of a bare path, one text per screenshot.

captures:
  - { file: captures/l8-before.png, alt: "Statement total read as 3 instead of 3,000" }
  - captures/l8-after.png          # a bare path still works: no alternative text

| Command | Effect | |---|---| | cadence news new <lot…> [--title t] | entry skeleton, dated and timed now (date, created), titled after the lot's public title (public), else its title; --title wins | | cadence news list | entries, newest first (see Order below) | | cadence news check | visible lots done without entry, unknown lots, missing or undeclared screenshots, entries without creation time, bad headers, a lot whose screen sheet has hypotheses and whose entry lacks the ## Hypothèses section (or a line for one of them) | | cadence news stamp | migration: writes created: into entries without one (or with an empty one), from the author date of the commit that added the file under its current name (now if not committed yet) | | cadence news build [-o dir] | nouveautes.json + index.html + screenshots (default docs/nouveautes/site) |

--dir changes the entries folder (default docs/nouveautes at the git root). The Markdown is deliberately small: headings (# to ######, rendered one level lower, <h2> to <h6>; a heading takes its own line and cuts the paragraph or list around it), paragraphs, - lists (a bullet may continue on the next lines, indented), **bold**, `code`, [links](url); everything else is escaped text. The JSON holds { project, generated, entries: [{ slug, title, date, lots, captures, html }] }, with screenshot paths relative to the JSON file. captures is always a list of paths; an entry that gives at least one alternative text also carries alts, the texts in the same order ("" for a screenshot without one) — an entry without any keeps exactly the shape above. The built page puts the text in the image's alt, and falls back to "Capture : ".

Order. Everywhere (list, build, the JSON), entries are strictly newest first: by date, then, on the same day, by creation time. Every entry carries it to the minute in its created header, which cadence news new writes as local time with an explicit offset (2026-09-29T14:32+02:00, or Z). The offset is required, so the order does not depend on the machine's time zone: news check (and raf check) flags a value without it, an impossible one, an empty created:, and an entry without created. cadence news stamp fills it in older entries (or replaces an empty one) from the author date of the commit that added the file under its current name; until then, that date is used for sorting (an entry not committed yet counts as the newest). Renames are not followed: a renamed entry counts as added on the day of the rename, so stamp it before renaming it. The file name only breaks the remaining ties, since it follows the title, not the chronology. The page and the JSON still show the day only.

UX review

raf ux enable                      # from today, a --visible lot needs a UX review before done
raf ux L4 "compliant after 2 fixes"   # record the verdict (from the ux-reviewer agent)
raf ux L8 "no screen: calculation fix"

With the rule on, raf done refuses a visible lot without a review (--force to override) and raf check reports visible lots finished after the uxSince day without one. An empty verdict is refused, and one left empty or blank by hand in the YAML counts as no review. Plans without uxSince are not affected.

Code review

raf review enable                  # from today, a lot with commits needs a code review before done
raf commits L4                     # what there is to review: the commits the gate counts for the lot
raf review L4 "compliant after 2 fixes"   # record the verdict (from the code-reviewer agent)

The counterpart of the UX review, off by default. With the rule on, raf done refuses a lot that has at least one commit citing it and no recorded verdict (--force to override), and raf check reports such lots finished after the reviewSince day. A lot with no commit has nothing to review; neither does a lot whose only commits touch plan files alone (the plan, a file listed under plan.files, or the QA expectations file), are version commits (see Releasing), predate the plan's since or match an ignore: pattern — raf commits <id> prints exactly the counted set.

The verdict is tied to what was reviewed: raf review stores it on the lot with the sha of the lot's latest counted commit (review: { date, verdict, commit }, commit: null when the lot had none). A counted commit made after that one makes the review stale: raf done refuses (--force to override), and raf check reports a finished lot, until the lot is reviewed again and raf review is rerun. A verdict written by hand without a commit field is not checked for staleness. An empty verdict is refused, and one left empty or blank by hand in the YAML counts as no review. Plans without reviewSince are not affected.

A lot that works in neighbouring repositories (repos:, see orchestrate) has its verdict tied to each of them: raf review also stores, under repos, the sha of the latest counted commit of the lot in each neighbour (the one raf commits lists last: it cites the lot, falls within the lot's period and contains the cite: string if any) (review: { date, verdict, commit, repos: { ../aetherwx-gitops: <sha> } }, null when a neighbour has none), and refuses to record when a listed repository cannot be read. raf check does not audit the neighbours: their staleness is not checked, only the project's own commits are.

Documentation that follows the code

A stale README misleads. cadence.yaml can declare which documents must follow which code:

docs:
  sync:
    - paths: [src/**, bin/*.js]        # what changes…
      docs: [README.md, docs/usage.md]  # …must change one of these, in the same lot
    - paths: [templates/]
      docs: [CLAUDE.md]
    - paths: [frontend/src/**, '!**/*.test.tsx']   # a pattern starting with ! leaves files out
      docs: [README.md]
  since: 2026-10-09                    # optional: also audit lots finished on or after this day

A pattern is an exact path, with * (inside a folder), ** (across folders), ?, or a trailing / for a whole folder. raf check takes the work commits of every lot in progress (plan-only commits and version commits do not count — so a "What's new" line written in the version commit is not documentation; a lot finished on or after docs.since is audited too) and, for each pair, reports the lot when those commits touch paths without any of them touching a file of docs:

✗ L7 : documentation en retard — src/a.ts, src/b.ts sans toucher README.md ou docs/usage.md (docs.sync)

It counts as drift: exit code 1, shown in the dérive column of cadence lead tour, and the review brief of cadence orchestrate (review and review-small) carries the list the program computed, to be reported as one major finding per line — a stale document is not left to the reviewer's memory of a sentence. Touching a document is what the program checks; whether its text is true is still the review's reading. The commits of neighbouring repositories (repos:) are not audited.

An article elsewhere that follows the delivery

A case study that lives in a neighbouring repository (the project's article on a showcase site) goes stale the same way. docs.articles names it:

docs:
  articles:
    - repo: ../claude-code-codex          # neighbouring repository, relative to the project
      file: src/pages/cas/demo.md         # the article in it
      name: demo                          # optional: the name in the lot title (default: the plan's project)

After a green cadence deliver, for the lots it lists as delivered that have a public title (public:, or the title of their Nouveautés entry), cadence opens in this repository's plan the lot Article demo à rafraîchir (todo, estimate 0.5, repos: [{ path: ../claude-code-codex, cite: demo }]), with a note that quotes the delivery, the public titles delivered, the article and the sections to read again: the headings (# lines, or <h1>…<h6> of an Astro page) that share a word of five letters or more with a delivered title, else all the ## headings. The lot is then played like any other, in an ordinary wave (cadence orchestrate: an implementer rewrites the article in the neighbouring repository, the review checks it against the note). If a lot of that title is still open, it receives a note instead of a duplicate. Two entries with the same name (the same project told in two neighbouring repositories) share one lot, which declares both repositories in repos:. Internal lots (no public title) open nothing; a red delivery opens nothing; cadence deliver --dry-run announces the articles. The plan is written but not committed (deliver never commits): the line article à rafraîchir : L9 … says so. With a plan kept by another tool (read-only), the same line carries the instruction to open the lot with that tool. This replaces the manual sync-site-docs skill.

QA review

No gate and no command here: the QA review comes after a delivery, and raf done does not wait for it. It follows any delivery that changes what a page shows or what it is served (screen, API, data source, configuration of either) — in practice every delivery except docs-, plan- or tests-only ones: a backend-only lot can empty a page without touching a screen, and the agent then walks the pages that call the changed endpoints. It also walks the pages that consume the services the lot changed; when the diff changes no route and no screen, or cannot be linked to pages, it walks every page and says so. The qa-reviewer agent opens each page of the running app in a real browser and judges it from the user's side. A page can be empty while everything else is green — no code changed, a data source went down upstream, the unit tests replace the network, /api/health answers ok, and the "nothing found" on screen is the message the code was written to show.

The agent cannot tell such an empty state from a normal one by itself: the project says what each page must show, in docs/qa/expectations.md — one ## <route> section per page, three kinds of lines:

# QA expectations

## *
- shows: the header and the navigation links
- never: "Loading failed", "Too Many Requests"
- api: /api/live/current — may be empty when no match is within 24 hours

## /players
- shows: the squad of the last match — at least 11 players
- shows: the season statistics table, 8 columns — 1440 only
- never: "No recent line-up found"
- api: /api/squad/last — a non-empty list

## /fixtures/:id   (the first match linked from /fixtures)
- shows: both team names, the date, the score once the match is played
- never: "Unknown match"
- api: /api/fixtures/:id
  • shows: — content that must be present and non-empty, with a count where one exists;
  • never: — texts that must not appear: error messages, and empty-state messages that mean missing data;
  • api: — the calls the page depends on: each must answer 2xx with a non-empty body (a 200 with [], {} or null is a failure, unless its line says may be empty when …).

An optional ## * section holds what every page must show, never show and call. A line may end with a condition in plain words, which the agent honours: may be empty when …, 1440 only, 390 only (a line without a width holds at both). Content hidden on the phone by design is not a defect unless a shows: line requires it at 390; content pushed outside the visible area (it needs a sideways scroll) is reported as suspect and handed to ux-reviewer in one line.

The rest is free text, written for a reader: a line can be repeated, and a route with a parameter names a real value to visit or says where to find one. The file can live elsewhere:

# cadence.yaml
qa:
  expectations: docs/quality/pages.md

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 the routes it discovers and still runs its universal checks: an error shown, a failed API call whose content is missing on screen, a broken or missing content image are defects with or without a file; an empty 2xx body, like whatever else would need an expectation to judge, is suspect at most (it may be a normal absence). For a route with a parameter, it finds a real value in the app's links or its API responses and says how it built the URL. The agent then returns a draft for you to correct — it never writes the file itself.

session

cadence session start              # notes from the last close, lots in progress (silent ones flagged),
                                   # work done since yesterday by lot, drift, repo state, 3 proposals
cadence session start --since "3 days ago" --idle 2
cadence session close              # commits by lot since the last `session start`,
                                   # commits without a lot, lots in progress with no commit in that window,
                                   # drift, uncommitted / unpushed work; exit 1 while something is still open
cadence session close --since "2 days ago"   # --since overrides the window
cadence session start --all        # from a folder that is not a repository: one "## project" section per
cadence session close --all --depth 2   # sub-folder holding a plan (--depth n: n levels down, 1 by default);
                                   # exit = the worst code of the projects (2 error, 1 open, 0 closed)
cadence session next "finish L3" "review L4"    # shown by the next session start; replaces the previous notes
cadence session next --clear       # erase those notes, on purpose
cadence session context            # `ctx 42 % (84000/200000)`: the context of YOUR session, as the cadence-hud band publishes
                                   # it: the one named by $CLAUDE_CODE_SESSION_ID (set in Claude Code's Bash); --session <id>
                                   # overrides it; with no variable, the folder decides only if a single session publishes for it
                                   # (two sessions in one folder = exit 2, "pass --session"); exit 2 too when no fresh figure
                                   # (band not loaded) = treat as at the threshold

session start records its time in the worktree's state (session.json, under the git dir); the next session close takes it as its window and names it in the section title: Commits de la période (depuis l'ouverture de 09:12) (with the date when it is not today). A close never moves the boundary, refused or not: run again without a session start in between (the session-close skill does, after fixing a point), it still reports since that opening instead of an empty period. With no recorded opening, or an unreadable file, the window is the current day, said as such (depuis 2026-09-28 00:00); --since is quoted as given. (A session.json written by 0.18.0 after a close, depuis la clôture de …, is still read.)

On success cadence session next prints what it recorded (2 lignes enregistrées pour la prochaine ouverture, then the lines as written, blank ones dropped), so session-close can report them without running session start again.

--all runs the same command inside each project found under the current folder — docs/plan/raf.yaml, or a cadence.yaml with plan: — and never enters a project to look for another. --since and --idle go to every project; an error in one project is printed in its section and the others still run. It replaces the shell loop over the sub-folders. A project that is not the root of its own git repository gets a ✗ line (and exit 2) instead of the parent repository's plan. --all and --depth are refused outside session (--depth is also read by cadence lead tour), and --all refuses --file, --config and RAF_FILE: each project reads its own plan. For a one-line-per-project overview, cadence lead tour.

cadence session next without a line refuses (exit 2) and leaves the notes of the last close as they are — it used to erase them silently; erasing is --clear.

Proposals come from the plan only: lots in progress, then ready lots (dependencies done), quick wins first. Local state lives in the git directory, never committed: the close notes per worktree, the delivery lock and log in .git/cadence/, shared by all the worktrees of a clone.

A project that already has its own morning and evening scripts keeps them: name them in cadence.yaml and their output is added to the report, under "Faits propres au projet", before the proposals (start) or the verdict (close).

session:
  start: ./scripts/morning.sh "$CADENCE_SINCE"    # sh, at the repo root, 120 s at most
  close: ./scripts/evening.sh "$CADENCE_SINCE"
session:
  clean: [ "~/projects/tmp/*", "tmp/*", "test-output-*" ]   # working files to propose for removal at close
  cleanDays: 7                                              # older than this many days (default 7)

cadence session close then lists, under "Nettoyage proposé", the entries matching these patterns (* stands for part of a name and never crosses a /; ~ is the home folder, a relative pattern starts at the repo root) that were not modified for more than cleanDays days, oldest first — a folder's age is that of the most recent entry it contains, at any depth, and an entry's age runs from the later of its modification time and its status-change time (ctime): what tar xf, cp -a or rsync -a just brought in with an old modification time is not old.

An entry is proposed only if it could be measured entirely. Never proposed:

  • a git repository, a folder that contains one at any depth, and anything under a .git folder;
  • a git directory without a .git entry — a bare repository (git init --bare, git clone --mirror, backup.git/), a --separate-git-dir, a worktree's admin folder —, recognised by its content as git does (HEAD with objects and refs, or HEAD with commondir): neither it, nor anything inside it, nor a folder that contains one;
  • anything git tracks, in any repository that has a .git entry above it. One limit, stated plainly: a bare repository driven with an external work tree (git --git-dir=~/.dotfiles --work-tree=~) leaves no .git beside the files, so its tracked files cannot be recognised and can be proposed — never point a pattern at such a work tree;
  • a name starting with . unless the pattern itself starts that name with . (as in the shell, tmp/* does not match tmp/.env; tmp/.cache-* does);
  • anything reached through a symbolic link matched by a * segment: unlike the shell, the walk never descends into such a link (tmp/*/* lists nothing behind tmp/link → ../outside), and a folder's age never looks behind a link; the link itself can be proposed, as a link — removing it leaves its target alone. Only segments written in full before the first * follow a link, as cd would (~/shared/* with ~/shared a link lists the target's entries); once a * has been crossed no link is followed any more, even on a segment written in full (tmp/*/out/* with tmp/run1/out → /elsewhere lists nothing behind out);
  • anything that could not be read entirely: a folder that cannot be listed — nor anything inside it, even named in full —, an entry that cannot be examined or vanished during the walk, an entry under a .git that git could not answer for. These are listed apart, under "Nettoyage : N élément(s) illisible(s), jamais proposé(s)";
  • the repository itself, or a folder that contains it.

The command deletes nothing, never fails on the cleanup and does not change the exit code: the session-close skill shows the list and removes the entries only after the human agrees.

The commands get CADENCE_SINCE and CADENCE_TODAY. CADENCE_SINCE is the --since as given; on session close without --since it is the instant of the last session start as an ISO 8601 UTC string (2026-09-28T07:12:00.000Z), or <today> 00:00 when no mark exists. The commands add facts and decide nothing: a failing command is reported and changes neither the exit code nor the verdict.

lead tour

cadence lead tour [folder] [--idle 3] [--depth n] [--json] is the lead's morning table, with no model in the loop. From a parent folder (default: the current one) it takes every direct sub-folder that holds docs/plan/raf.yaml, or a cadence.yaml with a plan: key (a plan kept by another tool included), and for each one reads the same facts as cadence session start — in-process, no sub-process, no call to claude — then prints one line per project:

alpha · en cours L1 (silencieux 4j) · dérive 1 (✗ L2 a 1 commit(s) mais est encore todo) · notes : finir L1 puis livrer · prochain L3 Typo · dépôt non commité, non poussé
beta · en cours rien · dérive aucune · notes : aucune · prochain T2 Second · dépôt propre

| Column | Content | |---|---| | en cours | ids of the lots in progress; (aucune activité) when it has none at all, (silencieux Nj) when the lot's last activity (commit, note or start) is more than --idle days old (default 3) | | dérive | the gaps raf check reports (a lot whose commits leave a document behind, docs.sync, included), counted and the first two shown | | notes | the notes left by the last session close, joined with / and cut at 120 characters | | prochain | the first ready lot (quick wins first) — id and title cut at 60 characters | | dépôt | non commité (modified or untracked files), non poussé (commits ahead of the upstream), livraison en cours (live delivery lock); propre otherwise |

When an orchestrated wave is live, one more line closes the tour (not part of --json): vagues en cours : 1 · sessions en cours : 0 · créneaux libres : 1 sur 2 — the lead reads the free slots there instead of computing them (L141). The sur M is the cap the human set (CADENCE_MAX_SESSIONS, 2 by default), not a fixed 2 (L208); an invalid value falls back to 2 in this display.

--json prints the same content as an array of objects (project, doing, drift, notes, next, repo, and error when the project could not be read), plus progress — { done, doing, todo, added7 }, the lots done / in progress / to do (dropped and recurring lots left out) and those created in the last 7 days (L149), which cadence-hud draws as one progress bar per project. Projects come in the order of the priority: key of the folder's cadence.yaml (the one orchestrate --continue reads), the others after, alphabetically; an unreadable cadence.yaml or a priority: that is not a list of names (priority: ol, priority: []) keeps the alphabetical order and adds a first row cadence.yaml · ✗ erreur : <cause> (project: "cadence.yaml" with error in --json). Sub-projects are looked for one level down by default. --depth n goes n levels down (same meaning as in cadence session start --all --depth n: a folder that is a project is not entered; an integer ≥ 1, otherwise refused), and the depth: key of the folder's cadence.yaml, next to priority:, sets it once for all (depth: 2); --depth wins over the key, and a depth: that is not an integer ≥ 1 adds the same first row cadence.yaml · ✗ erreur : … and leaves one level read. cadence-hud asks the tour from the folder of the session, so it follows the key with no change. A project is named by its folder's name; one found below the first level is named by its path from the folder (groupe/gamma), unique and ready for cadence orchestrate groupe/gamma:L1 run from there, while priority: and the alphabetical order read the folder's own name. "Created in the last 7 days" means today and the six days before. When the folder has no sub-project but is itself a project, the tour is that project's single line (this is what cadence-hud asks from a session opened inside a project). The tour is read-only: it changes no plan. It exits 0 even when a project is in error — that project's line reads beta · ✗ erreur : <cause>. The lead skill runs it instead of one subagent per project.

corrections

cadence corrections [folder] [--classify [--model m]] [--cache file] [--sample n] [--until date] [--json] measures how often a wave has to correct itself. From the folder a wave was launched from (default: the current one) it reads the journals <folder>/.cadence/runs/<wave>/<project>--<lot>.json of every project and prints:

  • the lots with a correction: of the lots that went through a successful write session, the share with at least one and at least two fix passes, per project and for visible versus non-visible lots (61 % and 29 % of 263 lots in the 10-10 baseline);
  • the passes by cause, in total, per project and per visibility (visible lots apart from the others, with the share of spec-floue, regle-inventee and test-manquant among each one's passes — the reference an evolution aimed at screens is compared to is that of the visible lots, not « all lots »: 16 % of 45 passes against 31 % overall in the 10-10 baseline). The cause of a pass is read from the findings of the review that precedes it, among: unclear spec or missing criterion (spec-floue), invented business rule (regle-inventee), architecture drift (derive-architecture), data / SQL (donnees-sql), code bug (bug-code), missing test (test-manquant), false review finding (faux-constat). One cause per pass: the root of what forced the fix. A pass with no review finding to read (triggered by red tests right after the write session) is counted apart as sans-constat and never sent to the model.

Without --classify the command calls no model: the passes not yet classified count as non classé. With --classify each pass is classified by a local model served by Ollama (OLLAMA_HOST, default http://localhost:11434; --model, default mistral-small3.2:24b; temperature 0, output constrained to the seven causes), off the Anthropic quota. The classification is cached in <folder>/.cadence/corrections.json (or --cache file): a re-run classifies only the new passes, and a pass the model could not answer stays out of the cache. --sample n prints n passes with findings spread evenly over the sorted keys, with their findings and the model's cause, to be read by hand; a manual field written beside a pass in the cache file (one of the seven causes, else the command refuses the file) overrides the model's cause in every table and gives the agreement rate of the model on the reviewed sample. --until date (ISO, e.g. 2026-10-10T18:31Z) reads only the waves created up to then (created in the wave's wave.json) and, in them, only the steps ended by then (ended), so that neither later waves nor a wave still running at that time change a published baseline; a wave or a step with no readable date is left out. --json prints the measure as data. The command reads the journals and writes nothing but the cache. The first baseline is in docs/mesures/corrections-2026-10-10.md; test/corrections-mesure.test.ts replays its command on the journals and checks the document's results word for word (skipped when the journals are not there). The measure of the screen-sheet step against the visible lots of this baseline (L176: 8 lots with a sheet, verdict per row of the success table, token cost beside the passes avoided) is in docs/mesures/etape-spec-2026-10-12.md; the target of its third row is ≤ 8 %, half the 16 % reference.

spec

cadence spec check <fiche.yaml> checks a sheet (fiche.yaml, the machine-readable output of the spec step): a screen sheet (a lot with screen:), a back-end sheet (a lot that changes a behaviour without a screen) or a mixed sheet with both parts. The screen part is designed in docs/superpowers/specs/2026-10-10-etape-spec-design.md). Start from the template templates/fiche.yaml (it passes the check as is), a worked example sits next to the design. It prints one ✗ … line per violation and exits 0 when the sheet is valid, 1 with violations, 2 when the file is missing, is not YAML or the usage is wrong. The invariants:

  • ecran has id, titre, route (starts with /), lot, maquette, and largeurs: [1440, 390]; etats holds at least vide, chargement, erreur, none twice;
  • champs[]: a unique kebab-case id (it is also the mockup's data-testid), libelle, type, source, and a boolean obligatoire;
  • regles[] are numbered R1, R2… in order with no gap; each has an etat that is one of etats, an enonce, a message (the exact text on screen, or null), a statut — sourcee with the source it comes from, or hypothese with the si_faux that says what would change — and at least one planned test, named Rn — …;
  • masque (optional, only on a rule of state vide) lists the fields the rule makes disappear: every id must be a field of the sheet and none may be one the rule shows in champs;
  • actions[] are numbered A1… in order, their declencheur is a field id (or null for the page load), their appel reads VERB /path, and the regles they carry exist.

Back-end sheet (L187; design: docs/superpowers/specs/2026-10-11-etape-spec-back-design.md). Start from templates/fiche-back.yaml (it passes the check as is); a worked example, a real lot of ol-companion, sits next to the design. The sheet has sujet instead of ecran (no etats, champs, mockup), and the same regles. The back-end invariants, one ✗ … line each:

  • sujet has id (kebab-case, the folder name docs/specs/<id>/), titre, lot, and a perimetre with modules, endpoints and jobs (lists of texts, at least one entry in the three) and an optional hors_perimetre;
  • cas[] are numbered C1, C2… with no gap, each with titre, acteur, declencheur, preconditions, nominal (a non-empty list of steps), resultat, and alternatives — a list of { si, alors, regles }, each citing at least one existing rule — or alternatives: [] with its reason in sans_alternative; every case is cited by at least one rule;
  • a back-end rule has cas (at least one existing case id) instead of etat, plus the screen rule's enonce, statut (sourcee + source, or hypothese + si_faux) and tests named Rn — …; message, champs and masque only make sense on a rule attached to a state;
  • contrats[] are numbered A1… with no gap, each with an appel (VERB /path, job <name> or commande <name>), entree, sortie, erreurs — a list of { code, message } where code is HTTP <n> or log debug|log|warn|warning|error|info|critical|fatal|verbose and message the exact text — or erreurs: [] with its reason in sans_erreur, and the regles it carries exist;
  • donnees[] are numbered D1…, each with objet, note, a boolean existant and a modification (aucune, ajout or changement); an existing object that is modified carries the decision of the lead (the answer to the targeted question). The list may be empty ([]: the lot touches no data).

Mixed sheet (a lot with screen: and a back-end part): sujet and ecran side by side, with the same id (the folder name) and the same lot. A rule is attached to a state (etat, a screen rule) or to cases (cas), at least one of the two. The A ids are shared: the actions of the screen and the contrats are numbered A1…An with no gap over the whole, and an action that calls an API already listed in contrats points to it with contrat: Ax instead of repeating its appel. An action that has its own appel equal to the appel of a contract is reported (it must point to the contract). A sheet without ecran refuses actions, etats and champs (they only make sense with a screen part), and a rule with etat there gets that single report. A sheet that only has ecran stays valid as it is (no migration); sujet is required as soon as the lot is back-end or mixed.

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.

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 (unchanged) sheet (of a delivered lot: add --porteur <lot in progress>), see The accept step.

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 (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 error output, nothing on the standard one, and exits 1. The derivation is built so that the planned empty and error states are never reported as defects:

  • ## <route> is the screen's route;
  • shows: — one line per mandatory field (its libelle), except the fields that belong to a state (listed in champs of a rule of state vide or erreur, like the empty-state message); the line of a field listed in the masque of a sourcee rule of state vide ends with — unless <the rule's statement>; the other fields (a static title) never get one;
  • never: — the messages of the rules of state erreur, on one line; the message of a rule of state vide is a planned display and never goes there;
  • api: — the load actions only (declencheur: null) in GET (the QA pass never sends a POST); the line ends with may be empty when <the rule's statement> when a sourcee rule of state vide carries the action. Actions triggered by a field stay with the acceptance tests.

spec qa only derives the screen part: a back-end sheet, which has no ecran, is refused (exit 2, « la fiche n'a pas de partie écran »); on a mixed sheet it reads the actions' calls through their contrat.

deliver

A delivery is done when its checks pass, not when a tool says "success".

# cadence.yaml, at the repository root
deliver:
  ci: github              # github | none | { command: "…" }   (default: none)
  ciTimeout: 1800         # seconds
  deploy:                 # sh commands, in order, at the repo root
    - ./scripts/deploy.sh "$CADENCE_SHORT"
  verify:                 # at least one; retried every 10 s until verifyTimeout
    - url: https://app.example.com/api/health
      status: 200         # default 200
    - url: https://app.example.com/version.txt
      contains: "${SHORT}"
    - command: kubectl rollout status deploy/app --timeout=60s
  verifyTimeout: 300
  deployTimeout: 1800     # seconds, per deploy command

A public title (public:) longer than the site's Plan page accepts fails its build. Declare the limit in cadence.yaml and it is caught before the push:

news:
  publicTitleMax: 80      # characte