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>
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:
- Backend exports
docs/openapi.jsonfrom C# controllers + XML docs - Frontend copies to
api-schema.json - Code generation creates Dart types from contract
- Frontend creates endpoint extensions using generated types
Key Files:
api-schema.json- OpenAPI contract (copy from backend)lib/api/client.dart- CQRS client implementationlib/api/types.dart- Core types (Result, Serializable, errors)lib/api/endpoints/- Type-safe endpoint extensionslib/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:
- Every variable must have explicit type annotation
- Every function parameter must be typed
- Every function return value must be typed
- NEVER use
dynamic(Dart's version ofany) - NEVER use untyped
vardeclarations - 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 failureApiErrorType.http- 4xx/5xx responsesApiErrorType.serialization- JSON parsing failedApiErrorType.timeout- Request took too longApiErrorType.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
- Backend team notifies: "Updated API - added X endpoint"
- Check backend CHANGELOG:
cat ../backend/docs/CHANGELOG.md - Update contract:
cp ../backend/docs/openapi.json ./api-schema.json - Regenerate:
./scripts/update_api_client.sh - Add endpoint extension if needed (see "Adding New API Endpoints")
- Run tests:
flutter test - Verify types:
./scripts/verify_api_types.sh - 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:
- Verify
api-schema.jsonmatches backend'sdocs/openapi.json - Check DTO
fromJsonmatches OpenAPI schema - 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
- OpenAPI is Source of Truth - Always regenerate from
api-schema.json - CQRS Pattern - All endpoints use POST with JSON body (even empty
{}) - Type Safety - No
dynamictypes, useSerializableinterface - Functional Errors - Use
Result<T>, not try-catch - Monitor Backend CHANGELOG - Breaking changes documented there
- Test Everything - Unit tests + integration tests
- Follow Response Protocol - All responses end with structured choices
- Strict Typing - Explicit types everywhere, no exceptions