# 5. Workflows

## Overview

All core business workflows in Sasha Studio, documenting triggers, actors, preconditions, steps, state transitions, side effects, and failure handling.

---

## 1. User Registration (First User)

**Trigger:** POST `/api/auth/register`
**Actor:** First visitor (no users exist)
**Precondition:** `users` table is empty

**Steps:**
1. Client submits username + password
2. Server checks user count = 0 (rejects if users exist)
3. Password hashed with bcrypt
4. User created with `is_admin = 1`
5. JWT token generated (no expiry)
6. Token returned to client

**State transitions:** No users → one admin user
**Side effects:** None
**Failure:** If users already exist, returns 403

---

## 2. User Login

**Trigger:** POST `/api/auth/login`
**Actor:** Any registered user
**Precondition:** Account exists and is active

**Steps:**
1. Client submits username + password
2. Server looks up user by username
3. bcrypt compare password against hash
4. If match: update `last_login`, generate JWT, return token
5. If no match: return 401 "Invalid credentials"

**State transitions:** `last_login` updated
**Side effects:** None
**Failure:** Invalid credentials → 401, account disabled → 403

---

## 3. Password Reset

**Trigger:** POST `/api/auth/forgot-password`
**Actor:** Any user (unauthenticated)
**Precondition:** User exists, Postmark configured

**Steps:**
1. User submits email/username
2. Server generates random token, stores SHA-256 hash in `password_resets`
3. Email sent via Postmark with reset link
4. User clicks link → GET `/api/auth/reset-password/verify` validates token
5. User submits new password → POST `/api/auth/reset-password`
6. Token marked as used (`used_at` set), password hash updated

**State transitions:** `password_resets.used_at` set, `users.password_hash` updated
**Side effects:** Email sent via Postmark
**Failure:** Invalid/expired token → 400, Postmark failure → 500 (logged)

---

## 4. Onboarding Flow

**Trigger:** First login after registration, or navigating to `/onboarding`
**Actor:** Admin user (typically)
**Precondition:** User authenticated, no company profile exists

**Steps:**
1. **Screen 1 -- Company Introduction**
   - User enters company name or website URL
   - Auto-detection of company from URL (optional)
   - POST `/api/profile/organization/initialize` creates `company_profiles` record
   
2. **Screen 2 -- Claude API Key**
   - User provides Anthropic API key
   - POST `/api/setup/configure-claude` stores key
   - POST `/api/setup/test-claude-key` validates
   - Can skip (limited functionality)

3. **Screen 3 -- Live Learning**
   - POST `/api/profile/organization/research/:projectId` starts Claude research
   - Claude researches the company using the name/URL
   - Real-time streaming updates via GET `/api/profile/organization/research/:projectId/stream`
   - Results stored in `research_documents` table
   - POST `/api/profile/onboarding/complete` marks complete

**State transitions:**
- `company_profiles.research_status`: pending → researching → completed
- `company_profiles.onboarding_completed`: 0 → 1

**Side effects:** Claude API calls for research, files created in project directory
**Failure:** API key invalid → step 2 error, research fails → can retry or skip

---

## 5. Chat Session

**Trigger:** User sends message in chat interface
**Actor:** Authenticated user
**Precondition:** Claude CLI configured, project selected

**Steps:**
1. User types message in ChatInterface
2. Message sent via WebSocket to server
3. Server spawns/reuses Claude CLI process via `node-pty`
4. System prompt injected via `--append-system-prompt`:
   - Active persona context
   - Applied guides
   - Permission mode instructions
   - AskUserQuestion instructions
5. User message piped to Claude CLI stdin
6. Claude CLI processes request (may use tools: file read/write, bash, etc.)
7. Response streamed token-by-token via WebSocket back to client
8. Session recorded in `sessions` table
9. Exchange recorded in `raw_exchanges` table
10. Usage metrics recorded in `usage_events` table
11. Response quality signals computed and stored asynchronously

