Browse all of Kata

Governance

Source docs/design/governance.mdMarkdown

On this page

How a token is added, how a chapter changes, and who decides.

Part of the Konjo design language.

A design system without governance becomes a style guide within two quarters: someone needs a colour, adds it locally, and the palette quietly forks. This chapter is short on purpose — every rule here exists because its absence has already cost something.

The source of truth is code

packages/design-tokens/tokens.ts is the only place a design value is defined. Not this documentation, not the skill, not Studio's CSS, not a component.

Three references are generated from it and are never hand-edited:

File Generated from
Token tables packages/design-tokens/tokens.ts
Component inventory src/components/ui/index.ts
apps/studio/src/app/tokens.generated.css packages/design-tokens/tokens.ts

npm run design:sync regenerates all three. npm run design:check fails if any is stale, and runs in CI.

This exists because the alternative was tried. The documentation has published, at various points: a type scale with two sizes the code did not ship, a contrast token that failed the law it was created to enforce, a belt ring in a width the ramp does not contain, an instruction to hand-compose eight components that already existed, and a whole second design brief — marked "strict — do not deviate" — still carrying four superseded colours. Every one survived multiple careful readings. Generation is not tidiness; it is the only thing that has worked.

Adding a token

  1. Prove the existing tokens can't do it. Most requests are a missing rule, not a missing value. "There's no colour for a warning row" usually means the feedback pair already covers it.
  2. Add it to both palettes. ThemeColors = typeof lightColors makes a one-sided addition a compile error — that guard is doing its job when it fires.
  3. State the contrast, computed. Every colour token arrives with its ratio against the surfaces it is legal on, from scripts/design/contrast.mjs. Never eyeballed, never remembered from a similar value.
  4. Say where it is illegal. text.tertiary is card-only; surface.tag needs a border to be a control. A token without a boundary becomes a token used everywhere.
  5. npm run design:sync, and commit the regenerated files alongside.
  6. Write the rule in the chapter that owns it. A value with no prose is a value the next agent will misuse.

Deprecating a token

Deprecate in place; never delete in the same change. Mark it @deprecated in the token source with the replacement named, leave it exported, and land the migration separately. A deleted token is a broken build for anyone mid-branch, and it turns a design decision into a merge conflict.

A token stops being exported only once design-check reports zero uses.

Changing a rule

  • A rule changes when it is wrong, not when it is inconvenient. "This screen would look better without the budget" is a screen problem.
  • Rules that came from founder calibration are not changed by an agent. The sixteen calibration rounds in the calibration record are settled decisions — bold athletic, light-first, ceilings not quotas, white cards on a tinted page, no shadows, one red role. Reopening one is a conversation, not a commit. The record exists precisely so nobody re-litigates from scratch.
  • Everything else is fair game with evidence. Evidence means a computed ratio, a measured count, a screenshot in both themes, or a blind test — not an opinion about taste.
  • A rule with no enforcement decays. When you add one, say how it gets checked: a design-check rule, a design-doc-check assertion, or an item on the skill's checklist. If it cannot be checked at all, say that too, in the rule.

Say how far a rule reaches

A rule written in a shape-specific chapter must say whether it is shape-local or system-wide. This is the cheapest correction in the whole record and it fixes a failure that has recurred in every round of the completeness test.

Two examples of the damage, both found by agents who read carefully and still got it wrong:

  • "Never render an unbounded list, and never infinite-scroll" and the two paging shapes live in studio/tables.md, in a chapter that opens by defining what a table is and is not. They are system-wide — a board column and a feed obey them too — but an agent building a board had to decide that alone.
  • "A region with nothing to say prints one line" lives in studio/dashboard.md and is shape-local: it is the fixed-grid answer, and it deliberately overrides the detail-screen rule in states.md. An agent applying it to a detail screen would be wrong.

Both read as universal truths in the voice they are written in. Neither says which it is.

So, when a chapter states a rule that is not obviously about its own shape:

  • System-wide → say so, in the sentence: "this holds for any scrolling collection, not just a table."
  • Shape-local → say what it overrides and why: "on a fixed grid, unlike a detail screen…"
  • A foundation chapter is system-wide by default, and states its exceptions.
  • A pattern or dialect chapter is shape-local by default, and states its reach.

A rule with no stated reach will be copied to the shape next door, correctly or not, and whichever it is will not be your decision.

Adding a chapter

Chapters live under docs/design/, one topic each, and are reachable from the index in the spine and from the router in the design skill. A chapter nobody routes to is a chapter nobody reads.

Every chapter opens with what it settles and closes with what never ships.

Testing the documentation

Two tests, one gate. Full protocol: design-completeness.

The gate is the design-completeness test. A fresh agent gets the skill, the router and product-facts.md, and specifies a screen. Green is zero design defects and zero contradictions — product blockers do not count against it, as long as each was either answered from the catalog or named and stopped on. That condition is reachable, and it is the honest definition of the design layer being done.

The blind test still runs, and is no longer a gate. Same setup minus the product facts, with every question the agent wanted to ask counted as a defect. It cannot come back green, by construction — some of what it needs was never design information. It is a smoke detector: you run it to find holes, and it earns its keep by going off. Three rounds of it produced four error borders invisible in dark mode, eight of nineteen belt ranks invisible on a dark card, a token that failed the law it existed to enforce, and a reference target teaching heights no token produces. A passing test would have found none of them.

Confusing the two is what made a working system look broken for three rounds. Keep them separate and read each for what it measures.

When the design system cannot answer

Some of what a screen needs is not a design decision. Whether a promotion can be undone, who may record one, what a removal cascades to — those are product facts, and no amount of design documentation will contain them.

The system's job is to make that visible rather than fillable. A blind test of the promotion screen produced a specification roughly a third of which was invention wearing the system's vocabulary, and the most dangerous single line in it was a consequence sentence asserting what "remove from dojo" does — written confidently, from nothing.

The answered ones now live in product-facts.md — promotions, incidents, removals, waivers, push categories — as given inputs rather than open questions. When a rule requires a fact that file does not hold, the answer is to name the missing fact and stop, not to write something plausible; the file's own "Not yet decided" table is where it goes. That applies to the documentation too. A chapter that does not know something says it does not know, rather than offering a default nobody chose.

Who decides

  • Founder — anything in the calibration record: brand, voice, the budgets, what the product feels like.
  • Whoever is building — everything else, under these rules, with evidence.
  • CI — whether the documentation is telling the truth. That one is not a judgement call.