Commit graph

192 commits

Author SHA1 Message Date
Vadym Samoilenko
416c9dce0e Fix QC scroll, show error toasts, enable Apache WS module
- ProofDetailView: remove h-full from root div so content can scroll
  naturally (h-full was capping height at viewport, blocking scroll)
- App.tsx: wire up error state to a top toast banner so users see
  failures (campaign load, proof upload, etc.) instead of silent drops;
  auto-dismiss after 8s, same pattern as notification toast
- Apache: enabled mod_proxy_wstunnel (was missing — proxy_http was
  handling WebSocket connections and dropping them due to HTTP-level
  timeouts; wstunnel provides a proper bidirectional tunnel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 12:52:30 +00:00
Vadym Samoilenko
5c338c31fb Fix WebSocket connection dropped during long proof analysis
- Add 25s heartbeat ping from backend to prevent Apache/proxy idle-timeout
  killing the connection during 1-3 min analysis runs
- Handle heartbeat silently in both analyzeProof and analyzeWIPProof frontend handlers
- Run PDF rasterization via asyncio.to_thread so heartbeats aren't blocked
- Wrap analyze_proof with asyncio.wait_for(timeout=300) for a hard 5-min cap
- Log dropped send_message calls in ConnectionManager instead of swallowing silently
- cloudrun.yaml: add sessionAffinity, startup probe, raise containerConcurrency 4→10,
  document DISABLE_AUTH option

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 11:23:59 +00:00
Vadym Samoilenko
ef1e4adabd Fix PDF logo URL to include Vite base path in production
window.location.origin alone gives https://baic.oliver.solutions, but the
app is deployed at /modcomms/ (VITE_BASE_PATH=/modcomms/), so the logo
was loading from the wrong path (404). Now uses:
  window.location.origin + import.meta.env.BASE_URL + filename
which resolves correctly in both dev (http://localhost:3000/...) and
production (https://baic.oliver.solutions/modcomms/...).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 14:11:12 +00:00
Vadym Samoilenko
f520aba397 Fix KB distillation fallback and SpecVersion type annotation
- knowledge_base_service.py: wrap Gemini distillation call in try/except
  to fall back to fallback_client/fallback_model if primary times out,
  matching the fallback behaviour in GeminiService._generate_content()

- models.py: fix SpecVersion.source_document_ids ORM type annotation from
  Mapped[Optional[dict]] to Mapped[Optional[list]] — the field stores a
  JSON array of document ID strings, not an object

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 14:03:25 +00:00
Vadym Samoilenko
060fbeba76 Fix GeminiService client attribute error in knowledge base distillation
Replace self.gemini.client with self.gemini.primary_client on line 295 of
knowledge_base_service.py. GeminiService only exposes primary_client and
fallback_client — there is no client attribute. This caused all processing
jobs to fail at the distillation step, which is also why Version History
was always blank (no SpecVersion records were ever created).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 13:59:59 +00:00
Vadym Samoilenko
538a32505e Fix LlamaParse 401 + update logo to v5
- Add LLAMA_CLOUD_BASE_URL config option so the LlamaCloud regional
  endpoint can be set without code changes (fixes 401/region errors
  on production); pass it through to AsyncLlamaCloud client init
- Document LLAMA_CLOUD_BASE_URL in .env.deploy.example with EU endpoint
- Copy BAR-ModComms-logo-v5.png to frontend/public
- Sidebar: update logo reference v4 → v5
- PDF header: update logo v4 → v5, wrap in black (#000) band for
  legibility, remove duplicate "Oliver" wordmark

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:22:31 +00:00
Vadym Samoilenko
da63629720 Auto-generate backend/.env and frontend/.env.local from .env.deploy
The deploy script now generates both env files automatically — no more
manual file creation on the server. All secrets and VITE_ vars are
centralised in .env.deploy (gitignored). Updated .env.deploy.example
with all required variables and inline documentation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 11:57:17 +00:00
Vadym Samoilenko
ff1c809249 Prepare production deployment for baic.oliver.solutions/modcomms
- Add VITE_BASE_PATH support to vite.config.ts so assets resolve correctly under /modcomms/ subpath
- Fix home URL in urlState.ts to use BASE_URL instead of hardcoded '/'
- Fix sidebar logo src to use BASE_URL prefix (Vite doesn't rewrite TSX src attributes)
- Fix Azure AD redirect/logout URIs to include BASE_URL subpath in authConfig.ts and App.tsx
- Add migration 009 to remove Mindshare/Zenith and add Rapp agency
- Update .env.deploy.example with production values for baic.oliver.solutions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 11:54:00 +00:00
Vadym Samoilenko
a2382cf027 Fix PDF page breaks, badge alignment, and responsive layout
- PDFReport: Fix RagStatus badge padding (symmetric 4px top/bottom + lineHeight 1.2) so text is centered rather than floating at the top
- PDFReport: Add breakInside: 'avoid' to preview+summary grid, agent reviews grid, and li elements to prevent content being cut mid-sentence across PDF page breaks
- Campaigns: Lower two-column layout breakpoint from xl (1280px) to lg (1024px) so laptop screens show side-by-side proof detail view
- Campaigns: Add flex-wrap to button row so Download Proof / Download Report / New Version buttons wrap gracefully on smaller screens

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 13:17:26 +00:00
Vadym Samoilenko
fdb5a2d961 Fix PDF export: switch to browser print, fix page breaks and bullet alignment
- Replace html2canvas + jsPDF with window.print() in both handleExportPDF
  and handleDownloadReport — browser print properly respects CSS break-inside:
  avoid on agent cards and page-break-before on proof pages, eliminating
  orphaned section headings
- Add listStylePosition: 'outside' and explicit lineHeight to <ul>/<li>
  elements in PDFReport so bullet symbols sit at the text baseline
- Add pageBreakInside: 'avoid' alongside existing breakInside: 'avoid' on
  agent cards for cross-browser compatibility
- Replace placeholder shield icon and plain-text Oliver SVG on cover page
  with BAR-ModComms-logo-v4.png (Barclays eagle) and styled Oliver wordmark

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 13:06:12 +00:00
Vadym Samoilenko
13a3ff87cc Remove misleading size constraint from proof upload modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 12:56:43 +00:00
Vadym Samoilenko
4a2182ce2a Fix Proof Detail page responsive layout and button states
- Add min-w-[900px] to root container so content scrolls horizontally below 900px instead of being crushed
- Change grid breakpoint from lg to xl so 3-column layout only fires at 1280px
- Add min-w-0 + truncate to proof title to prevent clipping at narrow widths
- Add flex-wrap to buttons row so buttons wrap rather than overflow
- Add shrink-0/whitespace-nowrap to all action buttons to prevent compression
- Improve button interaction states with hover:shadow-sm, active:scale-95, and transition-all

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 12:54:55 +00:00
Vadym Samoilenko
1f2a2e5016 Settings: fix sub-channel dropdown and Add button styling
- Sub-Channel dropdown: always white bg + azure border (never azure fill),
  even when a value is selected; channel dropdown retains azure fill
- Add button: joined to input field as a single group (no gap, shared
  border, matching corner radius); button colour is azure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 16:10:10 +00:00
Vadym Samoilenko
f22b24eb8a Fix Settings dropdown disabled state and chevron colours
- Disabled sub-channel dropdown: was showing grey bg + grey border;
  now shows azure outline (opacity-50) consistent with unselected state
- Chevrons: now white when dropdown is azure-filled (selected),
  azure when unselected — previously always azure which was invisible
  against the azure background

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 16:06:19 +00:00
Vadym Samoilenko
2553d1f952 Fix logo proportions: use w-full h-auto instead of fixed height
h-28 w-auto was locking height which can distort aspect ratio in the
narrow sidebar. w-full h-auto scales proportionally from the container
width with object-contain as a safety net.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 15:56:12 +00:00
Vadym Samoilenko
dfb758fa61 Fix email resolution from Azure AD token claims
Azure AD v1 access tokens (sts.windows.net issuer) use the 'upn' claim
for the user principal name/email, not 'email' or 'preferred_username'.
Add 'upn' as a fallback so email is correctly resolved on login.

Also add debug logging to show which claims are present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 15:55:00 +00:00
Vadym Samoilenko
fa00a86777 Analytics AI summary: restore thick left sky accent border (border-l-8)
Thin border all around, prominent 8px left accent in oliver-sky colour
per design guidance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 15:51:14 +00:00
Vadym Samoilenko
d5eba4c6da Apply design feedback: dropdowns, analytics, sidebar, logo
- Settings: selected dropdown state now shows azure bg with white text
- Analytics stats: icon circle bg changed from white to grey (#EFEFEF)
- Analytics AI summary: uniform border (remove asymmetric left border);
  lightbulb icon sized to match other icons (h-9 w-9)
- Sidebar: active nav item highlight changed from azure to white,
  visually connecting to the white main content area
- Sidebar: logo increased from h-20 to h-28

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 15:49:31 +00:00
Vadym Samoilenko
c6f7b0d5f1 Populate email on login for users with empty email field
When a user already exists in the DB, get_or_create_from_azure was
returning early without updating their email from Azure AD claims.
Users created before email sync was in place would permanently show
empty emails in User Management.

Now syncs email from Azure AD claims on each login if the stored
email is empty.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 15:45:48 +00:00
Vadym Samoilenko
d7fd435210 Add IVU Testing & Performance Monitoring implementation plan
Standalone Python CLI tool plan for Barclays IVU Model compliance testing:
batch WebSocket analysis, AI-based scoring via Claude, consistency metrics,
per-run PDF reports, and comparative drift detection reports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:28:56 +00:00
Vadym Samoilenko
0432635153 Grant oversight_admin write access to campaigns and proofs
Oversight admins can now create campaigns, upload proofs, and
flag/resolve issues when they have an agency assigned. They retain
all existing cross-agency read access for analytics, auditing, and
user management. Oversight admins without an agency see a read-only
campaigns view.

Changes:
- Add oversight_admin to canWrite permission in UserContext
- Guard readOnly for oversight_admin without agency in App.tsx
- Remove oversight_admin block from require_write_access dependency
- Remove WebSocket oversight_admin upload block in main.py
- Require agency for oversight_admin campaign creation in routes.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:08:54 +00:00
Vadym Samoilenko
0348693ebd Increase sidebar logo size from h-12 to h-20
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:29:43 +00:00
Vadym Samoilenko
8317e01568 Add azure border to all modal containers per Oliver design
All modal inner containers now have border-2 border-oliver-azure
for consistent Oliver branding across:
- CreateCampaignModal, CreateProjectModal
- FeedbackReport (resolve + flag modals)
- UserManagement (confirmation + history modals)
- Campaigns (upload, delete confirmation, version history modals)
- Projects (upload, delete modals)
- Login (support contact modal)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:21:59 +00:00
Vadym Samoilenko
4302b9391a Restyle full application from Barclays to Oliver Agency brand
Replace entire Barclays colour palette (navy #1A2142, lime #C3FB5A, violet
#7A0FF9) with Oliver brand tokens: black #1A1A1A, gold #FFCB05, orange
#FF5C00, azure #0487B6, sky #5DF5EA, grey #EFEFEF, green #09821F.

- Switch font from Inter/Barclays Effra to Arial (system font)
- Add new Oliver logo asset (BAR-ModComms-logo-v4.png)
- Sidebar: black background, new logo, azure active state
- Hero: orange "Intelligent Review" text, hide AI-Powered tagline
- Hide ChecksOverview on Home page per Oliver design
- Toast notification: orange background with black text
- All tables: sky headers, alternating white/grey rows
- Campaign badges: gold "In Progress", green "Completed"
- Analytics: grey KPI cards, sky accent on Key Insight, oliver trend colours
- All buttons: azure fill, pill-shaped (rounded-full)
- All tabs/toggles/dropdowns: azure accent colour
- Update HTML title to "Mod Comms - Intelligent Review"
- Default border radius set to 10px

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:16:26 +00:00
Vadym Samoilenko
44fa8ba527 Remove debug console.log from model_fallback handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 13:18:00 +00:00
Vadym Samoilenko
1b60f5deb6 Add console.log to model_fallback handler for debugging
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 13:12:40 +00:00
Vadym Samoilenko
05e1628086 Add debug logging to model_fallback callback in handlers.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 13:08:25 +00:00
Vadym Samoilenko
5735b9cbe6 Add fade-in animation for model fallback toast notification
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 13:04:23 +00:00
Vadym Samoilenko
efa6e772e0 Add toast notification when primary Gemini model falls back to backup
Backend: thread on_fallback callback through analysis chain
(gemini_service → agents → analysis_service → handlers). The handler
sends a 'model_fallback' WebSocket message exactly once per analysis
when the primary model is unavailable.

Frontend: handle 'model_fallback' WS message and show a dismissible
yellow toast at the bottom of the screen with an 8-second auto-dismiss.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 13:00:12 +00:00
Vadym Samoilenko
9ecabafa2b Fix Gemini http_options timeout unit: seconds → milliseconds
google-genai SDK expects http_options 'timeout' in milliseconds.
Passing 45 (seconds) was interpreted as 45ms → ~1s deadline,
which Google API rejected with 400 INVALID_ARGUMENT
'Manually set deadline 1s is too short. Minimum allowed deadline is 10s.'

Primary: 45_000ms (45s), Fallback: 150_000ms (150s)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 12:52:25 +00:00
Vadym Samoilenko
74585c5c18 Fix Gemini timeout by using HTTP-level timeout on separate clients
asyncio.wait_for cannot reliably cancel SDK-internal HTTP connections.
Replace with two genai.Client instances — one per model — each configured
with http_options={'timeout': N} so the TCP connection is actually torn
down when the deadline is reached.

Primary model: 45s, Fallback model: 150s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 12:49:11 +00:00
Vadym Samoilenko
a9bd6a2775 Increase fallback Gemini timeout from 60s to 150s
Log analysis showed fallback model responses up to 154s under parallel
load. 60s was too aggressive and would cause false timeouts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 12:43:39 +00:00
Vadym Samoilenko
e8c0257ea6 Add timeouts to Gemini API calls to prevent 10+ minute hangs
Primary model (gemini-3.1-pro-preview): 45s timeout
Fallback model (gemini-3-flash-preview): 60s timeout

Without timeouts, the fallback model under high load would wait
indefinitely, causing analysis to hang for 10+ minutes per file.
asyncio.TimeoutError from the primary model is now handled the same
as other exceptions (falls through to fallback).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 12:42:04 +00:00
Vadym Samoilenko
84d5b533f7 Handle WebSocket disconnect gracefully during analysis
When a client disconnects (navigates away, closes tab) while analysis is
still running, the result send raises RuntimeError "WebSocket is not
connected". Catch this specifically as INFO rather than ERROR, and guard
the fallback send_message in the general Exception handler so it doesn't
raise a second uncaught error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 12:30:51 +00:00
Vadym Samoilenko
46e7de5695 Fix MissingGreenlet 500 error on GET /api/campaigns
Add selectinload(Campaign.agency) to get_with_proof_counts query so the
agency relationship is eagerly loaded. Without it, accessing campaign.agency.name
in the route triggered a lazy load in an async context, raising
sqlalchemy.exc.MissingGreenlet and returning HTTP 500.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 12:17:12 +00:00
Vadym Samoilenko
3a66286900 Remove DATABASE_URL check from deploy.sh — injected by docker-compose
DATABASE_URL is set via the environment block in docker-compose.yml
and does not need to be present in backend/.env.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 11:23:46 +00:00
Vadym Samoilenko
f24ef038ef Validate critical env vars in deploy.sh before deployment
The script previously only checked that backend/.env existed, allowing
deployments with unset or placeholder values. This meant GEMINI_API_KEY
could be missing, causing every analysis to fail at 80% with a
PERMISSION_DENIED error from the Gemini API.

Now checks GEMINI_API_KEY, AZURE_TENANT_ID, AZURE_CLIENT_ID, and
DATABASE_URL are set to real values before any build step runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 11:21:32 +00:00
Vadym Samoilenko
82e38e8853 Add gemini-3-flash-preview fallback and Cloud Run service config
gemini_service.py: if the primary model (gemini-3.1-pro-preview) is
unavailable or returns a permission error, all three call sites now
automatically retry with gemini-3-flash-preview before propagating failure.

cloudrun.yaml: new Cloud Run service definition that ensures stable
WebSocket operation — 10-minute request timeout (vs 60s default),
2 vCPU / 4Gi RAM for PDF rasterisation, min 1 warm instance to prevent
cold-start disconnects, and GEMINI_API_KEY sourced from Secret Manager
so the service can actually reach the Gemini API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 11:18:57 +00:00
Vadym Samoilenko
138fa0fcdf Fix missing Submitter/Agency on Auditing Errors tab
The authenticated user's DB ID was fetched in main.py for a role check
but never forwarded to handle_analyze_message, so Proof.created_by was
always NULL. This caused submitter_name and submitter_agency to resolve
to None on the Errors tab.

Fix: capture current_user_id from the role-check session in main.py,
pass it to handle_analyze_message, and forward it to
add_version_with_review as created_by. Newly submitted proofs will now
have their submitter recorded and visible in all three Auditing tabs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 18:21:10 +00:00
michael
8f2f561c71 Fix stale UserContext after agency/role changes and remove hardcoded values in CreateProjectModal
UserManagement now calls refresh() on the global UserContext when the
current user's agency or role is changed, so downstream consumers
(e.g. CreateCampaignModal) immediately reflect the update.

CreateProjectModal now reads the Agency and Agency Lead fields from
the current user's profile instead of hardcoding "OLIVER Agency" and
"Steve O'Donoghue".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:44:55 -06:00
michael
b6078cf534 Add comprehensive Technical Architecture PDF documentation
Generate a professional 22-page A4 PDF covering the full ModComms system
architecture including: system overview, multi-agent AI pipeline, WebSocket
analysis flow, database schema (15 tables), frontend component hierarchy,
Azure AD authentication & RBAC, knowledge base processing pipeline,
deployment architecture, REST API reference, and appendices.

Includes 8 Mermaid diagrams rendered to high-res PNGs, styled tables,
and consistent Barclays design tokens throughout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 13:42:59 -06:00
michael
da32e0f888 Render Recommendation as same-level bullet grouped with its Issue
Each line in a bullet group (Issue, Recommendation, etc.) now renders as
its own top-level <li> at the same bullet level. Groups are visually
separated with top margin on the first item of each group.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:30:04 -06:00
michael
acd591fb8e Render Recommendation as nested sub-bullet under its Issue
Recommendation lines are now displayed as an indented nested bullet
beneath their parent Issue bullet, keeping them visually grouped together
while giving each Recommendation its own bullet marker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:29:16 -06:00
michael
a906b093d1 Give Recommendation lines their own bullet point in feedback report
Recommendation lines were rendered as continuation text within the same
bullet as their Issue, appearing without a bullet marker. Now lines
starting with "Recommendation:" are treated as new bullet groups so they
each get their own bullet point in the list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:16:52 -06:00
michael
c8f473d9b7 Fix literal \n characters appearing in proof analysis feedback
Gemini sometimes returns the literal two-character sequence \n instead of
a real newline in agent feedback text. This caused "Recommendation:" to
appear on the same line as "Issue:" with visible \n characters. Adding a
normalization step at the start of formatFeedbackText converts literal \n
sequences to real newlines so the existing line-splitting logic handles
them correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 12:36:58 -06:00
michael
766b95f54c Add Brand column to campaigns list table
Display the brandGuidelines field as a sortable, filterable "Brand"
column between Owning Agency and Last Modified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 12:29:24 -06:00
michael
a25c7a9d31 Grant oversight_admin read-only access to User Management
Allow oversight_admin users to view the User Management screen with
read-only access. They can see users, roles, agencies, and change
history but cannot edit roles, assign agencies, or create agencies.

Backend: open GET /users and GET /users/{id}/change-history to
oversight_admin (PUT /users stays super_admin only).
Frontend: add oversight_admin to sidebar nav and context permission,
render static text instead of dropdowns and hide the add-agency form
for non-super-admin users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 13:35:07 -06:00
michael
fd934bbb5f Update frontend UI text to use British English spelling
Change user-facing strings from American to British English: analyze→analyse,
analyzing→analysing, optimized→optimised, color→colour, analyzes→analyses,
synthesizes→synthesises, optimization→optimisation. Code identifiers, status
enums, and developer-facing messages are intentionally unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:11:10 -06:00
michael
81431d9aa9 Update Gemini model to gemini-3.1-pro-preview
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:04:59 -06:00
michael
e3575052ee Add per-agency analytics breakdown table for admin users
New GET /analytics/by-agency endpoint groups review metrics by agency.
The Analytics page now shows a sortable agency performance table with
pass rates, failures, errors, and legal review counts for each agency.
Only visible to super_admin and oversight_admin users. Selected agency
row is highlighted when the AgencyFilterBar is active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:33:48 -06:00