bc5f550ee3
* auto-claude: subtask-1-1 - Add debug logging to capture memory initialization state Added comprehensive debug logging to memory_manager.py to reveal if _ensure_initialized() is failing silently. This captures: - PRE-INIT STATE: Memory instance details before any save attempt - is_enabled, is_initialized, group_id - Internal component states (client, queries, search) - Config validation state (is_valid, providers, database) - State object details (initialized, episode_count, errors) - PRE-SAVE CHECK: Initialization state right before save method - Logs whether _ensure_initialized() will be called - POST-SAVE CHECK: State after save method returns - Confirms if initialization actually happened - Shows save result and component states This logging will reveal the root cause of kanban task memory save failures by showing exactly what state the memory system is in during the save flow. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * auto-claude: subtask-2-1 - Update graphiti_helpers.py to add explicit initialize() call - Made get_graphiti_memory() async function - Added await memory.initialize() call following GitHub pattern - Updated save_to_graphiti_async() to await the async helper - Added proper error handling for initialization failures Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * auto-claude: subtask-2-2 - Update memory_manager.py to use async get_graphiti_memory Updated both get_graphiti_context() and save_session_memory() to properly await the now-async get_graphiti_memory() helper, which initializes the GraphitiMemory instance internally before returning. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * auto-claude: subtask-3-1 - Add Sentry capture_exception to GraphitiClient ini * auto-claude: subtask-3-2 - Add Sentry capture_exception to GraphitiMemory class - Import capture_exception from core.sentry - Add Sentry tracking to initialize() method for initialization failures - Add try/except with capture_exception to all save_* methods: - save_session_insights - save_codebase_discoveries - save_pattern - save_gotcha - save_task_outcome - save_structured_insights - Add try/except with capture_exception to all get_* methods: - get_relevant_context - get_session_history - get_similar_task_outcomes - get_patterns_and_gotchas - Include relevant context (component, operation, etc.) with each capture Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * auto-claude: subtask-3-3 - Add Sentry capture_exception to GraphitiQueries class Track all episode save failures with operation type and content summary: - add_session_insight: tracks group_id, spec_id, session_number - add_codebase_discoveries: tracks group_id, spec_id, discovery_count - add_pattern: tracks group_id, spec_id, content_summary - add_gotcha: tracks group_id, spec_id, content_summary - add_task_outcome: tracks group_id, spec_id, task_id, success, content_summary - add_structured_insights: tracks group_id, spec_id, content_summary (insight types) * auto-claude: subtask-3-4 - Add Sentry capture_exception to GraphitiSearch class - Import capture_exception from core.sentry - Add Sentry tracking to get_relevant_context with query_summary and group_id - Add Sentry tracking to get_session_history with group_id context - Add Sentry tracking to get_similar_task_outcomes with query_summary and group_id - Add Sentry tracking to get_patterns_and_gotchas with query_summary and group_id - All exception handlers now include operation name for better error grouping Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * auto-claude: subtask-4-1 - Add Sentry capture_exception to graphiti_helpers.py Add Sentry error tracking to Graphiti helper functions: - Import capture_exception from core.sentry - Track get_graphiti_memory failures with spec_dir and project_dir context - Track save_to_graphiti_async failures with spec_dir, session_num context - Track connection close failures with context information Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * auto-claude: subtask-4-2 - Add Sentry capture_exception to memory_manager.py. * auto-claude: subtask-5-1 - Create test script for memory save verification * fix: resolve ruff lint and format errors - Fix import block sorting (I001) in graphiti.py and test_memory_save.py - Fix f-string without placeholders (F541) in test_memory_save.py - Apply ruff formatting to 4 files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(memory): await async get_graphiti_memory calls to prevent AttributeError The get_graphiti_memory function was changed to async but call sites in patterns.py, codebase_map.py, and tools/memory.py were not updated. This caused graphiti variables to be coroutine objects instead of GraphitiMemory instances, resulting in AttributeError when calling methods like save_gotcha() or save_pattern(). - Wrap sync callers with run_async() helper - Add await for async caller in _save_to_graphiti_async - Add graphiti.close() calls to prevent connection leaks Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(memory): fix run_async returning Future in async context - Fix ASYNC-004: run_async() now returns None when called from async context instead of returning a Future. This prevents AttributeError when callers try to use the Future as the actual result. - This fixes ASYNC-001, ASYNC-002, ASYNC-003 in codebase_map.py and patterns.py since they already check `if graphiti:` which will be False for None. - Close the coroutine when in async context to avoid "coroutine was never awaited" warning. - Remove investigation debug logging from memory_manager.py (CMT-001) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: StillKnotKnown <192589389+StillKnotKnown@users.noreply.github.com>
103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Codebase Map Management
|
|
=======================
|
|
|
|
Functions for managing the codebase map that tracks file purposes.
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
from .graphiti_helpers import get_graphiti_memory, is_graphiti_memory_enabled, run_async
|
|
from .paths import get_memory_dir
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def update_codebase_map(spec_dir: Path, discoveries: dict[str, str]) -> None:
|
|
"""
|
|
Update the codebase map with newly discovered file purposes.
|
|
|
|
This function merges new discoveries with existing ones. If a file path
|
|
already exists, its purpose will be updated.
|
|
|
|
Args:
|
|
spec_dir: Path to spec directory
|
|
discoveries: Dictionary mapping file paths to their purposes
|
|
Example: {
|
|
"src/api/auth.py": "Handles JWT authentication",
|
|
"src/models/user.py": "User database model"
|
|
}
|
|
"""
|
|
memory_dir = get_memory_dir(spec_dir)
|
|
map_file = memory_dir / "codebase_map.json"
|
|
|
|
# Load existing map or create new
|
|
if map_file.exists():
|
|
try:
|
|
with open(map_file, encoding="utf-8") as f:
|
|
codebase_map = json.load(f)
|
|
except (OSError, json.JSONDecodeError, UnicodeDecodeError):
|
|
codebase_map = {}
|
|
else:
|
|
codebase_map = {}
|
|
|
|
# Update with new discoveries
|
|
codebase_map.update(discoveries)
|
|
|
|
# Add metadata
|
|
if "_metadata" not in codebase_map:
|
|
codebase_map["_metadata"] = {}
|
|
|
|
codebase_map["_metadata"]["last_updated"] = datetime.now(timezone.utc).isoformat()
|
|
codebase_map["_metadata"]["total_files"] = len(
|
|
[k for k in codebase_map.keys() if k != "_metadata"]
|
|
)
|
|
|
|
# Write back
|
|
with open(map_file, "w", encoding="utf-8") as f:
|
|
json.dump(codebase_map, f, indent=2, sort_keys=True)
|
|
|
|
# Also save to Graphiti if enabled
|
|
if is_graphiti_memory_enabled() and discoveries:
|
|
try:
|
|
graphiti = run_async(get_graphiti_memory(spec_dir))
|
|
if graphiti:
|
|
run_async(graphiti.save_codebase_discoveries(discoveries))
|
|
run_async(graphiti.close())
|
|
logger.info("Codebase discoveries also saved to Graphiti")
|
|
except Exception as e:
|
|
logger.warning(f"Graphiti codebase save failed: {e}")
|
|
|
|
|
|
def load_codebase_map(spec_dir: Path) -> dict[str, str]:
|
|
"""
|
|
Load the codebase map.
|
|
|
|
Args:
|
|
spec_dir: Path to spec directory
|
|
|
|
Returns:
|
|
Dictionary mapping file paths to their purposes.
|
|
Returns empty dict if no map exists.
|
|
"""
|
|
memory_dir = get_memory_dir(spec_dir)
|
|
map_file = memory_dir / "codebase_map.json"
|
|
|
|
if not map_file.exists():
|
|
return {}
|
|
|
|
try:
|
|
with open(map_file, encoding="utf-8") as f:
|
|
codebase_map = json.load(f)
|
|
|
|
# Remove metadata before returning
|
|
codebase_map.pop("_metadata", None)
|
|
return codebase_map
|
|
|
|
except (OSError, json.JSONDecodeError, UnicodeDecodeError):
|
|
return {}
|