Files
ideaforge/docs/implementation-plan.md
Kai Chappell b1c827e4aa docs: fix issues found in planning document review
Riverpod updated to 3.2+ (Dart 3.7+), deployment platform set
to TBD, riverpod_generator updated to ^4.0.2.
2026-01-28 23:38:44 +00:00

534 lines
19 KiB
Markdown

# 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.2+ (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: ^3.2.0
riverpod_annotation: ^4.0.1
hooks_riverpod: ^3.2.0
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: ^4.0.2
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<scores>` or `AsyncError<Failure>`
### 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 │
│ (platform TBD) │
│ │
│ 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. Hosting platform to
be decided at deployment time (candidates: Fly.io, Railway, self-hosted).
### 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 |