Skip to content
This repository was archived by the owner on Aug 15, 2025. It is now read-only.

Commit 070b7cb

Browse files
committed
feat: add comprehensive database maintenance and diagnostic tools
Add suite of database maintenance, diagnostic, and emergency repair tools to improve system reliability and support development workflows. Database maintenance tools: - apply_schema_fix.py - Apply schema corrections and validations - check_db_access.py - Verify database connectivity and permissions - check_schema.py - Validate database schema integrity - create_fresh_db.py - Initialize clean database for development - emergency_schema_fix.py - Emergency schema repair procedures - fix_db_simple.py - Simplified database repair workflows - fix_schema.sql - SQL scripts for schema corrections - fix_unicode_error.py - Handle encoding and Unicode issues Emergency and diagnostic tools: - emergency_fix.py - Critical system repair procedures - quick_fix.py - Rapid issue resolution scripts - run_fix.py - Automated fix execution framework - verify_fix.py - Validation and testing after repairs - debug_maintenance_issue.py - Maintenance coordinator debugging Cleanup and task management: - cleanup_duplicate_tasks.py - Remove duplicate task entries - cleanup_old_tasks.py - Archive and clean stale tasks - simple_cleanup_script.py - Basic maintenance routines - test_maintenance_fix.py - Validate maintenance operations Benefits: - ✅ Improved system reliability and recovery capabilities - ✅ Better diagnostic tools for troubleshooting issues - ✅ Automated maintenance and cleanup procedures - ✅ Emergency repair capabilities for production issues - ✅ Enhanced development workflow support 🔧 Enhanced system maintenance and diagnostic capabilities
1 parent 78d0ce6 commit 070b7cb

17 files changed

Lines changed: 1281 additions & 0 deletions

apply_schema_fix.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import sqlite3
2+
import os
3+
4+
# Change to project directory
5+
import sys
6+
from pathlib import Path
7+
os.chdir(Path(__file__).parent)
8+
9+
print("🔧 Applying emergency database schema fix...")
10+
11+
# Connect to database
12+
conn = sqlite3.connect("task_orchestrator.db")
13+
cursor = conn.cursor()
14+
15+
# Check current schema
16+
cursor.execute("PRAGMA table_info(subtasks)")
17+
columns = cursor.fetchall()
18+
existing_cols = [col[1] for col in columns]
19+
print(f"Current columns: {existing_cols}")
20+
21+
# Add missing columns
22+
fixes = [
23+
"ALTER TABLE subtasks ADD COLUMN verification_status TEXT DEFAULT 'pending';",
24+
"ALTER TABLE subtasks ADD COLUMN prerequisite_satisfaction_required BOOLEAN DEFAULT 0;",
25+
"ALTER TABLE subtasks ADD COLUMN auto_maintenance_enabled BOOLEAN DEFAULT 1;",
26+
"ALTER TABLE subtasks ADD COLUMN quality_gate_level TEXT DEFAULT 'standard';"
27+
]
28+
29+
applied_fixes = []
30+
for fix in fixes:
31+
try:
32+
cursor.execute(fix)
33+
applied_fixes.append(fix)
34+
print(f"✅ Applied: {fix}")
35+
except Exception as e:
36+
if "duplicate column name" in str(e):
37+
print(f"⚠️ Column already exists: {fix}")
38+
else:
39+
print(f"❌ Failed: {fix} - {e}")
40+
41+
conn.commit()
42+
conn.close()
43+
44+
print(f"\n✅ Applied {len(applied_fixes)} schema fixes!")
45+
print("\n🧪 Testing orchestrator functionality...")
46+
47+
try:
48+
from mcp_task_orchestrator.db.persistence import DatabasePersistenceManager
49+
persistence = DatabasePersistenceManager()
50+
tasks = persistence.get_all_active_tasks()
51+
print(f"✅ Successfully imported and got {len(tasks)} active tasks")
52+
persistence.dispose()
53+
print("✅ Database connection disposed properly")
54+
print("\n🎉 Emergency schema repair SUCCESSFUL!")
55+
print("\n📋 Next steps:")
56+
print("1. Restart Claude Desktop to clear any cached tool states")
57+
print("2. Test the orchestration tools to verify they work")
58+
print("3. If working, proceed with Phase 2: orchestrated migration system")
59+
except Exception as e:
60+
print(f"❌ Orchestrator still not working: {e}")
61+
print("Additional investigation may be needed")