**State transitions:** New `sessions` record if first message, new `raw_exchanges` per turn
**Side effects:** Claude API costs incurred, files may be modified by tool use
**Failure:** Claude CLI crash → error message to user, WebSocket disconnect → reconnect attempt

---

## 6. File Upload

**Trigger:** POST `/api/files/:projectName/upload-documents`
**Actor:** Authenticated user
**Precondition:** Project exists

**Steps:**
1. User selects files via file picker or drag-drop
2. Files uploaded via multipart/form-data (multer)
3. Server validates file size against configurable limit (default 50MB)
4. Files written to project directory using `safePath()`
5. Document conversion if applicable (Word → HTML → Markdown via mammoth/turndown)
6. File tree cache invalidated
7. Success response with file metadata

**State transitions:** New files on filesystem
**Side effects:** Disk space consumed
**Failure:** File too large → 413, invalid path → 400, disk full → 500

---

## 7. Skill Execution

**Trigger:** POST `/api/skills/:name/run` or scheduled execution
**Actor:** Authenticated user or scheduler
**Precondition:** Skill exists as SKILL.md in `.claude/skills/`

**Steps:**
1. Skill markdown loaded from filesystem
2. If workflow (has `kind: agent-flow/workflow`), multi-step execution plan created
3. On-demand schedule created if needed
4. Claude CLI invoked with skill content as prompt
5. Execution logged in `executions` table
6. Result captured (output file, status, tokens used)
7. Schedule updated with last_run_at and last_status

**State transitions:** `executions` record created, schedule metadata updated
**Side effects:** Claude API costs, possible file modifications
**Failure:** Skill not found → 404, execution timeout → status 'timeout', Claude error → status 'failed'

---

## 8. Scheduled Prompt Execution

**Trigger:** Cron expression match (evaluated by `node-cron`)
**Actor:** Scheduler service (system)
**Precondition:** Schedule enabled, cron expression set

**Steps:**
1. Scheduler evaluates all enabled schedules every minute
2. When cron matches: generate deterministic execution_id from `sha256(schedule_id + slot)`
3. Check idempotency: `UNIQUE(schedule_id, scheduled_for)` prevents duplicates
4. Create `execution_history` record with status 'running'
5. POST to `/api/cron/execute-prompt` (localhost only)
6. Claude CLI executes the prompt
7. Result captured: status, tokens, output file, duration
8. `execution_history` updated: status → completed/failed/timeout
9. Schedule's `last_run_at` and `last_status` updated

**State transitions:** execution_history: running → completed/failed/timeout
**Side effects:** Claude API costs, output files created, JSONL log appended
**Failure:** Timeout (default 5 min) → status 'timeout', stale detection for orphaned 'running' records

---

## 9. Cloud Drive Connection

**Trigger:** Admin configures cloud drive in settings
**Actor:** Admin user
**Precondition:** OAuth provider configured

**Steps:**
1. Admin creates cloud provider (type: onedrive/sharepoint/gdrive)
2. Admin creates cloud connection with provider
3. OAuth flow initiated: GET `/api/auth/:provider/start`
4. User authorizes in OAuth popup
5. Callback received: GET `/api/auth/:provider/callback`
6. Tokens encrypted (AES-256-GCM) and stored in `cloud_credentials`
7. Connection status: pending → authorized
8. Admin creates mount point (path, access mode, VFS profile)
9. rclone remote configured and mounted
10. Mount health_status: unmounted → mounting → mounted

**State transitions:**
- `cloud_connections.status`: pending → authorized
- `cloud_mounts.health_status`: unmounted → mounting → mounted

**Side effects:** rclone process started, VFS cache created
**Failure:** OAuth denied → status remains 'pending', mount fails → health_status 'degraded'

---

## 10. Meeting Transcription

**Trigger:** POST `/api/meetings/start`
**Actor:** Authenticated user
**Precondition:** Meeting service configured

