266 lines
9.3 KiB
Python
Executable File
266 lines
9.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Migration script to convert flat task session files to directory structure.
|
|
|
|
This script migrates task sessions from the old flat file structure:
|
|
.workflow/versions/v001/task_sessions/task_design.yml
|
|
|
|
To the new directory structure:
|
|
.workflow/versions/v001/task_sessions/task_design/
|
|
session.yml
|
|
task.yml
|
|
operations.log
|
|
|
|
Usage:
|
|
python3 migrate_task_sessions.py [--dry-run]
|
|
|
|
Options:
|
|
--dry-run Show what would be done without making changes
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import argparse
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
# Add parent to path for imports
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
from version_manager import load_yaml, save_yaml, get_workflow_dir
|
|
|
|
|
|
# ============================================================================
|
|
# Discovery Functions
|
|
# ============================================================================
|
|
|
|
def find_flat_task_sessions() -> list[tuple[Path, str]]:
|
|
"""
|
|
Find all flat task session YAML files.
|
|
|
|
Returns:
|
|
List of tuples: (file_path, version_name)
|
|
"""
|
|
workflow_dir = get_workflow_dir()
|
|
versions_dir = workflow_dir / 'versions'
|
|
|
|
flat_sessions = []
|
|
if versions_dir.exists():
|
|
for version_dir in versions_dir.iterdir():
|
|
if version_dir.is_dir():
|
|
task_sessions_dir = version_dir / 'task_sessions'
|
|
if task_sessions_dir.exists():
|
|
for item in task_sessions_dir.iterdir():
|
|
# Check if it's a YAML file (not a directory)
|
|
if item.is_file() and item.suffix in ['.yml', '.yaml']:
|
|
flat_sessions.append((item, version_dir.name))
|
|
|
|
return flat_sessions
|
|
|
|
|
|
# ============================================================================
|
|
# Migration Functions
|
|
# ============================================================================
|
|
|
|
def migrate_task_session(file_path: Path, version: str, dry_run: bool = False) -> dict:
|
|
"""
|
|
Migrate a single flat task session to directory structure.
|
|
|
|
Args:
|
|
file_path: Path to the flat YAML file
|
|
version: Version identifier (e.g., 'v001')
|
|
dry_run: If True, only report what would be done
|
|
|
|
Returns:
|
|
Dictionary with migration results and actions taken
|
|
"""
|
|
task_id = file_path.stem # e.g., "task_design" from "task_design.yml"
|
|
parent_dir = file_path.parent
|
|
new_dir = parent_dir / task_id
|
|
|
|
result = {
|
|
'task_id': task_id,
|
|
'version': version,
|
|
'original_path': str(file_path),
|
|
'new_path': str(new_dir),
|
|
'success': False,
|
|
'actions': []
|
|
}
|
|
|
|
if dry_run:
|
|
result['actions'].append(f"Would create directory: {new_dir}")
|
|
result['actions'].append(f"Would move {file_path.name} to {new_dir}/session.yml")
|
|
result['actions'].append(f"Would create {new_dir}/task.yml (if source exists)")
|
|
result['actions'].append(f"Would create {new_dir}/operations.log")
|
|
result['success'] = True
|
|
return result
|
|
|
|
try:
|
|
# Create directory
|
|
new_dir.mkdir(exist_ok=True)
|
|
result['actions'].append(f"Created directory: {new_dir}")
|
|
|
|
# Move session file
|
|
session_data = load_yaml(str(file_path))
|
|
save_yaml(str(new_dir / 'session.yml'), session_data)
|
|
file_path.unlink() # Delete original
|
|
result['actions'].append(f"Moved session data to: {new_dir}/session.yml")
|
|
|
|
# Create task.yml snapshot (try to find original task)
|
|
task_file = Path('tasks') / f'{task_id}.yml'
|
|
if task_file.exists():
|
|
task_data = load_yaml(str(task_file))
|
|
task_data['snapshotted_at'] = datetime.now().isoformat()
|
|
task_data['source_path'] = str(task_file)
|
|
task_data['status_at_snapshot'] = task_data.get('status', 'migrated')
|
|
task_data['migration_note'] = 'Created during migration from flat file structure'
|
|
save_yaml(str(new_dir / 'task.yml'), task_data)
|
|
result['actions'].append(f"Created task snapshot: {new_dir}/task.yml")
|
|
else:
|
|
# Create minimal task.yml from session data
|
|
minimal_task = {
|
|
'id': task_id,
|
|
'type': session_data.get('task_type', 'unknown'),
|
|
'agent': session_data.get('agent', 'unknown'),
|
|
'snapshotted_at': datetime.now().isoformat(),
|
|
'source_path': 'N/A - reconstructed from session',
|
|
'status_at_snapshot': 'migrated',
|
|
'migration_note': 'Task file not found - reconstructed from session data'
|
|
}
|
|
save_yaml(str(new_dir / 'task.yml'), minimal_task)
|
|
result['actions'].append(f"Warning: Task file not found at {task_file}")
|
|
result['actions'].append(f"Created minimal task snapshot: {new_dir}/task.yml")
|
|
|
|
# Create operations.log
|
|
log_content = f"# Operations Log for {task_id}\n"
|
|
log_content += f"# Migrated: {datetime.now().isoformat()}\n"
|
|
log_content += "# Format: [timestamp] OPERATION target_type: target_id (path)\n"
|
|
log_content += "=" * 70 + "\n\n"
|
|
log_content += f"[{datetime.now().isoformat()}] MIGRATION: Converted from flat file structure\n"
|
|
|
|
# If session has operations, add them to the log
|
|
if 'operations' in session_data and session_data['operations']:
|
|
log_content += f"\n# Historical operations from session data:\n"
|
|
for op in session_data['operations']:
|
|
timestamp = op.get('performed_at', 'unknown')
|
|
op_type = op.get('type', 'UNKNOWN')
|
|
target_type = op.get('target_type', 'unknown')
|
|
target_id = op.get('target_id', 'unknown')
|
|
target_path = op.get('target_path', '')
|
|
|
|
entry = f"[{timestamp}] {op_type} {target_type}: {target_id}"
|
|
if target_path:
|
|
entry += f" ({target_path})"
|
|
|
|
diff_summary = op.get('changes', {}).get('diff_summary', '')
|
|
if diff_summary:
|
|
entry += f"\n Summary: {diff_summary}"
|
|
|
|
log_content += entry + "\n"
|
|
|
|
(new_dir / 'operations.log').write_text(log_content)
|
|
result['actions'].append(f"Created operations log: {new_dir}/operations.log")
|
|
|
|
result['success'] = True
|
|
|
|
except Exception as e:
|
|
result['error'] = str(e)
|
|
result['actions'].append(f"Error: {e}")
|
|
|
|
return result
|
|
|
|
|
|
# ============================================================================
|
|
# Main Entry Point
|
|
# ============================================================================
|
|
|
|
def main():
|
|
"""Main migration script entry point."""
|
|
parser = argparse.ArgumentParser(
|
|
description='Migrate task session files from flat structure to directories',
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog=__doc__
|
|
)
|
|
parser.add_argument(
|
|
'--dry-run',
|
|
action='store_true',
|
|
help='Show what would be done without making changes'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
dry_run = args.dry_run
|
|
|
|
# Header
|
|
print("=" * 70)
|
|
print("Task Session Migration Script".center(70))
|
|
print(f"Mode: {'DRY RUN' if dry_run else 'LIVE MIGRATION'}".center(70))
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# Find flat sessions
|
|
flat_sessions = find_flat_task_sessions()
|
|
|
|
if not flat_sessions:
|
|
print("No flat task session files found. Nothing to migrate.")
|
|
print()
|
|
print("This could mean:")
|
|
print(" 1. All task sessions are already migrated")
|
|
print(" 2. No task sessions exist yet")
|
|
print(" 3. .workflow directory doesn't exist")
|
|
return
|
|
|
|
print(f"Found {len(flat_sessions)} flat task session file(s) to migrate:")
|
|
print()
|
|
|
|
# Process each file
|
|
results = []
|
|
for file_path, version in flat_sessions:
|
|
print(f"Processing: {version}/{file_path.name}")
|
|
print("-" * 70)
|
|
|
|
result = migrate_task_session(file_path, version, dry_run)
|
|
results.append(result)
|
|
|
|
for action in result['actions']:
|
|
print(f" {action}")
|
|
|
|
if not result['success'] and 'error' in result:
|
|
print(f" ERROR: {result['error']}")
|
|
|
|
print()
|
|
|
|
# Summary
|
|
successful = sum(1 for r in results if r['success'])
|
|
failed = len(results) - successful
|
|
|
|
print("=" * 70)
|
|
print("Migration Summary".center(70))
|
|
print("=" * 70)
|
|
print(f"Total files processed: {len(results)}")
|
|
print(f"Successful migrations: {successful}")
|
|
print(f"Failed migrations: {failed}")
|
|
print()
|
|
|
|
if dry_run:
|
|
print("This was a DRY RUN. No files were modified.")
|
|
print("Run without --dry-run to perform the migration.")
|
|
else:
|
|
if successful > 0:
|
|
print("Migration completed successfully!")
|
|
print()
|
|
print("Next steps:")
|
|
print(" 1. Verify migrated files in .workflow/versions/*/task_sessions/")
|
|
print(" 2. Check that each task has session.yml, task.yml, and operations.log")
|
|
print(" 3. Test the system to ensure compatibility")
|
|
|
|
if failed > 0:
|
|
print()
|
|
print(f"WARNING: {failed} migration(s) failed. Review the errors above.")
|
|
|
|
print()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|