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:
+1
-2
@@ -23,8 +23,7 @@ target
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Session logs directory
|
||||
logs/
|
||||
# G3 session data directory
|
||||
.g3/
|
||||
|
||||
# g3 artifacts
|
||||
|
||||
@@ -60,7 +60,7 @@ G3 includes robust error handling with automatic retry logic:
|
||||
- **Recoverable Error Detection**: Automatically identifies recoverable errors (rate limits, network issues, server errors, timeouts)
|
||||
- **Exponential Backoff with Jitter**: Implements intelligent retry delays to avoid overwhelming services
|
||||
- **Detailed Error Logging**: Captures comprehensive error context including stack traces, request/response data, and session information
|
||||
- **Error Persistence**: Saves detailed error logs to `logs/errors/` for post-mortem analysis
|
||||
- **Error Persistence**: Saves detailed error logs to `.g3/errors/` for post-mortem analysis
|
||||
- **Graceful Degradation**: Non-recoverable errors are logged with full context before terminating
|
||||
|
||||
### Tool Call Duplicate Detection
|
||||
@@ -316,12 +316,12 @@ G3 can interact with your computer's GUI for automation tasks:
|
||||
|
||||
## Session Logs
|
||||
|
||||
G3 automatically saves session logs for each interaction in the `logs/` directory. These logs contain:
|
||||
G3 automatically saves session logs for each interaction in the `.g3/sessions/` directory. These logs contain:
|
||||
- Complete conversation history
|
||||
- Token usage statistics
|
||||
- Timestamps and session status
|
||||
|
||||
The `logs/` directory is created automatically on first use and is excluded from version control.
|
||||
The `.g3/` directory is created automatically on first use and is excluded from version control.
|
||||
|
||||
## Documentation Map
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ pub struct Cli {
|
||||
#[arg(long, value_name = "MODEL")]
|
||||
pub model: Option<String>,
|
||||
|
||||
/// Disable log file creation (no logs/ directory or session logs)
|
||||
/// Disable session log file creation (no .g3/sessions/ or error logs)
|
||||
#[arg(long)]
|
||||
pub quiet: bool,
|
||||
|
||||
|
||||
@@ -54,12 +54,7 @@ pub fn extract_from_logs(
|
||||
|
||||
/// Resolve the log file path, trying new path first then falling back to old.
|
||||
fn resolve_log_path(session_id: &str) -> std::path::PathBuf {
|
||||
let new_path = g3_core::get_session_file(session_id);
|
||||
if new_path.exists() {
|
||||
new_path
|
||||
} else {
|
||||
Path::new("logs").join(format!("g3_session_{}.json", session_id))
|
||||
}
|
||||
g3_core::get_session_file(session_id)
|
||||
}
|
||||
|
||||
/// Extract feedback from a session log file.
|
||||
|
||||
@@ -4,15 +4,17 @@ use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_extract_coach_feedback_with_timing_message() {
|
||||
// Create a temporary directory for logs
|
||||
// Create a temporary directory for session logs
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let logs_dir = temp_dir.path().join("logs");
|
||||
fs::create_dir(&logs_dir).unwrap();
|
||||
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
|
||||
fs::create_dir_all(&sessions_dir).unwrap();
|
||||
|
||||
// Create a mock session log with the problematic conversation history
|
||||
// where timing message appears after the tool result
|
||||
let session_id = "test_session_123";
|
||||
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
|
||||
let session_dir = sessions_dir.join(session_id);
|
||||
fs::create_dir_all(&session_dir).unwrap();
|
||||
let log_file_path = session_dir.join("session.json");
|
||||
|
||||
let log_content = json!({
|
||||
"session_id": session_id,
|
||||
@@ -93,11 +95,13 @@ fn test_extract_coach_feedback_with_timing_message() {
|
||||
fn test_extract_only_final_output_tool_results() {
|
||||
// Test that we only extract tool results from final_output, not from other tools
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let logs_dir = temp_dir.path().join("logs");
|
||||
fs::create_dir(&logs_dir).unwrap();
|
||||
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
|
||||
fs::create_dir_all(&sessions_dir).unwrap();
|
||||
|
||||
let session_id = "test_session_final_output_only";
|
||||
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
|
||||
let session_dir = sessions_dir.join(session_id);
|
||||
fs::create_dir_all(&session_dir).unwrap();
|
||||
let log_file_path = session_dir.join("session.json");
|
||||
|
||||
let log_content = json!({
|
||||
"session_id": session_id,
|
||||
@@ -184,14 +188,16 @@ fn test_extract_only_final_output_tool_results() {
|
||||
|
||||
#[test]
|
||||
fn test_extract_coach_feedback_without_timing_message() {
|
||||
// Create a temporary directory for logs
|
||||
// Create a temporary directory for session logs
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let logs_dir = temp_dir.path().join("logs");
|
||||
fs::create_dir(&logs_dir).unwrap();
|
||||
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
|
||||
fs::create_dir_all(&sessions_dir).unwrap();
|
||||
|
||||
// Test the case where there's no timing message (backward compatibility)
|
||||
let session_id = "test_session_456";
|
||||
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
|
||||
let session_dir = sessions_dir.join(session_id);
|
||||
fs::create_dir_all(&session_dir).unwrap();
|
||||
let log_file_path = session_dir.join("session.json");
|
||||
|
||||
let log_content = json!({
|
||||
"session_id": session_id,
|
||||
@@ -256,11 +262,13 @@ fn test_extract_coach_feedback_without_timing_message() {
|
||||
fn test_extract_coach_feedback_with_multiple_tool_results() {
|
||||
// Test that we get the LAST tool result when there are multiple
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let logs_dir = temp_dir.path().join("logs");
|
||||
fs::create_dir(&logs_dir).unwrap();
|
||||
let sessions_dir = temp_dir.path().join(".g3").join("sessions");
|
||||
fs::create_dir_all(&sessions_dir).unwrap();
|
||||
|
||||
let session_id = "test_session_789";
|
||||
let log_file_path = logs_dir.join(format!("g3_session_{}.json", session_id));
|
||||
let session_dir = sessions_dir.join(session_id);
|
||||
fs::create_dir_all(&session_dir).unwrap();
|
||||
let log_file_path = session_dir.join("session.json");
|
||||
|
||||
let log_content = json!({
|
||||
"session_id": session_id,
|
||||
|
||||
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
@@ -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);
|
||||
|
||||
@@ -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 _ = config; // config no longer used but kept for API compatibility
|
||||
|
||||
// Use .g3/sessions/<session_id>/session.json path
|
||||
let log_file_path = crate::get_session_file(session_id);
|
||||
|
||||
// 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))
|
||||
};
|
||||
|
||||
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 _ = config; // config no longer used but kept for API compatibility
|
||||
|
||||
// Use .g3/sessions/<session_id>/session.json path
|
||||
let log_file_path = crate::get_session_file(session_id);
|
||||
|
||||
// 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))
|
||||
};
|
||||
|
||||
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 _ = config; // config no longer used but kept for API compatibility
|
||||
|
||||
// Use .g3/sessions/<session_id>/session.json path
|
||||
let log_file_path = crate::get_session_file(session_id);
|
||||
|
||||
// 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))
|
||||
};
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
+21
-14
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! This module centralizes all path-related logic for:
|
||||
//! - TODO file location
|
||||
//! - Logs directory
|
||||
//! - 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.
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 ===");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -735,8 +735,8 @@ async fn run_segment(
|
||||
segment_status.errors += 1;
|
||||
}
|
||||
|
||||
// Try to extract metrics from session log if available
|
||||
let log_dir = segment_dir.join("logs");
|
||||
// Try to extract metrics from session log if available (check .g3/sessions/)
|
||||
let log_dir = segment_dir.join(".g3").join("sessions");
|
||||
if log_dir.exists() {
|
||||
if let Ok(entries) = std::fs::read_dir(&log_dir) {
|
||||
for entry in entries.flatten() {
|
||||
|
||||
@@ -66,7 +66,7 @@ pub async fn get_initial_discovery_messages(
|
||||
// Step 1: Run explore_codebase to get the codebase report
|
||||
let codebase_report = explore_codebase(codebase_path);
|
||||
|
||||
// Write the codebase report to logs directory
|
||||
// Write the codebase report to discovery directory
|
||||
write_code_report(&codebase_report)?;
|
||||
|
||||
// Step 2: Build the prompt with the codebase report appended
|
||||
@@ -112,7 +112,7 @@ pub async fn get_initial_discovery_messages(
|
||||
shell_commands.len()
|
||||
));
|
||||
|
||||
// Write the discovery commands to logs directory
|
||||
// Write the discovery commands to discovery directory
|
||||
write_discovery_commands(&shell_commands)?;
|
||||
|
||||
// Step 6: Format as tool messages
|
||||
@@ -194,21 +194,21 @@ pub fn extract_summary(response: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the codebase report to logs directory
|
||||
/// Write the codebase report to discovery directory
|
||||
fn write_code_report(report: &str) -> Result<()> {
|
||||
// Get logs directory from workspace path or current dir
|
||||
let logs_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
|
||||
std::path::PathBuf::from(workspace_path).join("logs")
|
||||
// Get discovery directory from workspace path or current dir
|
||||
let discovery_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
|
||||
std::path::PathBuf::from(workspace_path).join(".g3").join("discovery")
|
||||
} else {
|
||||
std::env::current_dir().unwrap_or_default().join("logs")
|
||||
std::env::current_dir().unwrap_or_default().join(".g3").join("discovery")
|
||||
};
|
||||
|
||||
// Ensure logs directory exists
|
||||
fs::create_dir_all(&logs_dir)?;
|
||||
// Ensure discovery directory exists
|
||||
fs::create_dir_all(&discovery_dir)?;
|
||||
|
||||
// Generate timestamp in same format as tool_calls log
|
||||
let timestamp = Local::now().format("%Y%m%d_%H%M%S").to_string();
|
||||
let filename = logs_dir.join(format!("code_report_{}.log", timestamp));
|
||||
let filename = discovery_dir.join(format!("code_report_{}.log", timestamp));
|
||||
|
||||
// Write the report to file
|
||||
let mut file = OpenOptions::new()
|
||||
@@ -223,21 +223,21 @@ fn write_code_report(report: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the discovery commands to logs directory
|
||||
/// Write the discovery commands to discovery directory
|
||||
fn write_discovery_commands(commands: &[String]) -> Result<()> {
|
||||
// Get logs directory from workspace path or current dir
|
||||
let logs_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
|
||||
std::path::PathBuf::from(workspace_path).join("logs")
|
||||
// Get discovery directory from workspace path or current dir
|
||||
let discovery_dir = if let Ok(workspace_path) = std::env::var("G3_WORKSPACE_PATH") {
|
||||
std::path::PathBuf::from(workspace_path).join(".g3").join("discovery")
|
||||
} else {
|
||||
std::env::current_dir().unwrap_or_default().join("logs")
|
||||
std::env::current_dir().unwrap_or_default().join(".g3").join("discovery")
|
||||
};
|
||||
|
||||
// Ensure logs directory exists
|
||||
fs::create_dir_all(&logs_dir)?;
|
||||
// Ensure discovery directory exists
|
||||
fs::create_dir_all(&discovery_dir)?;
|
||||
|
||||
// Generate timestamp in same format as tool_calls log
|
||||
let timestamp = Local::now().format("%Y%m%d_%H%M%S").to_string();
|
||||
let filename = logs_dir.join(format!("discovery_commands_{}.log", timestamp));
|
||||
let filename = discovery_dir.join(format!("discovery_commands_{}.log", timestamp));
|
||||
|
||||
// Write the commands to file
|
||||
let mut file = OpenOptions::new()
|
||||
|
||||
@@ -324,15 +324,13 @@ pub async fn call_refinement_llm_with_tools(
|
||||
let ui_writer = PlannerUiWriter::new();
|
||||
|
||||
// CRITICAL FIX: Use the actual workspace directory, NOT codepath!
|
||||
// The workspace is where logs should be written (e.g., /tmp/g3_test_workspace)
|
||||
// The workspace is where session data should be written (e.g., /tmp/g3_test_workspace)
|
||||
// The codepath is where the source code lives (e.g., ~/RustroverProjects/g3)
|
||||
// Previous bug: was using codepath as workspace, causing logs to go to wrong location
|
||||
let workspace_path = std::path::PathBuf::from(workspace);
|
||||
let project = Project::new(workspace_path.clone());
|
||||
project.ensure_workspace_exists()?;
|
||||
project.enter_workspace()?;
|
||||
|
||||
project.ensure_logs_dir()?;
|
||||
// Create agent - not autonomous mode, just regular agent with tools
|
||||
let mut agent = Agent::new_with_readme_and_quiet(
|
||||
planner_config,
|
||||
|
||||
@@ -777,13 +777,13 @@ pub async fn run_planning_mode(
|
||||
// Set G3_WORKSPACE_PATH environment variable EARLY for all logging
|
||||
std::env::set_var("G3_WORKSPACE_PATH", workspace_dir.display().to_string());
|
||||
|
||||
// Create logs directory and verify it exists
|
||||
let logs_dir = workspace_dir.join("logs");
|
||||
if !logs_dir.exists() {
|
||||
fs::create_dir_all(&logs_dir)
|
||||
.context("Failed to create logs directory")?;
|
||||
// Create .g3 directory and verify it exists
|
||||
let g3_dir = workspace_dir.join(".g3");
|
||||
if !g3_dir.exists() {
|
||||
fs::create_dir_all(&g3_dir)
|
||||
.context("Failed to create .g3 directory")?;
|
||||
}
|
||||
print_msg(&format!("📁 Logs directory: {}", logs_dir.display()));
|
||||
print_msg(&format!("📁 G3 directory: {}", g3_dir.display()));
|
||||
|
||||
// Create the LLM provider for planning
|
||||
print_msg("🔧 Initializing planner provider...");
|
||||
|
||||
@@ -6,22 +6,22 @@ use std::path::Path;
|
||||
#[test]
|
||||
fn test_log_files_created() {
|
||||
// This test verifies that the logging functions work correctly
|
||||
// by checking that files can be created in the logs directory
|
||||
// by checking that files can be created in the discovery directory
|
||||
|
||||
// Clean up any existing test logs
|
||||
let _ = fs::remove_dir_all("logs");
|
||||
// Clean up any existing test discovery dir
|
||||
let _ = fs::remove_dir_all(".g3/discovery");
|
||||
|
||||
// Create logs directory
|
||||
fs::create_dir_all("logs").expect("Failed to create logs directory");
|
||||
// Create discovery directory
|
||||
fs::create_dir_all(".g3/discovery").expect("Failed to create discovery directory");
|
||||
|
||||
// Verify directory exists
|
||||
assert!(Path::new("logs").exists());
|
||||
assert!(Path::new("logs").is_dir());
|
||||
assert!(Path::new(".g3/discovery").exists());
|
||||
assert!(Path::new(".g3/discovery").is_dir());
|
||||
|
||||
// Test writing a code report
|
||||
let test_report = "Test codebase report\nLine 2\nLine 3";
|
||||
let timestamp = chrono::Local::now().format("%Y%m%d_%H%M%S").to_string();
|
||||
let report_filename = format!("logs/code_report_{}.log", timestamp);
|
||||
let report_filename = format!(".g3/discovery/code_report_{}.log", timestamp);
|
||||
|
||||
fs::write(&report_filename, test_report).expect("Failed to write code report");
|
||||
assert!(Path::new(&report_filename).exists());
|
||||
@@ -30,7 +30,7 @@ fn test_log_files_created() {
|
||||
assert_eq!(content, test_report);
|
||||
|
||||
// Test writing discovery commands
|
||||
let commands_filename = format!("logs/discovery_commands_{}.log", timestamp);
|
||||
let commands_filename = format!(".g3/discovery/discovery_commands_{}.log", timestamp);
|
||||
let test_commands =
|
||||
"# Discovery Commands\n# Generated by g3-planner\n\nls -la\ncat README.md\n";
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ fn test_extracted_feedback_approval_detection() {
|
||||
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"));
|
||||
}
|
||||
|
||||
@@ -90,14 +89,9 @@ fn test_feedback_extraction_config_default() {
|
||||
fn test_feedback_extraction_config_custom() {
|
||||
let config = FeedbackExtractionConfig {
|
||||
verbose: true,
|
||||
logs_dir: Some(std::path::PathBuf::from("/tmp/test_logs")),
|
||||
default_feedback: "Custom fallback message for testing".to_string(),
|
||||
};
|
||||
assert!(config.verbose);
|
||||
assert_eq!(
|
||||
config.logs_dir,
|
||||
Some(std::path::PathBuf::from("/tmp/test_logs"))
|
||||
);
|
||||
assert!(config.default_feedback.contains("Custom fallback"));
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user