Claude Code Brain

WARBLOG BUILD LOG — 2026-03-31 — AUTOMATED MULTI-AGENT BUILD
863
Events scraped
49
Map locations
5
Scrape sources
24
UX/UI Pro packages
14
React components
~20
Agents spawned

Architecture

FrontendReact 19 + Vite + Tailwind CSS v4 + Framer Motion + react-leaflet
BackendFastAPI + SQLAlchemy + SQLite
AI EnrichmentClaude Haiku 4.5 via Anthropic SDK — auto-summarizes articles
ScraperBeautifulSoup + requests, 30min cron, SSRF-hardened
MapLeaflet + CartoDB dark_matter tiles, coordinate-grouped cluster markers
InfraHestiaCP + nginx + systemd + Let's Encrypt SSL
Design systemux-ui-pro (25 packages: marquee-content, cursor-blob, text-slicer, GSAP…)

Agent Execution Pipeline

1 WebFetch × 2
2 3 parallel agents
3 Debug + fix scraper
4 Deploy + SSL
5 Security audit agent
6 UI redesign
7 React rewrite
8 Full audit
9 Dynamic sources
10 Pro features

Step 1 — Research & Scrape

WebFetch Scraped both sources in parallel:

Step 2 — Parallel Build (3 agents)

Three agents launched simultaneously:

Step 3 — Scraper Debug

Svensk Krigare scraper returned 0 articles. Root cause: empty-text <a> tags were added to seen_urls first, blocking the later text-bearing duplicates. Fix: two-pass approach — first collect longest text per URL, then process.

Step 4 — Infrastructure Deploy

Step 5 — Security Audit

security-auditor Background agent reviewed all code. Applied:

Step 6 — UI Iterations

