diff --git a/CLAUDE.md b/CLAUDE.md index e1010a8..8d10647 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,7 +63,7 @@ ui/ ### Design System v2 (`ui/`) — added 2026-06-21 -Full spec: `DESIGN.md`. Branch: `feature/design-system-v2`. +Full spec: `DESIGN.md`. **Six pillars** (load-bearing — if any regress, the design regresses): @@ -103,6 +103,33 @@ Full spec: `DESIGN.md`. Branch: `feature/design-system-v2`. - **Language selector** in Settings: System / Deutsch / English. Stored as `locale` in SecureStore. Note: currently only stores the preference; runtime locale switching (AppCompatDelegate.setApplicationLocales) needs wiring in MainActivity on next iteration. - No server-side theme storage — web uses localStorage, app uses SecureStore. Both are client-local. +### Sentinel parsing — reasoning, sources, tools (added 2026-06-21) + +The `/api/v1/chat` stream embeds control sections via sentinel code points (from `lib/chat/constants.ts`): + +| Sentinel | Code | What | App UI | +|----------|------|------|--------| +| `\x00` USAGE | trailing | Usage/cost JSON | Stripped (display TODO) | +| `\x01` REASONING | toggle | Model's chain-of-thought | `ReasoningBlock` — collapsible "Gedankengang" | +| `\x02` SOURCES | trailing | Web search sources JSON | `SourcesBlock` — compact cards with domain | +| `\x03` TOOL | leading | Tool-call events (JSON+newline) | `ToolEventsBlock` — status icons per step | +| `\x04` QUERY | trailing | Search query string | Shown above sources | + +**StreamConsumer.Incremental** (`net/StreamConsumer.kt`) returns `StreamState` (not `String`): +- `content` — visible text (reasoning stripped, tool events stripped) +- `reasoning` — full reasoning text +- `tools` — `List` (type, name, args, result, error, elapsed) +- `query` — search query string +- `sources` — `List` (title, url, snippet) + +**KaizenApi.chat()** returns `Flow`. ChatScreen maps all fields to `Message` during streaming. + +**UI files:** `chat/StreamBlocks.kt` — `ReasoningBlock`, `ToolEventsBlock`, `SourcesBlock`, `SourceChip`. + +### App icon + +Adaptive icon: amber orb with multi-layer radial gradients (body, depth shadow, specular highlight, warm bounce light, crisp rim). Light mode: warm off-white background. Dark mode: Obsidian gradient. Monochrome layer for Android 13+ themed icons. Splash screen uses orb foreground (API 31+). Vector-only — no raster WebPs. + ### Offline-first cache layer (Room) — added 2026-06-21 Architecture decision document: `OFFLINE_CACHE_ADD.md`. @@ -212,9 +239,9 @@ These are what separate "prototype" from "daily-driver": ### 4. Features for parity with the web - [x] **Attachments** — image/file upload, attachment preview in messages, display in loaded conversations (camera capture still TODO) -- [ ] **Reasoning/thinking display** — parse `\x01` sentinel, show collapsible thinking block -- [ ] **Web search sources** — parse `\x02`/`\x04` sentinels, render source cards -- [ ] **Tool call display** — parse `\x03` sentinel, show live tool steps +- [x] **Reasoning/thinking display** — `ReasoningBlock` (collapsible), parses `\x01` sentinel +- [x] **Web search sources** — `SourcesBlock` + `SourceChip`, parses `\x02`/`\x04` sentinels +- [x] **Tool call display** — `ToolEventsBlock`, parses `\x03` sentinel, status icons per step - [ ] **Native passkey login** (WebAuthn/FIDO2 on Android) - [ ] **WebSocket streaming** (replace chunked HTTP for lower latency on mobile) - [ ] **Usage/cost display** @@ -253,9 +280,9 @@ securityCrypto = "1.1.0-alpha06" **Unit tests** (`app/src/test/`): JVM-only, no emulator needed. -- `SettingsViewModelTest` — state transitions, default values, theme selection, name updates -- `StreamConsumerTest` — incremental parser, sentinel stripping, edge cases -- `OklabTest` — 2 pre-existing failures (round-trip color drift > 0.01 tolerance, unrelated to backend wiring, likely too-tight tolerance or headless float precision) +- `SettingsViewModelTest` — state transitions, all 4 themes, appearance modes, name updates with store-less default +- `StreamConsumerTest` — batch + incremental parser, sentinel stripping, reasoning/sources/tools parsing, char-by-char edge cases +- `OklabTest` — round-trip conversion, interpolation midpoint, gradient generation (all green since Double-precision fix) - `MappersTest` — `ConversationSummary` ↔ `ConversationEntity`, `StoredMessage` ↔ `MessageEntity`, round-trip integrity, attachment preservation - `ConvertersTest` — `List` ↔ JSON round-trips, malformed JSON returns empty list, unknown fields tolerance via `ignoreUnknownKeys` - `ColorPipelineTest` — all tokens in DisplayP3, approximate value ranges, no pure black/white in neutrals @@ -263,10 +290,6 @@ securityCrypto = "1.1.0-alpha06" - `ThemeRegistryTest` — 4 themes present, forId lookup, fromString parsing + fallback, distinct blob colors - `ShadowStackTest` — layer counts per level, alpha range validation, blur/alpha progression -## Pre-existing issues (not from this work) - -- `OklabTest` has 2 headless failures (`testColorToOklabAndBack`, `testGradientGeneration` — round-trip color drift > 0.01 tolerance). Unrelated to backend wiring; likely too-tight tolerance or headless float precision. - ## Constraints & gotchas - Local dev Mac has **no RustFS** (storage on server only); upload/storage flows fail locally with `ECONNREFUSED :9000`.