Commit graph

167 commits

Author SHA1 Message Date
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
michael
2ffe3783d2 Add Mod Comms feature presentation and documentation
Create a comprehensive 25-slide PowerPoint presentation showcasing all
Mod Comms features, including multi-agent AI system, campaign management,
real-time analysis, feedback reports, knowledge base, analytics, auditing,
user roles, and technical architecture. Includes a Python generator script
for reproducible builds and a companion features markdown document.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 10:32:34 -06:00
michael
7f1df8c508 Fix user audit trail logging stale agency value for old and new
Expire the SQLAlchemy cached user object after flush() so the
subsequent get_by_id() reloads the agency relationship with fresh
data. Previously the identity map returned the same Python object
with the old .agency, causing audit logs to record identical old
and new values on agency changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:53:04 -06:00
michael
bcc20260de Add sortable, filterable campaign list with "My Campaigns Only" toggle
- Backend: Expose created_by field on CampaignResponse schema and all
  response constructors in routes.py
- Frontend API layer: Add created_by to CampaignResponse interface and
  createdBy to the frontend campaign converter
- Campaign list: Add column sorting (click headers to toggle asc/desc),
  per-column text filter inputs below headers, and a "My Campaigns Only"
  toggle that filters to campaigns created by the current user
- Default sort is lastModified descending to match existing behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:43:07 -06:00
michael
c7175f4261 Update Profile page layout: reorder fields and add separate Agency field
Reorder user details from Role/First/Last/Email to First/Last/Email/Role/Agency
so the 2-column grid shows names together at the top. Add Agency as its own
field instead of appending it to the Role display.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:25:25 -06:00
michael
99fa956c74 Fix submitter agency not loading in flagged/resolved audit queries
The selectinload for FlaggedItem.submitter and ResolvedItem.submitter
was not chaining .selectinload(User.agency), so the submitter's agency
was always None in the API response. This caused the "Submit Agency"
column to be empty in the Flags and Resolutions tabs of the Auditing
page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:15:35 -06:00
michael
3508154693 Use current user's agency in Create Campaign modal
Replace hardcoded "OLIVER Agency" with the logged-in user's agency name
from UserContext. The field remains disabled/non-editable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:57:13 -06:00
michael
407f11c003 Add user change history audit trail for compliance
Adds a user_change_logs table to track all role and agency changes made
to users by super admins. Includes a change history modal in the User
Management screen (clock icon per row) showing timestamped, human-readable
change descriptions with the actor who made each change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:41:16 -06:00
michael
a5d5d51d2a Add confirmation modal for Super Admin role assignment
Prevents accidental Super Admin privilege grants by requiring users to
type "make this user super admin" before the role change is applied.
Modal blocks paste/drag input and reverts the dropdown on cancel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:25:56 -06:00
michael
51c4909ee7 Add visual save confirmation to User Management page
Role and agency changes now show an inline green checkmark + "Saved" indicator
on the affected row that auto-clears after 2 seconds. Agency creation shows a
green success banner that auto-dismisses after 3 seconds. Successful actions
also clear any stale error banners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:15:17 -06:00
michael
8220955cd4 Fix audit entry click navigation for Oversight Admin
handleNavigateToAuditedItem silently failed because campaignProofs are
loaded lazily (only when viewing a campaign), and errors went to a
setError() state that was never rendered. Now the function fetches and
caches proofs on demand when not already loaded, and uses alert() for
visible error messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:03:55 -06:00
michael
ebfcd60c71 Fix campaign visibility bug for unassigned users after agency reassignment
Unassigned (no agency) non-admin users previously saw ALL campaigns due to
a truthiness check that treated None agency_id as "no filter". This was a
security bug — they should see NO campaigns and be blocked from creating them.

Backend: Add _NO_AGENCY sentinel to distinguish "no filter" from "no agency",
add early-returns at all 5 list/analytics endpoints, fix _check_campaign_access
to explicitly reject unassigned users, and block campaign creation with 403.