**Steps:**
1. User starts meeting capture
2. Audio segments captured and transcribed
3. Transcript segments accumulated
4. User can get real-time advice via POST `/api/meetings/advise-now`
5. User stops meeting: POST `/api/meetings/stop`
6. Full transcript available: GET `/api/meetings/transcript`
7. Analysis triggered: POST `/api/meetings/analyze`
8. Claude analyzes transcript content
9. Analysis results returned

**State transitions:** Meeting status: idle → active → analyzing → complete
**Side effects:** Claude API costs for analysis
**Failure:** Transcription service unavailable → error status

---

## 11. User Management (Admin)

**Trigger:** Admin actions in user management panel
**Actor:** Admin user
**Precondition:** Admin privileges

**Steps (Create User):**
1. Admin fills create user form (username, password, admin flag)
2. POST `/api/admin/users`
3. Password hashed with bcrypt
4. User record created
5. Optional: POST `/api/admin/users/:id/welcome-email` sends welcome via Postmark

**Steps (Delete User):**
1. Admin clicks delete on user row
2. Confirmation dialog shown
3. DELETE `/api/admin/users/:id`
4. User record deleted (CASCADE deletes related records)

**State transitions:** User created/updated/deleted
**Side effects:** Welcome email may be sent
**Failure:** Duplicate username → 409, email send failure → logged but not blocking

---

## 12. AI Provider Switch

**Trigger:** Admin configures AI provider in admin panel
**Actor:** Admin user

**Steps (Bedrock Setup):**
1. Admin opens AI Provider configuration
2. Enters AWS credentials (access key, secret key, region)
3. POST `/api/admin/bedrock/configure` → credentials encrypted and stored
4. POST `/api/admin/bedrock/test` → validates connection
5. POST `/api/admin/bedrock/switch-model` → selects active model
6. Optional: enable 1M context mode with acknowledgment audit

**State transitions:** `bedrock_config` single row created/updated
**Side effects:** `bedrock_context_audit` record if 1M context toggled
**Failure:** Invalid credentials → test fails, model unavailable → validation error

---

## 13. Git Operations

**Trigger:** User actions in Git panel
**Actor:** Authenticated user

**Steps (Commit & Push):**
1. User views changed files via GET `/api/git/status`
2. User views diffs via GET `/api/git/diff`
3. Optional: POST `/api/git/generate-commit-message` for AI-generated message
4. User writes commit message and commits: POST `/api/git/commit`
5. User pushes: POST `/api/git/push`

**State transitions:** Git repository state changes
**Side effects:** Remote repository updated on push
**Failure:** Merge conflicts → shown in UI, push rejected → error message

---

## 14. CLAUDE.md Version Management

**Trigger:** User edits CLAUDE.md via rules panel
**Actor:** Authenticated user

**Steps:**
1. GET `/api/rules/claude-md` loads current content
2. User edits content in editor
3. PUT `/api/rules/claude-md` saves new version
4. Server computes SHA-256 hash of content
5. New `prompt_versions` record created (scope: 'user' or 'project')
6. Previous version's `is_active` set to 0
7. `claudeMdWatcher` picks up file change for live sessions

**State transitions:** New prompt_versions record, old version deactivated
**Side effects:** Active Claude CLI sessions pick up new CLAUDE.md
**Failure:** Write permission error → 500

---

## 15. API Key Management

**Trigger:** User manages API keys in settings
**Actor:** Authenticated user

**Steps (Create):**
1. User provides key name and optional callback URL
2. POST `/api/api-keys/`
3. Server generates random API key with `sk_` prefix
4. Key hashed with bcrypt and stored in `api_keys`
5. HMAC signing secret generated for callback verification
6. Full key returned to user (shown once, never again)

**Steps (Use):**
1. External system sends request with `X-API-Key: sk_...` header
2. `authenticateApiKey` middleware looks up key by trying bcrypt compare
3. If valid: request proceeds, `last_used_at` updated
4. If invalid: 401 returned

**State transitions:** api_keys record created, last_used_at updated on use
**Side effects:** None
**Failure:** Key not found → 401, key deactivated → 401