check_db_access.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python3
2+
"""Check database access and try to fix issues."""
3+
4+
import os
5+
import shutil
6+
import sqlite3
7+
from pathlib import Path
8+
9+
os.chdir(Path(__file__).parent)
10+
11+
print("🔍 Checking database access...")
12+
13+
# Try to copy the database file first
14+
try:
15+
shutil.copy2("task_orchestrator.db", "task_orchestrator_backup.db")
16+
print("✅ Created backup: task_orchestrator_backup.db")
17+
except Exception as e:
18+
print(f"❌ Failed to create backup: {e}")
19+
20+
# Try to create a new temporary database
21+
try:
22+
test_conn = sqlite3.connect("test_db.db")
23+
test_conn.execute("CREATE TABLE test (id INTEGER)")
24+
test_conn.close()
25+
os.remove("test_db.db")
26+
print("✅ Can create new databases")
27+
except Exception as e:
28+
print(f"❌ Cannot create new databases: {e}")
29+
30+
# Try to open the database in read-only mode
31+
try:
32+
conn = sqlite3.connect("file:task_orchestrator.db?mode=ro", uri=True)
33+
cursor = conn.cursor()
34+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
35+
tables = cursor.fetchall()
36+
print(f"✅ Can read database - Tables: {[t[0] for t in tables]}")
37+
conn.close()
38+
except Exception as e:
39+
print(f"❌ Cannot read database: {e}")
40+
41+
print("\n💡 Attempting to recreate database with schema...")
42+
43+
# If we can't access the original, create a fresh one
44+
try:
45+
# Move the old one out of the way
46+
if os.path.exists("task_orchestrator.db"):
47+
os.rename("task_orchestrator.db", "task_orchestrator_old.db")
48+
print("📦 Moved old database to task_orchestrator_old.db")
49+
50+
# Create new database with proper schema
51+
from mcp_task_orchestrator.db.models import Base
52+
from sqlalchemy import create_engine
53+
54+
engine = create_engine('sqlite:///task_orchestrator.db')
55+
Base.metadata.create_all(engine)
56+
engine.dispose()
57+
58+
print("✅ Created new database with proper schema!")
59+
60+
except Exception as e:
61+
print(f"❌ Failed to recreate database: {e}")

check_schema.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import sqlite3
2+
3+
conn = sqlite3.connect('task_orchestrator.db')
4+
cursor = conn.cursor()
5+
6+
# Get table schema
7+
cursor.execute("PRAGMA table_info(subtasks)")
8+
columns = cursor.fetchall()
9+
10+
print("Subtasks table schema:")
11+
for col in columns:
12+
print(f" {col[1]}: {col[2]}")
13+
14+
conn.close()

