fix: critical permission modal and config issues #127

Merged
naomi merged 19 commits from feat/many into main 2026-02-07 01:55:50 -08:00

19 Commits

Author SHA1 Message Date
hikari d8c1c3dee1 fix: correct type assertion in stateMapper test
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m11s
CI / Lint & Test (pull_request) Successful in 16m8s
CI / Build Linux (pull_request) Successful in 20m20s
CI / Build Windows (cross-compile) (pull_request) Successful in 30m38s
The test for unknown result subtype was using an invalid subtype value
directly, which caused a TypeScript error. Changed to use a type
assertion to properly test the unknown subtype case, matching the
pattern used for testing unknown message types.

This fix ensures all TypeScript checks pass whilst still testing the
edge case behaviour for unexpected subtype values.
2026-02-07 00:19:55 -08:00
hikari d665dfa0cf test: improve coverage for snippets and stateMapper
Add comprehensive test coverage to prevent regressions:

**Snippets Store:**
- Test filteredSnippets derived store with and without category filter
- Cover all branches of the filter logic

**State Mapper:**
- Test unknown tool handling (defaults to typing)
- Test thinking content blocks in assistant messages
- Test empty content arrays
- Test all stream_event delta types (thinking_delta, text_delta)
- Test all content_block_start types (thinking, text, tool_use)
- Test unrecognized stream event types
- Test unknown result subtypes
- Test unknown message types
- Test extractTextFromMessage edge cases (null result, no delta text)

These additions bring test count from 335 to 351 and significantly improve
branch coverage for these critical utility functions.
2026-02-07 00:15:00 -08:00
hikari f6906640e4 test: add comprehensive race condition tests for config store
Add extensive test coverage to prevent regressions of bugs fixed in this branch:

- Test rapid sequential config updates
- Test concurrent updates preserving all fields
- Test overlapping save operations
- Test config data persistence across operations
- Test auto-granted tools not being lost
- Test custom theme colours persisting
- Test graceful error handling during saves
- Test config load/save cycle

Also update check-all.sh to run coverage for both frontend and backend tests,
matching the CI pipeline behaviour.

These tests would have caught both the config race condition and persistence
bugs we encountered, preventing future regressions.
2026-02-07 00:12:30 -08:00
hikari ea111569af chore: remove debugging infrastructure after permission fixes
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m0s
CI / Lint & Test (pull_request) Successful in 16m11s
CI / Build Linux (pull_request) Successful in 20m14s
CI / Build Windows (cross-compile) (pull_request) Successful in 30m1s
Removes temporary debugging docs and excessive logging that were added to diagnose and fix permission modal issues.

Cleaned up:
- Deleted DEBUGGING.md (temporary troubleshooting guide)
- Deleted FIXES-2026-02-06.md (temporary fix summary)
- Removed debug logging from all Rust modules
2026-02-06 23:40:09 -08:00
hikari 4078b2b640 fix: remove redundant fmt layer that outputs to hidden stdout
In production builds with windows_subsystem = "windows", stdout is suppressed.
The fmt::layer() was outputting to this hidden stdout, making logs invisible.

Now all tracing logs only go through TauriLogLayer to the debug console.

NOTE: There are still many println!/eprintln! calls throughout the codebase
that bypass tracing entirely. These should be gradually migrated to use
tracing::info!/tracing::error! macros for visibility in production builds.
2026-02-06 23:00:25 -08:00
hikari 0b69de4a43 feat: add comprehensive logging for permission modal debugging
Added extensive logging to help diagnose permission modal issues:

Frontend:
- Capture unhandled errors via window.addEventListener('error')
- Capture unhandled promise rejections
- All errors now visible in debug console

Backend:
- Log when emitting permission events (count and conversation ID)
- Log each individual permission request being processed
- Log when system tools are skipped
- Log each denial being processed

This will help identify where the permission flow breaks in production builds.
2026-02-06 22:57:39 -08:00
hikari 269674678c fix: update permission modal z-index to proper value
Updated the permission modal overlay z-index from z-50 to z-[60] to ensure
it displays above the character panel (z-50). This prevents the modal from
being obscured by other UI elements.

Part of the broader permission modal fix effort.
2026-02-06 22:50:38 -08:00
hikari add78a769a docs: add summary document for critical bug fixes 2026-02-06 22:43:36 -08:00
hikari 4a99848647 docs: massively expand DEBUGGING.md with comprehensive troubleshooting
Added:
- Table of contents for easy navigation
- Detailed explanations of BOTH bugs (permission modal + config loss)
- Step-by-step debugging procedures for each issue
- Code examples showing wrong vs right patterns
- Common anti-patterns to avoid (Svelte stores, event listeners)
- Quick debugging checklist
- Prevention tips and testing procedures
- Clear ordering: check console FIRST, not z-index!

This guide now documents the complete troubleshooting process so Naomi
never has to explain these issues again.
2026-02-06 22:43:08 -08:00
hikari e8b8ee17fa docs: update DEBUGGING.md with config race condition fix 2026-02-06 22:41:00 -08:00
hikari 2c64ef089e fix: resolve critical config store race condition causing config loss
CRITICAL BUG: The config store had a race condition in all methods that
accessed the current config. The pattern:

```typescript
let currentConfig = defaultConfig;
config.subscribe((c) => (currentConfig = c))();
```

This immediately unsubscribes (the `()` at the end), creating a race where
sometimes it would get the value, sometimes it wouldn't. This caused:
- Config appearing "lost" after permission approvals
- Settings resetting to defaults randomly
- Model selection not persisting

