# MiHCM Design System Full Agent Context Generated: docs-build This file is a clean Markdown bundle for agent ingestion. Each section includes canonical URL and source path before content. ## Scan Rules - Read /llms.txt first for routing and /agent-map.json when machine-readable metadata is easier. - Use /llms-full.txt only when broad context is needed; prefer narrow pages first. - Use /agent-tooling for MCP and skill installation. - Use /components/{slug}/usage before examples, then /api and /accessibility for implementation details. - Use MiHCM components, MiHCM icons, standard Tailwind utilities, and MiHCM semantic token classes. Do not hardcode raw visual values. - Validate AI-generated UI descriptors with Zod through @mihcm/ai-ui or mihcm_validate_descriptor before render. --- # Multi-Agent Orchestration URL: https://designsystem.mihcm.com/downloads/agent/docs/AGENTS.md Source: docs/AGENTS.md Purpose: > Specialised subagents dispatched automatically based on task type. Each agent has a focused role, clear inputs/outputs, structured handoff. # Multi-Agent Orchestration > Specialised subagents dispatched automatically based on task type. Each agent has a focused role, clear inputs/outputs, structured handoff. ## Core principle **Agent-first development** — delegate to specialised subagents instead of doing everything in one context. Less context-window pollution, more parallelism. ## Agent roster (mapped to this project) ### Planning & architecture | Agent | Purpose | Dispatch when | |-------|---------|---------------| | **Planner** | 8-persona analysis, write `tasks/todo.md` | Any 3+ step feature | | **Code Architect** | Cross-platform fit, semver impact, ADR draft | Boundary changes, new package | | **Code Explorer** | Map existing primitives, find prior art | Before adding a similar primitive | ### Implementation & quality | Agent | Purpose | Dispatch when | |-------|---------|---------------| | **Code Reviewer** | 5-lens review (security/QA/architect/UX/code) | After any code change | | **Silent-Failure Hunter** | Spot swallowed errors and over-broad catches | After error-handling additions | | **Type-Design Analyzer** | Public-API type quality | After exporting a new type/component | | **Build Resolver** | Fix build/type/lint errors | When verification loop fails | ### Design & UX | Agent | Purpose | Dispatch when | |-------|---------|---------------| | **Design Researcher** | Pull 3-5 production refs, screenshots | Before UI design | | **Design Auditor** | Compare against the 18-point quality checklist | After any UI | | **A11y Reviewer** | WCAG 2.1 AA check, both platforms | Every UI primitive | ### Testing | Agent | Purpose | Dispatch when | |-------|---------|---------------| | **TDD Guide** | Enforce RED-GREEN-REFACTOR | Bugs and new features | | **PR Test Analyzer** | Check coverage on changed files | Pre-merge | ### Documentation & wiki | Agent | Purpose | Dispatch when | |-------|---------|---------------| | **Wiki Maintainer** | Update `docs/index.md`, append `docs/log.md`, write per-component pages | After any code/decision change | | **Comment Analyzer** | Audit code comments for accuracy | Pre-merge | ## Orchestration chains ### Add a primitive ``` Planner (8-persona) → Code Explorer (find prior art) → Design Researcher (3-5 refs) → Code Architect (cross-platform fit) → [implementation] → Code Reviewer + Design Auditor + A11y Reviewer (parallel) → TDD Guide → Wiki Maintainer ``` ### Bug fix ``` Planner → TDD Guide → [fix] → Code Reviewer → Wiki Maintainer ``` ### Refactor ``` Code Architect → Code Reviewer → [refactor] → TDD Guide (regressions) → Wiki Maintainer ``` ### Release ``` PR Test Analyzer → Code Reviewer (security pass) → [pnpm changeset version] → CI/OIDC publish → Wiki Maintainer (release entry in log.md) ``` ## Handoff document format Every agent produces: ```markdown ## Agent: [name] ### Context What was analysed and why. ### Findings - Finding 1 (severity: critical/high/medium/low; rule ID if applicable) ### Files reviewed/modified - `src/packages/ui/src/Button.tsx` — what was done ### Open questions - ... ### Recommendations - ... ``` ## Parallel execution - **Independent** agents run simultaneously (Code Reviewer + Design Auditor + A11y Reviewer). - **Dependent** agents run sequentially (Planner before TDD Guide before any implementation). - Max 5 parallel. - Context isolation: each agent gets only the files it needs. ## Search-first protocol (before ANY new utility) 1. Define need + constraints. 2. Parallel search: npm registry, MCP tools, available skills, GitHub. 3. Evaluate: functionality, maintenance, community, license, size. 4. Decide: **Adopt** > **Extend** > **Compose** > **Build** (custom). 5. Avoid: jumping to code, ignoring MCPs, over-customising wrappers, dependency bloat. --- # Agent Scan Map URL: https://designsystem.mihcm.com/downloads/agent/docs/AGENT_SCAN.md Source: docs/AGENT_SCAN.md Purpose: This is the local repository companion to the docs-site agent files. # Agent Scan Map This is the local repository companion to the docs-site agent files. ## First Read Order 1. `AGENTS.md` — execution rules and repository contracts. 2. `MEMORY.md` — current decisions, previous sessions, and known next steps. 3. `docs/index.md` — wiki catalog. 4. `tasks/gotchas.md` — mistakes that should not repeat. 5. `src/apps/docs/public/llms.txt` — generated public scan map. 6. `src/apps/docs/public/agent-map.json` — generated machine-readable route/source map. ## Public Agent Entry Points | URL | Source | Use | |---|---|---| | `/agent-scan` | `src/apps/docs/app/agent-scan/page.mdx` | Human-readable scan guide. | | `/llms.txt` | generated by `src/apps/docs/scripts/generate-agent-docs.mjs` | Curated AI-readable documentation index. | | `/llms-full.txt` | generated by `src/apps/docs/scripts/generate-agent-docs.mjs` | Full Markdown bundle for broad context. | | `/agent-map.json` | generated by `src/apps/docs/scripts/generate-agent-docs.mjs` | Machine-readable map with route, source, purpose, and tags. | | `/agents.md` | generated by `src/apps/docs/scripts/generate-agent-docs.mjs` | Short fallback entry file for agents. | | `/mcp` | `src/apps/docs/app/mcp/route.ts` | Hosted Streamable HTTP MCP endpoint. | ## Source Routing | Need | Source | |---|---| | Component implementation | `src/packages/ui/src/.tsx` and optional `.native.tsx` | | Component docs-site content | `src/apps/docs/content/components//` | | Component wiki | `docs/components/.md` | | Design tokens | `src/packages/tokens/src/`, `src/packages/theme/src/`, `docs/tokens/` | | MCP tools | `src/packages/mcp/src/tools/` and `src/packages/mcp/src/server.ts` | | AI-UI schemas | `src/packages/ai-ui/src/schemas/` | | Public skills | `skills/*/SKILL.md` and generated downloads under `src/apps/docs/public/downloads/skills/` | | Docs navigation | `src/apps/docs/lib/nav.ts` | ## Maintenance Run the generated scan index before docs builds: ```bash cd src pnpm -F @mihcm/docs-site agent:index ``` The docs site build runs this automatically. When adding a new public docs page, component doc, runbook, ADR, or skill, confirm it appears in `src/apps/docs/public/agent-map.json`. --- # Architecture URL: https://designsystem.mihcm.com/downloads/agent/docs/ARCHITECTURE.md Source: docs/ARCHITECTURE.md Purpose: Architecture documentation. # Architecture ## High-level shape ``` ┌──────────────────────────────────────┐ │ npm registry │ │ (public @mihcm/*) │ └──────────────────▲───────────────────┘ │ trusted publish (CI) │ tokenless install (consumer) │ ┌──────────────┐ ┌─────────────────────────────────────────────────────┐ │ Figma │ │ This monorepo (src/) │ │ designs │──────│ src/packages/ │ │ (Figma │ │ tokens theme primitives icons ai-ui │ │ MCP) │ │ │ │ │ │ │ │ └──────────────┘ │ └─────────┴────┬──────┴──────────┴─────────┘ │ │ │ workspace:* deps │ │ ▼ │ │ apps/ │ │ docs (Next.js) │ │ storybook (RN-web-vite) │ │ playground-web (Next.js) │ │ playground-native (Expo) │ └─────────────────────────────────────────────────────┘ Consumers (separate repos) ┌────────────┐ ┌──────────────┐ ┌──────────────────┐ │ Next.js │ │ React │ │ React Native │ │ app │ │ web app │ │ (Expo) │ │ imports │ │ imports │ │ imports │ │ @mihcm/* │ │ @mihcm/* │ │ @mihcm/* │ └────────────┘ └──────────────┘ └──────────────────┘ ``` ## Cross-platform strategy (the universal-component contract) A primitive is implemented **once** if possible; otherwise the same file exports both via platform extensions. **Single-file path** (default): the component imports from `@mihcm/ui/internal/View`, which itself routes to `react-native-web`'s `View` on web and `react-native`'s `View` on native. NativeWind v4 transforms `className` into `style` on both. Use `class-variance-authority` for simple single-slot variants. Use `tailwind-variants` when a primitive has multiple styled slots, compound variants, or slot-level overrides that would otherwise require custom class maps. **Forked-file path** (only when truly needed, e.g. `Dialog` portal logic): use platform extensions: - `Dialog.tsx` — common types and prop API. - `Dialog.web.tsx` — `@radix-ui/react-dialog` implementation. - `Dialog.native.tsx` — `@rn-primitives/dialog` implementation. Bundlers resolve `.web.tsx` for web and `.native.tsx` for native automatically; `.tsx` is the fallback. ## Token flow ``` src/packages/tokens/src/*.ts ← TS source of truth (typed) │ ├─► CSS variables (tokens.css) ── used by Tailwind preset ├─► JSON ── used by Storybook + design-tools └─► RN theme object ── used by RN ThemeProvider ``` ## AI generative UI flow ``` User prompt │ ▼ AI gateway (server) │ model returns { component: 'Button', props: {...} } ▼ Zod parse via componentDescriptorSchema ◄── reject if invalid │ ▼ Lookup in COMPONENT_REGISTRY (allowlist) ◄── reject if name not allowed │ ▼ Render ── inside an "AI-generated" container ``` The same `componentDescriptor` schema and `COMPONENT_REGISTRY` work in **React, Next.js, and React Native** because the registry maps to `@mihcm/ui` which is universal. ## Data flow (none, by design) This repo ships no application state, no fetching, no auth. It is a **library**. Consumers wire those concerns themselves. The only network calls live in `src/apps/docs` (search index) and `src/apps/playground-web` (demo for the AI-UI renderer). ## Boundary rules - **Primitives** depend on `tokens` + `theme` + `icons` only. Never on `ai-ui`. - **AI-UI** depends on `primitives` to render. Primitives never depend on `ai-ui`. - **CLI** depends on no other workspace package at runtime. Reads source from a published archive. - **MCP** depends on `tokens` + `ai-ui` for live token lookup and descriptor validation. It reads repo docs/source as data and does not sit in any runtime consumer path. - **Apps** can depend on any package via `workspace:*`. ## Versioning - `changesets` drives semver per-package. - Public API of every package documented in `docs/components/*.md` (primitives) or the package README. - Breaking changes require **major** + a migration note in CHANGELOG + a deprecated-in-N-versions warning in the prior minor. --- # Conventions URL: https://designsystem.mihcm.com/downloads/agent/docs/CONVENTIONS.md Source: docs/CONVENTIONS.md Purpose: Components: `PascalCase.tsx` · Utils/hooks: `camelCase.ts` · Tests: `[name].test.ts(x)` · Stories: `[Name].stories.tsx` · Schemas: `[Name].ts` in `schemas/`. # Conventions ## Files & naming Components: `PascalCase.tsx` · Utils/hooks: `camelCase.ts` · Tests: `[name].test.ts(x)` · Stories: `[Name].stories.tsx` · Schemas: `[Name].ts` in `schemas/`. Target 200-400 lines per file. Max 600. One component per file. **No barrel exports at package root** (worse tree-shaking) — explicit per-file imports. Import order: externals → workspace `@mihcm/*` → relative → styles. ## Code-quality invariants - Functions <50 lines. Single responsibility. - Nesting max 4 deep. Extract early returns / helper functions. - Immutability default. `const`. Spread/map over push/splice. - Error handling at every level. Never swallow. Fail closed. - Validate at boundaries (public API, AI-UI input, descriptor parse). - No magic values. Extract constants. - Pure functions preferred. Side effects at the boundary. ## TypeScript `interface` for extendable shapes · `type` for unions · Never `any` → `unknown` + narrow · Explicit return types on exported functions · `as const` for literals · Tooling via `pnpm typecheck`. ## Errors Custom `AppError` exported from each package's `errors.ts` · `redirect()`/`notFound()` (Next.js consumers) outside try/catch · Fail closed · Generic client messages, structured server logs · No stack traces leaked. ## RSC boundaries (consumer concern, but our API must respect them) Server Components default · package component entrypoints may be rendered from Server Components when props are static and serialisable · `"use client"` wrappers are required for hooks/events/browser APIs, controlled state, routing hooks, charts, tables, complex menus, and render-function configs · No async client components · Serialisable props only across the RSC boundary (no Date/Map/Set/functions except server actions) · ISO date strings for any date prop. Consumer-facing guide: docs-site `/developing/client-server-components`. ## Universal-component checklist (every primitive) - [ ] Single file (preferred) or `.web.tsx` + `.native.tsx` + shared types. - [ ] Same prop API on both platforms. - [ ] `className` accepted and merged last with `cn(..., className)` on every visual root and visual sub-component. - [ ] `class-variance-authority` for simple variants; `tailwind-variants` for slot-heavy or compound variants. - [ ] `forwardRef` so refs work. - [ ] `accessibilityRole`/`role`, `accessibilityLabel`/`aria-label`, focus + keyboard. - [ ] Default + hover + focus + active + disabled + loading + error states. - [ ] Story with default + each variant + each size + adversarial (long text, RTL, disabled). - [ ] Zod descriptor in `@mihcm/ai-ui/schemas/.ts`. - [ ] Wiki page in `docs/components/.md`. ## Design standards (production-grade, zero AI slop) ### Research before designing 3-5 real production references (Linear, Vercel, Stripe, Notion, Apple). Use `figma-remote` MCP if Figma exists. WebSearch for current patterns. Never design from imagination alone. ### Accessibility (non-negotiable) WCAG 2.1 AA · Keyboard (Tab/Enter/Space) · Visible focus (never `outline: none` without replacement) · 4.5:1 normal, 3:1 large · Semantic HTML first, `aria-*` when needed · Touch targets 44×44 · `prefers-reduced-motion` · Skip links · Visible labels · `aria-describedby` for errors. Form controls are semantic-first. Use real `input`, `textarea`, `button`, `select`/combobox semantics, `label htmlFor`, and `aria-describedby` for helper/error text. Buttons default to `type="button"`; submit/reset must be explicit. Inputs must forward native attributes (`name`, `type`, `autoComplete`, `required`, `disabled`, `aria-invalid`, `aria-required`) instead of replacing them with visual-only wrappers. ### Typography Max 2 typefaces (1 preferred) · Hierarchy: display → heading → subheading → body → caption · Line height 1.5 body, 1.2-1.3 headings · Line length 45-75 chars · Min 14px body (16px preferred) · `tabular-nums` for numbers in tables. Use semantic tags for document structure. Headings must render as `h1`..`h6`, paragraphs as `p`, and inline fragments as `span`, `strong`, `em`, `small`, or `label`. In MiHCM UI code, use `Text as="h1"`..`Text as="h6"` instead of styling a `span` to look like a heading. Letter spacing is `0` in core components and docs; do not use negative tracking. ### Spacing & layout Base unit 4px (4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96) · Consistent component padding · Max 1280px pages, 640px text · 12-col desktop, stack mobile. ### Color Semantic tokens ONLY. Never raw hex. Dark mode from day 1. Status colors consistent. Never color-only. Apply the MiHCM 60/30/10 composition contract: - `60%` dominant neutral surfaces: `bg-ratio-60`, `bg-dominant`, `bg-background`, `bg-card`, `bg-muted`. - `30%` supporting brand blue: `bg-ratio-30`, `bg-supporting`, `bg-primary`, active navigation, primary actions, focus systems. - `10%` accent orange: `bg-ratio-10`, `bg-emphasis`, `bg-accent`, selected hints, notification marks, small highlights. This is a screen-level hierarchy heuristic, not a pixel counter. Components should default to neutral surfaces, use brand blue for primary structure/actions, and reserve orange for emphasis. AI agents must not hardcode hex values or invent extra brand colors; use Tailwind token utilities only. ### Component states Default · Hover · Focus · Active · Disabled · Loading · Error · Empty · Success. Skeletons not spinners. Recovery actions on errors. Guidance in empty states. ### Motion Purposeful only. 150-300ms micro, 300-500ms layout. ease-out entrances, ease-in exits. Respect `prefers-reduced-motion`. No animation on first paint. ### Principles **Gestalt** (proximity, similarity) · **Fitts's Law** (important = large + close) · **Hick's Law** (fewer choices, progressive disclosure) · **Jakob's Law** (follow conventions) · **Dieter Rams** ("Less, but better"). ## Tailwind 4 + NativeWind v4 conventions - Theme variables defined in `src/packages/theme/src/tokens.css` via `@theme { }`. - NativeWind preset extends those CSS vars for native equivalents. - `className="bg-primary text-primary-foreground"` is the only acceptable style API in primitives. - Consumer Tailwind classes must be able to override component defaults. Put defaults first and `className` last: `cn(defaultClasses, variantClasses, stateClasses, className)`. - Use `tailwind-variants` instead of handwritten slot maps when a component exposes multiple visual slots or compound styling rules. Keep existing `cva` usage for simple single-root components unless a change already needs slot-aware composition. - Every visual component and visual sub-component must expose `className`. Non-visual provider roots may omit it only when they render no DOM/native view. - `style` prop accepted but discouraged. - Use `gap-*` not `space-*`. Use `size-*` for equal width+height. ## Icons `@mihcm/icons` wraps Lucide. Same import on web and native. Always pair with text or `aria-label`. Consistent size per context. ## Git Branches: `feat/`, `fix/`, `refactor/`, `docs/`, `chore/`. Commits: `type(scope): description`. Pre-commit: `pnpm typecheck && pnpm lint && pnpm test`. PRs <400 lines. Changeset present if a package changed. ## Testing - **Unit**: vitest. Zod schemas, utils, hooks, variant resolution. - **Component**: @testing-library/react + @testing-library/react-native. Behavior, not snapshots. - **A11y**: axe-core for web; manual checklist for native. - **Story-driven**: Storybook play functions for interactive flows. - **Adversarial**: long text, RTL, very large lists, malformed AI descriptors, unicode, emoji. ## Public API stability Breaking change → major. Deprecate in a minor first; emit a console.warn pointing to the wiki page; remove in the major after. --- # Security Quick Reference URL: https://designsystem.mihcm.com/downloads/agent/docs/SECURITY.md Source: docs/SECURITY.md Purpose: > 32 rules. Full playbook: `docs/security-playbook.md` (100+ rules with IDs). # Security Quick Reference > 32 rules. Full playbook: `docs/security-playbook.md` (100+ rules with IDs). > Reference IDs in reviews: "Violates DEPS-02", "Fails AI-UI-01". ## Mindset On every change ask: Can a malicious package be installed? Can an AI model name an unsafe component? Can a token leak? Can a user bypass Zod? Can the registry be poisoned? ## Supply chain (top concern for a library) 01 — Pin every dep. Lockfile checked in. (DEPS-01) 02 — `pnpm audit` clean before any release. Critical/high block. (DEPS-02) 03 — Verify package authenticity with trusted publishing/provenance. (DEPS-03) 04 — Review every postinstall script before adding a dep. (DEPS-04) 05 — SRI hashes for any CDN script in docs site. (DEPS-05) ## Registry & publishing 06 — `@mihcm/*` scope only. Never publish unscoped. (REG-01) 07 — Publish from CI with OIDC trusted publishing. No static `NPM_TOKEN`. (REG-02) 08 — `.npmrc` with auth tokens never committed. (REG-03) 09 — Consumers install from public npm without package tokens. (REG-04) 10 — Two-factor auth required on all publishers' GitHub accounts. (REG-05) ## AI-UI (generative-UI security) 11 — Every AI-returned descriptor passes Zod validation before render. (AI-UI-01) 12 — Component names from a model checked against an allowlist. (AI-UI-02) 13 — Never run any dynamic-code-evaluation API on a model's output. (AI-UI-03) 14 — Raw HTML from a model is never injected — only validated descriptors. (AI-UI-04) 15 — Treat AI output as untrusted user input. Apply same input-validation rules. (AI-UI-05) 16 — Cap AI API costs in code AND provider dashboard. (AI-UI-06) ## Remote MCP 17 — Hosted MCP tools are read-only design-system facts. No repo mutation, deploy, publish, or GitHub write tools. (MCP-01) 18 — Hosted MCP never accepts model-supplied filesystem paths or dynamic imports. (MCP-02) 19 — Hosted MCP validates descriptor payloads through the live AI-UI Zod schema. (MCP-03) 20 — Hosted MCP never logs raw request bodies, prompts, tokens, or descriptors. (MCP-04) 21 — Hosted MCP disables source-code output even if local stdio allows it. (MCP-05) 22 — Hosted MCP enforces method, host/origin, request-size, and no-store response safeguards. (MCP-06) 23 — Private/internal MCP deployments require bearer auth via `MIHCM_MCP_BEARER_TOKEN`. (MCP-07) 24 — MCP review is advisory until paired with local lint/CI enforcement such as `mihcm-design-system-check`. (MCP-09) ## Tokens & secrets 25 — No secrets in package source. Anything `NEXT_PUBLIC_*` is public. (CRYPTO-01) 26 — Rotate signing/publishing credentials every 90 days. (CRYPTO-02) 27 — `.env*` and `.npmrc` in `.gitignore`. Pre-commit denies them. (CRYPTO-03) ## Consumer-facing API 28 — Validate every `@mihcm/*` public API input with Zod. Throw `AppError`. (API-01) 29 — Never expose internal package paths in error messages. (API-02) 30 — Generic error messages to consumers; structured logs server-side. (API-03) ## A11y is a security concern (denial-of-access) 31 — WCAG 2.1 AA on every primitive. Failing a11y is a release blocker. (UX-01) 32 — Test with reduced motion + screen reader on at least one primitive per release. (UX-02) ## Pre-commit No secrets · No `.env` · No `.npmrc` with tokens · `pnpm typecheck` · `pnpm lint` · `pnpm test` · changeset present if package modified. ## Pre-release `pnpm audit` clean · trusted publishing/provenance configured · all packages have `repository` field · no relative `file:` deps · changesets versioned · CHANGELOGs updated. --- # Security audit — 2026-05-23 URL: https://designsystem.mihcm.com/downloads/agent/docs/audits/2026-05-23-security-audit.md Source: docs/audits/2026-05-23-security-audit.md Purpose: > Full-stack security sweep across the eight `@mihcm/*` packages, the docs site, the hosted MCP server, the AI-UI renderer, and the CI/publish pipeline. Aligns with `docs/security-playbook.md` rule IDs (DEPS-*, AIUI-*, M # Security audit — 2026-05-23 > Full-stack security sweep across the eight `@mihcm/*` packages, the docs site, the hosted MCP server, the AI-UI renderer, and the CI/publish pipeline. Aligns with `docs/security-playbook.md` rule IDs (DEPS-*, AIUI-*, MCP-*, DOC-*, SUP-*, REG-*). ## Top-line summary | Severity | Count | Disposition | |---|---|---| | Critical | 0 | — | | High | 0 | — | | Moderate | 2 fixed, 0 remaining | `qs` DoS overridden to ≥ 6.15.2; `uuid` bounds-check overridden to ≥ 11.1.1 | | Low | 1 fixed | `check-contract` path-traversal probe (input regex tightened) | After the fixes in this audit, `pnpm audit --audit-level=low` reports **0 vulnerabilities** across the entire workspace (1,560 dependencies scanned). --- ## 1. Dependency vulnerabilities (DEPS-*) ### 1.1 `qs <= 6.15.1` — Moderate (CVE-2026-8723, GHSA-q8mj-m7cp-5q26) - Path: `@mihcm/docs-site > @modelcontextprotocol/sdk > express > qs` - CVSS: 5.3 (network-reachable DoS, no integrity/confidentiality impact) - Behaviour: `qs.stringify` throws `TypeError` when given an array with `null`/`undefined` elements under `arrayFormat: 'comma'` + `encodeValuesOnly: true`. Express catches the throw, returns 500; worker survives, but the call site is reachable from the hosted MCP route. - Fix applied: `pnpm.overrides.qs >= 6.15.2` added to root `package.json`. Re-audit confirms removed. ### 1.2 `uuid < 11.1.1` — Moderate - Path: `apps/playground-native > expo > @expo/config-plugins > xcode > uuid` - Scope: Internal Expo dev app only — never reaches a published `@mihcm/*` artefact. `xcode` runs at build time, not runtime. - Fix applied: `pnpm.overrides.uuid >= 11.1.1` added defensively. Re-audit clean. ### 1.3 Lockfile + supply chain hygiene (SUP-*) - pnpm-lock.yaml present (591 KB, 16 147 lines) and committed. - Every GitHub Action pinned by full SHA in `.github/workflows/*.yml`. Mitigates tag-poisoning supply-chain attacks. - `pnpm install --frozen-lockfile` runs in the publish workflow before any build step. - `pnpm audit --audit-level=high` runs in the publish workflow's audit step (will fail the publish if a high/critical vuln appears). --- ## 2. Secret hygiene (REG-*, DOC-*) | Check | Result | |---|---| | Hardcoded API keys / private keys in repo | none found (grep across PEM headers, common key-token patterns, AWS keys) | | Committed `.env`, `.env.local`, `.env.production` | none | | `NPM_TOKEN` / `NODE_AUTH_TOKEN` referenced in CI | none — publish workflow relies entirely on npm trusted-publisher OIDC with sigstore-signed identity claims (no long-lived publish token in CI) | | `GITHUB_TOKEN` usage | scoped to `release.yml` + `publish.yml`, minimum permissions only | --- ## 3. CI / publish pipeline (SUP-*, REG-*) ### 3.1 Workflow permissions per file | Workflow | `permissions:` | Notes | |---|---|---| | `audit.yml` | `contents: read` | Least-privilege read-only audit | | `ci.yml` | `contents: read` | Lint + typecheck + test on PRs | | `publish.yml` | `contents: write`, `pull-requests: write`, `id-token: write` | `id-token: write` required for OIDC trusted publishing | | `release.yml` | `contents: write`, `pull-requests: write` | Changesets PR automation only | ### 3.2 Workflow-injection risk - No untrusted `${{ github.event.* }}` substitution into shell `run:` blocks (issue/PR titles, commit messages, refs). - No `pull_request_target` triggers anywhere — all CI runs against the head commit's code, not main's secrets. ### 3.3 npm provenance — partially active The publish workflow currently sets `NPM_CONFIG_PROVENANCE: 'false'` because npm's provenance attestation requires a public GitHub source repo. The current repo is private. Trade-off documented in `.github/workflows/publish.yml`: - OIDC trusted-publisher auth still active — the publish identity is the `publish.yml` workflow, verified by npm against the configured trusted publisher on each published package. - Sigstore provenance can be re-enabled in a single line change if the repo is made public. --- ## 4. Application surface — XSS / injection (DOC-*, AIUI-*) ### 4.1 Raw-HTML injection sink audit Only two raw-HTML React-prop sinks (`d*ngerouslySetInnerHTML`) in the entire repo: | File | Source | Verdict | |---|---|---| | `src/apps/docs/app/layout.tsx` | Hard-coded theme bootstrap string. No user input. | safe | | `src/apps/docs/components/CodeHighlight.tsx` | shiki HTML output from hard-coded code strings in MDX. No user input ever flows in. | safe (inline comment in source) | Both are accompanied by inline `// Safety: …` documentation. ### 4.2 Dynamic-code-evaluation APIs - JavaScript `eval` and runtime function constructors: 0 occurrences in any workspace package or app source file. - `child_process` / `execSync` / `spawnSync` in published packages: 0 occurrences. - DOM write sinks (e.g. `doc<>ument.write`, direct element `inner<>HTML` assignment, `window.location` assignment): 0 occurrences in `@mihcm/ui`, `@mihcm/ai-ui`, `@mihcm/mcp`. ### 4.3 Open-redirect / tab-nabbing `@mihcm/ui/Link` derives `target` + `rel` defensively: ```ts const linkTarget = external ? (target ?? '_blank') : target; const opensNewContext = linkTarget === '_blank'; const linkRel = external || opensNewContext ? mergeRel(rel, 'noopener noreferrer') : rel; ``` Every `target="_blank"` automatically receives `rel="noopener noreferrer"` — no consumer override can drop it. --- ## 5. Docs site security headers (DOC-01) `src/apps/docs/next.config.ts` emits on every response: | Header | Value | |---|---| | `X-Frame-Options` | `DENY` | | `X-Content-Type-Options` | `nosniff` | | `Referrer-Policy` | `strict-origin-when-cross-origin` | | `Strict-Transport-Security` | `max-age=31536000; includeSubDomains; preload` | | `Content-Security-Policy` (prod) | strict directives — `default-src 'self'`, no `unsafe-eval`, `frame-ancestors 'none'`, `base-uri 'self'`, `form-action 'self'` | Dev CSP relaxes `script-src` to allow Turbopack / React DevTools. Production stays strict. Matches DOC-01. --- ## 6. Hosted MCP route (`src/apps/docs/app/mcp/route.ts`) — MCP-* | Control | State | |---|---| | Bearer-token gate via `MIHCM_MCP_BEARER_TOKEN` env | optional; when set, every request must present it | | Allowed-hosts whitelist (`MIHCM_MCP_ALLOWED_HOSTS`) | enforced — non-matching hosts get 403 | | Origin check | `if (requestOrigin && !origin)` → 403 | | Max body size | 64 KiB — both `Content-Length` header + actual byte count | | Method allowlist | POST + OPTIONS only | | `Access-Control-Allow-Origin` | echoed only for trusted origins | | Security headers | `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, `Cache-Control: no-store` on every response | | Source-output disabled in hosted mode | `createMihcmMcpServer({ allowSource: false })` — `mihcm_get_component` and `mihcm_get_component_examples` have `includeSource` forcibly set to `false` regardless of input | ### 6.1 MCP tool inputs — Zod-bounded All MCP tool inputs are Zod-validated with explicit `min`/`max` bounds. Specifically: - `readComponentSources(name)` enforces `/^[A-Z][A-Za-z0-9]+$/` PASCAL regex before any `fs.readFile` call. - FIXED in this audit: `checkContractInput` previously accepted `z.string().min(1).max(64)` for `name` without the PASCAL guard. The function downstream uses `name` in `join(REPO.storybookSrc, ${name}.stories.tsx)` for `fs.access`. A crafted `name` like `"../../etc/passwd"` would build a path outside `REPO.storybookSrc` — `fs.access` would only leak file existence (the tool never reads the file body), but the probe was still possible. Fix applied in `src/packages/mcp/src/tools/check-contract.ts`: ```ts export const checkContractInput = z.object({ name: z .string() .min(1) .max(64) .regex(/^[A-Z][A-Za-z0-9]+$/, 'name must be a PascalCase component identifier'), }); ``` After: `fs.access` only ever runs against paths that resolve inside `REPO.storybookSrc`. ### 6.2 No arbitrary filesystem read `fs.readFile` / `fs.readdir` call sites enumerated: | File | Path source | Notes | |---|---|---| | `registry.ts` | `REPO.uiSrc` + `name.tsx` (name is PASCAL-bounded) | safe | | `registry.ts` `readIfExists(path)` | callers pass `join(REPO.*, ${name}.ext)` | bounded via callers | | `check.ts` `auditDirectory` | recursive walk under explicit consumer path passed via CLI argv | deterministic checker run only against explicit paths | | `tools/recent-log.ts` | `join(REPO.docs, 'log.md')` — fixed | safe | | `tools/check-contract.ts` `fileContains(...)` | `join(REPO.docs, 'index.md')` — fixed, `name` only used as `String.includes(...)` needle | safe | --- ## 7. AI-UI renderer (`@mihcm/ai-ui`) — AIUI-* | Control | State | |---|---| | Zod-validated allowlist on every descriptor | `componentDescriptorSchema = z.union([...])` — only schemas in `ALLOWED_COMPONENTS` accepted | | No dynamic component resolution from model output | Renderer is a hardcoded `switch (descriptor.component) { case 'Button': ... }` | | Safe URL schema on every model-controlled href | `safeHrefSchema` rejects `j*vascript:`, protocol-relative `//foo`, and any non-HTTPS absolute URL. Allows only relative paths, `#hash`, or `https://` URLs | | Safe image URL | `safeImageUrlSchema = z.string().url().refine(... === 'https:')` | | No inline-HTML sinks in renderer or schemas | confirmed | | Coverage governance | `AI_UI_COMPONENT_COVERAGE` — every UI primitive is explicitly marked `supported` / `host-composed` / `blocked`. Tooling enforces parity between coverage, schemas, renderer cases, and tool-prompt allowlist. | --- ## 8. Per-package summary | Package | Source surface | Findings | |---|---|---| | `@mihcm/ui@0.16.1` | UI primitives | No XSS sinks. No dynamic code. Link tab-nabbing protected. | | `@mihcm/ai-ui@0.16.1` | Zod schemas + renderer | Allowlist + safe URL schemas + no raw HTML. | | `@mihcm/mcp@0.5.10` | MCP server tools | Path-probe in `check-contract.ts` (now fixed). | | `@mihcm/theme@0.2.11` | CSS variables + Tailwind preset | Static tokens only. No JS sinks. | | `@mihcm/tokens@0.6.4` | Generated CSS variables | Static. | | `@mihcm/icons@0.1.4` | Lucide re-exports | Pure pass-through. | | `@mihcm/cli@0.4.4` | Copy-paste CLI | Reads from `node_modules` only; refuses arbitrary URLs; no shell invocations against user-controlled commands. | | `@mihcm/eslint-config@0.1.4` | Lint config | Configuration-only. | --- ## 9. Action items + accepted residuals ### Fixed in this audit - [x] DEPS-01 — `qs` DoS — `pnpm overrides.qs >= 6.15.2` (root `package.json`) - [x] DEPS-02 — `uuid` bounds check — `pnpm overrides.uuid >= 11.1.1` - [x] MCP-01 — `check-contract.ts` input regex tightened to PASCAL ### Accepted residuals (documented) - SUP-prov-01 — npm sigstore provenance disabled because the source repo is private. OIDC trusted publishing remains the publish identity gate. Re-enable by flipping `NPM_CONFIG_PROVENANCE` to `'true'` when the repo goes public. ### Recommended follow-ups (not blocking) - MCP-02 — add the same PASCAL regex to any future tool that takes a component `name` (defensive — the existing `readComponentSources` guard already covers `get-component`, `get-component-examples`, `check-contract`). - DOC-02 — consider adding `Permissions-Policy: camera=(), microphone=(), geolocation=()` to the docs `next.config.ts` headers list. Not currently necessary because the docs site doesn't request those capabilities, but explicit denial is defence-in-depth. --- ## Re-audit verification ``` $ pnpm audit --audit-level=low --json | jq .metadata.vulnerabilities { "info": 0, "low": 0, "moderate": 0, "high": 0, "critical": 0 } $ pnpm typecheck → 18/18 ✓ $ pnpm test → 519 passed / 3 skipped / 0 failed ✓ $ pnpm -F @mihcm/mcp build → ✓ ``` Audit aligns with `docs/security-playbook.md` and CLAUDE.md §8. --- # Logo Usage URL: https://designsystem.mihcm.com/downloads/agent/docs/brand/Logo.md Source: docs/brand/Logo.md Purpose: > Brand usage, clear-space rules, background guidance, and downloadable logo assets for humans and AI tools. # Logo Usage > Brand usage, clear-space rules, background guidance, and downloadable logo assets for humans and AI tools. ## Source The logo assets were exported from temporary Figma logo node `24:388`. Logo usage rules were synced from Figma node `133:2` ("Logo Usage Guidelines"). Use these rules for `mihcm.com` and other marketing website surfaces. The artboard contains these brand groups: - MiHCM - MiHCM Lite - MiA ONE - Smart Assist - Syntra - Smart Assist icon - Syntra icon - MiHCM fav icon Each group has Primary, Secondary, White, and Black variants in the Figma source. The assets are stored in `src/apps/docs/public/brand/logos` so the docs site and AI tools can reference stable repo-hosted files instead of temporary Figma MCP URLs. ## Downloadable Assets | Brand | Primary | Secondary | White | Black | | ----------------- | ------------------------------- | --------------------------------- | ----------------------------- | ----------------------------- | | MiHCM | `mihcm-primary.svg` | `mihcm-secondary.svg` | `mihcm-white.svg` | `mihcm-black.svg` | | MiHCM Lite | `mihcm-lite-primary.svg` | `mihcm-lite-secondary.svg` | `mihcm-lite-white.svg` | `mihcm-lite-black.svg` | | MiA ONE | `mia-one-primary.svg` | `mia-one-secondary.svg` | `mia-one-white.svg` | `mia-one-black.svg` | | Smart Assist | `smart-assist-primary.svg` | `smart-assist-secondary.svg` | `smart-assist-white.svg` | `smart-assist-black.svg` | | Syntra | `syntra-primary.svg` | `syntra-secondary.svg` | `syntra-white.svg` | `syntra-black.svg` | | Smart Assist icon | `smart-assist-icon-primary.svg` | `smart-assist-icon-secondary.svg` | `smart-assist-icon-white.svg` | `smart-assist-icon-black.svg` | | Syntra icon | `syntra-icon-primary.svg` | `syntra-icon-secondary.svg` | `syntra-icon-white.svg` | `syntra-icon-black.svg` | | MiHCM fav icon | `mihcm-favicon-primary.svg` | `mihcm-favicon-secondary.svg` | `mihcm-favicon-white.svg` | `mihcm-favicon-black.svg` | Additional files: | Asset | File | Use | | -------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | Manifest | `src/apps/docs/public/brand/logos/manifest.json` | Machine-readable brand family and asset list for AI tools and automation. | | Bundle | `src/apps/docs/public/brand/logos/mihcm-logo-assets.zip` | All SVG logo assets plus the manifest. | | Legacy MiHCM aliases | `mihcm-logo-full-color.svg`, `mihcm-logo-reversed.svg`, `mihcm-logo-monochrome-navy.svg`, `mihcm-logo-white.svg` | Backwards-compatible filenames mapped to the official MiHCM Figma exports. | ## Brand Family Rules | Brand | Use | Rule | | -------------- | ---------------- | ------------------------------------------------------------------------------------- | | MiHCM | Master brand | Use for company-level, platform-level, and general MiHCM communication. | | MiHCM Lite | Product brand | Use only when the experience specifically refers to MiHCM Lite. | | MiA ONE | Product brand | Use only for MiA ONE surfaces, pages, campaigns, and app entries. | | Smart Assist | Product brand | Use the wordmark for named product surfaces; use the icon only in compact placements. | | Syntra | Product brand | Use the wordmark for named product surfaces; use the icon only in compact placements. | | MiHCM fav icon | Application icon | Use for favicons, app icons, launcher tiles, or compact browser-level identity. | Do not substitute one product brand for another. If the context is not product-specific, use the MiHCM master brand. ## Variant Selection | Variant | Use | | --------- | ----------------------------------------------------------------------------------------------------- | | Primary | Use on white or light neutral backgrounds. | | Secondary | Use only when the source asset set or a product-specific placement calls for the alternate treatment. | | White | Use on primary orange, secondary blue, dark navy, black, or quiet image backgrounds. | | Black | Use for single-colour applications on light backgrounds. | ## Logo on Backgrounds | Background | Token | Required variant | | ---------------- | ----- | ---------------- | | White background | White | Primary logo | | Light neutral | N20 | Primary logo | | Primary orange | P300 | White logo | | Secondary blue | S300 | White logo | | Dark navy | N900 | White logo | | Black | Black | White logo | ## Clear Space Use the MiHCM logo dimensions as the measurement source: - `x = logo width = 119px` - `y = logo height = 80px` - Horizontal clear space: `x/2 = 60px` on the left and right. - Vertical clear space: `y/2 = 40px` above and below. - No text, graphics, other logos, or UI elements should appear inside this zone. ## Background Usage | Background | Use | Avoid | | ------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------ | | White / light neutral | Primary or black logo. | White or low-contrast variants. | | MiHCM navy / dark neutral | White logo. | Primary navy marks with poor contrast. | | Image / gradient | White logo only when the area behind the logo is dark and visually quiet. | Busy image areas, low contrast, blur-only fixes. | | Single-color print or engraving | Black or white variant. | Mixing single-color and full-color pieces. | ## Minimum Size Never scale logos below these minimums. If the mark becomes illegible, use a larger size rather than simplifying the shape. | Logo | Min. digital width | Min. print width | Min. favicon size | | ------------- | ------------------ | ---------------- | ----------------- | | MiHCM | `80px` | `20mm` | `16px` | | MiHCM Lite | `80px` | `20mm` | - | | MiA ONE | `80px` | `20mm` | - | | SmartAssist | `120px` | `30mm` | - | | Syntra | `100px` | `25mm` | - | | Fav icon (mi) | `32px` | `8mm` | `16px` | ## Do - Use the SVG assets as supplied. - Preserve aspect ratio. - Keep sufficient contrast against the background. - Use the correct brand family for the product or surface. - Use wordmarks when there is room and icons only for compact placements. - Reference the manifest when AI tools need to choose a logo automatically. ## Do Not - Recolor individual logo parts. - Stretch, rotate, skew, crop, or outline the logo. - Add shadows, glows, strokes, or container effects to fix contrast. - Place logos on busy imagery without a quiet contrast area. - Rebuild logos with text or icon primitives. - Use screenshots as production assets. - Use Smart Assist or Syntra icons as substitutes for their full product wordmarks when there is enough room for the wordmark. - Use the Primary logo on dark or coloured backgrounds. - Use the MiHCM Lite logo to represent the main MiHCM brand. ## Developer Component Naming Logo component and asset APIs should keep the Figma component naming model so developers and AI tools can select the correct version consistently. | Component | Prop | Values | Status | | ---------------- | ------- | -------------------------------- | ------------------------------------------ | | Logo MiHCM | Version | Primary, Secondary, Black, White | Complete | | Logo MiHCM Lite | Version | Primary, Secondary, White, Black | Complete | | Logo MiA ONE | Version | Primary, Secondary, White, Black | Complete - confirm Black variant visually | | Logo SmartAssist | Version | Primary, Secondary, White, Black | Complete | | Logo Syntra | Version | Primary, Secondary, White, Black | Complete | | Icon SmartAssist | Version | Primary, Secondary, White, Black | Complete | | Fav icon | Version | Primary, Secondary, White, Black | Complete - verify White renders on dark bg | ## AI Tool Guidance When generating designs, AI tools should: 1. Read `/brand/logos/manifest.json`. 2. Select the brand family first. 3. Select the variant by background context. 4. Enforce the background matrix above: Primary only on white/light neutral surfaces; White on orange, blue, dark navy, and black surfaces. 5. Use icon-only assets only for favicons, app launchers, compact navigation, or square placements. 6. Preserve the SVG aspect ratio. 7. Enforce clear space: `x/2` horizontally and `y/2` vertically. 8. Avoid inventing alternate logos, colors, or wordmarks. If an AI tool cannot determine the product context, it should default to the MiHCM primary logo on a light neutral surface. --- # AccessLevelGroup URL: https://designsystem.mihcm.com/downloads/agent/docs/components/AccessLevelGroup.md Source: docs/components/AccessLevelGroup.md Purpose: > 4-state segmented button used to set per-module access: None / View / Edit / Full. Universal across React, Next.js, and React Native. # AccessLevelGroup > 4-state segmented button used to set per-module access: None / View / Edit / Full. Universal across React, Next.js, and React Native. ## Use cases - Per-module permission rows inside the MiHCM Security editor. - Anywhere a discrete 4-level (or `options`-overridden) access selector is needed. When NOT to use: - For a 2-state toggle → use [`Switch`](./Switch.md). - For free-form multi-select → use [`CheckboxGrid`](./CheckboxGrid.md). ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `'none' \| 'view' \| 'edit' \| 'full'` | — | Current access level. | | `onValueChange` | `(value: AccessLevel) => void` | — | Fired when a level is chosen. | | `options` | `readonly AccessLevelOption[]` | `ACCESS_LEVELS` | Override the 4 default levels. | | `disabled` | `boolean` | `false` | Disable all buttons. | | `aria-label` / `accessibilityLabel` | `string` | `'Access level'` | Group label for assistive tech. | | `className` | `string` | — | Merged via `tailwind-merge`. | `AccessLevelOption` shape: `{ value, label, tone }` where `tone` is one of `'neutral' | 'view' | 'accent' | 'primary'`. The `ACCESS_LEVELS` constant is exported so consumers can read it without re-declaring the defaults: ```ts ``` ## A11y - Web: renders as `role="radiogroup"` with each option as `role="radio"` and `aria-checked` driven by `value`. - Native: renders as `accessibilityRole="tablist"` (RN has no `radiogroup`) with each option `accessibilityRole="radio"` and `accessibilityState.selected`. ## History - 2026-05-11 — Initial implementation, extracted from MiHCM Security prototype's `.acc-btn` pattern. --- # Accordion URL: https://designsystem.mihcm.com/downloads/agent/docs/components/Accordion.md Source: docs/components/Accordion.md Purpose: Vertically stacked collapsible sections. Supports single (one open) and multiple (any number open) modes with animated height transitions. # Accordion Vertically stacked collapsible sections. Supports single (one open) and multiple (any number open) modes with animated height transitions. **Package**: `@mihcm/ui` **Import**: `import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@mihcm/ui/Accordion';` ## Sub-components | Component | Purpose | |---|---| | `Accordion` | Root wrapper. Accepts `type`, `variant`, `defaultValue`. | | `AccordionItem` | Container for one section. Accepts `value` (unique key) and `disabled`. | | `AccordionTrigger` | Clickable header button. Renders a chevron that rotates on open. | | `AccordionContent` | Collapsible content region. Animated via CSS grid `0fr` / `1fr`. | ## Props ### Accordion (root) | Prop | Type | Default | Description | |---|---|---|---| | `type` | `'single' \| 'multiple'` | `'single'` | Single allows one open section; multiple allows any number. | | `variant` | `'default' \| 'bordered' \| 'flush'` | `'default'` | Visual style. `default` = bottom-bordered, `bordered` = card-style, `flush` = minimal. | | `defaultValue` | `string \| string[]` | — | Value(s) that should be open initially. | | `duration` | `'fast' \| 'normal' \| 'slow'` | `'normal'` | Motion speed. Uses 300 ms, 500 ms, or 700 ms with a smooth drawer-like easing curve. | | `className` | `string` | — | Additional Tailwind classes. | ### AccordionItem | Prop | Type | Default | Description | |---|---|---|---| | `value` | `string` | — | Unique identifier for this item within the accordion. Required. | | `disabled` | `boolean` | `false` | When true, the trigger is disabled and the item cannot be toggled. | | `className` | `string` | — | Additional Tailwind classes. | ## Variants - **default** — bottom-bordered items (like Stripe / Linear FAQ sections). - **bordered** — each item in a rounded card with border; gains a shadow when open. - **flush** — no borders, minimal style, tight spacing. ## Motion Accordion content stays mounted while closing so the height transition can animate smoothly. Closed panels use `aria-hidden` and `inert` instead of `hidden`, preventing focus from entering collapsed content without cutting off the exit animation. Use `duration="fast" | "normal" | "slow"`: - `fast`: 300 ms - `normal`: 500 ms - `slow`: 700 ms All durations use `cubic-bezier(0.16, 1, 0.3, 1)` for a smoother, less abrupt expand/collapse feel. ## Platform support | Platform | Status | |---|---| | Web (React DOM) | Fully supported | | React Native | Not yet implemented | ## Accessibility - Trigger renders as a ` ``` Do not use raw hex values, arbitrary bracket utilities like `bg-[#123456]`, inline style color overrides, or local CSS modules. Those bypass the token system, dark mode, accessibility review, and MCP enforcement rules. ## A11y **Web**: - `