Remove legacy logs/ directory, consolidate all data under .g3/

This change removes the legacy logs/ directory and consolidates all
session data, error logs, and discovery files under the .g3/ directory.

New directory structure:
- .g3/sessions/<session_id>/session.json - session logs
- .g3/errors/ - error logs (was logs/errors/)
- .g3/background_processes/ - background process logs
- .g3/discovery/ - planner discovery files (was workspace/logs/)

Changes:
- paths.rs: Remove get_logs_dir()/logs_dir(), add get_errors_dir(),
  get_background_processes_dir(), get_discovery_dir()
- session.rs: Anonymous sessions now use .g3/sessions/anonymous_<ts>/
- error_handling.rs: Errors now saved to .g3/errors/
- project.rs: Remove logs_dir() and ensure_logs_dir() methods
- feedback_extraction.rs: Remove logs_dir field and fallback logic
- planner: Use .g3/ for workspace data and .g3/discovery/ for reports
- flock.rs: Look for session metrics in .g3/sessions/
- coach_feedback.rs: Remove fallback to logs/ path
- Update all tests to use new paths
- Update README.md and .gitignore
This commit is contained in:
Dhanji R. Prasanna
2026-01-12 18:20:08 +05:30
parent 43a5d27149
commit c2aa80647a
68 changed files with 744 additions and 159 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -129,8 +129,7 @@ impl ErrorContext {
return;
}
let base_logs_dir = crate::logs_dir();
let logs_dir = base_logs_dir.join("errors");
let logs_dir = crate::paths::get_errors_dir();
if !logs_dir.exists() {
if let Err(e) = std::fs::create_dir_all(&logs_dir) {
error!("Failed to create error logs directory: {}", e);

View File

@@ -8,10 +8,9 @@
//!
//! Used by both autonomous mode (g3-cli) and planning mode (g3-planner).
use crate::{logs_dir, Agent, TaskResult};
use crate::{Agent, TaskResult};
use crate::ui_writer::UiWriter;
use serde_json::Value;
use std::path::PathBuf;
use tracing::{debug, warn};
/// Result of feedback extraction with source information
@@ -60,8 +59,6 @@ impl ExtractedFeedback {
pub struct FeedbackExtractionConfig {
/// Whether to print debug information
pub verbose: bool,
/// Custom logs directory (overrides default)
pub logs_dir: Option<PathBuf>,
/// Default feedback message if extraction fails
pub default_feedback: String,
}
@@ -70,7 +67,6 @@ impl Default for FeedbackExtractionConfig {
fn default() -> Self {
Self {
verbose: false,
logs_dir: None,
default_feedback: "The implementation needs review. Please ensure all requirements are met and the code compiles without errors.".to_string(),
}
}
@@ -149,17 +145,10 @@ fn try_extract_last_assistant_message(
session_id: &str,
config: &FeedbackExtractionConfig,
) -> Option<String> {
// Try new .g3/sessions/<session_id>/session.json path first
let log_file_path = crate::get_session_file(session_id);
let _ = config; // config no longer used but kept for API compatibility
// Fall back to old logs/ path if new path doesn't exist
let log_file_path = if log_file_path.exists() {
log_file_path
} else {
let logs_path = config.logs_dir.clone().unwrap_or_else(logs_dir);
logs_path.join(format!("g3_session_{}.json", session_id))
};
// Use .g3/sessions/<session_id>/session.json path
let log_file_path = crate::get_session_file(session_id);
if !log_file_path.exists() {
debug!("Session log file not found: {:?}", log_file_path);
return None;
@@ -214,17 +203,10 @@ fn try_extract_from_session_log(
session_id: &str,
config: &FeedbackExtractionConfig,
) -> Option<String> {
// Try new .g3/sessions/<session_id>/session.json path first
let log_file_path = crate::get_session_file(session_id);
let _ = config; // config no longer used but kept for API compatibility
// Fall back to old logs/ path if new path doesn't exist
let log_file_path = if log_file_path.exists() {
log_file_path
} else {
let logs_path = config.logs_dir.clone().unwrap_or_else(logs_dir);
logs_path.join(format!("g3_session_{}.json", session_id))
};
// Use .g3/sessions/<session_id>/session.json path
let log_file_path = crate::get_session_file(session_id);
if !log_file_path.exists() {
debug!("Session log file not found: {:?}", log_file_path);
return None;
@@ -358,17 +340,10 @@ fn try_extract_from_conversation_history(
session_id: &str,
config: &FeedbackExtractionConfig,
) -> Option<String> {
// Try new .g3/sessions/<session_id>/session.json path first
let log_file_path = crate::get_session_file(session_id);
let _ = config; // config no longer used but kept for API compatibility
// Fall back to old logs/ path if new path doesn't exist
let log_file_path = if log_file_path.exists() {
log_file_path
} else {
let logs_path = config.logs_dir.clone().unwrap_or_else(logs_dir);
logs_path.join(format!("g3_session_{}.json", session_id))
};
// Use .g3/sessions/<session_id>/session.json path
let log_file_path = crate::get_session_file(session_id);
if !log_file_path.exists() {
return None;
}
@@ -652,7 +627,6 @@ mod tests {
fn test_feedback_extraction_config_default() {
let config = FeedbackExtractionConfig::default();
assert!(!config.verbose);
assert!(config.logs_dir.is_none());
assert!(config.default_feedback.contains("review"));
}
}

View File

@@ -53,10 +53,11 @@ use std::time::{Duration, Instant};
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, warn};
// Re-export path utilities for backward compatibility
// Re-export path utilities
pub use paths::{
G3_WORKSPACE_PATH_ENV, ensure_session_dir, get_context_summary_file, get_g3_dir, get_logs_dir,
get_session_file, get_session_logs_dir, get_session_todo_path, get_thinned_dir, logs_dir,
G3_WORKSPACE_PATH_ENV, ensure_session_dir, get_context_summary_file, get_g3_dir,
get_session_file, get_session_logs_dir, get_session_todo_path, get_thinned_dir,
get_errors_dir, get_background_processes_dir, get_discovery_dir,
};
use paths::get_todo_path;
@@ -291,7 +292,7 @@ impl<W: UiWriter> Agent<W> {
working_dir: None,
background_process_manager: std::sync::Arc::new(
background_process::BackgroundProcessManager::new(
paths::get_logs_dir().join("background_processes")
paths::get_background_processes_dir()
)),
pending_images: Vec::new(),
is_agent_mode: false,

View File

@@ -1,8 +1,8 @@
//! Path utilities for G3 session and workspace management.
//!
//! This module centralizes all path-related logic for:
//! - TODO file location
//! - Logs directory
//! - TODO file location
//! - Error logs directory
//! - Session directories and files
//! - Thinned content storage
@@ -33,22 +33,29 @@ pub fn get_session_todo_path(session_id: &str) -> PathBuf {
get_session_logs_dir(session_id).join("todo.g3.md")
}
/// Get the path to the logs directory.
/// Get the path to the errors directory.
///
/// Checks for G3_WORKSPACE_PATH environment variable first (used by planning mode),
/// then falls back to "logs" in the current directory.
pub fn get_logs_dir() -> PathBuf {
if let Ok(workspace_path) = std::env::var(G3_WORKSPACE_PATH_ENV) {
PathBuf::from(workspace_path).join("logs")
} else {
std::env::current_dir().unwrap_or_default().join("logs")
}
/// Returns `.g3/errors/` in the workspace or current directory.
pub fn get_errors_dir() -> PathBuf {
get_g3_dir().join("errors")
}
/// Public accessor for the logs directory path (for use by submodules).
/// Alias for `get_logs_dir()` for backward compatibility.
pub fn logs_dir() -> PathBuf {
get_logs_dir()
/// Get the path to the background processes directory.
///
/// Returns `.g3/background_processes/` in the workspace or current directory.
pub fn get_background_processes_dir() -> PathBuf {
get_g3_dir().join("background_processes")
}
/// Get the path to the discovery logs directory (for planner mode).
///
/// Returns `.g3/discovery/` in the workspace or current directory.
pub fn get_discovery_dir() -> PathBuf {
if let Ok(workspace_path) = std::env::var(G3_WORKSPACE_PATH_ENV) {
PathBuf::from(workspace_path).join(".g3").join("discovery")
} else {
get_g3_dir().join("discovery")
}
}
/// Get the base .g3 directory path.

View File

@@ -127,18 +127,4 @@ impl Project {
std::env::set_current_dir(&self.workspace_dir)?;
Ok(())
}
/// Get the logs directory for the project
pub fn logs_dir(&self) -> PathBuf {
self.workspace_dir.join("logs")
}
/// Ensure the logs directory exists
pub fn ensure_logs_dir(&self) -> Result<()> {
let logs_dir = self.logs_dir();
if !logs_dir.exists() {
std::fs::create_dir_all(&logs_dir)?;
}
Ok(())
}
}

View File

@@ -5,7 +5,7 @@
//! operations from the Agent, keeping the Agent as a thin orchestrator.
use crate::context_window::ContextWindow;
use crate::paths::{ensure_session_dir, get_context_summary_file, get_g3_dir, get_logs_dir, get_session_file};
use crate::paths::{ensure_session_dir, get_context_summary_file, get_g3_dir, get_session_file};
use g3_providers::MessageRole;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
@@ -90,7 +90,7 @@ pub fn generate_session_id(description: &str, agent_name: Option<&str>) -> Strin
/// Save the context window to a session file.
///
/// If session_id is provided, saves to `.g3/sessions/<session_id>/session.json`.
/// Otherwise, falls back to `logs/g3_context_<timestamp>.json`.
/// Otherwise, saves to `.g3/sessions/anonymous_<timestamp>/session.json`.
pub fn save_context_window(
session_id: Option<&str>,
context_window: &ContextWindow,
@@ -110,13 +110,13 @@ pub fn save_context_window(
}
get_session_file(id)
} else {
// Fallback to old logs/ directory for sessions without ID
let logs_dir = get_logs_dir();
if let Err(e) = std::fs::create_dir_all(&logs_dir) {
error!("Failed to create logs directory: {}", e);
// Create anonymous session for sessions without ID
let anonymous_id = format!("anonymous_{}", timestamp);
if let Err(e) = ensure_session_dir(&anonymous_id) {
error!("Failed to create anonymous session directory: {}", e);
return;
}
logs_dir.join(format!("g3_context_{}.json", timestamp))
get_session_file(&anonymous_id)
};
let context_data = serde_json::json!({
@@ -252,8 +252,8 @@ pub fn log_error_to_session(
.unwrap_or_default()
.as_secs();
let logs_dir = get_logs_dir();
let filename = logs_dir.join(format!("g3_session_{}.json", session_id));
// Use the new .g3/sessions/<session_id>/session.json path
let filename = get_session_file(session_id);
// Read existing session log
let mut session_data: serde_json::Value = if filename.exists() {

View File

@@ -29,7 +29,7 @@ echo "Done!"
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)).unwrap();
}
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
println!("\n=== Background Process Demo ===");

View File

@@ -53,7 +53,7 @@ done
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)).unwrap();
}
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
// Start the process
@@ -116,7 +116,7 @@ sleep 30
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)).unwrap();
}
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
// Start a process
@@ -151,7 +151,7 @@ sleep 30
let _ = fs::remove_dir_all(&test_dir);
fs::create_dir_all(&test_dir).unwrap();
let log_dir = test_dir.join("logs");
let log_dir = test_dir.join(".g3").join("background_processes");
let manager = BackgroundProcessManager::new(log_dir);
// Getting a process that doesn't exist should return None

View File

@@ -91,7 +91,7 @@ fn test_save_and_load_continuation() {
"save_load_test".to_string(),
None,
Some("Test summary content".to_string()),
"/logs/g3_session_save_load_test.json".to_string(),
"/.g3/sessions/save_load_test/session.json".to_string(),
35.5,
Some("- [ ] Pending task".to_string()),
temp_dir.path().to_string_lossy().to_string(),
@@ -321,9 +321,9 @@ fn test_has_valid_continuation_with_existing_session_log() {
let (temp_dir, original_dir) = setup_test_env();
// Create a fake session log file
let logs_dir = temp_dir.path().join("logs");
fs::create_dir_all(&logs_dir).expect("Failed to create logs dir");
let session_log_path = logs_dir.join("g3_session_valid_test.json");
let session_dir = temp_dir.path().join(".g3").join("sessions").join("valid_test");
fs::create_dir_all(&session_dir).expect("Failed to create session dir");
let session_log_path = session_dir.join("session.json");
fs::write(&session_log_path, "{}").expect("Failed to write session log");
// Create a continuation pointing to the existing session log