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:
- Client submits username + password
- Server checks user count = 0 (rejects if users exist)
- Password hashed with bcrypt
- User created with
is_admin = 1 - JWT token generated (no expiry)
- 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:
- Client submits username + password
- Server looks up user by username
- bcrypt compare password against hash
- If match: update
last_login, generate JWT, return token - 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:
- User submits email/username
- Server generates random token, stores SHA-256 hash in
password_resets - Email sent via Postmark with reset link
- User clicks link → GET
/api/auth/reset-password/verifyvalidates token - User submits new password → POST
/api/auth/reset-password - Token marked as used (
used_atset), 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:
Screen 1 -- Company Introduction
- User enters company name or website URL
- Auto-detection of company from URL (optional)
- POST
/api/profile/organization/initializecreatescompany_profilesrecord
Screen 2 -- Claude API Key
- User provides Anthropic API key
- POST
/api/setup/configure-claudestores key - POST
/api/setup/test-claude-keyvalidates - Can skip (limited functionality)
Screen 3 -- Live Learning
- POST
/api/profile/organization/research/:projectIdstarts 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_documentstable - POST
/api/profile/onboarding/completemarks complete
- POST
State transitions:
company_profiles.research_status: pending → researching → completedcompany_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:
- User types message in ChatInterface
- Message sent via WebSocket to server
- Server spawns/reuses Claude CLI process via
node-pty - System prompt injected via
--append-system-prompt:- Active persona context
- Applied guides
- Permission mode instructions
- AskUserQuestion instructions
- User message piped to Claude CLI stdin
- Claude CLI processes request (may use tools: file read/write, bash, etc.)
- Response streamed token-by-token via WebSocket back to client
- Session recorded in
sessionstable - Exchange recorded in
raw_exchangestable - Usage metrics recorded in
usage_eventstable - 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:
- User selects files via file picker or drag-drop
- Files uploaded via multipart/form-data (multer)
- Server validates file size against configurable limit (default 50MB)
- Files written to project directory using
safePath() - Document conversion if applicable (Word → HTML → Markdown via mammoth/turndown)
- File tree cache invalidated
- 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:
- Skill markdown loaded from filesystem
- If workflow (has
kind: agent-flow/workflow), multi-step execution plan created - On-demand schedule created if needed
- Claude CLI invoked with skill content as prompt
- Execution logged in
executionstable - Result captured (output file, status, tokens used)
- 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:
- Scheduler evaluates all enabled schedules every minute
- When cron matches: generate deterministic execution_id from
sha256(schedule_id + slot) - Check idempotency:
UNIQUE(schedule_id, scheduled_for)prevents duplicates - Create
execution_historyrecord with status 'running' - POST to
/api/cron/execute-prompt(localhost only) - Claude CLI executes the prompt
- Result captured: status, tokens, output file, duration
execution_historyupdated: status → completed/failed/timeout- Schedule's
last_run_atandlast_statusupdated
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:
- Admin creates cloud provider (type: onedrive/sharepoint/gdrive)
- Admin creates cloud connection with provider
- OAuth flow initiated: GET
/api/auth/:provider/start - User authorizes in OAuth popup
- Callback received: GET
/api/auth/:provider/callback - Tokens encrypted (AES-256-GCM) and stored in
cloud_credentials - Connection status: pending → authorized
- Admin creates mount point (path, access mode, VFS profile)
- rclone remote configured and mounted
- Mount health_status: unmounted → mounting → mounted
State transitions:
cloud_connections.status: pending → authorizedcloud_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:
- User starts meeting capture
- Audio segments captured and transcribed
- Transcript segments accumulated
- User can get real-time advice via POST
/api/meetings/advise-now - User stops meeting: POST
/api/meetings/stop - Full transcript available: GET
/api/meetings/transcript - Analysis triggered: POST
/api/meetings/analyze - Claude analyzes transcript content
- 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):
- Admin fills create user form (username, password, admin flag)
- POST
/api/admin/users - Password hashed with bcrypt
- User record created
- Optional: POST
/api/admin/users/:id/welcome-emailsends welcome via Postmark
Steps (Delete User):
- Admin clicks delete on user row
- Confirmation dialog shown
- DELETE
/api/admin/users/:id - 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):
- Admin opens AI Provider configuration
- Enters AWS credentials (access key, secret key, region)
- POST
/api/admin/bedrock/configure→ credentials encrypted and stored - POST
/api/admin/bedrock/test→ validates connection - POST
/api/admin/bedrock/switch-model→ selects active model - 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):
- User views changed files via GET
/api/git/status - User views diffs via GET
/api/git/diff - Optional: POST
/api/git/generate-commit-messagefor AI-generated message - User writes commit message and commits: POST
/api/git/commit - 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:
- GET
/api/rules/claude-mdloads current content - User edits content in editor
- PUT
/api/rules/claude-mdsaves new version - Server computes SHA-256 hash of content
- New
prompt_versionsrecord created (scope: 'user' or 'project') - Previous version's
is_activeset to 0 claudeMdWatcherpicks 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):
- User provides key name and optional callback URL
- POST
/api/api-keys/ - Server generates random API key with
sk_prefix - Key hashed with bcrypt and stored in
api_keys - HMAC signing secret generated for callback verification
- Full key returned to user (shown once, never again)
Steps (Use):
- External system sends request with
X-API-Key: sk_...header authenticateApiKeymiddleware looks up key by trying bcrypt compare- If valid: request proceeds,
last_used_atupdated - 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
