skills/gpui-rust-console/SKILL.md
Build and extend pd-console — Port Daddy's GPU-native macOS operator console (GPUI 0.2.x, Zed's Rust UI). Covers the render-agnostic Block/Pane(Surface) contract, the two-thread reqwest↔smol refresh pipeline, Taffy flexbox layout, uniform_list virtual scroll, focus + keyboard nav, the OKLCH theme and ICS maritime flag badges, GPUI's missing text-input, and the real feature-gated cargo/CI gate. Use when adding panes, visual polish, or debugging GPUI rendering/layout/focus in core/pd-console. NOT for the TypeScript daemon, generic Rust toolchain/borrow-checker help (use rust-with-claude-code), or non-pd GPUI apps with a different theme/architecture.
npx skillsauth add curiositech/windags-skills gpui-rust-consoleInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Authoritative skill for core/pd-console (crate pd-console v0.2.0, ADR-0046): a
GPU-native standalone macOS operator console built on GPUI 0.2.2, plus a headless ratatui
REPL that renders the same panes. The defining idea: a pane emits render-agnostic
Blocks; two renderers paint them. One pane, two faces — which is why every pane is
unit-tested on cheap Linux runners while the Metal window builds only on macOS.
✅ Use for:
core/pd-console/uniform_list/list), focus, keyboard navtheme.rs) and ICS maritime flag badges (maritime.rs)cx.notify() stormspd tube cockpit)cargo/CI gate right❌ NOT for:
agent.rs HTTP wiring beyond consuming itrust-with-claude-coderust-app-distributionflowchart TD
D["DaemonClient (reqwest)"] -->|"GET /route per pane"| PR
subgraph PR["Producer: std::thread + current-thread tokio (2s loop)"]
R["pane.refresh(&client).await"] --> V["pane.view() → Vec<Block>"]
end
PR -->|"std::sync::mpsc: Vec<(nav_idx, Vec<Block>)>"| CO
subgraph CO["Consumer: GPUI foreground (smol, main thread, 500ms)"]
U["window.update → view.update_panes"] --> N["cx.notify()"]
end
N --> PAINT["ConsoleView::render: Block → GPUI element (Tone resolved to OKLCH)"]
CO -.->|"control_tx: ControlMsg::InterruptLane"| PR
reqwest needs tokio; GPUI runs smol; they cannot share an executor. The producer owns
the daemon + all 17 panes and mpscs Blocks to the consumer; control flows back on a
second channel. Use channels, never Arc<Mutex<State>> (it blocks the renderer — the #1
GPUI perf bug). Full detail: references/console-architecture.md.
| Branch | Do | Load |
|--------|----|------|
| add-pane | New surface end to end | examples/add-a-pane.md, templates/new_pane.rs.tmpl, templates/pane_tests.rs.tmpl |
| layout | Taffy flexbox, three-panel skeleton | references/render-and-layout.md |
| scroll | uniform_list vs list, bounded parents | references/render-and-layout.md |
| theme | Add/preview an OKLCH tone | references/maritime-flags.md, examples/preview-theme.md, scripts/oklch_to_srgb.py |
| maritime | ICS flag mapping / badge | references/maritime-flags.md, scripts/flag_resolve.py |
| text-input | Cockpit chat / command entry | references/text-input.md |
| verify | Run the real CI gate locally | references/build-and-ci.md, scripts/verify_console.py |
Blocks with a Tone (meaning), never a color. The
renderer resolves Tone → theme OKLCH → rgb(u32) in one place (pane.rs::Tone::color).trait Pane is object-safe (Box<dyn Pane> registry). refresh is a hand-rolled
boxed future (no #[async_trait]); mutate/subscription/on_stream have defaults so
read-only panes need zero changes. SurfaceAction is an enum, never a generic.view() is sync and IO-free. All fetching is in refresh() on the producer thread.
A failed fetch is recorded in last_error and rendered as an error state — never
propagated (one bad route must not blank the console).cx.notify() only on real state change. Never mutate self inside render.Arc<Mutex<T>> across the two threadsNovice: "Wrap the pane state in Arc<Mutex> so both threads can touch it."
Expert: The producer owns the panes; it mpscs Vec<Block> snapshots to the
consumer. A mutex under refresh contention stalls the GPUI render loop — visible jank.
Detection: any Mutex / RwLock reachable from a pane; reqwest called off the
producer thread.
Novice: div().bg(rgb(0xe3b56d)) inside a pane's view.
Expert: Emit Block::Chip { tone: Tone::Accent }. The amber lives once in
theme.rs (DARK.accent, which oklch_to_srgb.py confirms is e3b56d). Color resolves
at paint time so retheme/light-mode is free; an inline hex also trips the brand-color guard.
Detection: rgb(0x…) literals in *_pane.rs.
RUST_MIN_STACK for the "gpui stack overflow"Novice: "GPUI macros overflow the stack — set RUST_MIN_STACK=16777216."
Expert: The error is a compile-time recursion limit, fixed by
#![recursion_limit = "512"] at the top of main.rs. RUST_MIN_STACK resizes a runtime
thread stack and does nothing for a compile error. CI sets neither — it runs plain
cargo check + cargo test with gpui feature-gated off on Linux.
Timeline: this corrected an earlier draft of this very skill; see references/build-and-ci.md.
Novice: "Use GPUI's text field for the cockpit."
Expert: GPUI 0.2.x ships no text-input widget. Use the full-screen entry overlay
pattern (state on the view, not the busy stream pane) or build an Element (~300 LOC).
Detection: searching the API for text_input(); cursor state bolted onto the Lane pane.
□ Pane emits Block+Tone only — zero rgb(0x…) in the pane
□ view() is sync and IO-free; refresh() records errors, never propagates
□ trait stays object-safe (boxed future; enum actions; no generics on dyn methods)
□ New pane proves 3 states (empty/error/populated) — templates/pane_tests.rs.tmpl
□ NAV index (app.rs) == producer slot index (main.rs)
□ Theme change is OKLCH in theme.rs, never inline hex; previewed via oklch_to_srgb.py
□ Maritime change keeps flag_resolve.py --selftest green (HITL→Foxtrot, mayday→Juliett)
□ python3 scripts/verify_console.py run --crate core/pd-console → cargo check + test ok
□ python3 scripts/validate_skill.py → 0 errors
□ No retired cinnabar hex / no daemon-URL literal outside agent.rs
| File | Consult When |
|------|--------------|
| references/console-architecture.md | Two-thread pipeline, Block/Pane(Surface) contract, registry, FsAssets, window bootstrap |
| references/render-and-layout.md | Render vs RenderOnce, Taffy flexbox, uniform_list/list, focus, keyboard, notify discipline, perf anti-patterns |
| references/maritime-flags.md | ICS flag mapping + badge colors, the OKLCH theme and to_srgb8(), Tone→color resolution |
| references/build-and-ci.md | The feature-gate, the real cargo/CI jobs, recursion_limit vs RUST_MIN_STACK, brand/URL guards |
| references/text-input.md | GPUI has no input widget — defer / overlay / build-an-Element decision tree |
| Script | Purpose |
|--------|---------|
| scripts/_envelope.py | Shared stdin/stdout script-io envelope (imported by the others) |
| scripts/oklch_to_srgb.py | Faithful port of theme.rs::to_srgb8; preview an OKLCH token's hex (oklch.to_srgb) |
| scripts/flag_resolve.py | Faithful port of maritime.rs; resolve agent-state → ICS flag + meanings (flag.resolve) |
| scripts/verify_console.py | Run the real CI gate locally: cargo check+test (+--features gpui on macOS) |
| scripts/validate_skill.py | Skill self-check: frontmatter, refs, schema, no phantom citations, script selftests |
| File | Used By |
|------|---------|
| schemas/script-io.schema.json | The Request/Response envelope every script wraps stdin/stdout against |
| Template | Output |
|----------|--------|
| templates/new_pane.rs.tmpl | A read-only Pane skeleton (error/empty/populated states, boxed-future refresh) |
| templates/pane_tests.rs.tmpl | The three-state pane test suite (sync, no tokio, Linux-CI-safe) |
| Example | Walks Through |
|---------|---------------|
| examples/add-a-pane.md | Adding a "Voyages" pane end to end with real main.rs/app.rs cites |
| examples/preview-theme.md | Designing a new OKLCH status tone and seeing its hex before touching Rust |
Every file in this skill, and when to open it. Auto-generated; run scripts/index_references.py --fix.
root
.gitignoreexamples/
examples/add-a-pane.md — Example: Add a new pane to pd-console end to end — Goal: add a "Voyages" pane that lists active voyages from GET /voyages, slotted after Lane, fully unit-tested, no gpui needed for the testexamples/preview-theme.md — Example: Preview a new OKLCH status tone without compiling Rust — Goal: you want to add a Stalled status tone (a desaturated amber, distinct from the warning amber) and see its hex before wiring it into `references/
references/build-and-ci.md — Building pd-console & the Real CI Gate — > Source of truth: core/pd-console/Cargo.toml and the rust-console / > rust-console-gpui jobs in .github/workflows/ci.yml.references/console-architecture.md — pd-console Architecture — The Unified Model — > Source of truth: core/pd-console/src/ (crate pd-console v0.2.0, ADR-0046).references/maritime-flags.md — ICS Maritime Flags & the OKLCH Theme — > Source: core/pd-console/src/maritime.rs and core/pd-console/src/theme.rs.references/render-and-layout.md — GPUI 0.2.x Rendering & Layout — the idioms that compile — > GPUI 0.2.2 (Cargo.toml: gpui = { version = "0.2.2", optional = true }).references/text-input.md — Text Input in GPUI 0.2.x — There Is No Widget — > The single most surprising gap for anyone coming from web/Qt/SwiftUI: GPUI 0.2.x ships > no text-input widget. Zed builds its own.schemas/
schemas/script-io.schema.json — script io.schema (data/schema)scripts/
scripts/_envelope.py — Shared script-io envelope helpers for the gpui-rust-console skill.scripts/flag_resolve.py — Resolve a canonical agent-state string to its ICS maritime flag, meanings, andscripts/oklch_to_srgb.py — Convert OKLCH theme tokens to packed 0xRRGGBB sRGB — a faithful Python port ofscripts/validate_skill.py — Self-check the gpui-rust-console skill: frontmatter, required references,scripts/verify_console.py — Run the real pd-console CI gate locally and report it as a script-io envelope.templates/
templates/new_pane.rs.tmpltemplates/pane_tests.rs.tmpldata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.