summaryrefslogtreecommitdiffhomepage
AgeCommit message (Collapse)Author
2026-06-07feat(chat): drop the faded opacity on in-flight messagesAdam Malczewski
Remove the opacity-50 dimming applied to provisional (streaming) chunks across user/assistant/tool/batch rendering; in-flight content now renders at full opacity. Test updated to assert no dimming.
2026-06-07test(probe): live-verify tool-call batching (stepId) end-to-endAdam Malczewski
Extend scripts/live-probe.ts with a second turn that elicits parallel tool calls and asserts: live tool events carry stepId, replayed chunks carry chunk.stepId, and groupRenderedChunks folds the batch identically live and on replay. Deltas now routed by conversationId. Gated (not in bun run test). Verified 13/13 against bin/up.
2026-06-07feat(chat): group batched tool calls into one DaisyUI listAdam Malczewski
Consume the backend's new stepId grouping key (wire/transport-contract 0.1.0 -> 0.2.0). foldEvent copies event.stepId onto live tool chunks so live and replay group identically. New pure selector groupRenderedChunks (core/chunks) folds a step's 2+ tool calls into one tool-batch group, pairing each call with its result by toolCallId; single/no-stepId calls stay as cards. ChatView renders a batch as a DaisyUI list (list-row per pair). Fixtures updated for the now-required event stepId.
2026-06-07feat(chat): restyle transcript — left-aligned, bubbleless assistant, tool ↵Adam Malczewski
cards All messages flow left in one column via the DaisyUI chat-start grid: - user keeps a primary speech bubble; - assistant/system/error render in a transparent (invisible) chat-bubble so they read as plain prose yet inherit identical left spacing, capped to a readable max-w-5xl column; - tool call/result render as regular (non-speech) rounded cards, nested in the same grid so they line up too; - role header labels dropped; chat-wide left padding added. Alignment uses specificity-based variants (no !important).
2026-06-07feat(tabs): polish new-chat button — stuck-only square edge, New Chat labelHEADmainAdam Malczewski
- isStuckToEnd (pure): square the sticky '+' right edge only while it floats over scrolled tabs; rounded at rest. Edge-measured in TabBar via a disposed scroll + ResizeObserver effect (RO guarded for non-browser envs). - Show a temporary 'New Chat' title when the draft is selected, with the '+' moved to the trailing close-button slot for consistency with real tabs.
2026-06-07feat(tabs): extract TabBar component with horizontal scroll + sticky end '+'Adam Malczewski
Move inline tab-bar markup from the composition root into a thin presentational TabBar in the tabs feature (feature-as-a-library: pure reducer -> reactive store -> UI). Adds overflow-x scroll (min-w-max strip) and a sticky right-pinned new-chat '+' that floats over scrolling tabs. Draft-on-select / create-on-send behavior unchanged.
2026-06-07docs(harness): add frontend-styling rule + parallel-build verify caveatAdam Malczewski
- add .dispatch/rules/frontend-styling.md (DaisyUI v5 + dracula; thin components) - package-agent: verify section explains whole-project svelte-check under parallel builds (judge only YOUR files)
2026-06-07ORCHESTRATOR.md: document parallel-execution craft + recovery patternsAdam Malczewski
Fold in the working practices that were implicit: - §2a Waves: disjoint+no-compile-dep batching; widen waves by removing edges; parallel = N summon calls in ONE message (resolves the no-background tension) - §3: pre-author the cross-unit seam; tell agents about concurrent siblings; make agents implement (not deliberate) - §4: whole-project-check concurrency caveat + double-run for shared-global flakiness - §5a Recovery: plan-only agent re-summon; sibling test fan-out; lane-stray handling; flaky green - §9 Live integration probe (gated scripts/live-probe.ts; backend is user's process) - §10 Living backend-handoff.md as the cross-repo courier channel - generalize contract mirroring to all .dispatch/*.reference.md; re-read rules before each wave (a miss this session)
2026-06-07fix: optimistic user message echo + tabs persistenceAdam Malczewski
Bug 1 (sent message didn't appear until turn end): the transcript only folded assistant AgentEvents, so the user's own message showed only after turn-sealed resync. Add core/chunks appendUserMessage() (provisional user chunk, superseded on history sync) and call it in chat send() — the message now renders instantly. Bug 2 (tabs didn't persist on refresh): the app passed { storage: undefined } to createLocalStore, which the adapter treats as a no-op store, so nothing was saved. Default to globalThis.localStorage. Regression test exercises the non-injected path. Also updated app store tests for the echo (assistant-vs-user chunk filtering). Verified: svelte-check 0/0, vitest 288 (stable x2), biome clean, build ok.
2026-06-07handoff: Slice 3 FE-complete; model-not-persisted findingAdam Malczewski
2026-06-07Slice 3 wave B: tabbed multi-conversation app + model selector (DaisyUI)Adam Malczewski
- store.svelte.ts: tabs store over injected localStorage; one chat store per conversation (Map); single WS routes chat.delta/error by conversationId; draft (null active) mints a conversationId and becomes a tab on first send (title from deriveTitle); GET /models catalog; default model flash; close tab = dispose + cache.delete (local forget) + neighbour activation; restore tabs from storage + load() on construct - App.svelte: DaisyUI tab strip (+ / close), model selector, chat, surfaces - AppStore: tabs/activeConversationId/activeChat/models/activeModel + send/selectModel/newDraft/selectTab/closeTab; +localStorage inject opt Verified: svelte-check 0/0, vitest 281 (stable x2), biome clean, build ok.
2026-06-07Slice 3 wave A: tabs model, model selector, cache delete, localStorageAdam Malczewski
- features/tabs: pure tab-workspace reducer (create/select/close/setModel/ setTitle/deriveTitle, draft=null active) + injected-persistence runes store - features/chat: mutable per-tab model (setModel) + delta routing guard (ignore foreign conversationId) + ModelSelector.svelte + DaisyUI chat bubbles / composer (keeps streaming <details> keying fix) - features/conversation-cache: surface delete(conversationId) on the wrapper for tab-close local-forget - adapters/local-storage: generic injected JSON localStore<T> (quota/corrupt-safe) Verified: svelte-check 0/0, vitest 273, biome clean, build ok.
2026-06-07Slice 3 setup: Tailwind v4 + DaisyUI v5 (dracula enabled) + 'tab' vocabAdam Malczewski
- add tailwindcss@4 + @tailwindcss/vite + daisyui@5; vite tailwind plugin - src/app.css: @import tailwindcss + @plugin daisyui { themes: dracula --default } (theme BUNDLED, not just named); imported in main.ts - index.html: <html data-theme="dracula"> - biome.json: enable css tailwindDirectives so @plugin parses - GLOSSARY: define FE term 'tab' (workspace slot referencing a conversation; holds conversationId + model + title; close = local forget) Verified: dracula tokens bundled in CSS, svelte-check 0/0, vitest 222, biome clean, build ok.
2026-06-07fix(chat): keep thinking <details> open while streamingAdam Malczewski
ChatView keyed the transcript each-block by object identity, but core/chunks returns new RenderedChunk objects per delta, so Svelte recreated each <article>/<details> every frame — an opened Thinking element snapped shut on the next token. Key by stable identity instead (c${seq} for committed, p${i} for append-only provisional) so streaming reuses the DOM. Adds a regression test that the <details> stays open across a streaming update. Verified: svelte-check 0/0, vitest 222, biome clean, build ok.
2026-06-07chore: biome-format scripts/live-probe.tsAdam Malczewski
2026-06-07Slice 2 live-verified: e2e chat probe 9/9 against running backendAdam Malczewski
- scripts/live-probe.ts: gated bun harness driving the real FE stack (adapters/ws + core/chunks + conversation-cache + adapters/idb + HTTP history) against bin/up; not part of `bun run test` - backend-handoff.md: record the 9/9 live result; no backend mismatch
2026-06-07fix: blank page on non-localhost HTTP (secure-context crypto.randomUUID)Adam Malczewski
crypto.randomUUID() is secure-context-only — undefined on plain-HTTP non-localhost origins (e.g. http://arch-razer:24204), so createAppStore threw during mount and nothing rendered. Add src/app/uuid.ts randomId(): prefer crypto.randomUUID when present, else build a v4 from crypto.getRandomValues (available in insecure contexts), else Math.random fallback. Use it for the conversation id. Verified: svelte-check 0/0, vitest 221, build ok.
2026-06-07handoff: Slice 2 FE-complete; request live e2e probe coordinationAdam Malczewski
2026-06-07Slice 2 wave 3: wire chat end-to-end at the composition rootAdam Malczewski
- app/store.svelte.ts: one WebSocket carries surfaces AND chat (onChat -> chatStore.handleDelta); build the conversation cache over the IndexedDB adapter; createChatStore wired to transport (socket.send), injected HTTP historySync, and the cache; load() on construct - app/resolve-http-url.ts: host-relative HTTP base (port 24203), mirrors resolve-ws-url; injected fetch - App.svelte: render ChatView + Composer alongside the surface picker - createAppStore gains optional injection points (httpUrl/fetchImpl/indexedDB/ conversationId) for tests - vitest-setup.ts: fake-indexeddb/auto for jsdom IndexedDB (orchestrator-owned config; agent change adopted) Verified green (x2, stable): svelte-check 0/0, vitest 218, biome clean, build ok. Slice 2 (conversation transcript: cache + delta streaming) feature-complete.
2026-06-07Slice 2 wave 2: IndexedDB cache adapter + chat featureAdam Malczewski
- adapters/idb: createIdbChunkStore implements the ConversationChunkStore port over IndexedDB (compound [conversationId,seq] key, idempotent append, meta store for lastAccess); 8 tests with fake-indexeddb - features/chat: createChatStore (runes-thin over the core/chunks reducer, all effects injected via ChatTransport/HistorySync/ConversationCache ports) + ChatView/Composer svelte-thin UI; folds chat.delta, syncs on turn-sealed, hydrates from cache then catches up; 25 tests Verified green: svelte-check 0/0, vitest 202, biome clean, build ok.
2026-06-07Slice 2 wave 1: transcript reducer, wire conformance, ws chat, cache coreAdam Malczewski
- core/chunks: the one pure transcript reducer (foldEvent live deltas + applyHistory seq-keyed reconcile + selectChunks/selectMessages); 27 tests - core/wire: FE-side contract-conformance exhaustiveness guards + drift smoke tests over wire/transport-contract unions (§2.9 drift signal); 10 tests - adapters/ws: additively multiplex chat.send/chat.delta/chat.error on the existing surface socket (onChat + widened send); surface API unchanged - features/conversation-cache: pure reconcileCache/nextSinceSeq/selectEvictions + ConversationChunkStore port + injected createConversationCache; 26 tests Verified green: svelte-check 0/0, vitest 169, biome clean, build ok.
2026-06-06Slice 2 unblock: pin wire + transport-contract; mirror contractsAdam Malczewski
- pin @dispatch/wire + @dispatch/transport-contract (+ ui-contract) as file: deps @0.1.0; overrides{} workaround for their workspace:* deps (bun can't resolve workspace: from outside the monorepo) - add fake-indexeddb (dev) for the upcoming IndexedDB adapter tests - mirror wire + transport-contract into .dispatch/*.reference.md for headless agents; point package-agent.md at all three references - backend-handoff.md: convert to a living FE<->backend seam doc Verified green: svelte-check 0/0, vitest 91, biome clean, build ok; contract import smoke-test passes.
2026-06-06Slice 1 follow-up: component-render interaction tests (CR-1/CR-2)Adam Malczewski
- vite: add @testing-library/svelte's svelteTesting() plugin so component render()/mount() resolves Svelte's browser build under vitest/jsdom - dep: @testing-library/user-event for realistic interaction tests - app: 7 component-render tests driving App.svelte through a fake socket (catalog render, subscribe-on-click, unsub/sub ordering, aria-current, error banner, action invoke) Verified green: svelte-check 0/0, vitest 91 passed, biome clean, vite build ok.
2026-06-06Slice 1: surface system + WS transport + composition rootAdam Malczewski
Pure-core feature libraries assembled at the composition root: - core/protocol: pure reducer over surface catalog/spec/error messages - features/surface-host: generic field-kind interpreter (toggle/progress/ selector/stat/button) + pure plan logic; no surface-id special-casing - adapters/ws: injected WebSocket client (effects at the edge) - app: composition root store (Svelte 5 runes over the pure reducer), host-relative surface WS URL resolution (resolveWsUrl), root App.svelte Verified green: svelte-check 0/0, vitest 84 passed, biome clean, vite build ok.