obsidian/wiki/client-knowledge/barclays.md
2026-04-29 21:46:53 +01:00

7.6 KiB

title description tags created updated
Client Knowledge: Barclays Barclays-specific context: projects, tech constraints, deployment quirks, and lessons learned
client-knowledge
barclays
2026-04-27 2026-04-28

Client Knowledge: Barclays

Key Takeaways

  • Two active projects: Mod Comms (GCP, multi-agent AI) and Banner Builder (optical-dev, React+FastAPI)
  • Barclays requires strict brand compliance — logo versions matter, Barclays design tokens used in UI
  • GCP deployment = no WebSockets — REST polling is mandatory for Mod Comms
  • Banner Builder uses Zustand for workflow state management (journey store pattern)

Projects

Project Server Stack Status Purpose
01 Projects/modcomms/Mod Comms GCP FastAPI + React + Gemini + PostgreSQL active AI proof review — compliance/brand/tone/channel checks
01 Projects/Barclays-banner-builder/Barclays Banner Builder optical-dev FastAPI + React + PostgreSQL + Docker active AI banner generation tool — Brief → Variants → Edit → Export

Mod Comms — Key Facts

What it does: Upload proof (image/PDF) → 4 AI agents analyze in parallel → lead agent synthesizes verdict

4 agents: Legal compliance, Brand adherence, Tone of Voice, Channel suitability

AI: Google Gemini Pro (primary) + Flash (fallback) — chosen for GCP co-location

Critical incident (2026-03-18): WebSocket connections dropped at 30s on GCP LB → switched to REST polling. See wiki/architecture/gcp-deployment-lb-timeout.

Auth: Azure AD (MSAL) — uses DISABLE_AUTH=true locally

Dev start:

# Backend
cd backend && uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

# Frontend
cd frontend && npm install && npm run dev

# DB migrations
cd backend && alembic upgrade head

Env vars (backend):

GEMINI_API_KEY=
DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/modcomms
AZURE_TENANT_ID=
AZURE_CLIENT_ID=
DISABLE_AUTH=true

Banner Builder — Key Facts

What it does: AI-assisted banner creation. Workflow: Brief → Edit Variants → Banner Editor → Export CSV/PDF

Workflow state: Managed with Zustand journey store — backward navigation allowed, forward steps grayed out until completed. See wiki/concepts/export-endpoint-filter-pattern.

Export quirk: PDF/CSV exports must receive variant_ids from frontend — backend cannot infer selection. Always pass explicitly.

Deploy: optical-dev at /barclays-banner-builder/ subpath. Deploy via bash deploy.sh on server.

Apache config: Barclays Include fragment at /opt/barclays-banner-builder/deploy/apache-barclays.conf. Port: 8010.

Critical incident (2026-04-17): Apache Include directive ordering — Banner Builder's conf was loading after hp-prod-tracker's catch-all ProxyPass / http://..., which intercepted all requests. Fixed by reordering Include lines in vhost config.

Stack:

  • Frontend: React + TypeScript + Vite + Zustand
  • Backend: FastAPI + Python + Alembic + PostgreSQL
  • Auth: Azure AD (MSAL)
  • Deploy: Docker Compose + Apache subpath

Brand Requirements

  • Logo versions matter — track which version is active (v4, v5, v6)
  • Barclays design tokens used in UI (Zustand journey stepper used Barclays color tokens)
  • Export outputs go to OMG media booking system — format must be exact

Banner Builder UI Rebrand (2026-04-28)

The banner builder UI was rebranded from the Barclays design system to the Oliver Modcomms design system. Visual layer changed; core functionality unchanged.

What changed:

  • Tailwind tokens renamed from barclays-*oliver-*
  • Layout: horizontal nav → dark vertical sidebar (w-[220px]), matching the Modcomms pattern
  • Theme colour picker added to both BannerEditor and VariantsGrid views; variants have a theme enum: navy | sky-blue | yellow | lime | teal
  • Logo: placeholder CopyGenBannerAgent_RFA.png generated via sips (PIL not available on macOS); real CopyGen/Oliver asset needed

What did NOT change (by design):

  • Internal localStorage key names (barclays-*) intentionally kept to avoid invalidating in-flight user sessions — see wiki/concepts/localstorage-key-migration-rebrand for why and how to handle this in a future release
  • AI Refine/Improve box only mutates copy text fields — cannot change visual theme/colours (by architecture)
  • Barclays brand hex codes (#00AEEF, #00395D, etc.) remain correct in tailwind.config.ts as of commit 47b3f12


Lessons from banner-builder (2026-04-28)

QA session on barclays-banner-builder surfaced three non-obvious bugs. All three share a common trait: they fail silently rather than throwing a clear error.

DB Seed Silent Skip

Seed scripts using INSERT IF NOT EXISTS or get_or_create silently skip existing rows. If a test/demo user was created before the seed ran (e.g., with a different password), the seed never updates it. Auth fails at login with no error in logs — the user row simply has the wrong password.

Fix: Always verify actual DB state when auth fails unexpectedly after seeding:

SELECT email, created_at FROM users WHERE email = 'seed@example.com';
-- If it exists with old data, DELETE and re-run seed, or UPDATE manually

Zustand Async Hydration Bug

ConversationLanding fired an API call on mount before the Zustand auth store had hydrated from localStorage. On first render, token was null (initial state) → API call sent without auth header → 401 → redirect to login, even though the user was logged in.

Fix: Gate all auth-dependent API calls behind hasHydrated:

const { token, hasHydrated } = useAuthStore()
useEffect(() => {
  if (!hasHydrated) return
  fetchData()
}, [hasHydrated, token])

See wiki/concepts/zustand-async-hydration for full pattern.

Pydantic Model/Dict Interface Bug in tasks.py

refine_variant_copy Celery task was written to accept a dict but was called with a BannerCopy Pydantic model. Every .get("field") call on the Pydantic object returned None silently — the task continued running with all-null inputs, hanging the AI refinement pipeline without raising an exception.

Fix: Convert at call site: task.delay(banner_copy.model_dump(), ...) or update the function to accept the Pydantic model directly.

See wiki/concepts/pydantic-model-dict-interface for full pattern.

LoginPage Hardcoded Redirect

LoginPage had a hardcoded redirect to /brief instead of / in the auth success handler. This caused the entire post-login flow to break for users who should land on the home route. Always use a configurable redirect target (e.g., location.state?.from or a constant) rather than a hardcoded path.