fix: critical permission modal and config issues (#127)
CI / Lint & Test (push) Has started running
CI / Build Linux (push) Has been cancelled
CI / Build Windows (cross-compile) (push) Has been cancelled
Security Scan and Upload / Security & DefectDojo Upload (push) Has been cancelled

## Summary

This PR resolves several critical bugs that were blocking the permission modal and causing config loss:

- **Permission modal not appearing** - Fixed z-index issues and runtime errors
- **Config store race condition** - Resolved critical race condition causing settings to be lost
- **Excessive logging** - Removed redundant fmt layer that was writing to hidden stdout
- **System tool prompts** - Prevented unnecessary permission prompts for built-in tools
- **Permission batching** - Added support for parallel permission requests
- **ExitPlanMode tool** - Fixed ExitPlanMode tool not functioning correctly

## Changes Made

### Permission Modal Fixes
- Updated z-index to proper value (9999) to ensure modal appears above all other UI elements
- Fixed runtime errors that were preventing modal from rendering
- Resolved issues with permission grants not being properly applied

### Config Store Race Condition
- Fixed critical race condition where multiple rapid config updates would result in lost settings
- Ensured config writes are properly sequenced to prevent data loss
- Added proper synchronisation for config store operations

### Logging Cleanup
- Removed redundant fmt formatting layer that was outputting to hidden stdout
- Cleaned up excessive debug logging added during troubleshooting
- Removed temporary debugging documentation files

### UX Improvements
- Added close confirmation modal with minimise to tray option
- Implemented batching for parallel permission requests
- Added debug console for viewing frontend and backend logs

### ExitPlanMode Fix
- Fixed ExitPlanMode tool not functioning correctly, ensuring proper transitions out of plan mode

## Issues Resolved

Closes #112 - Permission flow now properly handles multiple tool requests
Closes #113 - ExitPlanMode tool now functions correctly
Closes #126 - Debug console feature added (partial - basic implementation complete)

## Test Plan

- [x] Permission modal appears and functions correctly
- [x] Config settings persist across app restarts
- [x] No excessive logging in production builds
- [x] System tools don't trigger permission prompts
- [x] Parallel permission requests are properly batched
- [x] Debug console displays frontend and backend logs
- [x] ExitPlanMode properly exits plan mode

---
 This PR was created with help from Hikari~ 🌸

Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Reviewed-on: #127
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #127.
This commit is contained in:
2026-02-07 01:55:49 -08:00
committed by Naomi Carrigan
parent 97a93c31c2
commit bf411adeb7
34 changed files with 2010 additions and 307 deletions
+12 -12
View File
@@ -1671,7 +1671,7 @@ pub fn check_message_achievements(
let mut newly_unlocked = Vec::new();
let message_lower = message.to_lowercase();
println!("Checking message achievements for: {}", message);
tracing::info!("Checking message achievements for: {}", message);
// Relationship & Greetings
if message_lower.contains("good morning") && progress.unlock(AchievementId::GoodMorning) {
@@ -1863,18 +1863,18 @@ pub fn check_achievements(
) -> Vec<AchievementId> {
let mut newly_unlocked = Vec::new();
println!(
tracing::info!(
"Checking achievements with stats: messages={}, tokens={}, code_blocks={}",
stats.messages_exchanged,
stats.total_input_tokens + stats.total_output_tokens,
stats.code_blocks_generated
);
println!("Currently unlocked: {:?}", progress.unlocked);
tracing::info!("Currently unlocked: {:?}", progress.unlocked);
// Token milestones
let total_tokens = stats.total_input_tokens + stats.total_output_tokens;
if total_tokens >= 1_000 && progress.unlock(AchievementId::FirstSteps) {
println!("Unlocked FirstSteps achievement!");
tracing::info!("Unlocked FirstSteps achievement!");
newly_unlocked.push(AchievementId::FirstSteps);
}
if total_tokens >= 10_000 && progress.unlock(AchievementId::GrowingStrong) {
@@ -2244,7 +2244,7 @@ pub async fn save_achievements(
// Create a serializable version with just the unlocked achievement IDs
let unlocked_list: Vec<AchievementId> = progress.unlocked.iter().cloned().collect();
println!("Saving achievements: {:?}", unlocked_list);
tracing::info!("Saving achievements: {:?}", unlocked_list);
store.set(
"unlocked",
@@ -2252,18 +2252,18 @@ pub async fn save_achievements(
);
store.save().map_err(|e| e.to_string())?;
println!("Achievements saved successfully");
tracing::info!("Achievements saved successfully");
Ok(())
}
// Load achievements from persistent store
pub async fn load_achievements(app: &tauri::AppHandle) -> AchievementProgress {
println!("Loading achievements from store...");
tracing::info!("Loading achievements from store...");
let store = match app.store("achievements.json") {
Ok(s) => s,
Err(e) => {
println!("Failed to open achievements store: {}", e);
tracing::error!("Failed to open achievements store: {}", e);
return AchievementProgress::new();
}
};
@@ -2272,19 +2272,19 @@ pub async fn load_achievements(app: &tauri::AppHandle) -> AchievementProgress {
// Get unlocked achievements
if let Some(unlocked_value) = store.get("unlocked") {
println!("Found unlocked value in store: {:?}", unlocked_value);
tracing::info!("Found unlocked value in store: {:?}", unlocked_value);
if let Ok(unlocked_list) =
serde_json::from_value::<Vec<AchievementId>>(unlocked_value.clone())
{
println!("Loaded {} achievements", unlocked_list.len());
tracing::info!("Loaded {} achievements", unlocked_list.len());
for achievement_id in unlocked_list {
progress.unlocked.insert(achievement_id);
}
} else {
println!("Failed to parse unlocked achievements");
tracing::error!("Failed to parse unlocked achievements");
}
} else {
println!("No unlocked achievements found in store");
tracing::info!("No unlocked achievements found in store");
}
progress