# DESIGN:OS Studio (`design-os-studio`) — Agent Instructions (`CLAUDE.md`)

Native GPU-accelerated Rust design canvas, `.fig` / `.designos` / `.designos` editor, and headless MCP server (`designos-mcp` / `designos-mcp`) for the **DESIGN:OS / EaseUI** ecosystem.

> **Before starting any work**, read:
> 1. `.project-agent.md` — product identity, EaseUI BrainWave 2.0 theme tokens, and verification commands.
> 2. `CONTEXT.md` — canonical domain glossary (`Document`, `TileView::rev`, `SceneIndex`, Compact Native Format).
> 3. `.brv/context-tree/session-handoffs/latest.md` — latest session state, verified metrics, and active invariants.
> 4. `.brv/context-tree/architecture/crate-map.md` & `.brv/context-tree/architecture/figma-vs-designos-engine.md` — crate responsibilities, hot paths, and Figma engine mapping.

---

## 1. Required Skills (Mandatory)

Every agent working on `design-os-studio` MUST load and follow:
- **`es:rust`** — Rust 2021 ownership, borrowing, zero-allocation hot paths, `parking_lot` / `egui` lock discipline, and deterministic error handling.
- **`es:rust-gpu-canvas`** — `wgpu` Metal/Vulkan/GL rendering architecture, `egui-wgpu` callback lifecycle, `lyon` tessellation budgeting, spatial culling, and CPU/GPU parity.
- **`es:astra-work`** — Outer execution loop, outcome contract, and post-mutation verification with real evidence.
- **`es:fable-thinking`** — Epistemic ledger (`FACT` / `INFERENCE` / `ASSUMPTION`), falsification-first debugging, and prediction discipline.

---

## 2. Workspace Crate Map (11 Crates)

| Crate | Path / Binary | Role & Primary Responsibility |
|---|---|---|
| **`designos-doc`** | `crates/designos-doc` | Core document model (`Document`, `Node`, `NodeId`, `NodeKind`), compact JSON serializer (`serde_images` `"hex:..."` + `skip_serializing_if`), styles, variables, components, prototyping, and undo/redo history. |
| **`designos-fig`** | `crates/designos-fig` | Figma `.fig` (`fig-kiwi` binary container + DEFLATE/Zstd) and Figma REST JSON importer/exporter, VectorNetwork converter, and component instance expander. |
| **`designos-render`** | `crates/designos-render` | Deterministic CPU reference renderer (`tiny-skia 0.11`), `SceneIndex` world-bounds spatial & paint-order index, `TileCache`, boolean path ops (`i_overlay`), and `cosmic-text` shaping. |
| **`designos-gpu`** | `crates/designos-gpu` | `wgpu` GPU canvas renderer: `SceneCompiler` (`compile.rs`), zoom-bucket `GeomCache` (`geom.rs`, `lyon_tessellation`), `PaintCache` (`paintconv.rs`), and multi-pass `Renderer` (`exec.rs`, custom WGSL shaders). |
| **`designos-app`** | `crates/designos-app` (`designos-studio`, `designos`) | Desktop editor UI (`egui` + `eframe`, versions pinned in `crates/designos-app/Cargo.toml`), EaseUI BrainWave 2.0 theme (`theme.rs`), GPU/tile canvas viewport (`canvas/`), virtualized Layers panel (`panels/layers.rs`), Home library, and CLI (`cli.rs`). |
| **`designos-library`** | `crates/designos-library` | On-disk projects library (`store.rs`): `.designos`/`.designos`/`.fig` storage, 8 KB `file_kind` header probe, single-pass `import_file_with_doc`, thumbnails, version history, and published libraries. |
| **`designos-fonts`** | `crates/designos-fonts` | Bundled Inter font + on-demand Fontsource/Google Fonts HTTP fetcher, disk cache, script fallback, and zero-clone `ensure_font_requests_with`. |
| **`designos-svg`** | `crates/designos-svg` | SVG importer mapping `usvg` trees onto `designos_doc::Node` hierarchies with paint, text, and filter conversion. |
| **`designos-export`** | `crates/designos-export` | PNG/JPEG/SVG/PDF document exporter and Dev Mode codegen (`CSS`, `Tailwind`, `React`, `SwiftUI`, `Jetpack Compose`). |
| **`designos-mcp`** | `crates/designos-mcp` (`designos-mcp`, `designos-mcp`) | Headless stdio JSON-RPC MCP server (`rmcp`) exposing document inspection, creation, layout, export, and library operations to AI agents. |
| **`designos-cli` / `designos-diff`** | `crates/designos-diff` (`designos-diff` CLI) + `crates/designos-app/src/cli.rs` | CLI harness & pixel-diff test suite comparing CPU/GPU renders against reference screenshots (`crates/designos-diff`) plus desktop CLI flags (`--screenshot`, `--open-id`, `--gpu`, `--fixture`). |

