|
|
@@ -0,0 +1,631 @@
|
|
|
+# ClankerSaddle web UI — structural layout critique
|
|
|
+
|
|
|
+This is an opinions document about **structure**, not looks: the theme,
|
|
|
+colours and typography stay as they are. "Structure" here means page
|
|
|
+hierarchy (what leads a page, what folds away), information order,
|
|
|
+navigation between pages, where forms live, and density. Every claim
|
|
|
+below was read out of the templates in `src/pages/` on the goal branch
|
|
|
+(after the SSE and HTML-escaping fixes landed), so the wireframes
|
|
|
+describe the app as it ships today.
|
|
|
+
|
|
|
+## How the app is built (why the recommendations look the way they do)
|
|
|
+
|
|
|
+- Every page is a Statum template compiled by `statum-mkpstm`:
|
|
|
+ `stm-text` / `stm-if` / `stm-for` / `stm-attribute` / `stm-markdown`
|
|
|
+ bind server state, `stm-action` submits sealed actions, and the page
|
|
|
+ refreshes in place. The shared chrome lives in `main.html`: a header
|
|
|
+ nav (Dashboard, Projects, Tasks, Goals, Saddles, Rules, Users, then
|
|
|
+ the user cluster), one `<main>`, a footer.
|
|
|
+- The whole app renders inside a **single 64rem column** (`body {
|
|
|
+ max-width: 64rem }` in `app.css`). There are exactly two layout
|
|
|
+ primitives beyond that: `.cards` (an auto-fill card grid) and
|
|
|
+ `.grid-2` (the task page's 50/50 status/mandate grid, stacking below
|
|
|
+ 40rem). No page uses a persistent two-column layout, a sticky
|
|
|
+ sub-nav, or a sidebar.
|
|
|
+- `task_log.html` is the only page whose body is built by client
|
|
|
+ JavaScript (`createElement`/`textContent`, filters, search, paging,
|
|
|
+ SSE). Everything else is server-rendered bindings.
|
|
|
+- `details`/`summary` is the app's only progressive-disclosure device,
|
|
|
+ and it is doing a lot of work it was not meant for (see the
|
|
|
+ "expander fatigue" section).
|
|
|
+
|
|
|
+Two capabilities notes used throughout:
|
|
|
+
|
|
|
+- `stm-attribute` can set **any** attribute (`stm-attribute.href`,
|
|
|
+ `.class`, `.value`, `.data-*` already appear), so a section can be
|
|
|
+ collapsed by binding `hidden` or a `data-collapsed` class to state,
|
|
|
+ without client JS.
|
|
|
+- Client JS is not exotic: `task_log.html`, `tasks.html`
|
|
|
+ (`window.taskRowVisible` + `window.setTaskFilter`) and
|
|
|
+ `goal_detail.html` (`window.goalSetFilter`) already run page scripts
|
|
|
+ that cooperate with statum's render cycle. A "sticky sub-nav that
|
|
|
+ highlights the section in view" is a `scroll` listener plus one
|
|
|
+ class toggle — the pattern already exists on this codebase.
|
|
|
+
|
|
|
+## The five structural themes
|
|
|
+
|
|
|
+1. **Attention is buried under equal-weight sections.** The detail
|
|
|
+ pages are one long scroll of cards that all look equally
|
|
|
+ important. The one thing a human must do (answer a block, review a
|
|
|
+ plan) sits in card 2 or 3 of 11, between the status card and the
|
|
|
+ plan history. Nothing in the page's *shape* says "act here".
|
|
|
+2. **Reference material competes with action.** Rules, capabilities,
|
|
|
+ sessions, briefs, plan revision history — genuinely useful, read
|
|
|
+ maybe once — render at the same weight and in the same column as
|
|
|
+ the action surface, so a page visit for "approve the plan" scrolls
|
|
|
+ past everything else.
|
|
|
+3. **Expander fatigue.** Every create and edit surface hides behind a
|
|
|
+ `<details>` expander: New task, New goal, New project, New rule,
|
|
|
+ New user, New capability, Register saddle, Edit task, Link a
|
|
|
+ project, SSH key, Git identity, Edit rule, answered blocks, plan
|
|
|
+ revisions, progress history. Disclosure is the right instinct for
|
|
|
+ *history*; it is the wrong instinct for *primary actions*, where it
|
|
|
+ inverts affordance (the most important button on the page is the
|
|
|
+ least visible control).
|
|
|
+4. **No "where am I".** No breadcrumbs anywhere. "Back" is a manual
|
|
|
+ anchor each page draws by hand ("← Goals", "All tasks", "All
|
|
|
+ projects"), inconsistently placed (goal page: above the `h2`; task
|
|
|
+ page: a context line under the `h2` carrying project + enqueue
|
|
|
+ chain). Cross-links exist (task→project, task→goal, goal→project)
|
|
|
+ but the reverse links (project→its goals, task→its sessions' logs)
|
|
|
+ are partial, so navigation is a one-way tree.
|
|
|
+5. **Density mismatch.** A five-word status line and a 400-word
|
|
|
+ completion report both get a full-width card in the same column;
|
|
|
+ the tasks table carries eight columns of which three are reference
|
|
|
+ (goal, project, capabilities) that most visits do not need.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Page-by-page
|
|
|
+
|
|
|
+### 1. Home / dashboard (`home.html`)
|
|
|
+
|
|
|
+**What it's for.** The "is anything on fire / does anything need me"
|
|
|
+page: push bell, queue and saddle metrics, pinned needs-a-human
|
|
|
+blocks, active tasks with latest progress, saddle cards, recent
|
|
|
+activity.
|
|
|
+
|
|
|
+**Structural problem.** The page opens with a full-width card whose
|
|
|
+entire content is one button pair ("Enable notifications — be told
|
|
|
+when an agent asks a question…"). Then two metric cards (Queue,
|
|
|
+Saddles). Only *then* comes the section the page exists for: "Needs a
|
|
|
+human". On a fresh install the attention content is below the fold;
|
|
|
+on a busy day the pinned cards — the actionable items — are squeezed
|
|
|
+between metrics above and two more full sections below (Saddles,
|
|
|
+Activity), so comparing a block against the task it came from means
|
|
|
+scrolling past a saddle grid.
|
|
|
+
|
|
|
+**Recommendations.**
|
|
|
+
|
|
|
+- **A. Move "Needs a human" to the top** (S, low risk). Keep the bell
|
|
|
+ but demote it: it is a settings action, not news. It can become a
|
|
|
+ compact strip beside the metric cards, or move to the header's user
|
|
|
+ cluster entirely (arguably where a notification preference belongs).
|
|
|
+- **B. Put the metrics in a right-hand rail next to the attention
|
|
|
+ column** (M, low risk). One `.grid-2`-style row: left 2/3 pinned
|
|
|
+ blocks + active tasks, right 1/3 Queue/Saddles metrics, the bell
|
|
|
+ strip, and "Saddles" as a compact status list rather than cards.
|
|
|
+- **C. Fold Activity under a collapsed expander with a "new since your
|
|
|
+ last visit" count** (S, low risk), or cap it at ~8 rows with "view
|
|
|
+ all" — it is the page's only pure-read section.
|
|
|
+
|
|
|
+```
|
|
|
+BEFORE (linear) AFTER (attention-first + rail)
|
|
|
++----------------------------+ +----------------------------+
|
|
|
+| [Enable notifications ...] | | NEEDS A HUMAN (3) |
|
|
|
++----------------------------+ | [block] [block] [block] |
|
|
|
+| [Queue] [Saddles] | | ACTIVE TASKS |
|
|
|
++----------------------------+ | ...table... |
|
|
|
+| NEEDS A HUMAN (3) | +------------+---------------+
|
|
|
+| [block] [block] [block] | | Queue | bell (compact)|
|
|
|
++----------------------------+ | Saddles | saddle list |
|
|
|
+| ACTIVE TASKS | +------------+---------------+
|
|
|
+| ...table... | | ACTIVITY (collapsed, 3 new)|
|
|
|
++----------------------------+ +----------------------------+
|
|
|
+| SADDLES (cards) |
|
|
|
++----------------------------+
|
|
|
+| ACTIVITY (full table) |
|
|
|
++----------------------------+
|
|
|
+```
|
|
|
+
|
|
|
+**statum notes.** A and C need nothing new. B needs one CSS grid row
|
|
|
+plus the existing card markup; `stm-if` already gates the pinned
|
|
|
+section on `dashboard.pinned.length`.
|
|
|
+
|
|
|
+### 2. Tasks list (`tasks.html`)
|
|
|
+
|
|
|
+**What it's for.** The queue: find a task, judge the queue's shape,
|
|
|
+create a task.
|
|
|
+
|
|
|
+**Structural problems.**
|
|
|
+
|
|
|
+- The **create form** is the page's only primary action and it is a
|
|
|
+ collapsed expander above the table. New users' first act is to hunt
|
|
|
+ for the form.
|
|
|
+- **Eight columns** (`#`, Title, Status, Mode, Goal, Priority,
|
|
|
+ Project, Capabilities), three of which (Goal, Project,
|
|
|
+ Capabilities) are reference attributes. On the 64rem column this is
|
|
|
+ dense, and Priority is usually "5" noise.
|
|
|
+- The three filters (Status, Project, Goal) are bare `<select>`s with
|
|
|
+ an inline hint ("— filters apply instantly") — functional but
|
|
|
+ undiscoverable as *filters*: they read as form fields. They also
|
|
|
+ carry no counts, so you cannot see the queue's shape without
|
|
|
+ filtering.
|
|
|
+- The list has **no server-side limit** (`TaskService.list_tasks`
|
|
|
+ returns every task newest-first) and terminal tasks accumulate
|
|
|
+ forever. Done tasks from months ago share the page with the thing
|
|
|
+ you came for.
|
|
|
+- It duplicates the create form that project detail doesn't have
|
|
|
+ (project detail links back here instead — the reverse direction is
|
|
|
+ a dead end).
|
|
|
+
|
|
|
+**Recommendations.**
|
|
|
+
|
|
|
+- **A. Default the list to "open work"** (S, low risk): a
|
|
|
+ `draft/queued/assigned/working/blocked` default with an explicit
|
|
|
+ "show done/failed (N)" toggle. The filters exist already; this is a
|
|
|
+ default-value change plus a chip.
|
|
|
+- **B. Replace the three selects with faceted status chips + one
|
|
|
+ "Project / Goal" select** (M, low risk, client JS exists): the
|
|
|
+ goal-detail page already has exactly this pattern (a
|
|
|
+ `goal-strip` of status buttons with counts). Port it here and the
|
|
|
+ two pages stop feeling like different apps.
|
|
|
+- **C. Drop Mode / Priority / Capabilities to a per-row hover/expand
|
|
|
+ or merge into one "meta" cell** (S, low risk); keep the row scannable
|
|
|
+ as `# title — status — project`.
|
|
|
+- **D. Give "New task" a real button** (S, low risk) — see the
|
|
|
+ expander-fatigue section: an always-visible `+ New task` button that
|
|
|
+ opens the same form inline.
|
|
|
+
|
|
|
+```
|
|
|
+BEFORE AFTER
|
|
|
++------------------------------+ +------------------------------+
|
|
|
+| [New task >] (collapsed) | | Tasks [+ New task] |
|
|
|
++------------------------------+ +------------------------------+
|
|
|
+| Status[v] Project[v] Goal[v] | | (draft 2)(queued 5)(working 3)|
|
|
|
+| — filters apply instantly | | (blocked 1)(done 214) |
|
|
|
++------------------------------+ | Project/Goal [all v] search |
|
|
|
+| # | Title | St | Mode | Goal | +------------------------------+
|
|
|
+| Pri | Proj | Caps | | # Title Status |
|
|
|
+| ...8 columns... | | 42 Fix the SSE feed working |
|
|
|
++------------------------------+ | 41 Escape entities blocked |
|
|
|
+ | ...project folded into title |
|
|
|
+ +------------------------------+
|
|
|
+```
|
|
|
+
|
|
|
+**statum notes.** B is the client-side `taskRowVisible` filter with
|
|
|
+counts derived from `tasks.tasks` (already shipped to the client) —
|
|
|
+no server change. A could also be server-side (default status filter
|
|
|
+in the entrypoint) if the "all" count needs to stay cheap.
|
|
|
+
|
|
|
+### 3. Task detail (`task_detail.html`)
|
|
|
+
|
|
|
+**What it's for.** The human's control surface for one task: watch
|
|
|
+progress, answer blocks, read the completion report, comment, tweak
|
|
|
+rules/capabilities, inspect sessions.
|
|
|
+
|
|
|
+**Structural problem.** Thirteen `h3` sections in one 64rem column,
|
|
|
+half of them full-width cards, in this order: Progress → Status
|
|
|
+card / Goal card / Mandate card (the one `.grid-2` on the page) →
|
|
|
+Completion report → *Edit task expander* → Blocks → Comments →
|
|
|
+Rules → Required capabilities → Sessions. The page's actual verb —
|
|
|
+answer the block, approve the review — is the **sixth** thing you
|
|
|
+scroll to, and it sits between an edit expander and the comments
|
|
|
+thread. Meanwhile the Status card (five muted metadata lines plus
|
|
|
+buttons) takes half a screen width while the Mandate — the single
|
|
|
+most useful read — gets the other half at the same visual weight as
|
|
|
+"Created 3 days ago".
|
|
|
+
|
|
|
+**Recommendations.**
|
|
|
+
|
|
|
+- **A. Two zones: "Act" then "Understand"** (M, medium risk). Zone 1
|
|
|
+ (top, full width): Progress line, **Blocks** (open ones first,
|
|
|
+ full weight), Completion report. Zone 2: the status/mandate grid,
|
|
|
+ comments, then a reference cluster.
|
|
|
+- **B. An anchored sub-nav strip under the page title**
|
|
|
+ (`Progress · Blocks · Report · Comments · Rules · Sessions`) that
|
|
|
+ scroll-jumps and highlights — a dozen lines of client JS over
|
|
|
+ `data-section` ids. This is the cheap version of "restructure the
|
|
|
+ page": it keeps the DOM order but gives the reader a map and a
|
|
|
+ one-click path.
|
|
|
+- **C. Cluster the low-traffic reference sections** (Rules, Required
|
|
|
+ capabilities, Sessions) into one "Configuration & history" region —
|
|
|
+ either a single card with internal `h4`s or one collapsed expander
|
|
|
+ with a summary line ("2 rules · 1 capability · 3 sessions").
|
|
|
+- **D. The Sessions table's "Event log" link is the page's only
|
|
|
+ per-session affordance and it is a link in a table row** — surface
|
|
|
+ it once at the top level ("View event log" beside Progress) since
|
|
|
+ it is per-task, not per-session, anyway.
|
|
|
+
|
|
|
+```
|
|
|
+BEFORE (13 sections, one column) AFTER (Act / Understand / Reference)
|
|
|
++------------------------------+ +------------------------------+
|
|
|
+| Progress | | Progress [Event log] |
|
|
|
++------------------------------+ +------------------------------+
|
|
|
+| Status | Goal | Mandate | | BLOCKS (1 open) |
|
|
|
++------------------------------+ | [question card + answer] |
|
|
|
+| Completion report | +------------------------------+
|
|
|
++------------------------------+ | COMPLETION REPORT |
|
|
|
+| [Edit task >] | +------------------------------+
|
|
|
++------------------------------+ | Status | Mandate | Goal |
|
|
|
+| BLOCKS (1 open) <- the verb| +------------------------------+
|
|
|
++------------------------------+ | Comments |
|
|
|
+| Comments | +------------------------------+
|
|
|
++------------------------------+ | Configuration & history (2) |
|
|
|
+| Rules | | rules - caps - sessions |
|
|
|
++------------------------------+ +------------------------------+
|
|
|
+| Required capabilities |
|
|
|
++------------------------------+ (sub-nav strip pinned under
|
|
|
+| Sessions | the title: Progress / Blocks /
|
|
|
++------------------------------+ Report / Comments / Config)
|
|
|
+```
|
|
|
+
|
|
|
+**statum notes.** B and C need no new patterns (`stm-if`, anchors,
|
|
|
+existing client-JS style). A is a re-ordering of existing cards; the
|
|
|
+risk is only that regulars re-learn where things are, so ship it with
|
|
|
+the sub-nav.
|
|
|
+
|
|
|
+### 4. Task event log (`task_log.html`)
|
|
|
+
|
|
|
+**What it's for.** The live transcript of one task: turn cards,
|
|
|
+commands, results, comments, transitions, usage — the forensic view
|
|
|
+when something went wrong.
|
|
|
+
|
|
|
+**Structural problems.**
|
|
|
+
|
|
|
+- It is structurally unlike every other page (client-rendered feed in
|
|
|
+ a 72vh scroller), which is *fine for a transcript* — but its
|
|
|
+ toolbar (5 filter buttons + search) sits inside the card above the
|
|
|
+ feed, so it scrolls away. For the page where you spend the most
|
|
|
+ time scrolled deep, the controls should be pinned.
|
|
|
+- "Load older events" is a button *above* the feed — correct, since
|
|
|
+ older history grows upward, but nothing indicates how much history
|
|
|
+ remains or that the feed is a window onto a longer ledger.
|
|
|
+- There is no way back to the task's *blocks/comments* surface except
|
|
|
+ the "Task page" text link at the top — the two pages are one
|
|
|
+ investigation flow split across a page boundary, and the log opens
|
|
|
+ in a **new tab** from the task page, which severs even the back
|
|
|
+ button.
|
|
|
+- The feed and the task page's Progress/Sessions duplicate a job:
|
|
|
+ "what has the agent been doing".
|
|
|
+
|
|
|
+**Recommendations.**
|
|
|
+
|
|
|
+- **A. Sticky toolbar** (S, low risk): `position: sticky; top: 0`
|
|
|
+ on `.log-toolbar` inside the scroller, so filters/search survive
|
|
|
+ deep scrolling. Add a "paused / live" affordance to the
|
|
|
+ bottom-stick behaviour (the feed already knows whether it is
|
|
|
+ pinned; expose it as a "jump to latest" button when not).
|
|
|
+- **B. Context header, not a link farm** (S): the page opens with
|
|
|
+ "← Tasks" then "Event log" then "Task page · the unified event
|
|
|
+ ledger…". Replace with a single header row: task id + title +
|
|
|
+ status badge (the data is already in `task_log`), a back-chevron to
|
|
|
+ the task, and the window note ("showing most recent 30") folded
|
|
|
+ into the load-older control ("Load older events — 412 more").
|
|
|
+- **C. Consider the log a *tab of the task page* rather than a
|
|
|
+ separate URL** (L, medium risk — a decision more than a build):
|
|
|
+ either mount the feed component under a `?view=log` tab on
|
|
|
+ `/tasks/{id}` or keep the URL but add a persistent task-context
|
|
|
+ strip so both pages share their sub-nav. This is the single
|
|
|
+ highest-leverage navigation fix in the app: today the natural
|
|
|
+ investigation loop (task → log → task) costs a tab and a reload.
|
|
|
+
|
|
|
+```
|
|
|
+BEFORE AFTER
|
|
|
++----------------------------+ +----------------------------+
|
|
|
+| <- Tasks | | <- Task #42 "Fix SSE" [wg] |
|
|
|
+| Event log | +----------------------------+
|
|
|
+| Task page - unified ledger | | [cmds][res][cmts][tr][use] |
|
|
|
++----------------------------+ | [search____________] live |
|
|
|
+| [Show: cmds res cmts ...] | +----------------------------+
|
|
|
+| [search] | | ^ Load older (412 more) |
|
|
|
++----------------------------+ +----------------------------+
|
|
|
+| [Load older events] | | 14:02 turn 51 |
|
|
|
++----------------------------+ | ...feed (sticky filters) |
|
|
|
+| 14:02 turn 51 | | |
|
|
|
+| ...feed scrolls... | | [jump to end] |
|
|
|
++----------------------------+ +----------------------------+
|
|
|
+```
|
|
|
+
|
|
|
+**statum notes.** A and B are pure client CSS/markup inside the page
|
|
|
+that already owns its rendering. C needs either the feed lifted into
|
|
|
+a fragment included by the task page (partials already exist — see
|
|
|
+`progress_feed.html`, `goal-branches`) or a shared context strip;
|
|
|
+both are proven patterns.
|
|
|
+
|
|
|
+### 5. Goal detail (`goal_detail.html`)
|
|
|
+
|
|
|
+**What it's for.** The supervision surface for one goal: status and
|
|
|
+lifecycle actions, agent progress, **plan review**, **block
|
|
|
+answering**, the task fan-out table, the brief, rules, narrative,
|
|
|
+comments.
|
|
|
+
|
|
|
+**Structural problem.** Eleven sequential cards — Status, Agent
|
|
|
+progress, Goal agent blocks, Completion review, Plan, Tasks, Initial
|
|
|
+brief, Involved projects, Rules, Supervision narrative, Comments,
|
|
|
+(Goal agent runs) — in one column, all at the same weight. The good
|
|
|
+news is the *order* is already attention-first (blocks and the
|
|
|
+pending plan review sit above tasks and brief — the comments in the
|
|
|
+template say so). The problem is *depth and differentiation*: to
|
|
|
+check "has the plan been approved yet?" you scroll past status,
|
|
|
+progress, blocks, and completion review, then find the Plan card
|
|
|
+whose *content* is a collapsed expander ("Approved plan & revisions
|
|
|
+(N)"). The Tasks card embeds a 6-column table with its own
|
|
|
+client-side status-filter strip — a second, different filtering UI on
|
|
|
+the same site (see tasks list). Reference cards (Rules, Involved
|
|
|
+projects) sit between the action surface and Comments, so a
|
|
|
+conversation with the agent is a long scroll from the question you
|
|
|
+are answering.
|
|
|
+
|
|
|
+**Recommendations.**
|
|
|
+
|
|
|
+- **A. Make the page's state legible in one line** (S, low risk):
|
|
|
+ a compact "goal strip" at the top — status badge, the one open
|
|
|
+ item ("1 question waiting · plan revision 2 pending"), and the
|
|
|
+ primary lifecycle button (Start / Pause / Retry). The Status card
|
|
|
+ keeps the detail (branches, compare, saddle, timestamps) but the
|
|
|
+ reader gets the verdict without parsing it.
|
|
|
+- **B. Two-column attention-first layout on wide screens** (M,
|
|
|
+ medium risk): left column = Status, Agent progress, Blocks, Plan
|
|
|
+ review, Completion review, Comments (everything a human acts on);
|
|
|
+ right column = Tasks, Initial brief, Involved projects, Rules,
|
|
|
+ Supervision narrative, Runs (everything a human consults). The
|
|
|
+ single 64rem column is the core reason this page reads as "12
|
|
|
+ cards in a trenchcoat"; splitting the reading column from the
|
|
|
+ acting column fixes it without touching the theme.
|
|
|
+- **C. Sub-nav strip** as per task detail (`Status · Blocks · Plan ·
|
|
|
+ Tasks · Brief · Narrative · Comments`) — pairs well with B, and
|
|
|
+ makes B optional if you want the smaller change first.
|
|
|
+- **D. Unify the tasks-filtering pattern** with the tasks list (the
|
|
|
+ goal page's `goal-strip` is the better UI — port it *up* to
|
|
|
+ /tasks, not the other way).
|
|
|
+
|
|
|
+```
|
|
|
+BEFORE (11 cards, 1 column) AFTER (act | consult)
|
|
|
++----------------------------+ +--------------+---------------+
|
|
|
+| Status | | ACT | CONSULT |
|
|
|
++----------------------------+ | Status (cmpct)| Tasks |
|
|
|
+| Agent progress | | Progress | (filter |
|
|
|
++----------------------------+ | BLOCKS (1) | strip) |
|
|
|
+| Goal agent blocks | | Plan review | Initial brief|
|
|
|
++----------------------------+ | Completion | Projects |
|
|
|
+| Completion review | | Comments | Rules |
|
|
|
++----------------------------+ | | Narrative |
|
|
|
+| Plan (usually collapsed) | | | Runs |
|
|
|
++----------------------------+ +--------------+---------------+
|
|
|
+| Tasks (own filter strip) |
|
|
|
++----------------------------+ sub-nav: Status / Blocks / Plan /
|
|
|
+| Initial brief | Tasks / Brief / Narrative / Comments
|
|
|
++----------------------------+
|
|
|
+| Involved projects |
|
|
|
++----------------------------+
|
|
|
+| Rules / Narrative / |
|
|
|
+| Comments / Runs |
|
|
|
++----------------------------+
|
|
|
+```
|
|
|
+
|
|
|
+**statum notes.** A is `stm-if` + expression text over data the
|
|
|
+entrypoint already publishes (`open_blocks`, `plan_pending_block`).
|
|
|
+B is one CSS grid + card re-parenting; the risk is mobile (stack
|
|
|
+Act-then-Consult, which the existing 40rem breakpoint pattern
|
|
|
+already handles). C is the same client JS as task detail.
|
|
|
+
|
|
|
+### 6. Saddles (`saddles.html`)
|
|
|
+
|
|
|
+**What it's for.** Fleet health and configuration: per-saddle status,
|
|
|
+current work, SSH keys, git identity, capabilities; the capability
|
|
|
+tag catalogue at the bottom.
|
|
|
+
|
|
|
+**Structural problems.**
|
|
|
+
|
|
|
+- The card grid is right for this page, but each card is a long
|
|
|
+ stack of paragraphs (status, work, health line, token, SSH key
|
|
|
+ expander, git identity expander, capability chips, add-capability
|
|
|
+ form, Refresh/Remove buttons). "What is this saddle doing?" — the
|
|
|
+ question you ask daily — is the second of nine blocks, and the
|
|
|
+ admin machinery (keys, identity) crowds it.
|
|
|
+- The **Capabilities section at the bottom is a different modality**
|
|
|
+ (table) bolted under a card grid; it reads like a second page
|
|
|
+ appended to the first.
|
|
|
+- Health is expressed as three separate text fragments (health badge,
|
|
|
+ `daemon state`, `up X`, `contacted Y ago`) instead of one verdict.
|
|
|
+
|
|
|
+**Recommendations.**
|
|
|
+
|
|
|
+- **A. Split each card into a "now" head and an "admin" tail** (S,
|
|
|
+ low risk): head = name, status badge, health verdict (one line),
|
|
|
+ current work; tail (already-expanders, keep them) = key, identity,
|
|
|
+ capabilities, token, actions. Visual anchor: the head is the card's
|
|
|
+ first ~3 lines; everything else folds.
|
|
|
+- **B. Surface "unhealthy" at the top of the page** (S): if any
|
|
|
+ saddle is offline/stale/fenced, one summary line above the grid
|
|
|
+ ("1 of 3 saddles stale — dispatcher may be starved"), linking to
|
|
|
+ the card. The dashboard's idle-count metric does not currently
|
|
|
+ tell you this.
|
|
|
+- **C. Keep the capabilities table but visually separate it** (S) —
|
|
|
+ its own `h2`-level section ("Routing tags") rather than an `h3`
|
|
|
+ after the grid, so the page reads as two deliberate sections.
|
|
|
+
|
|
|
+```
|
|
|
+BEFORE (one 9-block card) AFTER (now head / admin tail)
|
|
|
++--------------------------+ +--------------------------+
|
|
|
+| saddle-1 | | saddle-1 [busy] |
|
|
|
+| [busy] localhost:7100 | | healthy - up 3d - 12s ago|
|
|
|
+| Supervising goal: X | | Supervising goal X (2h) |
|
|
|
+| since 2h | +--------------------------+
|
|
|
+| [healthy] daemon idle ...| | [Keys & admin v] |
|
|
|
+| API token: ... | | key / identity / caps /|
|
|
|
+| [SSH public key v] | | token / refresh/remove |
|
|
|
+| [Git identity v] | +--------------------------+
|
|
|
+| Capabilities: [gpu] [x] |
|
|
|
+| [Add capability v] |
|
|
|
+| [Refresh] [Remove] |
|
|
|
++--------------------------+
|
|
|
+```
|
|
|
+
|
|
|
+**statum notes.** All three are markup re-orders inside existing
|
|
|
+patterns (`stm-if`, `details`, badges); B is an expression over
|
|
|
+`saddles.saddles` the page already iterates.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Shorter notes
|
|
|
+
|
|
|
+### Projects list (`projects.html`) and project detail
|
|
|
+
|
|
|
+- List: table is fine; "New project" is the expander problem again
|
|
|
+ (give it a button). The Git URL column is long noise — truncate
|
|
|
+ with the branch column into one "repo" cell.
|
|
|
+- Detail: "Details" and "Edit" as two side-by-side cards is odd —
|
|
|
+ the Edit card is a form for exactly what Details displays. Make
|
|
|
+ Details the read view with an in-place **Edit** toggle (the
|
|
|
+ `data-long-form` in-place editor pattern from task/goal briefs
|
|
|
+ already exists in `main.html` and is a better fit here), freeing
|
|
|
+ the second column for "Tasks in this project".
|
|
|
+- Project detail has no create-task surface and links out to
|
|
|
+ `/tasks`; with a `?project=` pre-fill that becomes a flow instead
|
|
|
+ of a detour. It also lists tasks but not the project's goals —
|
|
|
+ the reverse cross-link gap.
|
|
|
+
|
|
|
+### Goals list (`goals.html`)
|
|
|
+
|
|
|
+- Same expander-pattern note for "New goal". The brief is a required
|
|
|
+ 6-row textarea — the goal's most important field — hiding behind a
|
|
|
+ collapsed summary; fine once expanded, invisible until then.
|
|
|
+- The Tasks column packs four counts into one muted string
|
|
|
+ ("2 draft / 5 queued / 3 running / 14 done") — unreadable; four
|
|
|
+ tiny badges (the `goal-strip` chip pattern again) or just
|
|
|
+ "19 tasks, 3 running" would carry more signal per pixel.
|
|
|
+- No status filter on the list at all, unlike tasks — the same chip
|
|
|
+ strip would fix both consistency and scanning.
|
|
|
+
|
|
|
+### Rules (`rules.html`)
|
|
|
+
|
|
|
+- Each rule is a full-width card with body, then an Edit expander and
|
|
|
+ a bare Delete button *outside* the expander — a destructive
|
|
|
+ action more prominent than the edit one. Invert: Edit as the
|
|
|
+ in-place editor pattern, Delete inside a "danger" expander or
|
|
|
+ behind the existing confirm alone but visually secondary.
|
|
|
+- No list of where a rule is attached (projects/tasks using it) —
|
|
|
+ the page says rules attach on project/task pages but you cannot
|
|
|
+ see the blast radius of a delete from here. A reference list, not
|
|
|
+ a theme change.
|
|
|
+
|
|
|
+### Users (`users.html`) and profile
|
|
|
+
|
|
|
+- Users: admin-only, table plus expander create form — acceptable,
|
|
|
+ lowest traffic in the app. Same "New user" button note applies.
|
|
|
+- Profile: fine structurally; the change-password form could sit in
|
|
|
+ a card for consistency, and this is the natural home for the
|
|
|
+ notification preference currently occupying the dashboard's first
|
|
|
+ card.
|
|
|
+
|
|
|
+### Login
|
|
|
+
|
|
|
+- No structural complaints: inline error handling, focused field,
|
|
|
+ narrow form. Good.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Expander fatigue — the cross-cutting fix
|
|
|
+
|
|
|
+Counted across the app, `<details>` currently serves three distinct
|
|
|
+jobs with one widget:
|
|
|
+
|
|
|
+1. **History folding** (progress history, answered blocks, plan
|
|
|
+ revisions, submitted markdown) — this is the right tool; keep it.
|
|
|
+2. **Primary create forms** (new task, goal, project, rule, user,
|
|
|
+ capability, register saddle) — wrong tool: it hides the page's
|
|
|
+ main action. Replace with a persistent button (`+ New task`)
|
|
|
+ that opens the form inline (a `data-*` toggle + `hidden`, exactly
|
|
|
+ the `data-long-form` / `data-lf-toggle` pattern in `main.html`),
|
|
|
+ or render the form directly on low-traffic pages where there is
|
|
|
+ room (rules, projects).
|
|
|
+3. **Admin/config editing** (SSH key, git identity, edit task, edit
|
|
|
+ rule) — defensible, but prefer the in-place editor (read view +
|
|
|
+ Edit toggle) where the form is short, because it shows the
|
|
|
+ current values in place instead of a cold empty form.
|
|
|
+
|
|
|
+Rule of thumb proposed: **history folds, actions surface.**
|
|
|
+
|
|
|
+## Navigation — the cross-cutting fix
|
|
|
+
|
|
|
+- **Breadcrumbs on detail pages** (S): `Dashboard / Tasks / #42` (or
|
|
|
+ `Goals / #7 🎯`), replacing the hand-drawn "← Goals" / "All tasks"
|
|
|
+ anchors that each page currently invents. One shared partial
|
|
|
+ (`pstm-fragment` already powers `progress-feed`, `goal-branches`)
|
|
|
+ makes it consistent for free.
|
|
|
+- **Reverse links**: project detail lists its tasks but not its
|
|
|
+ goals; task detail's sessions link out to the log but the log links
|
|
|
+ back only to the task. The dashboard's cross-links are the model to
|
|
|
+ copy: pinned blocks deep-link to the exact task or goal, and saddle
|
|
|
+ cards link to the saddle's current work.
|
|
|
+- **The log-in-a-new-tab decision** (see event log, C) is the one
|
|
|
+ navigation break that costs the most; everything else is polish.
|
|
|
+
|
|
|
+## Quick wins (a day or less each)
|
|
|
+
|
|
|
+1. Dashboard re-order: Needs-a-human first, bell compacted (S).
|
|
|
+2. Faceted status chips with counts on /tasks, defaulting to open
|
|
|
+ work (M but a port of existing `goal-strip` code).
|
|
|
+3. Breadcrumb partial across detail pages (S).
|
|
|
+4. `+ New …` buttons replacing create-form expanders (S, per page).
|
|
|
+5. Sticky toolbar + jump-to-latest on the event log (S).
|
|
|
+6. Task detail: cluster Rules + Capabilities + Sessions into one
|
|
|
+ collapsed "Configuration & history" region (S).
|
|
|
+7. Goal page: one-line state strip above the Status card (S).
|
|
|
+8. Saddles: card head/tail split + fleet health summary line (S).
|
|
|
+9. Goals list: readable task-count badges + status chips (S).
|
|
|
+
|
|
|
+## Structural bets (multi-day)
|
|
|
+
|
|
|
+1. **Act / Consult two-column layout** for goal detail (and
|
|
|
+ optionally task detail) — the biggest single improvement to the
|
|
|
+ app's most-used deep pages; needs a responsive fallback.
|
|
|
+2. **Anchor sub-nav** on the two detail pages (Progress · Blocks ·
|
|
|
+ Plan · …) with in-view highlighting — a new (small) client-side
|
|
|
+ pattern, ~a day including polish.
|
|
|
+3. **Task page ↔ event log unification**: shared context strip or a
|
|
|
+ tabbed task page (`Overview | Log`), turning two URLs and a new
|
|
|
+ tab into one investigation flow.
|
|
|
+4. **Saved views / URL state for the tasks list** (filters in the
|
|
|
+ query string, shareable "my blocked tasks" views) — needs the
|
|
|
+ entrypoint to read the query string or a small client state
|
|
|
+ sync; the current client-side filtering deliberately exists
|
|
|
+ because statum's fetch doesn't carry the page query string, so
|
|
|
+ this is the one item that brushes against that limitation.
|
|
|
+5. **Cross-entity "work" stream**: one feed joining task progress,
|
|
|
+ goal narrative and event-log highlights, replacing three
|
|
|
+ different feeds with different row grammars (activity table,
|
|
|
+ narrative card, event log). Large; only worth it if the dashboard
|
|
|
+ stays the daily driver.
|
|
|
+
|
|
|
+## Capability appendix — what needs what
|
|
|
+
|
|
|
+Already possible with today's statum.js patterns (`stm-for`,
|
|
|
+`stm-if`, `stm-attribute`, `stm-markdown`, `details`, `pstm-fragment`
|
|
|
+partials, per-page client JS as in `task_log` / `tasks` /
|
|
|
+`goal_detail`):
|
|
|
+
|
|
|
+- Every quick win above (1–9): chips, breadcrumbs, buttons,
|
|
|
+ sticky toolbar, clustering, strips, head/tail cards.
|
|
|
+- The Act/Consult two-column layouts: pure CSS grid + re-parenting
|
|
|
+ existing cards; `stm-if` gates each card's visibility already.
|
|
|
+- In-place editors replacing expanders: the `data-long-form`
|
|
|
+ machinery in `main.html` is generic — reuse it for project edit
|
|
|
+ and rule edit.
|
|
|
+
|
|
|
+Needs a new (small) pattern or a server change:
|
|
|
+
|
|
|
+- Sub-nav with in-view highlight: a scroll listener + class toggle;
|
|
|
+ no statum change, but it is a new client-side idiom for
|
|
|
+ server-rendered pages (only `task_log` carries this much JS
|
|
|
+ today).
|
|
|
+- URL-state filters / saved views on /tasks: either the entrypoint
|
|
|
+ learns the query string (server change; the template comment in
|
|
|
+ `tasks.html` says the statum entrypoint fetch does not carry it)
|
|
|
+ or the client owns filter state in `location.hash`.
|
|
|
+- Task↔log unification: fragment-include of a client-rendered
|
|
|
+ component inside a server-rendered page, or a shared context
|
|
|
+ strip; both are compositions of existing pieces but need real
|
|
|
+ design care around the SSE lifecycle on tab switches.
|