cleanup_duplicate_tasks.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env python3
2+
"""Clean up duplicate Generic Task Model tasks, keeping only the enhanced plan."""
3+
4+
import sqlite3
5+
import os
6+
from datetime import datetime
7+
8+
def cleanup_duplicate_tasks():
9+
"""Remove duplicate task plans, keeping only the enhanced version."""
10+
11+
db_path = os.path.join(".task_orchestrator", "orchestrator_state.db")
12+
13+
if not os.path.exists(db_path):
14+
print("❌ Database not found")
15+
return False
16+
17+
try:
18+
conn = sqlite3.connect(db_path)
19+
cursor = conn.cursor()
20+
21+
# First, let's see what parent tasks we have
22+
cursor.execute("""
23+
SELECT DISTINCT parent_task_id, created_at
24+
FROM tasks
25+
WHERE parent_task_id IS NOT NULL
26+
ORDER BY created_at DESC
27+
""")
28+
parent_tasks = cursor.fetchall()
29+
30+
print(f"Found {len(parent_tasks)} parent task groups")
31+
32+
# Find all tasks related to Generic Task Model (older versions)
33+
cursor.execute("""
34+
SELECT task_id, title, created_at, parent_task_id
35+
FROM tasks
36+
WHERE (title LIKE '%Plugin Architecture%'
37+
OR title LIKE '%Task Type Registry%'
38+
OR title LIKE '%Extensible%Attributes%'
39+
OR title LIKE '%Generic Task Model%')
40+
AND created_at < '2025-06-03 06:36:00'
41+
ORDER BY created_at
42+
""")
43+
44+
old_tasks = cursor.fetchall()
45+
print(f"\nFound {len(old_tasks)} old Generic Task Model tasks to remove")
46+
47+
if old_tasks:
48+
# Get parent task IDs from old tasks
49+
old_parent_ids = set()
50+
for task in old_tasks:
51+
if task[3]: # parent_task_id
52+
old_parent_ids.add(task[3])
53+
54+
# Delete old subtasks
55+
old_task_ids = [task[0] for task in old_tasks]
56+
placeholders = ','.join('?' * len(old_task_ids))
57+
cursor.execute(f"DELETE FROM tasks WHERE task_id IN ({placeholders})", old_task_ids)
58+
59+
# Delete old parent tasks
60+
if old_parent_ids:
61+
placeholders = ','.join('?' * len(old_parent_ids))
62+
cursor.execute(f"DELETE FROM tasks WHERE task_id IN ({placeholders})", list(old_parent_ids))
63+
print(f"Deleted {len(old_parent_ids)} old parent tasks")
64+
65+
print(f"Deleted {len(old_task_ids)} old subtasks")
66+
67+
# Verify what's left
68+
cursor.execute("""
69+
SELECT COUNT(*) FROM tasks WHERE status = 'pending'
70+
""")
71+
remaining = cursor.fetchone()[0]
72+
print(f"\n✅ Cleanup complete. {remaining} tasks remaining (should be 24)")
73+
74+
conn.commit()
75+
conn.close()
76+
return True
77+
78+
except Exception as e:
79+
print(f"❌ Error during cleanup: {e}")
80+
return False
81+
82+
if __name__ == "__main__":
83+
cleanup_duplicate_tasks()

