Architecture specification, API design, Claude integration patterns, demo mode strategy, and phased build order for IdeaForge development.
19 KiB
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(notidea_manager) - Base exception:
IdeaForgeError(notIdeaManagerError) - Config env prefix:
IDEAFORGE_(notIDEA_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-20250514model - Retries up to 2 times on RateLimitError with exponential backoff
- Raises
AIServiceErroron 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
Ideaandlist[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:
- Load idea with relationships from DB
- Load active metrics
- Build evaluation prompt
- Call Claude API via client
- Parse JSON response (handles markdown code blocks)
- Validate metric names and clamp scores to valid ranges
- Delete existing AI evaluations for this idea (evaluator="claude")
- Store new evaluations
- Return results
demo mode
Configuration:
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: trueheader 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_secondson limit breach - Adds
X-RateLimit-Remainingheader to responses
Startup seeding:
- On app startup (lifespan hook), if
demo_mode=Trueand 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)
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:
// 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
- User on idea detail → sees "AI Evaluate" button
- Taps → navigates to
/ideas/:id/evaluate - Confirmation card → "Start AI Evaluation" button
- Loading: shimmer placeholders + "Evaluating with Claude..." (5-10s)
- Success: animated radar chart draw + metric scores slide in + aggregate prominently displayed
- Error (429): "Rate limit exceeded" with countdown timer
- Error (500): "Evaluation failed" with retry button
State machine: null → AsyncLoading → AsyncData<scores> or AsyncError<Failure>
error handling
// 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
cd frontend/ && flutter build web --release --dart-define=API_BASE_URL=/api- Copy
frontend/build/web/tobackend/frontend_build/ docker build -t ideaforge backend/- 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)
- Scaffold
backend/with pyproject.toml, alembic.ini, config - Port models from idea_manager
- Port schemas + new AI schemas
- Create initial Alembic migration
- Port services (async-only)
- Port utilities (score calculation)
- Create API routers
- Wire up FastAPI app (main.py, dependencies, exception handlers, CORS)
phase 2 — AI integration (4-5 commits)
ai/client.py(Anthropic SDK wrapper with retry)ai/prompts.py(ported from idea_service)ai/evaluator.py(orchestration pipeline)POST /api/ideas/{id}/ai-evaluateendpoint- Tests for AI module (mocked Anthropic client)
phase 3 — demo mode (3-4 commits)
- Demo mode middleware (write protection)
- Rate limit middleware
- Demo seed data (18 curated ideas)
- Startup seeding in lifespan hook
phase 4 — frontend foundation (6-8 commits)
- Scaffold Flutter project, pubspec.yaml, analysis_options
- Core layer (theme, errors, network, logging)
- Ideas feature domain + data layers
- Ideas feature presentation (list screen, filter bar, cards)
- Idea detail screen with basic layout
- GoRouter setup + AppScaffold with navigation
phase 5 — evaluation & charts (5-6 commits)
- Evaluation feature domain + data layers
- Score radar chart widget (fl_chart)
- Metric score card / list widgets
- AI evaluation controller + evaluation screen
- Loading/error/success states
phase 6 — dashboard & comparison (4-5 commits)
- Dashboard feature (stats, top ideas, category chart)
- Comparison feature domain + data
- Comparison screen with overlapping radar charts
- Comparison score table
phase 7 — deployment & polish (3-4 commits)
- Dockerfile and docker-compose.yml
- Flutter web build integration with backend
- Portfolio site project entry
- 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 |