Frontend: Add isUnassigned boolean to UserContext, show informational empty
state on Campaigns view, and reinforce readOnly for defense-in-depth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:42:42 -06:00
michael
a08d54ec6d Fix Agency Admin campaign creation and proof upload permissions
Switch canWrite from blacklist (role !== 'oversight_admin') to explicit
whitelist (super_admin, agency_admin, basic_user) for clearer permission
logic. Propagate readOnly prop to CampaignDetail and ProofDetailView
subcomponents so upload/delete buttons are properly hidden for read-only
roles at all navigation levels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:23:58 -06:00
michael
3207ec301c Standardise Issue/Recommendation formatting across all agents
Replace single-line bullet format with a structured two-part format
(**Issue:** / **Recommendation:**) in all specialist and lead agent
prompts. Update Gemini response schema description to match. Update
frontend formatFeedbackText and formatFeedbackTextForPDF to parse
**bold** markdown and preserve line breaks within multi-line bullets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 10:17:08 -06:00
michael
f1183e1bff Display user role and agency on Profile page
Replace the hardcoded "Account Type" field with a "Role" field that
shows the user's role label and agency name from the backend /api/me
endpoint (e.g. "Super Admin (Oliver)", "Standard User (Barclays)").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 10:00:25 -06:00
michael
a2f52c0960 Add example-correction formatting rule to all agent prompts
Agents now show example corrections in the format they're recommending
(e.g. sentence case examples when recommending sentence case) to avoid
contradictions between advice and examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:55:01 -06:00
michael
4cae1e4c78 Remove "Verdict:" prefix inconsistency from lead agent summaries
Replace all "verdict" language in the lead agent prompt with "status/summary"
and add prescriptive opening-line templates so the LLM produces consistent
output without a "Verdict:" prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:50:26 -06:00
michael
dee0df57d8 Rename "Actionable Issues" heading to "Key Actions"
Replace the repetitive "Actionable Issues" label with the more
constructive "Key Actions" in the feedback report and PDF export.
Remove the uppercase CSS class so the heading renders in sentence case.
Update empty-state text to "No key actions identified."

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:42:27 -06:00
michael
37f364ec0f Add acronym expansion guidance to all agent prompts
Agents now spell out acronyms in full on first use (e.g. "Web Content
Accessibility Guidelines (WCAG)") for clarity. The instruction covers
common acronyms like WCAG, FSCS, GDE, APR, CTA, FCA, PRA, and T&Cs,
and applies to any acronym encountered in output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:35:45 -06:00
michael
f92e76b333 Add punctuation and capitalisation consistency rules to all agent prompts
Adds an IMPORTANT instruction block to all 5 agent prompt templates
(legal, brand, channel best practices, channel tech specs, lead) that
enforces: capitalisation after full stops and in labels, consistent
bullet-point ending style, and "e.g." without a trailing comma.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:31:37 -06:00
michael
6504511fb6 Add Plain English language instructions to all agent prompts
Instructs all five agents (legal, brand, channel best practices,
channel tech specs, lead) to prefer simple vocabulary over complex
alternatives (e.g. "add" over "incorporate", "about" over "regarding").
Also fixes "constitute" → "qualify as" in the legal agent prompt itself.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:26:39 -06:00
michael
265bf08470 Replace punitive "violation" language with constructive alternatives in agent prompts
UAT feedback flagged the use of "violations", "violates", etc. as feeling
accusatory. Replaced all instances with constructive terms ("issues",
"doesn't align with", "doesn't meet") and added an explicit instruction
to all 5 agent prompt templates to avoid this language in output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:18:52 -06:00
michael
4a684e7757 Enforce British English spelling in all agent prompt templates
Added explicit UK English instruction to the Response Format section of
all five agents (legal, brand, channel best practices, channel tech specs,
lead) so output uses spellings like "authorised", "colour", "capitalise".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 08:53:51 -06:00
michael
05e74becfe Add frontend RBAC: UserContext, role-based sidebar, agency filter, user management
- Add UserRole type and AppUser interface to types.ts
- Create UserContext with useUser() hook providing role-based permission booleans
- Split App into App (auth wrapper) + AppContent (uses UserContext)
- Update Sidebar to filter nav items by UserRole instead of boolean isAdmin
- Add User Management nav item (super_admin only)
- Add AgencyFilterBar component for oversight_admin/super_admin session-level filtering
- Pass agencyId to getCampaigns, getAnalytics, audit endpoints in apiService
- Add getMe, getUsers, updateUser, createAgency to apiService
- Build UserManagement page with user table (role/agency dropdowns) and agency CRUD
- Add readOnly prop to Campaigns (hides create/delete/status-toggle for oversight_admin)
- Add readOnly prop to Settings (disables all ManagementCards, shows view-only banner)
- Pass agencyId to Analytics component for filtered data
- Update urlState with Knowledge Base and User Management views

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 08:36:38 -06:00
michael
d21036a0de Add 4-tier RBAC backend: auth dependencies, role enforcement, agency filtering
- Add CHECK constraint migration for users.role (super_admin, oversight_admin, agency_admin, basic_user)
- Add get_current_db_user dependency resolving Azure claims to User ORM with agency
- Add require_role() factory and require_write_access() dependency
- Auto-promote dev user to super_admin when DISABLE_AUTH=true
- Add /api/me, PUT /api/users/{id}, POST /api/agencies endpoints
- Apply agency-based data filtering on campaigns, analytics, audit routes
- Block oversight_admin from all mutation routes (campaigns, proofs, flags, resolves)
- Restrict dropdown option mutations to super_admin only
- Add role check in WebSocket handler to block oversight_admin from analysis
- Add CurrentUserResponse, UserUpdate, AgencyCreate schemas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 08:28:23 -06:00
michael
6bd8a03a15 Remove page number and document name references from agent feedback
Add explicit no-citations instructions to all 5 agent prompts to prevent
Gemini from including page numbers, document names, or source citations
in analysis feedback. These references were unhelpful since the system
doesn't use RAG and users cannot action them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 08:39:41 -06:00