ui-contract-design
Skill3 starsChanged 17 days agoMITView on GitHub
Align an existing visual spec (design.md) against the structural spec - add the division-of-labor statement, move structural content out leaving a pointer, replace old component-style names with allowlist names, and move literal values into the token file so design.md holds rules rather than values. Use when a project has visual conventions that need to become a spec with a clean boundary. Or originate one when there are none at all - choose a brand direction on attributes rather than colours, derive the values, tune them on a page. Chinese: 生成品牌/配色规范/token 命名/主题调校.
In this repository15 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- constraint-by-typesSkill
- decision-interviewSkill
- decision-recordSkill
- deliverable-languageSkill
- spec-before-codeSkill
- ui-contract-assetsSkill
- ui-contract-auditSkill
- ui-contract-baselineSkill
- ui-contract-enforceSkill
- ui-contract-glossarySkill
- ui-contract-layoutSkill
- ui-contract-setupSkill
- ui-contractSkill
- ui-contract-wireframeSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 82
If the project already has a visual spec, do not rewrite it.
- Very specific: line 115
Do not silently invent values back into design.md to make it self-contained.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Visual spec — design.md
Produces one artifact: design.md, plus references into the project's token file. Skeleton in design-md-outline.md. Read it.
Two branches, same artifact and same completion criteria — so this is one unit, not two:
| What the project has | Branch |
| A visual spec, a token source, or mockups | align — this file, below |
| None of those | originate — originate.md. Read it before asking anything. |
For the interview discipline, call the Skill tool with "decision-interview". For which document a thing belongs in, call the Skill tool with "ui-contract-glossary". For the deliverable's language and terminology, call the Skill tool with "deliverable-language". For handling change requests, call the Skill tool with "spec-before-code".
Preconditions
Artifact paths come from docs/ui-contract/config.md's paths table. If that file doesn't exist, the specs are at the repo root. Never assume a path the config contradicts.
| Condition | Action |
layout.md does not exist | Stop. The allowlist is what region names get aligned against; without it this step produces a second vocabulary. Call the Skill tool with "ui-contract-layout" first |
design.md exists but layout.md does not | Stop and say so. Aligning a boundary against a document that doesn't exist means inventing the other half |
No visual conventions exist anywhere — no design.md, no token source, no mockups — and the visual half is authorised: docs/ui-contract/config.md says so, or the user asked for it in this session | Go to originate.md. Do not start from this file's align procedure — there is nothing to align against |
| No visual conventions exist anywhere, and nothing authorises the visual half | Stop, and say why. originate invents brand decisions out of nothing; entered without somebody answering for them, it produces a specification of decisions nobody made — and nothing in the output reveals that. Report what the structural half produced and what this unit would settle, and leave it there |
| Nothing exists and the user asks for "something that looks good, you pick" | Still originate.md, still the interview. Do not hand over a default palette. A palette the user rubber-stamps is no longer their spec, which is the failure this whole method exists to prevent |
The scope gate is on originate only, and that is deliberate
The two branches need different permissions, and giving them the same one breaks the other:
| Branch | What it does to the user's material | What authorises it |
align | Slims a document they already have — structural content moves out, a pointer stays | "May the existing visual spec be edited?" in config.md. It touches somebody's file; it invents no brand decision |
originate | Produces a brand direction, a scale and a set of values where none existed | The scope authorisation. These are decisions somebody has to answer for |
Gating align on scope would block a boundary cleanup that the structural half needs anyway —
an existing visual spec almost always has structural content mixed into it, and leaving it there
gives the project two vocabularies. That is the failure align exists to prevent, and it has
nothing to do with the one the handover exists to prevent.
Start by reading layout.md's ⏳ list
Structure and visuals are two dimensions of one interface, so a few numbers belong to neither alone. layout.md marks those ⏳ — a fixed panel width, a minimum usable width, a page size — and this step is where they get settled.
Read that list first. A type scale chosen without knowing that nine fields and five actions have to fit in 360px is a scale somebody will revisit.
Settling them is this unit's job, and it is done here whoever is answering. The ⏳ list is
always brought in as a list — "these three are waiting on your scale" — never folded silently
into the structural interview, because the two interviews sit in different halves. What
config.md changes is only when this interview happens, not how it runs:
| The handover was | When this runs |
| Soft — the same person answers for both halves | Straight after the hand-back, in the same sitting, with the ⏳ list on the table |
| Hard — somebody else, or nobody yet | Whenever that person picks it up. The ⏳ list travelled to them as the note; they should not have to read the whole structural spec to find those three numbers |
Answers go back into layout.md, by its own change process — this unit does not edit it directly.
Do not require the two people to be in a room together. That is often impossible, and a mechanism that assumes it simply fails in that half of the world. The marker travels between them precisely so the meeting is not the mechanism — this whole plugin exists because "somebody will remember to" is not one either.
A ⏳ line can come back refuted rather than filled. Building the projection can show that a marked number does not hold at all — nine fields and five actions do not fit in 360px, whatever the scale. That is a different fact from "waiting for a number", and it must not reuse the ⏳ marker: one says give me a value, the other says the direction you were going to give a value for is dead.
Mark it ⛔ in layout.md, in the number's own table cell, with what refuted it — the marker
and its placement rule are defined in ui-contract-layout's outline, §3 "A third outcome". Note
what that section records: ui-contract-audit's check 10 does not report ⛔ yet, so a refuted line
is invisible to the health check. Name the refuted lines explicitly in the hand-back; do not
rely on the check to carry them.
This step aligns; it does not rewrite
If the project already has a visual spec, do not rewrite it. Four things:
- Add the division-of-labor statement at the top, naming the cut ("does it change when the brand skin changes") and naming the token file as the value authority.
- Move structural content out: region trees, column widths, page archetypes go to
layout.md. Leave a one-line "structure: seelayout.md§X" behind, and rename the section if its title promised structure. - Move literal values out to the token file. Hex codes, px sizes, radius literals become token references. If the project already has a token source (
tailwind.config.*,tokens.css, a theme file), that is the token file — do not create a second one. - Align region names with the allowlist. Old component-style names (camel-cased widget names) must be replaced with allowlist names, or an agent reading both docs gets two vocabularies — and the allowlist mechanism fails on the spot.
Token names are not part of that. Region names get aligned; token names do not. Record what the project already calls them in the declaration table, marked this project, and change nothing. Renaming tokens turns a boundary alignment into a repo-wide rename that touches every consumer — a change request, which goes through spec-before-code.
Appendices that record mockup reality (value-drift mapping tables and the like) stay untouched — they document the mockup, not the spec.
The document is the name authority
The token file holds the values; this document settles which names exist. Say so in the opening blockquote, and carry the declaration table (design-md-outline.md §Opening) with it.
This has to be written down for the same reason the value authority is: without it, a borrowed naming convention quietly becomes the authority instead, and nothing in the output shows it — a token file produced either way is byte-for-byte the same. The health check reads its whitelist from that table, so a missing table degrades the check to nothing.
The same applies to tokens.input.json when the originate branch produced one:
| Records | On conflict | |
tokens.input.json | where the user left the knobs (an input record) | does not count |
| the token file | the values that are currently correct | wins |
Its keys are the names from the declaration table. Per-stack mapping changes the format, never the names.
When there is no token file yet
Say so in the document, not only in the report — the reader who never sees the report is the one this has to reach. Write the token references as they should be, and two things with them:
- Name no path that is not on disk. Write "the token file is the value authority; this project does not have one yet" rather than pointing at
src/tokens.cssand hoping. Following a pointer to a missing file is how a reader concludes the whole spec is stale. - Say who closes the gap. On the
alignbranch no unit generates the token file — the asset layer produces structural components only, so whoever builds it fills in the values under the names written here. On theoriginatebranch this section does not apply: that branch derives the values and writes the token file itself, so a deliverable from it should never contain "there is no token file yet".
Do not silently invent values back into design.md to make it self-contained. A document that quietly re-absorbs its values is exactly the second value authority this rule exists to prevent.
Before you call this document done
- Every ⏳ in
layout.mdwas resolved, refuted, or is explicitly left open with a reason — and read before settling the scales, not after: a scale chosen without knowing what has to fit at it is a scale that will be revisited. "Resolved" and "refuted" are different endings; the line-level item near the bottom of this list says how each is marked - Resolving one means editing
layout.md— the ⏳ becomes a plain number. Not the type size written intolayout.md, not the width copied intodesign.md: both directions are the bleed the split exists to prevent design.mdcontains no literal color or size values; the token file is the single value authority- Structural content has been moved out and only a one-line pointer remains
- Every region name matches the allowlist; no old component-style naming survives in the normative body
- Mockup-reality appendices were left alone
- If no token file exists yet,
design.mdsays so in its own body, and no path to a missing file is written anywhere in it - The declaration table is present in the opening section, one row per token, each row marked
borrowed/this project/not used— those three keywords in ASCII even in a non-English deliverable, because the health check anchors on them and a translation silently turns the check off - Every row marked
borrowedis actually in the borrowed set; nosidebar-*and nochart-*among them - Chapters the borrowed set does not cover say so, and their names follow the pairing rule
- On the
originatebranch: the three directions differ on at least two axes, and each cost is a consequence in this product rather than an adjective - On the
originatebranch: every derived value traces to a coordinate, and the hue's basis is named indesign.md§1 — one of the four inoriginate.md§The gap the axes do not close, or an explicit "none applies" that marks the hue as the weakest value in the set. The axes fix count and saturation but not hue, so it is the one slot with no source - On the
originatebranch: no value intokens.input.jsonis a framework default (the list is inoriginate.md§Do not emit a framework default) — emitting one means the coordinates never reached the values - On the
originatebranch: the hue's basis is stated without a literal colour — not the chosen value, not the default being contrasted against.design.mdcarries no hex anywhere, and explaining a choice is not an exemption from that - On the
originatebranch: the token file itself was written — the real one, at the path and format the paths table gives, committed. Not described, not left for another unit: the old write-the-record-and-let-another-unit-land-it handoff landed 2 of 5 runs (2026-08-25) - On the
originatebranch:tokens.input.jsonwas also written, at.ui-contract-local/tokens.input.json— pinned, not configurable, because the instrument and a grader both need one answer - On the
originatebranch: the user was not told to keep or commit the knobs record. It is not a deliverable; describing it as one invites somebody to maintain a file nothing reads - On the
originatebranch: the two are not confused in the deliverable (identity table above). Describing the record as where the values live is the second value authority this split exists to prevent - On the
originatebranch: the visual baseline was captured — wall frame, 480px copy, 200% crops, paths recorded in §1 — and the three-second sentences were written against it, not skipped - On the
originatebranch: the context-free review pass ran on the baseline and §1 records its verdict; when it was skipped, §1 says "not reviewed" in so many words - If the token file landed while the asset layer is still without one,
ui-contract-assetswas called again. It runs twice on a greenfield project and both runs are correct; pass 2 consumes the token file. This unit does not touch the asset layer itself — values are authored here, consumed there, and reversing that gives the project two value authorities - Every ⏳ line in
layout.mdwas either settled, refuted, or explicitly left open with a reason — settled means it became a plain number; refuted means it carries ⛔ and what refuted it (definition:ui-contract-layout's outline §3); left open means the reason is written down. A ⏳ that is merely still there is none of the three. This is the same condition as the first item in this list, stated at the line level — refuted lines were also named in the hand-back, because check 10 cannot see them - Reported to the user: which sections were touched, how many names were replaced, what was deliberately left alone
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

