Files
ideaforge/CLAUDE.md
Kai Chappell 1fa9804be5 chore: initial project scaffold
Set up IdeaForge project structure with documentation, architecture spec,
and development guidelines for both backend (Python) and frontend (Flutter).
2026-01-28 22:13:52 +00:00

11 KiB

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

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

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.

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.

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

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

@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

// 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:

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+):

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

@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

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

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