Files
pad/web
xarmian 6f7c09a44a fix(web): judge a relation's collection only when the list and index agree (TASK-2868)
Codex round 2, P1, real — and it is the retag window my round-1 fix left open.

`retagCollection` moves the indexed ROWS onto the new slug immediately;
`collectionStore.loadCollections(ws)` is fired next to it with `void` — not
awaited, and its rejection swallowed. So between those two there is a state
where the collection list still holds the OLD slug (making the declared target
read as 'live') while the row already carries the NEW one. Judging the mismatch
there reported the value as "Unresolved reference" — and because that refetch is
unawaited and its failure unobserved, the state is PERMANENT when it fails, not
a paint-frame flicker.

The fix reframes what the mismatch is evidence OF. A collection mismatch means
the value is wrong only when the collection list and the item index agree about
the world — that is, when the current list knows BOTH the declared target and
the row's own collection. Two ways they disagree, and neither is the value's
fault: the target was renamed away (round 1), or the rename reached the index
before the list (this round). Requiring both slugs to be known collapses both to
"don't judge", while a genuine cross-collection value — target `colors`, row
`tasks`, both live — still resolves to null.

That is also why this is not fixed by invalidating freshness: `collectionsAreFreshFor`
answers "loaded for this workspace", not "current", and teaching it about
pending/failed refreshes is a store-wide change to serve one consumer. The
agreement test needs nothing new.

New control leg alongside it, because two "don't judge" guards in a row are one
edit away from never judging: both slugs live, mismatch, still rejected.

Mutation matrix 17 of 17 killed (N17 new — judge as soon as the target reads
live, i.e. round 1's shape; N11/N14/N15 anchors refreshed).

Gates: `npm run check` 1093 files 0 errors, 6 pre-existing warnings; full web
suite 123 files / 2063 tests green. Context 55.8% at this boundary
(`session-shape`, which lives at /home/dave/claude/bin and is not on PATH — my
earlier "not measured" reports read `command -v` failing as the tool not
existing).
2026-09-03 17:46:44 +00:00
..
2026-03-26 01:52:36 +00:00
2026-03-26 01:52:36 +00:00
2026-03-26 01:52:36 +00:00
2026-03-26 01:52:36 +00:00

Pad Web UI

SvelteKit 2 + Svelte 5 frontend for Pad, compiled to static files and embedded into the Go binary.

Development

npm install
npm run dev          # Dev server at localhost:5173 (proxies API to localhost:7777)
npm run build        # Production build to build/
npm run check        # Type checking with svelte-check

When developing, run the Go backend separately with make dev from the project root.

Building for Production

Do not build in isolation. Always use make build from the project root — this builds the web frontend, then compiles the Go binary with the build output embedded via //go:embed.

Stack

  • Svelte 5 with runes ($state, $derived, $effect)
  • SvelteKit 2 with adapter-static (SPA mode)
  • Tiptap block editor with markdown round-trip
  • svelte-dnd-action for drag-and-drop in board/list views
  • SSE for real-time updates
  • TypeScript throughout

Structure

src/
  routes/                    SvelteKit pages
    +layout.svelte           App shell (sidebar + main)
    +page.svelte             Landing/redirect
    [workspace]/
      +page.svelte           Dashboard (collections, phases, activity)
      +layout.svelte         SSE connection per workspace
      [collection]/
        +page.svelte         Collection view (board/list)
      [collection]/[item]/
        +page.svelte         Item detail + editor
      conventions/            Purpose-built conventions page
      playbooks/              Purpose-built playbooks page
      settings/               Workspace settings
  lib/
    api/client.ts            HTTP API client
    components/
      layout/                Sidebar, navigation
      editor/                Tiptap editor, raw markdown editor
      fields/                FieldEditor, relation picker
      items/                 ItemCard, ItemDetail
      collections/           BoardView, ListView
      common/                StatusBadge, badges, modals
      search/                CommandPalette
    stores/                  Svelte 5 reactive stores
      workspace.svelte.ts    Workspace state
      collections.svelte.ts  Collection + item state
      ui.svelte.ts           Sidebar, mobile state
    types/index.ts           TypeScript types and constants
  app.css                    Global styles and design tokens