CODEX_ADK/FRONTEND/claude.md
jean-philippe 3fae2fcbe1 Initial commit: CODEX_ADK (Svrnty Console) MVP v1.0.0
This is the initial commit for the CODEX_ADK project, a full-stack AI agent
management platform featuring:

BACKEND (ASP.NET Core 8.0):
- CQRS architecture with 6 commands and 7 queries
- 16 API endpoints (all working and tested)
- PostgreSQL database with 5 entities
- AES-256 encryption for API keys
- FluentValidation on all commands
- Rate limiting and CORS configured
- OpenAPI/Swagger documentation
- Docker Compose setup (PostgreSQL + Ollama)

FRONTEND (Flutter 3.x):
- Dark theme with Svrnty branding
- Collapsible sidebar navigation
- CQRS API client with Result<T> error handling
- Type-safe endpoints from OpenAPI schema
- Multi-platform support (Web, iOS, Android, macOS, Linux, Windows)

DOCUMENTATION:
- Comprehensive API reference
- Architecture documentation
- Development guidelines for Claude Code
- API integration guides
- context-claude.md project overview

Status: Backend ready (Grade A-), Frontend integration pending

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 18:32:38 -04:00

10 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Svrnty Console is a Flutter-based management console for the Svrnty AI platform. It communicates with a C# CQRS backend using a type-safe, OpenAPI-driven API contract system.