### Declared `designos-mcp` Tools (`crates/designos-mcp`)

<!-- designos-mcp-tools:begin -->
<!-- Generated by `cargo run -p designos-mcp --bin gen-tool-docs`. Do not edit by hand. -->

| Tool (`37` total) | Mode | Risk | Required Args | Description |
| --- | --- | --- | --- | --- |
| `designos_get_document` | read-only | read | — | Summarize the current in-memory document: pages, counts, shared styles (fill, text, effect, grid) with their values, and undo state. |
| `designos_get_libraries` | read-only | read | — | List this file's local publishable assets, enabled libraries (with pinned and latest versions and update_available), and disabled libraries in the active library root. |
| `designos_get_metadata` | read-only | read | — | Return a sparse XML node hierarchy (IDs, types, names, page-space bounds, layout, component/variant props, text previews, and lint issue counts) for a page or subtree. |
| `designos_search_design_system` | read-only | read | `query` | Search components, component sets, shared styles, and variables across this file and its enabled libraries, returning ranked instance-ready references and NodeSpec examples. |
| `designos_list_nodes` | read-only | read | — | List node summaries in numeric ID order. Query matches names case-insensitively; parent_id filters direct children. Use next_offset to paginate. |
| `designos_get_node` | read-only | read | `node_id` | Read the full native node including geometry, paints, text, bindings, and child IDs. |
| `designos_get_variables` | read-only | read | — | Read variable definitions and collections. With node_id, also resolve that node's direct bindings using inherited collection modes. |
| `designos_generate_code` | read-only | read | `language`, `node_id` | Generate developer handoff code or SVG for a layer using resolved variable values. Returns source text; writes no files. |
| `designos_create_node` | mutating | additive | `kind`, `parent_id` | Create one layer in a container; defaults to x/y=0, width/height=100. Supports undo. Text is only valid for text layers. Changes are in memory until saved. Content added to, removed from or moved in an instance slot is kept when the main component changes. |
| `designos_update_node` | mutating | modify | `node_id` | Update specified fields of one non-page layer. Coordinates are parent-relative. Replacing text clears text runs and list ranges. Supports undo; changes remain in memory until saved. |
| `designos_delete_node` | mutating | destructive | `node_id` | Remove a non-page layer and its entire subtree. Supports undo; changes remain in memory until saved. Content added to, removed from or moved in an instance slot is kept when the main component changes. |
| `designos_list_comments` | read-only | read | — | List canvas comment threads enriched with resolved world coordinates, target node ID/name/kind, and top-level frame ID/name. Filter by resolved state, page_id, or node_id. |
| `designos_add_comment` | mutating | additive | `text` | Pin a new comment thread to a node (with local offset x/y) or page coordinates. Supports undo. |
| `designos_reply_comment` | mutating | modify | `text`, `thread_id` | Append a reply message to an existing canvas comment thread and optionally mark it resolved/unresolved. Supports undo. |
| `designos_resolve_comment` | mutating | modify | `thread_id` | Mark a canvas comment thread resolved (default true) or unresolved (false). Supports undo. |
| `designos_apply_spec` | mutating | additive | `spec` | Build a complete UI subtree from a declarative NodeSpec (supports EaseUI BrainWave 2.0 presets like easeui-screen/easeui-card/easeui-button-primary, $easeui.* tokens, and Auto-Layout 5.0 row/column/wrap/hug/fill) in a single atomic undoable step together with the variables it creates. Replies with bindings_created, styles_linked and warnings (for example DEPRECATED_EASEUI_TOKEN). Content added to, removed from or moved in an instance slot is kept when the main component changes.  Examples: 1) Token-bound frame with auto-layout: {"parent_id":1,"spec":{"kind":"frame","name":"Hero","layout":{"direction":"column","gap":16,"padding":[24,24,24,24]},"fill":"var:Color/Surface","children":[{"kind":"text","name":"Title","text":"Welcome"}]}} 2) Component instance with variant and overrides: {"parent_id":1,"spec":{"kind":"instance","name":"PrimaryCTA","component":"Button","variant":{"Size":"lg","State":"default"},"overrides":[{"path":"Label","text":"Continue"}]}} 3) Component instance with filled slots: {"parent_id":1,"spec":{"kind":"instance","name":"SettingsCard","component":"Card","slots":{"header":[{"kind":"instance","component":"CardHeader"}],"body":[{"kind":"instance","component":"FormRow"}],"footer":false}}} |
| `designos_export_spec` | read-only | read | `node_id` | Export a node and its descendants as a declarative NodeSpec JSON tree, enriched with Dev Mode status/annotations and any canvas comment threads pinned within the subtree. |
| `designos_set_dev_status` | mutating | modify | `node_id` | Set a layer's Dev Mode handoff status ('ready', 'completed', or 'clear') and/or append a developer annotation. Supports undo. |
| `designos_export_fig_file` | mutating | destructive | `output_path` | Export the current session document to a Figma binary .fig container (with fig-kiwi header and deflate chunks) inside the workspace. |
| `designos_query_xpath` | read-only | read | `selector` | Query layers in a page or subtree with a streaming single-pass XPath evaluator, node budget, cursor pagination, compact rows, and solver-backed linter predicates (has-problem, is-bound, has-hardcoded, @problems, @inProgress).  Examples: 1) Truncated text: {"selector":"//TEXT[has-problem('text-truncated')]"} 2) Hard-coded fills inside a frame: {"selector":"//FRAME[@name='Card']//*[has-hardcoded('fills/0')]"} 3) Layers an agent is working on: {"selector":"//*[@inProgress]"} |
| `designos_lint` | read-only | read | — | Run the unified solver-backed geometric, text, token, and structural design linter on a page, specific node subtrees, or the active page. |
| `designos_get_guidelines` | read-only | read | — | List available design guideline topics or return the guideline document for a topic ('design-system', surface guides, 'archetypes', 'archetype:<id>', or 'rules'). |
| `designos_import_web_capture` | mutating | additive | — | Import a .webcapture.json / .capture.json bundle (or inline capture JSON) into a page as an editable auto-layout frame tree in one undoable step, returning the ImportReport. |
| `designos_undo` | mutating | modify | — | Undo the latest successful edit in this session; returns changed=false if the stack is empty. |
| `designos_lint_fix` | mutating | modify | — | Apply deterministic fixes for auto-fixable lint findings on a page, specific node subtrees, or the active page in a single undo step. |
| `designos_redo` | mutating | modify | — | Redo the most recently undone edit in this session; returns changed=false if the stack is empty. |
| `designos_get_screenshot` | read-only | read | — | Render a node, a set of nodes on the same page, or a page-space rectangular region and return it inline as MCP image content (PNG or JPEG), automatically downsized to max_long_edge (default 1024). |
| `designos_set_in_progress` | mutating | modify | `node_ids`, `value` | Flag or clear root frames as in progress while an agent works on them (work state, not an undo step). |
| `designos_apply_batch` | mutating | destructive | `ops` | Execute 1..=200 write operations (apply_spec, insert/create, update, move, delete, set_slot, set_variables, set_style, set_in_progress) as a single atomic transaction and one undo group. Later ops may reference nodes created by earlier ops via '$prev', '@<as>', or '@<as>/<name path>'. With dry_run=true, computes and returns created IDs, name_to_id map, touched_roots, and touched_bounds without mutating the document or publishing a live patch. Pass idempotency_key to deduplicate retries (a key names one logical write; after a reconnect, call designos_get_node before trusting a replayed id). Note: in a live Studio session, if the editor remaps a colliding ID on replay, the reply holds the session IDs until the next canvas reload. Examples: (1) create + $prev: {"ops":[{"op":"create","parent":1,"kind":"frame","name":"Header"},{"op":"create","parent":"$prev","kind":"text","text":"Title"}]}; (2) apply_spec with as + @handle/Child update: {"ops":[{"op":"apply_spec","as":"hero","spec":{"kind":"frame","name":"Hero","children":[{"kind":"text","name":"Child","text":"Hi"}]}},{"op":"update","node":"@hero/Child","opacity":0.9}]}; (3) set_slot: {"ops":[{"op":"set_slot","node":"@card","slot":"body","fill":[{"kind":"text","text":"Body"}]}]}; (4) dry_run: {"dry_run":true,"ops":[{"op":"insert","kind":"rectangle","name":"Box"}]}. |
| `designos_get_design_context` | read-only | read | `node_id` | Return generated code in the target language, Dev Mode hints (variables with CSS/platform code syntax, linked styles, component instances/properties, annotations, dev status, Code Connect stub), and an inline screenshot (scale 0.5, max_long_edge 1024), with an XML outline fallback when the subtree exceeds 5,000 nodes or generated code exceeds 40,000 characters. |
| `designos_configure_session` | mutating | modify | — | Configure per-session MCP options for the automatic mutating-call lint issues block (issues.mode: preset \| errors_only \| off, issues.preset, issues.max_items: 1..=100) and auto_fix settings. Persists across reconnects in the session directory and resets on document switch. |
| `designos_get_app_state` | read-only | read | — | Return the pre-fetched design context block. Sections: freshness, document, depth-3 target frames outline, variables summary, components and libraries, active style, and rules pack, plus session state. Pass include_selection=true ONLY when the user explicitly asks about their canvas selection. |
| `designos_open_file` | mutating | additive | `path` | Import a .designos, .designos, .fig, SVG or Figma REST JSON file inside the workspace into this isolated session. Does not control an open editor. Unsaved changes are refused unless discard_changes=true, which needs the user's explicit confirmation to discard them. |
| `designos_save_file` | mutating | destructive | — | Save this session to a .designos file inside the workspace, or in place when path is omitted. Untitled documents require path. Existing files are not overwritten unless overwrite=true. Parent directory must exist. |
| `designos_close_document` | mutating | modify | — | Close this session document using unsaved save, discard, or fail (default fail). fail returns UNSAVED_CHANGES on a dirty document and never opens a dialog. Static risk tier destructive; treat as modify when unsaved is not discard. |
| `designos_render_png` | mutating | additive | `node_id`, `path` | Rasterize a frame or layer to a NEW .png file inside the workspace using DESIGN:OS Studio's deterministic renderer. Existing files are never overwritten. |
| `designos_new_document` | mutating | additive | `name` | Start a blank document with one page. Unsaved changes are refused unless discard_changes=true, which needs the user's explicit confirmation to discard them. |
<!-- designos-mcp-tools:end -->

