mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-11 13:28:57 +00:00
f7bb735771
Codex review round 5, two P1s, both about `localIndex.reset()` — the sign-out / 403-purge / deleted-workspace path. THE FLAG WAS THE WRONG WAY ROUND. `coldFailed` asked "did the last search fail", and that was false in three states that are not answers at all: before the first request, after a failure, and after a reset drops every row while the query sits in the box. Each one read as "fine" and put a create row on screen backed by nothing. Inverted to `coldAnswered` — set in exactly one place, by the event that earns it, and cleared wherever the answer stops describing what is in the box. A flag that must be cleared everywhere is one that will be missed somewhere; this is the same defect arriving twice (round 3 caught the failure case, round 5 the reset case) because the polarity made silence indistinguishable from success. THE EPOCH FENCE HAD TO BE TWO-SIDED. `upsert`'s own guard refuses a captured epoch BELOW the current one, which catches a resync. But `reset()` DELETES the workspace state and the next bootstrap starts a fresh one at `scopeEpoch` 0 — so a captured 7 is not below 0, sails through, and links a row minted under an identity that no longer holds. `createRelationTarget` now requires equality. The residual is in the code comment rather than papered over: a reset plus resyncs landing back on exactly the captured number would compare equal, which an exposed reset generation would catch and this does not. Also dropped the `loading` term from `showCreate`. It and the per-query `coldAnswered` reset were a redundant PAIR — each survived removal while the other stood, which is one guard and one line that looks like a guard, not defence in depth (this repo has a note about exactly that shape). `coldAnswered` is the one kept: it states the rule (something authoritative has answered FOR THIS QUERY) where `loading` is a UI state that correlates with it. Matrix: 24 mutants, all killed; baseline and restore both 85/85. Killing the per-query reset needed `aria-expanded`, not the row's absence — with `loading` still gating the MARKUP, `.picker-create` is missing either way and asserting on it measures the branch instead of the rule. Third time this suite has been fooled by that same separation. Re-verified end to end in a real browser on this exact build: create row offered for a non-matching query and keyboard-reachable; Enter created COLO-6 "Chartreuse" in COLORS (colors 2 -> 3, cars unchanged) with `status: approved` — the schema's declared default, which the "+ New" `options[0]` heuristic would have gotten wrong; the car's field holds that id; a second pass at the same text offers the existing row and no create; Escape leaves the value untouched; no bare UUID anywhere on the page.
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