Inbox V3 β The Clarity Pass (Superhuman, not Outlook)
Status: DRAFT (under /autoplan review)
Branch: claude/compassionate-golick-2d4aaf
Builds on: docs/designs/inbox-is-the-product.md (V2, shipped) and the live
apps/webapp/src/pages/inbox.astro + components/inbox/*.
Problem (one sentence)
Section titled βProblem (one sentence)βThe inbox V2 shipped the data model and the cockpit, but the surface still reads
like Outlook: every row carries ~11 competing visual objects, and one /inbox
route serves two different humans (the clerk who works items and the supervisor
who watches a team), so both see controls that arenβt for them.
The two users β make the split structural, not conditional
Section titled βThe two users β make the split structural, not conditionalβ| Clerk (CSR / operator) | Supervisor (manager / order desk) | |
|---|---|---|
| Job | Work my queue, one item at a time | Watch the team, keep SLAs green |
| Question on open | βWhatβs mine? What can I grab?" | "Who has what? Whatβs breaching?β |
| Primary action | claim β review β submit | assign, escalate, report |
| Ideal surface | fast list, keyboard-driven | table / kanban with people columns |
Today these are merged behind ?view= params and capability checks inside one
page. The clerk sees a disabled Dashboard button and a βUnified queueβ tab they
have no use for; the supervisor reaches their view through a URL param nobody
discovers. The conflation is the root cause of the clutter β every band-aid
control (scope dropdown, All tab, view toggle) exists to serve both users from
one surface.
Decision: split the route
Section titled βDecision: split the routeβ/inboxβ Clerk surface only. No supervisor controls, no disabled buttons, no?view=. This isInboxCockpit./teamβ Supervisor surface. First-class table + kanban (todayβsSupervisorInboxRoot), team selector, SLA threshold, export. Gets its own left-rail nav entry, gated oninbox.view.team.- A supervisor working their own queue uses
/inboxlike everyone else;/teamis for the monitoring job, not a second copy of the list.
Server-side API gates already re-validate caps, so this is mostly routing: a new
astro page that renders SupervisorInboxRoot + CockpitShell, and removal of
the supervisor branches from inbox.astro.
The row β three signals, not eleven
Section titled βThe row β three signals, not elevenβToday InboxItemRow.tsx renders, per row: checkbox, source icon, sender,
title, tier badge (T1/T2/T3), PO number, broker, duplicate flag, status badge,
ββ Extractedβ badge, ββ Errorβ badge, assignee label, SLA time. Eight of those
are colored. The user cannot triage by scanning β they must read every row.
V3 row contents:
β [sender] [title / subject] [time] ββ left-edge SLA strip (3px, color only)Three things: sender, title, time. The left-edge is a 3px vertical strip whose color encodes urgency/health, with zero text:
| Strip color | Meaning | Token |
|---|---|---|
| transparent | no SLA threshold configured | β |
| green | plenty of SLA budget | --color-success |
| amber | approaching SLA | --color-warning |
| red | breached, or item in error state | --color-danger |
Everything else moves to where it belongs:
| Removed from row | New home |
|---|---|
| Status badge (new/in_progress/awaiting_review/submitted) | Group header β rows are already grouped by uxState; the header says βAwaiting Review (3)β, so the per-row badge is pure redundancy |
| β Extracted / β Error badges | Strip color (error = red strip) + detail pane |
| Tier T1/T2/T3 | Detail pane header |
| PO number | Detail pane (prominent) |
| Broker name | Detail pane |
| Assignee label | Detail pane. In the list, assigned simply = not in the βAvailableβ queue. That IS the signal. |
| Source icon (email/upload/clone) | Removed from the scan path; upload/clone keep a tiny muted pip only |
Exceptions that earn a per-row chip (rare, judgment-bearing, not a normal
state): Dup? (amber β needs human check before claiming), Test (amber β
changes how you treat the submission), Price mismatch (the one extraction
exception worth surfacing pre-open). Nothing else.
Unread stays as the existing left dot + bold sender. New rows keep the subtle
bg-blue-50/30 tint.
Naming β speak the operatorβs language
Section titled βNaming β speak the operatorβs languageβPersonal and Organization are data-model words, not operator words. A CSR
thinks βmy workβ and βstuff I can grab,β not βthe organization queue.β
| Today | V3 | Why |
|---|---|---|
| Personal | Mine | items assigned to me |
| Organization | Available | unassigned, anyone can claim |
| Branch | Branch: [name] | unchanged, already clear |
| All | All | supervisor-only, lives on /team now |
Two string changes in InboxTabNav.tsx plus the smart-default logic in
inbox.astro (which already routes an empty personal queue to the org queue β
reword the comments, keep the behavior).
Queue counts β already computed, just unhidden
Section titled βQueue counts β already computed, just unhiddenβinbox.astro already computes queueCounts {user, organization, all} server-side
and passes it to InboxCockpit. Render them inline on the queue pills:
Mine (3) Available (12)Counts are scoped to the current lifecycle view (so βMine 3β matches what the Mine tab shows) and deliberately not narrowed by search/filter β theyβre stable queue-size badges, not filtered-result counts. No new query.
Keyboard-first β make the shortcuts visible, not modal-only
Section titled βKeyboard-first β make the shortcuts visible, not modal-onlyβThe cockpit already has full keyboard nav (KEYBOARD_HINTS_LIST, the ? help
modal). The gap is discoverability: today you only learn the keys by opening a
modal. Superhuman teaches you in context.
- On the focused row, show ghost-text hints at the right edge in
--om-surface-ink-3at 10px:earchive Β·rreview Β·zsnooze. They appear on focus/hover, never shout, and vanish otherwise. - Keep the
?modal as the full reference. /focuses search, hinted near the search box.
Pure additive UI, zero behavior change β the handlers already exist.
Toolbar β collapse three bands into one
Section titled βToolbar β collapse three bands into oneβToday, above the list: (1) tab nav, (2) search + lifecycle + date + dashboard + filters + refresh, (3) a collapsible filter drawer. For a clerk thatβs too much chrome. V3 clerk toolbar:
- Queue pills (always visible, with counts):
Mine (3) Β· Available (12) - Search β one debounced input, no submit button
- Lifecycle β Active / Archived / Trash as a compact icon-toggle group
Active filter presets become dismissible chips above the list (visible only
when a preset is on), instead of living hidden in a drawer. The drawer stays for
power users but isnβt the default surface. The supervisor toolbar (/team) is a
separate component with team selector / SLA / export β not this one.
Status model β pick one source of truth
Section titled βStatus model β pick one source of truthβTwo parallel status systems exist: raw item.status and derived item.uxState
(deriveUxState). The row shows both β the raw badge plus the uxState group it
sits under. V3: the group header is the status. The per-row badge is dropped
for normal states; only the three exception chips above remain.
Scope β what is NOT in this plan
Section titled βScope β what is NOT in this planβ- No data-model changes. workItems schema, SLA fields, polling all stay.
- No detail-pane rework. The split-pane review surface is V2 and stays.
- No supervisor feature changes.
/teamis the existingSupervisorInboxRootmoved to its own route, not a redesign of it. - No new endpoints. Queue counts and caps already exist server-side.
- Mobile detail layout, onboarding banner, dashboard internals: unchanged.
What already exists (leverage map)
Section titled βWhat already exists (leverage map)β| Sub-problem | Existing code |
|---|---|
| Supervisor surface | components/inbox/SupervisorInboxRoot.tsx, CockpitShell.astro |
| Clerk list + keyboard | InboxCockpit.tsx, cockpit/lib.ts (KEYBOARD_HINTS_LIST, groupItemsByState) |
| Row | InboxItemRow.tsx (and cockpit/CockpitRow.tsx) |
| Queue counts | inbox.astro queueCounts (already passed as prop) |
| uxState grouping | lib/inbox/work-item-ux-state.ts deriveUxState |
| SLA color logic | getSlaDisplay in InboxItemRow.tsx (reuse the ratioβcolor map for the strip) |
| Tab rename | InboxTabNav.tsx tabLabel |
Phases (ORIGINAL β superseded by the reframe below)
Section titled βPhases (ORIGINAL β superseded by the reframe below)βRoute splitβ DEFERRED (see Phase-1 outcome)Row simplification (hard strip)β DEFERRED (re-spec as density toggle)- Naming + counts β SHIP (premise-independent)
- Toolbar collapse + keyboard hints β partial SHIP (keyboard hints only; toolbar collapse folded into the deferred clerk-surface work)
Success criteria
Section titled βSuccess criteriaβ- A clerk on
/inboxsees zero supervisor controls and zero disabled buttons. - A row in the list shows exactly: sender, title, time, and a color strip (plus at most one exception chip). Measured by reading the rendered DOM.
- βMineβ and βAvailableβ replace βPersonalβ/βOrganizationβ everywhere a clerk sees.
- Queue counts visible on the pills without opening anything.
- A new clerk can discover
e/r/z//without opening the?modal.
/autoplan Review
Section titled β/autoplan ReviewβPhase 1 β CEO Review (Strategy & Scope)
Section titled βPhase 1 β CEO Review (Strategy & Scope)βCEO Dual Voices β Consensus Table
Section titled βCEO Dual Voices β Consensus Tableββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Dimension Claude Codex Consensus βββββββββββββββββββββββββββββββββββββ βββββββ ββββββ ββββββββββββββ 1. Premise valid (clutter/role = NO NO CONFIRMED-NO the real adoption blocker)? (unvalidated) 2. Right problem to solve? PARTLY NO DISAGREEβlean NO 3. Route split reduces confusion? NO NO CONFIRMED-NO (vs fragments dual-role supervisor) (needs design) 4. Row strip preserves triage? NO NO CONFIRMED-NO (tier/PO/broker are scan signals) (info debt) 5. "Superhuman" is the right star? NO NO CONFIRMED-NO (B2B work-queue β consumer email) (cargo-cult) 6. Alternatives explored? NO NO CONFIRMED-NO (density toggle / progressive) (dismissed)βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ5/6 CONFIRMED concerns, 1 lean-negative. This is unusually strong cross-model agreement against the plan as written β it is a User Challenge, not a set of taste decisions.
CODEX SAYS (CEO β strategy challenge)
Section titled βCODEX SAYS (CEO β strategy challenge)β10 findings (2 critical, 5 high, 3 medium). Highlights:
- C1 (critical): Wrong root cause. Repoβs own
docs/product/email-inbox-ux.mdnames the real adoption killers β false-positive noise (L77), lack of unified oversight/delegation (L104), duplicate work because Ordermatic canβt yet replace source-inbox actions (L160). Reframe around βqueue trustβ + βworkflow replacement,β prove drop-off before spending a quarter on visual cleanup. - C2 (critical):
/inboxvs/teamis clean for the designer, not the supervisor β who is a hybrid operator triaging their own queue while rebalancing others. Route split adds constant context switches + fragmented state. If you split, require instant mode-switch + preserved selection/filters. - H3: βSuperhumanβ is the wrong north star for a B2B exception console. Replace with βhigh-trust order deskβ; measure throughput, mis-triage, supervisor intervention β not elegance.
- H4: Row strip is information debt; same red strip = SLA breach OR extraction error collapses two different actions into one ambiguous cue. Donβt rely on color alone.
- H6: Density is a product capability, not a design failure β offer Focused/Dense, persist per-user, instrument which correlates with faster resolution.
- M9: Success criteria are UI assertions, not outcomes. Require claim-to-open time, items/active-hour, opened-then-abandoned %, before approval.
CLAUDE SUBAGENT (CEO β strategic independence)
Section titled βCLAUDE SUBAGENT (CEO β strategic independence)β10 findings (3 critical). Key independent code finding:
- F3 (critical): The clean-split premise is contradicted by the code.
SupervisorInboxRootβsViewSwitcheralready has a'cockpit'mode and the supervisor table/kanban links point back to/inbox?inbox_view=.... The two surfaces are one continuum with a shared switcher today; the dual-role lead flips a view toggle in place. A route split forces a route change (/teamβ/inbox), losing scroll/filters/selection β for the most valuable operator. - F1 (critical): No usage evidence row clutter is the blocker. For an ops tool the dominant risks are extraction accuracy, claim/submit latency, trust in AI output β not row density.
- F6 (critical): Tier IS the triage signal; PO/broker are how a CSR recognizes βthe Walmart reorderβ without reading the subject. Color-only strip fails ~8% colorblind. Honest version is a density toggle, not forced minimalism.
- Both models agree the surviving cheap wins: naming (PersonalβMine, OrganizationβAvailable), rendering existing queue counts, and in-context keyboard ghost-hints. Ship those independently; hold the route split + row strip.
What already exists (leverage map β CEO)
Section titled βWhat already exists (leverage map β CEO)β| Sub-problem | Existing code | Implication |
|---|---|---|
| Clerk/supervisor βseparationβ | SupervisorInboxRoot ViewSwitcher has cockpit mode; links target /inbox | Surfaces already entangled β split is a refactor, not routing |
| Real adoption levers | docs/product/email-inbox-ux.md (trust, delegation, writeback) | The strategic moat is elsewhere |
| Cheap wins | InboxTabNav labels, queueCounts prop, KEYBOARD_HINTS_LIST | Shippable today, reversible, premise-independent |
NOT in scope (CEO confirms, plus new deferrals)
Section titled βNOT in scope (CEO confirms, plus new deferrals)β- Detail-pane rework, data-model changes, supervisor feature changes (as written).
- New deferral flagged by both models: queue-trust / false-positive reduction and source-inbox workflow replacement β the likely real levers β are out of this plan and should be weighed against it, not buried under it.
Dream-state delta
Section titled βDream-state deltaβCURRENT: shipped V2, cluttered row, conflated route. THIS PLAN: cleaner clerk list, split route, renamed queues. 12-MONTH IDEAL (per both models): a trusted order desk where CSRs stop falling back to Outlook because the queue is accurate and the workflow is complete β not merely prettier. This plan moves the cosmetic needle; it does not move the trust needle. The cheap wins (naming/counts/hints) are pure-positive; the contested half (split + strip) may be motion, not progress, on an unvalidated premise.
Premise Gate Outcome β REFRAMED SCOPE (user decision)
Section titled βPremise Gate Outcome β REFRAMED SCOPE (user decision)βUser chose: Reframe β ship cheap wins, hold bold half. Both models recommended this.
SHIP NOW (reversible, premise-independent):
- S1 β Naming.
PersonalβMine,OrganizationβAvailableinInboxTabNav.tsxtabLabel. (Codex M8 caveat: confirm βAvailableβ only labels items any clerk may actually claim; if claimability isnβt guaranteed, use βUnassigned.β Resolve in Design.) - S2 β Queue counts on pills. Render existing
queueCounts {user, organization}inline:Mine (3) Β· Available (12). Show count semantics so β12 but I see 4β confusion (Codex M8) canβt happen. - S3 β In-context keyboard ghost-hints. Surface
e/r/z//as muted ghost text on the focused row; keep the?modal as full reference. Purely additive.
HOLD (deferred until unblock conditions met):
- H-Route-Split β blocked on: design the dual-role supervisorβs in-place
cockpitβteam mode-switch (preserve scroll/filters/selection) AND untangle the
shared
ViewSwitcher(SupervisorInboxRootalready has acockpitmode whose links point at/inbox?inbox_view=...). Not βjust routing.β - H-Row-Density β blocked on: (a) one piece of real CSR usage/qualitative data on what slows triage, and (b) re-spec as a Focused/Dense toggle (default Focused = 3-signal; Dense keeps tier/PO/broker/assignee), persisted per-user, instrumented. No color-only state (colorblind + SLA-vs-error ambiguity). No irreversible deletion of the data path.
Both modelsβ strategic note (logged, not actioned here): the larger adoption
levers per docs/product/email-inbox-ux.md are queue trust (false-positive
reduction), unified oversight/delegation, and source-inbox workflow replacement.
Tracked as separate scope, weighed against (not buried under) this polish sprint.
Decision Audit Trail
Section titled βDecision Audit Trailβ| # | Phase | Decision | Classification | Principle | Rationale |
|---|---|---|---|---|---|
| 1 | CEO | Run both dual voices | Mechanical | P6 | Always run available voices |
| 2 | CEO | Premise (clutter/role = blocker) is unvalidated | β | β | Both models CONFIRMED-NO; surfaced at gate |
| 3 | CEO | Reframe to ship-cheap / hold-bold | User Challenge | user decision | User chose A at premise gate |
| 4 | CEO | Ship S1 naming | Auto (post-reframe) | P3/P5 | Reversible string change, premise-free |
| 5 | CEO | Ship S2 counts | Auto | P3 | Data already computed; just render |
| 6 | CEO | Ship S3 keyboard hints | Auto | P1/P5 | Additive, zero behavior change |
| 7 | CEO | Defer route split | Auto | P6 | Both models: not just routing + dual-role cost |
| 8 | CEO | Defer hard row strip; re-spec as toggle | Auto | P5 | Reversible > irreversible on unvalidated premise |
Phase 2 β Design Review (Dual Voices)
Section titled βPhase 2 β Design Review (Dual Voices)βDesign Consensus Table
Section titled βDesign Consensus Tableββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Dimension Claude Codex Consensus βββββββββββββββββββββββββββββββββββββ βββββββ ββββββ ββββββββββββββ 1. Targets the live surface? NO NO CONFIRMED-NO (S1/S2/S3 hit legacy flag-off files) (re-point cockpit) 2. "Available" honest for the queue? NO NO CONFIRMED-NO β "Unassigned" 3. Count semantics unambiguous? NO NO CONFIRMED-NO (add "N of M") 4. Keyboard hints land on real keys? NO NO CONFIRMED-NO (r,/ dead) 5. Hint placement collision-free? NO NO CONFIRMED-NO (right edge taken) 6. A11y (contrast/aria) sufficient? NO NO CONFIRMED-NO (10px ink-3 fails) 7. Density-toggle default Focused Dense DISAGREE β taste (deferred)βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββKey resolved design decisions
Section titled βKey resolved design decisionsβ- N-1 Naming:
OrganizationβUnassigned(not βAvailableβ). Both flag βAvailableβ over-claims claimability; repo already uses βUnassignedβ preset and empty-state copy βNo unclaimed orders.βPersonalβMine(unambiguous). - QC-1 Counts: pill = stable queue total; when a filter/search is active, render
a separate
Showing N of Mline (role="status" aria-live="polite"), mono/tabular digits. Never make the pill track filtered results. - KH Hints: do NOT hint
r(no binding) or/until wired. Prefer extending the always-onListFooterlegend (KEYBOARD_HINTS_LIST) over per-row right-edge hints (the right edge already holds Claim/snooze/SLA). If a focused-row hint is added, put it in the SLA/date column, focus-only, β₯12px, β₯4.5:1 contrast (the--om-surface-ink-3token is a light-surface token; on the dark cockpit pane usetext-white/45-55). - DISAGREE (taste, deferred): density-toggle default β Claude=Focused (clean default), Codex=Dense (donβt strip scan signals from existing operators).
Phase 3 β Eng Review (Dual Voices)
Section titled βPhase 3 β Eng Review (Dual Voices)βEng Consensus Table
Section titled βEng Consensus Tableββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Dimension Claude Codex Consensus βββββββββββββββββββββββββββββββββββββ βββββββ ββββββ ββββββββββββββ 1. Rename scope complete? NO NO CONFIRMED-NO (6 surfaces) 2. "N of M" has a trustworthy M? NO NO CONFIRMED-NO (page vs total) 3. "/" binding edge-case safe? NO NO CONFIRMED-NO (IME/CE/modifier) 4. withCount fits aria/tabular work? NO NO CONFIRMED-NO (restructure) 5. Test coverage sufficient? NO NO CONFIRMED-NO (extract+test) 6. SSR/hydration safe? YES YES CONFIRMED-YES (client:only)βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββArchitecture note β the rename is a vocabulary migration, not a string swap
Section titled βArchitecture note β the rename is a vocabulary migration, not a string swapβSix clerk-visible surfaces carry Personal/Organization, several abbreviated so a
naive find/replace silently skips them:
InboxCockpit.tsx:624QueueSwitcher (LIVE) βwithCount('Personal'|'Organization')InboxTabNav.tsx:25-32tabLabel(legacy)InboxSidebar.tsx:20-39sidebar labels (legacy β missed by subagent first pass)InboxToolbar.tsx:301-310stats noun'personal'/'org'(legacy)InboxItemRow.tsx:253-266All-tab origin chip β abbreviated'Org', not βOrganizationβInboxEmailRow.tsx:111-120All-tab origin chip β the ACTUAL flag-off row path (!useWorkItemsrendersInboxEmailRow, perinbox.astro:855/InboxPage.tsx:623) Fix (both voices): centralize queue/origin labels in ONE shared helper; every consumer reads from it so live and legacy canβt fork. Grep gate before merge:grep -rn "Personal\|Organization" components/inboxreturns zero clerk-visible label hits.
βN of Mβ β define M before building (HIGH, both)
Section titled ββN of Mβ β define M before building (HIGH, both)βThe cockpit has no trustworthy queue-total in scope: items.length is the loaded page
(β€50, inbox.astro:617 offset), queueCounts ignores lifecycle/branch/search by design
(inbox.astro:620-661), flatList is a client-only pre-debounce filter. Resolution:
N = flatList.length (filtered); M = totalWorkItemCount (server count for the committed
query) and ONLY after the query is server-synced; make copy page-aware when page>1
(Showing 51-100 of 120) or suppress the total until synced. Never compare against
queueCounts. Gate the line on an active filter so the clean case stays clean.
β/β binding β guards required (HIGH, both)
Section titled ββ/β binding β guards required (HIGH, both)βExisting handler guards INPUT/SELECT/TEXTAREA only (InboxCockpit.tsx:302-305).
HelpDrawer.tsx:99-103 already shows the correct pattern (adds isContentEditable).
New branch, ordered before generic single-char branches:
} else if (e.key === '/' && !e.metaKey && !e.ctrlKey && !e.altKey) { e.preventDefault(); searchRef.current?.focus();Plus add if (e.isComposing || e.keyCode === 229) return; and target.isContentEditable
to the top-of-handler guard. preventDefault only when actually focusing search.
withCount restructure (both)
Section titled βwithCount restructure (both)βwithCount bakes label+count into one string β blocks per-pill aria-label and
mono/tabular digit styling. Replace with structured option data {label, count, ariaLabel}
(or a count-badge subcomponent); add optional ariaLabel to SegmentedOption
(defaults to label; lifecycle/view switchers pass through unchanged). Keep the
aria-live βN of Mβ node always mounted (empty when unfiltered) so SRs announce it.
Final β Corrected Shipping Scope + Implementation Tasks
Section titled βFinal β Corrected Shipping Scope + Implementation TasksβThe reframe (ship cheap wins) plus the design/eng corrections collapse the shippable work to a small, precise set. The original plan pointed at legacy files; the live target is the cockpit.
- T1 (P1, human ~2h / CC ~20m) β Centralized label rename. Add a shared
queueLabel/origin-label helper; renamePersonalβMine,OrganizationβUnassignedacross all 6 surfaces (cockpit QueueSwitcher, InboxTabNav, InboxSidebar, InboxToolbar stats, InboxItemRow'Org'chip, InboxEmailRow chip). Grep gate must pass. Tests: helper unit + legacy-flag-off manual check. - T2 (P2, human ~3h / CC ~30m) β βShowing N of Mβ filtered indicator. Structured
count
{label,count,ariaLabel}replaceswithCount; mono/tabular digits;role=status aria-live=politeline, always mounted, filled only when a filter is active; M = server-syncedtotalWorkItemCount, page-aware (Showing 51-100 of 120), neverqueueCounts. Tests:formatFilteredCountunit (not-filtered/page1/page>1/unsynced/edges). - T3 (P2, human ~2h / CC ~20m) β Wire
/+ footer hint. New guarded/branch (!meta/ctrl/alt,isComposing/keyCode 229,isContentEditable); add['/', 'search']toKEYBOARD_HINTS_LIST. Do NOT addr. Extract key dispatch to a pure helper for testing. Tests: β/β guard matrix. - T4 (P3, human ~1h / CC ~15m) β Pill a11y. Optional
ariaLabelonSegmentedOption(defaults tolabel);"Mine, 3 items"/"Unassigned, 12 items".
Deferred (gated, not in this sprint)
Section titled βDeferred (gated, not in this sprint)β- D-Route-Split β design dual-role supervisor in-place cockpitβteam mode-switch
(preserve scroll/filters/selection) + untangle shared
ViewSwitcherbefore any/teamroute. - D-Density-Toggle β Focused/Dense segmented control, localStorage
inbox:density, per-user later, instrumented (resolution-time by density). OPEN TASTE: default Focused (Claude) vs Dense (Codex, βdonβt strip existing operatorsβ). Blocked on one piece of real CSR usage data. - D-Queue-Trust (strategic, separate scope) β false-positive reduction, delegation,
source-inbox writeback per
docs/product/email-inbox-ux.mdβ the likely real adoption levers.
| # | Phase | Decision | Classification | Principle | Rationale |
|---|---|---|---|---|---|
| 9 | Design | βAvailableβββUnassignedβ | Auto | P5 (explicit/honest) | Both voices; over-claims claimability |
| 10 | Design | Counts: pill=total + βN of Mβ line | Auto | P1 | Kills β12 but I see 4β |
| 11 | Design | Drop r hint; gate / | Auto | P5 | Donβt teach dead keys |
| 12 | Design | Density default Focused vs Dense | Taste | β | DISAGREE β final gate (deferred work) |
| 13 | Eng | Rename = centralized helper, 6 surfaces | Auto | P4 (DRY) | Both voices; prevents fork |
| 14 | Eng | M = server total, page-aware | Auto | P1 | queueCounts/items.length untrustworthy |
| 15 | Eng | / IME+CE+modifier guards | Auto | P1 | Real correctness bugs once reachable |
| 16 | Eng | Restructure withCount for aria/tabular | Auto | P5 | Baked string blocks a11y |
| 17 | Eng | Extract pure helpers + unit test | Auto | P1 | Handler currently untested |