zz-meridian 0.16.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +6 -6
  2. package/dist/context.js +2 -1
  3. package/dist/create.js +1 -1
  4. package/dist/migrations.js +10 -2
  5. package/package.json +1 -1
  6. package/payload/AGENTS.md +1 -1
  7. package/payload/CHANGELOG.md +36 -0
  8. package/payload/CONTRIBUTING.md +15 -11
  9. package/payload/README.md +16 -14
  10. package/payload/app/(dashboard)/(home)/README.md +2 -0
  11. package/payload/app/(dashboard)/business/README.md +1 -1
  12. package/payload/docs/benchmark.md +1 -1
  13. package/payload/docs/distribution.md +12 -10
  14. package/payload/docs/start-a-dashboard.md +4 -3
  15. package/payload/docs/testing.md +134 -0
  16. package/payload/package.json +1 -1
  17. package/payload/scripts/ask.ts +77 -27
  18. package/payload/scripts/brand.ts +1 -1
  19. package/payload/scripts/gate.ts +13 -6
  20. package/payload/scripts/interactions.ts +19 -3
  21. package/payload/scripts/lib/budgets.ts +22 -1
  22. package/payload/scripts/lib/coverage.ts +23 -6
  23. package/payload/scripts/lib/journey.ts +9 -3
  24. package/payload/scripts/lib/routes.ts +37 -10
  25. package/payload/scripts/navigate.ts +13 -13
  26. package/payload/scripts/perf.ts +3 -3
  27. package/payload/scripts/verify.config.ts +1 -1
  28. package/payload/scripts/verify.ts +42 -27
  29. package/payload/skills/zz-meridian/SKILL.md +28 -23
  30. package/payload/skills/zz-meridian/references/customize.md +18 -18
  31. package/payload/skills/zz-meridian/references/existing-project.md +14 -14
  32. package/payload/skills/zz-meridian/references/live.md +3 -1
  33. package/payload/skills/zz-meridian/references/standard.md +25 -23
  34. package/payload/skills/zz-meridian/references/update.md +18 -14
  35. package/payload/skills/zz-meridian/references/validation.md +53 -42
  36. package/payload/src/components/patterns/featured-metric/README.md +5 -2
  37. package/payload/src/components/patterns/featured-metric/index.tsx +34 -11
  38. package/payload/src/components/patterns/featured-metric/preview.tsx +9 -0
  39. package/payload/src/lib/assistant/prompt.ts +15 -3
  40. package/payload/src/lib/assistant/respond.ts +1 -1
  41. package/payload/src/system/card-stage.tsx +2 -2
  42. package/payload/src/views/home.tsx +3 -0
  43. package/payload/src/views/revenue.tsx +3 -0
  44. package/payload/tests/assistant-prompt-writes.test.ts +69 -0
  45. package/payload/tests/assistant-withheld.test.ts +1 -1
  46. package/payload/tests/featured-metric.test.tsx +72 -0
  47. package/payload/tests/verify-suite-outcome.test.ts +4 -4
  48. package/payload/tests/view-tool-modules.test.ts +50 -0
package/README.md CHANGED
@@ -13,11 +13,11 @@ words at the end:
13
13
 
14
14
  In Claude Code or Codex, send it as a goal (`/goal Run npx zz-meridian@latest skill --global, then …`): the agent keeps
15
15
  working until the condition holds, and a separate model checks it after every turn against the hand-over, which quotes
16
- its evidence. The sentence says what you want; the skill carries how good it has to be. It builds, renders every view
17
- at phone and desktop width in both themes, scores each one on a scoreboard of floors (measured by `pnpm verify --full`)
18
- and craft (judged on the renders), and improves it round by round until nothing is weak, deciding what it can find out
19
- and recording each decision it made for you. You get the finished product, the renders, and the few questions only you
20
- can answer.
16
+ its evidence. The sentence says what you want; the skill carries how good it has to be. It builds, renders every view at
17
+ phone and desktop width in both themes, scores each one on a scoreboard of floors (measured by
18
+ `pnpm verify --full --timed`) and craft (judged on the renders), and improves it round by round until nothing is weak,
19
+ deciding what it can find out and recording each decision it made for you. You get the finished product, the renders,
20
+ and the few questions only you can answer.
21
21
 
22
22
  The skill picks the command from what you said and what is in the folder, and names it before running anything:
23
23
  `adopt` to change this Next.js App Router project in place ("make this admin panel look professional"), `create` for a
@@ -80,7 +80,7 @@ styles) and `src/app.config.ts` for the new flags and records them in the manife
80
80
  refuses when one of those outputs was edited, while an update is open, or on a dirty tree (unless `--allow-dirty`). Run
81
81
  it with the version the manifest records. The project's own `pnpm brand` still works, but its changes count as your edits.
82
82
 
83
- Requires Node 22.18 or newer. The default `pnpm verify` runs without Google Chrome and reports the browser checks as not run; `--full` and `--perf` need it.
83
+ Requires Node 22.18 or newer. The default `pnpm verify` runs without Google Chrome and reports the browser checks as not run; `--full`, `--timed` and `--perf` need it.
84
84
 
85
85
  ## What this package does not do
86
86
 
package/dist/context.js CHANGED
@@ -19,7 +19,8 @@ When it is absent, use the skill's brief template with the person's answers; do
19
19
  upgrade: resolve the report and run the pinned finalization command.
20
20
  - Before finishing: safely configured \`pnpm verify\` for the default gate/build/smoke path; report any partial coverage.
21
21
  Use \`pnpm gate\` alone for static feedback. Do not automatically chain it before verify, which already runs it.
22
- Use \`pnpm verify --full\` or \`pnpm verify --perf\` when deep checks are requested; release CI runs both.
22
+ Use \`pnpm verify --full\` for the exhaustive checks and \`pnpm verify --timed\` for the timed ones (it needs a quiet
23
+ machine); \`--full --timed\` is the whole standard. Weekly CI runs \`--full --timed --perf\`.
23
24
  - Agents in the product read through authorized collections and write only through an approved Proposal.
24
25
  ${END}`;
25
26
  /** The brief a team keeps at `docs/brief.md`; its guidance lines equal `BRIEF_GUIDANCE` in the assistant prompt. */
package/dist/create.js CHANGED
@@ -77,7 +77,7 @@ ${name} is a new Meridian ${VERSION} dashboard in ${root}.
77
77
  skill: .agents/skills/zz-meridian (Codex) and .claude/skills/zz-meridian (Claude Code)
78
78
 
79
79
  Next: follow .agents/skills/zz-meridian/SKILL.md from step 3's brief (the template is fetched and branded): the brief,
80
- step 4's colour check, then data and the pages, then the loop in references/standard.md until ${pm} run verify --full ends with "the project meets the
80
+ step 4's colour check, then data and the pages, then the loop in references/standard.md until ${pm} run verify --full --timed ends with "the project meets the
81
81
  Meridian standard" and no view on the scoreboard is Weak.`);
82
82
  return 0;
83
83
  }
@@ -235,8 +235,8 @@ export const RELEASE_MIGRATIONS = [
235
235
  id: 'verify-modes',
236
236
  since: '0.5.0',
237
237
  applies: removedVerifyFlags,
238
- summary: 'verify has three modes, and --quick, --no-vitals and --extra are gone.',
239
- instructions: 'Read "Validation" in references/validation.md. Run `pnpm verify` for the bounded default (the gate once, one build, the size checks and a smoke of up to three routes), `pnpm verify --full` for every exhaustive suite and `pnpm verify --perf` for the 20-sample navigation protocol, and replace the removed flags in the script, workflow or shell file. Read the coverage line each run ends with: it says what ran and what did not.',
238
+ summary: 'verify has four modes, and --quick, --no-vitals and --extra are gone.',
239
+ instructions: 'Read "Validation" in references/validation.md. Run `pnpm verify` for the bounded default (the gate once, one build, the size checks and a smoke of up to three routes), `pnpm verify --full` for every exhaustive suite, `pnpm verify --timed` for the timed ones (on a quiet machine) and `pnpm verify --perf` for the 20-sample navigation protocol, and replace the removed flags in the script, workflow or shell file. Read the coverage line each run ends with: it says what ran and what did not.',
240
240
  checks: ['gate'],
241
241
  },