Tech Stack:

  • Flutter 3.x / Dart 3.9.2+
  • CQRS API pattern (Command Query Responsibility Segregation)
  • OpenAPI 3.0.1 contract-first architecture
  • Custom theme: Crimson Red (#C44D58), Slate Blue (#475C6C)
  • Fonts: Montserrat (UI), IBM Plex Mono (code/technical)

Essential Commands

Development

# Install dependencies
flutter pub get

# Run the application
flutter run

# Run on specific platform
flutter run -d macos
flutter run -d chrome

Testing & Quality

# Run tests
flutter test

# Run static analysis
flutter analyze

# Verify API type safety (custom script)
./scripts/verify_api_types.sh

API Contract Updates

# After backend updates openapi.json:
cp ../backend/docs/openapi.json ./api-schema.json
./scripts/update_api_client.sh

# Or run code generation directly:
flutter pub run build_runner build --delete-conflicting-outputs

# Clean build (if generation fails):
flutter clean
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs

Build

# Build for production
flutter build macos
flutter build web
flutter build ios

Architecture Principles

1. OpenAPI-Driven API Contract

The backend and frontend share a single source of truth: api-schema.json (OpenAPI specification).

Flow:

  1. Backend exports docs/openapi.json from C# controllers + XML docs
  2. Frontend copies to api-schema.json
  3. Code generation creates Dart types from contract
  4. Frontend creates endpoint extensions using generated types

Key Files:

  • api-schema.json - OpenAPI contract (copy from backend)
  • lib/api/client.dart - CQRS client implementation
  • lib/api/types.dart - Core types (Result, Serializable, errors)
  • lib/api/endpoints/ - Type-safe endpoint extensions
  • lib/api/generated/ - Auto-generated code (git-ignored)

2. CQRS Pattern

All backend endpoints follow CQRS with three operation types:

Queries (Read):

final result = await client.executeQuery<UserDto>(
  endpoint: 'users/123',
  query: GetUserQuery(userId: '123'),
  fromJson: UserDto.fromJson,
);

Commands (Write):

final result = await client.executeCommand(
  endpoint: 'createUser',
  command: CreateUserCommand(name: 'John', email: 'john@example.com'),
);

Paginated Queries (Lists):

final result = await client.executePaginatedQuery<UserDto>(
  endpoint: 'users',
  query: ListUsersQuery(),
  itemFromJson: UserDto.fromJson,
  page: 1,
  pageSize: 20,
  filters: [FilterCriteria(field: 'status', operator: FilterOperator.equals, value: 'active')],
  sorting: [SortCriteria(field: 'createdAt', direction: SortDirection.descending)],
);

Important: ALL CQRS endpoints use JSON body via POST, even empty queries send {}.

3. Functional Error Handling

Never use try-catch for API calls. Use the Result<T> type:

// Pattern matching
final result = await client.getUser('123');

result.when(
  success: (user) => print('Hello ${user.name}'),
  error: (error) => print('Error: ${error.message}'),
);

// Or switch expression
final message = switch (result) {
  ApiSuccess(value: final user) => 'Hello ${user.name}',
  ApiError(error: final err) => 'Error: ${err.message}',
};

Mandatory Coding Standards

Strict Typing - NO EXCEPTIONS

See .claude-docs/strict-typing.md for complete requirements.

Core Rules:

  1. Every variable must have explicit type annotation
  2. Every function parameter must be typed
  3. Every function return value must be typed
  4. NEVER use dynamic (Dart's version of any)
  5. NEVER use untyped var declarations
  6. Use proper generics, interfaces, and type unions

Examples:

FORBIDDEN:

dynamic value = getValue();
void handleData(var data) { ... }

REQUIRED:

UserData value = getValue();
void handleData(RequestData data) { ... }

Serializable Interface

All queries, commands, and DTOs must implement:

abstract interface class Serializable {
  Map<String, Object?> toJson();
}

// Example:
class GetUserQuery implements Serializable {
  final String userId;
  const GetUserQuery({required this.userId});

  @override
  Map<String, Object?> toJson() => {'userId': userId};
}

Response Protocol

See .claude-docs/response-protocol.md for complete protocol.

All responses must end with structured choices:

Binary Choice:

(always read claude.md to keep context between interactions)

(Y) Yes — [brief action summary]
(N) No — [brief alternative/reason]
(+) I don't understand — ask for clarification

Multiple Choice:

(always read claude.md to keep context between interactions)

(A) Option A — [≤8 words description]
(B) Option B — [≤8 words description]
(C) Option C — [≤8 words description]
(+) I don't understand — ask for clarification

When user selects (+), explain the concept, then re-present options.


Adding New API Endpoints

Step 1: Backend adds endpoint

Backend team documents in C# controller with XML comments and exports docs/openapi.json.

Step 2: Update contract

cp ../backend/docs/openapi.json ./api-schema.json
./scripts/update_api_client.sh

Step 3: Create endpoint extension

Create file in lib/api/endpoints/:

import '../client.dart';
import '../types.dart';

extension UserEndpoint on CqrsApiClient {
  Future<Result<UserDto>> getUser(String userId) async {
    return executeQuery<UserDto>(
      endpoint: 'users/$userId',
      query: GetUserQuery(userId: userId),
      fromJson: UserDto.fromJson,
    );
  }
}

// Define query (matches backend contract)
class GetUserQuery implements Serializable {
  final String userId;
  const GetUserQuery({required this.userId});

  @override
  Map<String, Object?> toJson() => {'userId': userId};
}

// Define DTO (from OpenAPI schema)
class UserDto {
  final String id;
  final String name;
  final String email;

  const UserDto({required this.id, required this.name, required this.email});

  factory UserDto.fromJson(Map<String, Object?> json) => UserDto(
    id: json['id'] as String,
    name: json['name'] as String,
    email: json['email'] as String,
  );
}

Step 4: Export from api.dart

Add to lib/api/api.dart:

export 'endpoints/user_endpoint.dart';

Configuration

API Client Setup

Development (localhost):

final client = CqrsApiClient(
  config: ApiClientConfig.development, // http://localhost:5246
);

Android Emulator:

final client = CqrsApiClient(
  config: ApiClientConfig(
    baseUrl: 'http://10.0.2.2:5246', // Special emulator IP
    timeout: Duration(seconds: 30),
  ),
);

Production:

final client = CqrsApiClient(
  config: ApiClientConfig(
    baseUrl: 'https://api.svrnty.com',
    timeout: Duration(seconds: 30),
    defaultHeaders: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': 'Bearer $accessToken',
    },
  ),
);

Always call client.dispose() when done.


Error Handling

Error Types

  • ApiErrorType.network - No internet/DNS failure
  • ApiErrorType.http - 4xx/5xx responses
  • ApiErrorType.serialization - JSON parsing failed
  • ApiErrorType.timeout - Request took too long
  • ApiErrorType.validation - Backend validation error (422)
  • ApiErrorType.unknown - Unexpected errors

Handling Patterns

result.when(
  success: (data) { /* handle success */ },
  error: (error) {
    switch (error.type) {
      case ApiErrorType.network:
        showSnackbar('No internet connection');
      case ApiErrorType.timeout:
        showSnackbar('Request timed out');
      case ApiErrorType.validation:
        showValidationErrors(error.details);
      case ApiErrorType.http:
        if (error.statusCode == 401) navigateToLogin();
        else showSnackbar('Server error: ${error.message}');
      default:
        showSnackbar('Unexpected error: ${error.message}');
    }
  },
);

Important Workflows

When Backend Changes API Contract

  1. Backend team notifies: "Updated API - added X endpoint"
  2. Check backend CHANGELOG: cat ../backend/docs/CHANGELOG.md
  3. Update contract: cp ../backend/docs/openapi.json ./api-schema.json
  4. Regenerate: ./scripts/update_api_client.sh
  5. Add endpoint extension if needed (see "Adding New API Endpoints")
  6. Run tests: flutter test
  7. Verify types: ./scripts/verify_api_types.sh
  8. Commit: git commit -m "feat: Add X endpoint integration"

Troubleshooting

Code generation fails:

flutter clean
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs

Type errors after regenerating: Backend made breaking changes. Check ../backend/docs/CHANGELOG.md and update code accordingly.

Network error on device:

  • iOS/Real device: Use actual IP (e.g., http://192.168.1.100:5246)
  • Android emulator: Use http://10.0.2.2:5246

JSON parsing error:

  1. Verify api-schema.json matches backend's docs/openapi.json
  2. Check DTO fromJson matches OpenAPI schema
  3. Verify backend returned correct content-type

Additional Documentation

  • API Integration Guide: README_API.md (comprehensive API documentation)
  • Strict Typing Rules: .claude-docs/strict-typing.md (mandatory)
  • Response Protocol: .claude-docs/response-protocol.md (mandatory)
  • Backend Docs: ../backend/docs/ (architecture, changelog)
  • OpenAPI Contract: api-schema.json (source of truth)

Key Reminders

  1. OpenAPI is Source of Truth - Always regenerate from api-schema.json
  2. CQRS Pattern - All endpoints use POST with JSON body (even empty {})
  3. Type Safety - No dynamic types, use Serializable interface
  4. Functional Errors - Use Result<T>, not try-catch
  5. Monitor Backend CHANGELOG - Breaking changes documented there
  6. Test Everything - Unit tests + integration tests
  7. Follow Response Protocol - All responses end with structured choices
  8. Strict Typing - Explicit types everywhere, no exceptions