# Slice 1 S5: WebUI views for inbox, tasks, agents and trails (design note) Dewey, 2026-10-04. Row 40, #1522. Design only: no code, no commit. S5 follows S4, so nothing here starts the build. Revision 2, same day: lead decision 56 (c9c1699a) answered Q1 to Q5 and accepted all five recommendations. Sections 1, 3.2, 3.5, 5 and 7 now say what was decided. Section 8 is a new finding for the Q1 module. The mockup is in `mockups/slice1/`, checked by `checks/slice1-verify.mjs`. Its fixtures follow schema v3a, and the verify script loads every row into that schema. Revision 3, same day. Three things changed: - Schema v3b (179ffe35, lead decision 60) landed. It adds the `task_current` view and builds `tasks_open` on it (lead decision 59). The fixture check now loads v3b and reads current state from those two views. It agrees with the page on every task, #42 included. Section 8 records the ruling. - The mockup is reshaped to the Q1 module (Sage's Q1 shapes). `data.js` holds raw v3b rows written the way the candidate broker writes them. `mockups/slice1/q1.js` ports the four reader verbs, and `reads(S)` in `app.js` builds the views only from what those verbs return. A new check runs Rocko's own candidate reader over the same rows and compares. Section 1 and section 7 describe this. - Inbox rows now carry `authorization {action, target, approvalChoice}` from the `decision.raise` event, and the views show it (section 3.1). What the page needs and no Q1 verb returns is labelled on the page and listed for Rocko in section 9. Sources, pinned by sha256 prefix: - brief `docs/plans/2026-10-04_slice-1.md` (655e1290, commit 43c48d7a), S5 section and the shared rules; - data model note (948b94ce), addendum A (0b36acb0), addendum B (14e747f5). B wins over A, and A wins over the note; - schema v3 `agents/darkwing/work/slice1-proto/schema-v3.sql` (084d0a07, commit 7ed83178, lead decision 55), replaced by schema v3a `schema-v3a.sql` (d55e41fd), which S2 builds on, then by schema v3b `schema-v3b.sql` (179ffe35, lead decision 60). The mockup follows v3b; - `packages/bus` as committed in d27042fa (S2c, row 44; S2 was 38828a2c, S2b 4afab552): broker.mjs 51e03b33, store.mjs e69cb395, views.mjs 446b6da4; - PRD draft 0.4 `docs/prd/mosaic-stack.md` (d907705a): REQ-WEB-1, REQ-DEC-1 to 4, REQ-CLI-1, REQ-EVT-1; round 3, 7B; - DEFERRED.md, "CHAT-03 I1 round 2 follow-ups, for the I3 brief". Not read on purpose: today's queue. The views follow the data model. ## 1. What the WebUI reads, and through what REQ-WEB-1 says the WebUI shows the same data as the CLI. The S4 brief says every CLI command reads through the broker and never opens `bus.sqlite`. The WebUI follows the same rule: - The browser never talks to the broker. The WebUI server (`packages/webui`) calls the broker's read verbs over the local socket and serves JSON to the page, the way it proxies the board today. - The WebUI never opens `bus.sqlite`, the business file or a token file. Bucket titles, bot names and launch limits come from the broker, which already loaded them. - The WebUI adds no write route for slice 1 data (section 6). One set of read functions serves both renderers, so the two can't drift. Lead decision 56, Q1: the module lives in `packages/bus`, Rocko owns it, and both the CLI and the WebUI import it. The CLI formats text and the WebUI server serves JSON. The module, `packages/bus/src/views.mjs`, is a thin client: `inbox`, `tasks`, `agents` and `trail(subject)`, each one broker call. The mockup's `mockups/slice1/q1.js` stands in for those four calls over fixture rows, and the verify script checks it returns what the broker at commit d27042fa returns. `reads(S)` in `mockups/slice1/app.js` builds the view objects from the stand-in alone. A second object, `missing(S)`, holds what no verb returns. Each entry names its source, and every renderer that shows one labels it "not in the Q1 module". When Rocko pins an example fixture, it replaces `data.js`. ## 2. Routes Console keeps its shell, its palettes and its keyboard rules. The left column gains a section list above the project tree. Hash routes work with the back button, as the mockups did. | Route | View | Main source | |---|---|---| | `#/` | Control board (unchanged) | the board scan | | `#/inbox` | Decisions routed to the human, open first | `decisions`, `decision_events` | | `#/inbox/` | One decision, its options and its history | same, plus `supersedes` chain | | `#/tasks` | Tasks by state | latest `task_snapshots` per task, `task.*` events | | `#/tasks/` | One task and its trail | same, plus the trail (section 3.4) | | `#/agents` | Role instances, holders and live sessions | business file via broker, `role_claims`, `session.*` events | | `#/trail/task/`, `#/trail/decision/` | The ordered trail | events, decisions, messages, deliveries | | `#/conversation/...` | CHAT-02 history and CHAT-03 drive (existing) | the board and `packages/conversation` | A task ref in a URL is `vikunja:/`, encoded. The `#12` a person sees in Vikunja is display only (addendum A section 1). ## 3. The views Every view keeps the Console states: loading, empty, error with the last good data kept and a warning, and refusal with the reason. All agent-authored text (questions, option text, recommendations, message bodies, task titles and descriptions) is shown as text under the CHAT-02 rules: Markdown stays as source, and controls and bidi marks show as visible symbols. ### 3.1 Inbox - Rows: decisions with `route_to = 'human'` and no closing row (`resolved`, `withdrawn`, `expired`). Blocking first (the `urgent_inbox` view), then oldest first. The note's rule is "gated first, then oldest", and every human-routed decision is gated, so `blocking` is the useful split. - Each row: question, class and action, raised by role and run, age, `task_ref` and `requirement_ref` as links, a "blocking" badge, and whether a DM went out (a `deliveries` row with transport `discord-dm`). - Detail: the options as a numbered list of key and text, the recommendation marked, the `seen` history, and earlier versions through `supersedes`, each shown `withdrawn`. - Approval context, from the row's `authorization`: the card and the detail say "Choosing “” approves on .", and the approving option carries an "approves " badge. The approving choice need not be the recommendation: in the fixture, the conflict on #48 recommends `queue`, and `vikunja` approves `task.close`. A decision without a recorded raise says no option is marked as approving. - Resolution stays in the CLI (REQ-DEC-3). The detail shows the exact command, `mosaic decide `, one per option, with a Copy button. Copy puts text on the clipboard and does nothing else. There is no Resolve button, disabled or otherwise. - A second tab, "Routed to roles", lists open cross-role decisions by arbiter (`pm`, `cto`). It's read-only too. Closed decisions get a "Recent" list: choice, by, via and time open. ### 3.2 Tasks - Rows: the latest snapshot of each task in the business, grouped by the five bucket titles (`todo`, `in-progress`, `in-review`, `blocked`, `done`). `done` is collapsed and shows the last 20. - Columns: `#index` and title, assignee (bot name), requirement id (from `task.created`, not the description line), due date, priority, percent done, queue row if the task has one, and the time of the last change. - Markers, each linked to its event: - "changed in Vikunja" for a task in `task_external_changes`. A task the broker never wrote is always in that view (addendum B section 5), so the marker reads "not created by the stack" when no `self` snapshot exists; - "conflict" for an unresolved `task.conflict`, linked to the decision raised to the PM; - "missing" with the reason (`moved`, `not-found`, `no-access`) for a tombstone, and the new project id for `moved`. - Freshness: a line under the heading, "Vikunja last read