Design systemDraft
Spec lists by team
This page sorts the Gaps sections of the component specs by team, so each team has one list to work from. The specs stay the source of truth: when you close an item, tick it in the spec Gaps list in the same commit, and this page follows.
Lumen redesign is Lumen, redesigned. Its Gaps list comes first for capsule-pro, ahead of the items below.
How items are assigned:
- Deck items go to pwa (the deck shell,
deck/css,deck/js,deck/views, Glass), unless the file is chat's (deck/chat/) or native-core's (the Settings view). An item that touches files of two teams is listed under both and marked shared. - App items go to mobile. Lumen items go to capsule-pro.
- System items fix a board,
icons.txtor a token, and go to app-design. - One core item has no team yet (see the end).
pwa
Owns the Deck shell: the rail, top bar, phone shell, sheets, Now, Find, Planner, Vault, Devices,
Glass, the shared CSS in deck/css/deck.css and the icons.
Start here
- Tokens, not hand typed values: drop the old
--r-2,--signaland the--scrimand--floatredefined insheet.css; readdeck/css/tokens.css(button, sheet, icons, rail). - One button system:
.btnand.sbfold into one button with the five variants, hold and busy (button). - Sentence case:
.btnis mono caps with letter spacing; use Instrument Sans 13/18 weight 600 (button). - 44 touch targets: the phone shell switches at 720, not 760, and
.ibtnneeds the 44 size (phone shell, icon button). - AA and disabled: no
opacityfor disabled, a quiet fill and--labelink instead (button, sheet). - Status marks: the running ring, crossed circle and hollow done dot in
deck.css, and the 18 badge (status mark). - Violet only for needs you: remove the beacon wash and rules in Glass, the top bar, desktop Now, held cards and failed marks (glass frame, top bar, needs row, card).
All open items
Account picker row, deck/js/need-sheet.js (account picker row)
- The phone draft sheet has no From row; the picker opens as a sheet with 56 rows.
Agenda, deck/views/planner.js, deck/css/views/planner.css (agenda)
- No hour grid, event blocks or now line; the agenda is rows.
- The firing banner labels the ring " · ring 2" in beacon, not "Ringing again · 2 of 4" in
--text-2. - Snooze has no duration in its label, and there is no Dismiss.
- No "Live" status; no missed banner copy "Missed while the box was down".
Ask card, deck/js/need-sheet.js (ask card)
- Phone sheet primary reads "Approve" and key hints read ⏎/esc; spec: "Allow once", A and D.
Avatar, deck/css/deck.css, deck/css/sheet.css (avatar)
-
.initialis 24 with a--rule-strongborder, mono 11 weight 500; use the sans initial, no border, meta 12/16 weight 600. -
.avatar(the person) is 28, mono 11 with letter spacing, bordered; use 32 or 34 circle, sans, no border. -
.nsh-tileis 22 with radius 6; use 24 with radius 6.
Banner, deck/css/views/planner.css (banner)
- No shared banner:
.pl-bannerand chat's.term-noteeach draw their own, and the planner's banner uses a beacon border; use--hoverfill and no border. Shared with chat.
Button, deck/css/deck.css, deck/css/sheet.css (button)
-
.btnis JetBrains Mono 12, weight 500, upper case with letter spacing; use Instrument Sans 13/18 weight 600, sentence case. - No secondary, outline (plain
.btnis it but unnamed), hold or busy; disabled usesopacity: 0.45and primary disabled keeps the bone fill. -
.btn-ghostink is--text-2; use--text. Radius uses the old--r-2. - A second system,
.sb(min 46) and.sb-primary(54, 17/22) with opacity disabled; fold into the one button at 44 and 54.
Card, deck/css/views/now.css, deck/css/views/planner.css (card)
- No shared card class:
.np-card,.pl-card, chat's.gate-cardand.cv-tooleach define their own radius and padding; make one.cardwith--radius-card. Shared with chat. - Held cards on main draw body rules in
--beacon-rule: violet as a border, remove.
Chip, deck/css/deck.css (chip)
-
.chipis 26 tall with radius--r-1and ink--text, no on state; use 28, round,--text-2, and.chip-on. -
.tagis mono 11 with a--rule-strongborder; use sans 12 on a--hoverfill.
Command bar, deck/views/find.js, deck/css/views/find.css, deck/js/capsule.js, deck/js/commands.js (command bar)
- Desktop has no ⌘K palette: ⌘K focuses a 420 px field in the top bar with a dropdown, and Find is a separate full page ("Find or ask").
- Desktop placeholder reads "Find or ask", not "Jump to anything, or ask juno".
- Phone switch is at 760 px (
PHONE_QUERY), not 720. - Vault codes never show in Find; the Layout board places codes in Find on the phone.
Command result card, deck/views/find.js (command result card)
- ⌘K Run results open a terminal line; draw the card under the Run row.
Credential sheet, deck/js/sheet.js (credential sheet)
- Nothing built: the sheet on any
needs_credentialanswer, fromvault.needandvault.connect; the centred card and the phone sheet.
Device row, deck/views/vault-places.js, deck/css/views/vault.css, deck/js/health.js (device row)
- Devices lists only vault autofill browsers (
vault.devices), not the relay devices fromrelay.devices.list; no path, holds or trust columns. - Remove is a confirm button "Revoke" / "Revoke now", not a hold; no Rename.
- The relayed dot in
deck/js/health.jsis amber; use--label.
Draft card, deck/js/need-sheet.js, deck/js/editable.js (draft card)
- No "Send at 9:00 tomorrow"; no D key on Discard. Shared with chat.
- Presence line wording "Face ID covers sends until 14:32, confirmed 6 min ago" (only on the phone sheet).
Form controls, deck/css/deck.css (form controls)
-
.inputis 40 tall on--panelwith a 15 size; use 32 on--bgat 13 (40 is the large field). Focus has no ring. - The toggle
.swis 34 x 20 with an on track of--text; use 32 x 18 and--primary-bg. -
.seghas a--ruleborder and 26 segments witharia-pressed; use the--hovercontainer, 28 segments and radiogroup semantics. - No stepper or select component (native
select.inputin settings). -
.searchis 420 wide on--panelwith a--ruleborder; use the field.
Glass frame, deck/glass/ (glass frame)
- The idle chip uses
--beacon-inktext, border and--beacon-wash; spec is neutral. - Read-only and hold banners use
--beacon-wash(.gl-banner,.gl-notice-hold). - Copy says "Someone has control from <surface>", not "Your phone has control".
- Error rows and messages tint violet (
.gl-msg-err,.gl-tr-err,.gl-danger). - Accent reads the raw
--signalvariable instead of--focus.
Glass mini-view, Now (Glass mini-view)
- Nothing built: the card on Now under the acting agent's row. Shared with chat (the thread header, the Step link).
Icon button, deck/css/deck.css (icon button)
-
.ibtnradius uses the old--r-2; switch to--radius-button. No 44 size, no filled round variant, no busy.
Icons, deck/js/icons.js (icons)
- Drawings differ from icons.txt for now, projects, memory, agents, vault, settings, search, plus, mic, send (points right, not up), file, copy, bell, clock, key.
- Missing planner, devices, x (has close), failed, chev-d (has chevron), more, stop, faceid, eye, hand, qr, box, wifi-off, refresh, drive, link, download, share, globe, shield, pause, play, cable, alarm, todo, unlock.
- Extra names not in the set (lock, watch, pin, mute, edit, lines, mail, branch, login, pass, ask): add to icons.txt through a design review or retire.
-
mark()fills the dot with--signal, which the tokens no longer define; use--mark-dotand--beacon-dot.
Key hint, deck/css/deck.css (key hint)
-
.kbdis mono 11 with padding 0 6 and radius--r-1; use 12/16, padding 0 5, min width 20,--radius-chip. - Ask hints read "⏎" and "esc" where the design keys are A and D; lower-case "esc" should be "Esc".
List row, deck/css/views/find.css, deck/css/views/planner.css (list row)
- No generic row: chat's
.thread-rowand.fb-row,.pl-rowand.fd-roweach set their own height, padding and type; make one.liwith the variants. Shared with chat. - Failed marks are violet in places; use the crossed circle in
--text-2.
List, deck/css/deck.css (list)
-
.rowsand.section-headhave no sticky group headers, no filter chips with counts, no key footer; long lists page with "Show earlier" only. - No skeleton rows (lists show "Loading devices." text); no slow-box line at 10 s.
Needs row, phone, deck/js/need-rows.js, deck/js/now-phone.js, deck/css/views/now.css (needs row)
- Kind line omits the kind (
thirdLineis "agent · project"); no "asked by", no decided state; kinds limited to ask, draft, question, pair. - An approve goes at once with no Undo; a draft's right swipe opens the sheet instead of committing Send with the Face ID glyph.
- Swipe is pointer events in JavaScript, not a scroll-snap strip.
- Title 16/21, detail 15/20, age 13; the tile has a
--rule-strongborder.
Needs row, desktop, deck/views/now.js, deck/css/views/now.css (needs row)
- A card per item with inline actions, not list and detail; no J, K, A, D keys.
- Time and error text in
--beacon-ink, rules in--beacon-rule, gold--recall-ink: remove.
Authenticator code, deck/views/vault-item.js, deck/css/views/vault.css (otp)
- No next code; the code sits in a field row labelled "Code", not "Authenticator".
- Ring is 20 px, and seconds render "18s" without the space.
- Codes come from the box each period, not computed on the device, so offline shows nothing.
- Copy is an icon button with the tooltip "This vyred has no vault.copy yet" when missing.
- No phone Codes list with tap-to-copy rows.
Needs row, waiting, deck/js/needs.js (needs row)
-
deck/js/needs.jsmerges its own sources; draw fromwaiting.list, count fromwaiting.countandwaiting.changed, answer throughanswer.tool; add the Pairing row. - Push resolves by row
id, so one answer clears every device.
Phone shell, deck/js/app.js, deck/css/deck.css, deck/js/capsule.js, deck/js/sheet.js (phone shell)
- Switches at 760 px, not 720.
- The avatar opens a Settings sheet, not the Places sheet; there is no pin-a-fourth-page.
- Header gap is 12 and padding 0 12 0 16 (spec 14 and 0 16).
- Push and pop run 300 and 240 ms on their own curves, not
--motion-paneland--ease. - Lumen hides on every pushed screen, including Planner.
Pill, deck/js/reconnect.js, deck/js/pwa.js, deck/css/deck.css (pill)
- The reconnect state draws as
.reach, a full-width line above the view with a bottom rule; make it the floating pill. - 60 s words read "Reconnecting since 14:32"; use "No answer from the box since 14:02 · Retry now".
Presence line, deck/js/need-sheet.js, deck/js/person.js (presence line)
- Wording "Face ID covers sends until 14:32, confirmed 6 min ago"; spec: "Face ID confirmed 12 min ago · covers sends until 15:02".
- No "Last Face ID 34 min ago" line in the lapsed state.
- The sign-in sheet's primary reads "Sign in with your passkey"; spec: "Sign in with Face ID" plus the code path button and the unlock list.
Rail, deck/js/app.js, deck/css/deck.css (rail)
- The rail is 216 px with text rows 34 tall at 14 px, not the 72 px icon rail.
- Order is Now, Projects, Memory, Agents, Chat, Vault, Settings; Planner and Devices are missing.
- The count is violet mono 11 px text, not the 18 px badge; no 99+ cap.
- Brand sits in the top bar (
.brand, 216 wide) instead of the mark at the top of the rail. - Uses raw
--r-2radius and the old--beacon-washelsewhere in the shell. - No ⌘1 to ⌘9 place keys.
Sheet, deck/js/sheet.js, deck/css/sheet.css, deck/js/need-sheet.js (sheet)
- Motion is 340 in and 240 out on a custom curve; use
--motion-sheetand--motion-panelwith--ease. - The page behind scales to 0.94 over black; remove, the scrim is enough.
-
--scrimand--floatare redefined in sheet.css; read the tokens. - Desktop is always the 560 centred card; no right sheet. Buttons are
.sbat 46 and 54 with opacity for disabled; use the button spec. - Title is 26/32; use 22/28.
States, deck/css/deck.css, deck/js/empty-actions.js, deck/js/now-phone.js (states)
- No skeleton outside the phone Now card; loading is text.
- Offline is a full-width line (
.reach), not the pill; no 2 s and 60 s stages. - Long lists use "Show earlier", not "Load 40 more" with a count; no sticky group headers.
- Failed marks are violet in places (status marks); spec is the neutral crossed circle.
Status mark, deck/css/deck.css (status mark)
- No running ring, crossed circle or hollow done dot in
deck.css;.dot.recalland relayed health dots use gold. - Rail count is violet text, not a badge;
.needs-pillis a violet wash. - The phone tab bar badge is 16 tall, 10 px text; use 18 and meta size.
Stepper and checks, deck/views/pair.js, deck/js/pair-steps.js, deck/css/pair.css, deck/js/phone-setup.js, deck/onboard/onboard.js (stepper and checks)
- No laptop "Add your phone" sheet in Devices; no relay QR step and no optional Tailscale step.
-
phone-setup.jsis a three-step card on Now (install, notifications, passkey), not the five live checks.
Tabs, deck/css/deck.css, deck/glass/index.js, deck/css/views/memory.css (tabs)
- No Session, Terminal, Files tabs or teammate tabs;
.segis used witharia-pressedorrole="group"in places androle="tablist"only in Glass. -
.segdraws a--ruleborder, 26 tabs and--hoverfor the selected fill; use the--hovercontainer and the--panelselected tab. -
.mem-tabin Memory is a third tab style (34 tall, text only); fold into these two.
Tip, deck/js/tips.js (tip)
- Nothing built: the footer chip, the empty state line, the What's new card on
tips.updated. Shared with chat (the composer hint line).
Toast, deck/js/now-phone.js, deck/css/views/now.css, deck/css/views/vault.css (toast)
- Two toasts:
.np-toaston Now and.vt-toastin the vault; make one shared toast. - No in-place variant; an approve's toast has no Undo (the answer goes at once).
- Toast shadow is
--light-top; use--float. Words 15/20; use the type steps.
Top bar, deck/js/app.js, deck/css/deck.css (top bar)
- Bar is 48 tall, flex not the three-column grid; brand and the box address sit on its left.
- No page title in the bar; views draw their own headings.
- The search field is 420 wide inside the bar, not the centred command bar trigger.
- The needs pill uses
--beacon-washfill and a pill shape; spec is text in--beacon-inkon no fill. - No right-slot page primary with N.
Done when
npm run tokens -- --checkpasses and no Deck CSS file defines a value the tokens hold.npm run design:auditpasses for every board you changed, and you looked at each PNG.- The Deck tests pass (
node --test "deck/**/*.test.js"). - Every Deck item above is ticked in its spec, so no spec lists an open Deck gap on a pwa file.
chat
Owns the chat view in the Deck: turns, tool rows, diffs, ask, question, plan and draft cards, the terminal, the thread list, and the composer and mode chips (the composer is shared with native-core).
Start here
- Tokens and one button system: Stop is ghost, make it secondary; the ask card's "Always in" is ghost, make it outline (composer, ask card).
- Sentence case and the type steps: the mono 11 caps step labels, turn footers, steer markers and tool meta become meta 12 sans (question card, turn, tool row).
- Status marks: failed uses the crossed circle, not beacon ink; running is a spinner and a
--text-2verb (tool row, status mark). - Violet only for needs you: remove the beacon wash on the ask card,
.diff-deland the terminal error dots (ask card, diff, terminal). - The words: "Edits allowed" and "Plan first", not "Accepts edits" and "Plan mode" (mode chip).
- The plan card, which is not built at all (plan card).
All open items
Account picker row, deck/chat/gate-item.js (account picker row)
- The draft card has no From row and no account picker; add it, from
vault.connections.list {capability: "send_mail", surface: "chat"}.
Ask card, deck/chat/ask-item.js (ask card)
-
.cv-ask.ask-cardfills--beacon-wash(a violet wash); spec:--panelwith--rule. - Title weight 500; spec: 600.
- "Always in" is a ghost button; spec: outline.
- No per-button busy verb; all three disable with no spinner.
Avatar, deck/chat/chat.css (avatar)
-
.av-agentis 28 with--r-2and a border;.av-personis 28. Neither size is in the scale.
Banner, deck/chat/term.css (banner)
- No shared banner:
.term-notedraws its own; use the shared banner. Shared with pwa.
Card, deck/chat/chat.css (card)
- No shared card class:
.gate-cardand.cv-tooldefine their own radius and padding; use the one.cardwith--radius-card. Shared with pwa.
Command result card, deck/chat/core/tool-detail.js (command result card)
- Slash commands print terminal text in a tool row; ask with
render: trueand draw the card.
Composer, deck/chat/composer.js, deck/chat/core/composer-state.js, deck/chat/tray.js, deck/chat/pickers.js (composer)
- No Attach, vault chip or Dictate; placeholder "Steer kit, or Alt+Enter to queue for after".
- Mode labels "Accepts edits" and "Plan mode"; ⇧Tab walks three modes and never Doesn't ask.
- Rewind offers three restores; spec: one list, the "Also undo file changes" checkbox, the hold.
- Todos pinned above the composer on the desktop; spec: side panel.
- Stop is ghost; spec: secondary.
Credential sheet (credential sheet)
- A tool row or send that answers
needs_credentialopens the sheet instead of printing the error.
Diff, deck/chat/core/line-diff.js, deck/chat/lib/diff.js, deck/chat/chat.css (diff)
- Removed lines strike through (
text-decoration: line-through); spec: no strike. - Removed text is
--label; spec:--text-2. - Added sign in the signal colour; spec:
--labelsign, colour only in the fill. - Mono 12.5/21; spec: 12/20.
- Legacy
.diff-delindeck/chat/chat.cssuses the beacon colour and wash; remove it.
Draft card, deck/chat/gate-item.js (draft card)
- No "Send at 9:00 tomorrow"; no D key on Discard. Shared with pwa.
- Badge "Held for you" beside the title; spec: header dot and "Draft to send" label.
Form controls, deck/chat/chat.css (form controls)
- Two checkboxes:
.cv-chkon is bone, the design is--text.
Glass mini-view (Glass mini-view)
- Nothing built: the thread header frame and the tool row's Step link. Shared with pwa.
List row, deck/chat/chat.css (list row)
- No generic row:
.thread-rowand.fb-rowset their own height, padding and type; use the one.li. Shared with pwa.
Mode chip, deck/chat/composer.js, deck/chat/core/composer-state.js (mode chip)
- Labels are "Accepts edits" and "Plan mode"; use "Edits allowed" and "Plan first". The order is default, acceptEdits, plan; use plan, default, acceptEdits, bypass.
- ⇧Tab never reaches Doesn't ask, and the box's
threads.moderefuses bypass (ADR 0030); the 27 Sep decision needs a person-only path in core (proposed) and an ADR update. - No inverse chip, no mode icons, no menu (the chip only cycles); the model chip shows the model only, not provider and auth.
Pill, deck/chat/chat.css (pill)
-
.cv-queued-rowis a dashed bordered box for a queued chat message; the offline queued line is the plain clock and words above.
Plan card, deck/chat/core/grouping.js, deck/chat/core/tool-detail.js (plan card)
- Not built. The plan text shows as an unfolded tool row with no approve controls; the ask of kind plan needs this card, the Needs row "kit has a plan to approve", and the mode choice.
Popover, deck/chat/pickers.js (popover)
-
.composer-menuand.cv-menurows use their own sizes; align to 28 and 30 dense rows. - No mode popover with descriptions yet; mode labels read "Accepts edits" and "Plan mode".
Presence line, deck/chat/gate-item.js (presence line)
- The gate card has no presence line.
Question card, deck/chat/question.js, deck/chat/lib/answers.js (question card)
-
.cv-q-chipstep label is mono 11 px caps tracked; spec: meta sans, sentence case. - Question 15/23 at weight 500, labels 14/20 at 500; spec: read 600 and base 600.
- Card border
--rule-strong, radius 10; spec:--rule, radius 12. - Choice numbers mono 11 on the left; spec: kbd chip on the right.
Status mark, deck/chat/chat.css (status mark)
- Failed state words use
--beacon-inkinchat.css(.cv-state-*).
Suggestions, deck/chat/pickers.js, deck/chat/composer.js (suggestions)
- The / and @ pickers read their own command and file lists; call
suggest.queryper keystroke and draw its lanes and groups. Shared with native-core. - No agents, projects, threads or people after @; no Words lane; no ghost text.
- No
suggest.pickedon accept.
Terminal, deck/chat/term.js, deck/chat/term.css, deck/chat/lib/term-link.js (terminal)
- Key bar is one row of nine (Esc Tab Ctrl Alt ← ↑ ↓ → Paste); spec: two rows of seven with / | ~ - and Enter.
- Watch line reads "Watching at 120x40 · Take size"; spec: names the owner device.
- Connecting dot uses the gold
--recall; error and blocked dots use--beacon-dot; spec:--labeland the failed mark. - xterm font 13 with line height 1.2; spec: mono 12/18.
Tip (tip)
- Nothing built: the composer's hint line. Shared with pwa.
Tool row, deck/chat/core/grouping.js, deck/chat/core/tool-detail.js, deck/chat/chat.css (tool row)
-
.cv-toolis a bordered card (38 high,--panel, radius 8); spec: a borderless 28 line. - Failed uses
--beacon-inkand a beacon border; spec: crossed circle in--text. - Running state word is lowercase in the signal colour; spec: spinner plus
--text-2verb. - Meta is 11 px; spec: 12.
Turn, deck/chat/session.js, deck/chat/core/session-state.js, deck/chat/chat.css (turn)
- Steer marker reads "Steered at step N" in mono 11 px in the signal colour; spec: "you steered here · after 3 steps · 14:32", meta sans,
--label. - Turn footer
.cv-turnis mono 11 px; spec: meta 12 sans. -
.cv-textis 15/24; spec: 15/22. - Thinking body border 2 px
--rule-strong; spec: 1 px--rule.
Done when
npm run design:auditpasses for the Session boards you changed, and you looked at each PNG.- The chat tests pass (
node --test "deck/chat/**/*.test.js"). - Every item above is ticked in its spec, so no spec lists an open Deck gap on a
deck/chat/file.
native-core
Owns the Settings view in the Deck (deck/views/settings-keys.js and its CSS), the hub, and the
composer with chat.
Start here
- Chips only when they mean something: no source chip for a default value, and "Claude Code file" becomes the source itself (settings row, chip).
- Status marks and plain words: refusals say "Not saved" with the failed mark, not the raw error (settings row).
- The restart banner and the resting apply hints "Next session" and "After restart" (banner, settings row).
- A named Reset with Undo (settings row).
- The key id only in search results, not in mono under every label (settings row).
All open items
Account picker row (account picker row)
- Settings, Vault, Connections: draw these rows with a More menu (label, default, revoke).
Banner (banner)
- No restart banner on the Settings page.
Chip, deck/views/settings-keys.js (chip)
- Source labels include "Default" and "Not set"; show no chip for a default value, and add the "Claude Code file" chip.
Credential sheet (credential sheet)
- Onboarding's Claude step and Settings, Vault, Connections open this sheet.
Settings row, deck/views/settings-keys.js, deck/css/views/settings-keys.css (settings row)
- The source chip is always shown, including "Default" and "Not set"; show it only when the value is not the default, and drop "Not set".
- "Claude Code file" is a second chip on every Claude-owned key, beside the source; it should be the source itself, with the row read-only and Open file.
- Reset is a bare "Reset" that does not name its target; no Undo after a reset.
- Apply hint is printed after a save as "Applied", "From the next session", "Restart vyred to apply"; use the resting hints "Next session" and "After restart" and print nothing for live keys.
- No "Saved", no reserved slot (meta and note lines grow the row), no restart banner.
- The key id is printed under every label in mono; show it only in search results.
- Refusals print the raw error; use "Not saved" with the failed mark.
Suggestions, the composer (suggestions)
- The chat-core composer calls
suggest.queryper keystroke, with ghost text andsuggest.picked. Shared with chat.
Done when
npm run design:auditpasses for the Settings boards you changed, and you looked at each PNG.- The Deck tests for the Settings view pass (
node --test "deck/**/*.test.js"). - Every item above is ticked in its spec.
mobile
Owns the app (apps/app, Expo) on the phone and tablet.
Start here
- Load Instrument Sans: labels render in the system font today (phone shell).
- One button system:
Buttongains 28 and 54, hold, busy, leading icon and key hint, and screens stop rolling their own (button). - 44 targets: the 44 single and two-line rows, and
IconButtonat 44 (list row, icon button). - Status marks:
StatusMarkgains the badge, count, status word, the cross for failed and elapsed time on running (status mark). - One
Iconcomponent from the set, with react-native-svg (icons). - Violet only for needs you: the ask card border stays neutral while open (ask card).
- The shell: header labels and page swipe instead of the tab bar, the floating Lumen and Places as a bottom sheet (phone shell, sheet).
All open items
- Account picker row,
app/need/[id].tsx: nothing built; the From row and the picker sheet. (account picker row) - Agenda: no planner. (agenda)
- Ask card,
src/session/Rows.tsx: border turns--beaconwhile open; spec: neutral border. (ask card) - Ask card,
src/session/Rows.tsx: Allow once and Deny only; no Always in the project, no reason, no busy state. (ask card) - Avatar,
src/ui/Row.tsx,src/ui/Screen.tsx: the agent tile is a circle (radius.full) with--text-2ink at 32; it needs the rounded square and a sharedAvatarcomponent with the four sizes. (avatar) - Banner,
src/ui/NotifyBar.tsx,src/ui/SignInBar.tsx: inline bars with their own styles; build one Banner with icon, fact, detail and one action. (banner) - Button,
src/ui/Button.tsx: four variants at 44 and 32 only; add 28 and 54, hold, busy, leading icon and key hint; ghost ink istext2; screens roll their own buttons. (button) - Card,
src/session/Rows.tsx,src/vault/views.tsx: AskCard and TrustCard are inline styles; extract one Card with header and footer slots. (card) - Chip,
app/devices.tsx: the devices badge is bordered with radius chip; no filter chips, no source chip. (chip) - Command bar: no Find, no command bar, no Lumen. (command bar)
- Command result card: nothing built; the three views in the chat-core transcript, rows only. (command result card)
- Composer,
src/session/Composer.tsx: text only; no chips, attach, prefixes, queued row, pills or rewind; input 16 not 17. (composer) - Credential sheet: nothing built; the bottom sheet with Paste and Face ID on Connect. (credential sheet)
- Device row,
app/devices.tsx: no holds line, no More, no Rename or Remove hold, no Update ready. (device row) - Device row: trust note reads "Presence follows"; name the proof ("Face ID follows"). (device row)
- Device row: trust tags draw as outline chips; use the tag (fill
--hover, no border). (device row) - Device row: no 40 tile and no right-hand path column; the path is a text line under the name. (device row)
- Diff: not built; edits show as a tool row with no diff. (diff)
- Draft card,
app/need/[id].tsx: fields are read-only; spec: edit in place. (draft card) - Draft card: no presence line; no "Send with Face ID" state; sends from the Deck or Lumen "for now". (draft card)
- Form controls,
src/session/Composer.tsx: only the composer's text input exists (16 px); build the other eight at touch sizes. (form controls) - Glass frame: no Glass view. (glass frame)
- Glass mini-view: nothing built; the card from
sight.framestills, the pill when collapsed. (Glass mini-view) - Icon button: no icon button; screens use
Pressablewith text. BuildIconButtonwith 44 default and the icon component. (icon button) - Icons: no icon component; build one
Iconfrom the table with react-native-svg. (icons) - Key hint: no key hints; add them for iPad and web builds with a hardware keyboard. (key hint)
- List row,
src/ui/Row.tsx: one 86 tall three-line shape for everything (ROW_HEIGHT); add the 44 single and two-line variants and selected, focused states. (list row) - List row: rows sit on
--bgwith a bottom border, not in a card; the avatar is round, not a tile. (list row) - List,
src/ui/List.tsx: no sticky group headers, no Load 40 more, no chips (virtualized above 100 rows is correct). (list) - Mode chip,
app/session/[id].tsx: the mode is a text line under the composer and the header chip is "agent · provider · model · project"; build both chips and the mode sheet. (mode chip) - Needs row,
app/(tabs)/index.tsx: kind line is "agent · project"; only gate and ask kinds; no decided state. (needs row) - Needs row,
src/ui/SwipeRow.*.tsx: commit at 40% of the row width, not 100 or a fling; reveal has no icon or Face ID glyph. (needs row) - Needs row, waiting: read
waiting.listandwaiting.count; the Now label badge and the app icon badge from the one count; the Pairing row with its code field. (needs row) - Needs row: rows sit on
--bgfull width, not in a--panelcard. (needs row) - Authenticator code: no vault codes. (otp)
- Phone shell,
app/(tabs)/_layout.tsx: a bottom tab bar (Now, Chats, Agents) instead of the header labels and page swipe. (phone shell) - Phone shell,
app/places.tsx: no floating Lumen; Places is a modal screen, not a bottom sheet. (phone shell) - Phone shell,
app/_layout.tsx: Instrument Sans is not loaded, so labels render in the system font. (phone shell) - Pill,
src/state/connection.ts:toConnectionand the outbox list exist; no pill or queued line is drawn. (pill) - Plan card: not built. (plan card)
- Popover,
app/session/[id].tsx: no popover or picker; mode is plain text. (popover) - Presence line,
src/state/needs-model.ts: coverage is computed, nothing is drawn; sends go to the Deck or Lumen. (presence line) - Question card: not built; the ask card sends you to the Deck. (question card)
- Rail: no rail at 720 and up; tablets get the phone tab bar. (rail)
- Settings row,
app/settings.tsx: no settings rows; build the phone layout (44 controls, reserved line, stepper). (settings row) - Sheet: no sheet; Places is a modal screen and Needs detail is a pushed screen; build the bottom sheet on Reanimated with the same detents. (sheet)
- States,
src/ui/Screen.tsx:Emptyonly; no skeleton, no offline pill (useConnectionis unused), no decided state. (states) - Status mark,
src/ui/StatusMark.tsx: no badge, count or status word; done uses a 1 px border (1.5); the failed mark is a slashed circle, not the cross; no elapsed time on running. (status mark) - Stepper and checks,
app/pair.tsx: pairing shows a status line only; no steps, checks or QR. (stepper and checks) - Suggestions,
src/session/Composer.tsx: nothing built; take the chat-core list with 44 rows, 4 at most. (suggestions) - Tabs: no tabs; build
Tabswith the segmented and strip forms. (tabs) - Terminal: not built. (terminal)
- Tip: nothing built; the Places sheet line and empty screen lines. (tip)
- Toast,
src/ui/UndoToast.tsx: Undo text is--focus(bone); use--text, 600. (toast) - Toast,
src/ui/UndoToast.tsx: floating only; no in-place variant; no ⌘Z on the web build. (toast) - Tool row,
src/session/Rows.tsxRunRow: no icon, no elapsed timer, no detail; "Hide"/"Show" text instead of a chevron. (tool row) - Tool row: status mark dot instead of the icon. (tool row)
- Top bar,
src/ui/Screen.tsx: no desktop or tablet top bar;Screendraws a phone title only. (top bar) - Turn,
src/session/Rows.tsx: no author line (tile, name, time). (turn) - Turn: your bubble is
--panelwith a--ruleborder; spec:--hoverfill, no border. (turn) - Turn: thinking reads "Thought · N characters" and never opens; spec: "Thinking · 8 s", expandable. (turn)
- Turn: steer marker reads "Steering" / "Steered at step N"; spec: "you steered here · after 3 steps · 14:32". (turn)
Files are under apps/app/ on work/mobile.
Done when
npm run tokens -- --checkpasses and screens readapps/app/src/theme/tokens.ts, not typed values.npm run design:auditpasses for the phone boards you changed, and you looked at each PNG.- The app's own tests pass on work/mobile.
- Every App item above is ticked in its spec.
capsule-pro
Owns Lumen on the Mac (local/capsule/native/Sources/): the panel, the list of what waits,
the session panel, presence and the menu-bar item.
Start here
- The redesign first: the answer card grows then scrolls, no empty headings, the footer states, layered Esc, then Vyre Memory, voice and computer use (Lumen, redesigned, Gaps).
- Tokens, not hand typed values:
Theme.swiftmaps tokens to old names and types its own sizes; readTokens.generated.swift(Lumen on the Mac). - One button system:
AgentButtonusesTokensprimary colours,Radius.buttonand the five variants (button). - Sentence case: "HELD FOR YOU", "WAITING ON YOU", "OFFLINE" and the mono caps section headers become sentence case (ask card, list, states).
- 44 targets: rows are 40 tall; use 44 (list row, needs row).
- Violet only for needs you: the selected row has a violet left bar; use the
--hoverfill (needs row, Lumen on the Mac). - Status marks: the ring for Pulse, the badge, neutral relayed health (status mark).
- The icon set as SwiftUI shapes instead of SF Symbols (icons).
All open items
- Account picker row,
UI/CapsuleView.swift: no "Send from" group; readvault.connections.list {capability, surface: "capsule"}, with the states, the Sign in fix and Connect another. (account picker row) - Agenda,
Host/Planner.swift: the banner says "Missed: Timer"; no ring count. (agenda) - Ask card,
UI/AgentDeskView.swiftHeldCardView: buttons read "Allow" and "Deny"; no "Always in <project>", no reason line. (ask card) - Ask card: label "HELD FOR YOU" in mono caps, tracked; spec: "Permission", sentence case. (ask card)
- Avatar: no avatar; Lumen board draws 20 tiles with radius 5. (avatar)
- Banner,
UI/AgentDeskView.swiftWaitingHint: a 30 tall hint line; banners are not drawn in Lumen panel. (banner) - Button,
UI/AgentDeskView.swiftAgentButton: primary is abone(text) fill, radius 6, pressed at 0.8 opacity; useTokensprimary colours,Radius.button, the five variants and the states. (button) - Lumen on the Mac,
UI/Theme.swift: maps tokens to old names and hand-types sizes (query 22, title 14, label 10.5 mono, rows 40); only colours, status andRadius.cardcome from tokens. (Lumen on the Mac) - Lumen on the Mac: placeholder reads "Search, calculate, ask, or @ a session". (Lumen on the Mac)
- Lumen on the Mac: list header is "WAITING ON YOU · n" in mono caps in the attention colour; the focused row has a violet left bar instead of
signalWash. (Lumen on the Mac) - Lumen on the Mac: offline banner reads "OFFLINE" in caps, not "Works offline · 1 queued". (Lumen on the Mac)
- Lumen on the Mac: always dark; no
Tokens.paper. (Lumen on the Mac) - Lumen on the Mac,
UI/PresenceView.swift: reads "Touch ID to approve exactly this." with no 30 min covered line. (Lumen on the Mac) - Lumen on the Mac:
AgentButtonprimary isprimaryBg(bone) withprimaryInk, radius 6. (Lumen on the Mac) - Lumen on the Mac,
Host/Panel.swift: radius isTokens.Radius.card(12), notsheet. (Lumen on the Mac) - Lumen on the Mac:
Tokens.generated.swifthas no shadow tokens, so--floatcannot come from it yet. (Lumen on the Mac) - Card,
UI/AgentDeskView.swiftHeldCardView: hand-typed sizes and a caps mono label; useTokens.Radius.cardand the 12/600 sentence-case label. (card) - Chip,
UI/CapsuleView.swift,Extensions/sight/SessionPanel.swift: chips are capsule-shaped with gold icons (Theme.recall); use the tag and filter shapes and neutral ink. (chip) - Command result card: nothing built; the card under the input for a run command, 5 rows, ⌘O to the Deck. (command result card)
- Composer,
UI/CapsuleView.swift,Extensions/sight/SessionPanel.swift: plain field; no steer, queue, Stop, modes or prefixes. (composer) - Credential sheet: nothing built; the inline row and secure field, "Connect Deepgram to talk" in place of the copied terminal command. (credential sheet)
- Diff: not built. (diff)
- Draft card, HeldCardView: field keys in caps, tracked; label "HELD FOR YOU"; spec: sentence case. (draft card)
- Draft card: no scheduled send; no presence line under Send. (draft card)
- Form controls,
UI/CapsuleView.swift,UI/AgentDeskView.swift: systemTextFieldandTextEditorstyling; no toggles, segments or steppers. (form controls) - Glass mini-view: nothing built; the step pill for an agent's computer, ⌘O to Glass. (Glass mini-view)
- Icon button,
Extensions/sight/SessionPanel.swift,UI/CapsuleView.swift: close and mic buttons are SF Symbols at 11 to 13 pt with no hover fill or focus ring; draw the set's icons in a 28 square withTokenscolours. (icon button) - Icons: SF Symbols everywhere (xmark, chevron.down, sparkle.magnifyingglass, mic.fill); draw the set as SwiftUI
Shapes from the same path data. (icons) - Key hint,
UI/CapsuleView.swiftKeyCap: SF Rounded 10.5 semibold inTheme.stone, 17 square, filledTheme.raised; use JetBrains Mono 12, 20 tall, no fill,Tokenslabel ink. (key hint) - List row,
UI/CapsuleView.swiftRow: 40 tall with hand-typed sizes (14, 12, 11.5); selected is a rounded raised fill with a 3 px--focuscapsule on the left; use 44, the type steps and the--hoverfill. (list row) - List,
UI/CapsuleView.swiftSectionHeader: mono 10 caps with tracking; use 12/600 sentence case. (list) - List,
UI/AgentDeskView.swiftWaitingList: caps at 9 rows (Theme.maxRows) with no count or more. (list) - Mode chip: none; the session panel needs the provider chip at least. (mode chip)
- Needs row,
UI/AgentDeskView.swiftWaitingRow: two lines, no kind line, a 7 dot for the tile, 40 tall; header "WAITING ON YOU" in caps. (needs row) - Needs row, waiting: the list and the "Needs you · n" header from
waiting.list; the menu-bar dot fromwaiting.changed; answer throughanswer.tool. (needs row) - Needs row: selected row has a 2 px violet left bar (violet as a border); use the
--hoverfill only. (needs row) - Pill,
UI/AgentDeskView.swiftOfflineBanner: a 30 tall line with "OFFLINE" in caps and "vyred is not running on this Mac. Start it with vyre up."; use the pill words, sentence case, no caps label. (pill) - Plan card: not built (proposed: the Needs row with Start building on
⌘⏎, the card opening in Chat). (plan card) - Popover,
UI/AgentDeskView.swiftActionMenuView: caps title with tracking and a 2 pxTheme.signalleft bar on the active row; use the--signal-washfill and a 12/600 sentence-case header. (popover) - Presence line,
UI/PresenceView.swift: only the proof prompt ("Confirm it's you", "Touch ID to approve exactly this."); no covered line under Send and no lapsed "Send with Touch ID" label. (presence line) - Question card: not built. (question card)
- States, OfflineBanner: reads "OFFLINE" in caps; no queued count; no skeleton. (states)
- Status mark, WaitingRow,
UI/CapsuleView.swift,Host/MenuBarItem.swift: waiting dot usesTheme.attentionat 7 px; tool rows use SF Symbols; Pulse is a bone 7 px dot, not the ring; no badge; relayed health is not neutral. (status mark) - Suggestions,
UI/CapsuleView.swift: the @ target ranks its own agents and sessions; drawsuggest.queryrows under the local rows, never moving a drawn row; ghost text;suggest.picked. (suggestions) - Tip: nothing built; the line under the empty input, ⌘. to dismiss. (tip)
- Toast: no toast; add the floating variant under Lumen's list for answers given there. (toast)
- Tool row,
UI/CapsuleView.swiftToolRows: SF Symbols instead of the stroke set; no folding, no detail. (tool row) - Turn,
UI/CapsuleView.swift,UI/AgentDirectView.swift,Extensions/sight/SessionPanel.swift: three renderers; one turn view. (turn) - Turn: author in mono caps; spec: sans 600, sentence case. (turn)
- Turn: no steer marker, no thinking row, no turn footer. (turn)
Paths are under local/capsule/native/Sources/ on work/capsule-pro. The terminal spec lists the
Lumen as not used (it opens the terminal in the Deck); that is not an item to build.
Done when
npm run tokens -- --checkpasses and no Swift view types a size, colour or radius the tokens hold.npm run design:auditpasses for Lumen boards you changed, and you looked at each PNG.- Lumen tests pass (
local/capsule/native/Tests). - Every Lumen item above is ticked in its spec, and the items capsule.md adds.
app-design (system items)
These fix the boards, icons.txt or the tokens, not a surface.
- Account picker row: monochrome provider glyphs (google, microsoft, mail, slack) and a mail icon in
icons.txt; the AccountRow board. (account picker row) - Chip: the boards draw tags with radius 5; the token is
--radius-chip(4). (chip) - Command result card: the ResultCard board (table, text, card, error). (command result card)
- Credential sheet: the CredentialSheet board (key, file, sign-in, Lumen row). (credential sheet)
- Form controls: the stepper is drawn twice (a
--hoverfill in Settings, a--rule-strongoutline in Project settings); the spec takes the fill. (form controls) - Glass mini-view: the GlassMini board. (Glass mini-view)
- Icons: add minus and unlock to
icons.txt; the Vault board draws the faceid frame without its face, which is not in the set. (icons) - Key hint: the "Settings · account and project scopes" board draws ↵; use ⏎. (key hint)
- Mode chip: the composer chip radius 6 is not a token; add one or use
--radius-button. (mode chip) - Suggestions: the Suggestions board (the three lanes, ghost text, Lumen groups). (suggestions)
- Tabs: the teammate phone strip is drawn 40 tall; the spec makes it 44. (tabs)
- Tip: the
tip(lightbulb) icon inicons.txt; the TipLine board. (tip)
Done when npm run design:audit passes on every changed board and each item is ticked in its spec.
Other teams
Items in the specs that belong to teams outside the surfaces. The specs stay the source of truth.
cohesion
- Suggestions: the @ lane in chat asks file sources too (the composer's file list as an offered source). (suggestions)
- Needs row: merge the kinds waiting does not have yet (Sign-in grant, New teammate, Merge failed, Stuck, Usage, Device, Plan), each from its owner's tool. (needs row)
memory-iq
- Suggestions: offer the entity source (people, places and aliases from memory already loaded) with
suggest.offer, never a model call. (suggestions)
vault
- Account picker row: a default per capability (proposed
vault.connections.update {id, default}) and a last used time on each row. (account picker row) - Credential sheet:
vault.needreturnspurposein plain words and the provider's key page for Open. (credential sheet)
platform
- Command result card: the result shape on the commands field (
view,title,rows,text,actions) andrender: trueon the call. (command result card)
polish-cli
- Command result card:
--viewframes for the seven commands without JSON output (capsule, connect, hooks, mcp, sideview, voice, send). (command result card) - Needs row:
vyre needsand the status line count fromwaiting.count. (needs row) - Suggestions: later, the same lanes and order under the prompt; Tab accepts. (suggestions)
Not assigned
- Device row, core:
relay.devices.removeandrelay.devices.trustboth ask for presence; Remove (a hold) and Stop trusting need no proof. Parked with relay; the summary should name the device, not its id ("Trust browser Chrome on alex's Pixel 8 fully"). (device row)