feat: external design-system registry + ve:learn token learning#3
Merged
Conversation
Spec-first evals for the external design-system feature, in the existing fixture/expectation idiom: one extraction case per ve:learn modality (code: a synthetic acme-terracotta brand token module -> golden token set, exact match; url: saved HTML+CSS site served via injected fetch, no live network; image: quantized-palette PNG, tolerance match) plus loader cases (resolution order, repo-clone collision dedupe, unknown-preset fallback warning, built-ins never shadowed, malformed systems fail loudly). Reports required --ve-* token coverage per case. All fixtures are invented brands — real design systems are private user artifacts and never ship in this repo. The synthetic code fixture still exercises every mapping path: name hints, muted/faint contrast ranking, hue-classified danger, grid/weight/easing extraction, and defaults. Mutation-verified: flipping the muted-contrast ranking or the accent-fill precompositing makes the suite fail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Design systems are user-owned directories (tokens.css with --ve-* custom properties in a :root block + manifest.json with provenance/fonts/notes) resolved in order: $ARTIFACTURE_DESIGN_DIR -> ~/.artifacture/ design-systems -> <repo>/design-systems (first hit wins; deduped when ~/.artifacture is itself a repo clone). ve:export scans the source for preset references, re-scopes the winning tokens.css to [data-ve-preset="<slug>"] with derived diagram fallbacks and manifest font imports, and inlines it into the standalone HTML. Unknown names warn and fall back to built-in mono-industrial tokens; malformed systems throw instead of silently falling through; built-in names never consult the registry. The repo ships NO systems: repo-local design-systems/ is gitignored (only its README is tracked) because brand tokens are usually private and ~/.artifacture may BE a repo clone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r image npm run ve:learn -- <source> --name <slug> [--out <dir>] drafts a design system into the registry (default: $ARTIFACTURE_DESIGN_DIR, else ~/.artifacture/design-systems — the private, user-global home for brand tokens). Modalities: code files (named hex tokens, font stacks, weight/size ramps, grid geometry, easing), URLs (:root custom props, @font-face, dominant colors across fetched linked CSS), and images (palette quantization via a canvas in Playwright's bundled Chromium). A shared deterministic mapper turns any extraction into a full --ve-* token set (name hints first, then contrast/saturation/coverage statistics; panel/accent tints precomposited to solid hex so light systems survive naive contrast tooling). The draft manifest records provenance plus an extraction report: per-token decisions, size ramp, and required-token coverage. Pure heuristics live in learn-extractors.mjs; fetch/browser I/O in learn-sources.mjs (injectable fetch, no network in tests). The evals/design-systems suite is the spec these are fit to. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… to 0.8.0 - npm test (node:test: loader, extractors, diagram layout) added and run in the CI static job - npm run ve:eval now runs the design-system eval suite as its second leg (image case uses the chromium already installed in the evals job) - docs/design-systems.md: registry format, resolution order and the ~/.artifacture-repo-clone collision, ve:learn modalities, and the agent-assisted refinement flow. Design systems are intentionally external, user-owned artifacts — the repo ships the mechanism and synthetic eval fixtures only; private brand registries live outside the repo - README, CONTRIBUTING, SKILL.md updated; CHANGELOG 0.8.0; version sources bumped in lockstep (check:manifests green) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7 tasks
theclaymethod
force-pushed
the
feat/design-system-registry
branch
from
July 15, 2026 18:46
9ffd438 to
476c41c
Compare
Output escaping where untrusted content reaches the shared artifact (found in adversarial review): - tokens.css values are validated before inlining: the declaration parser's value group ([^;}]+) admits '</style><script>...' payloads, so any value containing '<' or control characters is rejected loudly (scripts/ve-mdx/design-systems.mjs assertSafeTokenValue, enforced in scopeTokensCss) - manifest.fonts.imports must be an array of plain http(s) URLs with no quote/angle-bracket/backslash/paren/whitespace characters (validatedFontImports); non-arrays throw instead of crashing - new injectDesignSystemCss() is the single injection point: refuses CSS containing '</style', '<!--', or '<script', and uses a function replacer so '$&'-style sequences insert verbatim instead of splicing the matched '</head>' (export.mjs now uses it) - ve:learn sanitizes extracted values at ingestion (angle brackets and control bytes stripped from font stacks and emitted tokens), so learned systems are clean at the source - SSRF guardrails in learn-sources.mjs: private/loopback hosts refused by default (--allow-private to override), ~5MB response cap, 15s fetch timeout, content-type check Evals/tests: new loader-hostile-tokens-inert eval (style-breakout throws; hostile font imports throw; '$&' lands verbatim with exactly one style block) — mutation-verified by disabling the value guard; extraction evals now do value-sanity (parseable colors / numeric / non-empty stacks) on top of key coverage, guarding the implementation-generated goldens against agreeing on garbage; unit tests for the injector and isPrivateHost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this change and why?
Design systems become user-owned artifacts maintained outside the skill and repo, resolved from a registry at export time — your brand tokens survive skill upgrades because they were never inside the skill. The repo intentionally ships NO design systems: brand token sets are usually private product material, so the public repo carries the mechanism plus synthetic eval fixtures only, and your (private) systems live in your own registry. See
docs/design-systems.mdfor the format and for creating your own withve:learn.1. External design-system registry
tokens.css(--ve-*custom properties in a:rootblock) +manifest.json(description, source provenance, font loads/fallbacks, hard-rule notes). Full docs indocs/design-systems.md.$ARTIFACTURE_DESIGN_DIR→~/.artifacture/design-systems/→<repo>/design-systems/. The search list is deduped for the case where~/.artifactureis a clone of this repo (then slots 2 and 3 are the same directory), and repo-localdesign-systems/*is gitignored (only its README is tracked) so learned/private systems can't be committed by accident.ve:exportscans the source for preset references, re-scopes the winningtokens.cssto[data-ve-preset="<slug>"](plus derived diagram fallbacks and manifest font imports), and inlines it into the standalone HTML. Unknown names warn and fall back to built-inmono-industrialtokens; malformed systems fail loudly instead of silently falling through; built-in names never consult the registry (no shadowing).2.
ve:learn— token learningnpm run ve:learn -- <source> --name <slug> [--out <dir>]drafts a system (default output: your user-global registry) from::rootcustom props,@font-face, dominant colorsA shared deterministic mapper (name hints → contrast/saturation/coverage statistics) produces the full
--ve-*set; the draft manifest records provenance and an extraction report (per-token decisions, required-token coverage). The agent-assisted refinement flow is documented indocs/design-systems.md.The eval suite defines "good" for token learning; the extractors are fit to it.
evals/design-systems/(second leg ofnpm run ve:eval, same fixture/expectation idiom as the verifier evals) pins fixture sources to golden token sets per modality — exact hex/font-stack match for code and url, tolerance match for image, no live network — plus loader cases for resolution order, the repo-clone collision, unknown-preset fallback, built-in non-shadowing, and loud malformed-system failures. All fixtures are synthetic invented brands (acme-terracotta, an Acme site, a generated palette PNG); the code fixture still exercises every mapping path (name hints, muted/faint contrast ranking, hue-classified danger, grid/weight/easing extraction, defaults). Mutation-verified: flipping the muted-contrast ranking or the accent-fill precompositing makes the suite fail.Security (adversarial-review fixes, included)
Design systems are third-party content that gets inlined into shared HTML artifacts, so the boundary is hardened end to end:
<or control characters are rejected loudly before inlining (the declaration parser's value group would otherwise admit</style><script>…payloads);manifest.fonts.importsmust be an array of plain http(s) URLs (no quotes/angle brackets/backslashes/parens/whitespace), rejected loudly otherwise.injectDesignSystemCss()helper refuses CSS containing</style,<!--, or<script, and uses a function replacer so$&-style replacement metacharacters insert verbatim instead of splicing the matched</head>.ve:learnstrips angle brackets/control bytes from extracted values at ingestion, so learned systems never contain them.--allow-privateto override), ~5MB response cap, 15s fetch timeout, content-type checks.loader-hostile-tokens-inertcase (style-breakout throws; hostile font imports throw;$&lands verbatim, exactly one style block) — mutation-verified by disabling the value guard. Extraction evals now also check value sanity (parseable colors, numeric tokens, non-empty font stacks), not just key presence, since the token goldens are implementation-generated (hand-verified against fixture sources).Why no shipped example system?
Intentional. A design system encodes a brand; the reference flow is: run
ve:learnagainst your own token source/site/image into~/.artifacture/design-systems/<slug>/, then refine per the docs. The syntheticacme-terracottaeval fixture doubles as the reference shape for whatve:learnconsumes and produces. The end-to-end path (registry resolution from the user-global dir → tokens inlined into a standalone export) is covered by the loader evals and was verified against a real private system in a local registry.Found and fixed along the way: derived
panel-strong/row/accent-softtints are precomposited to solid hex — opposite-polarity alpha bases read as dark surfaces to contrast tooling and flipped light systems' tables unreadable (caught by running a branded artifact through ve-verify).Test plan
npm test— 13 node:test cases (loader resolution order + scoping + preset scanning; one test per extractor modality incl. pure palette quantization); newly wired into the CI static jobnpm run ve:eval— all seeded verifier violations + clean fixtures pass, plus all 8 design-system eval cases (coverage: 47/47 required tokens in every modality)npm run ve:check,npm run ve:check-integrity,npm run check:manifests(versions bumped in lockstep to 0.8.0)~/.artifacture/design-systems/resolves by preset name and its tokens are inlined into the exported HTML with no repo-local copy presentChecklist
npm run ve:checkpasses.npm run ve:evalpasses.🤖 Generated with Claude Code