Phase 06 Tier 1: Complete Backend Implementation - Recovery Tracking & Swap System

COMPLETED TASKS:
 06-01: Workout Swap System
   - Added swapped_from_id to workout_logs
   - Created workout_swaps table for history
   - POST /api/workouts/:id/swap endpoint
   - GET /api/workouts/available endpoint
   - Reversible swaps with audit trail

 06-02: Muscle Group Recovery Tracking
   - Created muscle_group_recovery table
   - Implemented calculateRecoveryScore() function
   - GET /api/recovery/muscle-groups endpoint
   - GET /api/recovery/most-recovered endpoint
   - Auto-tracking on workout log completion

 06-03: Smart Workout Recommendations
   - GET /api/recommendations/smart-workout endpoint
   - 7-day workout analysis algorithm
   - Recovery-based filtering (>30% threshold)
   - Top 3 recommendations with context
   - Context-aware reasoning messages

DATABASE CHANGES:
- Added 4 new tables: muscle_group_recovery, workout_swaps, custom_workouts, custom_workout_exercises
- Extended workout_logs with: swapped_from_id, source_type, custom_workout_id, custom_workout_exercise_id
- Created 7 new indexes for performance

IMPLEMENTATION:
- Recovery service with 4 core functions
- 2 new route handlers (recovery, smartRecommendations)
- Updated workouts router with swap endpoints
- Integrated recovery tracking into POST /api/logs
- Full error handling and logging

TESTING:
- Test file created: /backend/test/phase-06-tests.js
- Ready for E2E and staging validation

STATUS: Ready for frontend integration and production review
Branch: feature/06-phase-06
This commit is contained in:
2026-03-06 20:54:03 +01:00
parent c153a9648f
commit d81e403f01
330 changed files with 87988 additions and 367 deletions
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env node
/**
* Claude Flow Session Manager
* Handles session lifecycle: start, restore, end
*/
const fs = require('fs');
const path = require('path');
const SESSION_DIR = path.join(process.cwd(), '.claude-flow', 'sessions');
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
const commands = {
start: () => {
const sessionId = `session-${Date.now()}`;
const session = {
id: sessionId,
startedAt: new Date().toISOString(),
cwd: process.cwd(),
context: {},
metrics: {
edits: 0,
commands: 0,
tasks: 0,
errors: 0,
},
};
fs.mkdirSync(SESSION_DIR, { recursive: true });
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
console.log(`Session started: ${sessionId}`);
return session;
},
restore: () => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No session to restore');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
session.restoredAt = new Date().toISOString();
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
console.log(`Session restored: ${session.id}`);
return session;
},
end: () => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No active session');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
session.endedAt = new Date().toISOString();
session.duration = Date.now() - new Date(session.startedAt).getTime();
// Archive session
const archivePath = path.join(SESSION_DIR, `${session.id}.json`);
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
fs.unlinkSync(SESSION_FILE);
console.log(`Session ended: ${session.id}`);
console.log(`Duration: ${Math.round(session.duration / 1000 / 60)} minutes`);
console.log(`Metrics: ${JSON.stringify(session.metrics)}`);
return session;
},
status: () => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No active session');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
const duration = Date.now() - new Date(session.startedAt).getTime();
console.log(`Session: ${session.id}`);
console.log(`Started: ${session.startedAt}`);
console.log(`Duration: ${Math.round(duration / 1000 / 60)} minutes`);
console.log(`Metrics: ${JSON.stringify(session.metrics)}`);
return session;
},
update: (key, value) => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No active session');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
session.context[key] = value;
session.updatedAt = new Date().toISOString();
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
return session;
},
get: (key) => {
if (!fs.existsSync(SESSION_FILE)) return null;
try {
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
return key ? (session.context || {})[key] : session.context;
} catch { return null; }
},
metric: (name) => {
if (!fs.existsSync(SESSION_FILE)) {
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
if (session.metrics[name] !== undefined) {
session.metrics[name]++;
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
}
return session;
},
};
// CLI
const [,, command, ...args] = process.argv;
if (command && commands[command]) {
commands[command](...args);
} else {
console.log('Usage: session.js <start|restore|end|status|update|metric> [args]');
}
module.exports = commands;