diff --git a/DESIGN.md b/DESIGN.md index ae8a3b5..12a5993 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -11,12 +11,23 @@ > versioned the golden references in-repo. The token names below are the contract the crate's > `theme.rs` / `tokens` module implement — a view that hardcodes a value the token layer already > names is a review failure. +> v4 (2026-07-02): an IA / flow / interaction pass on top of the grounded flow audit +> (`docs/research/11-ia-flows-audit.md`) and a `/design-consultation` +> (`docs/research/12-design-direction-v4.md`). **The visual system is unchanged** (fonts, grayscale, +> the two-signal actor axis, tokens, `⌘↵` confirm); the *structure* changed: a three-pane always-on +> right rail, the **request-origin model** (you / dapp / agent — one shared review, one attributed +> feed), the Projects layer dropped, auto-assigned agent handles (no fixed "Atlas"), a Transaction +> detail view, the identity masthead + the no-"Wallet"-label rule, holdings with a `$` value column, +> and the honest cap ledger. > > **Golden references (the pixel ground-truth agents build against), versioned in-repo under `designs/`:** +> - `designs/deckard-v4.html` — **the current v4 IA** (wallet home, the one shared clear-signing +> Review across origins, Activity, Transaction detail, three-pane rail; light + dark). Authoritative +> on **IA/layout/flow**; defers to the v3 refs below on the editorial atoms + the confirm styling. > - `designs/deckard-editorial-v3.html` — home, send confirm, swap compose, swap review, activity -> (the editorial language, light + dark). -> - `designs/deckard-agent-v4.html` — the agent surface, the compact agent presence on the home, and -> the redesigned transaction-as-hero confirm (this confirm supersedes the v3 send confirm). +> (the editorial language + atoms, light + dark). +> - `designs/deckard-agent-v4.html` — the redesigned transaction-as-hero confirm (this confirm styling +> supersedes the v3 send confirm; the v4 IA ref above reuses it). > When this doc and a reference disagree, the doc wins on rules and the reference wins on pixels; > fix whichever is stale. (These were unversioned under `~/.gstack/...` through v2; v3 checked them > into the repo so the "matches golden reference" definition-of-done item is actually verifiable.) @@ -113,24 +124,54 @@ AI-coded-design tell, and we reject it. --- ## Information architecture -**Two-pane shell** (sidebar + main + a thin top breadcrumb bar + a bottom status strip). No third -inspector pane; detail is contextual or a right slide-over when genuinely needed. - -**Sidebar = a single-column Conductor-style tree.** Top-level entities are **Projects**. Each -project expands to two first-class groups: -- **Wallets** — Splits-style rows: identity mark + name + truncated mono address + right-aligned - balance. -- **Agents** — **first-class, standalone** (this is a v2 change; agents were previously buried in - the wallet home). Row: cyan squircle + name + right-aligned spend magnitude + status glyph. +**Three-pane shell** (sidebar + main + a persistent right **metadata rail** + a thin top breadcrumb +bar + a bottom status strip). The rail shows contextual detail for the focused object (a wallet → its +holdings/status + cap ledger; a pending request → its clear-signing detail; an activity row → its +receipt). It is always on, not collapsible — the Linear nav·content·rail model. *(v4: this replaces +the v2/v3 "two-pane, no third inspector pane" rule; a request/agent/tx always has detail worth seeing +without navigating away, so the rail earns its place.)* + +**Sidebar = a single-column tree of the account's real entities.** There is **no Projects layer** — +Deckard has no project-switching, so the top-level groups are the things that actually exist *(v4: +dropped the `Projects` parent; it named a concept the product doesn't have)*: +- **Wallets** — identity mark + **name** + truncated mono address + right-aligned balance. Every wallet + has a real name, and the literal word **"Wallet" never appears as a label** anywhere (an unnamed + wallet gets a deterministic default, never "Wallet"). The breadcrumb names the entity (`Meridian`, + `Meridian › Swap`) — never a project prefix, never the word "Wallet". +- **Agents** — cyan squircle + an **auto-assigned, human-renamable handle** (a rotating codename/city + list, e.g. `Kyoto` — **not** an invented persona like "Atlas") + spend magnitude + status glyph. +- **Connections** — dapp origins (favicon + domain + a trust dot). First-class group; deep connection + management is deferred (ADR-0001 / #44), but the slot and the origin model exist now. + +### The request-origin model (v4 — the spine) +Every request to move money has an **origin**: **you** (in-app), a **dapp** (browser bridge today, +plugins later), or an **agent** (MCP). One shared review, one attributed feed, one Rules vocabulary +span them all — this is the product's spine, not the agent loop alone. The origin is rendered by +**identity + a header rail**, never a third signal color: amber = human and cyan = agent stay the only +two signal colors; a **dapp/external origin is a neutral identity** (favicon + domain) **+ a trust +badge that borrows the state colors** (verified = success, first-seen = amber caution, flagged = +danger). Non-human sessions get the auto-assigned handle above. Approval is **policy / per-origin**, +not one global posture — "approve every move" is itself a valid policy; agents run on policy, dapps +default to approve-per-request (a scoped per-origin grant is deferred, ADR-0001). **Views are contextual to selection:** -- Select a **wallet** → its home (hero balance, allocation, actions, holdings, and a **compact - agent presence** row, not the full policy). -- Select a **project** → aggregate across its wallets + its wallets and agents. -- Select an **agent** → the **agent surface** (§The agent interaction model). -- **Activity** is a destination (the see-and-stop log). **Settings** is a bottom gear (global). - **Send / Receive / Swap / Shield** are actions on the selected wallet. Clear-signing is the - confirm step inside them, and the same review renders for an agent's proposal awaiting approval. +- Select a **wallet** → its home: an identity **masthead** (name + mark above the mono hero) + the + hero USD/synced/verified meta line + allocation + **left-anchored** actions + a compact **"Waiting + on you"** strip (one line; the home flips supervision-forward — the queue surfaces above the + portfolio — only when a request/agent is live) + the **holdings ledger** (mono, a **$ value column + + 24h**, decimal-point aligned). +- Select an **agent** → the agent surface (§The agent interaction model). *(v4: kept light this pass; + the in-wallet agent-interaction model needs more thought — documented as an expandable slot.)* +- **Activity** is a destination: the origin-attributed see-and-stop feed (a `NEEDS YOU` queue above a + day-grouped log; rows are **scannable, not prose** — identity + an action tag + mono amount + a + warning tag + hash/glyph/time) with the **STOP** emergency control (`Stop all agents`, shown only + when an agent is active so it never reads as a start/stop toggle). +- Select a **transaction** → a **Transaction detail** view (v4 addition): the clear-signed receipt + + hash (copy + explorer) + from/to + amount + gas + block + time + **Authorized by** (the rule/origin + that permitted it). +- **Settings** is a bottom gear (global). **Send / Receive / Swap / Shield** are actions on the + selected wallet; the confirm step is the one **shared clear-signing Review** (§Clear-signing), which + renders identically for you, a dapp, or an agent — only the origin header rail changes. --- @@ -406,16 +447,28 @@ key-cap affordance** (like Linear/Raycast "Create ↵"), made spam-proof by tier `⌘↵` **arms** ("Press ⌘↵ again to send") and the second sends. No modal, no checkbox. - Mouse users get an equivalent click on the same button; the button is never a hold target. -### Clear-signing review (the shared trust engine; transaction-as-hero) -Rendered for self-send, swap, shield, and an agent proposal. It is a **statement, not a form**: -- The **transaction is the hero**: a tiny `SENDING`/`SWAPPING` label, then the **amount big in - mono**, then the USD-equiv, then `TO` with a **prominent identicon + ENS + full-ish address** - (via `truncated_address`). What can lose money (how much, to whom) dominates. -- **Danger first, in red** (`caution_line` danger): "Public on Ethereum and can't be undone." - Then amber caution lines (first-time recipient, slippage). Never a gray box. -- **Quiet supporting facts** demoted below a hairline: From, network fee, route/slippage. State each - fact **once** (no triple-restatement). -- The `⌘↵` confirm button + the arm note + an Edit link. +### Clear-signing review — the ONE shared trust engine (transaction-as-hero) +There is exactly **one** review surface, rendered for **every origin**: self-send, swap, shield, a +**dapp request**, or an agent proposal. Only the **origin header rail** (who is asking) changes; the +body is identical. Two review layouts would mean two ways to be fooled. It is a **statement, not a +form**: +- **Origin header rail**: the requester's identity — `You are sending` (amber), `Kyoto proposes` + (cyan agent handle), or `app.uniswap.org requests` (a dapp: neutral favicon + domain). A dapp or + agent is identity + a state-color trust badge, **never a third signal color**. +- The **transaction is the hero**: a tiny `SENDING`/`SWAPPING` label, then the **amount big in mono**, + then the USD-equiv, then `TO` with a **prominent identicon + ENS + full-ish address** (via + `truncated_address`). Say the amount **once** — do not repeat it as a "you pay / you receive" + balance-diff for a simple swap (a Rabby-style "what changes" block is only for txs whose net effects + aren't obvious from the hero). +- **Danger first, in red** (`caution_line` danger): "This can't be undone." *(v4: was the wordier + "Public on Ethereum and can't be undone" — plain and declarative, no textbook blockchain explainer.)* + Then amber caution lines (first-time recipient, slippage). Never a gray box. **No speculative + site-reputation** copy — we don't have it. +- **Quiet supporting facts** demoted below a hairline: From, network fee, route/slippage, and the + **Allowed by** authority line — the rule that permitted it + the cap **after this move** + (`Swap rule · $0.14 of $0.20 daily left after this`). State each fact **once**. +- The `⌘↵` confirm button (via a platform-aware **key-cap** widget — `⌘` on macOS, `Ctrl` on Linux) + + an Edit link. No prose arm-delay explainer beside it. ### Policy / agent surface, budget gauge, kill switch See §The agent interaction model. The `budget_gauge` and the Pause/Revoke/Rotate/Adjust controls are @@ -574,8 +627,18 @@ A GUI change is not done until ALL hold (paste screenshots as evidence): - [ ] Every new action has a ⌘K `Command`. - [ ] No leftover starter slop (keyboard-hint rows, "Welcome to Deckard", dead settings, leaked build-flag or provider strings, orphan `—`). -- [ ] Matches the golden-reference HTML (`designs/deckard-editorial-v3.html` / - `designs/deckard-agent-v4.html`) in layout and hierarchy. +- [ ] **Identity is named** *(v4)*: wallets/agents show a real name/handle; the literal word "Wallet" + appears nowhere as a label; the breadcrumb names the entity (never a project prefix, never "Wallet"). +- [ ] **Money keeps its context** *(v4)*: holdings carry a mono `$` value column + 24h, **decimal-point + aligned**; the balance hero carries a USD + synced/verified meta line (honest fallbacks off mainnet). +- [ ] **One shared Review** *(v4)*: send/swap/shield/dapp/agent all render the single + transaction-as-hero review; origin is a header rail + identity + a state-color trust badge, never + a third signal color; the `Allowed by` line shows the cap **after** this move; no unenforced cap + is ever shown as enforced. +- [ ] Key-caps via the platform-aware `key_cap` widget (`⌘` macOS / `Ctrl` Linux), not a hardcoded glyph. *(v4)* +- [ ] Matches the golden-reference HTML: **`designs/deckard-v4.html`** for IA/layout/flow; + `designs/deckard-editorial-v3.html` + `designs/deckard-agent-v4.html` for the editorial atoms + + the confirm styling. ## Decisions log | Date | Decision | Rationale | @@ -594,3 +657,4 @@ A GUI change is not done until ALL hold (paste screenshots as evidence): | 2026-06-20 | **Activity lean now = audit log + STOP**; triage inbox / keyboard nav / drill-in receipts / filtering deferred and documented. | Build the see-and-stop log first; layer the inbox interactions once the loop is real. | | 2026-06-20 | **UI/display face: General Sans → Schibsted Grotesk** (JetBrains Mono unchanged). | General Sans is Fontshare/ITF proprietary — its EULA forbids redistributing the raw files / public-server hosting, which the public repo violated once #114 committed them. Schibsted Grotesk is OFL-1.1, a structural drop-in (same 400/500/600 weights), and built for editorial publishing — it fits the locked Editorial direction. Chosen over Hanken Grotesk (safer/quieter) and IBM Plex Sans (more recognizable) via /design-consultation. | | 2026-07-01 | **v3 token layer + versioned references.** Named every scale (spacing, object sizes, type, radii, stroke, elevation, opacity, icon, motion) as a token → Rust const; gave the semantic roles their own names over the single warm light (deliberate overload, not accidental); promoted light to a full token table; added elevation/opacity/AA sub-specs; unified the transaction hero to one `text-tx-hero` (was 40/32); declared object sizes a separate ladder (resolving the 30px-off-grid tension); checked the golden HTML references into `designs/`. | A design-system audit found the doctrine strong but enforcement partial: color + fonts were centralized, but everything below (127 raw `px()` across ~43 values, 11 scattered `text_size(px())`, duplicated leaf widgets) was hand-rolled, and "matches golden reference" pointed at an out-of-repo, unversioned, stale file. Prose can't enforce a spacing grid; a `const` the compiler + a lint check can. | +| 2026-07-02 | **v4 request-origin IA.** Kept the visual system; reworked structure: a three-pane always-on right rail; the **request-origin model** (you / dapp / agent — one shared clear-signing Review, one origin-attributed feed, origin as identity + a state-color trust badge, never a third signal); dropped the Projects layer; auto-assigned renamable agent handles (retired the "Atlas" placeholder); a Transaction detail view; identity masthead + the no-"Wallet"-label rule; holdings with a mono `$` column + decimal alignment + a hero USD/verified meta line; the honest cap ledger; and a platform-aware key-cap widget. Golden ref: `designs/deckard-v4.html`. | A grounded flow audit (`docs/research/11-ia-flows-audit.md`) found the bones strong but four load-bearing gaps (identity, two clear-signing treatments, a read-only agent surface, an unenforced cap). A `/design-consultation` (`docs/research/12-design-direction-v4.md`; Linear/Raycast/Superhuman/Rabby references + two outside voices) landed the direction: the product is the request→review→approve/stop loop generalized across *who is asking*, and the agent is one origin among several — not the whole product. Deep agent internals + dapp-connection editing deferred. | diff --git a/designs/deckard-v4.html b/designs/deckard-v4.html new file mode 100644 index 0000000..c296692 --- /dev/null +++ b/designs/deckard-v4.html @@ -0,0 +1,663 @@ + + + + + +Deckard — v4 IA (golden reference) + + + + + +
+
+ + + + + +
+
+
+
+ +
+
Deckard
+
+ + + + + +
+ +
+ + +
+
+ M +
Meridian
0xf39F…2266
+
+
+
9.9799ETH
+
$24,180.55  ·  synced 4s ago  ·  verified on mainnet
+
+
+
Public 7.78Private 2.20
+
+ + +
+ + +
+ 2 waiting for you + Review +
+ + + +
Holdings
+
AssetBalance24hValue
+
+
Ξ
Ethereum
ETH
9.9799
+1.2%
$24,180.55
+
$
USD Coin
USDC
1,284.00
+0.0%
$1,284.00
+
r
Rocket Pool ETH
rETH
0.0000
−0.3%
$0.00
+
+
+ + +
+
+ +
+ U + app.uniswap.org requests + ⌘K +
+ +
Swapping
+
0.4 ETH1,284 USDC
+
$967.20 at this quote
+ +
To
+
c
CoW Settlement
0x9008…c41
+ +
+
This can't be undone.
+
+ +
+
From
Meridian · 0xf39F…2266
+
Route
CoW Protocol
+
Network fee
~0.0009 ETH · $2.10
+
Allowed by
Swap rule · $0.14 of $0.20 daily left after this
+
+ +
Approve swap⌘↵
+ Edit + +
The same surface renders for every origin — only the top row changes: You are sending / Kyoto proposes / app.uniswap.org requests.
+
+
+ + +
+
+ Activity + + + +
+ + +
+
Needs you2
+ +
+ U + app.uniswap.org + Swap + 0.4 ETH + USDC + + new site + ⌘↵X + 0:41 +
+ +
+ K + Kyoto + Shield + 0.8 ETH + + over cap + ⌘↵X + 2:10 +
+
+ + +
Today
+
+ K + Kyoto + Shield + 0.20 ETH + proving the spend + pendingnow +
+
+ M + You + Send + 0.01 ETH + vitalik.eth + 0x7099…dc7918m +
+
+ A + app.aave.com + Supply + 1,000 USDC + 3.8% APY + 0x87870…595142m +
+
+ K + Kyoto + Shield + 0.02 ETH + 0xbe0e34…b6dd1h +
+
Yesterday
+
+ K + Kyoto + Shield + 0.08 ETH + you declined · over cap + 1d +
+
+ + +
+
+
+ K + Kyoto shielded + 0xbe0e34…b6dd +
+ +
Confirmed
+
Shielded
+
0.02 ETH
+
$48.36 · moved to your private balance
+ +
+
Status
Confirmed
+
Hash
0xbe0e34…b6dd  copy  explorer ↗
+
From
Meridian · 0xf39F…2266
+
To
Railgun shield · private
+
Amount
0.02 ETH · $48.36
+
Network fee (gas)
~0.0006 ETH · $1.45
+
Block
10,822,981
+
Time
Today · 1h ago
+
Authorized by
Kyoto · Shield rule
+
+
+
+ + +
+
+
Needs you2
+
+ U + app.uniswap.org + Swap + 0.4 ETH + USDC + + new site + ⌘↵X + 0:41 +
+
+ K + Kyoto + Shield + 0.8 ETH + + over cap + ⌘↵X + 2:10 +
+
+
+
Holdings
+
+
Ξ
Ethereum
ETH
9.9799
+1.2%
$24,180.55
+
$
USD Coin
USDC
1,284.00
+0.0%
$1,284.00
+
+
+
+ +
+
+ + + + +
+
Synced · block 10,823,004Verified · mainnetEthereum
+
+ + + + diff --git a/docs/research/11-ia-flows-audit.md b/docs/research/11-ia-flows-audit.md new file mode 100644 index 0000000..45c19c2 --- /dev/null +++ b/docs/research/11-ia-flows-audit.md @@ -0,0 +1,214 @@ +# Grounded flow audit — the funded demo, as-built (feeds #170) + +> Discovery audit for the UI/UX overhaul (#170, step 1). The app was run on a **funded +> `just demo`** (anvil fork of Sepolia @ block 10822990, chain 11155111, verified-reads off) on +> macOS and driven live: onboarding → unlock → home → send / swap / shield / receive → +> clear-signing → agent propose → approve → activity → STOP → settings. Every flow was +> screenshotted as-built and graded against the north star (`DESIGN.md` "Your money on autopilot, +> and you can see and stop everything") and the versioned golden references +> (`designs/deckard-editorial-v3.html`, `designs/deckard-agent-v4.html`). +> +> Evidence: `docs/screenshots/audit-170/`. Method + caveats at the bottom (a few observations are +> **fork-conditioned** or **synthetic-driving artifacts**, called out explicitly so they don't get +> mistaken for product bugs). + +## Headline + +The bones are **strong and on-brand**. The editorial language is real in the running app — bundled +Schibsted Grotesk + JetBrains Mono, the oversized mono hero, hairline-ruled cockpit rows, the +two-signal actor axis (amber human / cyan agent), the `⌘↵` transaction-as-hero confirm, and the +see-and-stop Activity loop with a genuine NEEDS-YOU approval beat all render as designed. This is +**not** a rebuild-from-scratch situation. + +The overhaul should be a **consistency + honesty + naming pass**, not a redesign. The friction +clusters in five places: (1) **identity/naming** — an unnamed wallet reads as "Personal / Wallet" +everywhere; (2) **two different clear-signing treatments** (transaction-as-hero for Send vs a +boxed key/value card for the agent approval) instead of the one shared review DESIGN promises; +(3) the **agent surface is a read-only policy dump** (no per-row editing, dimmed controls, no +"what Atlas did" feed) — the exact thing v2 said it removed from the home; (4) **money loses its +USD/verified context** below the hero (holdings show bare integers, no `$` column); and (5) a +**trust-relevant cap mismatch** — the agent advertises a "0.1 ETH per move" cap but auto-shielded +0.15 ETH. + +Verdict per flow (● good · ◐ drifts from spec · ○ gap): + +| Flow | Grade | One-line | +|---|---|---| +| Onboarding — Welcome | ● | Promise-led ("your new favorite wallet"), full-bleed, AGPL/no-telemetry footer | +| Onboarding — Secure | ● | Live strength meter (Strong/Fair), "no one can reset it" consequence, min-8 | +| Unlock | ● | Clean; amber focus ring; neutral primary | +| Wallet home (hero/alloc/actions) | ● | 64px mono hero, dimmed decimals, Shield-primary action row, honest private-balance line | +| Holdings ledger | ○ | Bare integer balances, **no `$` value column, no 24h** (golden ref has 4 cols) | +| Balance meta line | ◐ | **No USD-equiv / "synced · verified" line** under the hero (golden ref has it) | +| Compact agent presence | ● | `Atlas · acting · 0.15 / 0.5 today · 30%` gauge + chevron — exactly the spec | +| Command palette (⌘K) | ● | Search + wallet-context + all commands w/ shortcuts; brightness-lift selection | +| Send — compose | ◐ | Correct fields + status-strip downgrade banner; but centered, blank header mark | +| Send — clear-signing | ◐ | Excellent hero; but **both warnings red** (no amber caution tier), **no fee/From row** | +| Shield — compose | ● | Plain-language, 0zk auto-filled, gated CTA | +| Swap — compose | ◐ | CoW copy good; token pickers are a sprawling chip grid, bare `—`, blank marks | +| Receive | ● | QR + full EIP-55 addr + amber network caution + copy | +| Agent surface | ○ | Autonomy statement + limits + gauge + Revoke good, but **read-only** (see #3) | +| Activity — empty | ● | "All clear", amber idle STOP | +| Activity — feed + NEEDS YOU | ● | Best screen in the app: amber NEEDS-YOU band, cited reason, day-grouped log, glyphs | +| Agent approval — review | ◐ | Cites reason + no-blind-approve keys; but **boxed card, not the shared hero review** | +| STOP | ● | Idle amber → (arm-to-`⌘↵` documented; armed state not re-captured) | +| Settings | ◐ | Well-labelled; but bordered cards per section (editorial language is card-free) | + +--- + +## Findings, ranked + +### P1 — trust + identity (fix first; these undercut the north star) + +**1. Wallet identity collapses to "Personal / Wallet" for an unnamed wallet.** +The breadcrumb reads `Personal › Wallet` (literal "Wallet"), and the page-header H1 is the *project* +name "Personal" with the address beneath — because `display_name` is blank (Settings → Profile). +DESIGN is explicit: the breadcrumb `current` "names the selected entity (the wallet/agent name, +**never the literal word 'Wallet'**)", and the golden ref shows `Personal › Main`. A machine spends +"Personal"'s money and the row that should say *which wallet* says nothing. Fix: derive a real +default wallet name (ENS → short address → "Wallet 1"), never the literal word, and never fall the +page title back to the project name. Evidence: `02-home-dark`, `07-send-clearsigning`. + +**2. Two different clear-signing treatments break the "one shared review" contract.** +Send renders the transaction-as-hero review (tiny `SENDING` → 44px mono amount → full untruncated +address → red danger → armed `⌘↵`) — this is beautiful and correct (`07`). But the **agent-proposal +approval** renders a *different* screen: "Review request" with a **bordered key/value box** +(Amount / To / Breached limit) and no oversized hero (`14`). DESIGN says "the same review renders +for an agent's proposal awaiting approval" and clear-signing is "a statement, not a form… never a +gray box." The highest-trust moment in the product (approving a machine's spend) uses the *weaker* +of the two layouts. Fix: render the agent approval through the same transaction-as-hero engine, with +the cited limit as the danger/caution line. + +**3. The agent surface is a read-only policy dump — the thing v2 said it deleted.** +The surface (`11`) has the good bones: cyan "Atlas · acting", a plain-language autonomy statement, +a LIMITS list, the budget gauge, and a red "Revoke & STOP". But: the limits have **no per-row Edit +affordances**; the `Pause / Rotate key / Adjust limits` controls render **dimmed/disabled**; there +is **no "What Atlas did" feed** (the golden `deckard-agent-v4.html` puts the agent's slice of +activity here); and Scope (allowed actions/assets, session-key expiry) is folded into one flat +LIMITS list rather than its own section. DESIGN's whole agent-model pivot was "a dedicated agent +surface **owns editable policy + controls + its own activity**… the old read-only policy dump on +the home is removed." Today the dump moved to the agent surface rather than becoming editable. This +is the single biggest gap between the built product and the design intent. + +**4. The agent advertises a per-move cap it doesn't enforce on shields.** +The agent surface and autonomy statement say "Per-transaction cap **0.1 ETH**" / "acts on its own +under 0.1 ETH per move and asks you above that." But a **0.15 ETH** auto-shield **auto-approved and +broadcast** (agent log: "auto-approved within cap · broadcast ✓"); only a 0.8 ETH shield (over the +0.5 **daily** cap) triggered the approval beat. The demo `policy.demo.json` shield rule has no +`per_tx_cap_wei`, so shields are gated only by the daily cap — but the UI states a 0.1 per-move cap +that shields ignore. For a wallet whose entire pitch is "software-enforced limits you can trust," +the displayed cap must match the enforced cap. Fix: either enforce a per-tx cap on shields or make +the surface show the *actual* gate per action ("Shields: capped by daily budget"). Evidence: agent +run log + `11` + `13`. + +### P2 — money loses its context (portfolio reads unfinished) + +**5. Holdings ledger has no USD value column and no 24h delta.** +Rows render `Ethereum / ETH …… 10,000` — a bare right-aligned integer, no `$` value, no 24h, no +dimmed decimals (`03`). The golden ref holdings row is 4 columns (asset · balance · 24h · $value, +Stripe-aligned). Test tokens show bare `2` / `8`. Partly fork-conditioned (no price feed on a +Sepolia fork), but the ledger *design* always carries a `$` column and DESIGN requires "every USD +figure carries `$`". At minimum the ledger needs the value column with an honest empty treatment +when price is unavailable, not a naked integer. + +**6. The balance hero has no USD-equiv / "synced · verified" meta line.** +Under `10,000.001 ETH` the golden ref shows `$24,180.55 · synced 4s ago · verified on mainnet`. +The built home jumps straight to "Total" + the allocation bar with no meta line (`02`). The +per-chain honesty *is* present elsewhere (the status strip's `⚠ Demo fork · NOT VERIFIED` amber +banner is great — `06`/`07`), but the hero itself is missing the freshness + verified signal that +makes the number trustworthy at a glance. (USD is fork-conditioned; "synced Xs ago" is not.) + +### P3 — consistency + polish + +**7. Action composes are center-floated, not left-anchored.** Send/Shield/Swap/Receive center their +form in the pane (`06`,`08`,`09`,`10`) while the home is left-anchored full-bleed (`02`). DESIGN's +cockpit language is "left-anchored, full-bleed, hairline-ruled columns." Pick one; the editorial +direction says left-anchor. + +**8. Blank-fill identity marks on action headers + swap token chips.** The Send/Shield/Swap page +headers show an empty gray rounded square where a glyph should be (`06`,`08`,`24`); swap token chips +(WETH/COW/USDC/USDT/GNO) also render blank-fill marks. DESIGN: `identity_mark` is "**never a blank +fill**". Give each a monogram/glyph or drop the mark. + +**9. Send's first-time-recipient warning is red, not amber.** The Send review shows *two* red danger +lines — "public on Ethereum" **and** "Double-check the destination… funds are lost" (`07`). The +golden ref uses red for the irreversible-public danger and **amber** for the first-time-recipient +caution. DESIGN reserves red for irreversible/loss and amber for recoverable caution; collapsing +both to red flattens the danger/caution tier the whole trust model leans on. + +**10. Send clear-signing omits the network fee + From.** The review shows amount + recipient + +dangers + confirm, but no "Network fee" and no "From" (`07`). The golden `deckard-agent-v4.html` +confirm demotes both below a hairline. A user approving a send can't see what gas will cost. + +**11. Swap token selection is a sprawling inline chip grid.** Both sell and buy show all five tokens +as a chip row (`24`), versus the golden ref's compact asset-chip-with-chevron dropdown. It eats +vertical space and reads less premium. Also the "receive at least" shows a bare `—` placeholder +(DESIGN: "loading is a skeleton, not a bare —"). + +**12. Settings uses a bordered card per section.** APPEARANCE / PRIVACY / NETWORK / PROFILE each sit +in a bordered box (`15`). DESIGN allows Settings to be "more spacious" but the editorial language is +card-free (whitespace + hairlines + section labels). Minor; flagged for consistency. + +**13. Agent status reads "acting" at rest.** On the home presence row, the sidebar, and the agent +surface, Atlas shows cyan "acting" even when the loop is idle and nothing is in flight (`03`,`11`). +The acting-pulse should mean *currently working*; a resting agent should read "idle"/"watching". + +**14. Every action ⌘K-reachable, but the agent surface is not.** The palette (`05`) has all 16 +commands, but there is **no command to open an agent** (`portfolio/send/receive/shield/swap/settings/ +copy/theme/mask/lock/approvals/activity/approve-selected/deny-selected/revoke-all/refresh`). The +agent surface is reachable only by clicking the sidebar row. CLAUDE.md: "every user-facing action +must be reachable from ⌘K." Add an "Open agent / Atlas" command. + +**15. Receive copy has no "Copied ✓" feedback.** The Receive "Copy address" writes to clipboard +silently (`10`); DESIGN calls for inline "Copied ✓". Minor. + +--- + +## Fork-conditioned observations (NOT product bugs — don't fix blindly) + +- No USD figures anywhere (hero, holdings, review) — the Sepolia fork has no price feed. The *design + gap* is the missing `$` column/row treatment; the empty values themselves are expected on a fork. +- Test tokens "USD Coin (Sepolia test) / GNO" with integer balances — inherited fork state. +- `NOT VERIFIED` / "Demo fork: not mainnet" everywhere — correct, honest behavior (verified reads + are mainnet-only). This is a feature, and it renders well. + +## Driving artifacts (measurement noise — explicitly excluded from findings) + +These are limitations of synthetic macOS input (cliclick / System Events) against GPUI, **not** +product defects — a human with real keyboard/mouse focus does not hit them: +- The Send/Swap `⌘↵` and mouse-click confirm did not broadcast under synthetic input; real + broadcasts were driven through the headless agent path instead (which is why the Activity feed and + balances are real). The review *screens* render correctly. +- The ⌘K palette query, Activity `j/k/x/Enter/Esc`, and onboarding `Continue` don't receive + synthetic key events (hand-rolled `on_key_down` handlers + focus quirks); registered-action + shortcuts like `⌘,` (Settings) and `⌘⇧D` (theme) *do* work. Palette *navigation* and the + onboarding Back-up/Verify/Ready sub-steps were therefore not captured live; their structure is + known from the code + DESIGN. + +## Method + +macOS, live app on `just demo` (RPC via a free Sepolia archive). Window driven with a throwaway +harness (`.context/drive.sh`: Swift `CGWindowListCopyWindowInfo` finder + `cliclick` + +`screencapture -l` to dodge occlusion). Onboarding captured from a second app instance on +a fresh vault-less config dir. Full 40-shot capture set in `.context/shots/`; the 17 curated in +`docs/screenshots/audit-170/` are the evidence cited above. + +## Recommended direction for #170 + +Frame the overhaul as **"make the built product match its own design system, and make the agent +surface earn its name"** — a consistency/trust pass, not a redesign. Suggested epic shape (draft; +confirm before filing): + +1. **Identity & naming** (P1 #1) — default wallet name, breadcrumb never says "Wallet", page title + never falls back to the project. +2. **One shared clear-signing engine** (P1 #2) — route the agent approval through the + transaction-as-hero review; kill the boxed variant. +3. **Editable agent surface** (P1 #3) — per-row Edit, wire Pause/Rotate/Adjust, add the "What Atlas + did" feed, split Scope from Limits. +4. **Cap honesty** (P1 #4) — displayed cap == enforced cap, per action. +5. **Money keeps its context** (P2 #5/#6) — `$` column + hero meta line with honest fork/price + fallbacks. +6. **Consistency sweep** (P3 #7–#15) — left-anchor composes, kill blank marks, restore the + danger/caution color tier, fee row on Send, swap picker, ⌘K agent command, copy feedback. + +Do #1–#4 first; they are where the "autopilot you can trust and stop" promise currently leaks. diff --git a/docs/research/12-design-direction-v4.md b/docs/research/12-design-direction-v4.md new file mode 100644 index 0000000..06cad6d --- /dev/null +++ b/docs/research/12-design-direction-v4.md @@ -0,0 +1,135 @@ +# Design direction v4 — the request-origin wallet (feeds #170) + +> Output of a `/design-consultation` run that followed the grounded flow audit +> (`docs/research/11-ia-flows-audit.md`). Method: real-reference competitive research (browsed +> Linear, Raycast, Superhuman, Stripe, Rabby, Rainbow) + two independent outside-voice design +> directions (Codex GPT-5 + a Claude designer) + synthesis, then requirements locked with the user +> across three clarification rounds. The visual system (DESIGN.md v3) is **kept**; this is an +> IA / flow / interaction pass. Pixel ground-truth: `designs/deckard-v4.html`. + +## The one line + +Deckard is a **desktop wallet where every request to move your money — from you, from a dapp, or +from an agent — lands in one honest review, and you keep your hand on a brake.** The category's core +verb is *sign*; Deckard's is *see who's asking, and approve or stop*. + +The audit found the bones strong and on-brand; all three design voices independently agreed the +energy belongs on the same thing: the **request → review → approve/stop** loop, generalized across +*who is asking*. The agent is one requester, not the whole product. + +## Competitive research (grounded in real reference captures) + +- **Linear** (`linear.app`, captured 2026-07-02) — now literally "the product development system for + teams **and agents**." Load-bearing patterns: activity attributed by actor ("created the issue **on + behalf of** karri"), a live **watch-the-agent-work** panel, and a **three-pane** nav · content · + **right metadata rail**. Its select→act model (hotkey / command menu / right-click all dispatch the + same action) is the blueprint for Deckard's dual-modality. +- **Raycast** (`raycast.com`) — "your shortcut to everything." The **⌘K launcher + contextual Action + Panel** (grouped actions, a shortcut shown per row) is the reference for Deckard's palette and its + per-object action model. +- **Superhuman** — command palette that **teaches its own shortcuts** by showing them every use; + **Split Inbox** (auto-categorized focused streams so the actionable is never buried). The "Waiting + on you" home reflex is this mechanic. +- **Stripe** — mono for IDs/amounts + humanist sans for chrome; object-detail with an event timeline. + Validates the mono-for-money discipline and the left-aligned-content / right-aligned-numbers grid. +- **Rabby** — its **product** is the domain gold: **simulate-before-sign**, showing the balance-change + diff and risk/allowance flags on one canonical pre-sign screen whether the request came from a dapp + or from you. Deckard's shared Review adopts this. Its **marketing** (purple hero, blob mascot, + centered) is the generic-crypto slop Deckard rightly defines against — validates the editorial, + card-free, two-signal direction. + +**First-principles read (where we break from the category):** every other wallet treats "review this +transaction" as a per-transaction modal interrupt from a single source. Deckard's reality is *many +request origins over time* (you, dapps, agents, later plugins/session-keys) against *one policy and +one key*. So the differentiator is not a prettier sign-modal — it's a **unified request model**: one +review surface, one attributed feed, one Rules vocabulary, spanning every origin. This is already how +the architecture is built (`docs/agent-authorization-map.md`: one approval path, one Rules vocabulary, +three principals) — the UI should make it visible. + +## Two outside voices (kept in full for the record) + +Both landed on the same core independently — strong convergence. + +- **Codex ("trading-desk terminal"):** keep the fonts + the two-signal axis; split the palette into + disjoint **actor** vs **state** color registers and lock danger/warning/success/ring as + non-skinnable; **one shared Review** (source changes the header rail, never the trust surface); a + **"Waiting on you"** queue as the home reflex with a designed empty state; the agent surface is a + **feed, not a policy dump**; the **cap is a live enforced ledger** and the UI must *never display an + enforcement claim the engine doesn't back* (struck-through "unenforced" tag until fixed); a + dual-modality model where **selection / verb / STOP are the same command id** no matter how you + reach them. Two departures: no cards ever; trust-loudly-revoke-fast. +- **Claude ("The Watchfloor"):** home foregrounds live supervision; a drivable "scrub the future" + time-scrubber on the confirm; the autonomy **sentence** as the policy editor; warm/cold made + temporal; a mono-money odometer. (User rejected the scrubber, the temperature shift, and the + odometer — see decisions.) + +Full transcripts of both are preserved in the session task outputs. + +## Locked v4 decisions (from the clarification rounds) + +**Scope — go deep on:** the everyday desktop wallet + the one shared Review + the origin-attributed +"Waiting on you" / Activity surface. **Light on agent internals** (the agent↔wallet interaction is +MCP-only today with no in-wallet model; needs more thought later — documented as an expandable slot, +not redesigned now). + +1. **Shell → three-pane.** Sidebar · main · a **collapsible right metadata rail** showing contextual + detail for the focused object (wallet → holdings/status; a pending request → its clear-signing + detail; an activity row → its receipt). This departs from v3's "no third inspector pane" — adopted + deliberately (the Linear model). Collapsible so casual use stays two-pane. +2. **The request-origin model is the spine.** Requests come from **you**, **dapps** (browser bridge + today, plugins later), and **agents** (MCP). One shared Review renders for all; the origin is a + **header rail + identity**, never a third signal color. Amber = human, cyan = agent stay sacred; a + **dapp/external origin is a neutral identity** (favicon + domain) **+ a trust badge that borrows the + state colors** (verified = success, first-seen = amber caution, flagged = danger). +3. **Requester handles, not a persona.** "Atlas" was a placeholder and is retired. Non-human sessions + get an **auto-assigned, human-renamable handle** (rotating codename/city list, e.g. Kyoto) shown + alongside the **underlying origin** (MCP client id / dapp domain). Distinguishable sessions, no + invented mascot. +4. **One shared clear-signing Review** — the **static, Rabby-style balance-diff** (you pay / you + receive, ± per asset). *(The drivable scrubber was rejected as bad UX.)* Plus recipient + known/whitelist/**unknown** badge, the **authorizing rule + remaining cap after this move**, and the + verified-read meta. Danger-first red, then amber caution, then quiet facts. `⌘↵` arm-delay confirm. +5. **Approval is policy / per-origin, not one global posture.** In-policy moves proceed per the policy + the user set; **"approve every move" is itself a valid policy**; the browser extension can carry + per-domain settings. Full autopilot is *not* a smart default (on-chain moves are irreversible; STOP + halts future action, it can't claw back). Editing approval models is a later feature. +6. **Everyday-wallet excellence.** Identity **masthead** (real wallet name + deterministic mark; the + literal word "Wallet" never appears anywhere as a label); holdings with a **mono `$` value column + + 24h**; a **hero USD + synced/verified meta line** (honest fork/price fallbacks); **left-aligned** + action composes. +7. **Watchfloor-when-active home.** Wallet-forward by default; the "Waiting on you" region expands + above the portfolio when a request/agent is live, origin-attributed with countdowns. Empty state: + one quiet line ("Nothing waiting · your agents are within policy"). +8. **Honest cap ledger.** Shown as `$X of $Y remaining` and as cap-after on the Review; enforced on + **every** value path including shields; if the engine doesn't enforce it, render it struck-through + with an **unenforced** danger tag rather than fake it. (Fixes audit gap #4.) +9. **Dual-modality, one command id.** Every object is one focused selection (locked, always-visible + ring); `j/k` in lists, `h/l` across panes; ⌘K teleport + a contextual action panel that *is* the + right-click menu *is* the hover cluster; STOP persistent, global-hotkey-even-inside-modals, top ⌘K + entry. Invariant: every verb is one command id all surfaces dispatch (QA-assertable both ways). + +**Kept exactly as v3:** Schibsted Grotesk + JetBrains Mono; ~95% grayscale on near-black; the +amber/cyan two-signal actor axis; editorial card-free composition; ⌘K; the `⌘↵` arm-delay confirm; +current multi-chain UX (network pill + status-strip honesty banner); and v3 motion (the acting-pulse +only — **no** temperature-shift, **no** odometer/number-roll, **no** scrubber). + +**Deferred / documented slots (don't build deep now):** agent internals + an in-wallet agent +interaction model; deep dapp Connections editing (ADR-0001 / epic #44 — the sidebar reserves a +`CONNECTIONS` group and the origin-approval model is represented, but editing is later); session-key +principals (ADR-0002); multi-chain portfolio expansion. + +## Maps to the audit's P1 gaps (#174) + +The four load-bearing gaps this direction closes: **#1 identity** → the named masthead (§6); **#2 two +clear-signing treatments** → the one shared Review across origins (§4); **#3 read-only agent surface** +→ reframed as origin-attributed feeds + editable policy (documented, kept light this iteration per +scope); **#4 unenforced cap** → the honest cap ledger (§8). + +## Deliverables + +- `designs/deckard-v4.html` — the new pixel ground-truth (five views: wallet-forward home, + watchfloor-when-active home, the shared Review for a dapp origin, Waiting-on-you + Activity, and the + three-pane rail expanded), built on the exact v3 system. +- DESIGN.md → v4 edits (three-pane + rail, the request-origin model, the generalized shared Review, + identity rules, money-context, requester handles, honest cap, decisions log) — applied after sign-off + on the golden ref. diff --git a/docs/research/13-v4-build-plan.md b/docs/research/13-v4-build-plan.md new file mode 100644 index 0000000..68a76fd --- /dev/null +++ b/docs/research/13-v4-build-plan.md @@ -0,0 +1,212 @@ +# v4 build plan — the request-origin wallet (implementation backlog for #170) + +> The executable, agent-proof breakdown for building the codified v4 design in the GPUI app. Sources +> of truth: `DESIGN.md` v4, `designs/deckard-v4.html` (pixel ground-truth, five views), the audit +> (`docs/research/11-ia-flows-audit.md`, gaps #170/#174), the direction (`docs/research/12-...`). +> **DRAFT backlog for review — no GitHub issues filed yet.** On the go-ahead: an epic + 8 children +> under #170, each linking the Implementer Contract below. + +--- + +## Implementer Contract (every child links here — read before writing code) + +**Reuse, never hand-roll** (convention; `/code-review` + the per-view fidelity checklist enforce it). +Compose every view from `widgets.rs`. If a primitive is missing, add it to `widgets.rs` (see E1) — +do NOT inline a one-off copy. Existing: `short_addr`, `caution_line`, `error_line`, `section_label`, +`divider`, `identity_mark`, `budget_gauge`, `truncated_address` (+ `money.rs`). v4-added (E1): +`key_cap`, `origin_header`, `action_tag`, the `meta_rail`, `balance_diff`. Colors via `theme.*` + +`amber`/`agent` only (no raw hex); sizes/spacing/radii/motion via `tokens` (no magic `px()`); money + +addresses via `money.rs` (mono, tabular, dimmed decimals). + +**Never regress (trust invariants — a PR that touches these must prove they still hold):** +- Clear-signing renders before every value move; the `⌘↵` arm-delay confirm (never hold-to-confirm). +- **No blind approve:** an approval resolves ONLY the still-pending record the human actually reviewed. +- **STOP** is always reachable, zeroizes the key, and denies in-flight work. +- **Cap enforcement is real:** the UI never shows an enforcement claim the engine doesn't back. + +**Definition of Done — ALL issues:** +1. `cargo fmt --all --check` clean · `just check` green (clippy `-D warnings`, both feature configs) · + `cargo test --workspace` green. +2. No new/changed deps (`Cargo.toml`/`Cargo.lock`) without explicit approval. +3. Every new user-facing action has a ⌘K `Command` (`palette_commands.rs` + handled in + `run_palette_command`). +4. GUI issues: annotated **before/after screenshots** off the funded `just demo` (use E0's recipe), + AND the per-view **fidelity checklist** (below) checked off in the PR. +5. A short **"do not touch"** honored (per child). + +**Definition of Done — trust-critical (E5 only), in addition:** +6. Regression tests for the two invariants at stake: **cap-enforced-on-shields** (a shield over the + stated per-move cap ASKS, never auto-broadcasts) and **no-blind-approve** (approve resolves only + the still-pending reviewed record). Paste the passing test output. +7. A **codex adversarial review** (GPT-5 xhigh, cross-model) of the FIX diff, looped until 0 findings + (per `docs/AGENTIC-ENGINEERING.md` + the "verify the fixes, not just features" practice). + +**Golden-ref match rule:** the target is the matching view in `designs/deckard-v4.html` (open it, +switch views via the top bar). Fidelity = the per-view checklist passes AND the before/after +screenshot reads like that view in layout + hierarchy. The HTML is web; the app is GPUI — there is no +pixel diff, so the checklist is the gate. + +--- + +## Per-view fidelity checklists (the golden-ref gate) + +Each references a `designs/deckard-v4.html` view. Check all in the PR; anything unchecked blocks merge. + +**Wallet home** (`data-s="home"`) +- [ ] Sidebar top group is `WALLETS` (no `PROJECTS`); Agents show a handle + status; Connections show favicon + domain. +- [ ] Breadcrumb is the entity name only (e.g. `Meridian`) — no `Personal ›`, no literal "Wallet". +- [ ] Identity masthead: name + `identity_mark` above the mono hero. +- [ ] Hero mono, dimmed decimals; meta line `$… · synced … · verified on mainnet` (honest fallback off mainnet / no price). +- [ ] Actions left-anchored (Shield primary; Send/Receive/Swap ghost). +- [ ] Waiting strip is ONE line (`N waiting for you · Review →` amber, else `Nothing waiting for you.`) — not a stacked band. +- [ ] Holdings: Asset · Balance · 24h · **$ Value**, mono, decimals **aligned on the point** across rows. +- [ ] Right rail "This wallet": sync/verified/network + cap ledger + Connections/Agents kv rows. + +**Shared Review** (`data-s="review"`) +- [ ] ONE transaction-as-hero surface; origin header rail (`You are sending` / ` proposes` / ` requests`) = identity + a state-color trust badge, never a third signal color. +- [ ] Amount stated once (hero + one USD line); no duplicate "you pay / you receive" for a simple swap. +- [ ] `TO` = identicon + full address + known/unknown badge. +- [ ] One danger line `This can't be undone.`; no speculative site-trust; no arm-delay prose explainer. +- [ ] Quiet facts once: From · Route · Network fee · **Allowed by: `` · $X of $Y daily left after this**. +- [ ] Confirm via the `key_cap` `⌘↵` (platform-aware, armed amber) + Edit link. + +**Activity** (`data-s="activity"`) +- [ ] Plain `ACTIVITY` header (no hero title, no explainer subtitle). +- [ ] `Stop all agents` emergency control, shown only when an agent is active. +- [ ] `NEEDS YOU` + a count badge; day-grouped log. +- [ ] Rows scannable: `[origin] [ACTION tag] [mono amount] [→ dest] · [warning tag] · [hash link] [glyph] [time]` — no "wants to / proposes to" prose. +- [ ] Attributed across you / dapp / agent. + +**Transaction** (`data-s="tx"`) +- [ ] Header origin + verb + hash; green `Confirmed` chip; read-only hero + USD. +- [ ] Rows: Status · Hash (copy + explorer link) · From · To · Amount · gas · Block · Time · **Authorized by** (rule/origin). +- [ ] Rail mirrors status/links; explorer link + copy work. + +**Rail** (`data-s="rail"`) +- [ ] Right rail always present (not collapsible), ~300px, hairline-left; content contextual to focus. +- [ ] No "Nothing selected." while a row is selected. + +--- + +## Verified current state (grounded 2026-07-02) +- Routing: `shell.rs` `enum Surface` (line 86); layout in `shell.rs` (~3149-3239) + `shell_chrome.rs`. +- Literal "Wallet": `shell_chrome.rs:73` (`Selection::Wallet => "Wallet"`), `palette.rs:205`. Breadcrumb `Personal ›` at `shell_chrome.rs:284-287`. +- `widgets.rs` has 8 primitives (above); the v4 five do not exist. +- Shared review = `commit_view.rs:263`; the divergent boxed agent card = `activity_view.rs:830`. +- Cap bug: `policy.demo.json` shield rule has no `per_tx_cap_wei`; a 0.15 shield auto-broadcast under a stated 0.1 cap. Enforcement: `deckard-core` `evaluate` + the signerd shield path. + +## Dependency graph + sequencing +``` +E0 driving ·(prereq for every GUI issue's evidence) +E1 widgets ─┬─> E2 identity + ├─> E4 money + ├─> E3 rail ─┬─> E6 activity/waiting + │ └─> E7 transaction + └─> E5 🔴 shared review + enforced cap ──> E7 + E2..E7 ─────────────────────────────────────────> E8 ⌘K +``` +E0 first (so evidence is possible). E1 next (all reuse it). E2/E4 small early wins. E3 structural +(E6/E7 rail content needs it). **E5 is the trust-critical center** (enforcement + the review that +displays it, merged, one adversarial review). E8 wires ⌘K last. + +--- + +## Epic: Build the v4 request-origin IA +One wallet, one shared Review, N request origins (you / dapp / agent). Visual system unchanged; +IA/flows rebuilt. Each child ~1-3 days, independently shippable, links the Implementer Contract. + +### E0 — App-driving + screenshot recipe (prerequisite) +Commit a documented, reusable way to drive the running app and capture per-window screenshots, so +every GUI issue can produce before/after evidence. +- macOS: `just demo` (funded fork) or `just run`; a helper for window-scoped capture + (`screencapture -l`, id via a Swift `CGWindowListCopyWindowInfo` snippet) + `cliclick` + for clicks/typing; unlock via click (synthetic Enter won't submit). +- **Honest caveats (save agents hours):** synthetic input does NOT reliably fire the `⌘↵`/click + confirm, the ⌘K query, or Activity `j/k/x/Esc` (GPUI hand-rolled `on_key_down` + focus quirks); + registered-action shortcuts (`⌘,`, `⌘⇧D`) DO. So: capture compose/review SCREENS via clicks; drive + real broadcasts through the headless agent (`just demo-agent` / `just demo-deposit`); verify + LOGIC via tests, not screenshots. Link `docs/dev/headless-gui-screenshots.md` for Linux/CI. +**Do not touch:** app code. **AC:** a committed `docs/dev/*` recipe + a helper script (not gitignored); +another agent can follow it to unlock the demo and capture a labelled screenshot. +**Files:** `docs/dev/`, `scripts/` (or `just` targets). + +### E1 — v4 widget foundation (`widgets.rs`) +Add `key_cap(keys, armed)` (platform-aware `⌘`/`Ctrl` via `std::env::consts::OS`, chord as one cap), +`origin_header(origin)`, `action_tag(kind)`, the `meta_rail` scaffold, `balance_diff`. Audit + land +any DESIGN-referenced primitive not yet built (`page_header`, `kv_row`, `status_glyph`, `stop_brake`). +**Do not touch:** view files (that's E2-E7). **AC:** primitives compile, unit-tested where pure +(assert `key_cap` yields `Ctrl` on a forced-Linux path, `⌘` on macOS); each used by ≥1 view; `just +check` green. **Files:** `widgets.rs`, `tokens.rs`. + +### E2 — Identity & naming (fixes P1 #1) +Kill the literal "Wallet" (`shell_chrome.rs:73`, `palette.rs:205`); breadcrumb names the entity, drop +`Personal ›` (`shell_chrome.rs:284-287`); wallet-home masthead; deterministic default wallet name; +auto-assigned renamable agent handles (retire "Atlas"). +**Do not touch:** the review/activity/tx flows. **AC:** reflective test asserts "Wallet" appears +nowhere as a UI label; breadcrumb + masthead render; handles generated + renamable; **home checklist** +passes; screenshots. **Files:** `shell_chrome.rs`, `palette.rs`, wallet-home view, a handle generator, +`widgets.rs`. + +### E3 — Three-pane shell + always-on right rail +Add the always-on ~300px right `meta_rail` (contextual to the focused `Surface`); drop the Projects +layer; sidebar groups → Wallets · Agents · Connections (Connections = reserved slot, list only). +**Do not touch:** deep Connections editing (deferred #44). **AC:** three-pane renders at app width, no +overflow, rows clamp; rail updates on selection; **rail checklist** passes; screenshots (home/activity/tx). +**Files:** `shell.rs` (layout), `shell_chrome.rs`, `meta_rail` (E1). + +### E4 — Money keeps its context (fixes P2) +Holdings `$` value column + 24h, decimal-point aligned; hero USD/synced/verified meta line with honest +fallbacks (explicit "unverified"/"—" off mainnet or with no price, never a fake number); left-anchor +the action composes. +**Do not touch:** multi-chain portfolio (deferred). **AC:** holdings `$`+24h decimals aligned; hero +meta honest on the fork; composes left-anchored; **home checklist** passes; screenshots mainnet AND fork. +**Files:** wallet-home + compose views, `money.rs`. + +### E5 — 🔴 The ONE shared Review + honest enforced cap (fixes P1 #2 + #4) · TRUST-CRITICAL +Merged because the review's "Allowed by: cap after this move" is only truthful if the engine enforces +the cap. Ships + gets adversarially reviewed together. +- **Engine:** enforce the per-tx cap on **every** value path including shields (`deckard-core` + `evaluate` + signerd shield path; `policy.demo.json` gains a shield cap). If a path is genuinely + unenforced, the UI renders it struck-through with an "unenforced" danger tag. +- **UI:** route the agent-approval (`activity_view.rs:830`, delete the boxed card) AND the dapp path + through the ONE transaction-as-hero review (`commit_view.rs:263`); add the `origin_header` rail and + the **Allowed by** authority line (rule + cap-after from `evaluate`); danger copy `This can't be + undone.`; drop the speculative site-trust + the arm-delay prose. +**Do not touch:** agent internals beyond routing the proposal into the shared review (deferred). +**AC (Contract §6/§7 apply):** one review renders for send/swap/shield/agent/dapp, header-rail-only +difference; **cap-enforced-on-shields** + **no-blind-approve** regression tests pass (paste output); +codex adversarial review looped to 0 findings; **review checklist** passes; screenshots of all origins. +**Files:** `commit_view.rs`, `activity_view.rs:830`, `shell.rs` review handlers (~1732/1944/2251), +`deckard-core` `evaluate` + policy schema, signerd shield path, `policy.demo.json`. + +### E6 — Origin-attributed Activity + "Waiting on you" +Scannable tag rows (identity + `action_tag` + mono amount + warning tag + hash/glyph/time); `NEEDS +YOU` + count badge; day-grouped log; origin attribution; the compact home waiting strip; STOP → "Stop +all agents" (only when an agent is active). +**Do not touch:** the no-blind-approve/STOP-zeroize logic (keep intact). **AC:** rows scannable (tags, +not prose); **activity checklist** passes; STOP reframed; screenshots empty + populated (driven by +`just demo-agent`). **Files:** `activity_view.rs` (302, 452, 495), wallet-home strip, `shell.rs`. + +### E7 — Transaction detail view (new) +New `Surface` variant + `transaction_view.rs`: read-only receipt (reuse the E5 review structure) + +Status/Hash(copy+explorer)/From/To/Amount/gas/Block/Time/**Authorized by**; rail mirrors it; reachable +by clicking a tx AND via ⌘K. +**Do not touch:** the shared review component's shape (reuse read-only, don't fork it). **AC:** click a +tx → detail; explorer + copy work; Authorized-by shows rule/origin; **transaction checklist** passes; +screenshots. **Files:** `shell.rs` (`Surface` + routing + `run_palette_command`), new +`transaction_view.rs`, `palette_commands.rs`, `activity_view.rs`. + +### E8 — ⌘K coverage + agent command +Add the missing commands (open agent — none today; open Connections; open a transaction); enforce +every v4 verb is one command id all surfaces dispatch. +**Do not touch:** existing command ids/shortcuts (add, don't rename). **AC:** every v4 +destination/action ⌘K-reachable; a reflective test asserts registry ↔ handler coverage; palette +screenshots. **Files:** `palette_commands.rs`, `shell.rs` `run_palette_command`. + +## Out of scope (documented slots — deferred) +Agent internals / in-wallet agent-interaction model; deep dapp Connections editing (ADR-0001 / #44); +session keys (ADR-0002 / #33); multi-chain portfolio expansion. + +## Effort (rough, CC-assisted) +E0 ~0.5d · E1 ~1d · E2 ~1d · E3 ~2d · E4 ~1d · **E5 ~3-4d (engine + UI + tests + adversarial review)** · +E6 ~2d · E7 ~1-2d · E8 ~1d. diff --git a/docs/screenshots/audit-170/01-unlock.png b/docs/screenshots/audit-170/01-unlock.png new file mode 100644 index 0000000..48dc25a Binary files /dev/null and b/docs/screenshots/audit-170/01-unlock.png differ diff --git a/docs/screenshots/audit-170/02-home-dark.png b/docs/screenshots/audit-170/02-home-dark.png new file mode 100644 index 0000000..4344839 Binary files /dev/null and b/docs/screenshots/audit-170/02-home-dark.png differ diff --git a/docs/screenshots/audit-170/03-home-agent-presence.png b/docs/screenshots/audit-170/03-home-agent-presence.png new file mode 100644 index 0000000..980867d Binary files /dev/null and b/docs/screenshots/audit-170/03-home-agent-presence.png differ diff --git a/docs/screenshots/audit-170/04-home-syncing.png b/docs/screenshots/audit-170/04-home-syncing.png new file mode 100644 index 0000000..7142b84 Binary files /dev/null and b/docs/screenshots/audit-170/04-home-syncing.png differ diff --git a/docs/screenshots/audit-170/05-command-palette.png b/docs/screenshots/audit-170/05-command-palette.png new file mode 100644 index 0000000..35341ae Binary files /dev/null and b/docs/screenshots/audit-170/05-command-palette.png differ diff --git a/docs/screenshots/audit-170/06-send-compose.png b/docs/screenshots/audit-170/06-send-compose.png new file mode 100644 index 0000000..8dded7c Binary files /dev/null and b/docs/screenshots/audit-170/06-send-compose.png differ diff --git a/docs/screenshots/audit-170/07-send-clearsigning.png b/docs/screenshots/audit-170/07-send-clearsigning.png new file mode 100644 index 0000000..51ca88e Binary files /dev/null and b/docs/screenshots/audit-170/07-send-clearsigning.png differ diff --git a/docs/screenshots/audit-170/08-shield-compose.png b/docs/screenshots/audit-170/08-shield-compose.png new file mode 100644 index 0000000..1c6f125 Binary files /dev/null and b/docs/screenshots/audit-170/08-shield-compose.png differ diff --git a/docs/screenshots/audit-170/09-swap-compose.png b/docs/screenshots/audit-170/09-swap-compose.png new file mode 100644 index 0000000..0f2f69c Binary files /dev/null and b/docs/screenshots/audit-170/09-swap-compose.png differ diff --git a/docs/screenshots/audit-170/10-receive.png b/docs/screenshots/audit-170/10-receive.png new file mode 100644 index 0000000..b5af6a6 Binary files /dev/null and b/docs/screenshots/audit-170/10-receive.png differ diff --git a/docs/screenshots/audit-170/11-agent-surface.png b/docs/screenshots/audit-170/11-agent-surface.png new file mode 100644 index 0000000..573a143 Binary files /dev/null and b/docs/screenshots/audit-170/11-agent-surface.png differ diff --git a/docs/screenshots/audit-170/12-activity-empty.png b/docs/screenshots/audit-170/12-activity-empty.png new file mode 100644 index 0000000..0f199ca Binary files /dev/null and b/docs/screenshots/audit-170/12-activity-empty.png differ diff --git a/docs/screenshots/audit-170/13-activity-needsyou.png b/docs/screenshots/audit-170/13-activity-needsyou.png new file mode 100644 index 0000000..708dbf9 Binary files /dev/null and b/docs/screenshots/audit-170/13-activity-needsyou.png differ diff --git a/docs/screenshots/audit-170/14-agent-approval-card.png b/docs/screenshots/audit-170/14-agent-approval-card.png new file mode 100644 index 0000000..216db2a Binary files /dev/null and b/docs/screenshots/audit-170/14-agent-approval-card.png differ diff --git a/docs/screenshots/audit-170/15-settings.png b/docs/screenshots/audit-170/15-settings.png new file mode 100644 index 0000000..de91e87 Binary files /dev/null and b/docs/screenshots/audit-170/15-settings.png differ diff --git a/docs/screenshots/audit-170/16-onboard-welcome.png b/docs/screenshots/audit-170/16-onboard-welcome.png new file mode 100644 index 0000000..13703b9 Binary files /dev/null and b/docs/screenshots/audit-170/16-onboard-welcome.png differ diff --git a/docs/screenshots/audit-170/17-onboard-secure.png b/docs/screenshots/audit-170/17-onboard-secure.png new file mode 100644 index 0000000..48ac06e Binary files /dev/null and b/docs/screenshots/audit-170/17-onboard-secure.png differ