242
242
  {
@@ -454,6 +454,14 @@ export const RELEASE_MIGRATIONS = [
454
454
  instructions: "Replace `clock().toISOString().slice(0, 10)` with `dayOf(clock())` (`dayOf` in `src/lib/format-date.ts`) in each file listed: the prepared query's `today` in `app/api/assistant/route.ts`, and `readDays`'s today in `src/data/metrics.ts`. The prompt has told the model today in `app.timezone` all along, while the prepared query and the page cut their periods at the UTC date, so east of UTC after local midnight (west of UTC in the evening) the model's today and its `7d` window's end were a day apart. A day row is kept under its day in `app.timezone`, so a file of your own that compares an instant's day with one — `now.slice(0, 10)`, `at.slice(0, 10)` — asks `dayOf` too. A product whose `app.timezone` is UTC sees no change. A project that kept the sample's views brings `src/views/now-parts.ts`, `src/views/revenue-parts.ts`, `src/views/customer-context.ts`, `src/views/customer.tsx` and `src/views/members.tsx` over from the release.",
455
455
  checks: ['gate'],
456
456
  },
457
+ {
458
+ id: 'verify-tiers',
459
+ since: '0.16.4',
460
+ applies: (root) => matching(root, (rel, src) => importsOf(rel, src).some(({ from, names }) => from === 'scripts/lib/coverage' && names.includes('finalOutcome')) && /(?<![\w.$])finalOutcome\(\s*(?!\{)/.test(src)),
461
+ summary: '`finalOutcome` in `scripts/lib/coverage.ts` takes `{ full, timed }` as its first argument, because verify has a timed tier apart from the full one.',
462
+ instructions: 'Bring `tests/verify-suite-outcome.test.ts` over from the release as it has it. A file of your own that calls `finalOutcome` passes the tiers, `finalOutcome({ full, timed }, notRun)`, where it passed whether the run was full (`finalOutcome(true, notRun)`): `references/validation.md` describes the tiers, `pnpm verify`, `--full` and `--timed`.',
463
+ checks: ['gate'],
464
+ },
457
465
  ];
458
466
  /**
459
467
  * A test of the rail that stands over a fake `ResizeObserver` answering `observe` with nothing (0.16.2). The lookbehind
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-meridian",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "description": "Bring ZZ Meridian, a dashboard design system, into a Next.js project, or start a new dashboard on it. Copies the files in; nothing depends on this package at runtime.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/payload/AGENTS.md CHANGED
@@ -15,6 +15,6 @@ Meridian is a dashboard design system and template: read `README.md` (what it is
15
15
  - **Build up, never sideways.** A page arranges patterns; a pattern composes components; a value is a token. Never write a colour, size or shadow that is not a token; `node scripts/check.ts` fails on literal colours and on Tailwind utilities outside Meridian's reset scales (they render nothing).
16
16
  - **Tokens** live in `tokens/*.tokens.json` (DTCG 2025.10). `src/styles/tokens.css` and `src/styles/theme.css` are generated: run `pnpm tokens`, never edit them.
17
17
  - **A card** is a folder with `index.tsx`, `preview.tsx` (every state, static, labelled) and `README.md` (the anatomy in CONTRIBUTING). After adding one, run `pnpm registry`.
18
- - **Before finishing**: `pnpm verify` (the gate once, one build, the size checks and a smoke of up to three routes; it ends with a coverage line that says what ran and what did not). For a change to shared components, the shell or data, run `pnpm verify --full`: every route, every control pressed and every link followed, the keyboard walk, the assistant and the live data. Then look at `node scripts/shot.ts <route> --width 1440,390 --theme dark,light`. `pnpm gate` alone is the fast loop while editing.
18
+ - **Before finishing**: `pnpm verify` (the gate once, one build, the size checks and a smoke of up to three routes; it ends with a coverage line that says what ran and what did not). For a change to shared components, the shell or data, run `pnpm verify --full`: every route, every control pressed and every link followed, the keyboard walk, the assistant and the product's own checks; it judges no time. `pnpm verify --full --timed` adds the navigation budgets, the live data and Web Vitals on a quiet machine, and is the whole standard. Then look at `node scripts/shot.ts <route> --width 1440,390 --theme dark,light`. `pnpm gate` alone is the fast loop while editing. `docs/testing.md` is the testing standard: its `## When each runs` says which command runs at which moment, and its `## Rules` say when a red is fixed and when a limit may move.
19
19
  - **Agents in the product** read freely and write only through a Proposal; mark agent work with the Agent mark and "via". See `docs/agents.md`.
20
20
  - Everything written to the repository is in English.
@@ -2,6 +2,42 @@
2
2
 
3
3
  Every release of ZZ Meridian, newest first. Versions follow semver: a removed or renamed token, prop or card is major; a new card, token or variant is minor; a corrected value is a patch. Each entry says what breaks and what to do instead.
4
4
 
5
+ ## [0.17.0] · 2026-10-18
6
+
7
+ ### Breaking
8
+
9
+ - **`pnpm verify --full` no longer judges a time.** It runs the complete suites: every rail route navigated for its function, the audit, every control pressed and link followed, the keyboard walk, the assistant and the product's own browser checks. The navigation budgets, the live-data suite and Web Vitals moved to the new `pnpm verify --timed`, which runs them alone and last and needs a quiet machine. Only `pnpm verify --full --timed` can end `the project meets the Meridian standard`; a `--full` run with nothing else unrun ends `every check that ran passed; not run: timing, live, vitals; pnpm verify --timed runs them`. A script, workflow or agent prompt that ran `--full` for the whole standard runs `--full --timed` instead; weekly CI runs `pnpm verify --full --timed --perf`. `docs/testing.md` classifies every check by size, type and tier.
10
+ - **`pnpm verify` and `--full` no longer fail a route for being slow.** Their navigation (`scripts/navigate.ts --timing off`) judges whether each route's shell, data and first interaction arrive, never how fast; `pnpm verify --timed` judges the same journeys against `budgets.navigation`. A project that relied on the default smoke to catch a slow route runs `pnpm verify --timed` on a quiet machine.
11
+ - **`pnpm gate` no longer runs `tests/deep-*`**, the browser-fixture tests of the checks, which only this repository has. Its closing line names the command that does: `pnpm exec vitest run tests/deep- --no-file-parallelism`.
12
+ - **The coverage line has new words.** Its depth can be `timed`, `full+timed` or `full+timed+perf`, and its `not run:` list can name `timing`. A script that matched `coverage: full;` for a complete run matches `coverage: full+timed;` and `not run: none`.
13
+ - **`finalOutcome` (`scripts/lib/coverage.ts`) takes `{ full, timed }` in place of a boolean.** A project's own copy of `tests/verify-suite-outcome.test.ts` from before the tiers calls `finalOutcome(true, …)` and failed its update's gate at the type check; the update now reports it as the `verify-tiers` migration. Bring `tests/verify-suite-outcome.test.ts` over from the release, and call `finalOutcome({ full, timed }, notRun)` in a file of your own.
14
+
15
+ ### New
16
+
17
+ - **A Featured metric can say its period has no number** ([#45](https://github.com/zhixuan312/zz-meridian/issues/45)). `FeaturedFigure.value` is `number | null`, and `points` takes `null` entries. With no number, the figure's line holds `missing`, the caller's words ("No words recorded", "Unavailable"; "Not recorded" by default), a size down and at the figure's own height, so a period without a number draws the same card as one with. A pointed point that is `null` reads the same way. Before this, the only ways through were a false `0`, or `NaN` with a `format` that turned it into a word; a card that did the second can pass `null` and `missing` now.
18
+
19
+ ### Changed
20
+
21
+ - **`docs/testing.md` is the testing standard.** Above its classification it says what kinds of test there are (size and type), the tiers that combine them, when each runs (while editing, before a commit, before finishing and handing over a change, before a release, when a given kind of change is made, and on a schedule), and the rules a red, a limit and a release follow, with the practice they rest on. A project is advised to follow it and Meridian follows it. `AGENTS.md`, `CONTRIBUTING.md`, `docs/distribution.md` and `references/validation.md` point to it instead of keeping their own account, and `tests/cli-testing-standard.test.ts` keeps its shape.
22
+
23
+ ### Fixed
24
+
25
+ - **The assistant is told it changes nothing where it is handed no write tool** ([#46](https://github.com/zhixuan312/zz-meridian/issues/46)). The system prompt always said "Your write tools change what this page shows…" and that the person approves each Proposal, even on a page whose collections the assistant may only read, where no write tool is built and no Proposal can follow. `respond` now tells `systemPrompt` whether it handed a write tool (`Audience.writes`). Without one, the prompt says the assistant changes nothing and, asked for any change, gives the page that makes it, or says it is not a change the person can make. With one, it keeps both lines. The shared guidance that no change exists unless a write tool returned one stays in both.
26
+ - **`ask` writes one transcript a page and can ask a detail page on the record you name** ([#47](https://github.com/zhixuan312/zz-meridian/issues/47)). A page with no view tool was named by its folder, a detail page by the folder before its `[id]`, so `/things` and `/things/[id]` both wrote `things.md` and the second overwrote the first. A page without a tool is now named by its whole route (`/things/[id]` is `things-id`), two views that would share a name are told apart by their routes, and a name still shared is refused before anything is asked. `--view` takes the assistant's tool name as well (`view_usage`), and `--path <address>` asks the page that renders that address on that record, rather than on the first row its listing draws, which may be a closed one. The view names from tools defined in modules the registry imports came with #43. A transcript of a page without a tool has a new file name: compare the new file with the old one, under its new name.
27
+ - **Pointing at a Featured metric's chart moves nothing below the figure** ([#44](https://github.com/zhixuan312/zz-meridian/issues/44)). A figure given `points` and no `delta` drew the row under the figure only while a point was pointed at, so the card grew by the point pill's row as the pointer crossed its chart and shrank when it left: measured at 390 px, 380 → 426 px, and a control below moved 46 px with it, enough for a press aimed at it to land on something else. The row is now drawn at rest as a line of its own, holding the pill's place empty, so the card stays 426 px either way however long the point's name.
28
+ - **The gate and `ask` find a view tool kept in a module of its own** ([#43](https://github.com/zhixuan312/zz-meridian/issues/43)). `viewToolPages()` (`scripts/lib/routes.ts`) read the `defineViewTool` calls written in `src/views/tools.ts` alone, so a project that keeps each tool in a module the registry imports got an empty list. `scripts/check.ts` then asked no page for its `### What the agent does here` — green by absence — and `scripts/ask.ts` named every view by its page's folder, never by its tool. It now reads every project module the registry reaches through its imports, by a relative path or a tsconfig alias, an `index` included. A project whose tools sat in such modules can find pages failing `check.ts` that passed before: each is a page a tool names whose specification lacks the part, and the fix is that part.
29
+ - **Home and Revenue are live pages.** Each body registers the collections its feature needs with `useLive`, so `/` and `/business` resync on open, on reconnect and on the safety poll, and `pnpm verify --perf` finds an after-live start for every Home and Business route again: since the rails became per workspace, `/members`, `/customers`, `/usage` and `/requests` had none (`no other rail route registers live data to start from`).
30
+ - **A link probe is pressed once the destination's data is measured.** The navigation measured the data and pressed the probe side by side, so on a route whose link is ready before its ready text (`/business`, while its title still loaded) the press could leave before the data counted, and a sample failed `main is there but never shows`. A dead link is still reported.
31
+ - **Switching an Atlas card's theme no longer drops a frame on a phone.** The stage's specimen sections are skipped by style and layout until they near the viewport (`content-visibility: auto`), so the Theme, Accent, Density and Width switches restyle only what is on screen; a chart pattern's specimen holds thousands of elements, and the switch cost one frame of 100 to 160 ms on a mid-range phone.
32
+ - **`pnpm verify --perf` holds a route to the budget its navigation check carries**, as `pnpm verify --timed` already did. A route's own figure (`budget` on its check in `verify.config.ts`) now stands in for the shared warm figure in both suites, through one function (`limitsFor` in `scripts/lib/budgets.ts`), so the two no longer judge one route against two bars: the start guide's phone warm shell is reported against its own 300 ms, not the shared 250 ms. Cold and after-live keep the shared figures.
33
+ - **The Atlas's Contributing page no longer links to a page that answers 404.** It linked `docs/testing.md` relatively, which resolved to `/system/system/docs/testing.md`; the file is now named in backticks, as every other Atlas document names a file, and `tests/cli-atlas-doc-links.test.ts` keeps every document the Atlas serves free of relative links.
34
+
35
+ ### Known issues
36
+
37
+ - **`node scripts/audit.ts --atlas` reports overlaps on the Atlas pattern pages whose specimen renders with `content-visibility: auto`** (featured metric, metric tile, column chart, heatmap and the records template). The pages draw nothing overlapping: the audit measures sections the browser has not laid out yet. Fixed in the next release. `pnpm verify` and its tiers are not affected.
38
+ - **`node scripts/vitals.ts --atlas` can drop one frame on a page specification's look control** (`/system/pages/revenue`, one run in three), where the stage restyles its console and phone frames in one tap. Fixed in the next release. `pnpm verify --timed` is not affected.
39
+
40
+
5
41
  ## [0.16.3] · 2026-10-17
6
42
 
7
43
  ### Breaking
@@ -82,25 +82,29 @@ Write the status on the line under the title: `Status: beta`.
82
82
 
83
83
  ## Tools
84
84
 
85
+ `docs/testing.md` is the testing standard: the kinds of test, the tiers that combine them, when each runs, the rules a red, a limit and a release follow, and each check's size, type, tier and cost. The When column below uses its moments.
86
+
85
87
  | Command | What it does | When |
86
88
  |---|---|---|
87
- | `pnpm gate` | Every check below, in order | Before every commit |
89
+ | `pnpm gate` | Every check below, in order; the deep tests (`tests/deep-*`) are left out | Before every commit |
88
90
  | `pnpm tokens` | Generates the token CSS and the Tailwind bridge from `tokens/` (`--check` only compares) | After any token change |
89
91
  | `node scripts/contrast.ts [--all]` | Every specified foreground on its background, in every theme and accent, and the chart palette's colour-vision checks | Gate |
90
92
  | `node scripts/registry.ts` | Regenerates the Atlas registry from the card folders (`--check` only compares) | After adding or renaming a card |
91
93
  | `rm -rf .next` before the next build, after editing a card's `README.md` | A production build in a working `.next` can serve the prerendered copy of the old text while the client hydrates the new one, which React reports as error #418 on that Atlas page. `pnpm dev` has no such copy, and a fresh clone (CI, a deployment) has none either | After changing what a card's page says |
92
94
  | `node scripts/check.ts` | Every card has its README and preview, every README follows the anatomy, no literal colour in components, every token a spec names exists and is written with its own value, every embed view a document offers is a route, an element that claims a control's role has a key handler, and the specification of every page with a view tool has its **What the agent does here**, with its Protagonist line and three questions in their form | Gate |
93
95
  | `pnpm typecheck`, `pnpm test` | TypeScript and the behaviour tests | Gate |
94
- | `node scripts/audit.ts` | With the app running: every page and embed view at 2560, 1440, 1024, 768 and 390px in both themes (embeds at 720 and 420 on a simulated host ground). It measures inside open shadow roots too. Fails on sideways scroll, text clipped without an ellipsis, text cut with an ellipsis that keeps the whole of it in no `title`, two pieces of text drawn in the same place or a stroked line drawn through one, a control with no accessible name, more than one page scroller, rendered text under its WCAG minimum (an SVG's text too, read by its `fill` against what the SVG paints behind it), a Tab stop with no focus ring (a change of outline, shadow or border when focus arrives), a custom element it cannot look inside (`unmeasured:`), and any uncaught exception or console error. Prints the design metrics: type sizes, weights, radii and the hierarchy ratio | Before release |
95
- | `node scripts/audit.ts --atlas` | With the app running: the same audit over the Design Atlas, at 1440 and 390px in both themes: every card's page and its bare preview, and every other entry the Atlas serves (the token views, the page specs, the guides). Its first line says what it reached, by kind, and `--list` prints the routes without opening a browser. Not part of `pnpm verify`, which stays fast: a card's preview is where a defect in its own box shows, so run it after changing a component | After changing a component |
96
- | `node scripts/vitals.ts [--atlas]` | With a production build running: LCP, INP, CLS and dropped frames on a mid-range phone for every static page, the Atlas's front page and one card page among them. `--atlas` measures every Atlas entry instead, about fifteen minutes, and is not part of `pnpm verify` | Inside `pnpm verify --full`; `--atlas` after changing what every card page shares |
97
- | `node scripts/interactions.ts` | With the app running: presses every control on every page (mouse at 1440px, taps at 390px) and follows every link. Reaches controls inside open shadow roots. Fails on a control that changes nothing, one something covers, and a link that answers 4xx | Before release |
98
- | `node scripts/keyboard.ts` | With the app running: Tab through each page until focus comes back round, then Shift+Tab back. Fails on a stop with no focus ring, a control hidden under the sticky bar, a visible control never reached, and a reverse order that differs | Before release |
96
+ | `node scripts/audit.ts` | With the app running: every page and embed view at 2560, 1440, 1024, 768 and 390px in both themes (embeds at 720 and 420 on a simulated host ground). It measures inside open shadow roots too. Fails on sideways scroll, text clipped without an ellipsis, text cut with an ellipsis that keeps the whole of it in no `title`, two pieces of text drawn in the same place or a stroked line drawn through one, a control with no accessible name, more than one page scroller, rendered text under its WCAG minimum (an SVG's text too, read by its `fill` against what the SVG paints behind it), a Tab stop with no focus ring (a change of outline, shadow or border when focus arrives), a custom element it cannot look inside (`unmeasured:`), and any uncaught exception or console error. Prints the design metrics: type sizes, weights, radii and the hierarchy ratio | Inside `pnpm verify --full` |
97
+ | `node scripts/audit.ts --atlas` | With the app running: the same audit over the Design Atlas, at 1440 and 390px in both themes: every card's page and its bare preview, and every other entry the Atlas serves (the token views, the page specs, the guides). Its first line says what it reached, by kind, and `--list` prints the routes without opening a browser. Not part of `pnpm verify`, which stays fast: a card's preview is where a defect in its own box shows, so run it after changing a component | After a change to a card, a pattern or the Atlas |
98
+ | `node scripts/vitals.ts [--atlas]` | With a production build running: LCP, INP, CLS and dropped frames on a mid-range phone for every static page, the Atlas's front page and one card page among them. `--atlas` measures every Atlas entry instead, about fifteen minutes, and is not part of `pnpm verify` | Inside `pnpm verify --timed`; `--atlas` after a change to a card, a pattern or the Atlas, on a quiet machine |
99
+ | `node scripts/interactions.ts` | With the app running: presses every control on every page (mouse at 1440px, taps at 390px) and follows every link. Reaches controls inside open shadow roots. Fails on a control that changes nothing, one something covers, and a link that answers 4xx | Inside `pnpm verify --full` |
100
+ | `node scripts/keyboard.ts` | With the app running: Tab through each page until focus comes back round, then Shift+Tab back. Fails on a stop with no focus ring, a control hidden under the sticky bar, a visible control never reached, and a reverse order that differs | Inside `pnpm verify --full` |
101
+ | `pnpm exec vitest run tests/deep- --no-file-parallelism` | The browser-fixture tests of the check scripts: each planted defect is found and each corrected twin passes | After a change to a check script, beside `pnpm verify --full`; Meridian's release runs it |
99
102
  | `pnpm verify` | The gate once, one production build, the route policy, the size checks and a smoke of up to three routes on desktop and the phone drawer; ends with a coverage line, and the report is in `out/verify.txt` | Before finishing any change |
100
- | `pnpm verify --full` | The navigation of every rail route, then every exhaustive suite: the audit, every control pressed and link followed, the keyboard walk, the assistant, the live data and Web Vitals | After a change to shared components, the shell or data, and before release |
101
- | `pnpm verify --perf` | The 20-sample navigation protocol; combine with `--full` to run both without repeating the gate or the build | When timing is what changed |
103
+ | `pnpm verify --full` | The navigation of every rail route, then every exhaustive suite: the audit, every control pressed and link followed, the keyboard walk, the assistant and the product's own browser journeys; it judges no time | After a change to shared components, the shell or data, and before a change is handed over |
104
+ | `pnpm verify --timed` | The navigation of every rail route with its budgets judged, then the live data and Web Vitals, alone and last. It needs a quiet machine. `pnpm verify --full --timed` is the whole standard, the only run that can end "the project meets the Meridian standard" | After a change that could move a time; with `--full`, before a release of the product, at the release commit |
105
+ | `pnpm verify --perf` | The 20-sample navigation protocol; combine with `--full` and `--timed` to run all three without repeating the gate or the build | When a navigation budget is in question, on a quiet machine; weekly in Meridian's repository |
102
106
  | `node scripts/brand.ts --name … [--hue … --chroma …]` | Renames and rebrands a copy in place; a brand hue becomes an accent preset that holds contrast in every theme | Starting a project |
103
- | `node scripts/shot.ts <routes> --width 1440,390 --theme light,dark [--full]` | Screenshots into `out/shots/` | While designing |
107
+ | `node scripts/shot.ts <routes> --width 1440,390 --theme light,dark [--full]` | Screenshots into `out/shots/` | While editing |
104
108
 
105
109
  A check is code, and it is wrong in the ways code is. Before a new check, or a change to one, is trusted:
106
110
 
@@ -112,8 +116,8 @@ A check is code, and it is wrong in the ways code is. Before a new check, or a c
112
116
  ## Releasing
113
117
 
114
118
  1. Add an entry to `CHANGELOG.md` under a new version. Semver: a removed or renamed token, prop or card is major; a new card, token or variant is minor; a corrected value is a patch. Say in one line what breaks and what to do instead.
115
- 2. `node cli/scripts/set-version.ts <v>`, then `pnpm gate` until it passes. Nothing else runs locally: the release workflow's gates are the proof.
116
- 3. Commit and push to `master`, then dispatch once: `gh workflow run release.yml -f version=<v>`. The workflow gates on the package's own tests (`tests/cli-*`) and the browser-fixture tests of the checks (`tests/deep-*`), one timed default `verify` (whose gate leaves both groups out) and the consumer smoke from the tarball, publishes with provenance, checks the registry's tarball is the tested one, and tags last. `verify --full --perf` runs weekly (`.github/workflows/weekly.yml`), not here.
119
+ 2. `node cli/scripts/set-version.ts <v>`, then `pnpm gate` until it passes. Nothing else runs locally: the release workflow's gates are the proof. A release that changes the checks themselves (a check script, a tier, a budget) first meets the `Releases` rule in `docs/testing.md`.
120
+ 3. Commit and push to `master`, then dispatch once: `gh workflow run release.yml -f version=<v>`. The workflow gates on the package's own tests (`tests/cli-*`) and the browser-fixture tests of the checks (`tests/deep-*`), one default `verify` within its time limit (whose gate leaves both groups out) and the consumer smoke from the tarball, publishes with provenance, checks the registry's tarball is the tested one, and tags last. `verify --full --timed --perf` runs weekly (`.github/workflows/weekly.yml`), not here.
117
121
 
118
122
  The `/release` command (`.claude/commands/release.md`) is this procedure as a runbook.
119
123
 
package/payload/README.md CHANGED
@@ -77,8 +77,8 @@ Dark is the default, written on `:root`; the light theme follows the operating s
77
77
  | `scripts/` | Generators, gates, `brand.ts` and `verify.ts` (see `CONTRIBUTING.md`) |
78
78
  | `skills/zz-meridian/` | The agent skill (Claude Code, Codex) that builds dashboards on this template or brings it into yours, to the standard in `references/standard.md`; how it is evaluated is `docs/skill-evals.md` |
79
79
  | `cli/` | The `zz-meridian` npm package: `create`, `adopt`, `update`, `brand` and `skill`, its build, its smoke test and the fixture apps (`docs/distribution.md`) |
80
- | `.github/workflows/release.yml` | The release, each check once: a timed default verify and the consumer path from the tarball (the release before updated; the last three weekly), then npm with provenance, the registry's bytes and provenance checked, then the tag (`.claude/commands/release.md`) |
81
- | `.github/workflows/weekly.yml` | Weekly, never at release: `verify --full --perf` (perf a report) and the consumer smoke's recovery and failure cases; nothing waits on it |
80
+ | `.github/workflows/release.yml` | The release, each check once: the default verify within its time limit and the consumer path from the tarball (the release before updated; the last three weekly), then npm with provenance, the registry's bytes and provenance checked, then the tag (`.claude/commands/release.md`) |
81
+ | `.github/workflows/weekly.yml` | Weekly, never at release: `verify --full --timed --perf` (perf a report) and the consumer smoke's recovery and failure cases; nothing waits on it |
82
82
  | `Dockerfile`, `captain-definition` | The demo deployment: the template and the Atlas behind `DEMO_PASSWORD` (`proxy.ts`, `app/sign-in/README.md`), deployed to CapRover from this repository; never in the package |
83
83
 
84
84
  ## Get a dashboard on Meridian, in one sentence
@@ -90,11 +90,11 @@ words at the end:
90
90
 
91
91
  In Claude Code or Codex, send it as a goal (`/goal Run npx zz-meridian@latest skill --global, then …`): the agent keeps
92
92
  working until the condition holds, and a separate model checks it after every turn against the hand-over, which quotes
93
- its evidence. The sentence says what you want; the skill carries how good it has to be. It builds, renders every view
94
- at phone and desktop width in both themes, scores each one on a scoreboard of floors (measured by `pnpm verify --full`)
95
- and craft (judged on the renders), and improves it round by round until nothing is weak, deciding what it can find out
96
- and recording each decision it made for you. You get the finished product, the renders, and the few questions only you
97
- can answer.
93
+ its evidence. The sentence says what you want; the skill carries how good it has to be. It builds, renders every view at
94
+ phone and desktop width in both themes, scores each one on a scoreboard of floors (measured by
95
+ `pnpm verify --full --timed`) and craft (judged on the renders), and improves it round by round until nothing is weak,
96
+ deciding what it can find out and recording each decision it made for you. You get the finished product, the renders,
97
+ and the few questions only you can answer.
98
98
 
99
99
  You do not choose between the package's commands; the skill does, from what you said and what is in the folder, and
100
100
  names the route before it runs anything:
@@ -108,12 +108,13 @@ names the route before it runs anything:
108
108
 
109
109
  `adopt` copies the components, tokens and gates in, merges the dependencies, brands it, adds Meridian's managed block to
110
110
  your `AGENTS.md` (your own text there is kept byte for byte) and an empty `docs/brief.md` for your product's own
111
- context, and installs the skill into the project. `create` writes a new, branded project with the same skill. The
112
- agent then does the judgement: building each page on Meridian, and running `pnpm verify` until the project meets the
113
- standard (`pnpm verify --full` after a change to shared components, the shell or data). It needs Node 22.18+, and
114
- Google Chrome for the browser checks. If your pages call a live API, say so in the sentence: `verify --full` presses
115
- every control, Delete included, so the agent builds a fake API first. The package (`cli/`, decision 0009) copies files
116
- and nothing depends on it afterwards; its commands are in `cli/README.md`.
111
+ context, and installs the skill into the project. `create` writes a new, branded project with the same skill. The agent
112
+ then does the judgement: building each page on Meridian, and running `pnpm verify` until the project meets the standard
113
+ (`pnpm verify --full` after a change to shared components, the shell or data, and `pnpm verify --full --timed`, the
114
+ whole standard, before it hands over). It needs Node 22.18+, and Google Chrome for the browser checks. If your pages
115
+ call a live API, say so in the sentence: `verify --full` presses every control, Delete included, so the agent builds a
116
+ fake API first. The package (`cli/`, decision 0009) copies files and nothing depends on it afterwards; its commands are
117
+ in `cli/README.md`.
117
118
 
118
119
  ## Privacy and feedback
119
120
 
@@ -137,7 +138,8 @@ pnpm install
137
138
  pnpm dev # the template at /, the Design Atlas at /system
138
139
  pnpm gate # tokens fresh, registry fresh, specs consistent, contrast in every theme and accent, types, tests
139
140
  pnpm verify # the gate once, one production build, the size checks and a smoke of up to three routes
140
- pnpm verify --full # every route, every control and link, the keyboard walk, the assistant and the live data
141
+ pnpm verify --full # every route, every control and link, the keyboard walk, the assistant and the product's own checks; no time is judged
142
+ pnpm verify --timed # the navigation budgets, the live data and Web Vitals; needs a quiet machine, runs alone and last
141
143
  # (the browser suites look inside open shadow roots; validation.md says what each line means)
142
144
  pnpm brand --name "Acme" --hue 25 --chroma 0.16 # rename and rebrand in place
143
145
  ```
@@ -74,6 +74,8 @@ Every figure is read on the server by the Home tool's read (`homeTool` in `src/v
74
74
 
75
75
  The page's client is handed these figures and lines and works nothing out beyond a change against the period before. Revenue is what customers were billed for metered usage, before credits, and over 30 days it is the customers' revenue on Customers and Revenue to the cent; latency is the median day's p95.
76
76
 
77
+ The body registers the ten collections its feature needs (`days`, `usage`, `models`, `compute`, `incidents`, `customers`, `services`, `tickets`, `tests` and `releases`) with `useLive` (`src/lib/live.ts`), before the empty state returns, so the page is drawn fresh in every state without a reload. It registers those and no others, because `GET /api/live` answers 403 for any name the caller may not read, and a person who reaches the page may read exactly what its feature needs: `costs`, which the page reads only for a person who may, is not registered. Nothing here writes, so no change hints; the page resyncs on open, on reconnect and on the safety poll.
78
+
77
79
  ## Surfaces
78
80
 
79
81
  - **Console**: as above.
@@ -70,7 +70,7 @@ Every figure is read on the server by the Revenue tool's read (`revenueTool` in
70
70
  - **What they buy** is the `usage` rows' revenue by model over the period's complete days, labelled from `models`; **where they are** is the `regions` collection.
71
71
  - **`facts.nextPlanAt`** (`src/app.config.ts`, 0.75 in the sample) is read each time the read runs.
72
72
 
73
- Revenue over 30 days equals Home's and the customers' revenue to the cent. Nothing writes.
73
+ Revenue over 30 days equals Home's and the customers' revenue to the cent. Nothing writes. The body registers `customers`, `days`, `usage`, `models` and `regions` with `useLive` (`src/lib/live.ts`), before the empty state returns, so the page is drawn fresh in every state without a reload. Those are the collections its feature needs and no others, because `GET /api/live` answers 403 for any name the caller may not read, and a person who reaches the page may read exactly what its feature needs.
74
74
 
75
75
  ## Surfaces
76
76
 
@@ -110,7 +110,7 @@ Those figures were measured before the suites looked inside open shadow roots. A
110
110
  - **Patterns:** a column chart left every other category unnamed; the assistant showed nothing between Send and its first words; narrow tiles read "vs previou…"; a read-only form drew its values at 1.5:1; three specimens contradicted their own captions.
111
111
  - **Pages:** a phone's record card left separator dots alone on a line and cut dates; Members looked unsorted because the column that explains its order was hidden; the Atlas home numbered its sections 01 to 05, which Meridian's own register rules out.
112
112
  - **Extreme values:** fed the largest figures the formatters print, a row of four tiles cut "9,876,543,210" to "9,876,54", a different number with no ellipsis, which no audit flags because the card clips it. A figure past nine characters now reads at a glance ("9.9B", "$1.2M"); the Extreme values specimens hold it. A probe that rewrote every name and figure in tables, lists and records to its longest also found nothing pushed sideways on any route.
113
- - **Measured:** `verify --full` on the final commit passes every suite: 0 audit issues on 22 routes at five widths in both themes, 361 controls and 56 links pressed, the keyboard walk, live data, and 18 of 18 pages under LCP 2.5 s, INP 200 ms and CLS 0.1. Web Vitals depend on a quiet machine: with other work holding the load at 11 to 38 on ten cores the same build read 10 to 17 of 18, because the four-times CPU slowdown applies to a host that is already busy; an A/B of the one shell change (the top bar as a container) showed INP tracking the load, not the build. Under reduced motion nothing moves on any route at 1440 or 390px.
113
+ - **Measured:** `verify --full` on the final commit, when it still ran the live data and Web Vitals (both are `verify --timed`'s now), passes every suite: 0 audit issues on 22 routes at five widths in both themes, 361 controls and 56 links pressed, the keyboard walk, live data, and 18 of 18 pages under LCP 2.5 s, INP 200 ms and CLS 0.1. Web Vitals depend on a quiet machine: with other work holding the load at 11 to 38 on ten cores the same build read 10 to 17 of 18, because the four-times CPU slowdown applies to a host that is already busy; an A/B of the one shell change (the top bar as a container) showed INP tracking the load, not the build. Under reduced motion nothing moves on any route at 1440 or 390px.
114
114
 
115
115
  ## Open
116
116
 
@@ -18,10 +18,10 @@ An earlier sentence named `adopt`, the wrong command for every request for a new
18
18
  The sentence is written to be sent as a goal (`/goal` in Claude Code and Codex), which keeps the agent working until a
19
19
  separate model, reading the conversation after every turn, finds the condition met. That model sees the sentence, not
20
20
  the skill, so the sentence ends with a condition the transcript can show: the hand-over (SKILL.md, step 7) quotes
21
- `pnpm verify --full`'s last line and the scoreboard's count, or names what only the person can decide or provide. The
22
- degree lives in the skill, never in the sentence: `skills/zz-meridian/references/standard.md` holds the register, the
23
- scoreboard of floors and craft kept for every view, the loop run until nothing is weak, and which decisions the agent
24
- makes and records and which wait for the person. People do not say how good it has to be, so the skill does not ask.
21
+ `pnpm verify --full --timed`'s last line and the scoreboard's count, or names what only the person can decide or
22
+ provide. The degree lives in the skill, never in the sentence: `skills/zz-meridian/references/standard.md` holds the
23
+ register, the scoreboard of floors and craft kept for every view, the loop run until nothing is weak, and which
24
+ decisions the agent makes and records and which wait for the person. People do not say how good it has to be, so the skill does not ask.
25
25
 
26
26
  The split follows what each part is good at. The package does the settled work, the same way every time, and proves it
27
27
  built: what to copy, which dependencies to merge, the alias, the stylesheet, the brand. The agent does the judgement:
@@ -147,14 +147,14 @@ Two environment variables exist for the updater's own tests and are used only wh
147
147
  reports as `migration:<id>` because it cannot make them. An entry applies when the project's recorded version is older
148
148
  than its `since` and the target is not, and its `applies(root)` returns the paths that still have the old shape, or
149
149
  null. It only reads, and only team-owned files (the ownership rule's managed paths are skipped); a file it cannot read is
150
- absent. A project already on the new shape gets no line. Every entry carries `gate` and `build` as its checks (`verify-modes`, `agent-reads-section`, `nav-needs`, `agent-reads-own-group` and `marker-measured-on-observe`, `gate` alone), and its
150
+ absent. A project already on the new shape gets no line. Every entry carries `gate` and `build` as its checks (`verify-modes`, `agent-reads-section`, `nav-needs`, `agent-reads-own-group`, `agent-does-here-section`, `marker-measured-on-observe`, `view-page-matched-as-next`, `journey-moved`, `today-in-timezone` and `verify-tiers`, `gate` alone), and its
151
151
  instructions name the section of a reference (`cache.md`, `live.md`, `validation.md`, `agents.md`, `customize.md`), of the Start guide or of `docs/assistant.md` to follow.
152
152
 
153
- Eleven arrived after 0.5.0 and are resolved as the skill's `references/update.md` says: `share-view-context` and `agent-reads-section` (0.9.0), `assistant-view-tools` (0.10.0), `nav-needs` (0.13.0), `agent-reads-own-group` (0.15.0), `refusal-declared`, `managed-exports-removed`, `view-tool-page-explore`, `agent-does-here-section` and `sample-pages-renamed` (0.16.0), and `marker-measured-on-observe` (0.16.2). The ten of 0.5.0 are:
153
+ Fifteen arrived after 0.5.0 and are resolved as the skill's `references/update.md` says: `share-view-context` and `agent-reads-section` (0.9.0), `assistant-view-tools` (0.10.0), `nav-needs` (0.13.0), `agent-reads-own-group` (0.15.0), `refusal-declared`, `managed-exports-removed`, `view-tool-page-explore`, `agent-does-here-section` and `sample-pages-renamed` (0.16.0), `marker-measured-on-observe` (0.16.2), `view-page-matched-as-next`, `journey-moved` and `today-in-timezone` (0.16.3), and `verify-tiers` (0.16.4). The ten of 0.5.0 are:
154
154
 
155
155
  | Id | Reported when a team-owned file still has | Completion step |
156
156
  |----|-------------------------------------------|-----------------|
157
- | `verify-modes` | a `package.json` script, workflow or shell file passing `--quick`, `--no-vitals` or `--extra` to `verify` | `validation.md`: replace each with `pnpm verify`, `--full` or `--perf` in the script, workflow or shell file |
157
+ | `verify-modes` | a `package.json` script, workflow or shell file passing `--quick`, `--no-vitals` or `--extra` to `verify` | `validation.md`: replace each with `pnpm verify`, `--full`, `--timed` or `--perf` in the script, workflow or shell file |
158
158
  | `shell-assistant-promise` | `<AppShell` with an `assistant=` prop and no promise (`.then(`, `Promise`, `use(`) | `cache.md`, "The shell's assistant promise": pass a promise and do not await it |
159
159
  | `assistant-available-promise` | `useAssistantAvailable()` used as a boolean | same section: `use(useAssistantAvailable())` inside `Suspense` |
160
160
  | `clock-now-required` | `<Freshness`, `<ShellTools` or `<AlertsPanel` without `now=`, or `formatRelative(` with one argument | `cache.md`, "The clock is the data's": pass the data's clock |
@@ -216,12 +216,13 @@ Modelled on the release pipeline of the owner's earlier packages, one package in
216
216
  1. **Dispatch**: `gh workflow run release.yml -f version=<v> [-f dry_run=true]`, from `master`. The version must equal
217
217
  `cli/package.json`'s and the tag must be unused.
218
218
  2. **Gates** (ubuntu-24.04, 4 CPUs, the reference profile), each run once: the package's own tests (`tests/cli-*`) and the
219
- browser-fixture tests of the checks (`tests/deep-*`), which no adopted or created project has and the timed default
219
+ browser-fixture tests of the checks (`tests/deep-*`), which no adopted or created project has and the default
220
220
  `verify` leaves out; the template's default `verify` from a clean `.next` through
221
221
  `cli/scripts/release-check.ts`, within 180 s on the shared runner (whose CPU varies: one commit measured 111 s on one
222
222
  and 152 s on another) and with its browser smoke (the gate and the template's unit tests run
223
223
  inside it); and the consumer smoke from the built tarball (step 4). The full, perf and recovery suites are never
224
224
  run here: they run weekly (below).
225
+ When each check runs, in a project and in this repository, is `docs/testing.md`'s `## When each runs`.
225
226
  3. **Pack and assert, before anything is irreversible**: `npm pack` in `cli/`, then on the tarball: the bin has its
226
227
  `#!/usr/bin/env node` line; `payload/skills/zz-meridian/SKILL.md` and its references are there; at least 60 component
227
228
  folders are there; no `node_modules/`, `tests/` of the CLI, `out/` or `.next/`.
@@ -246,14 +247,15 @@ Modelled on the release pipeline of the owner's earlier packages, one package in
246
247
  `dry_run` stops after step 4.
247
248
 
248
249
  **Weekly** (`.github/workflows/weekly.yml`, Mondays and on demand, master's current commit): the template's
249
- `verify --full --perf`, one gate and one build for both (the perf part a report: a p95 over budget is a warning, a
250
+ `verify --full --timed --perf`, one gate and one build for both (the perf part a report: a p95 over budget is a warning, a
250
251
  broken sample a failure), and the consumer smoke's `--adopt --update --origins 3 --update-all --verify` from a tarball packed from that
251
252
  commit, labelled as the next patch so `update` treats it as unpublished: the adopted project's default verify cases,
252
253
  the update from each of the last three releases, the updater's stages on the 0.3.0 fixture, and every recovery and
253
254
  failure case of the update. Both run the
254
255
  consumer smoke's `--adopt` and `--update`; weekly reaches three releases back and every recovery case, a release only the
255
256
  one before it. Each job keeps its log as an artifact. A failure notifies;
256
- nothing waits on it.
257
+ nothing waits on it. `docs/testing.md` says when each check runs, and that a red here does not of itself hold
258
+ a release.
257
259
 
258
260
  A `/release` runbook (`.claude/commands/release.md`) holds the judgement before dispatch: the version, the changelog
259
261
  section and the docs sweep. A `cli/scripts/set-version.ts`
@@ -44,7 +44,7 @@ Your pages, your server actions and the assistant read and change records throug
44
44
  - Read freshness from when the data arrived (the newest ingest time), never from `now()`.
45
45
  - Return `null` for "not measured"; the formatters render it as a dash, never as zero.
46
46
 
47
- The console keeps its collection-backed pages (Now, Members, API keys, Tickets, Tests, Releases) fresh without a reload: one stream per tab carries a hint that names only a collection, and the page refreshes through the same authorized `read()`. The sample stream is a single process; across processes, `skills/zz-meridian/references/live.md` holds the Postgres and Redis adapters a product writes for itself.
47
+ The console keeps its collection-backed pages (Home, Revenue, Now, Members, API keys, Tickets, Tests, Releases) fresh without a reload: one stream per tab carries a hint that names only a collection, and the page refreshes through the same authorized `read()`. The sample stream is a single process; across processes, `skills/zz-meridian/references/live.md` holds the Postgres and Redis adapters a product writes for itself.
48
48
 
49
49
  ## 5. Decide who may do what
50
50
 
@@ -91,7 +91,8 @@ The console's assistant is off until you set the `ASSISTANT_*` variables (`.env.
91
91
 
92
92
  ```sh
93
93
  pnpm verify # the gate once, one production build, the size checks and a smoke of up to three routes
94
- pnpm verify --full # every route, every control pressed and link followed, the keyboard walk, the assistant and the live data
94
+ pnpm verify --full # every route, every control pressed and link followed, the keyboard walk, the assistant and the product's own checks; no time is judged
95
+ pnpm verify --timed # the navigation budgets, the live data and Web Vitals; needs a quiet machine, runs alone and last
95
96
  ```
96
97
 
97
- `pnpm verify` is the check to run before you finish. Run `--full` after a change to shared components, the shell or data. Every run ends with a coverage line, such as `coverage: default; browser ran; 3 routes; data configured 3/3; interaction configured 3/3; not run: audit, presses, keyboard, assistant, live, vitals`, which says what ran and what did not. Nothing that did not run is reported as passed. `pnpm verify --perf` adds the 20-sample navigation protocol and reports it; a p95 over its budget is a `warn`, never a failure. The browser suites look inside open shadow roots, so a web component's content is measured, pressed and walked like any other; a custom element they cannot look inside is reported as `unmeasured:`, and a selector in `navigationChecks` that matches several elements prints a `note:`. `skills/zz-meridian/references/validation.md` says what each line means and what to do about it. The default is built to finish within two minutes; `--full` and `--perf` are what a team schedules, weekly for instance.
98
+ `pnpm verify` is the check to run before you finish. Run `--full` after a change to shared components, the shell or data, and `--timed` on a quiet machine to judge the times; `pnpm verify --full --timed` is the whole standard. Every run ends with a coverage line, such as `coverage: default; browser ran; 3 routes; data configured 3/3; interaction configured 3/3; not run: audit, presses, keyboard, assistant, browserChecks, timing, live, vitals`, which says what ran and what did not. Nothing that did not run is reported as passed. `pnpm verify --perf` adds the 20-sample navigation protocol and reports it; a p95 over its budget is a `warn`, never a failure. The browser suites look inside open shadow roots, so a web component's content is measured, pressed and walked like any other; a custom element they cannot look inside is reported as `unmeasured:`, and a selector in `navigationChecks` that matches several elements prints a `note:`. `skills/zz-meridian/references/validation.md` says what each line means and what to do about it. The default is built to finish within two minutes; `--full`, `--timed` and `--perf` are what a team schedules, weekly for instance.
@@ -0,0 +1,134 @@
1
+ # Testing
2
+
3
+ This page is the testing standard: the kinds of test, the tiers that combine them, when each runs, and the rules a red, a limit and a release follow. An adopted or created project is advised to follow it, Meridian follows it, and a spec cites it rather than restating it. The last section classifies every check Meridian runs by those terms. A row or a command that only Meridian's own repository has says so: a project made from Meridian ships without `cli/`, `.github/`, `tests/cli-*` and `tests/deep-*`.
4
+
5
+ ## The kinds
6
+
7
+ A check has one size and one type. The two are independent: a size never says what a check checks, and a type never says what it costs.
8
+
9
+ **Size** is what a check needs to run, not what it intends.
10
+
11
+ - `static`: reads files, with no test runner and no process of its own beyond the tool (the token and registry checks, the specification checks, contrast, route types, types, lint).
12
+ - `small`: one process, in the test runner, with no child process, no server, no browser and no build.
13
+ - `medium`: starts processes or servers on this machine, with no browser.
14
+ - `large`: needs a real browser or a production build.
15
+
16
+ Size sets the cost, so it sets when a check can run: the smaller it is, the earlier and the more often.
17
+
18
+ **Type** is what a check checks.
19
+
20
+ - `functional`: does it do what it should (a control works, a page reaches its data, a function returns the right value).
21
+ - `non-functional`: a quality other than speed (accessibility, contrast, layout and overflow, sizes and bytes, route policy, the safety of the data a run can reach).
22
+ - `performance`: time behaviour against a budget (navigation budgets, live-data bounds, the Web Vitals floors, the 20-sample p95).
23
+
24
+ Type sets whether a run needs a quiet machine: a performance check measures the machine as well as the page, so only a performance check needs one. Regression is not a type: it is why a change is checked, by the complete and the timed tier together.
25
+
26
+ ## The tiers
27
+
28
+ A tier is when a check runs, ordered by speed and scope. The first four are one command each. The change tier adds to the quick one and the complete tier to the change one; the timed tier stands beside the complete one, and the two together are the whole standard. In any combination of them, the gate and the build run once.
29
+
30
+ - **quick**: `pnpm gate`. The static checks and the tests, with no browser and no build; `tests/deep-*` is left out. In Meridian's repository it runs `tests/cli-*` too, which `node scripts/gate.ts --without-cli-tests` leaves out.
31
+ - **change**: `pnpm verify`. Adds to the gate (run without `tests/cli-*`) one production build, the route policy, the first-load and HTML size checks, and the navigation of up to three routes on desktop and through the phone drawer, judged on function. It ends with a coverage line that says what ran and what did not, and never refuses for a missing Chrome or safe backend: it says the browser did not run, and why.
32
+ - **complete**: `pnpm verify --full`. Adds the navigation of every rail route, the audit of every page, every control pressed and every link followed, the keyboard walk, the assistant and the product's own browser checks. Nothing in it judges a time, so it is not the whole standard: `pnpm verify --full --timed` is.
33
+ - **timed**: `pnpm verify --timed`. The navigation of every rail route with its budgets judged, then the live data and Web Vitals, alone and last, so it needs a quiet machine. It does not imply the complete tier ran. `pnpm verify --full --timed` is the whole standard, on one gate and one build with the complete suites first, and the only run that can end "the project meets the Meridian standard".
34
+ - **on request**: a command no tier runs, run at the moment `## When each runs` gives it. The combinations that exist:
35
+ - `pnpm verify --perf` adds the 20-sample navigation protocol to the tiers beside it, or to the change tier alone; `pnpm verify --full --timed --perf` runs all three on one gate and one build.
36
+ - `pnpm verify --as <persona>` signs the browser in as each persona it names (a comma-separated list), alone or beside `--full`. It is refused beside `--timed` or `--perf`, before anything runs.
37
+ - `node scripts/audit.ts --atlas` and `node scripts/vitals.ts --atlas` run against a served app, outside `pnpm verify`, over every Design Atlas entry.
38
+ - The browser-fixture tests of the checks, `pnpm exec vitest run tests/deep- --no-file-parallelism`, run beside `pnpm verify --full` after a change to a check script. Meridian's repository only.
39
+ - `node cli/scripts/smoke.ts --tarball <tarball>` is the consumer smoke, a packed tarball adopted, created and updated end to end. Meridian's repository only.
40
+
41
+ ## When each runs
42
+
43
+ A moment is when a person or an agent runs a command. A project's own CI may run the same commands at the same moments.
44
+
45
+ - **While editing**: the test of the file changed, `pnpm exec vitest run <file>`, and a look at the page with `node scripts/shot.ts <route> --width 1440,390 --theme dark,light`.
46
+ - **Before every commit**: `pnpm gate`.
47
+ - **Before finishing a change**: `pnpm verify`.
48
+ - **After a change to shared components, the shell or data, and before a change is handed over**: `pnpm verify --full`.
49
+ - **Before a release of the product**, at the release commit and on a quiet machine: `pnpm verify --full --timed`.
50
+ - **Triggered by what changed**:
51
+ - who may see or do what: `pnpm verify --as <persona>`, for each persona the change touches;
52
+ - a card, a pattern or the Atlas: `node scripts/audit.ts --atlas` and, on a quiet machine, `node scripts/vitals.ts --atlas`;
53
+ - a navigation budget in question: `pnpm verify --perf`, on a quiet machine;
54
+ - a check script: the browser-fixture tests beside `pnpm verify --full` (Meridian's repository only).
55
+ - **On a schedule**: the slow and statistical checks, `pnpm verify --full --timed --perf`, where the perf part is a report: a p95 over its budget is a warning and only a broken sample fails.
56
+ - **Meridian's release** (`.github/workflows/release.yml`, dispatched from master; Meridian's repository only), each command once:
57
+ - `pnpm exec vitest run tests/cli-` and `pnpm exec vitest run tests/deep- --no-file-parallelism`;
58
+ - `node cli/scripts/release-check.ts --limit 180`: the default verify from a clean build, within 180 s on the shared 4-CPU runner;
59
+ - `node cli/scripts/smoke.ts --tarball <tarball> --adopt --create --update`, on the tarball the release publishes;
60
+ - after the publish, a project created from the registry and one updated to it from the release before.
61
+ - It never runs the complete, timed or perf suites: the weekly run does.
62
+ - **Meridian's weekly run** (`.github/workflows/weekly.yml`, Mondays at 03:17 UTC and on demand, on master's current commit; Meridian's repository only):
63
+ - `pnpm verify --full --timed --perf`, as on a schedule above;
64
+ - `node cli/scripts/smoke.ts --tarball <tarball> --adopt --update --origins 3 --update-all --verify`, on a tarball packed from that commit: the adopted project's default verify cases, the update from each of the last three releases, and every recovery and failure case of the update.
65
+ - Nothing waits on it: a failure notifies.
66
+
67
+ ## Rules
68
+
69
+ - **Quiet machine**: A run needs a quiet machine exactly when it runs a check whose type is performance. A quiet machine runs nothing else heavy: no other build, browser or test run. Repeated runs of one check (the three of `Fixed means`) are separate runs, each alone on the machine, so other work can run between them.
70
+ - **Fixed means**: every red is fixed in the product or in the check, never by skipping, narrowing or deleting the check. A limit moved under `Limits` changes a value, never what is measured.
71
+ - A red is fixed when its cause is named and a test that failed on the old code passes, at the lowest layer that can show it.
72
+ - A measurement no test can reproduce (a time, a dropped frame) is fixed after three consecutive passes on a quiet machine. One pass of a measurement is not evidence.
73
+ - A red whose cause is not named after a run narrowed to its route or page, with the render instrumented, goes to the owner as a decision, with what was measured, before any limit is touched.
74
+ - **Limits**: a navigation budget, a live-data bound, or the LCP or INP floor may move, and only this way.
75
+ - It moves only after a trace of the narrowed run names where the time goes, and none of it is the page's own long task or request that a change in the page would remove.
76
+ - The new value is the nearest-rank p95 of at least 20 samples on a quiet machine, taken with the phone the timed suites emulate, the larger of the local and the scheduled runner's where both exist, times 1.25, rounded up to the next 10 ms.
77
+ - It is never more than twice the old value without the owner's word.
78
+ - CLS and dropped frames are never moved: a page that misses them is fixed.
79
+ - A moved limit is written where the limit lives (`scripts/lib/budgets.ts`, or the check), and recorded in a `## Budgets` section of this page, placed before `## The classification`: one row per limit with its old and new value, the run, machine and commit it was set from, the samples, the p95 and the margin.
80
+ - The release's time limit on the default verify is not such a limit, and the phone a timed suite emulates is a device, not a limit.
81
+ - **Once per commit**: evidence recorded on a commit stands for it and for any later commit whose difference touches none of the command's inputs (defined under `Releases`). A check is run again only when its record is missing, its inputs changed, its record disagrees with what was reported, or it is in concrete doubt. A tier that runs the gate and a build inside it is one command, not a second run of them.
82
+ - **Flaky is red**: a check that fails sometimes is a red under `Fixed means`. A retry that passes does not clear it.
83
+ - **Releases**: a release runs its own checks; Meridian's are the steps of `.github/workflows/release.yml`.
84
+ - The scheduled run's reds are fixed or decided like any red, and do not of themselves hold a release.
85
+ - A change to the checks themselves (a check script, a tier, a budget) is released from a commit at which every command in the classification has passed, on that commit or on one whose difference touches none of the command's inputs.
86
+ - A red left at a release is the owner's decision, named in the release's changelog as a known issue with where it is fixed.
87
+ - A command's inputs are: the scripts it runs; the product code and its configuration; the package manifest and lockfile; every document a page the command reaches renders; and, for a test command, the files its tests read.
88
+ - After a change that is not the fix of a red, a measurement is repeated on the pages the change touched, with the suite's own `--routes` (`scripts/audit.ts`, `scripts/vitals.ts`, `scripts/navigate.ts`, `scripts/perf.ts`), rather than on every page.
89
+
90
+ ## Basis
91
+
92
+ - **Sizes** by what a test needs, not what it intends: Google's small, medium and large (*Software Engineering at Google*, ch. 11, and the Google Testing Blog's "[Test Sizes](https://testing.googleblog.com/2010/12/test-sizes.html)", 2010). Meridian adds `static` for a check that runs no test at all, and counts a browser or a production build as `large` even on one machine, where Google's large is a test that needs more than one.
93
+ - **Tiers** as stages: fast checks before every commit and slower stages after, in one pipeline (Humble and Farley, *Continuous Delivery*, 2010, the deployment pipeline's commit and acceptance stages). In a project, a person or an agent runs the later stages at their moments; only Meridian's release and weekly run are automated.
94
+ - **Proportions**: many fast tests and few slow end-to-end ones ("[The Practical Test Pyramid](https://martinfowler.com/articles/practical-test-pyramid.html)", Ham Vocke, on Martin Fowler's site, 2018).
95
+ - **Types** after the ISTQB glossary and ISO/IEC 25010. Meridian lifts performance out of the non-functional qualities into a type of its own, because it alone needs a quiet machine, and does not count regression as a type, since the ISTQB defines regression testing by why it runs.
96
+ - **Measurement**: performance judged by percentiles of repeated samples on a quiet machine, the phone after Lighthouse's mobile throttling, the CPU slowed four times on Slow 4G ([Lighthouse's throttling guide](https://github.com/GoogleChrome/lighthouse/blob/main/docs/throttling.md)). The 390 px touch width, the 20 samples, the 1.25 margin and the rounding of `Limits` are Meridian's own.
97
+ - **Flakiness** as a defect: a test that passes and fails on the same code erodes trust in every result (*Software Engineering at Google*, ch. 11 and 14). Where a large test's flakiness is tolerated or retried, Meridian does not clear a red by a retry.
98
+
99
+ ## The classification
100
+
101
+ Every check Meridian runs has one row, named by a key: a gate step, a group of test files, a phase or suite of `pnpm verify`, or a standalone suite. In Meridian's repository the table is guarded by `tests/cli-test-tiers.test.ts`, which fails naming the key when a step, suite or group has no row or a row names something that does not exist. A run needs a quiet machine exactly when it runs a check whose type is performance.
102
+
103
+ | Key | Size | Type | Tier | Quiet machine | Command | Measured | What it checks |
104
+ |---|---|---|---|---|---|---|---|
105
+ | `gate/tokens are fresh` | static | non-functional | quick | no | `node scripts/tokens.ts --check` | 0.1 s at 0bee6d7 | The generated token CSS matches `tokens/`. |
106
+ | `gate/the registry is fresh` | static | non-functional | quick | no | `node scripts/registry.ts --check` | 0.1 s at 0bee6d7 | The Atlas registry matches the card folders. |
107
+ | `gate/specifications are consistent` | static | non-functional | quick | no | `node scripts/check.ts` | 0.3 s at 0bee6d7 | Cards, READMEs, tokens and page specifications follow the contract. |
108
+ | `gate/the product's own rules hold` | static | non-functional | quick | no | `node scripts/check.local.ts` | 0 s at 0bee6d7 (not present in this repository: it runs only where a product wrote scripts/check.local.ts) | A product's own rules, beside Meridian's. |
109
+ | `gate/contrast holds in every theme and accent` | static | non-functional | quick | no | `node scripts/contrast.ts` | 0.1 s at 0bee6d7 | Every specified foreground on its background, in every theme and accent. |
110
+ | `gate/route types are generated` | static | non-functional | quick | no | `next typegen` | 0.2 s at 0bee6d7 | Route types are generated before the type check. |
111
+ | `gate/types check` | static | functional | quick | no | `tsc --noEmit` | 2.7 s at 0bee6d7 | The TypeScript compiles. |
112
+ | `gate/lint passes` | static | non-functional | quick | no | `eslint .` | 17.1 s at 0bee6d7 | The code passes the Next ESLint config. |
113
+ | `gate/tests pass` | small, medium | functional | quick | no | `pnpm exec vitest run --exclude 'tests/deep-*'` | 68.8 s at 89e4ef4 | The tests outside `tests/deep-*` pass (run with the lint and the type check). |
114
+ | `tests/shipped` | small, medium | functional | quick | no | `pnpm exec vitest run --exclude 'tests/deep-*' --exclude 'tests/cli-*'` | 42 s at 7e6de42 | The behaviour tests of the components, pages and data (summed file time). |
115
+ | `tests/cli-*` | small, medium | functional | quick | no | `pnpm exec vitest run tests/cli-` | 124 s at 7e6de42 | Meridian's repository only. The package's own tests: the CLI, its update and release paths (summed file time). |
116
+ | `tests/deep-*` | large | functional | complete | no | `pnpm exec vitest run tests/deep- --no-file-parallelism` | 743 s at 89e4ef4 | Meridian's repository only. The check scripts against browser fixtures: each planted defect is found and each corrected twin passes. |
117
+ | `verify/build` | large | functional | change | no | `pnpm verify` | 6.9 s at bc1e10b | One production build. |
118
+ | `verify/route policy` | large | non-functional | change | no | `pnpm verify` | 0.1 s at bc1e10b | Every route is declared static or dynamic as its policy says. |
119
+ | `verify/first-load` | large | non-functional | change | no | `pnpm verify` | 0.3 s at bc1e10b | The first-load size of each route is within its budget. |
120
+ | `verify/html` | large | non-functional | change | no | `pnpm verify` | 0.3 s at bc1e10b | The served HTML of each route is within its size check. |
121
+ | `verify/navigation` | large | functional | change | no | `pnpm verify` | 88.5 s at 89e4ef4 | From a press in the rail, each route's shell, data and first interaction arrive, on desktop and through the phone drawer, judged on function only, never on time; up to three routes, every rail route under `--full` or `--timed`. |
122
+ | `verify/timing` | large | performance | timed | yes | `pnpm verify --timed` | 86.5 s at 89e4ef4 | The same navigation of every rail route, with each shell, data and interaction time judged against its budget. |
123
+ | `verify/personas` | large | functional | on request | no | `pnpm verify --as viewer` | 104 s at 0bee6d7 | Signed in as each named persona, the rail shows the routes that persona may open and a denied route draws its refusal. |
124
+ | `verify/audit` | large | non-functional | complete | no | `pnpm verify --full` | 626.6 s at bc1e10b | Every page and embed view at five widths in both themes, for layout and accessibility defects. |
125
+ | `verify/presses` | large | functional | complete | no | `pnpm verify --full` | 998.8 s at bc1e10b | Every control pressed and every link followed, on desktop and phone. |
126
+ | `verify/keyboard` | large | functional | complete | no | `pnpm verify --full` | 205.6 s at bc1e10b | Tab through each page and back: focus rings, reach and order. |
127
+ | `verify/assistant` | large | functional | complete | no | `pnpm verify --full` | 101.2 s at bc1e10b | The assistant absent and in a walk-through (54.7 s and 46.5 s). |
128
+ | `verify/live` | large | performance | timed | yes | `pnpm verify --timed` | 40.3 s at 89e4ef4 | Two tabs, a silent stream, a restart, a hidden and an offline tab and a burst: each converges within its bound. |
129
+ | `verify/vitals` | large | performance | timed | yes | `pnpm verify --timed` | 311.1 s at bc1e10b | LCP, INP, CLS and dropped frames on a mid-range phone for every static page. |
130
+ | `verify/browserChecks` | large | functional | complete | no | `pnpm verify --full` | 27.8 s at bc1e10b | The product's own browser journeys (`journeys/workspaces.ts`). |
131
+ | `verify/perf` | large | performance | on request | yes | `pnpm verify --perf` | 84.8 min at 89e4ef4 | The 20-sample navigation protocol. |
132
+ | `suite/audit --atlas` | large | non-functional | on request | no | `node scripts/audit.ts --atlas` | 21.5 min at 89e4ef4 | The audit over the Design Atlas: 183 routes at 1440 and 390px in both themes. |
133
+ | `suite/vitals --atlas` | large | performance | on request | yes | `node scripts/vitals.ts --atlas` | 16.3 min at 89e4ef4 | Web Vitals for every Atlas entry: 110 pages. |
134
+ | `suite/consumer smoke` | large | functional | on request | no | `node cli/scripts/smoke.ts --tarball <tarball> --adopt --create --update` | 4.5 min at 89e4ef4 | Meridian's repository only. The release case: a packed tarball adopted, created and updated end to end. Release runs this command. Weekly runs `--adopt --update --origins 3 --update-all --verify`, whose `--verify` runs the adopted project's default verify cases. The created project's case, `--create --verify`, runs its `verify --full --timed`, runs on request only and wants a quiet machine the release case does not. |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-meridian-template",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "private": true,
5
5
  "license": "MIT",
6
6
  "description": "ZZ Meridian: a dashboard design system and starter. Six layers, from DTCG tokens through four layers of React components to product workspaces, and a Design Atlas, in a light and a dark theme.",