Files
whynot-design/scripts/designbook-refresh.mjs
tegwick 7cf524137f feat(refresh): make designbook-refresh orchestrator + drift-triage runbook (WHYNOT-WP-0002 T09)
scripts/designbook-refresh.mjs chains the automatable steps
(check->pull->sync->ir->adapt-lit->parity) and stops for the human drift-triage
step, propagating the adapter-contract exit codes (3=stop for triage, 4=parity
fail). Gating steps call the node scripts directly so make doesn't collapse the
3/4 codes to 2. Best-effort steps (check/pull/sync) warn and continue; --no-pull
/--no-check/--no-parity flags. Documented the loop in stack-and-commands.md and a
step-6 drift-resolution runbook in designbook/README.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 09:19:44 +02:00

74 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// =============================================================
// designbook-refresh.mjs — the refresh orchestrator (WHYNOT-WP-0002 · T09)
//
// One routine to keep the Lit stack current with the canonical React designbook.
// Runs the *automatable* steps (15, 7) and stops for the *human* step (6) when
// drift is detected, honouring the adapter-contract exit codes:
//
// 1. make designbook-check — has the cloud designbook moved? (best-effort)
// 2. make designbook-pull — pull the React designbook → designbook/ (best-effort)
// 3. make designbook-sync — record the diff → RecentChanges.md (best-effort)
// 4. make ir — re-extract the IR (the blueprint) (gate)
// 5. make adapt-lit — tokens + scaffold + drift (drift gate → 3)
// 6. (human) resolve drift — adapters/lit/drift/*.md ← STOP here on drift
// 7. make parity-lit — contract + visual parity (parity gate → 4)
//
// Best-effort steps (13) need network / the local `claude` binary / llm-connect;
// their failure warns and continues (the IR re-extracts from the current mirror).
// Steps 4/5/7 are deterministic and gate. Exit: highest applicable adapter code —
// 0 ok · 2 usage · 3 drift (stop for triage) · 4 parity failure · 5 internal.
//
// Flags: --no-check --no-pull --no-parity (skip the matching step)
// =============================================================
import { spawnSync } from "node:child_process";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const REPO = join(dirname(fileURLToPath(import.meta.url)), "..");
const args = new Set(process.argv.slice(2));
function run(label, cmd, { gate = false } = {}) {
console.log(`\n\x1b[1m▶ ${label}\x1b[0m (${cmd})`);
const r = spawnSync(cmd, { cwd: REPO, shell: true, stdio: "inherit" });
const code = r.status == null ? 5 : r.status;
if (code !== 0 && !gate) console.log(`\x1b[33m ↪ step exited ${code} — best-effort, continuing.\x1b[0m`);
return code;
}
function done(code, msg) {
console.log(`\n\x1b[1m${code === 0 ? "✓" : "■"} designbook-refresh: ${msg}\x1b[0m`);
process.exit(code);
}
// 13: best-effort (never abort the refresh).
if (!args.has("--no-check")) run("1/7 cloud-ahead check", "make designbook-check");
if (!args.has("--no-pull")) run("2/7 pull React designbook", "make designbook-pull");
run("3/7 record diff (RecentChanges.md)", "make designbook-sync");
// Gating steps call the node scripts DIRECTLY (not via make, which collapses any
// recipe failure to exit 2 and would hide the adapter's 3/4 drift/parity codes).
const N = JSON.stringify(process.execPath);
// 4: IR extraction — deterministic gate; without it nothing downstream is valid.
const irCode = run("4/7 extract IR", `${N} scripts/ir-extract.mjs`, { gate: true });
if (irCode !== 0) done(irCode === 5 ? 5 : 2, `IR extraction failed (exit ${irCode}). Fix the designbook/ source before refreshing.`);
// 5: adapt-lit — drift gate. Exit 3 ⇒ stop for human triage (step 6).
const adaptCode = run("5/7 adapt Lit (tokens + scaffold + drift)", `${N} adapters/lit/adapt.mjs`, { gate: true });
if (adaptCode === 3) {
done(3, "DRIFT detected (step 6 is yours). Resolve adapters/lit/drift/*.md per " +
".claude/rules/designbook-propagation.md, then re-run `make designbook-refresh --no-pull` to re-check + run parity.");
}
if (adaptCode !== 0) done(adaptCode === 2 ? 2 : 5, `adapt-lit failed (exit ${adaptCode}).`);
// 6: human drift resolution — only reached when adapt-lit is clean.
// 7: parity gate.
if (args.has("--no-parity")) done(0, "tokens + scaffold clean; parity skipped (--no-parity).");
const parityCode = run("7/7 parity (contract + visual)", `${N} adapters/lit/parity.mjs`, { gate: true });
if (parityCode === 4) done(4, "PARITY FAILURE — see adapters/lit/parity/_parity.json.");
if (parityCode !== 0) done(parityCode === 2 ? 2 : 5, `parity-lit failed (exit ${parityCode}).`);
done(0, "in sync — IR extracted, Lit adapted with no drift, parity passed.");