---

## 3. Build, Test & Run Commands

Always prepend `$HOME/.cargo/bin` to `PATH` before invoking `cargo` or `rustc`:

```bash
export PATH="$HOME/.cargo/bin:$PATH"

# Build release binaries (desktop editor + MCP server)
cargo build --release -p designos-app -p designos-mcp
cargo build --release -p designos-app

# Sync release binaries into the macOS app bundle (always all three: Studio and designos-mcp
# share the live edit-log format, and an older designos-mcp still overwrites the open document)
cp target/release/designos-studio "target/release/bundle/DESIGN-OS Studio.app/Contents/MacOS/designos-studio"
cp target/release/designos "target/release/bundle/DESIGN-OS Studio.app/Contents/MacOS/designos"
cp target/release/designos-mcp "target/release/bundle/DESIGN-OS Studio.app/Contents/MacOS/designos-mcp"

# Launch the packaged macOS app bundle
open "target/release/bundle/DESIGN-OS Studio.app"

# Launch directly into the "VSF - PCP.fig" enterprise benchmark
# (1,058,895 Kiwi records -> 336,214 initial / 1,082,324 full scene nodes across 31 pages;
#  112,474 scene nodes / 723,136 Kiwi records on the 7-page subset)
"target/release/bundle/DESIGN-OS Studio.app/Contents/MacOS/designos-studio" "$HOME/Downloads/VSF - PCP.fig"

# Run verification suites
cargo test --workspace --lib
cargo test -p designos-gpu
cargo test -p designos-mcp
```

