<!-- Generated by scripts/build-agent-kit.ts for @aistrike-dev/ui@5.0.1. Do not edit. -->

# Octopus UI core rules

Octopus UI (`@aistrike-dev/ui`) is the AiStrike design system: a theme layer over MUI. These rules
apply to every interface you generate, revamp, or refactor. They are the non-negotiable part of the
guidance; per-component detail lives in the component reference, and help choosing between
similar-looking components lives in the choosing guides.

## 1. Import from the design system only

Everything comes from the single package entry - components, layouts, themes, and tokens:

```tsx
import { ThemeProvider, CssBaseline, darkTheme, Button, Chip } from '@aistrike-dev/ui';
```

- **Never import a component from `@mui/material` or `@mui/material/*`.** If the component exists in
  Octopus UI, it must come from `@aistrike-dev/ui`. Importing MUI directly bypasses the theme, the
  custom variants, and the severity palette, and produces off-brand UI.
- **Never import theming from MUI.** `ThemeProvider`, `CssBaseline`, `useTheme`, `darkTheme`, and
  `lightTheme` all ship from `@aistrike-dev/ui`. MUI is a peer dependency of the package, not
  something your generated code imports.
- **Never use a repo-internal path** like `@/atoms/Chip` or `@/organisms/Table`. Those aliases only
  resolve inside the design-system repository; in a consuming app they fail to build.

The only two things generated UI may import from MUI:

| Allowed | Why |
|---|---|
| Icon glyphs from `@mui/icons-material` | Not re-exported by the design system. Wrap them in the `Icon` atom when you need a tone. |
| Layout primitives `Box`, `Stack`, `Grid` from `@mui/material` | Unopinionated layout wrappers the design system deliberately does not ship. Style them with `sx` plus theme tokens only. |

Two entries live at subpaths rather than the package root. The registry's `importPath` field is
authoritative:

```tsx
import { CodeEditor } from '@aistrike-dev/ui/monaco';
import { BarChart } from '@aistrike-dev/ui/charts';
```

## 2. Never hand-roll a component the system already ships

No custom-styled `<button>`, no pill `<span>`, no `<div>` card, no `TextField` with a magnifier
adornment standing in for a search field. Check the component reference before building anything
from primitives. If you cannot find the right component, ask rather than inventing one.

## 3. Compose, don't inline-style

Use existing components and the theme. Avoid ad-hoc CSS and hardcoded colors - never a hex literal.
For backgrounds, prefer letting the surface ladder resolve them (rule 4) over naming a
`backgroundColors` token, since a named token fixes the colour to one context. See the tokens
reference for the roles, and rule 4 for how a component reads the one that applies to it.

## 4. Let the surface ladder pick your backgrounds

Dark UI signals containment with tone: each nested thing is slightly lighter than what holds it.
There are six rungs — `page` `#191919`, `primary` `#1F1F1F`, `widget` `#262626`, `surface` `#2E2E2E`,
`raised` `#333333`, `overlay` `#3A3A3A` — and you should almost never name one.

**Wrap a container in `Surface` and let it work out its own rung from nesting depth.** Naming a rung
hardcodes an assumption about where your component will be used, and it is wrong the first time
someone puts it in a dialog.

```tsx
<Surface>                 {/* page — the app shell */}
  <Surface>               {/* primary — the page body */}
    <Surface>             {/* widget — a card */}
      <TextField />       {/* recesses to primary on its own */}
```

**Read the custom properties for anything that depends on context.** They resolve against the
nearest enclosing surface, which is how one component works on every rung:

| Property | Use for |
|---|---|
| `--ds-bg` | This surface's background |
| `--ds-text` / `--ds-text-quiet` | Body text / de-emphasised text |
| `--ds-border` | A structural border legible on this rung (~1.35:1) |
| `--ds-separator` | A row-separator rule (~1.12:1) — lighter than `--ds-border` |
| `--ds-hover` / `--ds-selected` | Interaction states |

`--ds-text-quiet` is not just a grey: the `subtle` tier stops meeting 4.5:1 above `widget`, so the
scope swaps tier for you. Reading it is how you avoid shipping unreadable secondary text into a
dialog. Likewise `--ds-border`, because one border value cannot hold its weight across the ladder.
Table row rules use `--ds-separator` so the container outline stays the heavier edge. A filled
header uses `--ds-table-header` (~1.08:1, away from the elevation direction) with no underline
unless it is sticky. On a surface the table keeps that outline; inside a Card pass
`bordered={false} surface="transparent"` so the card is the surface and the table can span its
width with columns aligned to the title.

**Never set a background on an input.** Fields recess one rung *below* their host so they read as
inset, and that is already handled. On `page` a field is transparent with a heavier outline, because
there is no rung below to recess into — do not "fix" that.

**Three levels of nesting, and no more.** `overlay` is a ceiling, not a target. A fourth level paints
at its parent's rung and logs an error in development, so it will look like nothing happened. That is
deliberate: past three levels the steps are too close to read as hierarchy, so a fourth costs
legibility and communicates nothing.

If you want a fourth, the structure is the problem, not the limit. Almost always it is a dialog that
should be a page, or a card inside a panel inside a section that exists only to hold padding. Flatten
it, or put the deepest content on its own route. **If you cannot see how to, stop and ask** rather
than nesting anyway.

