You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/scripts/permutation-check.ts

590 lines
19 KiB

morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
/**
* Permutation runner — cycles soma components through the state space declared
* by their demo pages and validates morfo compliance after every transition.
*
* Closes the gap left by `morfo:check` (single state) and `smoke` (load-time
* errors only): reactivity bugs like toolbar's A35 loop and form's A36
* microtask loop manifest during state transitions, not on first paint.
*
* Opt-in per demo: tag interactive controls with `data-perm-step="N"`. The
* runner discovers them, executes them in order, and re-validates morfo after
* each settle. See `src/uix/morfo/PERMUTATION_RUNNER.md` for the full
* authoring convention.
*
* Exit codes:
* 0 — every demo with `data-perm-step` passes all permutations.
* 1 — at least one permutation failed.
* 2 — no dev server / no morfos found.
*/
import { chromium, type Page } from 'playwright';
import { readdirSync } from 'node:fs';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { dirname, join } from 'node:path';
import type { Morfo, MorfoPart, MorfoData } from '../src/uix/morfo/types';
import { validateMorfo } from '../src/uix/morfo/schema';
const __dirname = dirname(fileURLToPath(import.meta.url));
const MORFOS_DIR = join(__dirname, '..', 'src', 'uix', 'morfo', 'components');
// ── Shared with morfo-check.ts ──────────────────────────────────────────────
type Issue = { kind: 'missing' | 'bad-value' | 'undeclared'; message: string };
function flatParts(parts: readonly MorfoPart[]): MorfoPart[] {
const out: MorfoPart[] = [];
for (const p of parts) {
out.push(p);
if (p.parts && p.parts.length > 0) out.push(...flatParts(p.parts));
}
return out;
}
function dataAttrFor(kebab: string, part: MorfoPart): string {
return part.kebab === 'provider' ? `data-${kebab}` : `data-${kebab}-${part.kebab}`;
}
async function validateMorfoAgainstDom(page: Page, morfo: Morfo): Promise<Issue[]> {
const issues: Issue[] = [];
const parts = flatParts(morfo.parts).filter((p) => p.kind === 'public');
const allPartAttrs = new Set(parts.map((p) => dataAttrFor(morfo.kebab, p)));
for (const part of parts) {
const partAttr = dataAttrFor(morfo.kebab, part);
const elementsAttrs = await page.$$eval(`[${partAttr}]`, (nodes) =>
nodes.map((el) =>
Array.from(el.attributes)
.filter((a) => a.name.startsWith('data-'))
.map((a) => ({ name: a.name, value: a.value }))
)
);
if (elementsAttrs.length === 0) continue;
const declared = new Set(part.data.map((d) => d.attr));
declared.add(partAttr);
for (const siblingAttr of allPartAttrs) declared.add(siblingAttr);
const declaredByAttr = new Map<string, MorfoData>(part.data.map((d) => [d.attr, d]));
for (const elAttrs of elementsAttrs) {
for (const data of part.data) {
if ((data.severity ?? 'required') !== 'required') continue;
const found = elAttrs.find((a) => a.name === data.attr);
if (!found) {
issues.push({
kind: 'missing',
message: `${morfo.kebab}.${part.kebab}: required attr "${data.attr}" not emitted`
});
}
}
for (const { name, value } of elAttrs) {
const decl = declaredByAttr.get(name);
if (decl?.values && !decl.values.includes(value)) {
issues.push({
kind: 'bad-value',
message: `${morfo.kebab}.${part.kebab}: "${name}" has value "${value}", morfo declares [${decl.values.join(', ')}]`
});
}
}
for (const { name } of elAttrs) {
if (!name.startsWith(`data-${morfo.kebab}`) && !name.startsWith('data-')) continue;
if (name.startsWith(`data-_`)) continue;
if (declared.has(name)) continue;
if (!name.startsWith(`data-${morfo.kebab}`)) continue;
issues.push({
kind: 'undeclared',
message: `${morfo.kebab}.${part.kebab}: undeclared attr "${name}" (not in morfo)`
});
}
}
}
return issues;
}
// ── Probe port + load morfos (copied from morfo-check) ──────────────────────
async function probePort(start: number, end: number): Promise<string | null> {
for (let port = start; port <= end; port++) {
try {
const res = await fetch(`http://localhost:${port}/`, {
signal: AbortSignal.timeout(500)
});
if (res.ok || res.status === 404 || res.status === 500) {
return `http://localhost:${port}`;
}
} catch {
// try next
}
}
return null;
}
async function loadMorfos(): Promise<Morfo[]> {
const files = readdirSync(MORFOS_DIR).filter(
(f) => f.endsWith('.ts') && !f.endsWith('.test.ts')
);
const out: Morfo[] = [];
for (const f of files) {
const url = pathToFileURL(join(MORFOS_DIR, f)).href;
const mod = (await import(url)) as Record<string, unknown>;
for (const v of Object.values(mod)) {
if (typeof v === 'object' && v !== null && 'kebab' in v && 'parts' in v) {
out.push(validateMorfo(v));
}
}
}
return out;
}
// ── Permutation step schema ─────────────────────────────────────────────────
interface PermStep {
index: number;
label: string;
mode: { kind: 'click' } | { kind: 'focus' } | { kind: 'type'; value: string } | { kind: 'key'; key: string };
skipValidate: boolean;
settle: number;
}
/** Parse a `data-perm-mode` attribute value. */
function parseMode(raw: string | null): PermStep['mode'] {
if (!raw || raw === 'click') return { kind: 'click' };
if (raw === 'focus') return { kind: 'focus' };
const typeMatch = /^type="([^"]*)"$/.exec(raw);
if (typeMatch) return { kind: 'type', value: typeMatch[1] };
const keyMatch = /^key="([^"]+)"$/.exec(raw);
if (keyMatch) return { kind: 'key', key: keyMatch[1] };
return { kind: 'click' };
}
/**
* Collect perm steps CURRENTLY present in the DOM, sorted by index. Called
* before every step execution because new `data-perm-step` elements may
* mount as earlier steps fire (e.g. a Dialog.Close inside a portal that
* only exists after `open = true`).
*/
async function collectSteps(page: Page): Promise<PermStep[]> {
const raw = await page.$$eval('[data-perm-step]', (els) =>
els.map((el) => {
const step = Number(el.getAttribute('data-perm-step') ?? '0');
return {
step,
label:
el.getAttribute('data-perm-label') ??
(el.textContent ?? '').trim().slice(0, 40) ??
`step ${step}`,
mode: el.getAttribute('data-perm-mode'),
skipValidate: el.hasAttribute('data-perm-skip-validate'),
settle: Number(el.getAttribute('data-perm-settle') ?? '300')
};
})
);
// Deduplicate by step index — if multiple elements share an index, keep the
// first (demos should not do this, but portals sometimes mirror content).
const byIndex = new Map<number, (typeof raw)[number]>();
for (const r of raw) if (!byIndex.has(r.step)) byIndex.set(r.step, r);
return [...byIndex.values()]
.sort((a, b) => a.step - b.step)
.map((r) => ({
index: r.step,
label: r.label || `step ${r.step}`,
mode: parseMode(r.mode),
skipValidate: r.skipValidate,
settle: Number.isFinite(r.settle) && r.settle > 0 ? r.settle : 300
}));
}
/** Execute one step against the live page. Returns the label for logs. */
async function executeStep(page: Page, step: PermStep): Promise<void> {
const selector = `[data-perm-step="${step.index}"]`;
const locator = page.locator(selector).first();
switch (step.mode.kind) {
case 'click':
await locator.click({ timeout: 3000 });
break;
case 'focus':
await locator.focus({ timeout: 3000 });
break;
case 'type':
await locator.focus({ timeout: 3000 });
await page.keyboard.type(step.mode.value);
break;
case 'key':
await locator.focus({ timeout: 3000 });
await page.keyboard.press(step.mode.key);
break;
}
await page.waitForTimeout(step.settle);
}
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// ── URL variant matrix (v2 + v3 axis-at-a-time) ────────────────────────────
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
/**
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
* Universal baseline: every instrumented demo runs at least under `ltr`
* (plain URL) and `rtl` (`?perm.dir=rtl`). Additional per-component axes
* are discovered at runtime from the demo's `<meta name="perm-axes">` tag;
* each declared axis adds ONE variant that flips that axis from baseline
* while keeping everything else at the default — linear, not cartesian,
* so the matrix stays bounded.
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
*
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
* Supported axes (v3 initial set):
* dir — ltr (baseline) + rtl
* orientation — horizontal (baseline) + vertical
* disabled — unset + true
* readonly — unset + true
* invalid — unset + true
* loading — unset + true
*
* Demos opt in by listing the axes their `<script>` seed actually honours:
*
* ```svelte
* <svelte:head>
* <meta name="perm-axes" content="dir,orientation,disabled" />
* </svelte:head>
* ```
*
* Without the meta tag, only the universal `dir` axis runs. With the tag,
* the runner adds one variant per listed axis.
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
*/
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
type Variant = { suffix: string; label: string };
const AXIS_FLIP: Record<string, Variant> = {
dir: { suffix: '?perm.dir=rtl', label: 'rtl' },
orientation: { suffix: '?perm.orientation=vertical', label: 'orientation=vertical' },
disabled: { suffix: '?perm.disabled=true', label: 'disabled' },
readonly: { suffix: '?perm.readonly=true', label: 'readonly' },
invalid: { suffix: '?perm.invalid=true', label: 'invalid' },
loading: { suffix: '?perm.loading=true', label: 'loading' }
};
/** Read `<meta name="perm-axes" content="dir,orientation,...">` from the page. */
async function readDeclaredAxes(page: Page): Promise<string[]> {
try {
const content = await page.$eval(
'meta[name="perm-axes"]',
(el) => (el as HTMLMetaElement).content
);
return content
.split(',')
.map((s) => s.trim())
.filter(Boolean);
} catch {
// Meta not present — demo opted out of per-axis coverage.
return [];
}
}
/**
* Build the per-morfo variant list: baseline `ltr` + `rtl` (universal) +
* one flipped variant per axis the demo declared.
*/
function buildVariants(declaredAxes: string[]): Variant[] {
const base: Variant[] = [{ suffix: '', label: 'ltr' }, AXIS_FLIP.dir];
const extra: Variant[] = [];
for (const axis of declaredAxes) {
if (axis === 'dir') continue; // already in base
const v = AXIS_FLIP[axis];
if (v) extra.push(v);
}
return [...base, ...extra];
}
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// ── Main loop ───────────────────────────────────────────────────────────────
type StepResult =
| { kind: 'pass'; label: string }
| { kind: 'fail'; label: string; reason: string }
| { kind: 'error'; label: string; err: string }
| { kind: 'skipped'; label: string };
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
type VariantResult = {
variant: string;
url: string;
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
stepsTotal: number;
steps: StepResult[];
pageErrors: string[];
};
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
type RouteResult = {
morfo: string;
variants: VariantResult[];
};
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const BASE = process.argv[2] ?? (await probePort(5173, 5180));
if (!BASE) {
console.error('Could not find a running dev server on 5173-5180.');
console.error('Start it with `npm run dev` in another terminal.');
process.exit(2);
}
console.error(`Using dev server at ${BASE}`);
const morfos = await loadMorfos();
if (morfos.length === 0) {
console.error('No morfos found under src/uix/morfo/components/');
process.exit(2);
}
console.error(`Loaded ${morfos.length} morfo${morfos.length === 1 ? '' : 's'}`);
const browser = await chromium.launch();
const ctx = await browser.newContext();
const results: RouteResult[] = [];
const skipped: string[] = [];
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
/** Run one URL variant: load, cycle click-driven steps, report. */
async function runVariant(
base: string,
url: string,
variantLabel: string,
morfo: Morfo
): Promise<VariantResult | 'no-steps'> {
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const page = await ctx.newPage();
const pageErrors: string[] = [];
page.on('pageerror', (e) => pageErrors.push(e.message));
page.on('console', (m) => {
if (m.type() === 'error') pageErrors.push(m.text());
});
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const variantResult: VariantResult = {
variant: variantLabel,
url,
stepsTotal: 0,
steps: [],
pageErrors: []
};
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
try {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
await page.goto(base + url, { waitUntil: 'networkidle', timeout: 20000 });
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
await page.waitForTimeout(300);
soma test: complete permutation runner instrumentation (63/66 demos) Bulk-instruments the remaining 49 demo pages with `<meta name="perm-axes" content="dir" />`, bringing total coverage from 15/66 demos to 63/66. Only checkbox, radio-group, and switch remain skipped — they have no demo directory at `/test/soma/` (pre-existing gap, unrelated). ## Runner change `scripts/permutation-check.ts` now allows meta-only demos (no `data-perm-step` annotations) to participate: if a demo declares any axis via the meta tag, the runner still visits its baseline + RTL variants and runs the `[initial seed]` morfo validation at each, even without a click cycle. This gives passive LTR/RTL coverage to demos where a click-driven perm sequence doesn't apply naturally (leaf components like Avatar, Progress, Meter; grid/list components; pickers with inline controls). Key change in `probeDemo()`: a demo is "instrumented" if EITHER the meta tag OR at least one `data-perm-step` is present. Step-only demos get the v1 click cycle at baseline; meta-only demos get the v3 axis matrix with passive `[initial seed]` validation at each variant; declaring both is the full treatment. ## Bulk instrumentation For each of the 49 remaining demos, added: ```svelte <svelte:head> <title>… · Soma</title> <meta name="perm-axes" content="dir" /> </svelte:head> ``` Baseline axis (`dir`) only. Demos that benefit from additional axes (`orientation`, `disabled`, etc.) opt in incrementally — the bulk edit doesn't touch their `$state` seeds, only adds the meta tag. ## Coverage delta | Metric | Before | After | |--------|-------:|------:| | Instrumented demos | 15/66 | **63/66** | | URL variants | 35 | **131** | | Step validations | 106 | **202** | ~4× increase in URL variants visited per CI run. Most new demos contribute 2 variants each (ltr + rtl), validating morfo under direction flip. Fully-instrumented demos (tabs, toolbar, toggle-group, listbox) still contribute 3-4 variants with click cycles. ## Deferred - `context-menu` — right-click activation needs a `data-perm-mode='contextmenu'` runner extension. - `navigation-menu` — hover-driven, same as Tooltip. Tooltip was instrumented via `data-perm-mode="focus"` + long settle; NavigationMenu could follow the same pattern in a targeted batch. - `color-picker` — complex pointer state machine on Area / ChannelSlider. Meta-only covers the baseline structure. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 63/63 instrumented demos · 202 steps · 131 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// Meta-only demos (no perm-steps) still get passive `[initial seed]`
// validation at each URL variant — morfo compliance under RTL /
// orientation flips is worth catching even without a click cycle.
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// Any errors from the URL-seeded initial state count against the variant.
if (pageErrors.length > 0) {
const captured = pageErrors.splice(0, pageErrors.length).join(' | ');
variantResult.steps.push({
kind: 'fail',
label: '[initial seed]',
reason: `pageerror: ${captured.slice(0, 200)}`
});
} else {
// Validate morfo at the URL-seeded state BEFORE any clicks so v2
// state-injection alone (no click sequence) is exercised.
try {
const seedIssues = await validateMorfoAgainstDom(page, morfo);
if (seedIssues.length === 0) {
variantResult.steps.push({ kind: 'pass', label: '[initial seed]' });
} else {
variantResult.steps.push({
kind: 'fail',
label: '[initial seed]',
reason: seedIssues.map((i) => `[${i.kind}] ${i.message}`).join(' · ')
});
}
} catch (e) {
variantResult.steps.push({
kind: 'error',
label: '[initial seed]',
err: `validation: ${(e as Error).message.slice(0, 120)}`
});
}
}
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const executedIndices = new Set<number>();
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
let safety = 50;
while (safety-- > 0) {
const current = await collectSteps(page);
const next = current
.filter((s) => !executedIndices.has(s.index))
.sort((a, b) => a.index - b.index)[0];
if (!next) break;
const step = next;
executedIndices.add(step.index);
try {
await executeStep(page, step);
} catch (e) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.steps.push({
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
kind: 'error',
label: step.label,
err: (e as Error).message.slice(0, 120)
});
continue;
}
if (pageErrors.length > 0) {
const captured = pageErrors.splice(0, pageErrors.length).join(' | ');
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.steps.push({
kind: 'fail',
label: step.label,
reason: `pageerror: ${captured.slice(0, 200)}`
});
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
continue;
}
if (step.skipValidate) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.steps.push({ kind: 'pass', label: step.label + ' (no-validate)' });
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
continue;
}
try {
const issues = await validateMorfoAgainstDom(page, morfo);
if (issues.length === 0) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.steps.push({ kind: 'pass', label: step.label });
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} else {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.steps.push({
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
kind: 'fail',
label: step.label,
reason: issues.map((i) => `[${i.kind}] ${i.message}`).join(' · ')
});
}
} catch (e) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.steps.push({
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
kind: 'error',
label: step.label,
err: `validation: ${(e as Error).message.slice(0, 120)}`
});
}
}
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.pageErrors = pageErrors;
variantResult.stepsTotal = variantResult.steps.length;
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} catch (e) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
variantResult.steps.push({
kind: 'error',
label: '[navigation]',
err: (e as Error).message.slice(0, 120)
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
});
} finally {
await page.close();
}
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
return variantResult;
}
soma test: complete permutation runner instrumentation (63/66 demos) Bulk-instruments the remaining 49 demo pages with `<meta name="perm-axes" content="dir" />`, bringing total coverage from 15/66 demos to 63/66. Only checkbox, radio-group, and switch remain skipped — they have no demo directory at `/test/soma/` (pre-existing gap, unrelated). ## Runner change `scripts/permutation-check.ts` now allows meta-only demos (no `data-perm-step` annotations) to participate: if a demo declares any axis via the meta tag, the runner still visits its baseline + RTL variants and runs the `[initial seed]` morfo validation at each, even without a click cycle. This gives passive LTR/RTL coverage to demos where a click-driven perm sequence doesn't apply naturally (leaf components like Avatar, Progress, Meter; grid/list components; pickers with inline controls). Key change in `probeDemo()`: a demo is "instrumented" if EITHER the meta tag OR at least one `data-perm-step` is present. Step-only demos get the v1 click cycle at baseline; meta-only demos get the v3 axis matrix with passive `[initial seed]` validation at each variant; declaring both is the full treatment. ## Bulk instrumentation For each of the 49 remaining demos, added: ```svelte <svelte:head> <title>… · Soma</title> <meta name="perm-axes" content="dir" /> </svelte:head> ``` Baseline axis (`dir`) only. Demos that benefit from additional axes (`orientation`, `disabled`, etc.) opt in incrementally — the bulk edit doesn't touch their `$state` seeds, only adds the meta tag. ## Coverage delta | Metric | Before | After | |--------|-------:|------:| | Instrumented demos | 15/66 | **63/66** | | URL variants | 35 | **131** | | Step validations | 106 | **202** | ~4× increase in URL variants visited per CI run. Most new demos contribute 2 variants each (ltr + rtl), validating morfo under direction flip. Fully-instrumented demos (tabs, toolbar, toggle-group, listbox) still contribute 3-4 variants with click cycles. ## Deferred - `context-menu` — right-click activation needs a `data-perm-mode='contextmenu'` runner extension. - `navigation-menu` — hover-driven, same as Tooltip. Tooltip was instrumented via `data-perm-mode="focus"` + long settle; NavigationMenu could follow the same pattern in a targeted batch. - `color-picker` — complex pointer state machine on Area / ChannelSlider. Meta-only covers the baseline structure. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 63/63 instrumented demos · 202 steps · 131 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
/**
* Discover opt-in signals from the demo page: the declared axes (`perm-axes`
* meta) and whether any `data-perm-step` element exists. The demo is
* considered "instrumented" if it declares at least one of these.
*/
async function probeDemo(
base: string,
morfoKebab: string
): Promise<{ axes: string[]; hasSteps: boolean } | 'no-demo'> {
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const page = await ctx.newPage();
try {
const res = await page.goto(`${base}/test/soma/${morfoKebab}`, {
waitUntil: 'networkidle',
timeout: 20000
});
if (!res || res.status() >= 400) return 'no-demo';
soma test: complete permutation runner instrumentation (63/66 demos) Bulk-instruments the remaining 49 demo pages with `<meta name="perm-axes" content="dir" />`, bringing total coverage from 15/66 demos to 63/66. Only checkbox, radio-group, and switch remain skipped — they have no demo directory at `/test/soma/` (pre-existing gap, unrelated). ## Runner change `scripts/permutation-check.ts` now allows meta-only demos (no `data-perm-step` annotations) to participate: if a demo declares any axis via the meta tag, the runner still visits its baseline + RTL variants and runs the `[initial seed]` morfo validation at each, even without a click cycle. This gives passive LTR/RTL coverage to demos where a click-driven perm sequence doesn't apply naturally (leaf components like Avatar, Progress, Meter; grid/list components; pickers with inline controls). Key change in `probeDemo()`: a demo is "instrumented" if EITHER the meta tag OR at least one `data-perm-step` is present. Step-only demos get the v1 click cycle at baseline; meta-only demos get the v3 axis matrix with passive `[initial seed]` validation at each variant; declaring both is the full treatment. ## Bulk instrumentation For each of the 49 remaining demos, added: ```svelte <svelte:head> <title>… · Soma</title> <meta name="perm-axes" content="dir" /> </svelte:head> ``` Baseline axis (`dir`) only. Demos that benefit from additional axes (`orientation`, `disabled`, etc.) opt in incrementally — the bulk edit doesn't touch their `$state` seeds, only adds the meta tag. ## Coverage delta | Metric | Before | After | |--------|-------:|------:| | Instrumented demos | 15/66 | **63/66** | | URL variants | 35 | **131** | | Step validations | 106 | **202** | ~4× increase in URL variants visited per CI run. Most new demos contribute 2 variants each (ltr + rtl), validating morfo under direction flip. Fully-instrumented demos (tabs, toolbar, toggle-group, listbox) still contribute 3-4 variants with click cycles. ## Deferred - `context-menu` — right-click activation needs a `data-perm-mode='contextmenu'` runner extension. - `navigation-menu` — hover-driven, same as Tooltip. Tooltip was instrumented via `data-perm-mode="focus"` + long settle; NavigationMenu could follow the same pattern in a targeted batch. - `color-picker` — complex pointer state machine on Area / ChannelSlider. Meta-only covers the baseline structure. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 63/63 instrumented demos · 202 steps · 131 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
await page.waitForTimeout(200);
const axes = await readDeclaredAxes(page);
const hasSteps = (await collectSteps(page)).length > 0;
return { axes, hasSteps };
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} catch {
return 'no-demo';
} finally {
await page.close();
}
}
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
for (const morfo of morfos) {
const variantResults: VariantResult[] = [];
soma test: complete permutation runner instrumentation (63/66 demos) Bulk-instruments the remaining 49 demo pages with `<meta name="perm-axes" content="dir" />`, bringing total coverage from 15/66 demos to 63/66. Only checkbox, radio-group, and switch remain skipped — they have no demo directory at `/test/soma/` (pre-existing gap, unrelated). ## Runner change `scripts/permutation-check.ts` now allows meta-only demos (no `data-perm-step` annotations) to participate: if a demo declares any axis via the meta tag, the runner still visits its baseline + RTL variants and runs the `[initial seed]` morfo validation at each, even without a click cycle. This gives passive LTR/RTL coverage to demos where a click-driven perm sequence doesn't apply naturally (leaf components like Avatar, Progress, Meter; grid/list components; pickers with inline controls). Key change in `probeDemo()`: a demo is "instrumented" if EITHER the meta tag OR at least one `data-perm-step` is present. Step-only demos get the v1 click cycle at baseline; meta-only demos get the v3 axis matrix with passive `[initial seed]` validation at each variant; declaring both is the full treatment. ## Bulk instrumentation For each of the 49 remaining demos, added: ```svelte <svelte:head> <title>… · Soma</title> <meta name="perm-axes" content="dir" /> </svelte:head> ``` Baseline axis (`dir`) only. Demos that benefit from additional axes (`orientation`, `disabled`, etc.) opt in incrementally — the bulk edit doesn't touch their `$state` seeds, only adds the meta tag. ## Coverage delta | Metric | Before | After | |--------|-------:|------:| | Instrumented demos | 15/66 | **63/66** | | URL variants | 35 | **131** | | Step validations | 106 | **202** | ~4× increase in URL variants visited per CI run. Most new demos contribute 2 variants each (ltr + rtl), validating morfo under direction flip. Fully-instrumented demos (tabs, toolbar, toggle-group, listbox) still contribute 3-4 variants with click cycles. ## Deferred - `context-menu` — right-click activation needs a `data-perm-mode='contextmenu'` runner extension. - `navigation-menu` — hover-driven, same as Tooltip. Tooltip was instrumented via `data-perm-mode="focus"` + long settle; NavigationMenu could follow the same pattern in a targeted batch. - `color-picker` — complex pointer state machine on Area / ChannelSlider. Meta-only covers the baseline structure. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 63/63 instrumented demos · 202 steps · 131 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const probe = await probeDemo(BASE, morfo.kebab);
if (probe === 'no-demo') {
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
skipped.push(morfo.kebab);
continue;
}
soma test: complete permutation runner instrumentation (63/66 demos) Bulk-instruments the remaining 49 demo pages with `<meta name="perm-axes" content="dir" />`, bringing total coverage from 15/66 demos to 63/66. Only checkbox, radio-group, and switch remain skipped — they have no demo directory at `/test/soma/` (pre-existing gap, unrelated). ## Runner change `scripts/permutation-check.ts` now allows meta-only demos (no `data-perm-step` annotations) to participate: if a demo declares any axis via the meta tag, the runner still visits its baseline + RTL variants and runs the `[initial seed]` morfo validation at each, even without a click cycle. This gives passive LTR/RTL coverage to demos where a click-driven perm sequence doesn't apply naturally (leaf components like Avatar, Progress, Meter; grid/list components; pickers with inline controls). Key change in `probeDemo()`: a demo is "instrumented" if EITHER the meta tag OR at least one `data-perm-step` is present. Step-only demos get the v1 click cycle at baseline; meta-only demos get the v3 axis matrix with passive `[initial seed]` validation at each variant; declaring both is the full treatment. ## Bulk instrumentation For each of the 49 remaining demos, added: ```svelte <svelte:head> <title>… · Soma</title> <meta name="perm-axes" content="dir" /> </svelte:head> ``` Baseline axis (`dir`) only. Demos that benefit from additional axes (`orientation`, `disabled`, etc.) opt in incrementally — the bulk edit doesn't touch their `$state` seeds, only adds the meta tag. ## Coverage delta | Metric | Before | After | |--------|-------:|------:| | Instrumented demos | 15/66 | **63/66** | | URL variants | 35 | **131** | | Step validations | 106 | **202** | ~4× increase in URL variants visited per CI run. Most new demos contribute 2 variants each (ltr + rtl), validating morfo under direction flip. Fully-instrumented demos (tabs, toolbar, toggle-group, listbox) still contribute 3-4 variants with click cycles. ## Deferred - `context-menu` — right-click activation needs a `data-perm-mode='contextmenu'` runner extension. - `navigation-menu` — hover-driven, same as Tooltip. Tooltip was instrumented via `data-perm-mode="focus"` + long settle; NavigationMenu could follow the same pattern in a targeted batch. - `color-picker` — complex pointer state machine on Area / ChannelSlider. Meta-only covers the baseline structure. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 63/63 instrumented demos · 202 steps · 131 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// Opt-in signal: either a `perm-axes` meta OR at least one
// `data-perm-step` element. Meta-only demos get passive `[initial seed]`
// validation at each URL variant; step-only demos get the v1 click cycle
// at baseline; declaring both is the full v1+v2+v3 treatment.
if (probe.axes.length === 0 && !probe.hasSteps) {
skipped.push(morfo.kebab);
continue;
}
const variants = buildVariants(probe.axes);
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
for (const v of variants) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const url = `/test/soma/${morfo.kebab}${v.suffix}`;
const r = await runVariant(BASE, url, v.label, morfo);
if (r === 'no-steps') continue;
variantResults.push(r);
}
soma test: complete permutation runner instrumentation (63/66 demos) Bulk-instruments the remaining 49 demo pages with `<meta name="perm-axes" content="dir" />`, bringing total coverage from 15/66 demos to 63/66. Only checkbox, radio-group, and switch remain skipped — they have no demo directory at `/test/soma/` (pre-existing gap, unrelated). ## Runner change `scripts/permutation-check.ts` now allows meta-only demos (no `data-perm-step` annotations) to participate: if a demo declares any axis via the meta tag, the runner still visits its baseline + RTL variants and runs the `[initial seed]` morfo validation at each, even without a click cycle. This gives passive LTR/RTL coverage to demos where a click-driven perm sequence doesn't apply naturally (leaf components like Avatar, Progress, Meter; grid/list components; pickers with inline controls). Key change in `probeDemo()`: a demo is "instrumented" if EITHER the meta tag OR at least one `data-perm-step` is present. Step-only demos get the v1 click cycle at baseline; meta-only demos get the v3 axis matrix with passive `[initial seed]` validation at each variant; declaring both is the full treatment. ## Bulk instrumentation For each of the 49 remaining demos, added: ```svelte <svelte:head> <title>… · Soma</title> <meta name="perm-axes" content="dir" /> </svelte:head> ``` Baseline axis (`dir`) only. Demos that benefit from additional axes (`orientation`, `disabled`, etc.) opt in incrementally — the bulk edit doesn't touch their `$state` seeds, only adds the meta tag. ## Coverage delta | Metric | Before | After | |--------|-------:|------:| | Instrumented demos | 15/66 | **63/66** | | URL variants | 35 | **131** | | Step validations | 106 | **202** | ~4× increase in URL variants visited per CI run. Most new demos contribute 2 variants each (ltr + rtl), validating morfo under direction flip. Fully-instrumented demos (tabs, toolbar, toggle-group, listbox) still contribute 3-4 variants with click cycles. ## Deferred - `context-menu` — right-click activation needs a `data-perm-mode='contextmenu'` runner extension. - `navigation-menu` — hover-driven, same as Tooltip. Tooltip was instrumented via `data-perm-mode="focus"` + long settle; NavigationMenu could follow the same pattern in a targeted batch. - `color-picker` — complex pointer state machine on Area / ChannelSlider. Meta-only covers the baseline structure. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 63/63 instrumented demos · 202 steps · 131 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
if (variantResults.length === 0) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
skipped.push(morfo.kebab);
continue;
}
results.push({ morfo: morfo.kebab, variants: variantResults });
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
await browser.close();
// ── Report ──────────────────────────────────────────────────────────────────
console.log('');
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
let passedRoutes = 0;
let failedRoutes = 0;
let totalSteps = 0;
let totalFailed = 0;
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
for (const r of results) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const bad = r.variants
.flatMap((v) => v.steps)
.filter((s) => s.kind !== 'pass');
totalSteps += r.variants.reduce((acc, v) => acc + v.steps.length, 0);
totalFailed += bad.length;
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
if (bad.length === 0) {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const variantCount = r.variants.length;
const stepCount = r.variants.reduce((acc, v) => acc + v.steps.length, 0);
console.log(
`PASS ${r.morfo.padEnd(20)} ${variantCount} variant${variantCount === 1 ? '' : 's'} · ${stepCount} step${stepCount === 1 ? '' : 's'}`
);
for (const v of r.variants) {
console.log(` [${v.variant}] ${v.url}`);
for (const s of v.steps) console.log(` · ${s.label}`);
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
passedRoutes++;
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} else {
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
console.log(`FAIL ${r.morfo.padEnd(20)} ${bad.length} step${bad.length === 1 ? '' : 's'} failed`);
for (const v of r.variants) {
console.log(` [${v.variant}] ${v.url}`);
for (const s of v.steps) {
if (s.kind === 'pass') console.log(` · ${s.label}`);
else if (s.kind === 'skipped') console.log(` · ${s.label} (skipped)`);
else if (s.kind === 'fail') console.log(` ✖ ${s.label} ${s.reason}`);
else console.log(` ✖ ${s.label} ERROR ${s.err}`);
}
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
failedRoutes++;
morfo: add permutation runner (A37) — third validation layer for state transitions ## Why `morfo:check` validates single-state DOM at page load; `smoke` catches hydration-time errors. Neither ejercices state TRANSITIONS — and that is where every reactivity bug we caught this week actually lived: - toolbar A35 loop (mount-time, not caught by either) - form A36 microtask loop (mount-time with onChange) - slider RTL transform (static value, off-by-thumb-width) All passed the existing CI. All would have failed a "click, re-validate" pass. ## What - **`scripts/permutation-check.ts`** — Playwright runner. For each demo, collects `data-perm-step="N"` elements, executes them in ascending order (re-discovering after each step so portal-gated controls get counted), and re-validates the component's morfo + captures any `pageerror` / `console.error` between steps. - **`data-perm-*` authoring convention** — step index, human label, mode (click / focus / type / key), settle delay, skip-validate flag. Demos opt in incrementally; the runner SKIPs annotation-less pages without failing. - **`src/uix/morfo/PERMUTATION_RUNNER.md`** — full design doc covering what it validates, the annotation convention, example, failure format, and v2–v4 roadmap (URL-driven states → morfo-inferred cycles → MutationObserver ordering for Sema). - **COMPONENT_GUIDE A37 + checklist item 40** — instrumentation is now a ship-gate rule; doc lists coverage targets (overlays / toggleables / composite roving / forms / RTL) and explicit skips (alerts, file pickers). - **`npm run perm:check`** — pipeline entry, exit codes 0/1/2 parallelling `morfo:check`. ## Instrumented demos (v1 seed) - dialog — 2 steps: open via trigger → close via Close button. Step 1 is inside `{#if open}` + Portal; runner's dynamic re-discovery handles it. - tabs — 3 steps: initial tab 1 → switch to tab 2 → switch to tab 3. - toolbar — 3 steps: toggle bold / italic / underline GroupItems (exercises the A35 pattern that previously looped). Result: 8/8 permutations pass; 63 demos SKIPPED pending instrumentation. ## Verification - `npm run check`: 0 errors in soma/morfo/scripts scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 instrumented demos, 8/8 permutations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
}
console.log('');
if (skipped.length > 0) {
console.log(
`SKIPPED (${skipped.length}): ${skipped.slice(0, 10).join(', ')}${skipped.length > 10 ? ', …' : ''}`
);
console.log(' (demo has no `data-perm-step` annotations yet — see src/uix/morfo/PERMUTATION_RUNNER.md)');
}
console.log('');
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const totalVariants = results.reduce((acc, r) => acc + r.variants.length, 0);
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const summary =
failedRoutes === 0
morfo: permutation runner v3 — per-demo axis matrix via `<meta name="perm-axes">` v2 ran every demo under a hard-coded `ltr` + `rtl` matrix. v3 lets each demo declare which additional axes it honours — `orientation`, `disabled`, `readonly`, `invalid`, `loading` — and the runner adds ONE variant per declared axis (axis-at-a-time, not cartesian, so the matrix stays bounded at O(N) instead of O(2^N)). ## Convention ```svelte <svelte:head> <meta name="perm-axes" content="dir,orientation,disabled" /> </svelte:head> ``` `dir` is universal (always runs). The other axes are opt-in: they only produce variants when the demo both lists them in the meta AND wires the corresponding state from `Perms.getOr(...)`. Without the meta, only `ltr` + `rtl` run. ## Runner changes - New `discoverAxes(base, morfoKebab)` pass: loads the baseline URL, reads `<meta name="perm-axes">`, returns the declared axis list. - `buildVariants(declaredAxes)` composes `ltr` + `rtl` + one `?perm.X=Y` variant per declared axis from the `AXIS_FLIP` map. - `[initial seed]` step validates morfo at each URL before any click, so URL-seeded state alone is exercised (catches seed regressions even when the click cycle never fires). ## Demo opt-in (this commit) - `dialog` — `perm-axes="dir"` (no orientation / disabled / loading axes apply to Dialog). - `tabs` — `perm-axes="dir,orientation,disabled"` + wires `orientation` + `disabled` to the first `Tabs.Provider`. - `toolbar` — `perm-axes="dir,orientation"` + wires `orientation` to the first `Toolbar.Provider`. Result: 3 demos, 9 URL variants, 34 step validations per CI run (v2 was 22 steps across 6 variants). ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` v3 section: supported axis table, 3-step opt-in recipe, roadmap updated (v4 = morfo-inferred value cycling, v5 = MutationObserver ordering for Sema). ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 34 steps · 9 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
? `All ${passedRoutes} instrumented demo${passedRoutes === 1 ? '' : 's'} passed. (${totalSteps} step${totalSteps === 1 ? '' : 's'} across ${totalVariants} URL variant${totalVariants === 1 ? '' : 's'}.)`
morfo: permutation runner v2 — URL-driven state seeds + RTL variant v1 shipped click-based state cycles. v2 adds URL-seeded initial state so the runner can reach any state the component exposes without walking a click sequence, and runs every instrumented demo TWICE in CI (LTR + RTL) to catch direction-specific regressions like the slider thumb transform that landed this week. ## New test-route infrastructure - `src/routes/test/soma/_perms.svelte.ts` — parses `?perm.X=Y` query params into a typed `Perms` record (naive coercion: `'true'` → boolean, digits → number, else string). Exposed via `Perms.getOr({})` context. Underscore prefix so SvelteKit does not route it. - `+layout.svelte` — parses perms once at mount, sets context, honours `?perm.dir=rtl` and `?perm.locale=en` by routing through `App.setDir` / `App.setLocale` so the existing control strip stays in sync. ## Demo opt-in Three demos seed initial state from URL params (v1 + v2 combined): - `dialog` — `?perm.open=true` lands open. - `tabs` — `?perm.value=tab-2` lands on that tab. - `toolbar` — `?perm.format=bold,italic` + `?perm.align=center` preselect toggles. ## Runner extension `scripts/permutation-check.ts` now runs each instrumented demo under a matrix of URL variants (currently `ltr` + `rtl`), validates morfo at the URL-seeded state BEFORE any clicks (logged as `[initial seed]`), then runs the v1 click cycle. Output format updated to show per-variant blocks. Result: 3 instrumented demos × 2 variants = 22 step validations per CI run. RTL seeding reproduces the specific state machine the slider `translate(50%, -50%)` bug needed to fail — had v2 been in place that day, the regression would have fired before shipping. ## Docs `src/uix/morfo/PERMUTATION_RUNNER.md` gains the v2 section (layout plumbing, demo opt-in, URL variant table, `[initial seed]` step semantics) and marks v1 + v2 as shipped 2026-04-22. v3 (morfo-inferred per-component axis matrix) and v4 (MutationObserver ordering for Sema) remain on the roadmap. ## Verification - `npm run check`: 0 errors in soma/morfo/test-route scope. - `npm run smoke`: 65/65 routes. - `npm run morfo:check`: 66/66 morfos. - `npm run perm:check`: 3/3 demos · 22 steps across 2 variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
: `${failedRoutes} demo${failedRoutes === 1 ? '' : 's'} failed (${totalFailed}/${totalSteps} steps): ${results
.filter((r) => r.variants.some((v) => v.steps.some((s) => s.kind !== 'pass')))
.map((r) => r.morfo)
.join(', ')}`;
console.log(summary);
process.exit(failedRoutes === 0 ? 0 : 1);

Powered by TurnKey Linux.