- Issue: Could not access the app. Sandbox browser is disabled (
agents.defaults.sandbox.browser.enabledis false), host browser requires Chrome extension tab attachment, andweb_fetchblocks private IPs. - Action needed: Enable sandbox browser in OpenClaw config, or attach a Chrome tab before running QA.
- Bugs found: 0
- UX suggestions: 0
- Area 1: Onboarding / First run (once browser access is resolved)
- Pages visited: Login page (
/), Invalid invite page (/invite/test-invalid-token), Non-existent page redirect - Browser: Used
openclawprofile (headless Chromium) - works! - Login method: Used JS
evaluateto set React input values (nativetypeaction doesn't trigger React state updates on this app) - Bugs found: 2 (issues created: #22, #23)
- #22 (bug): App crashes with
TypeError: Cannot read properties of null (reading 'charAt')when a user has nouser_profilesrow. Root cause:UserMenu.tsxaccessesuser.firstName.charAt(0)without null check. Affects 4 files. - #23 (bug/security): The
POST /api/auth/sign-up/emailendpoint is open to anyone, allowing unauthenticated account creation even though the UI has no sign-up form. Combined with #22, this means anyone can create accounts that crash the app.
- #22 (bug): App crashes with
- UX suggestions: 1 (issue created: #24)
- #24 (enhancement): Login page missing "Forgot password?" link, loading state on Sign In button, and Enter-to-submit confirmation.
- All clear:
- Invalid credentials error message is clear and well-positioned
- Show/hide password toggle works correctly (label updates too)
- Empty form submission handled by HTML5 validation
- Invalid invite tokens show proper error with "Go to login" button
- Non-authenticated routes correctly redirect to login
- Cloudflare Turnstile is integrated (visible in page source, though may not block headless browsers)
- Area 2: Kin management (need to login as Nicolas or create a proper test user through onboarding)
- Note: To test authenticated pages, either need Nicolas's password or a way to create users through the onboarding flow
- Pages visited: Main dashboard (
/), New Kin dialog, Edit Kin dialog (General, Tools, Memory tabs), Delete confirmation dialog, Kin chat view (/kin/test-qa-kin) - Browser:
openclawprofile (headless Chromium), logged in as existing user - Login: Credentials from
e2e/helpers/auth.ts(test@kinbot.local) - Bugs found: 1 (issue created: #30)
- #30 (bug): Import button in "New Kin" dialog does nothing when clicked. No file picker, no UI change, no error. Either the feature is broken or unimplemented without any user feedback.
- UX suggestions: 2 (issues created: #29, #31)
- #29 (enhancement): Model selector shows ALL models from providers including non-chat models (audio, realtime, transcribe, TTS, image-only). The list is enormous and overwhelming. Should filter to chat-compatible models only.
- #31 (enhancement): Slug field is only available in Edit mode, not during Kin creation. Minor QoL improvement to allow setting it upfront.
- All clear:
- Create flow works well: "Describe your Kin" dialog with Generate/Create manually/Import options
- Generate button correctly disabled until text is entered
- Manual creation form has proper required field validation (Name, Role, Model all required, Create button disabled until filled)
- Avatar auto-generates initials from name (e.g. "TE" for "Test QA Kin")
- Model selector has provider filter buttons (All, Claude, OpenAI) that work correctly
- Token count updates in real-time for Character and Expertise sections
- Default Character and Expertise templates are helpful
- Edit dialog has all creation fields plus Slug, and full Tools/Memory tabs
- Tools tab shows all native tool categories with toggle switches and count (e.g. "Search 1/1", "Web Browse 3/3")
- Tool categories are expandable to show individual tools
- Opt-in tools (Kin Management, System, Database) are correctly off by default
- Memory tab in edit shows search, category filter, empty state, and "Add memory" button
- Add memory form has Content, Category dropdown (default: Fact), Subject (optional)
- Memory save button correctly disabled until content is filled
- Delete flow has proper confirmation dialog ("Are you sure? This will permanently delete...")
- Delete works correctly, Kin removed from sidebar immediately
- Chat view loads properly when selecting a Kin, with conversation starters and rich text editor
- Kin card ordering in sidebar can be changed (drag handles present)
- Slug auto-generated from name correctly (e.g. "test-qa-kin")
- Area 3: Conversations (send messages, check chat UI, scroll behavior, empty states)
- Or Area 4: Tasks/Crons (create, edit, enable/disable, delete tasks)
- Pages visited: Settings dialog (all 11 tabs: General, AI Providers, Search, MCP Servers, Vault, Memories, Files, Channels, Webhooks, Contacts, Users, Notifications)
- Browser:
openclawprofile (headless Chromium) - Login: Existing session from previous run
- Bugs found: 1 (issue created: #37)
- #37 (bug/i18n): In Settings > Notifications, the last toggle shows raw i18n keys
notifications.types.mentionandnotifications.descriptions.mentioninstead of translated text. All other notification types display correctly.
- #37 (bug/i18n): In Settings > Notifications, the last toggle shows raw i18n keys
- UX suggestions: 0
- All clear:
- Settings dialog opens correctly as a modal overlay
- Left navigation has clear grouping (Core, Extensions, Connections, Access) with 11 tabs total
- General: Global prompt textarea with live token counter, Save button disabled when no changes
- AI Providers: Two providers (Anthropic Claude Max, OpenAI) displayed with capability badges (LLM, Embedding, Image). "Test all connections" button works perfectly - shows progress bar, "2 passed" result, and toast notification. "Add provider" form has 22+ provider types in dropdown with capability labels, proper required field validation (API key), "Show password" toggle, helpful links to get API keys
- Search: Brave Search configured, default provider dropdown, add button
- MCP Servers: Clean empty state with description and add button
- Vault: Category filter tabs (All, Favorites, Secret, Credential, Card, Note, Identity), "Manage types" button, clean empty state
- Memories: Model Configuration section (Extraction Model, Embedding Model dropdowns), "Re-embed all memories" button, search bar with category and Kin filters, 5 memories shown with rich metadata (category, subject, source Kin, auto/manual, score), edit/delete per memory, "Add memory" button
- Files: Clean empty state with upload button
- Channels: Clean empty state with descriptive text
- Webhooks: Clean empty state
- Contacts: Shows Nicolas VARROT with "Human" badge, email, edit/delete buttons, "Add note" and "Add contact" buttons
- Users: Shows user profile (Nicolas VARROT, @MarlburroW, email, join date, language "fr"), Invitations section with "Invite" button
- Notifications: 7 notification toggles with descriptions, all checked by default. Notification sound toggle. External delivery section with "Add delivery channel" button
- Footer shows version (v0.9.0), uptime, and summary counts consistently across all tabs
- "What is this?" expandable help section present on most tabs
- Every empty state has clear description text and a call-to-action button
- Note: Kin pages (
/kin/dev,/kin/dispatcher) consistently cause the headless browser to timeout/hang, likely due to WebSocket connections or heavy React rendering. This blocked testing Area 3 (Conversations). May be specific to headless Chromium rather than a real user-facing issue.
- Area 4: Tasks/Crons (create, edit, enable/disable, delete tasks - can test from sidebar without entering a Kin page)
- Or Area 3: Conversations (if browser stability improves)
- Pages visited: Main dashboard (
/), Create scheduled job dialog, Job detail dialog, Edit job dialog, Delete confirmation dialog - Browser:
openclawprofile (headless Chromium), host target - Login: qa@kinbot.local (password reset via DB to bypass auth)
- Bugs found: 0
- UX suggestions: 3 (issues created: #38, #39, #40)
- #38 (enhancement): Schedule field shows no validation feedback for invalid cron expressions. The human-readable description disappears silently, no error message or red border.
- #39 (enhancement): Task instructions are not required to create a scheduled job. A job with empty instructions can be created and will fire with nothing for the Kin to do.
- #40 (enhancement): Schedule display ("At 09:00") shows no timezone information. Users don't know if this is UTC, server time, or their local timezone.
- All clear:
- Scheduled Jobs section has clean empty state with description and CTA button
- "New job" button opens a well-structured creation dialog
- Cron preset buttons (Every 5 min, Hourly, Daily 9am, etc.) work correctly and fill the schedule field
- Human-readable cron description ("At 09:00") appears correctly for valid expressions
- "Minute Hour Day Month Weekday" helper text below schedule field is helpful
- Owner Kin dropdown shows all Kins with avatars, names, and descriptions
- Target Kin and Model fields are optional with good default messaging
- Form validation enables Create button when Name + Owner Kin + Schedule are filled
- Created jobs appear immediately in sidebar with name, schedule description, and next-run countdown
- Toggle switch to enable/disable jobs works correctly - paused jobs lose countdown timer
- Job detail dialog shows all info: name, owner Kin, status, schedule, instructions, model, execution history
- Edit dialog pre-fills all fields correctly
- Edit has "Delete job" and "Save changes" buttons
- Delete has proper confirmation dialog with clear warning text
- Unsaved changes dialog appears when closing form with modifications
- Task search in sidebar works (filters tasks by name, shows "No tasks found" empty state)
- Collapsible sidebar sections (Tasks, Scheduled Jobs, Mini-Apps) toggle correctly
- Jobs search bar appears when jobs exist
- Note: Browser automation continues to be challenging with KinBot. The
typeaction does not reliably fill React controlled inputs (Name field). CodeMirror editor (Task instructions) requiresdocument.execCommandto properly update state. Kin pages still cause browser timeouts. Auth required resetting the QA user password via DB because the test@kinbot.local user from E2E helpers doesn't exist in the live instance.
- Area 3: Conversations (send messages, chat UI, scroll behavior) - requires navigating to a Kin page which causes browser hangs
- Area 11: Contacts (add, approve, edit, delete) - testable from Settings
- Area 12: Webhooks (create, edit, test, delete) - testable from Settings
- Area 5: Provider settings - testable from Settings
- Pages visited: Settings > Contacts, Settings > Webhooks, Settings > Channels
- Browser:
openclawprofile (headless Chromium), host target - Login: Existing session
- Bugs found: 0
- UX suggestions: 0
- All clear:
- Contact list shows existing contacts with type badge (Human), name, and identifiers
- "Add contact" form: Name (required), Type dropdown (Human, Kin), Link to system user (None, existing users), Identifiers section
- Identifiers: "Add identifier" creates inline row with type combobox (email, phone, mobile, twitter, instagram, linkedin, github, slack, website + searchable) and value textbox
- Button correctly disabled until Name is filled
- Contact creation shows "Contact added" toast, appears immediately in list
- Edit form pre-populates all fields correctly (name, type, identifiers)
- "Link to system user" dropdown shows registered users with display name + username
- "Add note" feature: inline note form with Kin selector, scope (Global/Private), and note textbox
- Delete has proper confirmation dialog ("This will permanently delete this contact and all associated identifiers and notes")
- Delete shows "Contact deleted" toast, contact removed immediately
- CRUD flow is complete and works correctly
- Bugs found: 0
- UX suggestions: 0
- All clear:
- Clean empty state with description and CTA
- "Add webhook" form: Target Kin (required, shows Kin list with descriptions), Name (required), Description (optional)
- Creation shows "Webhook created" dialog with URL and masked token, warning "Save this token now - it will not be shown again" (good security)
- Webhook list shows: name, target Kin, trigger count, last triggered, enable/disable toggle
- Actions: View logs, Copy URL, Regenerate token, Edit, Delete
- Toggle works correctly with "Webhook updated" toast
- View logs shows clean empty state ("No triggers yet")
- Delete confirmation warns about external services receiving 404 errors (helpful)
- Delete works correctly with "Webhook deleted" toast
-
All clear:
- Clean empty state with clear description
- "Add channel" form: Name, Kin (required), Platform dropdown, platform-specific fields
- Platform options: Telegram, Discord, Slack, WhatsApp, Signal, Matrix
- Telegram selected by default, shows Bot token field with password toggle and "How to get your bot token" link
- Token stored encrypted in vault (noted in UI)
-
Note: Both Contacts and Webhooks areas are very polished. CRUD flows work end-to-end, confirmation dialogs are present for destructive actions, toasts provide good feedback, empty states are clear. No bugs found in these areas.
- Area 3: Conversations (send messages, chat UI, scroll behavior) - requires entering Kin page
- Area 13: MCP Servers (add, configure, remove) - testable from Settings
- Area 7: Memory (browse, search, delete) - testable from Settings
- Pages visited: Settings > MCP Servers, Settings > Plugins, Settings > Vault, Settings > Memories, Settings > Files, Settings > Search, Settings > Users, Settings > Notifications
- Browser:
openclawprofile (headless Chromium), host target - Login: Existing session
- Bugs found: 0
- UX suggestions: 0
- All clear:
- Clean empty state with clear description and CTA button
- "What is this?" expandable section with detailed MCP documentation (4 bullet points)
- "Add MCP server" form: Name (required), Command (required), Arguments (optional, one per line), Environment variables (optional)
- Button correctly disabled until Name + Command are filled
- Env variables: KEY/value inputs, value masked as password with show/hide toggle, delete button per row, "Add variable" adds rows
- Creation shows "MCP server added" toast, server appears immediately in list
- Server card shows: name, status (Active), command, env var keys (values hidden)
- Edit button opens "Edit MCP server" dialog with all fields pre-populated
- Env var value correctly masked in edit form
- Delete has proper confirmation dialog: "This will permanently remove this MCP server and disconnect it from all Kins"
- Delete shows "MCP server deleted" toast, returns to empty state
- Full CRUD cycle works end-to-end
- Bugs found: 1
- #43 (bug): Clicking "Plugins" in Settings crashes the entire app with error boundary: "Cannot read properties of undefined (reading 'length')". 100% reproducible. Critical - crashes entire React app.
- UX suggestions: 0
- Vault: Works. Filter tabs (All, Favorites, Secret, Credential, Card, Note, Identity), "Manage types" button, clean empty state
- Memories: Works. Model configuration (extraction + embedding models), re-embed button, search with category/Kin filters, 5 memories displayed with edit/delete buttons
- Files: Works. Clean empty state with upload CTA
- Search: Works. Shows existing Brave Search provider, default provider dropdown
- Users: Works. Shows user info (Nicolas VARROT), invitation section
- Notifications: Works. 8 notification types with toggles, external delivery section
- Area 3: Conversations (send messages, chat UI, scroll behavior)
- Area 5: Provider settings (deeper testing of add/edit/delete/test connection)
- Area 14: Account (profile, password, language)
- Pages visited: / (home), /kin/dispatcher (Dispatcher conversation)
- Browser:
openclawprofile (headless Chromium), host target - Login: Existing session (qa@kinbot.local)
Bug: Raw task prompt leaked as visible text (#46)
- When a sub-task fails, the entire raw task prompt (including HTML source, internal instructions, tool call JSON) is rendered as plain text in the conversation
- Makes the conversation unreadable with walls of unformatted code
- Also a security concern (internal prompts exposed)
Bug: Duplicate message content after Memorize tool calls (#47)
- Last assistant message shows the same response content twice with minor wording differences
- Appears to be related to Memorize tool call results being rendered inline as message content
Enhancement: Unlabeled buttons in conversation header (#48)
- Multiple icon-only buttons in the nav bar and Kin header have no aria-label or tooltip
- Impossible to know their function without clicking them
Bug: Model selector still shows non-chat models (#49)
- Regression of #29 (closed) - model dropdown still lists TTS, transcribe, realtime, audio, image, codex, search-api models
- Users could accidentally select an incompatible model
Other observations (not filed):
- User avatar shows "??" for qa@kinbot.local (likely because no display name is configured - works as designed)
- Model selector UI works well: provider grouping, search filter, nice layout
- Message actions (Copy, Edit & resend, React, Read aloud, Regenerate) are well placed
- Tool call panel on the right side works and shows timestamps
- "Compacting conversation" indicator with memory extraction count is clean
- Failed task cards with "Show error details" and "View task details" buttons are good
- Auto-scroll toggle and Scroll to top are present
- Formatting toolbar (Bold, Italic, Strikethrough, Code, Code block) present on input
- Send button correctly disabled when input is empty
- Date separators and time gap indicators ("48 min later") work nicely
Note: Browser repeatedly timed out when interacting with the Dispatcher conversation page. The page is very heavy due to long conversation history + large raw text blocks. Could be a performance concern for long conversations.
- Bugs found: 3 (issues #46, #47, #49)
- UX suggestions: 1 (issue #48)
- All clear: Model selector UI, message actions, tool call panel, compaction indicator, date separators, formatting toolbar, auto-scroll, empty state send button
- Area 14: Account (profile, password, language settings)
- Area 3 continued: Test actually sending a message, editing a message, using reactions (was blocked by browser timeouts on Dispatcher page - try with Dev Kin which has less history)
- Area 15: Quick chat / Ephemeral sessions
- Pages visited: Home page, "My account" dialog (via avatar dropdown menu)
- Browser:
openclawprofile (headless Chromium), host target
Bug: Profile data not persisted after saving (#54)
- Fill in First name ("QA"), Last name ("Tester"), Pseudonym ("qa_bot")
- Click "Save changes" - success toast "Profile updated" appears
- Close and reopen dialog: all fields are empty
- Page reload: all fields still empty
- Avatar initials never update from "??" even after "successful" save
- Either the API call fails silently, backend doesn't persist, or dialog doesn't load saved data on mount
-
Avatar dropdown menu works: shows email, "My account", "Settings", "Sign out"
-
"My account" dialog layout is clean: avatar upload button, email display, form fields, language dropdown
-
Language dropdown works: shows English and Francais (only 2 languages, matching open issues #5/#6 for German/Spanish)
-
Cancel and Close buttons work correctly
-
Form validation: no required field validation on first/last name (acceptable - they're optional)
-
File input for avatar is present (hidden, triggered by avatar button)
-
Version button (v0.9.0) in sidebar timed out on click - could not verify what it opens
-
Bugs found: 1 (issue #54)
-
UX suggestions: 0
-
All clear: Dialog layout, avatar dropdown menu, language selector, Cancel/Close behavior
- Area 15: Quick chat / Ephemeral sessions
- Area 11: Contacts (add, approve, edit, delete)
- Area 12: Webhooks
- Pages visited: Settings > Contacts, Settings > Webhooks, Settings > MCP Servers, Settings > Vault
- Browser:
openclawprofile (headless Chromium), host target
Enhancement: Submit buttons hidden until form is valid in create dialogs (#58)
- In Add contact, Add webhook, and Add MCP server dialogs, the submit button is completely invisible until required fields are filled
- Better pattern: always show the button but keep it disabled
- Affects discoverability for new users
Contacts:
- CRUD flow works perfectly: Add, Edit, Delete all function correctly
- Contact creation saves identifiers (email tested) properly
- Edit dialog loads saved data correctly (unlike profile - see #54)
- Delete has a proper confirmation dialog with clear warning text
- "Add note" feature works: shows Kin selector, scope (Global), and note text field
- Identifier type dropdown comprehensive: email, phone, mobile, twitter, instagram, linkedin, github, slack, website
- Type selector: Human/Kin options available
- "Link to system user" combobox present
Webhooks:
- Full CRUD works: create, view, delete
- Creation shows webhook URL and masked token with "Save this token now" warning (good security)
- Webhook list shows: toggle (enable/disable), View logs, Copy URL, Regenerate token, Edit, Delete
- Trigger log dialog shows (empty state with illustration)
- Delete confirmation dialog with clear "External services will receive 404 errors" warning
- Kin selector dropdown works in creation flow
MCP Servers:
- Empty state with clear description
- "What is this?" expandable info section
- Add dialog has: Name, Command, Arguments, Environment variables sections
- Add variable button for env vars
Vault:
- Categories: All, Favorites, Secret, Credential, Card, Note, Identity
- "Manage types" button present
- Clean empty state
Performance note:
-
Conversation pages (/kin/dev, /kin/dispatcher) consistently cause browser snapshot timeouts (>20s)
-
Settings dialog pages load and respond quickly
-
This is a recurring issue noted in previous sessions
-
Bugs found: 0
-
UX suggestions: 1 (issue #58)
-
All clear: Contacts CRUD, Webhooks CRUD, MCP Servers UI, Vault UI, delete confirmations, identifier management, webhook security (token masking), category filtering in Vault
- Area 15: Quick chat / Ephemeral sessions (need to access via a conversation page, which may require addressing the conversation page timeout issue)
- Area 9: Settings page - remaining tabs (General, Plugins, Browse Plugins, Files, Channels, Users, Notifications)
- Area 8: Mini-apps
- Pages visited: Settings > General, Search, Plugins, Browse Plugins, Memories, Files, Channels, Users, Notifications
Bug: Plugins tab crashes app (regression of #43) - Issue #61
- Settings > Plugins crashes entire app with error boundary
- Error:
Cannot read properties of undefined (reading 'length') - Requires page reload to recover
- 100% reproducible
- Was fixed in #43 but has regressed
Bug: Browse Plugins shows error toasts - Issue #62
- Settings > Browse Plugins shows two error toasts:
Cannot read properties of undefined (reading 'plugins') - Page renders but shows "No plugins found"
- Related to #61, likely same underlying data/API issue
General tab:
- Global prompt field works: typing enables Save button, token counter updates in real-time (~5 tokens for test text)
- "What is this?" expandable info section present
- Save button properly disabled when no changes
Search tab:
- Brave Search provider configured and displayed correctly
- Default provider dropdown with "Automatic (first valid)" option
- Provider card shows capabilities (Web Search)
- Delete, edit buttons present
Memories tab:
- Shows 5 memories with proper metadata (category, scope, Kin, source, relevance score)
- Search bar, category filter, Kin filter all present
- Model Configuration section: Extraction Model and Embedding Model dropdowns
- "Re-embed all memories" button available
- "Add memory" button at bottom
- Each memory has edit and delete buttons
Files tab:
- Clean empty state with illustration
- "Upload file" button (appears twice - one in empty state, one at bottom)
Channels tab:
- Clean empty state with good description
- "Add channel" button (appears twice - one in empty state, one at bottom)
Users tab:
- Shows Nicolas VARROT user with avatar, username (@MarlburroW), email, join date, language (fr)
- Invitations section with "Invite" button
- Clean layout
Notifications tab:
- Comprehensive toggle list: Notification sound, Input needed, User pending approval, Cron pending approval, MCP pending approval, Kin error, Kin alert, Mention
- All toggles checked by default
- External delivery section with "Add delivery channel" button
- Clean descriptions for each notification type
Footer bar (all tabs):
-
Shows version (v0.9.0), uptime (1d 12h), and stats (2 Kins, 3 providers, 5 memories, 1 channel(?), 2 users(?))
-
Bugs found: 2 (issues #61, #62)
-
UX suggestions: 0
-
All clear: General, Search, Memories, Files, Channels, Users, Notifications tabs all work well
- Area 15: Quick chat / Ephemeral sessions
- Area 8: Mini-apps (gallery, viewer)
- Area 4: Tasks/Crons (create, edit, enable/disable, delete)
- Pages visited: Home page sidebar (Mini-Apps section), App Gallery dialog
Bug: Mini-Apps sidebar not updated after cloning from App Gallery - Issue #63
- Clone an app via App Gallery, success toast appears, but sidebar still shows "No apps yet"
- Even after full page refresh, cloned app doesn't appear in sidebar
- Root cause:
useMiniAppshook filters byselectedKinId, which is null on home page - The SSE event
miniapp:createdis filtered by kinId match, so it's silently dropped
Enhancement: Misleading empty state when no Kin selected - Issue #64
- Sidebar shows "No apps yet - Ask a Kin to create one" even when apps exist on other Kins
- Should show "Select a Kin to see its apps" or show all apps across Kins
Enhancement: Clone button doesn't update after cloning, allows duplicates - Issue #65
- After cloning, the Clone button stays active (not "Owned"/"Cloned")
- User can clone the same app multiple times, creating duplicates
- Gallery doesn't refresh data or track cloned state
-
MiniAppViewer is well-built: postMessage SDK with toast, navigate, fullpage, confirm/prompt dialogs, clipboard, download, notifications, send-message, share, resize, locale/theme sync
-
Rate limiting on send-message (5/30s) is good
-
Sandbox iframe with appropriate permissions
-
MiniAppCard has proper keyboard accessibility, delete confirmation
-
E2E tests exist for gallery (16-mini-app-gallery.spec.ts)
-
Could not test mini-app viewer/iframe rendering due to browser service timeouts
-
Bugs found: 1 (issue #63)
-
UX suggestions: 2 (issues #64, #65)
-
All clear: MiniAppViewer component, MiniAppCard component, clone backend logic, E2E test coverage, SDK message handling
- Area 15: Quick chat / Ephemeral sessions
- Area 4: Tasks/Crons (create, edit, enable/disable, delete)
- Area 3: Conversations (start, send messages, chat UI)
- Pages visited: Code review of QuickChatPanel.tsx, useQuickChat.ts, useQuickSession.ts, quick-sessions.ts (routes), quick-session-cleanup.ts
- Note: Browser unavailable (sandbox disabled, no host tab), testing done via thorough code review
Bug: Quick session memory saved without embedding - Issue #69
- When closing with "Save as memory", route uses raw db.insert() instead of createMemory() service
- Memories have no embedding vector, won't appear in semantic search
- Critical: defeats the purpose of the "save as memory" feature
Bug: Expired quick sessions closed silently without SSE notification - Issue #70
- Cleanup service closes expired sessions in DB but doesn't emit SSE events
- Client panel stays open, user gets 409 errors when trying to send messages
- No visual feedback that the session expired
Enhancement: Model picker in quick chat changes Kin model globally - Issue #71
- Changing model in quick chat affects main conversation too
- Should be session-scoped or removed from quick chat
Enhancement: No quick session history/review - Issue #72
- Closed sessions are inaccessible from UI despite being in DB for 7 days
- Users can't review past quick conversations
-
SSE event handling is well-implemented with proper kinId+sessionId filtering
-
Optimistic message updates work correctly
-
Stop streaming functionality is properly wired
-
File upload in quick chat reuses the main chat infrastructure (good)
-
Sheet panel is 500px wide, responsive down to sm breakpoint
-
Mobile: quick chat accessible via overflow menu (acceptable)
-
Auto-scroll on new messages works
-
Close dialog with save-as-memory checkbox is a nice touch
-
Cleanup service handles both expiry and retention deletion correctly
-
No E2E tests exist for quick chat
-
Bugs found: 2 (issues #69, #70)
-
UX suggestions: 2 (issues #71, #72)
-
All clear: SSE streaming, message sending, file upload, stop streaming, session creation/closing flow, mobile menu access, auto-scroll, close dialog UX
- Area 4: Tasks/Crons (create, edit, enable/disable, delete)
- Area 3: Conversations (start, send messages, chat UI)
- Area 11: Contacts
- Pages visited: Code review of ChatPanel.tsx, MessageBubble.tsx, MessageInput.tsx, ConversationHeader.tsx, ChatEmptyState.tsx, ConversationSearch.tsx, MarkdownContent.tsx, DateNavigator.tsx, DateSeparator.tsx, useChat.ts, useReactions.ts, useDraftMessage.ts, useInputHistory.ts, useExportConversation.ts, messages.ts (routes), reactions.ts (routes)
- Note: Browser unavailable (sandbox disabled, host browser timed out), testing done via thorough code review
Bug: ReactionPicker popover does not close on outside click - Issue #78
- Custom useState-based popover lacks click-outside handling
- User must click the trigger button again to dismiss
- Should use Radix Popover components already available in the app
Bug: Inconsistent max-width between assistant message bubbles - Issue #79
- Messages with tool calls: max-w-[80%]
- Messages without tool calls: max-w-[75%]
- Same Kin's messages have different widths depending on tool usage
Bug: ConversationSearch Escape handler conflicts with modal Escape - Issue #80
- Global window keydown listener fires even when modals are focused
- Pressing Escape closes both search bar AND any open modal simultaneously
- Should be scoped to the search input element
Enhancement: Search should include streaming message - Issue #81
- Search passes
messages(persisted only) to ConversationSearch - Streaming message in
displayMessagesis not searchable
Enhancement: Persist message drafts across page reloads - Issue #82
- useDraftMessage uses module-level Map, lost on refresh
- Should use localStorage with debounced saves
Enhancement: No server-side message length validation - Issue #83
- Client shows character counter (cosmetic only)
- Server accepts arbitrarily long messages without limit
- Potential for abuse and unnecessary token consumption
-
Chat system is well-architected: SSE streaming with batched token updates (50ms), optimistic message sends, infinite scroll with position restoration
-
Keyboard shortcuts are comprehensive: Ctrl+F search, Escape refocus, Up/Down input history, Ctrl+1-9 kin switching
-
Message grouping (2-min window) works well for visual clarity
-
Date separators are sticky with backdrop blur, nice UX
-
DateNavigator with jump-to-date is a solid feature
-
ConversationStats and export (MD/JSON) are polished
-
File drag-and-drop works at both panel level and input level
-
Code blocks have copy, download, wrap toggle, line numbers, language detection
-
Markdown rendering lazily loads heavy plugins (rehype-highlight, remark-math, rehype-katex)
-
Mention autocomplete with @ trigger is well-implemented
-
Formatting toolbar (bold, italic, code) with keyboard shortcuts
-
Auto-scroll toggle with pin icon is clever
-
Reading time estimate for long messages
-
Context menu with copy, quote, edit/resend, read aloud, regenerate
-
MarkdownContent plain text shortcut regex has a minor false positive (
\d+\.matches version numbers like "1.0") but no visual impact -
No E2E tests found specifically for conversation search or reactions
-
Bugs found: 3 (issues #78, #79, #80)
-
UX suggestions: 3 (issues #81, #82, #83)
-
All clear: SSE streaming, message sending/receiving, optimistic updates, file upload/drag-drop, input history, mention autocomplete, formatting toolbar, markdown rendering, code blocks, date separators, infinite scroll, export, context menu, typing indicator, empty state, auto-scroll, keyboard shortcuts, model picker, context usage display, clear conversation, regenerate
- Area 11: Contacts (add, approve, edit, delete)
- Area 12: Webhooks (create, edit, test, delete)
- Area 13: MCP servers (add, configure, remove)
- Pages visited: Code review of ContactsSettings.tsx, ContactCard.tsx, ContactFormDialog.tsx, ContactNotes.tsx, ContactPlatformIds.tsx, ApprovalDialog.tsx, contacts.ts (routes), contacts.ts (service), channels.ts (service for platform IDs), schema.ts (FK cascades)
- Note: Browser unavailable (sandbox disabled, Chrome extension relay not attached), testing done via thorough code review
-
No Kin selector for "kin" type contacts - Issue #84
- ContactFormDialog shows user selector for "human" but no Kin selector for "kin" type
- linkedKinId always null from UI, breaking prompt builder resolution
-
Server accepts whitespace-only and empty contact names - Issue #85
- POST checks
!name(truthy for" "), PATCH has no name validation at all - Client validates but server should not depend on it
- POST checks
-
createContact silently returns existing on duplicate linkedUserId - Issue #86
- Returns existing contact with 201 status instead of 409 conflict
- UI shows "Contact added" toast even though nothing was created
-
Delete confirmation should warn about cascading effects - Issue #87
- Deleting contact cascades to platform IDs (channel access revocation)
- Users not warned they're locking someone out of messaging
-
N+1 API calls for platform IDs - Issue #88
- Each ContactCard fetches platform IDs separately
- Should be included in contact detail response like identifiers/notes
- Contact CRUD flow (create/edit/delete) works correctly via UI
- Identifier management (add/remove/edit with LabelCombo) is well-built
- Notes system with per-Kin scoping (global/private) is solid
- SSE real-time updates for contact changes
- Approval dialog for channel users (create new or link existing)
- FK cascade on delete properly configured for identifiers, notes, platform IDs
- Platform ID display with platform icons and hover-to-revoke UX
- Empty state with call to action
- Loading skeleton while fetching
- Area 12: Webhooks (create, edit, test, delete)
- Area 13: MCP servers (add, configure, remove)
-
Pages visited: Code review of WebhooksSettings.tsx, WebhookFormDialog.tsx, WebhookCard.tsx, WebhookLogDialog.tsx, webhooks.ts (routes), webhooks-incoming.ts (routes), webhooks.ts (service), webhook-tools.ts, schema.ts (webhooks + webhookLogs tables), 09-webhook-management.spec.ts (E2E)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 3 (issues #89, #92, #94)
- #89: Server accepts whitespace-only webhook names (no trim/validation)
- #92: WebhookFormDialog swallows API errors silently (try/finally, no catch)
- #94: Webhook creation does not validate kinId exists (FK error leaks to user)
-
UX suggestions: 3 (issues #90, #91, #93)
- #90: Webhook logs grow unbounded with no retention/cleanup
- #91: Incoming webhook endpoint has no rate limiting
- #93: Inactive webhooks should have visual distinction beyond toggle switch
-
All clear:
- Token generation and reveal flow is well-designed (shown once, hidden by default, copy buttons)
- Constant-time token comparison (timingSafeEqual) for security
- SSE real-time updates for webhook CRUD and triggers
- Webhook log dialog with expandable payloads and source IP display
- Copy URL button on each card
- Regenerate token with confirmation dialog
- ConfirmDeleteButton for safe deletion
- Max webhooks per Kin limit (configurable, default 20)
- Max payload size limit (1MB) on incoming endpoint
- Webhook tools for Kins (create/update/delete/list) with ownership verification
- E2E test coverage is comprehensive (create, edit, toggle, delete, token reveal, empty state)
- Log payload truncation to 10KB in DB
- Help panel with documentation bullets
- Empty state with call-to-action
- Area 13: MCP servers (add, configure, remove)
- Area 14: Account (profile, password, language settings)
- Pages visited: Code review of McpServersSettings.tsx, McpServerFormDialog.tsx, McpServerCard.tsx, mcp-servers.ts (routes), mcp.ts (service), mcp-tools.ts (Kin tools), mcp.test.ts (unit tests), schema.ts, 14-mcp-servers.spec.ts (E2E)
- Note: Browser unavailable (sandbox disabled, Chrome extension relay not attached), testing done via thorough code review
-
Server accepts whitespace-only MCP server names/commands - Issue #95
- POST uses
!body.name(truthy for" "), PATCH has zero validation - Same pattern as contacts (#85) and webhooks (#89)
- POST uses
-
API exposes env var values (secrets) to frontend - Issue #96
serialize()returns full env object including API keys and tokens- PasswordInput hides visually but values are in API response (DevTools)
- Security concern
-
Connection pool has no reconnection or cleanup - Issue #98
- Dead connections stay in pool, no timeout on connect, no shutdown hook
- Tool calls silently fail if MCP process crashes
-
No connection status indicator or health check - Issue #97
- "Active" badge = approval status, not connection health
- No way to test if server works, no tool count, no error feedback
-
Unicode chars silently dropped from tool names - Issue #99
- Non-Latin server names produce empty/colliding tool key prefixes
- MCP server CRUD flow (create/edit/delete) works correctly
- E2E test coverage is comprehensive (create, edit, delete, empty state)
- Form validation on client side (name + command required)
- SSE real-time updates for server CRUD events
- ConfirmDeleteButton for safe deletion
- Approval workflow for Kin-created servers (pending_approval status)
- Auto-disconnect on config change (command/args/env)
- PATH augmentation for child processes (NVM detection)
- JSON Schema to Zod conversion is well-tested
- Tool access control per Kin (mcpAccess allowlist + auto-enabled for creator)
- Lazy connection pooling (connect on first use)
- Empty state with call to action
- Loading skeleton while fetching
- Help panel with documentation
- Env var key/value UI with PasswordInput for values
- Delete cascade removes kin_mcp_servers junction entries
- Area 14: Account (profile, password, language settings)
- Area 15: Quick chat / Ephemeral sessions
-
Pages visited: Code review of AccountDialog (AccountPage.tsx), UserMenu.tsx, me.ts (routes), users.ts (routes), invitations.ts (routes+service), UsersSettings.tsx, GeneralSettings.tsx, schema.ts (userProfiles)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 4 (issues created: #100, #101, #102, #105)
- #100: Avatar upload has no file size limit or type validation (XSS risk via HTML upload)
- #101: PATCH /api/me has no input validation (whitespace, length, type) - recurring pattern
- #102: Invitation tokens exposed in full in list API response
- #105: DELETE /api/users/:id has no admin role check - any authenticated user can delete others (privilege escalation)
-
UX suggestions: 2 (issues created: #103, #104)
- #103: Account dialog should support password change
- #104: Account dialog should show last login and account creation date
-
All clear:
- Account dialog hero design with gradient background is polished
- Avatar upload UX (click avatar, hover overlay with camera icon) is intuitive
- Form state properly resets when dialog opens (useEffect on open+user)
- Language selector updates i18n in real-time on save
- User menu dropdown is clean and well-organized
- Users settings page has invitation flow (create, copy link, revoke, status badges)
- Delete user has confirmation dialog
- Self-deletion prevention ("you" badge + no delete button on self)
- Invitation expiry system with active/used/expired status
- Client-side
accept="image/*"filter on avatar file input - Loading state on save button with spinner
- Area 15: Quick chat / Ephemeral sessions
- Area 9: Settings page (all tabs, toggle options, save/cancel)
- Pages visited: Code review of QuickChatPanel.tsx, QuickSessionHistory.tsx, useQuickChat.ts, useQuickSession.ts, useQuickSessionHistory.ts, quick-sessions.ts (routes), quick-session-cleanup.ts, ChatPanel.tsx (integration), schema.ts, config.ts
- Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
closeSession clears UI state even when API call fails - Issue #113
setActiveSession(null)runs in all cases, even on error- Session becomes invisible client-side but stays active on server
-
Expired sessions still accept messages until cleanup runs - Issue #114
- Message endpoint only checks
status !== 'active', notexpiresAt - Up to 60-min gap where expired sessions accept messages
- Message endpoint only checks
-
All client hooks silently swallow errors - Issue #115
- Empty
catchblocks across useQuickChat, useQuickSession, useQuickSessionHistory - No toast, no error state, no retry UI
- Empty
-
Title and memory summary have no server-side validation - Issue #116
- Whitespace-only titles accepted, no length limit on title or memorySummary
- Same recurring pattern as #85, #89, #95, #101
-
quick_sessions.created_by FK missing onDelete cascade - Issue #117
- User deletion orphans quick sessions
- kinId has cascade, createdBy does not
- Quick session creation flow with max active limit (1 per user per kin) works correctly
- Session ownership verification (loadSession helper) is solid
- Idempotent close (already-closed returns ok) is good
- Expiration/cleanup system with configurable intervals works
- SSE real-time updates for session closed events
- Streaming message support with batched token updates (50ms)
- Optimistic message insertion with rollback on error
- Memory save on close with proper embedding via createMemory
- Session history with message count and date formatting
- Sheet-based side panel UI is clean
- Draft message persistence per session
- File upload support in quick chat
- Stop streaming functionality
- Empty state with helpful message
- Auto-scroll on new messages
- Lazy-loaded components (Suspense + lazy import)
- Area 9: Settings page (all tabs, toggle options, save/cancel)
-
Pages visited: Code review of SettingsPage.tsx, GeneralSettings.tsx, FileStorageSettings.tsx, VaultSettings.tsx, PluginsSettings.tsx, PluginMarketplace.tsx, SearchProvidersSettings.tsx, MemoriesSettings.tsx, NotificationPreferences.tsx, settings.ts (routes), notifications.ts (routes), app.ts (/api/info), plugins.ts (routes+service)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #126, #127)
- #126: Path traversal in plugin store routes (
/api/plugins/store/:name) allows reading arbitrary files (security) - #127: FileStorageSettings and VaultSettings still silently swallow fetch errors (incomplete fix of #122)
- #126: Path traversal in plugin store routes (
-
UX suggestions: 2 (issues created: #128, #129)
- #128: Marketplace plugin uninstall has no confirmation dialog (inconsistent with Plugins page)
- #129: Global prompt editor has no discard/reset button
-
All clear:
- Settings modal layout: clean sidebar (desktop) + select dropdown (mobile) pattern
- Section groups with labeled categories work well
- SettingsFooter with version, uptime, and entity stats with tooltips is polished
- GeneralSettings has proper error state with retry button (post-#122 fix)
- Global prompt has character counter, token estimate, and over-limit warning
- Hub Kin selector with immediate save + toast feedback
- Server-side validation for global prompt (type check, length limit, trimming)
- Admin guard on all settings routes
- SearchProviders: default provider selector with "Automatic" option
- SearchProviders: TestAllProviders component for batch testing
- PluginsSettings: rich card UI with version, source badge, stats (tools/hooks/providers/channels)
- PluginsSettings: collapsible permissions detail
- PluginsSettings: proper uninstall confirmation dialog
- Plugin config dialog with dynamic field renderer supporting string, number, boolean, select, password, text types
- PluginMarketplace: grid layout, search, detail modal with README rendering
- VaultSettings: type filter tabs (all, favorites, built-in types, custom types)
- VaultSettings: favorite toggle with optimistic update + rollback
- NotificationPreferences: per-type toggles with optimistic update + rollback
- NotificationPreferences: external delivery channels with test button
- Sound toggle (localStorage-based, appropriate for client-side preference)
- MemoriesSettings: clean delegation to MemoryModelConfig + MemoryList components
- /api/info endpoint efficiently counts all entity types for the footer
- Area 11: Contacts (add, approve, edit, delete contacts)
- Area 12: Webhooks (create, edit, test, delete webhooks)
-
Pages visited: Code review of ContactsSettings.tsx, ContactFormDialog.tsx, ContactCard.tsx, ContactNotes.tsx, ContactPlatformIds.tsx, contacts.ts (routes), contacts.ts (service), WebhooksSettings.tsx, WebhookFormDialog.tsx, WebhookCard.tsx, WebhookLogDialog.tsx, webhooks.ts (routes), webhooks.ts (service), webhooks-incoming.ts
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 4 (issues created: #131, #132, #133, #134)
- #131: Contact identifier label/value have no length limit or whitespace-only validation
- #132: Webhook name and description have no server-side length limits
- #133: Contact note content has no max length validation (risk: injected into Kin prompts)
- #134: fetchContacts/fetchWebhooks silently swallow errors (same pattern as #115, #127)
-
UX suggestions: 3 (issues created: #135, #136, #137)
- #135: No UI to manually add platform IDs to a contact (API exists but no form)
- #136: No search/filter on contacts list (backend search exists but no UI)
- #137: No search/filter on webhooks list, no Kin filter dropdown
- Contact CRUD with proper SSE real-time updates
- Contact form dialog: clean layout, type selector (human/kin), linked user/kin selector, identifier management with LabelCombo
- Contact name validation: trim + empty check + 200 char max (server-side)
- Contact type validation: only "human" or "kin" accepted
- Duplicate user-contact link prevention (409 with helpful message)
- Contact card: nice layout with icon, badges for type/linked user, identifier badges
- Contact delete: confirmation dialog with cascade warning for platform IDs
- Contact notes: inline editing with cancel/save, scope selector (global/private), Kin selector
- Contact notes: proper visibility rules (admin sees all, Kin sees global + own private)
- Platform IDs: display with platform icons, hover-to-reveal revoke button
- Webhook CRUD with SSE real-time updates
- Webhook creation: token reveal dialog with show/hide toggle and copy buttons
- Webhook token regeneration: confirmation dialog before regenerating
- Webhook card: clean layout with Kin badge, trigger count, last triggered date, active/inactive state
- Webhook active/inactive toggle with visual dimming
- Webhook URL copy button
- Webhook trigger logs dialog with expandable payloads, source IP badges, empty state
- Webhook incoming route: proper rate limiting (sliding window per webhook), token validation (timing-safe), payload size limit, inactive check
- Webhook log pruning: retention period + per-webhook cap, runs every 6 hours
- Max webhooks per Kin limit enforced
- Contact identifiers: batch creation during contact create
- Contact identifier update: proper diff logic (detect added/changed/removed identifiers)
- searchContacts: searches across names, identifiers, and notes with deduplication
- ensureUserContactsExist: auto-creates contacts for all users (backfill)
- deleteNotesByKin: cleanup when a Kin is deleted
- listContactsForPrompt: efficient summary with linked kin slug and identifier summary
- Area 13: MCP servers (add, configure, remove MCP servers)
- Area 14: Account (profile, password, language settings)
-
Pages visited: Code review of McpServersSettings.tsx, McpServerFormDialog.tsx, McpServerCard.tsx, mcp-servers.ts (routes), mcp.ts (service), AccountPage.tsx (AccountDialog), me.ts (routes), auth/index.ts
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 3 (issues created: #139, #140, #141)
- #139: McpServerCard uses double /api prefix for status/test endpoints (connection status and test button completely broken)
- #140: fetchServers silently swallows errors (same pattern as #115, #127, #134)
- #141: MCP server name and command have no server-side length limits
-
UX suggestions: 2 (issues created: #142, #143)
- #142: MCP env var editing gives no visual feedback that existing secrets are preserved when left empty
- #143: Account page name/pseudonym fields have no client-side character counters or format hints
- MCP server CRUD with proper SSE real-time updates
- MCP server form: clean layout with name, command, args (textarea, one per line), and env vars (key/value pairs with password input)
- MCP env var masking: backend never exposes env values to frontend (only keys with empty strings)
- MCP env var merge logic on update: empty values preserve existing secrets
- MCP server card: clean layout with connection status dot, tool count badge, error tooltip, Kin badge
- MCP server approval flow for Kin-created servers (pending_approval status)
- MCP server delete: confirmation dialog, disconnects running server
- MCP connection pool with auto-reconnect on tool call failure
- MCP connection timeout (30s) to avoid hanging on unresponsive servers
- MCP PATH augmentation for child processes (detects NVM, common system paths)
- MCP tool resolution with per-Kin access control (mcpAccess allowlist or auto-enable for Kin-created servers)
- MCP JSON Schema to Zod conversion for tool input validation
- MCP tool name sanitization with Unicode/accent handling and hash fallback
- Account dialog: polished hero header with gradient, avatar crop (react-easy-crop), member since date
- Account profile: proper server-side validation (name length, pseudonym format, language allowlist)
- Account avatar: size limit (2MB), type validation, crop before upload
- Account password change: collapsible section, min length 8, confirm match, Better Auth handles backend
- Account profile upsert: handles missing profile row gracefully (onConflictDoUpdate)
- Account language change: updates i18n on save
- Area 15: Quick chat / Ephemeral sessions
- Area 1 (revisit): Onboarding / First run (rotate back to start)
-
Pages visited: Code review of QuickChatPanel.tsx, useQuickChat.ts, useQuickSession.ts, QuickSessionHistory.tsx, useQuickSessionHistory.ts, quick-sessions.ts (routes), quick-session-cleanup.ts, ChatPanel.tsx (quick session integration), config.ts (quickSessions config)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 3 (issues created: #146, #147, #148)
- #146: N+1 query for message counts in quick session listing (fetches all rows per session, uses .length instead of COUNT(*))
- #147: No server-side message content length limit on quick session messages
- #148: No fileIds array length or format validation on quick session messages
-
UX suggestions: 3 (issues created: #149, #150, #151)
- #149: Close dialog memory checkbox and summary text not reset between cancel/reopen
- #150: No pagination or infinite scroll in quick session history (hard-capped at 20)
- #151: Quick chat side panel width may overflow on small tablets (500px fixed width)
- Quick session CRUD with proper SSE real-time updates
- Session creation with title validation (200 char max) and max active sessions limit
- Session ownership verification (loadSession helper, 403 on mismatch)
- Session close flow with optional memory save (summary validated at 5000 chars)
- Memory creation uses createMemory with embedding + vector index
- Idempotent close (already-closed returns ok)
- Expiration check before accepting messages (409 SESSION_EXPIRED)
- Streaming with batched UI updates (50ms debounce) for smooth rendering
- Stop streaming with abort support
- Quick session cleanup job: closes expired sessions, deletes stale closed sessions after retention period
- SSE notifications for session closure (UI auto-updates)
- Quick chat panel: clean header with Kin avatar, history button, end session button, hide button
- Empty state with icon and message
- Optimistic user message rendering with rollback on error
- Draft message persistence per session (useDraftMessage with quick- prefix)
- File upload support with optimistic file display
- Quick session history: session list with title, date, message count, click to view messages
- History message viewing with back navigation
- Lazy loading of QuickChatPanel and QuickSessionHistory components
- Quick chat accessible from conversation header (desktop button + mobile dropdown menu)
- Area 1 (revisit): Onboarding / First run (rotate back to beginning)
- Area 2 (revisit): Kin management
-
Pages visited: Code review of OnboardingPage.tsx, StepIdentity.tsx, StepPreferences.tsx, StepProviders.tsx, StepMemory.tsx, StepSearchProviders.tsx, onboarding.ts (routes), auth/index.ts, auth/middleware.ts, App.tsx
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 4 (issues created: #153, #154, #155, #156)
- #153: Language preference selected in Step 2 never saved to profile (Step 1 hardcodes 'en')
- #154: Progress bar formula never reaches 100% (maxes out at 80% on step 5)
- #155: No server-side length/format validation on onboarding profile fields (firstName, lastName, pseudonym, language)
- #156: If registration succeeds but profile creation fails, user is permanently locked out (can't re-register, can't access app)
-
UX suggestions: 0
- Onboarding step flow (5 steps: Identity, Preferences, Providers, Memory, Search Providers)
- Provider capability cards showing LLM/Embedding/Image coverage status
- Quick Finish option when LLM + Embedding are covered (skips Memory + Search)
- Resume onboarding at step 3 if admin exists but providers missing
- Backend-unreachable error screen with retry button
- Avatar upload with preview and camera overlay
- Password confirmation check (client-side)
- Provider guidance cards for new users (OpenAI, Gemini, Ollama with "Get Key" links)
- Auth middleware correctly blocks profile-less users from accessing protected routes
- Invitation token flow for non-first users (handled via InvitePage)
- MemoryModelConfig with non-blocking save (optional step)
- Search provider step properly optional (skip button available)
- Lazy loading of all page components
- Decorative aurora orbs for visual polish
- Area 2 (revisit): Kin management
- Area 3 (revisit): Conversations
-
Pages visited: Code review of KinFormModal.tsx, KinCard.tsx, KinToolsTab.tsx, KinList.tsx, SortableKinCard.tsx, useKins.ts, useKinTools.ts, kins.ts (routes), kins.ts (services), slug.ts, db/schema.ts (kins table)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 4 (issues created: #157, #158, #161, #162)
- #157: No server-side validation on Kin create/update fields (name, role, character, expertise, model) - accepts empty strings, unlimited length
- #158: Kin PATCH allows setting providerId to non-existent provider, resulting in 500 instead of 400
- #161: Kin form wizard-generated character/expertise don't call markDirty(), unsaved changes guard doesn't fire
- #162: Kin delete cascade doesn't clean up quick sessions referencing deleted Kin
-
UX suggestions: 2 (issues created: #159, #160)
- #159: Kin export leaks MCP server env var key names
- #160: Kin import doesn't verify model ID exists, no warning or suggestion
- Kin CRUD with proper SSE real-time updates across clients
- Kin list with drag-and-drop reorder (dnd-kit), order persisted per user
- Hub Kin pinned at top of sidebar, outside drag zone
- Kin card: clean layout with avatar, name, role, model name, processing state, queue size badge
- Kin card context menu: edit, export, set as hub, delete (with confirmation dialog)
- Keyboard shortcuts (Cmd/Ctrl+1-9) for quick Kin selection
- Kin search filter in sidebar (appears when >= 5 Kins)
- Kin form: tabbed layout (General, Tools, Memory) with left sidebar navigation
- AI wizard: describe -> generate -> refine flow, clean UX
- Wizard: Import from .kinbot.json file with proper error handling
- Avatar picker: upload with crop, AI generation (auto/prompt), per-provider image model selection
- Avatar generation runs in background during wizard, cancellable via AbortController
- Slug generation: auto from name, unique collision handling (-2, -3 suffix)
- Slug validation: regex with proper format rules, checked on update
- Tool config: dual model (deny-list for standard tools, opt-in allow-list for admin tools)
- Tool domains: collapsible groups with bulk toggle and per-tool switches
- MCP tool access: per-server granular control with wildcard support
- Search provider override per Kin
- Memory tab: embedded MemoryList component for edit mode
- Unsaved changes guard with confirmation dialog
- Model unavailable warning with visual indicators (dimmed card, alert icon)
- Kin deletion: comprehensive cascade delete with SSE notifications for all affected entities
- Export: full .kinbot.json with version metadata, downloadable
- Area 3 (revisit): Conversations
- Area 4 (revisit): Tasks/Crons
-
Pages visited: Code review of ChatPage.tsx, ChatPanel.tsx, MessageInput.tsx, MessageBubble.tsx, ConversationHeader.tsx, ChatEmptyState.tsx, useChat.ts, useReactions.ts, useFileUpload.ts, messages.ts (routes), reactions.ts (routes), queue.ts (service)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 3 (issues created: #164, #165, #167)
- #164: Redacted messages have no visual indicator in chat UI - isRedacted flag tracked but never rendered
- #165: Reaction toggle lacks optimistic update and error handling - bare await with no try/catch
- #167: Streaming message promoted with hardcoded sourceType "kin", loses real source metadata until fetchMessages
-
UX suggestions: 1 (issues created: #166)
- #166: Clear conversation orphans uploaded files on disk - files nullified but not deleted
- Chat panel: clean layout with messages area, tool calls side panel, mini-app panel
- Message input: formatting toolbar (bold, italic, strikethrough, code, code block) with keyboard shortcuts
- @mention autocomplete with proper keyboard navigation (Up/Down/Enter/Tab/Escape)
- File upload: drag-and-drop (both on input area and full panel), paste from clipboard, file picker button
- Pending file chips with thumbnail preview, upload spinner, error state, remove button
- Character count with progressive color warning (50%/75%/100% of 32k limit)
- Input history navigation (Up/Down arrows at cursor position 0)
- Optimistic user message rendering with rollback on API error
- Draft message persistence per Kin (survives navigation, cleared on send)
- Message streaming with 50ms batched UI updates for smooth rendering
- Streaming message promoted in-place (same React key) to avoid re-mount animation flash
- Stop streaming button with server-side abort support
- Infinite scroll: IntersectionObserver on top sentinel, scroll position restoration after prepend
- Auto-scroll toggle (pinned bottom-right) with localStorage persistence
- Scroll-to-bottom button with new message count badge
- Scroll-to-top button (appears when scrolled past 300px)
- Message grouping: consecutive same-sender messages within 2min window, tighter spacing, hidden avatar
- Date separators between messages on different days
- Time gap indicator between non-consecutive messages
- Message context menu: copy, quote reply, edit & resend (user), read aloud (assistant), regenerate
- Conversation search (Ctrl+F) with highlight navigation and match scrolling
- Empty state with Kin avatar, greeting, suggestion chips, hint text
- Conversation header: model picker, context usage bar (tokens/window), tool calls toggle, quick session, search, date navigator, stats
- Clear conversation with confirmation dialog and comprehensive cascade cleanup
- Export as Markdown or JSON
- Force compact button in more actions dropdown
- Reading time estimate for long messages (>100 words)
- Read aloud via Web Speech API with play/stop toggle
- Copy message button (hover, positioned outside bubble)
- Edit & resend button for user messages
- Regenerate button on last assistant message
- Reaction system: preset emoji picker, toggle behavior, grouped display with count
- Image lightbox for attached images
- Inline tool calls with interleaved text/tool display
- Task result cards (live + persisted) with detail modal
- Compacting cards (live + persisted) with memory extraction count
- Human prompt cards for interactive responses
- File attachments: image thumbnails (max 48x48), non-image chips with download
- Injected memories indicator (collapsible, shows category + content)
- Platform icon for channel messages (telegram, discord, etc.)
- Connection banner for SSE disconnection
- Onboarding progress banner when setup incomplete
- Keyboard shortcuts: Cmd+1-9 kin switch, Cmd+Shift+N create kin, Cmd+, settings, Escape refocus input
- Unread count in browser tab title + favicon badge
- Lazy loading of modals (KinFormModal, SettingsModal, AccountDialog, TaskDetailModal, QuickChatPanel, QuickSessionHistory, ConversationSearch, MiniAppViewer)
- Area 4 (revisit): Tasks/Crons
- Area 5 (revisit): Provider settings
-
Pages visited: Code review of CronList.tsx, CronFormModal.tsx, CronDetailModal.tsx, TaskList.tsx, TaskDetailModal.tsx, useCrons.ts, cron-next.ts, cron-tools.ts, crons.ts (service), crons.ts (routes), tasks.ts (routes), tasks.ts (service), wakeup-scheduler.ts, kins.ts (cascade delete)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 3 (issues created: #168, #169, #170)
- #168: Kin cascade delete does not stop in-memory cron scheduler jobs (leaks croner timers)
- #169: Unused variable
targetKinIdintriggerCron()function (dead code) - #170:
scheduleJob()casts Date to string with unsafeas stringtype assertion
-
UX suggestions: 1 (issues created: #171)
- #171: Cron form does not support one-shot (run_once) or ISO datetime schedules despite backend support
- Cron CRUD: clean create/edit/delete flow with proper validation and SSE real-time updates
- Cron scheduling: croner-based in-memory scheduler with proper boot recovery (initCronScheduler)
- Cron presets: 9 preset buttons for common schedules (5m, 15m, 30m, hourly, daily, weekly, monthly)
- Schedule validation: real-time human-readable translation + next 3 runs preview
- Invalid schedule feedback: red border, error message, submit button disabled
- Cron detail modal: schedule info, description, target kin, model, execution history with task drill-down
- Manual trigger: "Run Now" button in detail modal with proper API call and history refresh
- Approval flow: Kin-created crons require user approval, pending badge, approve button, notification
- Active toggle: switch on cron cards and in detail modal, with proper scheduler start/stop
- Drag-and-drop reorder: dnd-kit with user-persisted order via /me endpoint
- Search filter: filter by name, kin name, or schedule expression
- Duplicate cron: one-click duplicate from detail modal with "(copy)" suffix
- One-shot crons: backend supports
runOnceflag with auto-deactivation after first fire - ISO datetime schedules: backend parses and schedules one-time future dates
- Max active crons limit (config.crons.maxActive) enforced on creation
- Max concurrent executions per cron (config.crons.maxConcurrentExecutions) prevents overlapping runs
- Task list: timeline layout with day grouping, status icons, duration, infinite scroll
- Task detail modal: full conversation view with streaming, tool calls side panel, cancel button, result/error blocks
- Task cancel: proper API with status check (409 if already finished)
- Stale task recovery on server restart (marks pending/in_progress as failed)
- SSE events: cron:created, cron:updated, cron:deleted, cron:triggered all properly emitted and consumed
- Kin delete cascade: properly cleans up crons from DB and nullifies targetKinId references
- Unsaved changes guard on cron form with confirmation dialog
- Cron tools: create, update, delete, list, trigger, journal (get history) - comprehensive tooling for Kins
- Area 11: Contacts
- Area 12: Webhooks
-
Pages visited: Code review of ContactsSettings.tsx, ContactCard.tsx, ContactFormDialog.tsx, ContactNotes.tsx, ContactPlatformIds.tsx, contacts.ts (routes), contacts.ts (service), contact-tools.ts
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #174, #175)
- #174: Identifier, note, and platform-id sub-resource routes ignore parent contactId URL parameter (can update/delete resources across contacts)
- #175: update_contact Kin tool allows duplicate identifiers (no uniqueness check before insert)
-
UX suggestions: 2 (issues created: #176, #177)
- #176: Platform ID revoke button has no confirmation dialog, risks accidental access loss
- #177: Contacts list API has N+1 query problem (4N+1 queries for N contacts)
- Contact CRUD: clean create/edit/delete flow with proper validation
- Contact form: name, type selector (human/kin), linked user/kin selector, identifier management with label combo (suggestions + custom), proper empty/edit state handling
- Contact card: clean layout with type badge, linked user badge, identifier badges, notes section, platform IDs section
- Search filter: works on name and identifier label/value
- Empty state: proper icon, description, and CTA button
- SSE real-time updates: contact:created, contact:updated, contact:deleted events properly handled
- Delete cascade: FK cascade on identifiers, notes, and platform IDs with proper confirmation dialog
- Delete warning: shows platform count and names in confirmation when platform IDs exist
- Notes: inline create/edit/delete with Kin selector, scope selector (global/private), textarea
- Notes visibility: admin view shows all notes, Kin view shows global + own private
- Platform IDs: inline add with platform picker (8 platforms) and ID input, revoke with X button
- Identifier suggestions: predefined labels (email, phone, etc.) with custom entry support via LabelCombo
- Input validation: name required and max 200 chars, identifier labels max 100, values max 500, note content max 10000
- Duplicate user-contact link prevention (409 response)
- Contact tools (Kin-side): get, search, create, update, delete, set_note, find_by_identifier - comprehensive tooling
- User contact backfill: ensureUserContactsExist() auto-creates contacts for all users on startup
- Prompt helpers: listContactsForPrompt() provides summary with linked kin slugs and identifier labels
- Area 12: Webhooks
-
Pages visited: Code review of webhooks.ts (routes), webhooks-incoming.ts (routes), webhooks.ts (service), webhooks.test.ts, webhook-tools.ts, webhook-tools.test.ts, WebhooksSettings.tsx, WebhookFormDialog.tsx, WebhookCard.tsx, WebhookLogDialog.tsx, 09-webhook-management.spec.ts, schema.ts (webhooks/webhookLogs tables), config.ts (webhook settings), app.ts (route mounting), auth/middleware.ts (auth bypass), kins.ts (cascade delete)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #180, #181)
- #180: validateToken returns true for empty strings, allowing unauthenticated webhook calls if token field is ever empty
- #181: pruneWebhookLogs per-webhook cap fails when multiple logs share the same timestamp (uses strict lt instead of proper subquery)
-
UX suggestions: 2 (issues created: #182, #183)
- #182: Webhook list API has N+1 query problem for Kin info (same pattern as contacts #177)
- #183: Webhook incoming route returns 404 instead of 405 for non-POST methods
- Webhook CRUD: clean create/edit/delete flow with proper validation (name required, max 200 chars, description max 1000)
- Token security: 32-byte random hex token, timing-safe comparison, token shown only once at creation
- Token regeneration: confirmation dialog, proper SSE event, toast notification
- Token reveal dialog: show/hide toggle, copy buttons for URL and token, warning about one-time display
- Rate limiting: per-webhook sliding window rate limiter (configurable, default 60/min), in-memory with periodic cleanup
- Payload size limit: configurable max (default 1MB), proper 413 response
- Auth bypass: incoming webhook route properly excluded from session auth middleware
- Incoming webhook flow: token via Bearer header or query param, active check (409 if inactive), proper HTTP status codes
- Webhook trigger: increments counter, logs payload (truncated to 10KB), enqueues message to target Kin, SSE event
- Log viewer: expandable payloads, source IP badges, timestamp display, empty state, scroll area
- Log cleanup: periodic pruning every 6h with configurable retention (default 30 days) and per-webhook cap (default 500)
- Cascade delete: Kin deletion properly deletes webhooks and emits SSE events; webhook logs cascade via FK
- SSE real-time updates: webhook:created, webhook:updated, webhook:deleted, webhook:triggered all properly emitted and consumed
- Search and filter: text search on name/description/kinName, Kin selector filter
- Empty state: proper icon, description, and CTA button
- Max webhooks per Kin: configurable limit (default 20) enforced at creation
- Kin tools: create, update, delete, list webhooks with proper ownership verification (kinId check)
- E2E tests: comprehensive Playwright tests covering CRUD, token reveal, toggle, confirmation dialogs
- Unit tests: validateToken edge cases, buildWebhookUrl construction
- Config: all limits configurable via env vars (rate limit, payload size, retention, max per kin, max logs)
- Area 13: MCP servers
- Area 14: Account settings
-
Pages visited: Code review of mcp-servers.ts (routes), mcp.ts (service), mcp.test.ts, mcp-tools.ts (Kin tools), McpServersSettings.tsx, McpServerCard.tsx, McpServerFormDialog.tsx, 14-mcp-servers.spec.ts, schema.ts (mcpServers/kinMcpServers tables), kins.ts (cascade/links)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #193, #194)
- #193: Unit test sanitizeName is stale copy, diverges from real implementation (misses NFD normalization and hash fallback)
- #194: update_mcp_server Kin tool replaces all env vars instead of merging (unlike HTTP PATCH route which merges secrets)
-
UX suggestions: 1 (issues created: #195)
- #195: MCP form silently drops new env vars with empty values, no validation feedback
- MCP CRUD: clean create/edit/delete flow with proper validation (name max 200, command max 500, args max 50 with 1000 char limit each, env max 50 vars with key/value limits)
- Env security: values never exposed to frontend (serialize masks with empty strings), PasswordInput for values, merge logic in HTTP PATCH preserves existing secrets
- Connection management: lazy connect with 30s timeout, auto-reconnect on tool call failure, proper disconnect on config change or deletion
- Connection status UI: green/red dot indicator, tool count badge, error tooltip, manual test button with loading spinner
- Approval flow: pending_approval status, approve button, notification creation, config flag (mcp.requireApproval)
- Kin auto-assignment: servers created by Kins auto-linked via kinMcpServers junction table
- Tool resolution: per-Kin mcpAccess config with wildcard ('*') support, auto-enabled for self-created servers
- Tool naming: sanitizeName with NFD normalization and hash fallback for non-Latin characters, prefixed mcp_{server}_{tool}
- JSON Schema to Zod: comprehensive conversion supporting string/number/integer/boolean/array/object/enum/nested with descriptions
- PATH augmentation: auto-detects NVM and common system paths for child processes (handles Snap sandboxing)
- Cascade delete: Kin deletion sets createdByKinId to null, kinMcpServers cascade via FK, disconnects active connections and emits SSE events
- SSE real-time updates: mcp-server:created/updated/deleted all properly emitted and consumed
- Empty state: proper icon, description, and CTA button
- Help panel: collapsible with 4 bullet points explaining MCP usage
- E2E tests: comprehensive Playwright tests covering full CRUD lifecycle, env vars, edit, delete confirmation, empty state
- Unit tests: sanitizeName edge cases and jsonSchemaToZod with complex nested schemas (though sanitizeName tests need sync with source)
- Kin tools: add, update, remove, list MCP servers with proper ownership and SSE events
- Config for tools tab: getMCPToolsForConfig shows all servers with per-tool enabled/disabled state for Kin configuration UI
- Area 14: Account settings
- Area 15: Quick chat / Ephemeral sessions
-
Pages visited: Code review of AccountDialog.tsx (AccountPage.tsx), me.ts (routes), auth/index.ts, users.ts (routes), UsersSettings.tsx, 17-account-settings.spec.ts, 19-users-settings.spec.ts
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #196, #198)
- #196: PATCH /api/me accepts arbitrary kinOrder and cronOrder values without validation (can store non-JSON, non-array values)
- #198: Password change has no server-side minimum length enforcement (only client-side 8-char check, Better Auth accepts any length)
-
UX suggestions: 1 (issues created: #197)
- #197: Avatar size limit mismatch between client (2MB) and server (10MB), inconsistent but not breaking
- Account dialog: clean modal with hero header, gradient bg, avatar with camera overlay, user info display
- Avatar upload: client-side type + size validation, cropper with zoom slider, crop to square, proper loading states
- Profile form: firstName/lastName (max 100), pseudonym (max 30, alphanumeric + underscore/hyphen), language selector
- Server validation: comprehensive input checks on PATCH /me (type, length, regex, trimming), proper error aggregation
- Profile upsert: handles missing profile row (e.g. skipped onboarding) via insert...onConflictDoUpdate
- User table sync: updates Better Auth user.name when firstName/lastName change
- Password change: expandable section with current/new/confirm fields, client-side mismatch + length checks, loading spinner, toast feedback
- Avatar server: MIME type + extension validation, stores in data/uploads/avatars/, cache-busting URL
- Form state reset: useEffect resets all state when dialog opens, cancel discards unsaved changes
- Cancel behavior: discards unsaved edits properly
- Language change: updates i18n.changeLanguage on save
- Admin badge: shows "Admin" for admin users
- Member since: displays join date with calendar icon
- Users settings (admin): user list with avatars, roles, delete with confirmation, self-delete prevention
- Invitation system: create with label + expiry, one-time URL reveal, copy button, status badges (active/used/expired), revoke with confirmation
- Auth: Better Auth with email/password, cookie session with 5-min cache, trusted origins configured
- E2E tests: comprehensive Playwright tests for dialog open/close, field editing, persistence, cancel reset, avatar fallback, language selector
- Area 15: Quick chat / Ephemeral sessions
- Area 1: Onboarding / First run (re-test)
-
Pages visited: Code review of QuickChatPanel.tsx, QuickSessionHistory.tsx, useQuickChat.ts, useQuickSession.ts, useQuickSessionHistory.ts, quick-sessions.ts (routes), quick-session-cleanup.ts, ChatPanel.tsx (integration), ConversationHeader.tsx (button), schema.ts (quickSessions table), config.ts (quickSessions config)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #199, #200)
- #199: Save-as-memory silently fails when summary is left empty (server skips memory creation, no user feedback)
- #200: Sheet panel shows blank content when session closes via SSE (activeSession becomes null while Sheet is open)
-
UX suggestions: 1 (issues created: #201)
- #201: No client-side session expiry awareness (no timer, no specific error handling for SESSION_EXPIRED, expiresAt not in API response type)
- Quick session CRUD: clean create/close flow with proper validation (title max 200 chars)
- Session ownership: loadSession helper verifies user ownership, returns 403 for unauthorized access
- Max active sessions: configurable limit per user per kin (default 1), 409 on exceeded
- Message sending: proper validation (content or files required, content max 100K chars, fileIds max 10 with UUID validation)
- Streaming: SSE-based token streaming with batched UI updates (50ms), stop generation support
- Close dialog: confirmation with save-as-memory option, checkbox + textarea, proper state reset on cancel
- Memory save: creates memory with category 'knowledge', proper subject fallback, summary max 5000 chars
- Session history: paginated list of closed sessions with message counts, load-more support
- Session detail view: back navigation, loading state, message bubbles with proper avatars
- Cleanup service: periodic expiry (closes active sessions past expiresAt) and retention (deletes closed sessions past retentionDays), SSE notifications
- Cascade delete: Kin deletion cascades to quick sessions via FK, messages cascade via session FK
- SSE real-time: quick-session:closed event properly emitted and consumed
- Optimistic UI: user messages appear immediately, reverted on send failure
- Draft persistence: per-session draft via useDraftMessage hook
- File upload: reuses existing useFileUpload hook, optimistic file display
- Tool calls: useToolCalls hook properly resolves tool call metadata for quick session messages
- Responsive: quick chat button hidden on mobile header, available in dropdown menu
- Lazy loading: QuickChatPanel and QuickSessionHistory loaded via React.lazy/Suspense
- Sheet panel: proper side panel with close/hide distinction, history toggle
- Empty state: proper icon + description when no messages
- Config: all limits configurable via env vars (expiration hours, max per user/kin, retention days, cleanup interval)
- Area 1: Onboarding / First run (re-test)
- Area 10: Navigation & Layout
-
Pages visited: Code review of AppSidebar.tsx, SidebarFooterContent.tsx, SystemHealthBar.tsx, KinList.tsx, ChatPage.tsx, ConversationHeader.tsx, CommandPalette.tsx, KeyboardShortcutsDialog.tsx, ThemeToggle.tsx, PaletteToggle.tsx, PaletteSwitcher.tsx, theme-provider.tsx, SSEStatusIndicator.tsx, ConnectionBanner.tsx, GettingStartedChecklist.tsx, UserMenu.tsx, StatusNotifications.tsx, sidebar.tsx (UI), MessageInput.tsx (shortcut conflict)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #202, #203)
- #202: Cmd+B keyboard shortcut conflict: toggles sidebar AND bolds text simultaneously (MessageInput lacks stopPropagation, sidebar handler on window catches it too; also listed twice in shortcuts dialog)
- #203: Sidebar resize jumps on first drag when width is in rem units (parseInt("20rem") = 20, far below SIDEBAR_WIDTH_MIN of 260, causing snap to 260px)
-
UX suggestions: 1 (issues created: #204)
- #204: Sidebar resize handle too narrow (4px w-1), hard to grab on trackpads. Suggest wider invisible hit area with narrow visible indicator.
- Sidebar structure: clean SidebarHeader/Content/Footer layout with proper overflow management
- Sidebar tabs: Tasks/Jobs/Apps with tab persistence via localStorage, badge counts for active tasks, pulse animation for awaiting human input
- KinList: drag-and-drop reorder via @dnd-kit, search when >=5 kins, hub kin pinned at top outside DnD, export functionality
- SystemHealthBar: real-time provider/channel health indicators via SSE, clickable to open relevant settings
- SidebarFooterContent: version badge with changelog dialog, Cmd+K and ? shortcut hints, settings button
- CommandPalette: Cmd+K with kin search, settings sections, theme toggle, proper keyboard navigation
- KeyboardShortcutsDialog: '?' trigger with proper input detection (skips when typing), Mac/Windows key detection
- ThemeToggle: light/dark/system with reduce contrast option, proper tooltips
- PaletteToggle: 8 color palettes with visual preview dots
- PaletteSwitcher: combined palette+mode picker for settings/onboarding
- Theme system: oklch colors, data-palette/data-contrast attributes, localStorage persistence, SSR-safe defaults
- Header: SidebarTrigger, separator, SSE status indicator, palette/theme toggles, notification bell, user menu
- UserMenu: avatar with initials fallback, account/settings/logout
- SSEStatusIndicator: green/amber/red dot for connected/reconnecting/disconnected
- ConnectionBanner: animated banner for connection loss, auto-hide with "reconnected" confirmation, dismissable
- StatusNotifications: toast notifications for provider/channel status changes, version update available alerts
- GettingStartedChecklist: 4-step onboarding (providers -> hub -> specialist -> channels) with progress indicators
- ConversationHeader: responsive design with 8 mobile-hidden elements, mobile popover for model picker and context, date navigator, conversation stats, more actions dropdown, clear conversation with confirmation dialog
- Keyboard shortcuts: Cmd+1-9 for kin switching, Cmd+Shift+N for new kin, Cmd+, for settings, Escape for focus input (context-aware)
- Lazy loading: modals (KinFormModal, SettingsModal, AccountDialog) loaded via React.lazy/Suspense
- Dynamic document title: shows kin name + processing state + unread count
- Favicon badge: unread message indicator
- Unread per-kin tracking with clear on select
- Area 1: Onboarding / First run (re-test)
- Area 3: Conversations (re-test)
-
Pages visited: Code review of ChatPage.tsx, ChatPanel.tsx, MessageInput.tsx, MessageBubble.tsx, useChat.ts, ChatEmptyState.tsx, ConversationSearch.tsx, MarkdownContent.tsx, MentionPopover.tsx, TypingIndicator.tsx, DateSeparator.tsx, TimeGapIndicator.tsx, useReactions.ts, QueuePreview.tsx, messages.ts (server routes)
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 2 (issues created: #206, #208)
- #206: Regenerate discards file attachments from original message (sendMessage called with content only, no fileIds)
- #208: Plain text detection regex false positives:
\d+\.matches any number+period anywhere, not just ordered list syntax at line start
-
UX suggestions: 3 (issues created: #207, #209, #210)
- #207: Conversation search only covers loaded messages (~50), no indication of limitation, no server-side search
- #209: @mention popover position is hardcoded (bottom:8, left:0), doesn't follow cursor in multiline input
- #210: Typing indicator renders simultaneously with streaming content, redundant visual signal
- Chat panel architecture: clean separation of concerns with ChatPanel orchestrating useChat, useToolCalls, useHumanPrompts, useQuickSession, useReactions, useDraftMessage, useQueueItems, useFileUpload, useExportConversation, useMentionables
- Message rendering: well-structured MessageBubble with memo, proper grouping (2-min window), date separators, time gap indicators (30-min threshold)
- Streaming: SSE-based token streaming with 50ms batched UI updates, streaming message promoted to messages array on done (prevents remount/animation replay), MutationObserver for auto-scroll
- Auto-scroll: pin/unpin toggle with localStorage persistence, ResizeObserver to compensate viewport height changes, new message counter when scrolled up
- Message input: controlled value, input history (Up/Down arrows), formatting toolbar (bold/italic/strikethrough/code/codeBlock), character count with color thresholds, @mention autocomplete, file drag-and-drop + paste + button, draft persistence per kin
- Markdown rendering: lazy-loaded rehype-highlight and remark-math/rehype-katex, plain text fast path, code blocks with copy/download/wrap/line numbers/language label
- File handling: image thumbnails with lightbox, non-image download chips, optimistic file display on send
- Reactions: preset emoji picker with toggle, optimistic-like display grouped by emoji, SSE sync for added/removed reactions
- Context menu: copy, quote reply (blockquote from first 3 lines), edit & resend (user messages), read aloud (Web Speech API), regenerate
- Conversation search: client-side Ctrl+F with match highlighting, keyboard navigation (Enter/Shift+Enter), scroll to match
- Message grouping: consecutive messages from same sender within 2-min window collapse (hidden avatar/name, tighter spacing)
- Queue preview: pending messages shown above input with remove button, proper empty state
- Tool calls: interleaved text + tool call parts using content offsets, deduplication of trailing repeated text
- Injected memories: collapsible indicator with category badges
- Empty state: greeting with kin avatar, suggestion chips from i18n, keyboard shortcut hints
- Compacting: live card during compacting, input disabled with reason tooltip, force compact from header
- Human prompts: card UI for pending approvals with respond action
- Export: markdown and JSON export from header menu
- Optimistic updates: user messages appear immediately, reverted on send failure
- Loading states: skeleton placeholders during initial load, spinner during infinite scroll
- Server routes: proper validation (content/files required, max length, fileIds max 10 with UUID), paginated with hasMore, file and reaction maps built efficiently, kin source info resolved for inter-kin messages
- Area 11: Contacts
- Area 12: Webhooks
-
Pages visited: Code review of ContactsSettings.tsx, ContactCard.tsx, ContactFormDialog.tsx, ContactNotes.tsx, ContactPlatformIds.tsx, routes/contacts.ts, services/contacts.ts, services/channels.ts (addContactPlatformId), migration 0015
-
Note: Browser unavailable (sandbox disabled), testing done via thorough code review
-
Bugs found: 3 (issues created: #299, #300, #301)
- #299: Contact note "add" silently overwrites existing note for same kin+scope (setContactNote upserts without UI warning)
- #300: Contact identifier edits are non-atomic, sequential API calls can leave partial state on failure
- #301: Platform ID creation returns misleading 409 DUPLICATE_PLATFORM_ID when contact doesn't exist (FK error caught generically)
-
UX suggestions: 1 (issues created: #302)
- #302: Contact search should include platform IDs and notes (currently only searches name + identifiers)
- ContactsSettings: proper loading skeleton, empty state with action, search bar appears when contacts exist, SSE real-time updates for create/update/delete
- ContactCard: clean layout with icon (User/Bot), type badge, linked user badge, identifiers as badges, edit/delete with confirmation, cascade warning on delete mentioning platform IDs
- ContactFormDialog: proper form reset on open/close, name validation (required, max 200), type selector (human/kin), conditional linked user/kin selectors, identifier CRUD with combobox label picker (suggestions + custom), proper error display
- ContactNotes: per-kin notes with avatar, scope icons (global/private), inline edit with textarea, add note flow with kin selector + scope selector, proper empty states
- ContactPlatformIds: platform selector with extra platforms (IRC, webchat), inline add form with Enter/Escape support, revoke with confirmation dialog explaining consequences
- Server routes: solid input validation (name trim, max lengths, type enum check, required fields), proper 404/409 error codes, user-link uniqueness check
- Server services: batch-fetched listContactsWithDetails (efficient), search across names/identifiers/notes, FK cascade on delete, SSE broadcasts on all mutations, duplicate identifier prevention
- Area 12: Webhooks