**Never use a translucent overlay for a state or a band.** `rgba(255,255,255,0.08)` over a row looks
like it adapts for free, but it compounds — hovered plus selected stacks layers on a colour nobody
chose — and it weakens as the ladder lightens. Use the resolved tokens from the host rung.

## 5. Prefer the highest useful level

Octopus UI follows Atomic Design. Reach for the largest piece that fits:

- **Templates** for full-page generation (`DashboardLayout`, `TablePageLayout`).
- **Organisms** for complex sections (`Table`, `Card`, `Dialog`, `LeftNavigation`).
- **Molecules** for reusable groups (`FormField`, `Search`, `Tabs`, `Alert`).
- **Atoms** only for basic controls.

## 6. Use tokens for meaning

Severity is a reserved scale, not a color choice. Every compact label is a `Chip`, and you pick its
`purpose` from what the value means - never its appearance:

| Meaning | Purpose |
|---|---|
| Risk level | `<Chip purpose="severity" level="critical" />` |
| Workflow or health state | `<Chip purpose="status" tone="warning" label="Investigating" />` |
| Classification metadata | `<Chip purpose="category" label="Credential Access" />` |
| A measured value | `<Chip purpose="metric" label="12 related" />` |
| How mature a feature is | `<Chip purpose="lifecycle" stage="beta" />` |
| An applied filter | `<Chip purpose="filter" field="Severity" label="Critical" onRemove={clear} />` |
| A toggleable option | `<Chip purpose="select" label="EDR" selected={on} onToggle={setOn} />` |
| A filter field the user picks values from | `<FilterMenu field="Severity" options={opts} value={sev} onChange={setSev} />` |
| An entity value in prose | `<Chip purpose="entity" kind="host" value="WIN-DC-02" onOpen={open} />` |
| A `+N more` disclosure | `<Chip purpose="overflow" count={3} onDisclose={showAll} />` |

The appearance, the rendered element and the keyboard contract all follow from `purpose`, so **never
pass `variant` or `color`** - that is the deprecated API and it warns in development. `critical |
high | medium | low` means risk and nothing else: verdict, confidence, health and chart series have
their own tokens. Group more than two chips with `ChipGroup`, which owns the spacing and the
overflow. Inside a `Table`, workflow state is `TableStatusCell` rather than `purpose="status"`.

A chip carries a value, never a verb, so it is not an action trigger - `Isolate host` is a `Button`
and changing a record's status is a `Menu` behind one. `FilterMenu` is the one exception, and a narrow
one: it changes which values a view is filtered on, not the record underneath.

## 7. Space on the 8-point grid

Every margin, padding and gap comes from the spacing scale - never an arbitrary pixel value like
`13px`. In `sx` props use the MUI spacing factor, where each unit is 8px (`p: 3` is 24px); elsewhere
import the named `spacing` tokens. Prefer parent-owned spacing (`gap`, `Stack spacing`) over
per-child margins, and group by proximity: tighter within a group, wider between groups. The tokens
reference lists the full scale and what each rung is for.

## 8. Set type with Typography variants

Every piece of visible text uses `Typography` with an **explicit `variant`**, or
lives in a component slot that already applies one (`DialogTitle`,
`DialogContentText`, `AlertTitle`, `EmptyState` title/description, `FormField`
label). Never drop a raw string into a `Box`, `div`, or `span` and size it with
a `fontSize` literal.

Pick the variant for the **role**, not the pixel size. The tokens reference
lists the full mapping; the short version:

| Role | Variant |
|---|---|
| Page title | `h4` |
| Drawer / empty-state heading | `h6` |
| Card, form section, accordion summary, dialog title | `subtitle1` |
| Compact heading, table row title, filter panel | `subtitle2` |
| Body copy, descriptions, dialog body, current breadcrumb | `body1` (14px) |
| Dense annotation (delta, compact secondary line) | `body2` (12px) — never default copy |
| Metadata, helper, form label, tooltip | `caption` |
| Column header, uppercase tag | `overline` |
| KPI figure | `h3` |

A container that may hold non-text children (a card body, a tab panel) keeps a
`Box` and applies the variant through `sx={{ typography: 'body1' }}` so it does
not wrap a table in a `<p>`.

## 9. Respect required props and accessibility

Honour `requiredProps` from the component reference. Provide labels and `aria-label`s. Icon-only
controls need an accessible label and, ideally, a tooltip. Never rely on color alone to convey
state.

## 10. Only generate components marked `llmSafe`

Each component in the reference carries an `llmSafe` flag. Do not auto-generate anything marked
`llmSafe: false`, and use the alternative the entry names instead - `Modal` is `llmSafe: false`, so
prefer `Dialog`. The example pages (`DashboardExample`, `ThreatsListExample`, and the rest) are
reference material for humans to read, not components to import into product UI; they are excluded
from the reference for that reason.

## 11. When you are unsure, ask

Several of these boundaries are genuinely fuzzy in real designs - which button is primary, whether a
row of labels is a filter or a set of tabs, which surface a panel sits on. **If you cannot
confidently make the call, stop and ask before building.** State the decision you are weighing, the
option you are leaning toward, and why.

Ask in concrete terms, not in the abstract:

> "This row is All / Open / Resolved. I'm treating it as a `ToggleButtonGroup` filter over one table.
> Did you intend these as separate `Tabs` with their own columns?"

A single clarifying question is far cheaper than shipping a control whose behaviour surprises the
user.
