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>
170 lines
5.1 KiB
Python
170 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Patterns and Gotchas Management
|
|
================================
|
|
|
|
Functions for managing code patterns and gotchas (pitfalls to avoid).
|
|
"""
|
|
|
|
import logging
|
|
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 append_gotcha(spec_dir: Path, gotcha: str) -> None:
|
|
"""
|
|
Append a gotcha (pitfall to avoid) to the gotchas list.
|
|
|
|
Gotchas are deduplicated - if the same gotcha already exists,
|
|
it won't be added again.
|
|
|
|
Args:
|
|
spec_dir: Path to spec directory
|
|
gotcha: Description of the pitfall to avoid
|
|
|
|
Example:
|
|
append_gotcha(spec_dir, "Database connections must be closed in workers")
|
|
append_gotcha(spec_dir, "API rate limits: 100 req/min per IP")
|
|
"""
|
|
memory_dir = get_memory_dir(spec_dir)
|
|
gotchas_file = memory_dir / "gotchas.md"
|
|
|
|
# Load existing gotchas
|
|
existing_gotchas = set()
|
|
if gotchas_file.exists():
|
|
content = gotchas_file.read_text(encoding="utf-8")
|
|
# Extract bullet points
|
|
for line in content.split("\n"):
|
|
line = line.strip()
|
|
if line.startswith("- "):
|
|
existing_gotchas.add(line[2:].strip())
|
|
|
|
# Add new gotcha if not duplicate
|
|
gotcha_stripped = gotcha.strip()
|
|
if gotcha_stripped and gotcha_stripped not in existing_gotchas:
|
|
# Append to file
|
|
with open(gotchas_file, "a", encoding="utf-8") as f:
|
|
if gotchas_file.stat().st_size == 0:
|
|
# First entry - add header
|
|
f.write("# Gotchas and Pitfalls\n\n")
|
|
f.write("Things to watch out for in this codebase:\n\n")
|
|
f.write(f"- {gotcha_stripped}\n")
|
|
|
|
# Also save to Graphiti if enabled
|
|
if is_graphiti_memory_enabled():
|
|
try:
|
|
graphiti = run_async(get_graphiti_memory(spec_dir))
|
|
if graphiti:
|
|
run_async(graphiti.save_gotcha(gotcha_stripped))
|
|
run_async(graphiti.close())
|
|
except Exception as e:
|
|
logger.warning(f"Graphiti gotcha save failed: {e}")
|
|
|
|
|
|
def load_gotchas(spec_dir: Path) -> list[str]:
|
|
"""
|
|
Load all gotchas.
|
|
|
|
Args:
|
|
spec_dir: Path to spec directory
|
|
|
|
Returns:
|
|
List of gotcha strings
|
|
"""
|
|
memory_dir = get_memory_dir(spec_dir)
|
|
gotchas_file = memory_dir / "gotchas.md"
|
|
|
|
if not gotchas_file.exists():
|
|
return []
|
|
|
|
content = gotchas_file.read_text(encoding="utf-8")
|
|
gotchas = []
|
|
|
|
for line in content.split("\n"):
|
|
line = line.strip()
|
|
if line.startswith("- "):
|
|
gotchas.append(line[2:].strip())
|
|
|
|
return gotchas
|
|
|
|
|
|
def append_pattern(spec_dir: Path, pattern: str) -> None:
|
|
"""
|
|
Append a code pattern to follow.
|
|
|
|
Patterns are deduplicated - if the same pattern already exists,
|
|
it won't be added again.
|
|
|
|
Args:
|
|
spec_dir: Path to spec directory
|
|
pattern: Description of the code pattern
|
|
|
|
Example:
|
|
append_pattern(spec_dir, "Use try/except with specific exceptions")
|
|
append_pattern(spec_dir, "All API responses use {success: bool, data: any, error: string}")
|
|
"""
|
|
memory_dir = get_memory_dir(spec_dir)
|
|
patterns_file = memory_dir / "patterns.md"
|
|
|
|
# Load existing patterns
|
|
existing_patterns = set()
|
|
if patterns_file.exists():
|
|
content = patterns_file.read_text(encoding="utf-8")
|
|
# Extract bullet points
|
|
for line in content.split("\n"):
|
|
line = line.strip()
|
|
if line.startswith("- "):
|
|
existing_patterns.add(line[2:].strip())
|
|
|
|
# Add new pattern if not duplicate
|
|
pattern_stripped = pattern.strip()
|
|
if pattern_stripped and pattern_stripped not in existing_patterns:
|
|
# Append to file
|
|
with open(patterns_file, "a", encoding="utf-8") as f:
|
|
if patterns_file.stat().st_size == 0:
|
|
# First entry - add header
|
|
f.write("# Code Patterns\n\n")
|
|
f.write("Established patterns to follow in this codebase:\n\n")
|
|
f.write(f"- {pattern_stripped}\n")
|
|
|
|
# Also save to Graphiti if enabled
|
|
if is_graphiti_memory_enabled():
|
|
try:
|
|
graphiti = run_async(get_graphiti_memory(spec_dir))
|
|
if graphiti:
|
|
run_async(graphiti.save_pattern(pattern_stripped))
|
|
run_async(graphiti.close())
|
|
except Exception as e:
|
|
logger.warning(f"Graphiti pattern save failed: {e}")
|
|
|
|
|
|
def load_patterns(spec_dir: Path) -> list[str]:
|
|
"""
|
|
Load all code patterns.
|
|
|
|
Args:
|
|
spec_dir: Path to spec directory
|
|
|
|
Returns:
|
|
List of pattern strings
|
|
"""
|
|
memory_dir = get_memory_dir(spec_dir)
|
|
patterns_file = memory_dir / "patterns.md"
|
|
|
|
if not patterns_file.exists():
|
|
return []
|
|
|
|
content = patterns_file.read_text(encoding="utf-8")
|
|
patterns = []
|
|
|
|
for line in content.split("\n"):
|
|
line = line.strip()
|
|
if line.startswith("- "):
|
|
patterns.append(line[2:].strip())
|
|
|
|
return patterns
|