# 8. State & Edge Cases

## Status Lifecycles

### Session Status

```
idle → active (message sent) → streaming (response in progress) → idle
                                                                 ↘ error → idle
```

No explicit session close. Sessions become inactive when CLI process exits or times out.

### Onboarding Research Status

```
pending → researching → completed
                      ↘ failed (can retry)
```

Stored in `company_profiles.research_status`.

### Execution Status

```
running → completed
        → failed
        → timeout (exceeded timeout_ms)
        → stale (detected as orphaned)
        → cancelled (manual cancellation)
```

Applies to: `execution_history.status`, `executions.status`.

**Stale detection:** Scheduler checks for executions stuck in 'running' beyond their timeout and marks them 'stale'.

### Cloud Mount Health

```
unmounted → mounting → mounted
                     ↘ degraded → remounting → mounted
                                             ↘ degraded (persistent failure)
```

Stored in `cloud_mounts.health_status`.

### Cloud Connection Status

```
pending → authorized (OAuth successful)
        → revoked (user or admin revoked)
```

Stored in `cloud_connections.status`.

### Schedule State

```
enabled (cron active) ↔ disabled (cron paused)
```

`schedules.enabled` is an integer (0/1). Toggle via `/api/scheduler/enable/:name` and `/disable/:name`.

## Edge Cases

### Authentication

| Case | Behavior |
|------|----------|
| JWT_SECRET not set | Falls back to hardcoded dev default (insecure) |
| JWT with deleted user | Token validates but user lookup fails → 401 |
| Concurrent logins | Both sessions valid (no single-session enforcement) |
| Browser data cleared | Session lost, user must re-login. Filesystem sessions survive. |
| Container restart | JWT_SECRET may change (if env-based), invalidating all tokens |

### Chat Sessions

| Case | Behavior |
|------|----------|
| Claude CLI crashes mid-response | WebSocket error event, partial response shown |
| WebSocket disconnect | Client auto-reconnects, new CLI process spawned |
| Very large response | Streamed token-by-token, no size limit enforced |
| Concurrent messages from same user | Each spawns separate CLI process |
| API key invalid or expired | Error surfaced in chat: "Claude is not configured" |
| File system full | CLI write operations fail, error in response |

### File Operations

| Case | Behavior |
|------|----------|
| File exceeds size limit | 413 response, file rejected |
| Path traversal attempt | `safePath()` blocks with error |
| Duplicate filename upload | Overwrites existing file (no versioning) |
| Delete system file (CLAUDE.md) | UI prevents deletion via `isCloudItem()` / `canDeleteFolder()` checks |
| Concurrent file writes | SQLite busy_timeout (10s) handles contention, filesystem has no lock |

### Scheduling

| Case | Behavior |
|------|----------|
| Duplicate execution (same slot) | `UNIQUE(schedule_id, scheduled_for)` constraint prevents |
| Schedule fires while previous still running | New execution starts (no queue, no backpressure) |
| Server restart during execution | Execution stays 'running', eventually marked 'stale' |
| Invalid cron expression | Validation at creation time, rejected with 400 |
| Timezone edge cases | Handled by `cron-parser` with timezone support |
| NULL cron_expression | On-demand task only, never auto-fires |

### Cloud Drives

| Case | Behavior |
|------|----------|
| OAuth token expired | Automatic refresh via stored refresh_token |
| Refresh token revoked | Connection marked 'revoked', manual re-auth required |
| rclone process dies | Mount health → 'degraded', auto-remount attempted |
| Network timeout | `RCLONE_RC_TIMEOUT_MS` (default 60s), error returned |
| Large file download | Streamed, no memory buffering |
| Concurrent mount operations | rclone handles internally |

### Database

| Case | Behavior |
|------|----------|
| Database corruption | `PRAGMA integrity_check` on startup, warn in logs |
| Concurrent writes | `busy_timeout = 10000` waits up to 10s for lock |
| Database file missing | Auto-created with full schema from init.sql |
| Migration failure | Error logged, server may start in degraded state |
| `RESET_DATABASE=true` | Full schema rebuild (destroys all data) |

### Admin Operations

| Case | Behavior |
|------|----------|
| Delete only admin user | TBD -- no protection against this currently |
| Delete user with active sessions | CASCADE deletes session records, CLI process orphaned |
| Change own admin status | TBD -- UI may not prevent self-demotion |

## Empty States

| Screen | Empty State |
|--------|------------|
| Chat | Welcome message with suggested prompts |
| File Tree | "No files" with upload button CTA |
| Skills | Empty list with "Create skill" button |
| Schedules | Empty list with "Create schedule" button |
| Meetings | No active meeting indicator |
| Git | "No repository" or "No changes" |
| Analytics | "No data" with refresh button |
| Users (admin) | First user always exists |

## Loading States

| Screen | Loading Pattern |
|--------|----------------|
| Chat response | Token-by-token streaming, animated dots |
| File tree | Skeleton loader |
| Skill list | Spinner |
| Schedule list | Spinner |
| Analytics reports | Section-by-section loading |
| Cloud file browsing | Spinner with directory path |
| Onboarding research | Step-by-step progress messages |

## Error States

| Error | User-Facing Message | Recovery |
|-------|-------------------|----------|
| Claude not configured | "Claude is not configured" banner | Setup form |
| API key invalid | "Invalid API key" in chat | Reconfigure in settings |
| Database error | 500 error | Restart container |
| File upload too large | "File exceeds maximum size" | Reduce file size |
| Network error | "Connection lost" | Auto-reconnect attempt |
| Permission denied (non-admin) | 403 response | Contact admin |
| WebSocket failure | "Reconnecting..." banner | Auto-retry |

## Race Conditions

| Scenario | Mitigation |
|----------|-----------|
| Two users editing CLAUDE.md | Last write wins (no locking) |
| Schedule fires during server shutdown | Execution stays 'running', marked 'stale' on restart |
| Multiple file uploads to same path | Last write wins (filesystem) |
| Concurrent API key validation | bcrypt is thread-safe, no issue |
| Stats refresh triggered while running | `stats_refresh_log` status check prevents duplicate |
| OAuth callback arrives after timeout | Connection stays 'pending' until successful callback |

## Timezone Handling

- **Schedules:** Stored timezone per schedule, evaluated by `cron-parser`
- **Timestamps:** All database timestamps are UTC (ISO 8601 strings or `CURRENT_TIMESTAMP`)
- **Display:** Client-side conversion to local timezone
- **Logs:** UTC timestamps in JSONL files
