chore: initial project scaffold
Set up IdeaForge project structure with documentation, architecture spec, and development guidelines for both backend (Python) and frontend (Flutter).
This commit is contained in:
361
CLAUDE.md
Normal file
361
CLAUDE.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# CLAUDE.md
|
||||
|
||||
Guidelines for working on IdeaForge.
|
||||
|
||||
## Project Overview
|
||||
|
||||
IdeaForge is a full-stack idea management platform: a Flutter Web/Mobile client backed by
|
||||
a Python API with integrated AI evaluation. The backend extends the existing idea-manager
|
||||
project with Claude API integration and a public demo mode.
|
||||
|
||||
## Project Stack
|
||||
|
||||
### Backend (Python)
|
||||
|
||||
- **Language:** Python 3.11+
|
||||
- **Package Manager:** uv
|
||||
- **API Framework:** FastAPI 0.115+ with uvicorn
|
||||
- **ORM:** SQLAlchemy 2.0+ (async) with aiosqlite
|
||||
- **Database:** SQLite (file-based)
|
||||
- **Validation:** Pydantic 2.10+, pydantic-settings
|
||||
- **Logging:** structlog (JSON structured logs)
|
||||
- **Linting:** ruff (lint + format)
|
||||
- **Type Checking:** mypy (strict)
|
||||
- **Testing:** pytest, pytest-asyncio, pytest-cov, httpx
|
||||
- **AI Integration:** Anthropic Python SDK (Claude API)
|
||||
|
||||
### Frontend (Flutter)
|
||||
|
||||
- **Language:** Dart 3.6+ / Flutter 3.27+
|
||||
- **Architecture:** Feature-First with Riverpod
|
||||
- **State Management:** Riverpod 3.1+ (generator syntax)
|
||||
- **Navigation:** GoRouter
|
||||
- **HTTP Client:** dio
|
||||
- **Charts:** fl_chart (radar/spider charts for metric scores)
|
||||
- **Testing:** flutter_test, mocktail
|
||||
- **Linting:** flutter analyze (zero issues including info-level)
|
||||
|
||||
## Commands
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
cd backend/
|
||||
uv sync # Install dependencies
|
||||
uv run alembic upgrade head # Run migrations
|
||||
uv run ideaforge serve # Start API server
|
||||
uv run pytest --cov=src/ideaforge # Tests (target: ≥80%)
|
||||
uv run ruff check . # Linting (must return ZERO issues)
|
||||
uv run ruff format --check . # Format check (ZERO changes)
|
||||
uv run mypy src/ # Type checking (must pass)
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
cd frontend/
|
||||
flutter pub get # Install dependencies
|
||||
dart run build_runner build --delete-conflicting-outputs # Code generation
|
||||
flutter test --coverage # Tests (target: ≥85%)
|
||||
flutter analyze # Must return ZERO issues
|
||||
dart format . # Must produce ZERO changes
|
||||
flutter build web # Web build
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
ideaforge/
|
||||
├── backend/ # Python API
|
||||
│ ├── src/ideaforge/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── main.py # FastAPI app entry point
|
||||
│ │ ├── config.py # Pydantic settings
|
||||
│ │ ├── database.py # DB engine, session factory
|
||||
│ │ ├── dependencies.py # FastAPI dependency injection
|
||||
│ │ ├── exceptions.py # Custom exception hierarchy
|
||||
│ │ ├── logging.py # structlog configuration
|
||||
│ │ ├── models/ # SQLAlchemy ORM models
|
||||
│ │ ├── schemas/ # Pydantic request/response schemas
|
||||
│ │ ├── services/ # Business logic layer
|
||||
│ │ ├── api/ # FastAPI routers
|
||||
│ │ └── ai/ # Claude API integration
|
||||
│ │ ├── client.py # Anthropic SDK wrapper
|
||||
│ │ ├── prompts.py # Evaluation prompt templates
|
||||
│ │ └── evaluator.py # AI evaluation orchestration
|
||||
│ ├── alembic/ # Database migrations
|
||||
│ ├── tests/
|
||||
│ ├── pyproject.toml
|
||||
│ └── alembic.ini
|
||||
│
|
||||
├── frontend/ # Flutter app
|
||||
│ ├── lib/
|
||||
│ │ ├── main.dart
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── core/ # Shared utilities, theme, errors
|
||||
│ │ │ ├── features/
|
||||
│ │ │ │ ├── ideas/ # Idea listing and filtering
|
||||
│ │ │ │ │ ├── domain/
|
||||
│ │ │ │ │ ├── data/
|
||||
│ │ │ │ │ └── presentation/
|
||||
│ │ │ │ ├── evaluation/ # AI evaluation and scoring
|
||||
│ │ │ │ │ ├── domain/
|
||||
│ │ │ │ │ ├── data/
|
||||
│ │ │ │ │ └── presentation/
|
||||
│ │ │ │ └── dashboard/ # Overview and charts
|
||||
│ │ │ │ └── presentation/
|
||||
│ │ │ └── shared/ # Cross-feature widgets
|
||||
│ │ └── app.dart # App root with providers and router
|
||||
│ ├── test/
|
||||
│ ├── web/
|
||||
│ └── pubspec.yaml
|
||||
│
|
||||
├── docs/
|
||||
├── readme.md
|
||||
├── changelog.md
|
||||
└── docker-compose.yml # Local development (API + DB)
|
||||
```
|
||||
|
||||
### Layer Responsibilities (Backend)
|
||||
|
||||
| Layer | Purpose | Dependencies |
|
||||
|-------|---------|--------------|
|
||||
| **models/** | SQLAlchemy ORM models | SQLAlchemy only |
|
||||
| **schemas/** | Pydantic request/response validation | Pydantic only |
|
||||
| **services/** | Business logic, orchestration | models, schemas |
|
||||
| **ai/** | Claude API integration, prompt management | Anthropic SDK, schemas |
|
||||
| **api/** | HTTP routing, request handling | services, schemas |
|
||||
|
||||
**Critical:** Services NEVER import from api/. API depends on services, not the reverse.
|
||||
|
||||
### Layer Responsibilities (Frontend)
|
||||
|
||||
| Layer | Purpose | Dependencies |
|
||||
|-------|---------|--------------|
|
||||
| **domain/** | Entities, repository interfaces | None (pure Dart) |
|
||||
| **data/** | DTOs, API client, repository implementations | dio, domain |
|
||||
| **presentation/** | Controllers, views, widgets | Riverpod, domain |
|
||||
|
||||
**Critical:** Domain layer has zero external dependencies. Data implements domain interfaces.
|
||||
|
||||
## Code Patterns
|
||||
|
||||
### Backend — Error Handling
|
||||
|
||||
Custom exceptions inherit from a base class. Services raise domain exceptions, API routes
|
||||
convert them to HTTP responses via exception handlers.
|
||||
|
||||
```python
|
||||
class IdeaForgeError(Exception):
|
||||
def __init__(self, message: str, detail: str | None = None):
|
||||
self.message = message
|
||||
self.detail = detail
|
||||
super().__init__(message)
|
||||
|
||||
class NotFoundError(IdeaForgeError): ...
|
||||
class EvaluationError(IdeaForgeError): ...
|
||||
class AIServiceError(IdeaForgeError): ...
|
||||
```
|
||||
|
||||
### Backend — Logging (structlog)
|
||||
|
||||
Every service method logs entry, success, and failure with context.
|
||||
|
||||
```python
|
||||
import structlog
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
async def evaluate_idea(self, idea_id: int) -> Evaluation:
|
||||
logger.info("evaluating_idea", idea_id=idea_id)
|
||||
try:
|
||||
result = await self._run_evaluation(idea_id)
|
||||
logger.info("idea_evaluated", idea_id=idea_id, score=result.score)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("evaluation_failed", idea_id=idea_id, error=str(e))
|
||||
raise
|
||||
```
|
||||
|
||||
Never use `print()`. Always include context parameters.
|
||||
|
||||
### Backend — AI Integration
|
||||
|
||||
```python
|
||||
from anthropic import AsyncAnthropic
|
||||
|
||||
class EvaluationService:
|
||||
def __init__(self, session: AsyncSession, ai_client: AsyncAnthropic):
|
||||
self.session = session
|
||||
self.ai_client = ai_client
|
||||
|
||||
async def run_ai_evaluation(self, idea_id: int) -> list[MetricScore]:
|
||||
idea = await self._get_idea(idea_id)
|
||||
prompt = build_evaluation_prompt(idea)
|
||||
response = await self.ai_client.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=4096,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
)
|
||||
return parse_evaluation_response(response)
|
||||
```
|
||||
|
||||
### Frontend — Riverpod Controller
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class IdeasController extends _$IdeasController {
|
||||
@override
|
||||
Future<List<IdeaEntity>> build() async {
|
||||
return ref.read(ideaRepositoryProvider).getIdeas();
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(
|
||||
() => ref.read(ideaRepositoryProvider).getIdeas(),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Frontend — Repository Pattern
|
||||
|
||||
```dart
|
||||
// domain/repositories/idea_repository.dart (interface)
|
||||
abstract class IdeaRepository {
|
||||
Future<List<IdeaEntity>> getIdeas({String? status, String? category});
|
||||
Future<IdeaEntity> getIdea(int id);
|
||||
Future<EvaluationResult> triggerEvaluation(int id);
|
||||
}
|
||||
|
||||
// data/repositories/api_idea_repository.dart (implementation)
|
||||
class ApiIdeaRepository implements IdeaRepository {
|
||||
ApiIdeaRepository({required this.client});
|
||||
final Dio client;
|
||||
|
||||
@override
|
||||
Future<List<IdeaEntity>> getIdeas({String? status, String? category}) async {
|
||||
final response = await client.get('/api/ideas', queryParameters: {
|
||||
if (status != null) 'status': status,
|
||||
if (category != null) 'category': category,
|
||||
});
|
||||
return (response.data as List)
|
||||
.map((json) => IdeaDto.fromJson(json).toEntity())
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Import Ordering
|
||||
|
||||
### Python
|
||||
|
||||
Use ruff import sorting (isort-compatible):
|
||||
|
||||
1. Standard library
|
||||
2. Third-party packages
|
||||
3. Local imports
|
||||
|
||||
Always use absolute imports from package root.
|
||||
|
||||
### Dart
|
||||
|
||||
All imports in one section, sorted alphabetically, no blank lines between:
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ideaforge/src/core/errors/failures.dart';
|
||||
import 'package:ideaforge/src/features/ideas/ideas.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
```
|
||||
|
||||
Always use package imports, never relative.
|
||||
|
||||
## Type Safety
|
||||
|
||||
### Python
|
||||
|
||||
All functions must have complete type hints. Use modern syntax (Python 3.11+):
|
||||
|
||||
```python
|
||||
def process(items: list[str]) -> dict[str, int]: ...
|
||||
def get_user(user_id: int | None = None) -> User | None: ...
|
||||
```
|
||||
|
||||
### Dart
|
||||
|
||||
Avoid `dynamic`. Prefer explicit types on public APIs. Use `required` for named parameters
|
||||
that must be provided.
|
||||
|
||||
## Testing
|
||||
|
||||
### Backend
|
||||
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_evaluate_idea(client: AsyncClient):
|
||||
response = await client.post("/api/ideas/1/evaluate")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "scores" in data
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```dart
|
||||
testWidgets('shows idea list', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [ideaRepositoryProvider.overrideWithValue(mockRepo)],
|
||||
child: const MaterialApp(home: IdeasScreen()),
|
||||
),
|
||||
);
|
||||
expect(find.text('Test Idea'), findsOneWidget);
|
||||
});
|
||||
```
|
||||
|
||||
## Git
|
||||
|
||||
- **Repo:** https://gitea.kschappell.com/kschappell/ideaforge
|
||||
- **Branch:** main
|
||||
- See workspace CLAUDE.md for commit format and signing requirements
|
||||
|
||||
## Demo Mode
|
||||
|
||||
The deployed instance runs in demo mode:
|
||||
|
||||
- Seeded with 15-20 curated ideas across 3-4 domains
|
||||
- Write operations disabled for anonymous users
|
||||
- AI evaluation rate-limited (prevent abuse)
|
||||
- Data resets periodically
|
||||
|
||||
Configuration via environment variable: `IDEAFORGE_DEMO_MODE=true`
|
||||
|
||||
## Pre-Completion Checklist
|
||||
|
||||
Before marking ANY task complete:
|
||||
|
||||
**Backend:**
|
||||
- [ ] `uv run ruff check .` returns 0 issues
|
||||
- [ ] `uv run ruff format --check .` returns 0 changes
|
||||
- [ ] `uv run mypy src/` passes
|
||||
- [ ] `uv run pytest` passes
|
||||
- [ ] All new functions have complete type hints
|
||||
- [ ] All new service methods have structlog logging
|
||||
- [ ] All new exceptions inherit from `IdeaForgeError`
|
||||
- [ ] No `print()` statements
|
||||
|
||||
**Frontend:**
|
||||
- [ ] `flutter analyze` returns 0 issues
|
||||
- [ ] `dart format .` returns 0 changes
|
||||
- [ ] `flutter test` passes
|
||||
- [ ] New features have at least one widget test
|
||||
- [ ] No AI/LLM references anywhere in the codebase
|
||||
|
||||
**Both:**
|
||||
- [ ] changelog.md updated
|
||||
- [ ] Commit is signed
|
||||
- [ ] Commit message follows `type(scope): description` format
|
||||
Reference in New Issue
Block a user