ucsd-decorator-kit
v2.4.0
Published
Agent-facing contract for building on the UC San Diego Decorator 5 design system.
Maintainers
Readme
UCSD Decorator Kit
The official agent-facing contract for building on the UC San Diego Decorator 5 design system.
Getting started
Building a new UC San Diego site with an AI agent:
mkdir my-site && cd my-site
npx ucsd-decorator-kit@latest initTo run an unreleased change — or if the registry is unreachable — every command on this page also works straight from the repository, which is what CI uses:
npx -y github:UCSD/decorator-kit initThat runs whatever is on main at the time, released or not, so use it to try
a change, not to set up a site.
That installs ucsd-decorator-v5 — the Decorator itself — plus this kit, writes
the rules in the format each AI tool reads, installs the skill, wires
Dependabot and a CI workflow, and — for Claude Code — adds a Stop hook that
runs the chrome integrity gate after every turn. It runs in the background and
only speaks up if it finds a regression: see
"Running it automatically".
Then open Claude Code, Cursor, Copilot or Antigravity in that directory and
describe the site you want. There is nothing to point the tool at: the rule files
sit at the project root, which is where all of them already look, and they name
node_modules/ucsd-decorator-v5/dist/ as the source of truth for markup.
The agent will ask which layout to start from rather than picking one. That is deliberate — choosing a template unasked is exactly the class of decision this kit exists to prevent.
Not sure what it should look like? Say "help me get started"
If you know what the app has to do but not how it should look, tell your agent:
help me get startedInstead of writing markup, the agent interviews you first — six questions in
one message, each with lettered options, so you can reply 1b, 2 students
checking waitlists, 3b…:
- What are you building — an information site, a request or application form, a dashboard, a directory or admin tool, a knowledge base?
- Who uses it, and what is the one thing they must be able to do?
- Which campus area — advising, enrollment, research, housing, IT, facilities, library, HR?
- How will it be built and hosted — static HTML, a JavaScript framework, server-rendered templates, Cascade CMS?
- What data does it touch — public only, campus sign-in, student or personal records?
- The site name, and a short form for phones.
From your answers it recommends a layout and the modules and patterns to use,
each linked to its card in the
Decorator Kit Developer Guide
so you can see it first. You pick the layout; the agent writes a BRIEF.md at
the project root with the plan, then builds a first version from it inside the
canvas, runs verify, and suggests next prompts. Later sessions read
BRIEF.md before changing the page.
It works in Claude Code, Cursor, Copilot, and any agent that reads AGENTS.md,
because the interview is a rule — rules/60-getting-started.md — compiled into
every instruction file, with its full text in DECORATOR.md. For a chat AI with no kit installed, such as TritonGPT,
paste the Starter interview prompt from the guide's
Start here
section instead.
Skip it when you already know the template, the page name, and the pieces you want: the agent only asks for what your request leaves out.
Already have a project
npx ucsd-decorator-kit addadd installs the rules and the skill and touches nothing else. It does not
modify package.json, does not add CI, and never writes AGENTS.md — that
filename is also the convention for a repository's own agent contract, and yours
is the more specific document. Pass --with-decorator to add the Decorator
dependency, --with-ci for the Dependabot config and workflow, --with-hook
for the Claude Code Stop hook. The last one merges into an existing
.claude/settings.json rather than overwriting it, so a project's own hooks
and permissions survive.
Each of those three flags also adds ucsd-decorator-kit as a devDependency,
at the version you ran, along with the decorator:* npm scripts. The workflow
and the hook run the kit from node_modules, and without it installed they
have nothing to run. A project that already lists the kit keeps its version.
Staying current
| Command | Does |
|---|---|
| npx ucsd-decorator-kit sync | rewrite the generated files after a kit upgrade |
| npx ucsd-decorator-kit check | fail if they are stale — wire this into CI |
| npx ucsd-decorator-kit drift | report whether the Decorator moved upstream |
| npx ucsd-decorator-kit verify | run the chrome integrity gate against this project's actual markup — wire this into CI too |
Both ucsd-decorator-v5 and ucsd-decorator-kit are devDependencies, so
Dependabot opens a pull request when either moves: the Decorator and the rules
update through one mechanism. With the kit installed, npx ucsd-decorator-kit
runs that installed copy. Without it, npx downloads the newest release on
every run, so the kit version changes whenever a release ships, not when you
upgrade. check fails the build if a kit upgrade landed
without a sync, so a project cannot quietly run last year's rules.
drift covers the gap Dependabot cannot see — a push to UCSD/Decorator or a
cdn.ucsd.edu deploy with no npm release.
Project canvas rules
A project will have rules of its own: house components, content conventions,
how it loads data. Put them in canvas-rules/ at the project root, one
Markdown file per topic. init and add create the directory with a README
that explains it.
npx ucsd-decorator-kit syncsync compiles every *.md file there, in filename order, into a "Project
canvas rules" section at the end of CLAUDE.md, .cursorrules,
.github/copilot-instructions.md, and AGENTS.md where the kit manages it. It
skips the README. Every tool picks the rules up the same way it picks up the
kit's, and check fails in CI if a file there changed without a sync.
Those files load in full in every session, so keep a canvas rule to what must
hold every time. sync and check print a note when a project's rules push
CLAUDE.md past 40,000 characters, the size at which Claude Code warns that it
will impact performance.
The compiled section limits those rules to the canvas and ranks them below the kit's own. A rule file that anyone can add is not a review, so a chrome instruction in one gets refused rather than followed, just like a chrome request made in chat.
Component libraries
A canvas that needs more than the Decorator's own components gets them from
canvas-components/: shadcn/ui, a charting library, web components. Give each
library its own folder, holding the files the page loads and a README.md
saying when and how to use it.
canvas-components/
shadcn/
README.md compiled into DECORATOR.md by sync
dist/app.css
dist/app.jssync lists each library in a "Project component libraries" section of the
rule files, and compiles its README into DECORATOR.md, where agents read it
before using the library. That section relaxes three Decorator look-and-feel rules inside the
canvas: agents may use the library's components and classes, its icon set, and
its heading styles. Typography stays on brand (Roboto, Teko, Brix Sans, or
Refrigerator Deluxe), and no chrome,
accessibility, or security rule relaxes.
A library's own CSS is the usual way one leaks into the shell, so verify
holds canvas-components/ to a stricter standard than other site CSS. It scans
minified files there too. It fails on any selector that names no class, id, or
attribute (*, body, h1), unless the rule sets only custom properties. The
README that init and add write into the folder covers the setup that keeps
a library inside the canvas: no global reset, prefixed class names, and popups
rendered inside the canvas.
check and verify answer different questions and are easy to conflate: check
is about whether this kit's generated rule files are current, verify is
about whether the project's built chrome is still correct. verify needs
nothing this CLI doesn't already give it — node checks/chrome-contract.mjs
--check works standalone, with --accept and --explain alongside it. See
checks/README.md.
Where markup comes from
ucsd-decorator-v5 on npm is the source of truth. Decorator-V5.zip is not,
despite being the download the documentation links to: measured 2026-08, the
archive is behind the package on every file it ships, and its
scripts/base.min.js is a January 2023 build missing the runtime behavior that
governs the mobile drawer search. See
references/distribution.md.
The problem this solves
The Decorator shell is identical on every page of a site. That is the point of it, and it is also why agent-authored changes to it are dangerous: a shell edit propagates to every page at once, and it usually propagates away from the campus standard rather than toward it.
Two real regressions motivated this kit:
- An agent deleted the mobile drawer's search form and replaced it with a link to the search page. Visually near-identical; functionally it drops the scope selector and the typed query and submits nothing to the hosted search API.
- An agent altered an embedded campus widget after reading the DOM it renders into.
Both happened under rules that already said "do not modify the header, nav, or footer." Prose was not the missing piece.
Three more reached production after the markup gate was live, and none of them
changed a tag: site CSS rebuilt the drawer search, #chat-bubble { …
!important } reshaped the TritonGPT launcher, and site JS deleted an id the
Decorator assigns at runtime and styles the mobile drawer search through. A
markup contract cannot see any of that, which is why there is a fourth tier.
What actually prevents it
Three things, in order of how much they matter.
1. A source of truth that is a file, not a rendered page. The rules tell agents to fetch official documentation — but that documentation is itself Decorator pages wrapped in chrome, so an agent following the rules faithfully still ends up reconstructing markup from a DOM. Worse, Jasny Bootstrap clones the offcanvas drawer into the body at runtime, so a live DOM contains navigation markup that exists in no file at all. Pin the templates; read from disk.
2. A named canvas. One writable region per project. Everything else is
chrome, declared as a selector list that lives outside the markup it protects.
In-file <!-- DO NOT EDIT --> markers do not work — the agent you are defending
against can delete them too.
3. A gate that fails the build, with an interlock. Four tiers:
| Tier | Asks | Escape hatch |
|---|---|---|
| chrome/consistent/* | do all routes agree? | none needed |
| chrome/golden/* | does chrome match the recorded contract? | --accept, after a human reads the diff |
| chrome/structure/* | is it still a search form? | none |
| chrome/styling/* | does site CSS or JS reach into the shell? | none — a reviewed exception with an expiry date |
This is not a design to go implement — it ships, as checks/chrome-contract.mjs
and checks/lib/, reading contracts/. npx ucsd-decorator-kit verify runs it.
Tier 3 is the load-bearing one. When one shell feeds every route, a shell edit
is perfectly consistent drift — tier 1 stays green. Tier 2 fails, but its
remedy says "accept if intentional," and an agent that believes its own change
is intentional will do exactly that, rebaselining the regression. So
--accept refuses to write while tier 3 fails. That refusal is the
design.
Tier 4 exists because tiers 1–3 all read markup, and the shell can be wrecked
without touching any. Its protected token set is derived per run — every class
and id inside a chrome region and nowhere inside the canvas — so it tracks the
Decorator instead of a list someone has to remember to update. --accept
refuses while it fails too, for a different reason: the markup is intact, so
regenerating the golden would hide the finding rather than resolve it.
Contents
bin/cli.mjs init / add / sync / check / drift / verify
rules/ canonical rule source
skills/ucsd-decorator/ the skill — SKILL.md plus references
library/ Skills Library publishing staging (see below)
templates/ Dependabot config, workflow, and the canvas-rules/
and canvas-components/ READMEs written into projects
scripts/lib/rules.mjs the renderer, shared by the CLI and the compiler
scripts/compile-rules.mjs rules/ -> CLAUDE.md, AGENTS.md, .cursorrules,
.github/copilot-instructions.md, DECORATOR.md
scripts/check-library.mjs validates library/ against the sync contract
scripts/pin-decorator.mjs dependency-free Decorator pinning, npm only
contracts/ portable chrome regions, selector rules, and
styling policy — each with a JSON schema
checks/ the chrome integrity gate: chrome-contract.mjs
(CLI) and lib/ (tiers 1-4) — runs standalone,
`bin/cli.mjs verify` is a thin wrapper around it
test/ including the AGENTS.md refusal, the replayed
chrome-regression incidents, and an install of
the packed tarball
RELEASING.md how a version reaches npmCLAUDE.md, AGENTS.md, .cursorrules, .github/copilot-instructions.md, and
DECORATOR.md are generated. Edit rules/ and run:
node scripts/compile-rules.mjs--check verifies the committed output is current; npm test runs it along with
the library contract and the CLI tests.
What loads every session, and what loads on demand
Claude Code loads all of CLAUDE.md at the start of every session and warns
once it passes 40,000 characters, and an @ import would not help, since an
imported file loads at launch too. So the four rule files hold only what must
hold in every session. Detail that one kind of task needs, such as measurements,
tables, or a procedure, goes below a marker in its section of rules/:
<!-- on-demand: before you copy `two-column.html` or `three-column.html` -->The rule files keep what sits above the marker and, in place of the rest, a
line telling the agent to read that section in full in DECORATOR.md before
that task. DECORATOR.md gets the whole section, and nothing imports it. A
marker above a rule's first section moves the rest of that rule. Write the
trigger as the task, starting "before you" or "when". test/rules.test.mjs
holds the compiled CLAUDE.md to 25,000 characters, leaving a project's
canvas rules room under the warning. Past that, move detail behind a marker
rather than raise the budget.
Using the skill without the CLI
Claude Code — install as a plugin (/plugin marketplace add UCSD/decorator-kit),
or copy skills/ucsd-decorator/ into a project's .claude/skills/.
Cursor, Copilot, Antigravity — the compiled instruction files are picked up
automatically once present at the project root, with DECORATOR.md beside
them.
Any tool that reads AGENTS.md — read this repository's copy in place, in a
checkout beside your project, along with the DECORATOR.md it points to. Do not copy it in: your repository's own
AGENTS.md, if it has one, is the more specific contract and the more important
of the two. add will not write that file for the same reason.
Publishing to the TritonAI Skills Library
The public page at https://tritonai.ucsd.edu/skills/index.html is generated
from a Skills Library repository, synced by tritonai-website and rendered from
content/skills/library.json. Only paths matching
<collection>/<name>/SKILL.md are read — nothing deeper.
Do not put this repository inside the Skills Library. The sync only reads
<collection>/<name>/SKILL.md and nothing deeper, so a kit dropped at
tritonai/ucsd-decorator-kit/ would leave the skill at
.../skills/ucsd-decorator/SKILL.md — two levels too deep, and invisible. The
kit is the upstream source; the Skills Library is one of its distribution
channels, alongside the Claude Code plugin and the generated IDE rule files.
library/ holds the publish-ready shape:
library/tritonai/ucsd-decorator/SKILL.md + references/ (published from skills/)
library/tritonai/ucsd-branding/SKILL.md retirement pointerlibrary/tritonai/ucsd-decorator/ is a published copy of
skills/ucsd-decorator/. Regenerate it with npm run sync:library;
check:library fails if the two have drifted, because a hand-maintained
duplicate is the same failure mode this kit exists to prevent.
ucsd-branding is the skill this one replaces. It is reduced to a pointer
rather than deleted, so anyone already invoking that slug is told where it went
instead of silently loading guidance that routes agents into rendered chrome.
Validate before publishing:
npm run check:libraryIt enforces exactly what the sync enforces — path shape, non-empty name and
description, name matching the directory, maintainer on community skills,
no duplicate names, and no nested SKILL.md files that would be silently
ignored.
Copy library/tritonai/* into the Skills Library repository to publish.
The chrome integrity gate
npx ucsd-decorator-kit verify runs it against this project — four tiers,
described above, reading nothing but files on disk. It needs no CLI, either:
node checks/chrome-contract.mjs --check is the same thing, standalone, and
checks/ ships inside the installed package. See
checks/README.md for the full design, what's portable
versus project-owned, and tier 4's CSS/JS scanning in detail.
tritonai-website runs its own, separately maintained implementation of this
same four-tier design at 54-route scale — the project this engine was modeled
on, not a consumer of it. It does not depend on this package; nothing here
reads from or writes to that repository.
Related
skills/ucsd-decorator/references/distribution.md— how Decorator is published today, the version-identity gap, and what the maintainers should addchorta/antigravity-code-kit— the Antigravity starter this kit generalizesUCSD/Decorator— Decorator source- https://developer.ucsd.edu/design/decorator/ — official documentation