---

## Git Workflow (`main` is protected)

Since 2026-10-17 `main` is protected by a repository ruleset. Nobody bypasses it: not the owner, and not any agent runtime (Claude, Codex, Grok, Gemini).

- Never push to `main`. Work on a branch or worktree, push it, and open a pull request.
- To merge, a PR needs green `Test (macos-15)`, `Clippy` and `Rustfmt` on a branch that is up to date with `main`. Run `gh pr update-branch <n> --rebase`, then wait for CI. Ubuntu and Windows CI also run but do not block, because releases are verified on macOS.
- Merge with squash only (`gh pr merge <n> --squash`). The head branch is deleted on merge, and `main` keeps a linear history.
- Release tags `v*` cannot be moved or deleted once pushed. Tag the squash commit of the release PR (see `docs/releasing.md`).

## Marketing Videos (owner HARD RULE, 2026-10-17)

- Every piece of demo UI in a project video (launch and promo films, micro clips, GIFs, long-form captures) comes from the Untitled UI PRO VARIABLES v8.0 kit, `/Users/jangtrinh/Downloads/❖ Untitled UI Figma – PRO VARIABLES (v8.0) gKXp7LqFY2NV (Copy).fig`, opened in Studio. Never show a blank or self-built demo file, another kit, or a client file (e.g. VSF - PCP).
- Work from a copy (a `.fig` import saved as `.designos`) so takes never save over the original. Feature demos may edit that copy through Studio or `designos-mcp`. Extra demo frames go on a scratch page inside it.
- Kit content keeps the kit's original colours, purple included (owner: "Purple này là style của Untitled nên chấp nhận", i.e. this purple is Untitled's own style, so accept it). The Purple Ban still applies to the film's own chrome and type, not to kit content. Never recolour pixels. Photos of people in kit content are allowed (owner: "Show people cũng được, kệ đi").
- Demos must be true to the released build. Pick kit frames that the release renders the way Figma does: the CPU and `--gpu` renders match, and no known render bug is hit. Never show behaviour the code does not have. For example, agent undo is one step per tool call, and a paste from Figma drops styles.
- Credit "Untitled UI PRO v8.0 kit by Untitled UI" in the end card.

