docs: close out task-completion safety net plan; fix stale relative links
Fixes 1-3 deployed and verified live: fresh trivial Q&A sessions now reach done immediately, and a goal-bearing session that stalled was correctly nudged by the idle sweep. Fix 4 (backfill) was replaced with deletion after the operator's call — verified against the DB first that zero knowledge notes were linked to or written by any of the 53 removed sessions, so nothing was lost. Documents the pagination gap in listSessions (hardcoded LIMIT 50, no total count) that hid 6 of those sessions from the original audit. Also fixes relative links in this plan and in the UI-review plan that broke when both moved from plans/ to plans/done/ (one directory level deeper). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,13 +25,13 @@ structure — implement on a later "proceed."
|
||||
### A. Information architecture
|
||||
|
||||
**A1. Entity detail has two competing UI patterns for the same content.**
|
||||
[`Entities.svelte:16-19,155`](../web/src/pages/Entities.svelte) opens entity
|
||||
[`Entities.svelte:16-19,155`](../../web/src/pages/Entities.svelte) opens entity
|
||||
detail as an in-page `EntitySheet` slide-over (no URL change, no sidebar
|
||||
state change). [`Knowledge.svelte:57-59`](../web/src/pages/Knowledge.svelte)
|
||||
and [`Graph.svelte:464`](../web/src/pages/Graph.svelte) instead navigate via
|
||||
state change). [`Knowledge.svelte:57-59`](../../web/src/pages/Knowledge.svelte)
|
||||
and [`Graph.svelte:464`](../../web/src/pages/Graph.svelte) instead navigate via
|
||||
`location.hash = '#/entity/' + slug`, which `App.svelte`'s router resolves to
|
||||
a full-page `EntityDetail` route — but `'entity'` isn't in `navItems`
|
||||
([`App.svelte:68-79`](../web/src/App.svelte)), so landing there leaves the
|
||||
([`App.svelte:68-79`](../../web/src/App.svelte)), so landing there leaves the
|
||||
sidebar with nothing highlighted and the header showing the raw slug instead
|
||||
of a section name. Same underlying view
|
||||
(`EntityDetailContent.svelte`), three different entry points, two
|
||||
@@ -42,8 +42,8 @@ A user who reaches an entity via Knowledge or Graph has no way back to
|
||||
**A2. Two chat entry points with no visual link between them.**
|
||||
The sidebar's "Tasks" section (board → `Chat.svelte` detail,
|
||||
`isActive={page === 'tasks' || page === 'chat'}`,
|
||||
[`App.svelte:127`](../web/src/App.svelte)) and the footer's "Chat drawer"
|
||||
button ([`App.svelte:160-163`](../web/src/App.svelte), opens a `Sheet`
|
||||
[`App.svelte:127`](../../web/src/App.svelte)) and the footer's "Chat drawer"
|
||||
button ([`App.svelte:160-163`](../../web/src/App.svelte), opens a `Sheet`
|
||||
wrapping the same `Chat` component) are both valid, intentional ways to
|
||||
reach chat — but nothing in the UI explains they're different modes (drawer
|
||||
= overlay on current page, keeps your place; Tasks = full navigation). A
|
||||
@@ -51,10 +51,10 @@ first-time user has no way to know which one preserves their current page.
|
||||
Low-severity, but worth a tooltip/label distinction.
|
||||
|
||||
**A3. Overview's KPI cards don't drill down.**
|
||||
[`Overview.svelte`](../web/src/pages/Overview.svelte) shows "Pending
|
||||
[`Overview.svelte`](../../web/src/pages/Overview.svelte) shows "Pending
|
||||
approvals," "Open signals," and fleet-health counts as static cards. The
|
||||
header badges for the same data (`approvalsPending`, `openSignals`,
|
||||
[`App.svelte:185-194`](../web/src/App.svelte)) ARE clickable and navigate to
|
||||
[`App.svelte:185-194`](../../web/src/App.svelte)) ARE clickable and navigate to
|
||||
Ops/Signals — so the pattern exists in the app, just not on the page whose
|
||||
entire purpose is summarizing this data. A dashboard card showing a count
|
||||
that doesn't lead anywhere is a standard drill-down gap.
|
||||
@@ -63,17 +63,17 @@ that doesn't lead anywhere is a standard drill-down gap.
|
||||
|
||||
**B1. Table-row click targets lack keyboard/screen-reader support in one
|
||||
place but not others.**
|
||||
[`Entities.svelte:117-120`](../web/src/pages/Entities.svelte) makes an
|
||||
[`Entities.svelte:117-120`](../../web/src/pages/Entities.svelte) makes an
|
||||
entire `Table.Row` clickable via a bare `onclick`, with no `role`,
|
||||
`tabindex`, or `onkeydown` — unreachable and inoperable via keyboard, and
|
||||
screen readers get no indication the row is interactive. This is a
|
||||
regression against the codebase's own established pattern: `Tasks.svelte`
|
||||
wraps its cards in real `<button>` elements
|
||||
([`Tasks.svelte:172`](../web/src/pages/Tasks.svelte)), `Events.svelte`'s
|
||||
([`Tasks.svelte:172`](../../web/src/pages/Tasks.svelte)), `Events.svelte`'s
|
||||
correlation-group headers are real `<button>`s
|
||||
([`Events.svelte:110-114`](../web/src/pages/Events.svelte)), and
|
||||
([`Events.svelte:110-114`](../../web/src/pages/Events.svelte)), and
|
||||
`Graph.svelte`'s SVG nodes explicitly add `role="button"`, `tabindex="0"`,
|
||||
and `onkeydown` ([`Graph.svelte:416-421`](../web/src/pages/Graph.svelte)).
|
||||
and `onkeydown` ([`Graph.svelte:416-421`](../../web/src/pages/Graph.svelte)).
|
||||
Entities is the outlier.
|
||||
|
||||
**B2. Filter inputs are inconsistently "live" vs. "apply-on-blur," with no
|
||||
@@ -81,10 +81,10 @@ visual cue either way.**
|
||||
`Entities.svelte`'s slug/name filter and `Graph.svelte`'s search box filter
|
||||
as-you-type (bound to a `$derived`). But `Ops.svelte` (implicitly, no text
|
||||
filters), `Audit.svelte`'s action/entity inputs
|
||||
([`Audit.svelte:71-72`](../web/src/pages/Audit.svelte)),
|
||||
([`Audit.svelte:71-72`](../../web/src/pages/Audit.svelte)),
|
||||
`Agent.svelte`'s agent_id input
|
||||
([`Agent.svelte:59`](../web/src/pages/Agent.svelte)), and `Events.svelte`'s
|
||||
type/severity inputs ([`Events.svelte:92-93`](../web/src/pages/Events.svelte))
|
||||
([`Agent.svelte:59`](../../web/src/pages/Agent.svelte)), and `Events.svelte`'s
|
||||
type/severity inputs ([`Events.svelte:92-93`](../../web/src/pages/Events.svelte))
|
||||
all use `onchange`, which only fires on blur — a user typing a filter value
|
||||
and watching the table sees nothing happen until they click or tab away, and
|
||||
nothing in the UI (placeholder text, a debounce spinner, an "Enter to
|
||||
@@ -93,10 +93,10 @@ apply" hint) tells them why. Three different pages share the same
|
||||
it reads as broken on first use.
|
||||
|
||||
**B3. Entity filter is case-sensitive; nothing else in the app is.**
|
||||
[`Entities.svelte:50`](../web/src/pages/Entities.svelte) matches with raw
|
||||
[`Entities.svelte:50`](../../web/src/pages/Entities.svelte) matches with raw
|
||||
`.includes()`, no `.toLowerCase()`. `Graph.svelte`'s equivalent search
|
||||
normalizes both sides
|
||||
([`Graph.svelte:175-176`](../web/src/pages/Graph.svelte):
|
||||
([`Graph.svelte:175-176`](../../web/src/pages/Graph.svelte):
|
||||
`n.slug.toLowerCase().includes(q)`). Slugs are lowercase by convention today,
|
||||
which is why this hasn't bitten anyone yet, but entity *names* are
|
||||
free text and can be mixed-case — a name filter that silently returns zero
|
||||
@@ -105,7 +105,7 @@ one-line fix already has a working reference implementation three files
|
||||
away.
|
||||
|
||||
**B4. `{@html}` on server-provided search snippets.**
|
||||
[`Knowledge.svelte:120-121`](../web/src/pages/Knowledge.svelte) renders
|
||||
[`Knowledge.svelte:120-121`](../../web/src/pages/Knowledge.svelte) renders
|
||||
`hit.snippet` with `{@html}`, justified by a comment claiming the backend's
|
||||
`ts_headline` output is pre-sanitized. That's true for Postgres
|
||||
`ts_headline` today (it only wraps matched terms in `<b>` from a
|
||||
@@ -119,7 +119,7 @@ comment to hold forever.
|
||||
### C. Accessibility
|
||||
|
||||
**C1. `SessionRail.svelte`'s delete control is a `<span>`, not a button.**
|
||||
[`SessionRail.svelte:54-64`](../web/src/lib/components/SessionRail.svelte)
|
||||
[`SessionRail.svelte:54-64`](../../web/src/lib/components/SessionRail.svelte)
|
||||
attaches `onclick` to a `<span>` for the per-session delete affordance, with
|
||||
no `role`, `tabindex`, or keyboard handler — same defect class as B1, on a
|
||||
destructive action this time (delete a chat session), which makes it a
|
||||
@@ -143,7 +143,7 @@ C1/B1).
|
||||
**D1. One page bypasses the shared `Button` component.**
|
||||
`Agent.svelte`'s "Refresh" control is a bare
|
||||
`<button class="rounded-md border px-3 py-1.5 text-xs">`
|
||||
([`Agent.svelte:73`](../web/src/pages/Agent.svelte)) instead of
|
||||
([`Agent.svelte:73`](../../web/src/pages/Agent.svelte)) instead of
|
||||
`Button` (`variant="outline"`), which every other page's refresh/action
|
||||
buttons use (`Ops.svelte`, `Signals.svelte`, `Audit.svelte`, `Events.svelte`
|
||||
all use `<Button variant="outline">`). Cosmetically near-identical today
|
||||
@@ -151,7 +151,7 @@ all use `<Button variant="outline">`). Cosmetically near-identical today
|
||||
tokens on `Button` change, since this one doesn't inherit them.
|
||||
|
||||
**D2. `formatEventLabel` is a needless indirection.**
|
||||
[`Overview.svelte`](../web/src/pages/Overview.svelte)'s
|
||||
[`Overview.svelte`](../../web/src/pages/Overview.svelte)'s
|
||||
`formatEventLabel(ev)` returns `ev.type` verbatim — a one-line wrapper with
|
||||
no formatting logic. Trivial, but noted since it reads as if formatting
|
||||
were intended and never finished.
|
||||
@@ -174,7 +174,7 @@ Priority order, grounded in user impact:
|
||||
action that's currently unreachable by keyboard at all. Swap the `<span>`
|
||||
for a real `<button>` with `aria-label="Delete session"`, matching the
|
||||
pattern `Tasks.svelte` already uses for its own delete affordance
|
||||
([`Tasks.svelte:195-207`](../web/src/pages/Tasks.svelte) — same feature,
|
||||
([`Tasks.svelte:195-207`](../../web/src/pages/Tasks.svelte) — same feature,
|
||||
done correctly, in the same codebase).
|
||||
2. **B1 (Entities row click)** — wrap row content in a `<button>` (or add
|
||||
`role="button" tabindex="0" onkeydown`) matching `Tasks.svelte` /
|
||||
|
||||
Reference in New Issue
Block a user