Skip to content

Commit a7d2223

Browse files
frankbriaTest User
andauthored
feat(api): Phase 2 Server Layer - Routes delegating to core modules (#322) (#325)
* docs: add Phase 2 route audit and gap analysis Step 1: Route Audit (PHASE_2_ROUTE_AUDIT.md) - Inventoried all 78 API routes across 19 router files - Classified routes: 3 delegating, 52 need extraction, 23 server-only - Priority matrix: 5 CRITICAL, 18 HIGH, 22 MEDIUM, 10 LOW/Deferred Step 2: Gap Analysis (PHASE_2_GAP_ANALYSIS.md) - Mapped CLI commands to core modules (all present) - Identified 11 gaps requiring new core functions/modules - Documented extraction priority order for route refactoring - Added core module function reference appendix Part of #322 - Server Layer Refactor * feat(server): add v2 discovery router delegating to core Step 3.1 of #322 - Extract discovery business logic to core. Core module enhancements (core/prd_discovery.py): - Add module-level convenience functions for route delegation: - start_discovery_session() - creates and starts session - get_session() - loads existing session by ID - process_discovery_answer() - processes answer with result dict - generate_prd_from_discovery() - generates PRD from complete session - get_discovery_status() - returns status with progress/question - reset_discovery() - marks session as complete/abandoned New v2 discovery router (ui/routers/discovery_v2.py): - POST /api/v2/discovery/start - start new session - GET /api/v2/discovery/status - get session status - POST /api/v2/discovery/{session_id}/answer - submit answer - POST /api/v2/discovery/{session_id}/generate-prd - generate PRD - POST /api/v2/discovery/reset - reset session - POST /api/v2/discovery/generate-tasks - generate tasks from PRD New v2 workspace dependency (ui/dependencies.py): - get_v2_workspace() - resolves Workspace from path or default The v1 discovery router remains for backwards compatibility with existing web UI. The v2 router uses Workspace (path-based) instead of project_id and delegates to core modules. * docs: update gap analysis with discovery extraction progress Track Step 3.1 completion: - Core convenience functions added to prd_discovery.py - V2 discovery router created at /api/v2/discovery/* - V2 workspace dependency added - Updated progress tracker with completed/remaining items * feat(server): add v2 task execution router delegating to core Step 3.2 of #322 - Extract task execution business logic to core. Core module enhancements (core/runtime.py): - Add ApprovalResult and AssignmentResult dataclasses - Add approve_tasks() - transitions BACKLOG tasks to READY - Add check_assignment_status() - checks if execution can start - Add get_ready_task_ids() - gets all READY task IDs New v2 task router (ui/routers/tasks_v2.py): - GET /api/v2/tasks - list tasks with status filter - GET /api/v2/tasks/{id} - get single task - POST /api/v2/tasks/approve - approve tasks with optional execution - GET /api/v2/tasks/assignment-status - check if can assign - POST /api/v2/tasks/execute - start batch execution - POST /api/v2/tasks/{id}/start - start single task run - POST /api/v2/tasks/{id}/stop - stop running task - POST /api/v2/tasks/{id}/resume - resume blocked task The v1 router remains for backwards compatibility. V2 routes use conductor.start_batch() for execution instead of LeadAgent. * docs: update gap analysis with task execution progress Track Step 3.2 completion: - Core runtime functions added for task approval/assignment - V2 tasks router created at /api/v2/tasks/* - Updated progress tracker - Added v2 task routes to documentation table * feat(api): add v2 checkpoints router delegating to core Add checkpoint diff function to core/checkpoints.py and create checkpoints_v2 router that delegates all operations to core module. - Add diff() function to compare two checkpoints and show task changes - Add TaskDiff and CheckpointDiff dataclasses for structured diff results - Create checkpoints_v2.py router with 6 endpoints: - POST /api/v2/checkpoints (create) - GET /api/v2/checkpoints (list) - GET /api/v2/checkpoints/{id} (get) - POST /api/v2/checkpoints/{id}/restore - DELETE /api/v2/checkpoints/{id} - GET /api/v2/checkpoints/{a}/diff/{b} - Register router in server.py Part of Phase 2 server layer refactor (#322). * feat(api): add v2 schedule router delegating to core Create core/schedule.py with v2-compatible wrappers around the existing TaskScheduler, bridging v2 Workspace/Task models with v1 scheduler using ID mapping. - Add get_schedule() function for task scheduling with CPM - Add predict_completion() function for completion date prediction - Add get_bottlenecks() function for identifying scheduling issues - Create schedule_v2.py router with 3 endpoints: - GET /api/v2/schedule (get schedule) - GET /api/v2/schedule/predict (predict completion) - GET /api/v2/schedule/bottlenecks (identify bottlenecks) - Register router in server.py Part of Phase 2 server layer refactor (#322). * feat(api): add v2 templates router delegating to core Create core/templates.py with v2-compatible wrappers around the TaskTemplateManager, using v2 Workspace/Task models for task creation. - Add list_templates() function for listing available templates - Add get_template() function for getting template details - Add get_categories() function for listing categories - Add apply_template() function for creating tasks from templates - Create templates_v2.py router with 4 endpoints: - GET /api/v2/templates (list templates) - GET /api/v2/templates/categories (list categories) - GET /api/v2/templates/{id} (get template details) - POST /api/v2/templates/apply (apply template) - Register router in server.py Part of Phase 2 server layer refactor (#322). * docs: update gap analysis with completed HIGH priority items Mark checkpoints, schedule, and templates extractions as complete. Update core module inventory to include new schedule.py and templates.py. Add v2 route documentation for checkpoints, schedule, and templates. All HIGH priority extraction items from Phase 2B are now complete. * feat(api): add v2 projects router delegating to core Create core/project_status.py with v2-compatible functions for workspace status, progress metrics, and session state management. - Add get_task_counts() for task count statistics - Add get_progress_metrics() for completion progress and blockers - Add get_workspace_status() for comprehensive workspace status - Add get_session_state(), save_session_state(), clear_session_state() - Create projects_v2.py router with 5 endpoints: - GET /api/v2/projects/status (workspace status) - GET /api/v2/projects/progress (progress metrics) - GET /api/v2/projects/task-counts (task counts) - GET /api/v2/projects/session (session state) - DELETE /api/v2/projects/session (clear session) - Register router in server.py Part of Phase 2 server layer refactor (#322). * docs: update gap analysis with projects/session extraction Mark project status and session management extraction as complete. Update core module inventory to include new project_status.py. Add v2 projects route documentation. * feat(api): add v2 git and review routers delegating to core MEDIUM priority feature extraction (Steps 3.7-3.8): Core modules: - core/git.py: Headless git operations (status, commit, diff, branch) - core/review.py: Code review with quality analyzers V2 routers: - ui/routers/git_v2.py: /api/v2/git/* endpoints - ui/routers/review_v2.py: /api/v2/review/* endpoints Feature categorization: - Essential: git status/commit, review trigger/status - Nice-to-have: branch management, metrics, chat - Can-defer: advanced analytics All 956 core tests pass. Part of #322 - Server Layer Refactor * feat(api): establish route consistency standards for v2 API Phase 2 - Route Consistency (Step 4): Standards established: - URL Pattern: /api/v2/{resource}/{id?}/{action?} - Response Format: { success, data, message } - Error Format: { error, detail, code } New files: - ui/response_models.py: Standard ApiResponse, ApiError, ErrorCodes Updated: - ui/routers/tasks_v2.py: Uses api_error() for all exceptions - docs/PHASE_2_ROUTE_CONSISTENCY.md: Audit document 84 task-related tests pass. Part of #322 - Server Layer Refactor * feat(api): add v2 routers for blockers, PRD CRUD, and task management - Add blockers_v2.py with full CRUD for human-in-the-loop workflow - GET/POST /api/v2/blockers for list/create - GET /api/v2/blockers/{id} for show - POST /api/v2/blockers/{id}/answer and /resolve for state transitions - Add prd_v2.py for PRD document management - GET /api/v2/prd for list, GET /api/v2/prd/latest for most recent - POST /api/v2/prd for store, DELETE /api/v2/prd/{id} - GET/POST /api/v2/prd/{id}/versions for version management - GET /api/v2/prd/{id}/diff for version comparison - Add PATCH/DELETE to tasks_v2.py for task CRUD - PATCH /api/v2/tasks/{id} for updating title/description/priority/status - DELETE /api/v2/tasks/{id} for task deletion - Add CLI-to-API mapping documentation for Phase 2 tracking Coverage improvements: - Blockers: 0% → 100% - PRD CRUD: 0% → 100% - Tasks CRUD: 0% → 100% * feat(api): add SSE streaming and run status endpoints for task output - Add GET /api/v2/tasks/{id}/stream for real-time output streaming - Server-Sent Events (SSE) with line, info, error, and done event types - Support for --tail N to show historical lines before live streaming - 5-minute timeout with graceful connection handling - Add GET /api/v2/tasks/{id}/run for run status - Returns current or most recent run details - Includes status, timing, and output availability - Add get_latest_run() to core/runtime.py - Retrieves most recent run for a task regardless of status High Priority routes now 100% complete: - Blockers CRUD ✅ - PRD CRUD ✅ - Task CRUD ✅ - Streaming/Run status ✅ * test(ui): add integration tests for v2 routers Add comprehensive integration tests for blockers_v2, prd_v2, and tasks_v2 routers using FastAPI TestClient with workspace dependency overrides. Tests verify: - Core module delegation (routes call correct core functions) - Valid input handling (correct responses for valid requests) - Error handling (appropriate 4xx/5xx responses) - Standard API response format adherence 50 new tests covering: - blockers_v2: list, create, get, answer, resolve, error cases - prd_v2: list, latest, create, get, delete, versions, diff, error cases - tasks_v2: list, get, update, delete, start, stop, run, stream, error cases Also adds PHASE_2_BUSINESS_LOGIC_AUDIT.md documenting v1 routers that still contain embedded business logic requiring future extraction. Issue: #322 * docs: update Phase 2 documentation with progress and developer guide Updates: - V2_STRATEGIC_ROADMAP.md: Mark Phase 2 as in progress, add progress summary table showing v2 router completion status - PHASE_2_BUSINESS_LOGIC_AUDIT.md: Add remaining work section with priority table - PHASE_2_DEVELOPER_GUIDE.md: New guide documenting the thin adapter pattern for implementing v2 routers, including templates, anti-patterns, and testing The developer guide provides: - Implementation template for new v2 routers - URL pattern and response format standards - Error handling patterns with standard codes - Testing approach with dependency overrides - Checklist for new router implementation Issue: #322 * feat(api): add 6 missing v2 routers completing Phase 2 server layer (#322) Add all remaining v2 routers to achieve 1:1 CLI-to-API mapping: - workspace_v2.py: POST/GET/PATCH /api/v2/workspaces (4 routes) - batches_v2.py: GET/POST /api/v2/batches (5 routes) - diagnose_v2.py: POST/GET /api/v2/tasks/{id}/diagnose (2 routes) - pr_v2.py: GET/POST /api/v2/pr (5 routes) - environment_v2.py: GET/POST /api/v2/env (4 routes) - gates_v2.py: GET/POST /api/v2/gates (2 routes) All routers follow the thin adapter pattern: - Parse HTTP requests - Delegate to core modules - Format responses Total: 80 v2 routes across 16 routers Tests: 126 passing (50 router + 76 CLI integration) * fix(api): address code review feedback for Phase 2 PR Critical fixes: - review.py: Fix API mismatches with quality analyzers - OWASPPatterns now receives project_path argument - Use analyze_file (not scan_file) for SecurityScanner - Use check_file (not check) for OWASPPatterns - Handle List[ReviewFinding] return types correctly - environment_v2.py: Fix ToolInstaller API - Use install_tool() instead of non-existent install() - Replace list_installable_tools() with hardcoded list Major fixes: - project_status.py: Fix workspace.name → workspace.repo_path.name - project_status.py: Remove invalid limit param from blockers.list_open() - git.py: Handle empty repos (no commits) in branch detection - git.py: Add path validation to prevent traversal attacks - dependencies.py: Handle FileNotFoundError from get_workspace() - dependencies.py: Guard against None default_workspace_path - schedule_v2.py: Standardize error handling with api_error/ErrorCodes All 126 integration tests pass. --------- Co-authored-by: Test User <test@example.com>
1 parent 924ba7f commit a7d2223

35 files changed

Lines changed: 10431 additions & 31 deletions

codeframe/core/checkpoints.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,135 @@ def _row_to_checkpoint(row: tuple) -> Checkpoint:
347347
snapshot=json.loads(row[3]) if row[3] else {},
348348
created_at=datetime.fromisoformat(row[4]),
349349
)
350+
351+
352+
# ============================================================================
353+
# Checkpoint Diff (Route Delegation Helper)
354+
# ============================================================================
355+
356+
357+
@dataclass
358+
class TaskDiff:
359+
"""Difference in a single task between checkpoints."""
360+
361+
task_id: str
362+
title: str
363+
old_status: Optional[str]
364+
new_status: Optional[str]
365+
change_type: str # "added", "removed", "status_changed", "unchanged"
366+
367+
368+
@dataclass
369+
class CheckpointDiff:
370+
"""Difference between two checkpoints.
371+
372+
Attributes:
373+
checkpoint_a: First checkpoint (older)
374+
checkpoint_b: Second checkpoint (newer)
375+
task_diffs: List of task differences
376+
summary: Summary counts of changes
377+
"""
378+
379+
checkpoint_a_id: str
380+
checkpoint_a_name: str
381+
checkpoint_b_id: str
382+
checkpoint_b_name: str
383+
task_diffs: list[TaskDiff]
384+
summary: dict[str, int]
385+
386+
387+
def diff(
388+
workspace: Workspace,
389+
checkpoint_id_a: str,
390+
checkpoint_id_b: str,
391+
) -> CheckpointDiff:
392+
"""Compare two checkpoints and return the differences.
393+
394+
Compares task statuses between two checkpoints to show what changed.
395+
396+
Args:
397+
workspace: Target workspace
398+
checkpoint_id_a: First checkpoint ID (typically older)
399+
checkpoint_id_b: Second checkpoint ID (typically newer)
400+
401+
Returns:
402+
CheckpointDiff with task differences and summary
403+
404+
Raises:
405+
ValueError: If either checkpoint not found
406+
407+
Example:
408+
diff_result = diff(workspace, "abc123", "def456")
409+
for task_diff in diff_result.task_diffs:
410+
if task_diff.change_type == "status_changed":
411+
print(f"{task_diff.title}: {task_diff.old_status} -> {task_diff.new_status}")
412+
"""
413+
# Load both checkpoints
414+
checkpoint_a = get(workspace, checkpoint_id_a)
415+
if not checkpoint_a:
416+
raise ValueError(f"Checkpoint not found: {checkpoint_id_a}")
417+
418+
checkpoint_b = get(workspace, checkpoint_id_b)
419+
if not checkpoint_b:
420+
raise ValueError(f"Checkpoint not found: {checkpoint_id_b}")
421+
422+
# Extract task data from snapshots
423+
tasks_a = {t["id"]: t for t in checkpoint_a.snapshot.get("tasks", [])}
424+
tasks_b = {t["id"]: t for t in checkpoint_b.snapshot.get("tasks", [])}
425+
426+
# Find all task IDs
427+
all_task_ids = set(tasks_a.keys()) | set(tasks_b.keys())
428+
429+
# Compare tasks
430+
task_diffs = []
431+
summary = {"added": 0, "removed": 0, "status_changed": 0, "unchanged": 0}
432+
433+
for task_id in sorted(all_task_ids):
434+
task_a = tasks_a.get(task_id)
435+
task_b = tasks_b.get(task_id)
436+
437+
if task_a is None:
438+
# Task added in checkpoint B
439+
task_diffs.append(TaskDiff(
440+
task_id=task_id,
441+
title=task_b.get("title", "Unknown"),
442+
old_status=None,
443+
new_status=task_b.get("status"),
444+
change_type="added",
445+
))
446+
summary["added"] += 1
447+
448+
elif task_b is None:
449+
# Task removed in checkpoint B
450+
task_diffs.append(TaskDiff(
451+
task_id=task_id,
452+
title=task_a.get("title", "Unknown"),
453+
old_status=task_a.get("status"),
454+
new_status=None,
455+
change_type="removed",
456+
))
457+
summary["removed"] += 1
458+
459+
elif task_a.get("status") != task_b.get("status"):
460+
# Status changed
461+
task_diffs.append(TaskDiff(
462+
task_id=task_id,
463+
title=task_b.get("title", task_a.get("title", "Unknown")),
464+
old_status=task_a.get("status"),
465+
new_status=task_b.get("status"),
466+
change_type="status_changed",
467+
))
468+
summary["status_changed"] += 1
469+
470+
else:
471+
# Unchanged (only include if requested, skip for brevity)
472+
summary["unchanged"] += 1
473+
474+
return CheckpointDiff(
475+
checkpoint_a_id=checkpoint_a.id,
476+
checkpoint_a_name=checkpoint_a.name,
477+
checkpoint_b_id=checkpoint_b.id,
478+
checkpoint_b_name=checkpoint_b.name,
479+
task_diffs=task_diffs,
480+
summary=summary,
481+
)

0 commit comments

Comments
 (0)