## 4. Non-Negotiable Performance & Safety Invariants

1. **NEVER call `doc.to_json()` inside `doc_hash` or on the UI thread (`crates/designos-app/src/home/session.rs`):**
   - `doc_hash(doc: &Document) -> u64` MUST use the zero-allocation `HashSerializer` streaming directly into `DefaultHasher` (and sampling image byte headers/tails). Allocating a JSON string on a 112,474-node (723,136 Kiwi records) document stalls the UI thread and spikes memory by gigabytes.
   - Viewport-only operations (pan, zoom, fit-to-screen, marquee selection) MUST NEVER call `app.changed()` or increment `TileView::rev`.

2. **NEVER call `ctx.load_texture` inside `ctx.data_mut` (`crates/designos-app/src/icons.rs` & all `egui` code):**
   - `egui::Context::data_mut` acquires a write lock on `egui`'s internal `parking_lot::RwLock<ContextImpl>`. Calling `ctx.load_texture(...)` while that closure is active attempts to re-lock `ContextImpl`, causing an immediate non-reentrant `RwLock` self-deadlock on Frame 1. Always release `ctx.data_mut` before calling `ctx.load_texture`, then insert the handle in a separate `ctx.data_mut` call.

3. **PRESERVE the `interactive()` guard in `crates/designos-gpu/src/compile.rs`:**
   - `fn interactive(&self) -> bool { self.cull && self.geom.budget.is_some() }` distinguishes live interactive viewport rendering (`budget = Some(8ms)`) from headless reference rendering (`budget = None`).
   - All interactive fast-paths (pre-resolve `< 0.75px` sub-pixel culling, `< 3.5px` text greeking, hardware scissor fallback on rounded clips, and offscreen `Program` caps) MUST stay gated behind `self.interactive()` so `cargo test -p designos-gpu` headless CPU-vs-GPU pixel-diff tests remain bit-exact.

