--- id: CUST-WP-0030 type: workplan title: "Dashboard Entity List UX" domain: custodian repo: the-custodian status: done owner: custodian topic_slug: custodian created: "2026-03-29" updated: "2026-03-29" state_hub_workstream_id: "9d8e1c33-2067-4593-a5d8-d28dda3b1d21" --- # Dashboard Entity List UX ## Goal Make every entity table in the dashboard navigable and self-documenting. Two new UI primitives: 1. **REF cell** — running row number (1-based); click copies a deep-link (`/data//`) to the clipboard; double-click opens the link in a new tab. 2. **Name cell** — second column, titled after the record type; shows the entity name truncated at 80 chars with full-name tooltip on hover. Support these cells with a landing page (`/data/[type]/[id]`) that renders a key-value view of every field in the record, each key decorated with a `` (one-sentence description + link to the relevant help-doc section). Pilot all three features on the **Token Cost** page, then extend to other entity tables in later workplans. ## Background Current entity tables show only IDs (often truncated UUIDs) with no way to navigate to a record detail view or discover what a field means. For a person reviewing agent activity the tables are hard to parse. Providing a running reference number, an entity name column, and a click-through detail page substantially lowers the cognitive load. The `` custom element already exists in `src/components/help-tip.js` and is used on several pages — the field-help registry will reuse it as the rendering layer. ## Implementation Plan The pilot targets the Token Cost page. Observable Framework supports parameterised pages via `[param].md` file naming. The API already exposes `GET /token-events/?task_id=…` and `GET /token-events/summary/`; a per-record GET endpoint is needed before the landing page can work. ### Component layer - `src/components/ref-cell.js` — exports `refCell(index, type, id)` that returns an `HTMLElement` with click/dblclick handlers. - `src/components/field-help.js` — exports `FIELD_HELP` map and `fieldRow(key, value)` helper that wraps the key in ``. ### API layer - `GET /token-events/{event_id}` — returns `TokenEventRead` for a single event; needed by the landing page data loader. ### Dashboard pages - `src/data/token-events/[id].json.py` — data loader that calls the new single-event endpoint; returns JSON for the landing page. - `src/data/token-events/[id].md` — landing page: key-value table, each row built with `fieldRow()`. - `src/token-cost.md` — updated: REF column (refCell) and Name column added to By Repo, By Workplan, and Top Tasks tables. ## Tasks ```task id: T01 title: "REF cell component" status: done priority: high description: > Create src/components/ref-cell.js. Export refCell(index, recordType, id) → HTMLElement. - Displays 1-based index as monospace text, cursor:pointer. - Single click: copies `${location.origin}/data/${recordType}/${id}` to clipboard; briefly flashes "Copied!" as a transient tooltip using the existing help-tip positioning pattern. - Double click: window.open(deeplink, '_blank'). - No external dependencies; plain DOM. state_hub_task_id: "8ee527cf-436b-4bab-bdb8-406314a38d99" ``` ```task id: T02 title: "GET /token-events/{event_id} endpoint" status: done priority: high description: > Add GET /token-events/{event_id} to api/routers/token_events.py. Returns TokenEventRead (already defined in schemas/token_event.py). 404 if not found. No migration needed. Add a test in tests/test_token_events.py: get by id → 200, unknown id → 404. state_hub_task_id: "02c27d25-d744-4da0-9bcb-b40ada54d5a5" ``` ```task id: T03 title: "Field-help registry" status: done priority: medium description: > Create src/components/field-help.js. Export FIELD_HELP: a plain object keyed by field name. Each entry: { label, description, doc } — matches help-tip attributes. Cover at minimum all TokenEventRead fields: id, tokens_in, tokens_out, tokens_total, task_id, workstream_id, repo_id, session_id, model, agent, ref_type, ref_id, note, created_at. Export fieldRow(key, value) → HTMLElement () that wraps key in a (or plain if key is not in FIELD_HELP) and value in a second . Import HelpTip from ./help-tip.js to ensure the custom element is registered. state_hub_task_id: "7721d884-bd70-459f-b36d-450d69aac549" ``` ```task id: T04 title: "Token-event landing page" status: done priority: medium description: > Create two files: 1. src/data/token-events/[id].json.py — data loader. Reads `id` from argv[1]; calls GET /token-events/{id}; exits 1 if 404 so Observable renders an error page. 2. src/data/token-events/[id].md — landing page. Imports fieldRow from components/field-help.js. Fetches FileAttachment("token-events/{id}.json").json(). Renders an HTML with one row per field using fieldRow(). Title: "Token Event · {id.slice(0,8)}…". Back link: "← Token Cost". state_hub_task_id: "dc63746d-74b3-434a-925c-1cead480198f" ``` ```task id: T05 title: "Apply REF and Name columns to Token Cost page" status: done priority: high description: > Update src/token-cost.md. Import refCell from ./components/ref-cell.js. For each of the three entity tables (By Repo, By Workplan, Top Tasks): - Prepend a "REF" column using refCell(i+1, recordType, row.id). Record types: "repos" for By Repo (using repo_id), "workstreams" for By Workplan (using scope_id), "token-events" for Top Tasks (using task_id — note: links to the task landing page, not a token event page, until T04 is done; use recordType "tasks"). - Add a Name column as the second data column: - By Repo: repo_slug (no truncation needed, slugs are short) - By Workplan: scope_id displayed as first 8 chars + "…" (unchanged) → replace with workstream title if available; for now show scope_id truncated to 36 chars with full UUID tooltip. - Top Tasks: task_id truncated to 80 chars with full-id tooltip. Keep all existing columns unchanged. state_hub_task_id: "3225cc6c-2574-41e9-b8fd-e5e703a9dd7c" ``` ```task id: T06 title: "Consistency gate and docs update" status: done priority: low description: > 1. Run `cd state-hub && make test` — all token_events tests must pass. 2. Run `make fix-consistency REPO=the-custodian`. 3. Add a one-paragraph entry to src/docs/reference.md describing the /data// URL scheme and the REF column convention. state_hub_task_id: "107cb5bb-ff0c-4c97-af04-2cdeff11f0b2" ```