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

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() {