Multiple design passes driven by user feedback:

  1. Initial GitHub-dark theme → user wanted Western Front timeline concept
  2. Warm gold WW1 aesthetic → user wanted modern, not the theme
  3. frontend-design skill "SIGINT Terminal" cyan theme → user wanted red, not cyan
  4. Final: red accent (#ef4444), Outfit/IBM Plex Mono fonts, LIVE ticker, cluster markers with popup timelines

Step 7 — Cluster Markers & Mobile

Events grouped by coordinate. Clicking a cluster shows scrollable timeline popup (desktop) or bottom sheet (mobile). Map touch events blocked while sheet is open via stopPropagation + pointer-events:none.

Step 8 — Deep Scraping + AI Summaries

python-pro Built enrichment pipeline:

Step 9 — Admin Panel

Standalone admin.html at /admin.html. Source management CRUD via /api/admin/sources. Manual scrape trigger.

Step 10 — UX/UI Pro Integration

Explore agent researched all 43 repos by ux-ui-pro on GitHub. Found 24 published to npm:

marquee-content text-slicer cursor-blob liqbg wave-path bubbles-rising cascading-reel coverflow-carousel clicktone easy-confetti btn-kit dialog-lite gridline iconly masonry-simple media-trigger persist-flag typographics snowfall-canvas scratch-reveal wheel-fortune wheel-duo reel-deal zero-hour

All installed to /opt/warblog/node_modules/. UMD builds copied to vendor dir. Integrated: marquee-content (GSAP ticker), cursor-blob (desktop custom cursor), text-slicer (text animation).

Step 11 — React Rewrite

general-purpose Full rewrite to React 19 + Vite + Tailwind:

ComponentPurpose
App.jsxState management, data fetching, view routing
Header.jsxBrand + search + source filter
StatsBar.jsxLive stats dashboard
TabNav.jsxMap / Split / Timeline tabs
MapView.jsxreact-leaflet with coordinate-grouped cluster markers
Timeline.jsxWestern Front-style centered spine with year/month markers
TimelineCard.jsxExpandable card with framer-motion, AI summary display
EventModal.jsxDetail modal with mini-map
BottomSheet.jsxMobile cluster event list
Loading.jsxSpinner overlay

Step 12 — Full Project Audit

Two general-purpose agents launched in parallel:

Issues found and fixed:

#SeverityIssueFix
1MediumBoth uvicorn workers ran scrape+enrich → duplicate Claude API calls, double costAdded fcntl.LOCK_EX file lock — only PRIMARY worker runs scrape/enrich
2Low/api/refresh didn't trigger enrichment after scrapingChanged to call _initial_scrape_and_enrich
3Lowdatetime.utcnow() deprecated in Python 3.12Replaced with datetime.now(timezone.utc)
4LowCORS missing Vite dev server portAdded http://localhost:5173
5LowBuild/deploy mismatch (stale JS/CSS hashes)Rebuilt and redeployed, verified MD5 match

Step 13 — Dynamic Source Scraping

User added Kyiv Independent via admin page but it didn't appear — the admin sources were saved to DB but the scraper was hardcoded and never queried the sources table.

Fix: rewired run_scrape() to query get_active_sources() from DB after running hardcoded scrapers. Added scrape_generic() — a universal scraper that works with any news site:

Result: kyivindependent.com → 22 new articles scraped + AI-summarized. Total: 650 events, 3 sources.

Step 14 — Professional Features (Backend)

fastapi-pro Added production-grade API features:

FeatureEndpointDetail
PaginationGET /api/events?page=1&per_page=50Returns {count, page, per_page, total_pages, events}. Backward compatible — omit page param for full list.
Health checkGET /api/healthUptime, event/source counts, last scrape time, enrichment progress, version
RSS feedGET /api/rssRSS 2.0 XML, latest 50 events with AI summaries
AnalyticsGET /api/analytics/dailyEvent counts grouped by date for charts
Categories?category=militaryAuto-categorized by Claude: military, diplomatic, humanitarian, economic, infrastructure, political, nuclear, weapons

Also expanded location dictionary from 18 to 49 locations (added Kursk, Belgorod, Melitopol, Vovchansk, Robotyne, Lviv, Mykolaiv, Sevastopol, etc.) and added 2 default sources (Ukrinform, Liveuamap).

Step 15 — Professional Features (Frontend)

general-purpose Added React components:

FeatureComponent
Event frequency chartAnalytics.jsx — SVG bar chart, last 60 days, hover tooltips
Category filter pillsCategoryFilter.jsx — color-coded pill buttons, multi-select
Footer with linksFooter.jsx — RSS, Admin, Brain links + keyboard hints
Deep links#event-{id} in URL → shareable event links
Keyboard shortcuts1/2/3 views, / search, r refresh, Esc close
Infinite scrollTimeline loads 50 at a time via IntersectionObserver
Source badge colorsConsistent color per source (Cornucopia=blue, Svensk Krigare=green, Kyiv=yellow)
PWA manifestmanifest.json — installable, standalone, dark theme

File Tree

/opt/warblog/
├── backend/
│   ├── main.py            FastAPI app, endpoints, background scrape
│   ├── database.py        SQLAlchemy models (Event, Source), migrations
│   ├── scraper.py         BeautifulSoup scraper, SSRF protection
│   ├── enricher.py        Deep scrape + Claude AI summaries
│   ├── requirements.txt   Pinned dependencies
│   └── .env               ANTHROPIC_API_KEY
├── frontend-react/
│   ├── src/
│   │   ├── App.jsx        Main layout + state
│   │   ├── index.css      Tailwind + custom styles
│   │   └── components/    14 React components
│   ├── dist/              Production build → deployed to webroot
│   └── package.json       React, Tailwind, Leaflet, Framer Motion
├── node_modules/          25 ux-ui-pro packages + GSAP
├── data/events.db         SQLite (863 events, 49 locations, 5 sources)
├── warblog.service        systemd unit
├── deploy.sh              Deployment script
└── nginx/warblog.conf     Nginx config template

Tech Decisions

Side Project: KLM.com Broken Link Audit

Built a standalone broken link crawler for klm.com at klm.svensk.ai.

Built by Claude Opus 4.6 (1M context) via Claude Code CLI — single conversation session