agent_tracker/CLAUDE.md
nickviljoen 7445c30caf Document UI & brand conventions in CLAUDE.md
Adds a "UI & Brand (2026-05)" section covering the OLIVER design tokens
(yellow #FFCB05, near-black #1A1A1A, Montserrat), signature motifs
(.page-title highlight, .stat-tile, card-header accent), and the two
gotchas to avoid re-introducing (don't override .navbar position in
nav.html; tabs must use button data-bs-target, not a href).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 10:26:39 +02:00

429 lines
No EOL
30 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
AgentHub is a FastAPI-based AI Agent Management System with MongoDB backend. It provides three-tier role-based authentication (admin/readonly_admin/user), agent CRUD operations, client verification workflow, email notifications, user management, and a web interface built with Jinja2 templates and Bootstrap 5.
## Common Development Tasks
### Running the Application
```bash
uvicorn main:app --reload --port 8000
```
Access at: http://localhost:8000
### Installing Dependencies
```bash
pip install -r requirements.txt
```
### Environment Setup
Create `.env` file with:
- `MONGODB_URI`: MongoDB connection string (default: mongodb://localhost:27017)
- `MONGODB_DBNAME`: Database name (default: agenthub_db)
- `SECRET_KEY`: JWT secret key
- `ALGORITHM`: JWT algorithm (default: HS256)
- `ACCESS_TOKEN_EXPIRE_MINUTES`: Token expiration (default: 60)
#### Optional: Prompt Audit (Gemini)
- `GOOGLE_API_KEY`: Google API key for Gemini (if not set, audit is silently disabled)
- `AUDIT_GEMINI_MODEL`: Gemini model name (default: `gemini-2.5-pro`)
- `AUDIT_CONCURRENCY`: Batch size for sequential processing (default: 2)
#### Optional: Email Notifications (Mailgun)
- `MAILGUN_API_KEY`: Mailgun API key (if not set, notifications are silently disabled)
- `MAILGUN_DOMAIN`: Mailgun sending domain (e.g. your-domain.mailgun.org)
- `MAILGUN_FROM_EMAIL`: Sender address (default: `AgentHub <noreply@{MAILGUN_DOMAIN}>`)
- `NOTIFICATION_REPLY_TO`: Reply-To address attached to every Mailgun send (default: `Nick.Viljoen@oliver.agency`). Lets recipients reply to a real mailbox instead of the `noreply@` sender.
- `TOKEN_USAGE_THRESHOLD`: Weekly token count that triggers an alert (default: 100000)
- `NOTIFICATION_COOLDOWN_HOURS`: Hours between repeat alerts for the same agent (default: 24)
- `CLIENT_AGENT_NOTIFY_EMAILS`: Comma-separated list of emails for client agent notifications
- `WEEKLY_DIGEST_HOUR`: Hour (24h format) to send weekly digest on Mondays (default: 7)
#### Optional: Completion-Reminder Emails (LibreChat completion flow)
- `COMPLETION_REMINDER_COOLDOWN_DAYS`: Min days between reminders for the same user (default: 7)
- `COMPLETION_REMINDER_MAX_NUDGES`: After this many nudges, stop emailing the user (default: 4)
- `COMPLETION_REMINDER_HOUR`: Hour (24h) for the daily reminder cron (default: 8)
- `AGENTHUB_PUBLIC_URL`: Public absolute URL used in email links (e.g. `https://agenthub.example.com`). If unset, links are relative and won't work in email clients.
### Default Login Credentials
- Admin: `admin@agenthub.com` / `admin123`
- Test User: `test@example.com` / `testpass123`
## Code Architecture
### Core Application Structure
**main.py**: FastAPI application with:
- JWT cookie-based authentication system
- HTML routes for web interface
- REST API endpoints for agent/user management
- Three-tier role-based access control (admin / readonly_admin / user)
- Client verification workflow with email notifications
- Daily agent digest scheduler (APScheduler)
- Template rendering with Jinja2
**Key Authentication Functions**:
- `get_current_user_optional()`: Cookie-based auth for templates
- `get_current_user_from_cookie()`: Required auth for API endpoints
- `require_admin()`: Admin-only access control (write operations)
- `require_admin_or_readonly()`: Admin + readonly_admin access (read-only dashboard views)
### Data Layer
**models.py**: Pydantic models for:
- `AiAgent`: Core agent model with comprehensive fields (includes `discipline`, `rating`, `client`, `client_name`, `studio_name`)
- `UsageTimelineEntry`: Daily usage data including `message_count` and `token_count`
- `UserCreate/UserResponse`: User management models (includes `role` field: `user`/`admin`/`readonly_admin`)
- `UserUpdate`: Includes `role` field for three-tier role management
- `AiAgentCreate/AiAgentResponse`: API request/response models (includes `total_tokens`, `prompt_tokens`, `completion_tokens`, `discipline`, `rating`, `rating_count`, `client`, `client_name`, `studio_name`, `verification_status`, `verified_by`, `verified_date`)
- `AgentCollectorCreate`: Collector API input model (includes `total_tokens`, `prompt_tokens`, `completion_tokens`, `discipline`, `client`, `client_name`, `studio_name`)
- `AuditReviewRequest`: Audit review input (`audit_status`: flagged/reviewed/cleared, `reviewer_notes`)
- `AgentUsageStatsResponse`: Usage statistics response (includes `total_tokens`, `prompt_tokens`, `completion_tokens`)
**crud.py**: Database operations using Motor (async MongoDB driver):
- User CRUD: authentication, creation, management
- Agent CRUD: create, read, update, delete with user ownership
- Advanced features: search, filtering, statistics, pagination
- All operations use ObjectId for MongoDB document IDs
**database.py**: MongoDB connection setup with Motor async client
- Collections: `users`, `agents`, `agent_usage`, `token_notifications`, `agent_ratings`, `audit_history`, `completion_reminders`
- `ensure_indexes()`: Creates compound unique index on `agent_ratings(agent_id, user_id)`, indexes on `verification_status`, `audit_status`, and `audit_history(agent_id, audit_date)`
**audit_analyzer.py**: Gemini-powered agent classification and audit system:
- `is_gemini_configured()`: Returns False if `GOOGLE_API_KEY` not set (gracefully disabled)
- `analyze_single_agent()`: Sends agent instructions to Gemini 2.5 Pro, returns structured JSON with category, discipline, department, client detection, risk level, flags, and recommendations. Includes retry with exponential backoff for rate limits.
- `store_audit_result()`: Writes audit results to agent document (`audit_status`, `audit_category`, `audit_risk_level`, etc.) and inserts into `audit_history` collection
- `apply_classification_fields()`: Auto-assigns `discipline` (from defined list), `agent_department` (free text inferred from instructions), and client detection (`client = "yes"`, `verification_status = "needs_verification"`) — only overwrites fields that are currently empty/null
- `classify_single_agent()`: Convenience function for post-sync automatic classification. Loads agent, analyses, stores result, applies fields.
- `run_audit_batch()`: Batch processes agents sequentially with rate-limit-safe pauses. Supports `unclassified_only` and `single_agent_id` params. Returns summary with audited/failed/skipped counts.
- `get_all_audit_results()`: Returns all agents with audit fields for the Prompt Audit tab
- `update_audit_review()`: Admin marks audit as reviewed/cleared with notes
- Uses `google-genai` SDK (new API), model configurable via `AUDIT_GEMINI_MODEL` env var (default: `gemini-2.5-pro`)
**notifications.py**: Mailgun email notification system:
- `is_mailgun_configured()`: Returns False if env vars not set (gracefully disabled)
- `send_mailgun_email()`: POST to Mailgun HTTP API with 10s timeout
- `build_threshold_email()`: HTML email template for threshold alerts
- `check_and_notify_threshold()`: Checks 7-day token usage against threshold, enforces cooldown via `token_notifications` collection, sends to admin users
- `send_client_agent_notification()`: Sends email when client-facing agent is created (to `CLIENT_AGENT_NOTIFY_EMAILS`)
- `build_client_agent_email()`: HTML email template for client agent notifications
- `send_weekly_agent_digest()`: Queries agents created in last 7 days, sends summary to all admin users
- `build_weekly_digest_email()`: HTML email template for weekly digest
**auth.py**: JWT authentication with:
- bcrypt password hashing
- JWT token creation/validation using python-jose
- Configurable token expiration
### Frontend Templates
Located in `templates/` directory:
- **base.html**: Bootstrap 5 base template with navigation
- **nav.html**: Dynamic navigation based on user role
- **index.html**: Landing page
- **login.html/register.html**: Authentication forms
- **agent_register.html**: Agent creation form
- **agent_management.html**: Agent dashboard with real data
- **search.html**: Global search functionality
- **user_management.html**: User management interface
- **admin/dashboard.html**: Admin statistics, management, and verification workflow
### Static Assets
**static/style.css**: Custom CSS with:
- CSS variables for consistent theming
- Gradient backgrounds and modern styling
- Responsive design for mobile devices
- Bootstrap 5 customizations
## Key Features
### Authentication Flow
- Cookie-based JWT authentication
- Three-tier role-based access: `user`, `admin`, `readonly_admin`
- `user`: Standard access, can manage own agents
- `admin`: Full access to admin dashboard, all write operations
- `readonly_admin`: Can view admin dashboard but all write actions (edit, delete, approve, create) are hidden
- `role` field on user documents; `is_admin` kept in sync for backward compatibility
- `require_admin_or_readonly()` dependency for read-only admin endpoints
- Automatic redirects based on user role (admin/readonly_admin → /admin, user → /agent-management)
- Secure logout with token cleanup
### Agent Management
- Full CRUD operations with user ownership
- Status tracking (Active, Inactive, Development, Deprecated)
- Rich metadata: tags, userbase, department, contact person
- Search functionality across multiple fields
- Filtering by status, audit status (Audited / Not Audited), and discipline
- Admin can view/manage all agents
### Client & Verification System
- `client` field on agents: `"yes"` or `"no"` (mandatory on registration form)
- `client_name`: free text, required when client is "yes"
- `studio_name`: optional free text field
- Registration form order: Name, Description, Purpose, Client (Yes/No), Client Name (conditional), Studio Name, Tool, then Version/Status/etc.
- When `client == "yes"`: agent auto-tagged with `verification_status = "needs_verification"`
- Verification tab on admin dashboard shows pending agents with Approve button
- `PUT /api/admin/agents/{id}/verify` — admin-only, sets status to "verified" with verifier info
- `GET /api/admin/agents/pending-verification` — returns agents needing verification
- Verification badges displayed on agent cards (orange "Needs Verification", green "Verified")
### Client Agent Email Notification
- When `client == "yes"` on agent creation, sends email via Mailgun to `CLIENT_AGENT_NOTIFY_EMAILS`
- Subject: "Client Agent Created"
- Body includes: Agent Name, Description, Purpose, Client Name, Studio Name, Tool, Created By
- Non-blocking: failure does not break agent creation
### Weekly Agent Digest Email
- Scheduled via APScheduler to run every Monday morning (default 7:00 AM, `WEEKLY_DIGEST_HOUR` env var)
- Queries agents created in last 7 days
- Sends to all active admin users
- Body includes: Agent Name, Purpose, Description, Created By (email)
- Subject: "Agents Created in Last Week"
- Skips sending if no agents created
- Can be manually triggered via `POST /api/admin/digest/send` (admin-only)
### Discipline & Star Rating
- `discipline` field classifies agents into business categories: Strategy, Creative, Oversight including delivery, Optimization, Back Office including operations, Pencil Agents
- Required on registration form, optional on edit (to support legacy agents)
- Pencil Agents discipline is auto-assigned to agents with "pencil" in the name when no discipline is set (collector API auto-tag + startup migration)
- `rating` field stores the **average** star rating (1-5) computed from all per-user ratings
- `rating_count` field stores the number of individual ratings
- **Per-user rating system**: Any authenticated user can rate any agent via `PUT /api/agents/{id}/rating`
- Individual ratings stored in `agent_ratings` collection with compound unique index on `(agent_id, user_id)`
- After each rating, the agent's average rating and count are recalculated and stored on the agent document
- `GET /api/agents/{id}/my-rating` returns the current user's rating plus the average and count
- Interactive star rating widget in detail modal shows the **user's own rating** as filled stars
- Average rating and count displayed below the stars and on agent card badges
- Rating removed from edit modals (rating is per-user, not admin-set)
- Rating framework info modal accessible via info icon next to "Rating:" label
- Dashboard supports filtering by discipline and sorting by rating
- Discipline badge (purple) and star rating badge displayed on agent cards
- Both fields included in CSV export/import
- Discipline passed through collector API; rating is human-only (not in collector)
### Token Usage Tracking
- `total_tokens`, `prompt_tokens`, `completion_tokens` fields on agents track cumulative LLM token consumption
- `prompt_tokens` (input) and `completion_tokens` (output) provide cost breakdown detail
- `token_count` per day in usage timeline entries alongside `message_count`
- Token badge displayed on agent cards (gold/coins icon) with prompt/completion breakdown in tooltip
- Usage modal shows Total Tokens stat with In/Out breakdown alongside messages/conversations/users
- Dual-axis chart (messages left axis, tokens right axis) when token data exists
- Sort agents by Total Tokens
- CSV export includes `total_tokens`, `prompt_tokens`, `completion_tokens` columns
### High Usage Email Notifications
- Entirely optional — silently disabled when Mailgun env vars are not set
- Triggered from the Agent Collector endpoint (POST `/agents`); checks 7-day rolling token usage from `usage_timeline`
- Alerts when weekly token usage exceeds threshold (default 100,000, configurable via `TOKEN_USAGE_THRESHOLD`)
- Non-blocking — notification failure never breaks the collector API
- Cooldown tracking in MongoDB `token_notifications` collection (default 24h, configurable)
- Sends to all active admin users' email addresses
### Prompt Audit & Auto-Classification (Gemini)
- Automated analysis of agent `instructions` (system prompts) using Google Gemini 2.5 Pro
- **Two trigger modes:**
1. **Automatic post-sync**: After the collector API (`POST /agents`) creates/updates an agent with instructions, a background task (`asyncio.create_task`) auto-classifies it. Non-blocking — sync response is not delayed.
2. **Manual batch**: Admin clicks "Run Full Audit" or "Run Unclassified Only" on the Prompt Audit tab. Processes agents sequentially with 4-second pauses between batches to avoid Gemini rate limits.
- **Classification outputs per agent:**
- `audit_category`: Cat 1 (Internal Sandbox), Cat 1B (High Cost Internal), Cat 2 (Client-Exposed), Cat 3 (Client-Sold)
- `audit_risk_level`: low / medium / high / critical
- `audit_discipline`: Picks from existing discipline list (Strategy, Creative, Oversight including delivery, Optimization, Back Office including operations, Pencil Agents)
- `audit_department`: Free text inferred from instructions (e.g., "Project Management", "Media")
- `audit_is_client_work`: Boolean — detects client names, brands, client deliverables in instructions
- `audit_flags`: Array of risk flags (client_facing, handles_pii, uses_external_tools, etc.)
- `audit_summary`, `audit_recommendations`, `audit_category_reasoning`, `audit_discipline_reasoning`, `audit_client_work_reasoning`, `audit_client_name_detected`
- **Auto-assignment**: Gemini results auto-populate `discipline` and `agent_department` fields on the agent document (only if currently empty, to respect manual edits)
- **Client work auto-detection**: When `audit_is_client_work = true`, auto-sets `client = "yes"` and `verification_status = "needs_verification"` — agent appears on Verification tab. Does NOT overwrite manually-set values.
- **Audit statuses**: All audited agents start as `flagged`. Admin can mark as `reviewed` or `cleared` via the detail modal with optional notes.
- **Prompt Audit tab** on admin dashboard: Summary cards (Audited, Flagged, Reviewed, Cleared, Client Detected, No Instructions), filterable results table, detail modal with full analysis and review controls
- **Rate limit handling**: Retry with exponential backoff (10s, 20s, 40s) for 429/quota errors
- **Logging**: Uses Python `logging` module (`audit_analyzer` logger) for systemd journal visibility
- Agents without `instructions` are skipped (counted as "No Instructions")
- `audit_history` collection stores historical record of each audit run and review action
- Gracefully disabled when `GOOGLE_API_KEY` not set — UI shows config warning, buttons hidden
### User Management
- User registration with validation
- Admin user creation capabilities
- Three-tier role system: `user`, `admin`, `readonly_admin`
- Role dropdown in admin user edit modal (replaces is_admin checkbox)
- `role` and `is_admin` fields kept in sync on update
- Profile management
- User statistics and administration
### Database Integration
- MongoDB with proper ObjectId handling
- Async operations using Motor driver
- Indexed queries for performance
- Data aggregation for statistics
- Collections: `users`, `agents`, `agent_usage`, `token_notifications`, `agent_ratings`, `audit_history`, `completion_reminders`
## Development Guidelines
### Database Operations
- Always use ObjectId for MongoDB document IDs
- Use Motor async driver methods (await collection.find_one())
- Handle ObjectId conversion in CRUD operations
- Implement proper error handling with try/except blocks
### Authentication
- Use cookie-based auth for web interface
- API endpoints require `get_current_user_from_cookie()` dependency
- Write endpoints use `require_admin()` dependency
- Read-only admin endpoints use `require_admin_or_readonly()` dependency
- Always validate user permissions for data access
- `readonly_admin` users can view admin dashboard but UI hides all write-action buttons via `admin-write-action` CSS class
### Template Context
- Pass `current_user` to all templates for navigation
- Handle dict objects (not User model instances) in templates
- Use proper null checks for optional user data
### API Response Models
- Convert ObjectId to string in API responses
- Handle optional datetime fields with isoformat()
- Maintain consistency between Create and Response models
### Error Handling
- Provide meaningful error messages in templates
- Use proper HTTP status codes in API responses
- Graceful degradation for missing data
## Project Dependencies
Key dependencies from requirements.txt:
- **fastapi**: Web framework
- **uvicorn**: ASGI server
- **motor**: Async MongoDB driver
- **pymongo**: MongoDB operations
- **python-jose**: JWT token handling
- **passlib**: Password hashing
- **bcrypt**: Password encryption
- **pydantic**: Data validation
- **jinja2**: Template engine
- **python-multipart**: Form handling
- **requests**: HTTP client (used for Mailgun API calls)
- **apscheduler**: Task scheduling (weekly digest email)
- **google-genai**: Google Gemini API client (used for prompt audit auto-classification)
## API Endpoints (New)
### Verification
- `GET /api/admin/agents/pending-verification` — List agents with verification status (admin + readonly_admin)
- `PUT /api/admin/agents/{agent_id}/verify` — Approve/verify an agent (admin only)
### Weekly Digest
- `POST /api/admin/digest/send` — Manually trigger the weekly agent digest email (admin only)
### Prompt Audit
- `POST /api/admin/audit/run` — Run Gemini audit batch. Optional JSON body: `{agent_id, unclassified_only}`. Returns `{status, total, audited_count, failed_count, skipped_count, results_summary}` (admin only)
- `GET /api/admin/audit/results` — Get all agents with audit data + `config_status.gemini_configured` (admin + readonly_admin)
- `PUT /api/admin/audit/{agent_id}/review` — Mark audit as reviewed/cleared with `{audit_status, reviewer_notes}` (admin only)
### Registration Completion Flow (LibreChat-synced agents)
- `GET /api/agents/incomplete` — Current user's incomplete agents (admins see all). Used by the agent management page banner. Owner resolution: `created_by == user_id` OR (`created_by == "agent_collector_api"` AND `agent_contact_person == user_email`).
- `GET /agent-complete/{agent_id}` — HTML form (parameterised `agent_register.html` in completion mode). Pre-fills existing fields, required new governance fields blank. Auth: agent owner (by ID or email) or admin.
- `POST /agent-complete/{agent_id}` — Submit completion. Sets `registration_complete=True`, reassigns `created_by` from `"agent_collector_api"` to the submitting user's ID.
- `POST /api/admin/completion-reminders/send` — Manually trigger the daily reminder digest. Optional `?force=true` to bypass cooldown + max-nudges cap (admin only).
- `GET /api/admin/agents/unresolved-owner` — Collector-created agents whose `agent_contact_person` doesn't match an active user (admin only)
- `PUT /api/admin/agents/{agent_id}/reassign-owner` — Admin reassigns ownership; body `{new_contact_email, new_owner_user_id?}`. Resolves user by email if `new_owner_user_id` omitted (admin only)
## Registration Form Redesign (2026-05)
The agent registration form was rebuilt around 7 governance sections per `Indext2.html` mockup. See `PLAN-registration-form-redesign.md` for full design rationale.
### New schema fields on agents
Top-level (all `Optional`):
- `business_entity` (enum: OLIVER, DARE, Brandtech Group, Pencil, Jellyfish, Adjust, Other) — required on form
- `client_scope` (enum: `internal`, `all`, `specific`) — required on form. Maps to legacy `client` field: `specific``yes`, else→`no`. Both fields stay in sync to preserve existing verification + Mailgun client-agent-email flows.
- `agent_classification` (enum: Utility, Functional, Supervisory, Guardian) — required on form
- `autonomy_level` (enum: Human-Led, Hybrid, Autopilot) — required on form
- `ip_ownership` (enum: Brandtech IP, Client IP, Shared/TBD) — required on form
- `foundation_model` (string) — optional, dropdown with optgroups by provider
- `validated_by`, `validation_date`, `evals_method` — optional Performance & Testing fields
- `registration_complete` (bool) — `True` for form-submitted agents, `False` for collector-created. Drives the completion-reminder flow.
Nested objects:
- `safety: { off_switch_confirmed, access_rights_confirmed }` — booleans, recorded but don't block submission
- `pii: { handles_pii, legal_ref, data_types, consent_recorded }` — Yes/No radio with conditional fields
- `declarations: { governance, accuracy, upkeep }` — three required checkboxes
Module-level enum constants live in `models.py` (`BUSINESS_ENTITIES`, `CLIENT_SCOPES`, `AGENT_CLASSIFICATIONS`, `AUTONOMY_LEVELS`, `IP_OWNERSHIPS`, `DISCIPLINES`).
### Form field mapping
- "Studio / Department" form input → stored as `studio_name` (not `agent_department``agent_department` continues to be auto-populated by Gemini audit)
- Author / Contact Person — autofilled from logged-in user's email; not a user-editable input
- Capabilities — 8 fixed checkboxes comma-split values from "Other" free-text input, stored as `agent_capabilities: list[str]`
### Discipline rename
Discipline value `Optimization` (US) was renamed to `Optimisation` (UK) in 2026-05. Migration runs on startup (`crud.migrate_optimisation_spelling`). All template dropdowns + Gemini system prompt updated. Defensive `Optimization → Optimisation` normalisation in `audit_analyzer.store_audit_result` and `apply_classification_fields`.
### Required-field enforcement
The `/agent-register` POST handler enforces required fields server-side via `Form(...)` defaults plus explicit conditional checks (e.g. `client_name` required if `client_scope == "specific"`, all 3 declarations required). Existing agents are grandfathered via the startup migration that backfilled `registration_complete` based on `created_by`.
### Completion flow for LibreChat-synced agents
LibreChat-synced agents enter via the collector with `created_by="agent_collector_api"` and only a subset of fields populated. They land with `registration_complete=False` (set in `crud.create_agent_from_collector` via `setdefault`). Owners are nudged via daily APScheduler email job (`notifications.send_completion_reminders`):
1. Job groups incomplete agents by lowercased `agent_contact_person` and resolves to active users (case-insensitive email match)
2. Per-user cooldown (default 7 days) + nudge cap (default 4) tracked in `completion_reminders` collection
3. Single digest email per user with one "Complete →" CTA per agent
4. Submitting the completion form flips `registration_complete=True` AND reassigns `created_by` from the marker to the submitting user's ID — they then own it normally for future edits
`agent_register.html` is parameterised: `mode="register"` (default) or `mode="complete"`. The completion-mode handler `_build_completion_context()` in `main.py` pre-computes capability sets, derived `client_scope` from legacy `client` field, and PII/safety pre-fills. Special case: `agent_purpose` renders blank if it equals `agent_description` (collector defaults purpose=description for ~94% of agents per prod data analysis).
### Gemini audit auto-classification (extended 2026-05)
`audit_analyzer.SYSTEM_INSTRUCTION` schema extended to also return:
- `agent_classification` (Utility/Functional/Supervisory/Guardian) — auto-applied to `agent_classification` field if empty
- `autonomy_level_hint` (Human-Led/Hybrid/Autopilot/null) — auto-applied to `autonomy_level` if empty
- `agent_classification_reasoning`, `autonomy_reasoning` — stored on agent doc as audit fields (not used as primary values)
Running `POST /api/admin/audit/run` after deploy auto-fills these on the ~225 agents with instructions, leaving only the truly non-inferable fields (business_entity, ip_ownership, declarations) for the human owner via the completion form.
### Filter dimensions
Both `agent_management.html` and `admin/dashboard.html` agent views support filtering by:
- Business Entity, Agent Type, Autonomy Level (new dropdowns)
- "Compliance risks" quick toggle — shows agents where `pii.handles_pii=true` OR `ip_ownership="Shared/TBD"` OR `autonomy_level="Autopilot"`
### CSV roundtrip for backfilling
`/api/admin/agents/export/csv` includes 21 new columns covering all governance fields. Import accepts them; nested objects (`safety`, `pii`, `declarations`) only build up if at least one cell is populated, so partially-filled CSVs don't clobber existing data with `False`. Defensive `_csv_bool()` helper returns `None` for empty cells.
## UI & Brand (2026-05)
The frontend was rebranded to the OLIVER master template (`/Users/nickviljoen/Documents/Optical_Projects/Pres Template/OLIVER Master PPT Template.potx`). The old muddy-orange `#f3ae3e` palette and Inter font are gone. See `PLAN-ui-refresh.md` for design rationale.
### Design tokens (`static/style.css` `:root`)
Always use these tokens — do **not** introduce new hex codes:
- `--brand-yellow: #FFCB05` — primary accent (page-title highlight, stat-tile left strip, card-header icons, active states, primary buttons)
- `--brand-yellow-soft: #FFF5C4` — subtle hover backgrounds, dropdown-item hover
- `--brand-orange: #FF5C00` — destructive accent (btn-danger, Microsoft SSO button, logout link)
- `--brand-dark: #1A1A1A` — body text, table headers, modal headers, nav text
- `--brand-grey: #626262` — secondary text / muted copy
- `--brand-grey-light: #DEE2E5` — borders, dividers
- `--brand-off-white: #F6F7F7` — page background, table row hover
- Font: **Montserrat** 400/500/600/700/800 (loaded from Google Fonts in `base.html`)
### Signature motifs
- **Page title** — `.page-title` class. Yellow highlight rectangle behind black text via a `linear-gradient` (yellow up to 70% of the height, transparent above). Apply to every page `<h2>`; subtitle goes in `<p class="page-subtitle">` below. **Don't** wrap with a leading icon — the highlight is the visual.
- **Stat tile** — `.stat-tile` class. White card with 8px yellow `::before` strip on the left, yellow Font Awesome icon, big near-black `.stat-tile-value` and uppercase `.stat-tile-label`. Replaced the old orange-gradient `.stat-card`.
- **Card section accent** — every `<h5>` / `<h6>` inside `.card-header` automatically gets a leading yellow icon (CSS targets `.card-header h5 > i.fas:first-child`) and a 28×3px yellow underline accent (`::after`). The accent doesn't apply to modal headers or to header titles displaying counts (those go in `.card-body`).
- **Modal header** — solid `--brand-dark` background, white text, white close button.
- **Active nav-link / nav-tab** — 2-3px `--brand-yellow` bottom border, never a coloured background pill.
### Gotchas (don't re-introduce these bugs)
- **Navbar must use `position: fixed`** (set in `style.css`). Do **not** add `position: relative` or `position: sticky` overrides in `templates/nav.html` — a previous inline `<style>` rule there silently overrode the fixed positioning and made the navbar scroll away with the page.
- **Bootstrap tabs must use `<button data-bs-target="#x">`**, not `<a href="#x">`. The anchor form causes the browser to scroll to the tab pane on click (visible as the page "jumping down"). Bootstrap 5 recommends buttons for this reason.
- **Body has `padding-top: 64px`** to clear the fixed navbar — keep it whenever you change navbar height.
- **No `transform: scale(...)` on table-row hover** — jittery and shifts neighbour rows. Use a background-color change.
- **Chart.js colours** in `templates/admin/dashboard.html` reference the brand palette directly (`#FFCB05`, `#1A1A1A`, `#FF5C00`) because the library can't read CSS vars. Keep them aligned when adding charts.
### Where things live
- `static/style.css` — all tokens + component styles (palette, navbar, tabs, buttons, forms, tables, cards, stat-tile, page-title, modal, alerts).
- `templates/base.html` — Google Fonts link (Montserrat).
- `templates/nav.html` — navbar markup + scoped styles for user-avatar / dropdown.
- `templates/admin/dashboard.html` — inline `<style>` block has admin-specific overrides (column widths, audit risk badge colours, tabs); kept in-file because they're not reused elsewhere.
- Page templates apply `.page-title` to their main `<h2>` and `.page-subtitle` to the description line.