cleanup_old_tasks.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Direct cleanup script to archive old tasks from the database.
4+
"""
5+
6+
import sqlite3
7+
import datetime
8+
from pathlib import Path
9+
import json
10+
11+
def cleanup_old_tasks(db_path, age_days=2):
12+
"""Archive tasks older than specified days."""
13+
14+
conn = sqlite3.connect(db_path)
15+
cursor = conn.cursor()
16+
17+
# Calculate cutoff date
18+
cutoff_date = datetime.datetime.now() - datetime.timedelta(days=age_days)
19+
20+
print(f"Archiving tasks older than {age_days} days (before {cutoff_date.strftime('%Y-%m-%d')})")
21+
print("=" * 80)
22+
23+
# Find old active/pending tasks
24+
cursor.execute("""
25+
SELECT task_id, title, status, specialist_type, created_at
26+
FROM subtasks
27+
WHERE status IN ('active', 'pending')
28+
AND created_at < ?
29+
ORDER BY created_at
30+
""", (cutoff_date.isoformat(),))
31+
32+
old_tasks = cursor.fetchall()
33+
34+
if not old_tasks:
35+
print("No old tasks found to archive.")
36+
return
37+
38+
print(f"\nFound {len(old_tasks)} tasks to archive:")
39+
40+
# Create archive record
41+
archive_data = {
42+
'archived_at': datetime.datetime.now().isoformat(),
43+
'reason': f'Tasks older than {age_days} days',
44+
'tasks': []
45+
}
46+
47+
for task in old_tasks:
48+
task_id, title, status, specialist_type, created_at = task
49+
print(f"\n - {task_id}")
50+
print(f" Title: {title}")
51+
print(f" Status: {status}")
52+
print(f" Created: {created_at}")
53+
54+
archive_data['tasks'].append({
55+
'task_id': task_id,
56+
'title': title,
57+
'status': status,
58+
'specialist_type': specialist_type,
59+
'created_at': created_at
60+
})
61+
62+
# Save archive
63+
archive_dir = Path("archives/task_cleanup")
64+
archive_dir.mkdir(parents=True, exist_ok=True)
65+
66+
archive_file = archive_dir / f"archived_tasks_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
67+
with open(archive_file, 'w') as f:
68+
json.dump(archive_data, f, indent=2)
69+
70+
print(f"\n\nArchive saved to: {archive_file}")
71+
72+
# Ask for confirmation
73+
response = input("\nProceed with archiving these tasks? (yes/no): ")
74+
75+
if response.lower() == 'yes':
76+
# Update tasks to completed with archive note
77+
for task in old_tasks:
78+
cursor.execute("""
79+
UPDATE subtasks
80+
SET status = 'completed',
81+
results = 'Archived due to age (auto-cleanup)',
82+
completed_at = ?
83+
WHERE task_id = ?
84+
""", (datetime.datetime.now().isoformat(), task[0]))
85+
86+
conn.commit()
87+
print(f"\n✓ Successfully archived {len(old_tasks)} tasks")
88+
else:
89+
print("\nArchiving cancelled.")
90+
91+
conn.close()
92+
93+
if __name__ == "__main__":
94+
db_path = Path("task_orchestrator.db")
95+
96+
if not db_path.exists():
97+
print("Database not found!")
98+
exit(1)
99+
100+
cleanup_old_tasks(db_path)

create_fresh_db.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python3
2+
"""Create a fresh database with the correct schema."""
3+
4+
import os
5+
import sys
6+
from pathlib import Path
7+
8+
# Add the project root to Python path
9+
project_root = Path(__file__).parent
10+
sys.path.insert(0, str(project_root))
11+
12+
os.chdir(project_root)
13+
14+
print("🔧 Creating fresh database with correct schema...")
15+
16+
try:
17+
# Import the models to get the schema
18+
from mcp_task_orchestrator.db.models import Base
19+
from sqlalchemy import create_engine
20+
21+
# Create a new database file with a different name
22+
db_path = "task_orchestrator_new.db"
23+
24+
# Remove if exists
25+
if os.path.exists(db_path):
26+
os.remove(db_path)
27+
28+
# Create new database with schema
29+
engine = create_engine(f'sqlite:///{db_path}')
30+
Base.metadata.create_all(engine)
31+
engine.dispose()
32+
33+
print(f"✅ Created new database: {db_path}")
34+
35+
# Verify the schema
36+
import sqlite3
37+
conn = sqlite3.connect(db_path)
38+
cursor = conn.cursor()
39+
40+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
41+
tables = [row[0] for row in cursor.fetchall()]
42+
print(f"📋 Tables created: {tables}")
43+
44+
# Check subtasks schema
45+
cursor.execute("PRAGMA table_info(subtasks)")
46+
columns = cursor.fetchall()
47+
print(f"\n📊 Subtasks table columns:")
48+
for col in columns:
49+
print(f" - {col[1]}: {col[2]}")
50+
51+
conn.close()
52+
53+
print(f"\n✅ Database created successfully!")
54+
print(f"\n📋 Next steps:")
55+
print(f"1. Stop any running orchestrator processes")
56+
print(f"2. In Windows, rename task_orchestrator.db to task_orchestrator_old.db")
57+
print(f"3. Rename task_orchestrator_new.db to task_orchestrator.db")
58+
print(f"4. Restart the orchestrator")
59+
60+
except Exception as e:
61+
print(f"❌ Failed to create database: {e}")
62+
import traceback
63+
traceback.print_exc()

0 commit comments

Comments
 (0)