- Change placeholder from '#WF_12345' to 'WF1234567'
- Remove format validation and error messaging (field is optional, free-form)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Updates all display labels (PDF report, campaign page, Knowledge Base card, analytics, status dashboard, checks overview) and aligns internal agent name in backend. Adds migration 010 to update the knowledge base display_name in production DB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a server-side CSV export covering all campaign, proof, and version
data including agent RAG statuses. The export respects the active agency
filter so oversight admins can scope the download to a single agency.
- backend: `CampaignRepository.get_export_rows()` — flat join across
Campaign → Proof → ProofVersion with Agency and User, extracts agent
RAG statuses from the `agent_review` JSONB column
- backend: `GET /api/export/campaigns-csv` endpoint gated to
super_admin / oversight_admin, streams a dated CSV file
- frontend: `apiService.downloadCampaignsCsv(agencyId?)` — fetches blob
and triggers browser download
- frontend: threads `selectedAgencyId` prop from App → Campaigns →
CampaignList so the export uses the active filter
- frontend: Export CSV button in CampaignList header, visible only to
super_admin / oversight_admin, with spinner while downloading
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POST /api/analyze submits an analysis job and returns job_id instantly.
GET /api/analyze/{job_id} returns progress + result; frontend polls every 2s.
Analysis runs as asyncio.create_task in the background — each HTTP request
completes in milliseconds, well within the 30s GCP Load Balancer limit.
- Add backend/app/services/job_store.py: in-memory AnalysisJob store with
30-min TTL cleanup
- Add backend/app/api/analysis_routes.py: POST + GET /api/analyze endpoints
with full analysis pipeline (hash check, DB persistence, PDF pages, etc.)
- Remove backend/app/websocket/: handlers.py, manager.py, __init__.py
- Update backend/app/main.py: wire analysis_router, store analysis_service
in app.state, drop all WebSocket imports and endpoint
- Update frontend/services/geminiService.ts: replace WS with fetch+poll;
function signatures unchanged so App.tsx / WIPReviewer.tsx need no edits
- Remove VITE_BACKEND_WS_URL from vite.config.ts, deploy.sh, .env.deploy.example
- Update cloudrun.yaml: remove WebSocket-specific session affinity annotation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend now sends client→server ping every 15s during analysis to keep
the GCP LB idle timeout alive from both directions. Backend responds
with pong. Previously only server→client heartbeats were sent, which
didn't reset the proxy's client-side idle timer.
Also updates favicon to Oliver brand mark (gold M).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- index.html: remove broken /index.css link (file never existed, noisy 404)
- index.html: fix favicon path to use %BASE_URL% so Vite rewrites it
correctly for /modcomms/ subpath deployment
- public/favicon.svg: add minimal M favicon (was completely missing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>