4. **CAP offscreen GPU `Program` allocations (`self.progs.len() < 64`) and reuse `FrameKey` (`crates/designos-app/src/canvas/gpu.rs`):**
   - Unbounded offscreen render passes (`Program`s) on 112,474-node (723,136 Kiwi records) pages cost CPU and GPU time. Keep offscreen layer/mask/clip/blend `Program` creation capped at `< 64` in `interactive()` mode (falling back to hardware scissor clips).
   - Device limit (measured, #467): wgpu-hal's Metal backend allows 4,096 unsubmitted command buffers per queue. That is 2,047 render passes per submission, two command buffers per pass, and a blurred layer alone is 4 passes. Past it the device is lost and egui-wgpu panics with "Failed to create staging buffer". `Renderer::render_submitted` and `render_to_rgba` therefore split a frame over several submissions before it reaches `SUBMIT_HAL_BUDGET` (2,048 command buffers, half the limit; `crates/designos-gpu/src/submit.rs`, `docs/gpu-command-buffer-limit.md`). Never record a whole frame into one caller-owned encoder unless it is known to fit.
   - Exception (owner ruling 2026-10-16, ratified 2026-10-17 after a live Metal check): when the view is idle, `GpuView::plan` runs exactly ONE compile with `CompileOptions.uncap_programs = true`, caches it under the same `FrameKey` (`refined = true`), and from then on re-blits with `Cb { frame: None }`. Masks, blur, blend modes and group opacity render at full quality at rest. Any viewport or document change goes back to the capped interactive compile.
   - Idle = the `FrameKey` stayed unchanged for `IDLE_REFINE_DELAY` (250 ms of `ctx.input(|i| i.time)`, no timer thread), `stale_hits == 0`, no pointer down, and no scroll or zoom delta in the last frame (a delta restarts the delay, so a trackpad pan or momentum scroll never triggers the refine). `plan` returns `FramePlan::Wait`, which re-blits the cache and requests ONE `request_repaint_after` for the remaining delay; after the refine every idle frame is `Reuse` (`Cb { frame: None }`) with no repaint request (0% idle CPU).
   - VRAM ceiling (owner ruling 2026-10-16, replaces any program-count limit for the refine): the uncapped compile accounts the GPU bytes of every offscreen target (`program_bytes`: pooled extent x `FORMAT` 4 B x (`MSAA` 4 + 1 resolve), plus blur/displace/backdrop scratch targets; formula mirrors `resources.rs` `Pool`) and opens a new `Program` only while the running total stays within `CompileOptions.refine_vram_budget_bytes` (default 512 MiB, `DEFAULT_REFINE_VRAM_BUDGET_BYTES`). Past the budget it degrades exactly like the capped path (scissor clip instead of a mask program, effect or layer skipped). Capped interactive and headless (`budget = None`) compiles never consult the budget.
   - In `canvas::gpu::paint`, when `last_key == Some(key) && compiler.geom.stale_hits == 0`, pass `Cb { frame: None }` so `Canvas` re-blits the cached `Output` texture at `0.00%` idle CPU without recompiling or re-uploading vertex buffers.
   - `Autosave::autosave_tick` (`crates/designos-app/src/home/session.rs`) MUST watch `watched_disk_path()` off the UI thread (`DiskWatcher`) and call `ctx.request_repaint()` only when the on-disk `modified()` timestamp changes — never schedule a periodic UI-thread `request_repaint_after` when idle. `canvas::grids::paint` MUST take its frames from `SceneIndex::grid_nodes()` (drawn frames only, kept in paint order) rather than walking the page tree; it walks `page.children` only when no scene index is available.
   - **Measured Idle CPU & Method**: `0.00% / 0.00%` (`dCPU = 0.000s` across two 20 s idle windows / 40 s total on Apple M4 Max), measured via cumulative process CPU time (`ps -p <pid> -o time=`) on `target/release/designos "VSF - PCP (Copy).fig" --gpu --select billing` after initial load and the 250 ms `IDLE_REFINE_DELAY` settle.
   - `GeomCache::get` MUST track cumulative `frame_build_nanos` against the 8ms per-frame budget (`compiler.geom.budget`) and defer uncached `Text`/`Vector`/`BooleanOp` tessellation across subsequent frames via `stale_hits`.

---

## 5. DESIGN:OS & Self-Evolving Design Agents

All UI chrome, design tokens, component definitions, and critique loops in `design-os-studio` are governed by the **DESIGN:OS Self-Evolving Design System** (`design/`) in live parity with `crates/designos-app/src/theme.rs`.

### Mandatory Design Read Order (Before Any UI / Token / Canvas Change)
1. `design/principles.md` — Project design constitution, `## Always` / `## Never` laws (Purple Ban, `>=24px` pointer targets, 4px rhythm, Zero Fake UI Law).
2. `design/soul.md` — Ratified project soul (`status: ratified`) and visual/ergonomic identity.
3. `ui ds context --dir .` — Compiled design system context, token roles, and component registry.
4. `crates/designos-app/src/theme.rs` — Authoritative Rust `egui` visual constants (must remain in 100% hex parity with `design/design.tokens.json`).

### Specialized Project Design Agents (`.claude/agents/` & `.agents/agents/`)
| Agent | Role & Responsibility |
|---|---|
| **`designer-jang-designos-studio`** | Soul-bound Designer for `design:os studio`: UI generation, Rust `egui` layout/token refinement in `crates/designos-app/src/`, and component registration (`ui ds component add`). Never scores its own output; records discovered gaps via `ui memory record gap`. |
| **`curator-jang-designos-studio`** | Independent Curator & Critic (report-only; generator is NEVER grader): VSF-PCP Operate Rubric (`8×2 = 16pt`) scoring, `ui ds a11y` contrast verification, visual inspection via `designos --screenshot`, and verdict logging via `ui memory record taste_verdict`. |
| **`figma-jang-designos-studio`** | Canvas & `.fig` / `.pen` / MCP Bridge Specialist: drives `designos-mcp`, `designos --apply-spec`, `.fig` / `.pen` round-trip, and `design-os figma`. Verifies every canvas mutation via `--inspect-json` or `--screenshot`. |
| **`librarian-jang-designos-studio`** | Self-Evolution & Knowledge Graduation Agent: runs `scripts/design_os_self_evolve.py`, collects open `gap` and `insight` events (`design-os librarian collect`), verifies `theme.rs` ↔ `design/design.tokens.json` parity, executes `harvest` + `heartbeat` + `memory compile`, and graduates recurrent insights (`seen > 1`) into `design/principles.md` and `scripts/zero_fake_ui_gate.py`. |

### Mandatory Pre-Commit / Post-UI-Change Verification Commands
```bash
export PATH="/Users/jangtrinh/.local/bin:$HOME/.cargo/bin:$PATH"

# 1. Zero Fake UI Deterministic Gate (14/14 invariants in crates/designos-app/src/)
python3 scripts/zero_fake_ui_gate.py

# 2. Automated Self-Evolution Engine & Rust <-> design:os Live Token Parity Audit (7/7 gates)
python3 scripts/design_os_self_evolve.py

# 3. Verify Self-Evolution Loop Proof (verdict=ALIVE, proof.level=APPLIED|IMPROVING, 0 findings)
design-os evolution
```
