Kit Anatomy

What a complete ShruggieTech brand kit contains, file by file.

Every kit shruggie-brandbuilder generates has this shape. An agent that has seen one kit knows where everything is in all of them, which is half the point.

Legend: R required, O optional, N new relative to the existing three kits.

The five layers

| Layer | Purpose | State of the existing kits | | --- | --- | --- | | 0 Canon | One machine-readable source of truth | fragcap half-does it | | 1 Identity | Positioning, voice, logo, prohibitions | Strong. Keep. | | 2 Ecosystem bindings | The same tokens in the shapes Next.js eats | Missing | | 3 Enforcement | Checks that fail at the moment of the mistake | One lint file, unused | | 4 Proof | Rendered artifacts a human can eyeball | Strong. Keep. |

Layer 2 is why agents ignore the current kits. A kit that publishes only --fc-signal-cyan gets read, agreed with, and then bypassed for bg-slate-900, because that is what the agent's hands already know.

File tree

<brand>-brand/
  SKILL.md                     R    consume-mode entry. SIX-KEY frontmatter only.
  README.md                    R    the full brand system, human and agent
  brand.json                   R N  canon instance: inherits shruggietech canon,
                                    declares this brand's constrained choices
  manifest.json                R    every file with bytes and sha256
  VERIFY.md                    R    measured numbers, generated, never typed
  styles.css                   R    single CSS entry for non-React surfaces

  tokens/                      R    ALL GENERATED by gen_vanilla.py. Never
    brand.tokens.json               hand-written: colors.css and
    colors.css                      nextjs/globals.css are the same system
    typography.css                  described twice, and only the second one
    spacing.css                     was ever checked.
    base.css

  nextjs/                      R N  ---- THE GAP LAYER ----
    README.md                       install instructions for a consuming project
    globals.css                     Tailwind v4: @import, @custom-variant dark,
                                    @theme inline, :root and .dark in OKLCH
    components.json.snippet         the registries entry a consumer pastes
    fonts.ts                        next/font wiring, local bundled faces
    providers.tsx                   next-themes, dark as default
    app-icon.tsx                    App Router icon convention
    opengraph-image.tsx             App Router OG convention
    registry/
      registry.json                 the catalog
      theme.json                    registry:theme carrying cssVars
      fonts.json                    registry:font
      <component>.json              registry:ui, ONLY where the brand
                                    genuinely diverges from stock shadcn

  components/                  R    vanilla CSS + thin wrappers, for mocks
    components.css                  and non-React surfaces
    core/  forms/                   Button Badge Card Divider SectionHeading,
                                    Input Textarea Select, plus domain components

  enforcement/                 R N  ---- THE TEETH ----
    AGENTS.md                       the agent contract. Read before any UI work.
    adherence.oxlintrc.json         generated per brand: raw hex banned, raw px
                                    banned, per-component prop allowlists
    stylelint.config.json           the CSS-side equivalent

  logos/svg/  logos/png/       R    vector masters and raster exports
  favicons/                    R    SVG, real multi-entry ICO, PNGs, touch,
                                    android, webmanifest
  icons/                       O    bespoke domain icons ONLY. 24 grid, 1.5
                                    stroke, currentColor. Never a lucide clone.
  fonts/                       R    woff2 + ttf + OFL licenses. BUNDLED.
    licenses/
  specimens/                   R    type specimen, fully outlined
  guidelines/index.html        R    the system rendered from itself
  ui_kits/<brand>-web/         R    a demo product surface in the brand's
                                    own register
  brand-guide.pdf              R    printable reference, fonts embedded Type0

  build/                       R    Python. Quarantined so a text-only ingest
    mk_paths.py                     is unaffected. mk_paths.py is the LOGO
    <the template set>              MASTER: parametric geometry against
                                    glyphkit. logo.paths in brand.json is
                                    generated from it, never hand-edited.

The three layers people forget

tokens/ and components/ are generated by gen_vanilla.py from brand.json and the canon. In the first four kits they were hand-authored per brand, which is exactly where drift enters: an agent updates nextjs/globals.css, the vanilla layer keeps the old value, and only one of the two is checked.

build/mk_paths.py is required, not optional. It is the only place mark geometry is authored. See 08-glyph-construction.md.

qc/probe.json records the capability tier the build actually ran at, so a VERIFY.md skip can name the missing tool instead of reading as "not applicable".

Notes on the files that are new or commonly done wrong

SKILL.md frontmatter

Six keys and no more: name, description, license, compatibility, metadata, allowed-tools. All three existing kits declare user-invocable, which hard-fails claude.ai and Skills-API upload validation. Strip it.

brand.json

The kit's own canon instance. It inherits every immutable from 01-canon.json by reference, declares the handful of constrained choices this brand made, and carries the measured contrast numbers for its own accent trio. verify.py diffs it against canon; any drift on an immutable fails the build.

This is what makes the contract enforceable rather than aspirational.

nextjs/globals.css

The radius pegs are overridden explicitly here rather than derived from a single --radius, because shadcn's fixed ratios cannot land 6/8/12 at once. Canon records the deviation and the reason.

nextjs/registry/

Publish the catalog and let a consuming project install with npx shadcn add @<brand>/theme. Registries can also carry AGENTS.md and agent rule files, so the enforcement layer ships through the same pipe as the tokens. That is the whole trick: the brand arrives by the same command the agent was already going to run.

Only write a registry:ui override where the brand genuinely diverges. A kit that forks all forty shadcn components has created a maintenance burden and bought nothing.

enforcement/AGENTS.md

Short, blunt, and read before any UI work. It states the token vocabulary, the component vocabulary, and the stop condition: if you need a value that is not here, stop and ask rather than inventing one.

fonts/

Bundled. Verified practice, both in shruggie-docs (six TTFs embedded into every emitted docx) and in Anthropic's own morning skill. The build path never makes a network request; the CDN URLs in the README document where the bytes came from so they can be refreshed.

icons/

Exists only when lucide genuinely lacks a domain symbol. fragcap's six capture icons are the model. A kit with an icons/ directory full of re-drawn lucide glyphs has misunderstood the rule.

What gets checked

Every kit passes the verify.checks list in 01-canon.json. A kit that fails any check is not shippable. The numbers in VERIFY.md are measured at build time from the shipped files, never transcribed from a previous run.