FIX:
- Created a proper `getCurrentConfig()` helper that unsubscribes cleanly
- Replaced ALL instances of the buggy pattern (12 occurrences!)
- getConfig(), updateConfig(), toggles, theme setters, font methods, etc.

This should completely fix the config loss issue.
2026-02-06 22:40:47 -08:00
hikari 80ee25fa09 docs: add comprehensive debugging guide for common issues
Created DEBUGGING.md to document:
- Permission modal troubleshooting (NOT z-index!)
- Config loss prevention and fixes
- Event listener debugging
- Testing checklist
- Emergency recovery procedures

This should prevent us from going through the same debugging nightmare twice.
2026-02-06 22:38:11 -08:00
hikari d16644a1d6 fix: resolve critical runtime errors blocking permission modal
This fixes two severe bugs:
1. Missing debugConsoleStore import causing undefined variable errors
2. Replace non-existent .log() method with console.log()
3. Add #[serde(default)] to HikariConfig to handle missing fields gracefully

The undefined variable was causing initialization to fail, which prevented
the permission modal from displaying properly.
2026-02-06 22:36:00 -08:00
hikari 1d94bdfbb0 feat: add close confirmation modal with minimize to tray option
Implemented a confirmation modal when users try to close the application:
- Modal always shows with three options: Cancel, Minimize to Tray, Close Application
- Detects if Claude is actively running and shows appropriate warning message
- Removed minimize_to_tray config setting (no longer needed)
- Added core:window:allow-hide permission for window hiding
- Created CloseAppConfirmModal component with keyboard shortcuts (Escape to cancel)
- Added close_application command to properly exit the app
- Backend emits window-close-requested event for frontend to handle

This provides better UX by giving users clear choices every time they close,
preventing accidental closures during active work sessions.
2026-02-06 21:49:13 -08:00
hikari f654c3c3ff fix: resolve permission modal issues with successful operations
Fixes #113 - EnterPlanMode/ExitPlanMode infinite permission loops

This commit addresses multiple related issues with the permission system:

1. Added system tool filtering to sibling tools loop to prevent
   EnterPlanMode/ExitPlanMode from appearing in permission modals

2. Skip permission modal processing entirely when operations succeed
   (subtype == "success"), since tools were already approved and executed

3. Emit proper state change before early return to prevent Hikari
   from getting stuck in "typing" state

The early return happens after all stats, costs, and achievements
are processed, so no data tracking is affected.

 This issue was fixed with help from Hikari~ 🌸
2026-02-06 20:50:23 -08:00
hikari 24b2e3eb48 fix: prevent permission prompts for system tools
Fixed infinite permission loop when using ExitPlanMode and other
system tools. These tools are now automatically allowed without
requiring user approval.

The issue occurred because all tool denials triggered permission
prompts, including system tools like ExitPlanMode that should never
require permission. This caused an infinite loop where:
1. Claude Code calls ExitPlanMode
2. Hikari shows permission modal
3. User approves
4. Claude Code retries ExitPlanMode
5. Loop repeats

Solution:
- Added is_system_tool helper function to identify system tools
- System tools (ExitPlanMode, EnterPlanMode) are now skipped in
  permission denial processing
- These tools execute immediately without user intervention

Closes #113
2026-02-06 20:21:05 -08:00
hikari 5b8ae63de1 feat: add debug console for frontend and backend logs
Added a comprehensive debug console feature that captures and displays
logs from both the frontend and backend in a unified interface.

Frontend changes:
- Created DebugConsole component with real-time log display
- Added debugConsoleStore for state management and console capture
- Integrated console into main layout
- Added toggle button in StatusBar with console icon
- Implemented Ctrl+` keyboard shortcut to open/close console
- Features: log filtering by level, auto-scroll, timestamps, colour-coding

Backend changes:
- Added tracing and tracing-subscriber dependencies
- Created custom TauriLogLayer to emit Rust logs to frontend
- Integrated tracing subscriber in lib.rs setup
- Logs are forwarded via Tauri events (debug:log)

Key features:
- Circular buffer (max 1000 logs) prevents memory issues
- Frontend logs captured via console method overrides
- Backend logs forwarded from Rust tracing layer
- Log level filtering (debug, info, warn, error, all)
- Source badges distinguish frontend vs backend logs
- Colour-coded log levels for easy identification
- Auto-scroll toggle for inspecting older logs
- Clear logs button for resetting the console
- Beautiful dark-themed UI matching app aesthetic

Closes #126
2026-02-06 20:16:26 -08:00
hikari 82061f125b feat: batch parallel permission requests for improved UX
Implemented intelligent permission batching that detects cancelled sibling
tool calls and presents them together in a single modal. This dramatically
improves the user experience when multiple tools require permission.

Key changes:
- Track pending tool uses from Assistant messages in thread-local storage
- Capture and batch sibling tools that get cancelled due to permission denials
- Clear pending tools on each Result message to prevent accumulation
- Use SvelteSet for reactive permission selection in the modal
- Update permission modal to display count when multiple permissions requested
- Fix check-all.sh to source nvm for pnpm access
- Add git commit instructions to CLAUDE.md for this project

Technical improvements:
- Thread-local storage for cross-message tool tracking
- Proper null checking in TypeScript permission handling
- Clippy-compliant const initialisation for thread_local
- All ESLint, TypeScript, and Rust checks passing

The modal now shows both the explicitly denied tool AND any sibling tools
that were called in parallel, allowing users to approve all permissions
in one go instead of clicking through multiple modals.
2026-02-06 19:54:12 -08:00
naomi 870de7588f feat: add claude file 2026-02-06 18:16:55 -08:00