# 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
```bash
# Install dependencies
flutter pub get

# Run the application
flutter run

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

### Testing & Quality
```bash
# Run tests
flutter test

# Run static analysis
flutter analyze

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

### API Contract Updates
```bash
# 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
```bash
# 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):**
```dart
final result = await client.executeQuery<UserDto>(
  endpoint: 'users/123',
  query: GetUserQuery(userId: '123'),
  fromJson: UserDto.fromJson,
);
```

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

**Paginated Queries (Lists):**
```dart
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:

```dart
// 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:
```dart
dynamic value = getValue();
void handleData(var data) { ... }
```

✅ REQUIRED:
```dart
UserData value = getValue();
void handleData(RequestData data) { ... }
```

### Serializable Interface

All queries, commands, and DTOs must implement:

```dart
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
```bash
cp ../backend/docs/openapi.json ./api-schema.json
./scripts/update_api_client.sh
```

### Step 3: Create endpoint extension
Create file in `lib/api/endpoints/`:

```dart
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`:
```dart
export 'endpoints/user_endpoint.dart';
```

---

## Configuration

### API Client Setup

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

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

**Production:**
```dart
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
```dart
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:**
```bash
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
