From d1a01c130782a8bbebe62f54737fc3b1ea2b5fc4 Mon Sep 17 00:00:00 2001 From: Kai Chappell Date: Wed, 28 Jan 2026 23:21:10 +0000 Subject: [PATCH] docs(planning): add implementation plan Architecture specification, API design, Claude integration patterns, demo mode strategy, and phased build order for IdeaForge development. --- changelog.md | 1 + docs/implementation-plan.md | 532 ++++++++++++++++++++++++++++++++++++ 2 files changed, 533 insertions(+) create mode 100644 docs/implementation-plan.md diff --git a/changelog.md b/changelog.md index f4c6ab9..502b5a6 100644 --- a/changelog.md +++ b/changelog.md @@ -12,3 +12,4 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Initial project scaffold - Project documentation (readme, CLAUDE.md, changelog) - Backend and frontend directory structure +- Implementation plan with architecture, API design, and phased build order diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md new file mode 100644 index 0000000..eac8acd --- /dev/null +++ b/docs/implementation-plan.md @@ -0,0 +1,532 @@ +# implementation plan + +Detailed architecture and build plan for IdeaForge. + +## decisions + +- **Backend:** Python 3.11+, FastAPI, async SQLAlchemy, Pydantic, structlog, Anthropic SDK +- **Frontend:** Flutter 3.27+, Riverpod 3.1+ (generator syntax), GoRouter, dio, fl_chart +- **Database:** SQLite (file-based, persistent Docker volume) +- **Relationship to idea_manager:** Fresh codebase with core code ported (not fork, not dependency) +- **Authentication:** None for v1 — demo mode uses middleware protection +- **Deployment:** Single Docker container (FastAPI serves Flutter Web build as static files) + +## relationship to idea_manager + +**Decision: Port, don't fork or import.** + +- Fork implies ongoing upstream sync with a 65KB CLI and features irrelevant to IdeaForge +- Import via git URL creates tight coupling and makes extension painful +- Monorepo conflates two different audiences (personal tool vs portfolio showcase) + +**What gets ported:** + +- SQLAlchemy models (Idea, EvaluationMetric, IdeaEvaluation, Tag, IdeaTag, IdeaDetail) +- Base + TimestampMixin +- Pydantic schemas (create/update/response for all models) +- Service layer (IdeaService, EvaluationService, MetricService, TagService, DetailService) +- Score calculation utilities +- Exception hierarchy (renamed to IdeaForgeError) +- Seed metrics (8 canonical metrics with scoring guides) +- Evaluation prompt construction (from idea_service.get_evaluation_prompt) + +**What does NOT get ported:** + +- CLI (Typer) — IdeaForge is API-only +- Dual async/sync session pattern — async only +- GenerationPrompt model and prompt_service — IdeaForge evaluates, not generates +- Export templates — not needed +- Expansion prompts — not needed + +**Changes during porting:** + +- Package name: `ideaforge` (not `idea_manager`) +- Base exception: `IdeaForgeError` (not `IdeaManagerError`) +- Config env prefix: `IDEAFORGE_` (not `IDEA_MANAGER_`) +- All services async-only (drop sync wrappers) +- New modules: `ai/`, middleware for demo mode, CORS + +## backend architecture + +### directory structure + +``` +backend/ +├── src/ideaforge/ +│ ├── __init__.py # version +│ ├── main.py # FastAPI app, CORS, exception handlers, lifespan +│ ├── config.py # Pydantic Settings (IDEAFORGE_ prefix) +│ ├── database.py # Async engine + session factory +│ ├── dependencies.py # get_session, get_ai_client, get_demo_guard +│ ├── exceptions.py # IdeaForgeError hierarchy + AIServiceError +│ ├── logging.py # structlog configuration +│ ├── enums.py # IdeaCategory, IdeaStatus, DetailType +│ ├── utils.py # score calculation +│ ├── middleware/ +│ │ ├── demo.py # DemoModeMiddleware (blocks writes) +│ │ └── rate_limit.py # RateLimitMiddleware (per-IP, AI endpoint) +│ ├── models/ # SQLAlchemy models (ported) +│ │ ├── base.py # Base, TimestampMixin +│ │ ├── idea.py +│ │ ├── metric.py +│ │ ├── evaluation.py +│ │ ├── detail.py +│ │ └── tag.py +│ ├── schemas/ # Pydantic schemas (ported + new) +│ │ ├── idea.py +│ │ ├── metric.py +│ │ ├── evaluation.py +│ │ ├── detail.py +│ │ ├── tag.py +│ │ └── ai.py # AIEvaluationRequest/Response +│ ├── services/ # Business logic (ported, async-only) +│ │ ├── idea_service.py +│ │ ├── evaluation_service.py +│ │ ├── metric_service.py +│ │ ├── tag_service.py +│ │ └── detail_service.py +│ ├── ai/ # Claude API integration (new) +│ │ ├── client.py # AsyncAnthropic wrapper with retry +│ │ ├── prompts.py # build_evaluation_prompt() +│ │ └── evaluator.py # AIEvaluator orchestration +│ ├── api/ # FastAPI routers +│ │ ├── ideas.py +│ │ ├── evaluations.py +│ │ ├── metrics.py +│ │ └── tags.py +│ └── seed/ +│ ├── metrics.py # 8 canonical metrics (ported) +│ └── demo_ideas.py # 18 curated ideas with pre-computed scores +├── alembic/ +│ └── versions/ +│ └── 001_initial.py # Full schema (fresh, not migrated from idea_manager) +├── tests/ +│ ├── conftest.py +│ ├── test_api/ +│ │ ├── test_ideas.py +│ │ ├── test_evaluations.py +│ │ └── test_ai_evaluation.py +│ ├── test_services/ +│ │ └── test_ai_evaluator.py +│ └── test_middleware/ +│ ├── test_demo_mode.py +│ └── test_rate_limit.py +├── pyproject.toml +├── alembic.ini +└── Dockerfile +``` + +### API endpoints + +**Carried from idea_manager (read-only subset):** + +| Method | Path | Description | +|--------|------|-------------| +| GET | /api/ideas | List ideas with filters + pagination | +| GET | /api/ideas/ranking | Ideas ranked by aggregate scores | +| GET | /api/ideas/stats | Aggregate statistics | +| GET | /api/ideas/compare?ids=1,2,3 | Side-by-side comparison | +| GET | /api/ideas/{id} | Single idea with evaluations | +| GET | /api/ideas/{id}/evaluations | Evaluations for an idea | +| GET | /api/metrics | List evaluation metrics | +| GET | /api/tags | List all tags | +| GET | /health | Health check | + +**New for IdeaForge:** + +| Method | Path | Description | +|--------|------|-------------| +| POST | /api/ideas/{id}/ai-evaluate | Trigger Claude API evaluation | +| GET | /api/demo/status | Demo mode info (rate limits, counts) | + +### claude API integration + +**`ai/client.py`** — AsyncAnthropic wrapper with retry: + +- Uses `claude-sonnet-4-20250514` model +- Retries up to 2 times on RateLimitError with exponential backoff +- Raises `AIServiceError` on persistent failure +- Logs request start, completion (with token usage), and errors + +**`ai/prompts.py`** — Prompt construction: + +- Ported from `idea_service.get_evaluation_prompt()` (idea_manager lines 326-409) +- Takes `Idea` and `list[EvaluationMetric]` as input +- Constructs structured prompt with idea details, all 8 metrics with scoring guides +- Requests JSON output: `{"evaluations": [{"metric_name": "", "score": N, "reasoning": ""}]}` + +**`ai/evaluator.py`** — Orchestration pipeline: + +1. Load idea with relationships from DB +2. Load active metrics +3. Build evaluation prompt +4. Call Claude API via client +5. Parse JSON response (handles markdown code blocks) +6. Validate metric names and clamp scores to valid ranges +7. Delete existing AI evaluations for this idea (evaluator="claude") +8. Store new evaluations +9. Return results + +### demo mode + +**Configuration:** + +```python +class Settings(BaseSettings): + demo_mode: bool = False + demo_rate_limit_per_hour: int = 5 + demo_seed_on_startup: bool = True +``` + +**DemoModeMiddleware:** + +- Blocks POST/PUT/DELETE/PATCH on all endpoints +- Exception: allows POST to `/api/ideas/{id}/ai-evaluate` (rate-limited separately) +- Returns 403 with message: "Write operations are disabled in demo mode" +- Adds `X-Demo-Mode: true` header to all responses + +**RateLimitMiddleware:** + +- Applies only to POST on paths containing `/ai-evaluate` +- In-memory IP tracking with sliding window (1 hour) +- Returns 429 with `retry_after_seconds` on limit breach +- Adds `X-RateLimit-Remaining` header to responses + +**Startup seeding:** + +- On app startup (lifespan hook), if `demo_mode=True` and DB is empty +- Seeds 8 metrics, then 18 curated ideas with pre-computed evaluation scores +- Ideas span 5 categories with varied score profiles for visual diversity in radar charts + +### data seeding + +18 curated ideas across 5 categories, each with pre-computed 8-metric scores: + +| Category | Count | Score profile intent | +|----------|-------|---------------------| +| saas | 4 | High revenue + market, varied feasibility | +| devtool | 3 | High feasibility + learning, moderate revenue | +| web_app | 4 | Varied complexity, strong market | +| mobile_app | 3 | High interest, moderate effort | +| game / library | 4 | High uniqueness, varied skepticism | + +Each idea includes: title, description, problem_statement, proposed_solution, +target_audience, category, status (evaluated), tags, and 8 metric scores with reasoning. + +## frontend architecture + +### flutter dependencies (pubspec.yaml) + +```yaml +dependencies: + flutter: + sdk: flutter + flutter_riverpod: ^2.6.1 + riverpod_annotation: ^2.6.1 + hooks_riverpod: ^2.6.1 + flutter_hooks: ^0.20.5 + go_router: ^14.8.1 + dio: ^5.7.0 + fpdart: ^1.1.0 + freezed_annotation: ^2.4.4 + json_annotation: ^4.9.0 + fl_chart: ^0.70.2 + talker: ^4.5.2 + talker_dio_logger: ^4.5.2 + shimmer: ^3.0.0 + intl: ^0.19.0 + +dev_dependencies: + flutter_test: + sdk: flutter + build_runner: ^2.4.13 + riverpod_generator: ^2.6.3 + freezed: ^2.5.7 + json_serializable: ^6.9.0 + very_good_analysis: ^6.0.0 + mocktail: ^1.0.4 +``` + +### feature structure + +``` +frontend/lib/src/ +├── core/ +│ ├── theme/ # app_theme.dart, app_colors.dart +│ ├── errors/ # failures.dart (Freezed sealed class), result.dart (TaskResult) +│ ├── network/ # api_client.dart (dio), api_interceptors.dart +│ └── logging/ # talker_setup.dart +│ +├── features/ +│ ├── ideas/ +│ │ ├── domain/ # IdeaEntity (Freezed), IdeaRepository (abstract) +│ │ ├── data/ # IdeaDto (JsonSerializable), ApiIdeaRepository +│ │ └── presentation/ +│ │ ├── controllers/ # IdeasController, IdeaDetailController +│ │ ├── screens/ # IdeasScreen, IdeaDetailScreen +│ │ └── widgets/ # IdeaCard, IdeaFilterBar +│ │ +│ ├── evaluation/ +│ │ ├── domain/ # EvaluationEntity, MetricScoreEntity +│ │ ├── data/ # EvaluationDto, ApiEvaluationRepository +│ │ └── presentation/ +│ │ ├── controllers/ # AiEvaluationController +│ │ ├── screens/ # EvaluationScreen +│ │ └── widgets/ # ScoreRadarChart, MetricScoreCard, MetricScoreList +│ │ +│ ├── comparison/ +│ │ ├── domain/ # ComparisonEntity +│ │ ├── data/ # ComparisonDto +│ │ └── presentation/ +│ │ ├── controllers/ # ComparisonController +│ │ ├── screens/ # ComparisonScreen +│ │ └── widgets/ # ComparisonRadarChart, ComparisonTable +│ │ +│ └── dashboard/ +│ └── presentation/ +│ ├── controllers/ # DashboardController +│ ├── screens/ # DashboardScreen +│ └── widgets/ # StatsSummaryCard, TopIdeasList, CategoryChart +│ +└── shared/ + └── widgets/ # AppScaffold, ErrorView, LoadingView, EmptyView, TagChip +``` + +### navigation (GoRouter) + +``` +/dashboard → DashboardScreen (home) +/ideas → IdeasScreen (list with filters) +/ideas/:id → IdeaDetailScreen (detail + radar chart) +/ideas/:id/evaluate → EvaluationScreen (AI eval trigger) +/compare?ids=1,2,3 → ComparisonScreen (side-by-side) +``` + +ShellRoute wraps all screens in AppScaffold with: +- Sidebar (desktop): Dashboard, Ideas, Compare +- Bottom nav (mobile): same 3 items + +### key screens + +**Dashboard (`/dashboard`):** +- Stats cards row: total ideas, evaluated count, average score +- Score distribution chart (high/medium/low) +- Top 10 ranked ideas list +- Category breakdown bar chart + +**Ideas list (`/ideas`):** +- Search bar + filter chips (category, status, tags) +- Sort dropdown (score, date, name) +- Scrollable card list with pagination +- Multi-select for comparison (floating compare bar appears) + +**Idea detail (`/ideas/:id`):** +- Title, category badge, status, tags +- Description, problem statement, proposed solution, target audience +- Radar chart (8 metrics, fl_chart RadarChart) +- Metric score cards with progress bars and reasoning text +- "AI Evaluate" button (shows rate limit remaining in demo mode) + +**Evaluation (`/ideas/:id/evaluate`):** +- Confirmation card explaining the process +- "Start AI Evaluation" button +- Loading state: shimmer placeholders for 8 metrics, "Evaluating with Claude..." text +- Success state: animated radar chart + scores + aggregate +- Error state: rate limit countdown or retry button + +**Comparison (`/compare?ids=1,2,3`):** +- Overlapping radar charts (one colour per idea, up to 5) +- Score comparison table (metrics as rows, ideas as columns) +- Legend with idea titles + +### theming + +Dark-first design matching portfolio site: + +```dart +// Dark theme colours +darkBackground: Color(0xFF0A0A0A) // near-black +darkSurface: Color(0xFF141414) // slightly lighter +darkSurfaceVariant: Color(0xFF1E1E1E) // cards +darkOnSurface: Color(0xFFE5E5E5) // text +darkPrimary: Color(0xFF6366F1) // indigo-500 +darkOutline: Color(0xFF333333) // borders + +// Light theme colours +lightBackground: Color(0xFFFAFAFA) +lightSurface: Color(0xFFFFFFFF) +lightPrimary: Color(0xFF4F46E5) // indigo-600 + +// Semantic +success: Color(0xFF22C55E) // green-500 (score >= 7) +warning: Color(0xFFF59E0B) // amber-500 (score 5-6.9) +error: Color(0xFFEF4444) // red-500 (score < 5) +``` + +Font: Inter (matching portfolio site). + +### AI evaluation UX flow + +1. User on idea detail → sees "AI Evaluate" button +2. Taps → navigates to `/ideas/:id/evaluate` +3. Confirmation card → "Start AI Evaluation" button +4. Loading: shimmer placeholders + "Evaluating with Claude..." (5-10s) +5. Success: animated radar chart draw + metric scores slide in + aggregate prominently displayed +6. Error (429): "Rate limit exceeded" with countdown timer +7. Error (500): "Evaluation failed" with retry button + +State machine: `null` → `AsyncLoading` → `AsyncData` or `AsyncError` + +### error handling + +```dart +// Failure types (Freezed sealed class) +ServerFailure(message, statusCode?) +NetworkFailure(message?) +RateLimitFailure(message, retryAfterSeconds) +NotFoundFailure(message) +DemoRestrictionFailure(message) +UnknownFailure(message?) +``` + +Dio interceptor maps HTTP status codes to Failure types: +- 404 → NotFoundFailure +- 403 → DemoRestrictionFailure +- 429 → RateLimitFailure (with retry_after_seconds) +- 5xx → ServerFailure + +Retry interceptor: 2 retries on 502/503/504 and connection timeouts only. + +## deployment + +### topology + +``` + Internet + │ + ┌────────▼────────┐ + │ Docker Container │ + │ (Fly.io/Railway) │ + │ │ + │ FastAPI serves: │ + │ - /api/* routes │ + │ - Flutter Web │ + │ static files │ + │ │ + │ ┌──────────────┐ │ + │ │ SQLite volume │ │ + │ └──────────────┘ │ + └────────────────────┘ +``` + +Single container. FastAPI serves both the API and the Flutter Web build as static files +via `StaticFiles` mount. No CORS issues, single deployment target. + +### build pipeline + +1. `cd frontend/ && flutter build web --release --dart-define=API_BASE_URL=/api` +2. Copy `frontend/build/web/` to `backend/frontend_build/` +3. `docker build -t ideaforge backend/` +4. Deploy container with persistent SQLite volume + +### environment variables + +| Variable | Default | Required | Description | +|----------|---------|----------|-------------| +| IDEAFORGE_DATABASE_URL | sqlite+aiosqlite:///data/ideas.db | No | DB path | +| IDEAFORGE_HOST | 127.0.0.1 | No | API host | +| IDEAFORGE_PORT | 8000 | No | API port | +| IDEAFORGE_ANTHROPIC_API_KEY | — | Yes (for AI eval) | Claude API key | +| IDEAFORGE_AI_MODEL | claude-sonnet-4-20250514 | No | Model ID | +| IDEAFORGE_DEMO_MODE | false | No | Enable demo mode | +| IDEAFORGE_DEMO_RATE_LIMIT_PER_HOUR | 5 | No | AI eval rate limit | +| IDEAFORGE_LOG_LEVEL | INFO | No | Log level | +| IDEAFORGE_LOG_FORMAT | json | No | json or console | + +## portfolio site integration + +**Decision: Link out to dedicated subdomain (ideaforge.kschappell.com), not embedded.** + +IdeaForge is a Flutter Web app — embedding in an iframe creates cross-origin issues, +doubles load time, and looks unprofessional. A dedicated subdomain gives the project its +own identity. + +Portfolio site `/projects/ideaforge` page will include: +- Architecture diagram +- Screenshot gallery (dashboard, radar chart, AI evaluation, comparison) +- "View Live Demo" primary CTA button +- Technology breakdown + +## build order + +### phase 1 — backend foundation (8-10 commits) + +1. Scaffold `backend/` with pyproject.toml, alembic.ini, config +2. Port models from idea_manager +3. Port schemas + new AI schemas +4. Create initial Alembic migration +5. Port services (async-only) +6. Port utilities (score calculation) +7. Create API routers +8. Wire up FastAPI app (main.py, dependencies, exception handlers, CORS) + +### phase 2 — AI integration (4-5 commits) + +1. `ai/client.py` (Anthropic SDK wrapper with retry) +2. `ai/prompts.py` (ported from idea_service) +3. `ai/evaluator.py` (orchestration pipeline) +4. `POST /api/ideas/{id}/ai-evaluate` endpoint +5. Tests for AI module (mocked Anthropic client) + +### phase 3 — demo mode (3-4 commits) + +1. Demo mode middleware (write protection) +2. Rate limit middleware +3. Demo seed data (18 curated ideas) +4. Startup seeding in lifespan hook + +### phase 4 — frontend foundation (6-8 commits) + +1. Scaffold Flutter project, pubspec.yaml, analysis_options +2. Core layer (theme, errors, network, logging) +3. Ideas feature domain + data layers +4. Ideas feature presentation (list screen, filter bar, cards) +5. Idea detail screen with basic layout +6. GoRouter setup + AppScaffold with navigation + +### phase 5 — evaluation & charts (5-6 commits) + +1. Evaluation feature domain + data layers +2. Score radar chart widget (fl_chart) +3. Metric score card / list widgets +4. AI evaluation controller + evaluation screen +5. Loading/error/success states + +### phase 6 — dashboard & comparison (4-5 commits) + +1. Dashboard feature (stats, top ideas, category chart) +2. Comparison feature domain + data +3. Comparison screen with overlapping radar charts +4. Comparison score table + +### phase 7 — deployment & polish (3-4 commits) + +1. Dockerfile and docker-compose.yml +2. Flutter web build integration with backend +3. Portfolio site project entry +4. Final polish (animations, responsive breakpoints) + +## critical source files for porting + +| Source (idea_manager) | Target (ideaforge) | Notes | +|---|---|---| +| `services/idea_service.py` | `services/idea_service.py` | Drop sync, drop CLI helpers | +| `services/idea_service.py:326-409` | `ai/prompts.py` | Extract prompt builder | +| `seed/default_metrics.py` | `seed/metrics.py` | 8 metrics verbatim | +| `models/*.py` | `models/*.py` | Rename base exception | +| `schemas/*.py` | `schemas/*.py` | Add AI schemas | +| `exceptions.py` | `exceptions.py` | Rename to IdeaForgeError | +| `utils.py` | `utils.py` | Score calculation | +| `config.py` | `config.py` | Change prefix | +| `database.py` | `database.py` | Async only | +| `dependencies.py` | `dependencies.py` | Add AI client dependency |