mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-26 01:24:59 +02:00
7013b2f9f15413689422425c8e09a4eb13960558
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
97f5aa8af7 |
refactor(proto): extract new Sebuf RPCs from #1399 (lspassos1) (#1888)
* feat(proto): add new RPCs for satellites, oref alerts, telegram, GPS interference, company enrichment Extracted from #1399 (originally by @lspassos1). Adds 12 new proto message/service files and updates service.proto for intelligence, aviation, and infrastructure domains. Intelligence: - ListSatellites + satellite.proto (TLE orbit data) - ListOrefAlerts (Israeli Home Front Command alerts) - ListTelegramFeed (Telegram intelligence feed) - ListGpsInterference + gps_jamming.proto - GetCompanyEnrichment (GitHub/SEC/HN enrichment) - ListCompanySignals Aviation: - GetYoutubeLiveStreamInfo Infrastructure: - GetBootstrapData - GetIpGeo - ReverseGeocode Co-authored-by: Lucas Passos <lspassos1@users.noreply.github.com> * chore(proto): regenerate clients/servers/openapi after new RPC additions * fix(proto): restore GetCountryFacts and ListSecurityAdvisories RPCs removed by contributor * chore(handlers): add stub implementations for new proto RPCs * fix(handler): correct stub shapes for GetCompanyEnrichment and ListCompanySignals * fix(proto): fix handler stub shapes for listOrefAlerts, listTelegramFeed, listGpsInterference * fix(proto): fix remaining handler stub shapes for aviation and infrastructure * fix(proto): add cache tier entries for new generated GET routes, remove stale classify-event entry * fix(pr1888): restore rpc contracts and real handlers * fix(oref): read history from Redis instead of re-calling relay relay:oref:history:v1 is seeded by ais-relay on every poll cycle. History mode now reads directly from Redis (no relay hit). Live alerts still call relay (in-memory only), with Redis counts as fallback. * fix(gateway): change youtube-live-stream-info tier from no-store to fast Matches existing api/youtube/live.js which caches at s-maxage=600. fast tier = s-maxage=300 stale-while-revalidate=60 — appropriate for live detection that changes at most every few minutes. * fix(geocode): await setCachedJson to prevent edge isolate termination race * fix(youtube): use CHROME_UA constant in fallback fetch paths * fix(pr1888): address P1/P2 review findings - gateway: oref+telegram slow->fast (matches legacy s-maxage=300/120) - gateway: ip-geo slow->no-store (per-request user data, must not share) - list-gps-interference: recompute stats from filtered hexes when region filter active - get-company-enrichment: throw ValidationError(400) on missing domain+name - list-company-signals: throw ValidationError(400) on missing company * fix(validation): use FieldViolation.description, remove unused buildEmptyResponse --------- Co-authored-by: Lucas Passos <lspassos1@users.noreply.github.com> Co-authored-by: lspassos1 <lspassos@icloud.com> |
||
|
|
45f5e5a457 |
feat(forecast): AI Forecasts prediction module (#1579)
* feat(forecast): add AI Forecasts prediction module (Pro-tier)
MiroFish-inspired prediction engine that generates structured forecasts
across 6 domains (conflict, market, supply chain, political, military,
infrastructure) using existing WorldMonitor data streams.
- Proto definitions for ForecastService with GetForecasts RPC
- Dedicated seed script (seed-forecasts.mjs) with 6 domain detectors,
cross-domain cascade resolver, prediction market calibration, and
trend detection via prior snapshot comparison
- Premium-gated RPC handler (PREMIUM_RPC_PATHS enforcement)
- Lazy-loaded ForecastPanel with domain filters, probability bars,
trend arrows, signal evidence, and cascade links
- Health monitoring integration (seed-meta freshness tracking)
- Refresh scheduler with API key guard
* test(forecast): add 47 unit tests for forecast detectors and utilities
Covers forecastId, normalize, resolveCascades, calibrateWithMarkets,
computeTrends, and smoke tests for all 6 domain detectors. Exports
testable functions from seed script with direct-run guard.
* fix(forecast): domain mismatch 'infra' vs 'infrastructure', add panel category
- Seed script used 'infra' but ForecastPanel filtered on 'infrastructure',
causing Infra tab to show zero results
- Added 'forecast' to intelligence category in PANEL_CATEGORY_MAP
* fix(forecast): move CSS to one-time injection, improve type safety
- P2: Move style block from setContent to one-time document.head injection
to prevent CSS accumulation on repeated renders
- P3: Replace +toFixed(3) with Math.round for readability in seed script
- P3: Use Forecast type instead of any[] in RPC handler filter
* fix(forecast): handle sebuf proto data shapes from Redis
Detectors now normalize CII scores from server-side proto format
(combinedScore, TREND_DIRECTION_RISING, region) to uniform shape.
Outage severity handles proto enum format (SEVERITY_LEVEL_HIGH).
Added confidence floor of 0.3 for single-source predictions.
Verified against live Redis: 2 predictions generated (Iran infra
shutdown, IL political instability).
* feat(forecast): unlock AI Forecasts on web, lock desktop only (trial)
- Remove forecast RPC from PREMIUM_RPC_PATHS (web access is free)
- Panel locked on desktop only (same as oref-sirens/telegram-intel)
- Remove API key guards from data-loader and refresh scheduler
- Web users get full access during trial period
* chore: regenerate proto types with make generate
Re-ran make generate after rebasing on main. Plugin v0.7.0 dropped
@ts-nocheck from output, added it back to all 50 generated files.
Fixed 4 type errors from proto codegen changes:
- MarketSource enum -> string union type
- TemporalAnomalyProto -> TemporalAnomaly rename
- webcam lastUpdated number -> string
* fix(forecast): use chokepoints v4 key, include ciiContribution in unrest
- P1: Switch chokepoints input from stale v2 to active v4 Redis key,
matching bootstrap.js and cache-keys.ts
- P2: Add ciiContribution to unrest component fallback chain in
normalizeCiiEntry so political detector reads the correct sebuf field
* feat(forecast): Phase 2 LLM scenario enrichment + confidence model
MiroFish-inspired enhancements:
- LLM scenario narratives via Groq/OpenRouter (narrative-only, no numeric
adjustment). Evidence-grounded prompts with mandatory signal citation
and few-shot examples from MiroFish's SECTION_SYSTEM_PROMPT_TEMPLATE.
- Top-4 predictions batched into single LLM call for cost efficiency.
- News context from newsInsights attached to all predictions for LLM
prompt grounding (NOT in signals, cannot affect confidence).
- Deterministic confidence model: source diversity via SIGNAL_TO_SOURCE
mapping (deduplicates cii+cii_delta, theater+indicators) + calibration
agreement from prediction market drift. Floor 0.2, ceiling 1.0.
- Output validation: rejects scenarios without signal references.
- Truncated JSON repair for small model output.
- Structured JSON logging for LLM calls.
- Redis cache for LLM scenarios (1h TTL).
- 23 new tests (70 total), all passing.
- Live-tested: OpenRouter gemini-2.5-flash produces evidence-grounded
scenario narratives from real WorldMonitor data.
* feat(forecast): Phase 3 multi-perspective scenarios, projections, data-driven cascades
MiroFish-inspired enhancements:
- Multi-perspective LLM analysis: top-2 predictions get strategic,
regional, and contrarian viewpoints via combined LLM call
- Probability projections: domain-specific decay curves (h24/d7/d30)
anchored to timeHorizon so probability equals projections[timeHorizon]
- Data-driven cascade rules: moved from hardcoded array to JSON config
(scripts/data/cascade-rules.json) with schema validation, named
predicate evaluators, unknown key rejection, and fallback to defaults
- 4 new cascade paths: infrastructure->supply_chain, infrastructure->market
(both requiresSeverity:total), conflict->political, political->market
- Proto: added Perspectives and Projections messages to Forecast
- ForecastPanel: renders projections row and conditional perspectives toggle
- 89 tests (19 new), all passing
- Live-tested: OpenRouter produces perspectives from real data
* feat(forecast): Phase 4 data utilization + entity graph
Fixes data gaps that prevented 4 of 6 detectors from firing:
- Input normalizers: chokepoint v4 shape + GPS hexes-to-zones mapping
- Chokepoint warm-ping (production-only, requires WM_API_BASE_URL)
- Lowered CII conflict threshold from 70 to 60, gated on level=high|critical
4 new standalone detectors:
- UCDP conflict zones (10+ events per country)
- Cyber threat concentration (5+ threats per country)
- GPS jamming in maritime shipping zones (5 regions)
- Prediction markets as signals (60-90% probability markets)
Entity-relationship graph (file-based, 38 nodes):
- Countries, theaters, commodities, chokepoints, alliances
- Alias table resolves both ISO codes and display names
- Graph cascade discovery links predictions across entities
Result: 51 predictions (up from 1-2), spanning conflict, infrastructure,
and supply chain domains. 112 tests, all passing.
* fix(forecast): redis cache format, signal source mapping, type safety
Fresh-eyes audit fixes:
- BUG: redisSet used wrong Upstash API format (POST body with {value,ex}
instead of command array ['SET',key,value,'EX',ttl]). LLM cache writes
were silently failing, causing fresh LLM calls every run.
- BUG: prediction_market signal type missing from SIGNAL_TO_SOURCE,
inflating confidence for market-derived predictions.
- CLEANUP: Remove unnecessary (f as any) casts in ForecastPanel since
generated Forecast type already has projections/perspectives fields.
- CLEANUP: Bump health maxStaleMin from 60 to 90 to avoid false STALE
alerts when LLM calls add latency to seed runs.
* feat(forecast): headline-entity matching with news corroboration signals
Uses entity graph aliases to match headlines to predictions by
country/theater (excludes commodity/infrastructure nodes to prevent
false positives). Predictions with matching headlines get a
news_corroboration signal visible in the panel.
Also fixes buildUserPrompt to merge unique headlines from ALL
predictions in the LLM batch (was only reading preds[0].newsContext).
Live-tested: 13 of 51 predictions now have corroborating headlines
(Iran, Israel, Syria, Ukraine, etc). 116 tests, all passing.
* feat(forecast): add country-codes.json for headline-entity matching
56 countries with ISO codes, full names, and scoring keywords (extracted
from src/config/countries.ts + UCDP-relevant additions). Used by
attachNewsContext for richer headline matching via getSearchTermsForRegion
which combines country-codes + entity graph + keyword aliases.
14/57 predictions now have news corroboration (limited by headline
coverage, not matching quality: only 8 headlines currently available).
* feat(forecast): read 300 headlines from news digest instead of 8
Read news:digest:v1:full:en (300 headlines across 16 categories) instead
of just news:insights:v1 topStories (8 headlines). Fallback to topStories
if digest is unavailable.
Result: news corroboration jumped from 25% to 64% (38/59 predictions).
* fix(forecast): handle parenthetical country names in headline matching
Strip suffixes like '(Zaire)', '(Burma)', '(Soviet Union)' from UCDP
region names before matching against country-codes.json. Also use
includes() for reverse name lookup to catch partial matches.
Corroboration: 64% -> 69% (41/59). Remaining 18 unmatched are countries
with no current English-language news coverage.
* fix(forecast): cache validated LLM output, add digest test, log cache errors
Fresh-eyes audit fixes:
- Combined LLM cache now stores only validated items (was caching raw
unvalidated output, serving potentially invalid scenarios on cache hit)
- redisSet logs warnings on failure (was silently swallowing all errors)
- Added digest-based test for attachNewsContext (primary path was untested)
- Fixed test arity: attachNewsContext(preds, news, digest) with 3 params
* fix(forecast): remove dead confidenceFromSources, reduce warm-ping timeout
- P2: Remove confidenceFromSources (dead code, computeConfidence overwrites
all initial confidence values). Inline the formula in original detectors.
- P3: Reduce warm-ping timeout from 30s to 15s (non-critical step)
- P3: Add trial status comment on forecast panel config
* fix(forecast): resolve ISO codes to country names, fix market detector, safe pre-push
P1 fixes from code review:
- CII ISO codes (IL, IR) now resolved to full country names (Israel, Iran)
via country-codes.json. Prevents substring false positives (IL matching
Chile) in event correlation. Uses word-boundary regex for matching.
- Market detector CII-to-theater mapping now uses entity graph traversal
instead of broken theater-name substring matching. Iran correctly maps
to Middle East theater via graph links.
- Pre-push hook no longer runs destructive git checkout on proto freshness
failure. Reports mismatch and exits without modifying worktree.
|
||
|
|
a7efa7dda8 |
feat: implement deduct situation feature (#636) (#642)
* Add Security Advisories panel with government travel alerts (#460) * feat: add Security Advisories panel with government travel advisory feeds Adds a new panel aggregating travel/security advisories from official government foreign affairs agencies (US State Dept, AU DFAT Smartraveller, UK FCDO, NZ MFAT). Advisories are categorized by severity level (Do Not Travel, Reconsider, Caution, Normal) with filter tabs by source country. Includes summary counts, auto-refresh, and persistent caching via the existing data-freshness system. * chore: update package-lock.json * fix: event delegation, localization, and cleanup for SecurityAdvisories panel P1 fixes: - Use event delegation on this.content (bound once in constructor) instead of direct addEventListener after each innerHTML replacement — prevents memory leaks and stale listener issues on re-render - Use setContent() consistently instead of mixing with this.content.innerHTML - Add securityAdvisories translations to all 16 non-English locale files (panels name, component strings, common.all key) - Revert unrelated package-lock.json version bump P2 fixes: - Deduplicate loadSecurityAdvisories — loadIntelligenceData now calls the shared method instead of inlining duplicate fetch+set logic - Add Accept header to fetch calls for better content negotiation * feat(advisories): add US embassy alerts, CDC, ECDC, and WHO health feeds Adds 21 new advisory RSS feeds: - 13 US Embassy per-country security alerts (TH, AE, DE, UA, MX, IN, PK, CO, PL, BD, IT, DO, MM) - CDC Travel Notices - 5 ECDC feeds (epidemiological, threats, risk assessments, avian flu, publications) - 2 WHO feeds (global news, Africa emergencies) Panel gains a Health filter tab for CDC/ECDC/WHO sources. All new domains added to RSS proxy allowlist. i18n "health" key added across all 17 locales. * feat(cache): add negative-result caching to cachedFetchJson (#466) When upstream APIs return errors (HTTP 403, 429, timeout), fetchers return null. Previously null results were not cached, causing repeated request storms against broken APIs every refresh cycle. Now caches a sentinel value ('__WM_NEG__') with a short 2-minute TTL on null results. Subsequent requests within that window get null immediately without hitting upstream. Thrown errors (transient) skip sentinel caching and retry immediately. Also filters sentinels from getCachedJsonBatch pipeline reads and fixes theater posture coalescing test (expected 2 OpenSky fetches for 2 theater query regions, not 1). * feat: convert 52 API endpoints from POST to GET for edge caching (#468) * feat: convert 52 API endpoints from POST to GET for edge caching Convert all cacheable sebuf RPC endpoints to HTTP GET with query/path parameters, enabling CDN edge caching to reduce costs. Flatten nested request types (TimeRange, PaginationRequest, BoundingBox) into scalar query params. Add path params for resource lookups (GetFredSeries, GetHumanitarianSummary, GetCountryStockIndex, GetCountryIntelBrief, GetAircraftDetails). Rewrite router with hybrid static/dynamic matching for path param support. Kept as POST: SummarizeArticle, ClassifyEvent, RecordBaselineSnapshot, GetAircraftDetailsBatch, RegisterInterest. Generated with sebuf v0.9.0 (protoc-gen-ts-client, protoc-gen-ts-server). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add rate_limited field to market response protos The rateLimited field was hand-patched into generated files on main but never declared in the proto definitions. Regenerating wiped it out, breaking the build. Now properly defined in both ListEtfFlowsResponse and ListMarketQuotesResponse protos. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove accidentally committed .planning files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Cloudflare edge caching infrastructure for api.worldmonitor.app (#471) Route web production RPC traffic through api.worldmonitor.app via fetch interceptor (installWebApiRedirect). Add default Cache-Control headers (s-maxage=300, stale-while-revalidate=60) on GET 200 responses, with no-store override for real-time endpoints (vessel snapshot). Update CORS to allow GET method. Skip Vercel bot middleware for API subdomain using hostname check (non-spoofable, replacing CF-Ray header approach). Update desktop cloud fallback to route through api.worldmonitor.app. * fix(beta): eagerly load T5-small model when beta mode is enabled BETA_MODE now couples the badge AND model loading — the summarization-beta model starts loading on startup instead of waiting for the first summarization call. * fix: move 5 path-param endpoints to query params for Vercel routing (#472) Vercel's `api/[domain]/v1/[rpc].ts` captures one dynamic segment. Path params like `/get-humanitarian-summary/SA` add an extra segment that has no matching route file, causing 404 on both OPTIONS preflight and direct requests. These endpoints were broken in production. Changes: - Remove `{param}` from 5 service.proto HTTP paths - Add `(sebuf.http.query)` annotations to request message fields - Update generated client/server code to use URLSearchParams - Update OpenAPI specs (YAML + JSON) to declare query params - Add early-return guards in 4 handlers for missing required params - Add happy.worldmonitor.app to runtime.ts redirect hosts Affected endpoints: - GET /api/conflict/v1/get-humanitarian-summary?country_code=SA - GET /api/economic/v1/get-fred-series?series_id=T10Y2Y&limit=120 - GET /api/market/v1/get-country-stock-index?country_code=US - GET /api/intelligence/v1/get-country-intel-brief?country_code=US - GET /api/military/v1/get-aircraft-details?icao24=a12345 * fix(security-advisories): route feeds through RSS proxy to avoid CORS blocks (#473) - Advisory feeds were fetched directly from the browser, hitting CORS on all 21 feeds (US State Dept, AU Smartraveller, US Embassies, ECDC, CDC, WHO). Route through /api/rss-proxy on web, keep proxyUrl for desktop. - Fix double slash in ECDC Avian Influenza URL (323//feed → 323/feed) - Add feeds.news24.com to RSS proxy allowlist (was returning 403) * feat(cache): tiered edge Cache-Control aligned to upstream TTLs (#474) * fix: move 5 path-param endpoints to query params for Vercel routing Vercel's `api/[domain]/v1/[rpc].ts` captures one dynamic segment. Path params like `/get-humanitarian-summary/SA` add an extra segment that has no matching route file, causing 404 on both OPTIONS preflight and direct requests. These endpoints were broken in production. Changes: - Remove `{param}` from 5 service.proto HTTP paths - Add `(sebuf.http.query)` annotations to request message fields - Update generated client/server code to use URLSearchParams - Update OpenAPI specs (YAML + JSON) to declare query params - Add early-return guards in 4 handlers for missing required params - Add happy.worldmonitor.app to runtime.ts redirect hosts Affected endpoints: - GET /api/conflict/v1/get-humanitarian-summary?country_code=SA - GET /api/economic/v1/get-fred-series?series_id=T10Y2Y&limit=120 - GET /api/market/v1/get-country-stock-index?country_code=US - GET /api/intelligence/v1/get-country-intel-brief?country_code=US - GET /api/military/v1/get-aircraft-details?icao24=a12345 * feat(cache): add tiered edge Cache-Control aligned to upstream TTLs Replace flat s-maxage=300 with 5 tiers (fast/medium/slow/static/no-store) mapped per-endpoint to respect upstream Redis TTLs. Adds stale-if-error resilience headers and X-No-Cache plumbing for future degraded responses. X-Cache-Tier debug header gated behind ?_debug query param. * fix(tech): use rss() for CISA feed, drop build from pre-push hook (#475) - CISA Advisories used dead rss.worldmonitor.app domain (404), switch to rss() helper - Remove Vite build from pre-push hook (tsc already catches errors) * fix(desktop): enable click-to-play YouTube embeds + CISA feed fixes (#476) * fix(tech): use rss() for CISA feed, drop build from pre-push hook - CISA Advisories used dead rss.worldmonitor.app domain (404), switch to rss() helper - Remove Vite build from pre-push hook (tsc already catches errors) * fix(desktop): enable click-to-play for YouTube embeds in WKWebView WKWebView blocks programmatic autoplay in cross-origin iframes regardless of allow attributes, Permissions-Policy, mute-first retries, or secure context. Documented all 10 approaches tested in docs/internal/. Changes: - Switch sidecar embed origin from 127.0.0.1 to localhost (secure context) - Add MutationObserver + retry chain as best-effort autoplay attempts - Use postMessage('*') to fix tauri://localhost cross-origin messaging - Make sidecar play overlay non-interactive (pointer-events:none) - Fix .webcam-iframe pointer-events:none blocking clicks in grid view - Add expand button to grid cells for switching to single view on desktop - Add http://localhost:* to CSP frame-src in index.html and tauri.conf.json * fix(gateway): convert stale POST requests to GET for backwards compat (#477) Stale cached client bundles still send POST to endpoints converted to GET in PR #468, causing 404s. The gateway now parses the POST JSON body into query params and retries the match as GET. * feat(proxy): add Cloudflare edge caching for proxy.worldmonitor.app (#478) Add CDN-Cache-Control headers to all proxy endpoints so Cloudflare can cache responses at the edge independently of browser Cache-Control: - RSS: 600s edge + stale-while-revalidate=300 (browser: 300s) - UCDP: 3600s edge (matches browser) - OpenSky: 15s edge (browser: 30s) for fresher flight data - WorldBank: 1800s/86400s edge (matches browser) - Polymarket: 120s edge (matches browser) - Telegram: 10s edge (matches browser) - AIS snapshot: 2s edge (matches browser) Also fixes: - Vary header merging: sendCompressed/sendPreGzipped now merge existing Vary: Origin instead of overwriting, preventing cross-origin cache poisoning at the edge - Stale fallback responses (OpenSky, WorldBank, Polymarket, RSS) now set Cache-Control: no-store + CDN-Cache-Control: no-store to prevent edge caching of degraded responses - All no-cache branches get CDN-Cache-Control: no-store - /opensky-reset gets no-store (state-changing endpoint) * fix(sentry): add noise filters for 4 unresolved issues (#479) - Tighten AbortError filter to match "AbortError: The operation was aborted" - Filter "The user aborted a request" (normal navigation cancellation) - Filter UltraViewer service worker injection errors (/uv/service/) - Filter Huawei WebView __isInQueue__ injection * feat: configurable VITE_WS_API_URL + harden POST→GET shim (#480) * fix(gateway): harden POST→GET shim with scalar guard and size limit - Only convert string/number/boolean values to query params (skip objects, nested arrays, __proto__ etc.) to prevent prototype pollution vectors - Skip body parsing for Content-Length > 1MB to avoid memory pressure * feat: make API base URL configurable via VITE_WS_API_URL Replace hardcoded api.worldmonitor.app with VITE_WS_API_URL env var. When empty, installWebApiRedirect() is skipped entirely — relative /api/* calls stay on the same domain (local installs). When set, browser fetch is redirected to that URL. Also adds VITE_WS_API_URL and VITE_WS_RELAY_URL hostnames to APP_HOSTS allowlist dynamically. * fix(analytics): use greedy regex in PostHog ingest rewrites (#481) Vercel's :path* wildcard doesn't match trailing slashes that PostHog SDK appends (e.g. /ingest/s/?compression=...), causing 404s. Switch to :path(.*) which matches all path segments including trailing slashes. Ref: PostHog/posthog#17596 * perf(proxy): increase AIS snapshot edge TTL from 2s to 10s (#482) With 20k requests/30min (60% of proxy traffic) and per-PoP caching, a 2s edge TTL expires before the next request from the same PoP arrives, resulting in near-zero cache hits. 10s allows same-PoP dedup while keeping browser TTL at 2s for fresh vessel positions. * fix(markets): commodities panel showing stocks instead of commodities (#483) The shared circuit breaker (cacheTtlMs: 0) cached the stocks response, then the stale-while-revalidate path returned that cached stocks data for the subsequent commodities fetch. Skip SWR when caching is disabled. * feat(gateway): complete edge cache tier coverage + degraded-response policy (#484) - Add 11 missing GET routes to RPC_CACHE_TIER map (8 slow, 3 medium) - Add response-headers side-channel (WeakMap) so handlers can signal X-No-Cache without codegen changes; wire into military-flights and positive-geo-events handlers on upstream failure - Add env-controlled per-endpoint tier override (CACHE_TIER_OVERRIDE_*) for incident response rollback - Add VITE_WS_API_URL hostname allowlist (*.worldmonitor.app + localhost) - Fix fetch.bind(globalThis) in positive-events-geo.ts (deferred lambda) - Add CI test asserting every generated GET route has an explicit cache tier entry (prevents silent default-tier drift) * chore: bump version to 2.5.20 + changelog Covers PRs #452–#484: Cloudflare edge caching, commodities SWR fix, security advisories panel, settings redesign, 52 POST→GET migrations. * fix(rss): remove stale indianewsnetwork.com from proxy allowlist (#486) Feed has no <pubDate> fields and latest content is from April 2022. Not referenced in any feed config — only in the proxy domain allowlist. * feat(i18n): add Korean (한국어) localization (#487) - Add ko.json with all 1606 translation keys matching en.json structure - Register 'ko' in SUPPORTED_LANGUAGES, LANGUAGES display array, and locale map - Korean appears as 🇰🇷 한국어 in the language dropdown * feat: add Polish tv livestreams (#488) * feat(rss): add Axios (api.axios.com/feed) as US news source (#494) Add api.axios.com to proxy allowlist and CSP connect-src, register Axios feed under US category as Tier 2 mainstream source. * perf: bootstrap endpoint + polling optimization (#495) * perf: bootstrap endpoint + polling optimization (phases 3-4) Replace 15+ individual RPC calls on startup with a single /api/bootstrap batch call that fetches pre-cached data from Redis. Consolidate 6 panel setInterval timers into the central RefreshScheduler for hidden-tab awareness (10x multiplier) and adaptive backoff (up to 4x for unchanged data). Convert IntelligenceGapBadge from 10s polling to event-driven updates with 60s safety fallback. * fix(bootstrap): inline Redis + cache keys in edge function Vercel Edge Functions cannot resolve cross-directory TypeScript imports from server/_shared/. Inline getCachedJsonBatch and BOOTSTRAP_CACHE_KEYS directly in api/bootstrap.js. Add sync test to ensure inlined keys stay in sync with the canonical server/_shared/cache-keys.ts registry. * test: add Edge Function module isolation guard for all api/*.js files Prevents any Edge Function from importing from ../server/ or ../src/ which breaks Vercel builds. Scans all 12 non-helper Edge Functions. * fix(bootstrap): read unprefixed cache keys on all environments Preview deploys set VERCEL_ENV=preview which caused getKeyPrefix() to prefix Redis keys with preview:<sha>:, but handlers only write to unprefixed keys on production. Bootstrap is a read-only consumer of production cache — always read unprefixed keys. * fix(bootstrap): wire sectors hydration + add coverage guard - Wire getHydratedData('sectors') in data-loader to skip Yahoo Finance fetch when bootstrap provides sector data - Add test ensuring every bootstrap key has a getHydratedData consumer — prevents adding keys without wiring them * fix(server): resolve 25 TypeScript errors + add server typecheck to CI - _shared.ts: remove unused `delay` variable - list-etf-flows.ts: add missing `rateLimited` field to 3 return literals - list-market-quotes.ts: add missing `rateLimited` field to 4 return literals - get-cable-health.ts: add non-null assertions for regex groups and array access - list-positive-geo-events.ts: add non-null assertion for array index - get-chokepoint-status.ts: add required fields to request objects - CI: run `typecheck:api` (tsconfig.api.json) alongside `typecheck` to catch server/ TS errors before merge * feat(military): server-side military bases 125K + rate limiting (#496) * feat(military): server-side military bases with 125K entries + rate limiting (#485) Migrate military bases from 224 static client-side entries to 125,380 server-side entries stored in Redis GEO sorted sets, served via bbox-filtered GEOSEARCH endpoint with server-side clustering. Data pipeline: - Pizzint/Polyglobe: 79,156 entries (Supabase extraction) - OpenStreetMap: 45,185 entries - MIRTA: 821 entries - Curated strategic: 218 entries - 277 proximity duplicates removed Server: - ListMilitaryBases RPC with GEOSEARCH + HMGET + tier/filter/clustering - Antimeridian handling (split bbox queries) - Blue-green Redis deployment with atomic version pointer switch - geoSearchByBox() + getHashFieldsBatch() helpers in redis.ts Security: - @upstash/ratelimit: 60 req/min sliding window per IP - IP spoofing fix: prioritize x-real-ip (Vercel-injected) over x-forwarded-for - Require API key for non-browser requests (blocks unauthenticated curl/scripts) - Input validation: allowlisted types/kinds, regex country, clamped bbox/zoom Frontend: - Viewport-driven loading with bbox quantization + debounce - Server-side grid clustering at low zoom levels - Enriched popup with kind, category badges (airforce/naval/nuclear/space) - Static 224 bases kept as search fallback + initial render * fix(military): fallback to production Redis keys in preview deployments Preview deployments prefix Redis keys with `preview:{sha}:` but military bases data is seeded to unprefixed (production) keys. When the prefixed `military:bases:active` key is missing, fall back to the unprefixed key and use raw (unprefixed) keys for geo/meta lookups. * fix: remove unused 'remaining' destructure in rate-limit (TS6133) * ci: add typecheck:api to pre-push hook to catch server-side TS errors * debug(military): add X-Bases-Debug response header for preview diagnostics * fix(bases): trigger initial server fetch on map load fetchServerBases() was only called on moveend — if the user never panned/zoomed, the API was never called and only the 224 static fallback bases showed. * perf(military): debounce base fetches + upgrade edge cache to static tier (#497) - Add 300ms debounce on moveend to prevent rapid pan flooding - Fixes stale-bbox bug where pendingFetch returns old viewport data - Upgrade edge cache tier from medium (5min) to static (1hr) — bases are static infrastructure, aligned with server-side cachedFetchJson TTL - Keep error logging in catch blocks for production diagnostics * fix(cyber): make GeoIP centroid fallback jitter deterministic (#498) Replace Math.random() jitter with DJB2 hash seeded by the threat indicator (IP/URL), so the same threat always maps to the same coordinates across requests while different threats from the same country still spread out. Closes #203 Co-authored-by: Chris Chen <fuleinist@users.noreply.github.com> * fix: use cross-env for Windows-compatible npm scripts (#499) Replace direct `VAR=value command` syntax with cross-env/cross-env-shell so dev, build, test, and desktop scripts work on Windows PowerShell/CMD. Co-authored-by: facusturla <facusturla@users.noreply.github.com> * feat(live-news): add CBC News to optional North America channels (#502) YouTube handle @CBCNews with fallback video ID 5vfaDsMhCF4. * fix(bootstrap): harden hydration cache + polling review fixes (#504) - Filter null/undefined values before storing in hydration cache to prevent future consumers using !== undefined from misinterpreting null as valid data - Debounce wm:intelligence-updated event handler via requestAnimationFrame to coalesce rapid alert generation into a single render pass - Include alert IDs in StrategicRiskPanel change fingerprint so content changes are detected even when alert count stays the same - Replace JSON.stringify change detection in ServiceStatusPanel with lightweight name:status fingerprint - Document max effective refresh interval (40x base) in scheduler * fix(geo): tokenization-based keyword matching to prevent false positives (#503) * fix(geo): tokenization-based keyword matching to prevent false positives Replace String.includes() with tokenization-based Set.has() matching across the geo-tagging pipeline. Prevents false positives like "assad" matching inside "ambassador" and "hts" matching inside "rights". - Add src/utils/keyword-match.ts as single source of truth - Decompose possessives/hyphens ("Assad's" → includes "assad") - Support multi-word phrase matching ("white house" as contiguous) - Remove false-positive-prone DC keywords ('house', 'us ') - Update 9 consumer files across geo-hub, map, CII, and asset systems - Add 44 tests covering false positives, true positives, edge cases Co-authored-by: karim <mirakijka@gmail.com> Fixes #324 * fix(geo): add inflection suffix matching + fix test imports Address code review feedback: P1a: Add suffix-aware matching for plurals and demonyms so existing keyword lists don't regress (houthi→houthis, ukraine→ukrainian, iran→iranian, israel→israeli, russia→russian, taiwan→taiwanese). Uses curated suffix list + e-dropping rule to avoid false positives. P1b: Expand conflictTopics arrays in DeckGLMap and Map with demonym forms so "Iranian senate..." correctly registers as conflict topic. P2: Replace inline test functions with real module import via tsx. Tests now exercise the production keyword-match.ts directly. * fix: wire geo-keyword tests into test:data command The .mts test file wasn't covered by `node --test tests/*.test.mjs`. Add `npx tsx --test tests/*.test.mts` so test:data runs both suites. * fix: cross-platform test:data + pin tsx in devDependencies - Use tsx as test runner for both .mjs and .mts (single invocation) - Removes ; separator which breaks on Windows cmd.exe - Add tsx to devDependencies so it works in offline/CI environments * fix(geo): multi-word demonym matching + short-keyword suffix guard - Add wordMatches() for suffix-aware phrase matching so "South Korean" matches keyword "south korea" and "North Korean" matches "north korea" - Add MIN_SUFFIX_KEYWORD_LEN=4 guard so short keywords like "ai", "us", "hts" only do exact-match (prevents "ais"→"ai", "uses"→"us" false positives) - Add 5 new tests covering both fixes (58 total, all passing) * fix(geo): support plural demonyms in keyword matching Add compound suffixes (ians, eans, ans, ns, is) to handle plural demonym forms like "Iranians"→"iran", "Ukrainians"→"ukraine", "Russians"→"russia", "Israelis"→"israel". Adds 5 new tests (63 total). --------- Co-authored-by: karim <mirakijka@gmail.com> * chore: strip 61 debug console.log calls from 20 service files (#501) * chore: strip 61 debug console.log calls from services Remove development/tracing console.log statements from 20 files. These add noise to production browser consoles and increase bundle size. Preserved: all console.error (error handling) and console.warn (warnings). Preserved: debug-gated logs in runtime.ts (controlled by verbose flag). Removed: debugInjectTestEvents() from geo-convergence.ts (test-only code). Removed: logSummary()/logReport() methods that were pure console.log wrappers. * fix: remove orphaned stubs and remaining debug logs from stripped services - Remove empty logReport() method and unused startTime variable (parallel-analysis.ts) - Remove orphaned console.group/console.groupEnd pair (parallel-analysis.ts) - Remove empty logSignalSummary() export (signal-aggregator.ts) - Remove logSignalSummary import/call and 3 remaining console.logs (InsightsPanel.ts) - Remove no-op logDirectFetchBlockedOnce() and dead infrastructure (prediction/index.ts) * fix: generalize Vercel preview origin regex + include filters in bases cache key (#506) - api/_api-key.js: preview URL pattern was user-specific (-elie-), rejecting other collaborators' Vercel preview deployments. Generalized to match any worldmonitor-*.vercel.app origin. - military-bases.ts: client cache key only checked bbox/zoom, ignoring type/kind/country filters. Switching filters without panning returned stale results. Unified into single cacheKey string. * fix(prediction): filter stale/expired markets from Polymarket panel (#507) Prediction panel was showing expired markets (e.g. "Will US strike Iran on Feb 9" at 0%). Root causes: no active/archived API filters, no end_date_min param, no client-side expiry guard, and sub-market selection picking highest volume before filtering expired ones. - Add active=true, archived=false, end_date_min API params to all 3 Gamma API call sites (events, markets, probe) - Pre-filter sub-markets by closed/expired BEFORE volume selection in both fetchPredictions() and fetchCountryMarkets() - Add defense-in-depth isExpired() client-side filter on final results - Propagate endDate through all market object paths including sebuf fallback - Show expiry date in PredictionPanel UI with new .prediction-meta layout - Add "closes" i18n key to all 18 locale files - Add endDate to server handler GammaMarket/GammaEvent interfaces and map to proto closesAt field * fix(relay): guard proxy handlers against ERR_HTTP_HEADERS_SENT crash (#509) Polymarket and World Bank proxy handlers had unguarded res.writeHead() calls in error/timeout callbacks that race with the response callback. When upstream partially responds then times out, both paths write headers → process crash. Replace 5 raw writeHead+end calls with safeEnd() which checks res.headersSent before writing. * feat(breaking-news): add active alert banner with audio for critical/high RSS items (#508) RSS items classified as critical/high threat now trigger a full-width breaking news banner with audio alert, auto-dismiss (60s/30s by severity), visibility-aware timer pause, dedup, and a toggle in the Intelligence Findings dropdown. * fix(sentry): filter Android OEM WebView bridge injection errors (#510) Add ignoreErrors pattern for LIDNotifyId, onWebViewAppeared, and onGetWiFiBSSID — native bridge functions injected by Lenovo/Huawei device SDKs into Chrome Mobile WebView. No stack frames in our code. * chore: add validated telegram channels list (global + ME + Iran + cyber) (#249) * feat(conflict): add Iran Attacks map layer + strip debug logs (#511) * chore: strip 61 debug console.log calls from services Remove development/tracing console.log statements from 20 files. These add noise to production browser consoles and increase bundle size. Preserved: all console.error (error handling) and console.warn (warnings). Preserved: debug-gated logs in runtime.ts (controlled by verbose flag). Removed: debugInjectTestEvents() from geo-convergence.ts (test-only code). Removed: logSummary()/logReport() methods that were pure console.log wrappers. * fix: remove orphaned stubs and remaining debug logs from stripped services - Remove empty logReport() method and unused startTime variable (parallel-analysis.ts) - Remove orphaned console.group/console.groupEnd pair (parallel-analysis.ts) - Remove empty logSignalSummary() export (signal-aggregator.ts) - Remove logSignalSummary import/call and 3 remaining console.logs (InsightsPanel.ts) - Remove no-op logDirectFetchBlockedOnce() and dead infrastructure (prediction/index.ts) * feat(conflict): add Iran Attacks map layer Adds a new Iran-focused conflict events layer that aggregates real-time events, geocodes via 40-city lookup table, caches 15min in Redis, and renders as a toggleable DeckGL ScatterplotLayer with severity coloring. - New proto + codegen for ListIranEvents RPC - Server handler with HTML parsing, city geocoding, category mapping - Frontend service with circuit breaker - DeckGL ScatterplotLayer with severity-based color/size - MapPopup with sanitized source links - iranAttacks toggle across all variants, harnesses, and URL state * fix: resolve bootstrap 401 and 429 rate limiting on page init (#512) Same-origin browser requests don't send Origin header (per CORS spec), causing validateApiKey to reject them. Extract origin from Referer as fallback. Increase rate limit from 60 to 200 req/min to accommodate the ~50 requests fired during page initialization. * fix(relay): prevent Polymarket OOM via request deduplication (#513) Concurrent Polymarket requests for the same cache key each fired independent https.get() calls. With 12 categories × multiple clients, 740 requests piled up in 10s, all buffering response bodies → 4.1GB heap → OOM crash on Railway. Fix: in-flight promise map deduplicates concurrent requests to the same cache key. 429/error responses are negative-cached for 30s to prevent retry storms. * fix(threat-classifier): add military/conflict keyword gaps and news-to-conflict bridge (#514) Breaking news headlines like "Israel's strike on Iran" were classified as info level because the keyword classifier lacked standalone conflict phrases. Additionally, the conflict instability score depended solely on ACLED data (1-7 day lag) with no bridge from real-time breaking news. - Add 3 critical + 18 high contextual military/conflict keywords - Preserve threat classification on semantically merged clusters - Add news-derived conflict floor when ACLED/HAPI report zero signal - Upsert news events by cluster ID to prevent duplicates - Extract newsEventIndex to module-level Map for serialization safety * fix(breaking-news): let critical alerts bypass global cooldown and replace HIGH alerts (#516) Global cooldown (60s) was blocking critical alerts when a less important HIGH alert fired from an earlier RSS batch. Added priority-aware cooldown so critical alerts always break through. Banner now auto-dismisses HIGH alerts when a CRITICAL arrives. Added Iran/strikes keywords to classifier. * fix(rate-limit): increase sliding window to 300 req/min (#515) App init fires many concurrent classify-event, summarize-article, and record-baseline-snapshot calls, exhausting the 200/min limit and causing 429s. Bump to 300 as a temporary measure while client-side batching is implemented. * fix(breaking-news): fix fake pubDate fallback and filter noisy think-tank alerts (#517) Two bugs causing stale CrisisWatch article to fire as breaking alert: 1. Non-standard pubDate format ("Friday, February 27, 2026 - 12:38") failed to parse → fallback was `new Date()` (NOW) → day-old articles appeared as "just now" and passed recency gate on every fetch 2. Tier 3+ sources (think tanks) firing alerts on keyword-only matches like "War" in policy analysis titles — too noisy for breaking alerts Fix: parsePubDate() handles non-standard formats and falls back to epoch (not now). Tier 3+ sources require LLM classification to fire. * fix: make iran-events handler read-only from Redis (#518) Remove server-side LiveUAMap scraper (blocked by Cloudflare 403 on Vercel IPs). Handler now reads pre-populated Redis cache pushed from local browser scraping. Change cache tier from slow to fast to prevent CDN from serving stale empty responses for 30+ minutes. * fix(relay): Polymarket circuit breaker + concurrency limiter (OOM fix) (#519) * fix(rate-limit): increase sliding window to 300 req/min App init fires many concurrent classify-event, summarize-article, and record-baseline-snapshot calls, exhausting the 200/min limit and causing 429s. Bump to 300 as a temporary measure while client-side batching is implemented. * fix(relay): add Polymarket circuit breaker + concurrency limiter to prevent OOM Railway relay OOM crash: 280 Polymarket 429 errors in 8s, heap hit 3.7GB. Multiple unique cache keys bypassed per-key dedup, flooding upstream. - Circuit breaker: trips after 5 consecutive failures, 60s cooldown - Concurrent upstream limiter: max 3 simultaneous requests - Negative cache TTL: 30s → 60s to reduce retry frequency - Upstream slot freed on response.on('end'), not headers, preventing body buffer accumulation past the concurrency cap * fix(relay): guard against double-finalization on Polymarket timeout request.destroy() in timeout handler also fires request.on('error'), causing double decrement of polymarketActiveUpstream (counter goes negative, disabling concurrency cap) and double circuit breaker trip. Add finalized guard so decrement + failure accounting happens exactly once per request regardless of which error path fires first. * fix(threat-classifier): stagger AI classification requests to avoid Groq 429 (#520) flushBatch() fired up to 20 classifyEvent RPCs simultaneously via Promise.all, instantly hitting Groq's ~30 req/min rate limit. - Sequential execution with 2s min-gap between requests (~28 req/min) - waitForGap() enforces hard floor + jitter across batch boundaries - batchInFlight guard prevents concurrent flush loops - 429/5xx: requeue failed job (with retry cap) + remaining untouched jobs - Queue cap at 100 items with warn on overflow * fix(relay): regenerate package-lock.json with telegram dependency The lockfile was missing resolved entries for the telegram package, causing Railway to skip installation despite it being in package.json. * chore: trigger deploy to flush CDN cache for iran-events endpoint * Revert "fix(relay): regenerate package-lock.json with telegram dependency" This reverts commit |
||
|
|
6669d373cf |
feat: convert 52 API endpoints from POST to GET for edge caching (#468)
* feat: convert 52 API endpoints from POST to GET for edge caching Convert all cacheable sebuf RPC endpoints to HTTP GET with query/path parameters, enabling CDN edge caching to reduce costs. Flatten nested request types (TimeRange, PaginationRequest, BoundingBox) into scalar query params. Add path params for resource lookups (GetFredSeries, GetHumanitarianSummary, GetCountryStockIndex, GetCountryIntelBrief, GetAircraftDetails). Rewrite router with hybrid static/dynamic matching for path param support. Kept as POST: SummarizeArticle, ClassifyEvent, RecordBaselineSnapshot, GetAircraftDetailsBatch, RegisterInterest. Generated with sebuf v0.9.0 (protoc-gen-ts-client, protoc-gen-ts-server). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add rate_limited field to market response protos The rateLimited field was hand-patched into generated files on main but never declared in the proto definitions. Regenerating wiped it out, breaking the build. Now properly defined in both ListEtfFlowsResponse and ListMarketQuotesResponse protos. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove accidentally committed .planning files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c939cc6296 |
Proto-first API rebuild: sebuf contracts, handlers, gateway, and generated docs (#106)
* docs: initialize sebuf integration project with codebase map Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: add project config Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: complete project research * docs: define v1 requirements (34 requirements, 8 categories) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: create roadmap (8 phases) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(01): capture phase context * docs(state): record phase 1 context session * docs(01): research phase domain - buf toolchain, sebuf codegen, proto patterns Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(01-proto-foundation): create phase plan * chore(01-01): configure buf toolchain with buf.yaml, buf.gen.yaml, buf.lock - buf.yaml v2 with STANDARD+COMMENTS lint, FILE+PACKAGE+WIRE_JSON breaking, deps on protovalidate and sebuf - buf.gen.yaml configures protoc-gen-ts-client, protoc-gen-ts-server, protoc-gen-openapiv3 plugins - buf.lock generated with resolved dependency versions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(01-01): add shared core proto type definitions - geo.proto: GeoCoordinates with lat/lng validation, BoundingBox for spatial queries - time.proto: TimeRange with google.protobuf.Timestamp start/end - pagination.proto: cursor-based PaginationRequest (1-100 page_size) and PaginationResponse - i18n.proto: LocalizableString for pre-localized upstream API strings - identifiers.proto: typed ID wrappers (HotspotID, EventID, ProviderID) for cross-domain refs - general_error.proto: GeneralError with RateLimited, UpstreamDown, GeoBlocked, MaintenanceMode All files pass buf lint (STANDARD+COMMENTS) and buf build with zero errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(01-01): complete buf toolchain and core proto types plan - SUMMARY.md documents 2 tasks, 9 files created, 2 deviations auto-fixed - STATE.md updated: plan 1/2 in phase 1, decisions recorded - ROADMAP.md updated: phase 01 in progress (1/2 plans) - REQUIREMENTS.md updated: PROTO-01, PROTO-02, PROTO-03 marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(01-01): use int64 epoch millis instead of google.protobuf.Timestamp User preference: all time fields use int64 (Unix epoch milliseconds) instead of google.protobuf.Timestamp for simpler serialization and JS interop. Applied to TimeRange and MaintenanceMode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(01-02): create test domain proto files with core type imports - Add test_item.proto with GeoCoordinates import and int64 timestamps - Add get_test_items.proto with TimeRange and Pagination imports - Add service.proto with HTTP annotations for TestService - All proto files pass buf lint and buf build * feat(01-02): run buf generate and create Makefile for code generation pipeline - Add Makefile with generate, lint, clean, install, check, format, breaking targets - Update buf.gen.yaml with managed mode and paths=source_relative for correct output paths - Generate TypeScript client (TestServiceClient class) at src/generated/client/ - Generate TypeScript server (TestServiceHandler interface) at src/generated/server/ - Generate OpenAPI 3.1.0 specs (JSON + YAML) at docs/api/ - Core type imports (GeoCoordinates, TimeRange, Pagination) flow through to generated output * docs(01-02): complete test domain code generation pipeline plan - Create 01-02-SUMMARY.md with pipeline validation results - Update STATE.md: phase 1 complete, 2/2 plans done, new decisions recorded - Update ROADMAP.md: phase 1 marked complete (2/2) - Update REQUIREMENTS.md: mark PROTO-04 and PROTO-05 complete * docs(phase-01): complete phase execution and verification * test(01): complete UAT - 6 passed, 0 issues * feat(2A): define all 17 domain proto packages with generated clients, servers, and OpenAPI specs Remove test domain protos (Phase 1 scaffolding). Add core enhancements (severity.proto, country.proto, expanded identifiers.proto). Define all 17 domain services: seismology, wildfire, climate, conflict, displacement, unrest, military, aviation, maritime, cyber, market, prediction, economic, news, research, infrastructure, intelligence. 79 proto files producing 34 TypeScript files and 34 OpenAPI specs. buf lint clean, tsc clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2B): add server runtime phase context and handoff checkpoint Prepare Phase 2B with full context file covering deliverables, key reference files, generated code patterns, and constraints. Update STATE.md with resume pointer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2B): research phase domain * docs(2B): create phase plan * feat(02-01): add shared server infrastructure (router, CORS, error mapper) - router.ts: Map-based route matcher from RouteDescriptor[] arrays - cors.ts: TypeScript port of api/_cors.js with POST/OPTIONS methods - error-mapper.ts: onError callback handling ApiError, network, and unknown errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(02-01): implement seismology handler as first end-to-end proof - Implements SeismologyServiceHandler from generated server types - Fetches USGS M4.5+ earthquake GeoJSON feed and transforms to proto-shaped Earthquake[] - Maps all fields: id, place, magnitude, depthKm, location, occurredAt (String), sourceUrl Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(02-01): complete server infrastructure plan - SUMMARY.md with task commits, decisions, and self-check - STATE.md updated: position, decisions, session info - REQUIREMENTS.md: SERVER-01, SERVER-02, SERVER-06 marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(02-02): create Vercel catch-all gateway, tsconfig.api.json, and typecheck:api script - api/[[...path]].ts mounts seismology routes via catch-all with CORS on every response path - tsconfig.api.json extends base config without vite/client types for edge runtime - package.json adds typecheck:api script * feat(02-02): add Vite dev server plugin for sebuf API routes - sebufApiPlugin() intercepts /api/{domain}/v1/* in dev mode - Uses dynamic imports to lazily load handler modules inside configureServer - Converts Connect IncomingMessage to Web Standard Request - CORS headers applied to all plugin responses (200, 204, 403, 404) - Falls through to existing proxy rules for non-sebuf /api/* paths * docs(02-02): complete gateway integration plan - SUMMARY.md documenting catch-all gateway + Vite plugin implementation - STATE.md updated: Phase 2B complete, decisions recorded - ROADMAP.md updated: Phase 02 marked complete (2/2 plans) - REQUIREMENTS.md: SERVER-03, SERVER-04, SERVER-05 marked complete * docs(02-server-runtime): create gap closure plan for SERVER-05 Tauri sidecar * feat(02-03): add esbuild compilation step for sebuf sidecar gateway bundle - Create scripts/build-sidecar-sebuf.mjs that bundles api/[[...path]].ts into a single ESM .js file - Add build:sidecar-sebuf npm script and chain it into the main build command - Install esbuild as explicit devDependency - Gitignore the compiled api/[[...path]].js build artifact Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(02-03): verify sidecar discovery and annotate SERVER-05 gap closure - Confirm compiled bundle handler returns status 200 for POST requests - Add gap closure note to SERVER-05 in REQUIREMENTS.md - Verify typecheck:api and full build pipeline pass without regressions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(02-03): complete sidecar sebuf bundle plan - Create 02-03-SUMMARY.md documenting esbuild bundle compilation - Update STATE.md with plan 03 position, decisions, and metrics - Update ROADMAP.md plan progress (3/3 plans complete) - Annotate SERVER-05 gap closure in REQUIREMENTS.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-02): complete phase execution * docs(2C): capture seismology migration phase context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(state): record phase 2C context session Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2C): research seismology migration phase Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2C): create seismology migration phase plan * feat(2C-01): annotate all int64 time fields with INT64_ENCODING_NUMBER - Vendor sebuf/http/annotations.proto locally with Int64Encoding extension (50010) - Remove buf.build/sebmelki/sebuf BSR dep, use local vendored proto instead - Add INT64_ENCODING_NUMBER annotation to 34 time fields across 20 proto files - Regenerate all TypeScript client and server code (time fields now `number` not `string`) - Fix seismology handler: occurredAt returns number directly (no String() wrapper) - All non-time int64 fields (displacement counts, population) left as string - buf lint, buf generate, tsc, and sidecar build all pass with zero errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2C-01): complete INT64_ENCODING_NUMBER plan - Create 2C-01-SUMMARY.md with execution results and deviations - Update STATE.md: plan 01 complete, int64 blocker resolved, new decisions - Update ROADMAP.md: mark 2C-01 plan complete - Update REQUIREMENTS.md: mark CLIENT-01 complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(lint): exclude .planning/ from markdownlint GSD planning docs use formatting that triggers MD032 -- these are machine-generated and not user-facing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2C-02): rewrite earthquake adapter to use SeismologyServiceClient and adapt all consumers to proto types - Replace legacy fetch/circuit-breaker adapter with port/adapter wrapping SeismologyServiceClient - Update 7 consuming files to import Earthquake from @/services/earthquakes (the port) - Adapt all field accesses: lat/lon -> location?.latitude/longitude, depth -> depthKm, time -> occurredAt, url -> sourceUrl - Remove unused filterByTime from Map.ts (only called for earthquakes, replaced with inline filter) - Update e2e test data to proto Earthquake shape Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(2C-02): delete legacy earthquake endpoint, remove Vite proxy, clean API_URLS config - Delete api/earthquakes.js (legacy Vercel edge function proxying USGS) - Remove /api/earthquake Vite dev proxy (sebufApiPlugin handles seismology now) - Remove API_URLS.earthquakes entry from base config (no longer referenced) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2C-02): complete seismology client wiring plan - Create 2C-02-SUMMARY.md with execution results - Update STATE.md: phase 2C complete, decisions, metrics - Update ROADMAP.md: mark 2C-02 and phase 2C complete - Mark requirements CLIENT-02, CLIENT-04, CLEAN-01, CLEAN-02 complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2C): complete phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2D): create wildfire migration phase plan * feat(2D-01): enhance FireDetection proto and implement wildfire handler - Add region (field 8) and day_night (field 9) to FireDetection proto - Regenerate TypeScript client and server types - Implement WildfireServiceHandler with NASA FIRMS CSV proxy - Fetch all 9 monitored regions in parallel via Promise.allSettled - Graceful degradation to empty list when API key is missing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2D-01): wire wildfire routes into gateway and rebuild sidecar - Import createWildfireServiceRoutes and wildfireHandler in catch-all - Mount wildfire routes alongside seismology in allRoutes array - Rebuild sidecar-sebuf bundle with wildfire endpoint included Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2D-01): complete wildfire handler plan - Create 2D-01-SUMMARY.md with execution results - Update STATE.md position to 2D plan 01 complete - Update ROADMAP.md with 2D progress (1/2 plans) - Mark DOMAIN-01 requirement complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2D-02): create wildfires service module and rewire all consumers - Add src/services/wildfires/index.ts with fetchAllFires, computeRegionStats, flattenFires, toMapFires - Rewire App.ts to import from @/services/wildfires with proto field mappings - Rewire SatelliteFiresPanel.ts to import FireRegionStats from @/services/wildfires - Update signal-aggregator.ts source comment * chore(2D-02): delete legacy wildfire endpoint and service module - Remove api/firms-fires.js (replaced by api/server/worldmonitor/wildfire/v1/handler.ts) - Remove src/services/firms-satellite.ts (replaced by src/services/wildfires/index.ts) - Zero dangling references confirmed - Full build passes (tsc, vite, sidecar) * docs(2D-02): complete wildfire consumer wiring plan - Create 2D-02-SUMMARY.md with execution results - Update STATE.md: phase 2D complete, progress ~52% - Update ROADMAP.md: phase 2D plan progress * docs(phase-2D): complete phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2E): research climate migration domain Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2E): create phase plan * feat(2E-01): implement climate handler with 15-zone monitoring and baseline comparison - Create ClimateServiceHandler with 15 hardcoded monitored zones matching legacy - Parallel fetch from Open-Meteo Archive API via Promise.allSettled - 30-day baseline comparison: last 7 days vs preceding baseline - Null filtering with paired data points, minimum 14-point threshold - Severity classification (normal/moderate/extreme) and type (warm/cold/wet/dry/mixed) - 1-decimal rounding for tempDelta and precipDelta - Proto ClimateAnomaly mapping with GeoCoordinates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2E-01): wire climate routes into gateway and rebuild sidecar - Import createClimateServiceRoutes and climateHandler in catch-all gateway - Mount climate routes alongside seismology and wildfire - Rebuild sidecar-sebuf bundle with climate routes included Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2E-01): complete climate handler plan - Create 2E-01-SUMMARY.md with execution results - Update STATE.md: position to 2E plan 01, add decisions - Update ROADMAP.md: mark 2E-01 complete, update progress table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2E-02): rewrite climate service module and rewire all consumers - Replace src/services/climate.ts with src/services/climate/index.ts directory module - Port/adapter pattern: ClimateServiceClient maps proto shapes to legacy consumer shapes - Rewire ClimateAnomalyPanel, DeckGLMap, MapContainer, country-instability, conflict-impact - All 6 consumers import ClimateAnomaly from @/services/climate instead of @/types - Drop dead getSeverityColor function, keep getSeverityIcon and formatDelta - Fix minSeverity required param in listClimateAnomalies call * chore(2E-02): delete legacy climate endpoint and remove dead types - Delete api/climate-anomalies.js (replaced by sebuf climate handler) - Remove ClimateAnomaly and AnomalySeverity from src/types/index.ts - Full build passes with zero errors * docs(2E-02): complete climate client wiring plan - Create 2E-02-SUMMARY.md with execution results - Update STATE.md: phase 2E complete, decisions, session continuity - Update ROADMAP.md: phase 2E progress * docs(phase-2E): complete phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2F): research prediction migration domain * docs(2F): create prediction migration phase plans * feat(2F-01): implement prediction handler with Gamma API proxy - PredictionServiceHandler proxying Gamma API with 8s timeout - Maps events/markets to proto PredictionMarket with 0-1 yesPrice scale - Graceful degradation: returns empty markets on any failure (Cloudflare expected) - Supports category-based events endpoint and default markets endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2F-01): wire prediction routes into gateway - Import createPredictionServiceRoutes and predictionHandler - Mount prediction routes in allRoutes alongside seismology, wildfire, climate - Sidecar bundle rebuilt successfully (21.2 KB) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2F-01): complete prediction handler plan - SUMMARY.md with handler implementation details and deviation log - STATE.md updated to 2F in-progress position with decisions - ROADMAP.md updated to 1/2 plans complete for phase 2F - REQUIREMENTS.md marked DOMAIN-02 complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2F-02): create prediction service module and rewire all consumers - Create src/services/prediction/index.ts preserving all business logic from polymarket.ts - Replace strategy 4 (Vercel edge) with PredictionServiceClient in polyFetch - Update barrel export from polymarket to prediction in services/index.ts - Rewire 7 consumers to import PredictionMarket from @/services/prediction - Fix 3 yesPrice bugs: CountryIntelModal (*100), App.ts search (*100), App.ts snapshot (1-y) - Drop dead code getPolymarketStatus() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(2F-02): delete legacy endpoint and remove dead types - Delete api/polymarket.js (replaced by sebuf handler) - Delete src/services/polymarket.ts (replaced by src/services/prediction/index.ts) - Remove PredictionMarket interface from src/types/index.ts (now in prediction module) - Type check and sidecar build both pass Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2F-02): complete prediction consumer wiring plan Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2F): complete phase execution * docs(phase-2F): fix roadmap plan counts and completion status Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2G): research displacement migration phase * docs(2G): create displacement migration phase plans * feat(2G-01): implement displacement handler with UNHCR API pagination and aggregation - 40-entry COUNTRY_CENTROIDS map for geographic coordinates - UNHCR Population API pagination (10,000/page, 25-page guard) - Year fallback: current year to current-2 until data found - Per-country origin + asylum aggregation with unified merge - Global totals computation across all raw records - Flow corridor building sorted by refugees, capped by flowLimit - All int64 fields returned as String() per proto types - Graceful empty response on any failure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2G-01): wire displacement routes into gateway and rebuild sidecar - Import createDisplacementServiceRoutes and displacementHandler - Mount displacement routes alongside seismology, wildfire, climate, prediction - Sidecar bundle rebuilt with displacement included (31.0 KB) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2G-01): complete displacement handler plan - SUMMARY.md with execution metrics and decisions - STATE.md updated to 2G phase position - ROADMAP.md updated with 2G-01 plan progress Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2G-02): create displacement service module and rewire all consumers - Create src/services/displacement/index.ts as port/adapter using DisplacementServiceClient - Map proto int64 strings to numbers and GeoCoordinates to flat lat/lon - Preserve circuit breaker, presentation helpers (getDisplacementColor, formatPopulation, etc.) - Rewire App.ts, DisplacementPanel, MapContainer, DeckGLMap, conflict-impact, country-instability - Delete legacy src/services/unhcr.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(2G-02): delete legacy endpoint and remove dead displacement types - Delete api/unhcr-population.js (replaced by displacement handler from 2G-01) - Remove DisplacementFlow, CountryDisplacement, UnhcrSummary from src/types/index.ts - All consumers now import from @/services/displacement - Sidecar rebuild, tsc, and full Vite build pass clean Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2G-02): complete displacement consumer wiring plan - SUMMARY.md with 2 task commits, decisions, deviation documentation - STATE.md updated: phase 2G complete, 02/02 plans done - ROADMAP.md updated with plan progress Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2G): complete phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2H): research aviation migration phase * docs(2H): create aviation migration phase plans * feat(2H-01): implement aviation handler with FAA XML parsing and simulated delays - Install fast-xml-parser for server-side XML parsing (edge-compatible) - Create AviationServiceHandler with FAA NASSTATUS XML fetch and parse - Enrich US airports with MONITORED_AIRPORTS metadata (lat, lon, name, icao) - Generate simulated delays for non-US airports with rush-hour weighting - Map short-form strings to proto enums (FlightDelayType, FlightDelaySeverity, etc.) - Wrap flat lat/lon into GeoCoordinates for proto response - Graceful empty alerts on any upstream failure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2H-01): wire aviation routes into gateway and rebuild sidecar - Mount createAviationServiceRoutes in catch-all gateway alongside 5 existing domains - Import aviationHandler for route wiring - Rebuild sidecar-sebuf bundle with aviation routes included Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2H-01): complete aviation handler plan - Create 2H-01-SUMMARY.md with execution results - Update STATE.md position to 2H-01 with aviation decisions - Update ROADMAP.md progress for phase 2H (1/2 plans) - Mark DOMAIN-08 requirement as complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2H-02): create aviation service module and rewire all consumers - Create src/services/aviation/index.ts as port/adapter wrapping AviationServiceClient - Map proto enum strings to short-form (severity, delayType, region, source) - Unwrap GeoCoordinates to flat lat/lon, convert epoch-ms updatedAt to Date - Preserve circuit breaker with identical name string - Rewire Map, DeckGLMap, MapContainer, MapPopup, map-harness to import from @/services/aviation - Update barrel export: flights -> aviation - Delete legacy src/services/flights.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(2H-02): delete legacy endpoint and remove dead aviation types - Delete api/faa-status.js (replaced by aviation handler in 2H-01) - Remove FlightDelaySource, FlightDelaySeverity, FlightDelayType, AirportRegion, AirportDelayAlert from src/types/index.ts - Preserve MonitoredAirport with inlined region type union - Full build (tsc + vite + sidecar) passes with zero errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2H-02): complete aviation consumer wiring plan Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2H): complete phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2I): research phase domain * docs(2I): create phase plan * feat(2I-01): implement ResearchServiceHandler with 3 RPCs - arXiv XML parsing with fast-xml-parser (ignoreAttributes: false for attributes) - GitHub trending repos with primary + fallback API URLs - Hacker News Firebase API with 2-step fetch and bounded concurrency (10) - All RPCs return empty arrays on failure (graceful degradation) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2I-01): mount research routes in gateway and rebuild sidecar - Import createResearchServiceRoutes and researchHandler in catch-all gateway - Add research routes to allRoutes array (after aviation) - Sidecar bundle rebuilt (116.6 KB) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2I-01): complete research handler plan - SUMMARY.md with self-check passed - STATE.md updated to phase 2I, plan 01 of 02 - ROADMAP.md updated with plan 2I-01 complete - REQUIREMENTS.md: DOMAIN-05 marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2I-02): create research service module and delete legacy code - Add src/services/research/index.ts with fetchArxivPapers, fetchTrendingRepos, fetchHackernewsItems backed by ResearchServiceClient - Re-export proto types ArxivPaper, GithubRepo, HackernewsItem (no enum mapping needed) - Circuit breakers wrap all 3 client calls with empty-array fallback - Delete legacy API endpoints: api/arxiv.js, api/github-trending.js, api/hackernews.js - Delete legacy service files: src/services/arxiv.ts, src/services/github-trending.ts, src/services/hackernews.ts - Remove arxiv, githubTrending, hackernews entries from API_URLS and REFRESH_INTERVALS in config Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2I-02): complete research consumer wiring plan - SUMMARY.md documenting service module creation and 6 legacy file deletions - STATE.md updated: phase 2I complete, decisions recorded - ROADMAP.md updated: phase 2I marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2I): complete phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2J): complete unrest migration research * docs(2J): create unrest migration phase plans * feat(2J-01): implement UnrestServiceHandler with ACLED + GDELT dual-fetch - Create handler with listUnrestEvents RPC proxying ACLED API and GDELT GEO API - ACLED fetch uses Bearer auth from ACLED_ACCESS_TOKEN env var, returns empty on missing token - GDELT fetch returns GeoJSON protest events with no auth required - Deduplication uses 0.5-degree grid + date key, preferring ACLED over GDELT on collision - Severity classification and event type mapping ported from legacy protests.ts - Sort by severity (high first) then recency (newest first) - Graceful degradation: returns empty events on any upstream failure * feat(2J-01): mount unrest routes in gateway and rebuild sidecar - Import createUnrestServiceRoutes and unrestHandler in catch-all gateway - Add unrest service routes to allRoutes array - Sidecar bundle rebuilt to include unrest endpoint - RPC routable at POST /api/unrest/v1/list-unrest-events * docs(2J-01): complete unrest handler plan - Create 2J-01-SUMMARY.md with execution results and self-check - Update STATE.md with phase 2J position, decisions, session continuity - Update ROADMAP.md with plan 01 completion status * feat(2J-02): create unrest service module with proto-to-legacy type mapping - Full adapter maps proto UnrestEvent to legacy SocialUnrestEvent shape - 4 enum mappers: severity, eventType, sourceType, confidence - fetchProtestEvents returns ProtestData with events, byCountry, highSeverityCount, sources - getProtestStatus infers ACLED configuration from response event sources - Circuit breaker wraps client call with empty fallback * feat(2J-02): update services barrel, remove vite proxies, delete legacy files - Services barrel: protests -> unrest re-export - Vite proxy entries removed: /api/acled, /api/gdelt-geo - Legacy files deleted: api/acled.js, api/gdelt-geo.js, src/services/protests.ts - Preserved: api/acled-conflict.js (conflict domain), SocialUnrestEvent type * docs(2J-02): complete unrest service module plan - SUMMARY.md created with full adapter pattern documentation - STATE.md updated: 2J-02 complete, decisions recorded - ROADMAP.md updated: Phase 2J marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2J): complete phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2K): complete conflict migration research * docs(2K): create phase plan * feat(2K-01): implement ConflictServiceHandler with 3 RPCs - listAcledEvents proxies ACLED API for battles/explosions/violence with Bearer auth - listUcdpEvents discovers UCDP GED API version dynamically, fetches backward with 365-day trailing window - getHumanitarianSummary proxies HAPI API with ISO-2 to ISO-3 country mapping - All RPCs have graceful degradation returning empty on failure * feat(2K-01): mount conflict routes in gateway and rebuild sidecar - Add createConflictServiceRoutes and conflictHandler imports to catch-all gateway - Spread conflict routes into allRoutes array (3 RPC endpoints) - Rebuild sidecar bundle with conflict endpoints included * docs(2K-01): complete conflict handler plan - Create 2K-01-SUMMARY.md with execution details and self-check - Update STATE.md: position to 2K-01, add 5 decisions - Update ROADMAP.md: mark 2K-01 complete (1/2 plans done) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2K-02): create conflict service module with 4-shape proto-to-legacy type mapping - Port/adapter mapping AcledConflictEvent -> ConflictEvent, UcdpViolenceEvent -> UcdpGeoEvent, HumanitarianCountrySummary -> HapiConflictSummary - UCDP classifications derived heuristically from GED events (deaths/events thresholds -> war/minor/none) - deduplicateAgainstAcled ported exactly with haversine + date + fatality matching - 3 circuit breakers for 3 RPCs, exports 5 functions + 2 group helpers + all legacy types * feat(2K-02): rewire consumer imports and delete 9 legacy conflict files - App.ts consolidated from 4 direct imports to single @/services/conflict import - country-instability.ts consolidated from 3 type imports to single ./conflict import - Deleted 4 API endpoints: acled-conflict.js, ucdp-events.js, ucdp.js, hapi.js - Deleted 4 service files: conflicts.ts, ucdp.ts, ucdp-events.ts, hapi.ts - Deleted 1 dead code file: conflict-impact.ts - UcdpGeoEvent preserved in src/types/index.ts (scope guard for map components) * docs(2K-02): complete conflict service module plan - SUMMARY.md with 4-shape proto adapter, consumer consolidation, 9 legacy deletions - STATE.md updated: Phase 2K complete (2/2 plans), progress ~100% - ROADMAP.md updated: Phase 2K marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-2K): complete conflict migration phase execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2L): research maritime migration phase domain * docs(2L): create maritime migration phase plans * feat(2L-01): implement MaritimeServiceHandler with 2 RPCs - getVesselSnapshot proxies WS relay with wss->https URL conversion - Maps density/disruptions to proto shape with GeoCoordinates nesting - Disruption type/severity mapped from lowercase to proto enums - listNavigationalWarnings proxies NGA MSI broadcast warnings API - NGA military date parsing (081653Z MAY 2024) to epoch ms - Both RPCs gracefully degrade to empty on upstream failure - No caching (client-side polling manages refresh intervals) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2L-01): mount maritime routes in gateway and rebuild sidecar - Import createMaritimeServiceRoutes and maritimeHandler - Add maritime routes to allRoutes array in catch-all gateway - Sidecar bundle rebuilt (148.0 KB) with maritime endpoints - RPCs routable at /api/maritime/v1/get-vessel-snapshot and /api/maritime/v1/list-navigational-warnings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(2L-01): complete maritime handler plan - SUMMARY.md with 2 task commits documented - STATE.md updated to 2L phase, plan 01/02 complete - ROADMAP.md progress updated for phase 2L - REQUIREMENTS.md: DOMAIN-06 marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(2L-02): create maritime service module with hybrid fetch and polling/callback preservation - Port/adapter wrapping MaritimeServiceClient for proto RPC path - Full polling/callback architecture preserved from legacy ais.ts - Hybrid fetch: proto RPC for snapshot-only, raw WS relay for candidates - Proto-to-legacy type mapping for AisDisruptionEvent and AisDensityZone - Exports fetchAisSignals, initAisStream, disconnectAisStream, getAisStatus, isAisConfigured, registerAisCallback, unregisterAisCallback, AisPositionData * feat(2L-02): rewire consumer imports and delete 3 legacy maritime files - cable-activity.ts: fetch NGA warnings via MaritimeServiceClient.listNavigationalWarnings() with NgaWarning shape reconstruction from proto fields - military-vessels.ts: imports updated from './ais' to './maritime' - Services barrel: updated from './ais' to './maritime' - desktop-readiness.ts: service/api references updated to maritime handler paths - Deleted: api/ais-snapshot.js, api/nga-warnings.js, src/services/ais.ts - AisDisruptionEvent/AisDensityZone/AisDisruptionType preserved in src/types/index.ts * docs(2L-02): complete maritime service module plan - SUMMARY.md with hybrid fetch pattern, polling/callback preservation, 3 legacy files deleted - STATE.md updated: phase 2L complete, 5 decisions recorded - ROADMAP.md updated: 2L plans marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: bind globalThis.fetch in all sebuf service clients Generated sebuf clients store globalThis.fetch as a class property, then call it as this.fetchFn(). This loses the window binding and throws "Illegal invocation" in browsers. Pass { fetch: fetch.bind(globalThis) } to all 11 client constructors. Also includes vite.config.ts with all 10 migrated domain handlers registered in the sebuf dev server plugin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate cyber + economic domains to sebuf (12/17) Cyber (Phase 2M): - Create handler aggregating 5 upstream sources (Feodo, URLhaus, C2Intel, OTX, AbuseIPDB) with dedup, GeoIP hydration, country centroid fallback - Create service module with CyberServiceClient + circuit breaker - Delete api/cyber-threats.js, api/cyber-threats.test.mjs, src/services/cyber-threats.ts Economic (Phase 2N) — consolidates 3 legacy services: - Create handler with 3 RPCs: getFredSeries (FRED API), listWorldBankIndicators (World Bank API), getEnergyPrices (EIA API) - Create unified service module replacing fred.ts, oil-analytics.ts, worldbank.ts - Preserve all exported functions/types for EconomicPanel and TechReadinessPanel - Delete api/fred-data.js, api/worldbank.js, src/services/fred.ts, src/services/oil-analytics.ts, src/services/worldbank.ts Both domains registered in vite.config.ts and api/[[...path]].ts. TypeScript check and vite build pass cleanly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate infrastructure domain to sebuf (13/17) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate market domain to sebuf (14/17) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate news domain to sebuf (15/17) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate intelligence domain to sebuf (16/17) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate military domain to sebuf (17/17) All 17 domains now have sebuf handlers registered in the gateway. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate intelligence services to sebuf client Rewire pizzint.ts, cached-risk-scores.ts, and threat-classifier.ts to use IntelligenceServiceClient instead of legacy /api/ fetch calls. Handler now preserves raw threat level in subcategory field. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate military theater posture to sebuf client Rewire cached-theater-posture.ts to use MilitaryServiceClient instead of legacy /api/theater-posture fetch. Adds theater metadata map for proto→legacy TheaterPostureSummary adapter. UI gracefully falls back to total counts when per-type breakdowns aren't available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: rewire country-intel to sebuf client Replace legacy fetch('/api/country-intel') with typed IntelligenceServiceClient.getCountryIntelBrief() RPC call in App.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate stablecoin-markets to sebuf (market domain) Add ListStablecoinMarkets RPC to market service. Port CoinGecko stablecoin peg-health logic from api/stablecoin-markets.js into the market handler. Rewire StablecoinPanel to use typed sebuf client. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate etf-flows to sebuf (market domain) Add ListEtfFlows RPC to market service. Port Yahoo Finance BTC spot ETF flow estimation logic from api/etf-flows.js into the market handler. Rewire ETFFlowsPanel to use typed sebuf client. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate worldpop-exposure to sebuf (displacement domain) Add GetPopulationExposure RPC to displacement service. Port country population data and radius-based exposure estimation from api/worldpop-exposure.js into the displacement handler. Rewire population-exposure.ts to use typed sebuf client. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove superseded legacy edge functions Delete 4 legacy api/*.js files that are now fully replaced by sebuf handlers: - api/stablecoin-markets.js -> market/ListStablecoinMarkets - api/etf-flows.js -> market/ListEtfFlows - api/worldpop-exposure.js -> displacement/GetPopulationExposure - api/classify-batch.js -> intelligence/ClassifyEvent Remaining legacy files are still actively used by client code (stock-index, opensky, gdelt-doc, rss-proxy, summarize endpoints, macro-signals, tech-events) or are shared utilities. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: delete dead legacy files and unused API_URLS config Remove coingecko.js, debug-env.js, cache-telemetry.js, _cache-telemetry.js (all zero active consumers). Delete unused API_URLS export from base config. Update desktop-readiness market-panel metadata to reference sebuf paths. Remove dead CoinGecko dev proxy from vite.config.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: migrate stock-index and opensky to sebuf - Add GetCountryStockIndex RPC to market domain (Yahoo Finance + cache) - Fill ListMilitaryFlights stub in military handler (OpenSky with bounding box) - Rewire App.ts stock-index fetch to MarketServiceClient.getCountryStockIndex() - Delete api/stock-index.js and api/opensky.js edge functions - OpenSky client path unchanged (relay primary, vite proxy for dev) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * wip: sebuf legacy migration paused at phase 3/10 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(03): capture phase context * docs(state): record phase 3 context session * docs(03): research phase domain * docs(03): create phase plan — 5 plans in 2 waves * feat(03-01): commit wingbits migration (step 3) -- 3 RPCs added to military domain - Add GetAircraftDetails, GetAircraftDetailsBatch, GetWingbitsStatus RPCs - Rewire src/services/wingbits.ts to use MilitaryServiceClient - Update desktop-readiness.ts routes to match new RPC paths - Delete legacy api/wingbits/ edge functions (3 files) - Regenerate military service client/server TypeScript + OpenAPI docs * feat(03-02): add SummarizeArticle proto and implement handler - Create summarize_article.proto with request/response messages - Add SummarizeArticle RPC to NewsService proto - Implement full handler with provider dispatch (ollama/groq/openrouter) - Port cache key builder, deduplication, prompt builder, think-token stripping - Inline Upstash Redis helpers for edge-compatible caching * feat(03-01): migrate gdelt-doc to intelligence RPC + delete _ip-rate-limit.js - Add SearchGdeltDocuments RPC to IntelligenceService proto - Implement searchGdeltDocuments handler (port from api/gdelt-doc.js) - Rewire src/services/gdelt-intel.ts to use IntelligenceServiceClient - Delete legacy api/gdelt-doc.js edge function - Delete dead api/_ip-rate-limit.js (zero importers) - Regenerate intelligence service client/server TypeScript + OpenAPI docs * feat(03-02): rewire summarization client to NewsService RPC, delete 4 legacy files - Replace direct fetch to /api/{provider}-summarize with NewsServiceClient.summarizeArticle() - Preserve identical fallback chain: ollama -> groq -> openrouter -> browser T5 - Delete api/groq-summarize.js, api/ollama-summarize.js, api/openrouter-summarize.js - Delete api/_summarize-handler.js and api/_summarize-handler.test.mjs - Update desktop-readiness.ts to reference new sebuf route * feat(03-03): rewire MacroSignalsPanel to EconomicServiceClient + delete legacy - Replace fetch('/api/macro-signals') with EconomicServiceClient.getMacroSignals() - Add mapProtoToData() to convert proto optional fields to null for rendering - Delete legacy api/macro-signals.js edge function * feat(03-04): add ListTechEvents proto, city-coords data, and handler - Create list_tech_events.proto with TechEvent, TechEventCoords messages - Add ListTechEvents RPC to ResearchService proto - Extract 360-city geocoding table to api/data/city-coords.ts - Implement listTechEvents handler with ICS+RSS parsing, curated events, dedup, filtering - Regenerate TypeScript client/server from proto Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(03-01): complete wingbits + GDELT doc migration plan - Create 03-01-SUMMARY.md with execution results - Update STATE.md with plan 01 completion, steps 3-4 done - Update ROADMAP.md plan progress (2/5 plans complete) - Mark DOMAIN-10 requirement complete * docs(03-02): complete summarization migration plan - Create 03-02-SUMMARY.md with execution results - Update STATE.md position to step 6/10 - Update ROADMAP.md plan progress - Mark DOMAIN-09 requirement complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(03-04): rewire TechEventsPanel and App to ResearchServiceClient, delete legacy - Replace fetch('/api/tech-events') with ResearchServiceClient.listTechEvents() in TechEventsPanel - Replace fetch('/api/tech-events') with ResearchServiceClient.listTechEvents() in App.loadTechEvents() - Delete legacy api/tech-events.js (737 lines) - TypeScript compiles cleanly with no references to legacy endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(03-03): complete macro-signals migration plan - Create 03-03-SUMMARY.md with execution results - Mark DOMAIN-04 requirement complete in REQUIREMENTS.md * docs(03-04): complete tech-events migration plan - Add 03-04-SUMMARY.md with execution results - Update STATE.md: advance to plan 5/step 8, add decisions - Update ROADMAP.md: 4/5 plans complete for phase 03 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(03-05): add temporal baseline protos + handler with Welford's algorithm - GetTemporalBaseline RPC: anomaly detection with z-score thresholds - RecordBaselineSnapshot RPC: batch update via Welford's online algorithm - Inline mgetJson helper for Redis batch reads - Inline getCachedJson/setCachedJson Redis helpers - Generated TypeScript client/server + OpenAPI docs * feat(03-05): migrate temporal-baseline + tag non-JSON + final cleanup - Rewire temporal-baseline.ts to InfrastructureServiceClient RPCs - Delete api/temporal-baseline.js (migrated to sebuf handler) - Delete api/_upstash-cache.js (no importers remain) - Tag 6 non-JSON edge functions with // Non-sebuf: comment header - Update desktop-readiness.ts: fix stale cloudflare-outages reference * docs(03-05): complete temporal-baseline + non-JSON tagging + final cleanup plan - SUMMARY.md with Welford algorithm migration details - STATE.md updated: Phase 3 complete (100%) - ROADMAP.md updated: 5/5 plans complete * chore(03): delete orphaned ollama-summarize test after RPC migration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-3): complete phase execution * docs(v1): create milestone audit report Audits all 13 phases of the v1 sebuf integration milestone. 12/13 phases verified (2L maritime missing VERIFICATION.md). 25/34 requirements satisfied, 6 superseded, 2 partial, 1 unsatisfied (CLEAN-03). All 17 domains wired end-to-end. Integration check passes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(v1): update audit — mark CLEAN-03/04 + MIGRATE-* as superseded CLEAN-03 superseded by port/adapter architecture (internal types intentionally decoupled from proto wire types). MIGRATE-01-05 superseded by direct cutover approach. DOMAIN-03 checkbox updated. Milestone status: tech_debt (no unsatisfied requirements). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(roadmap): add gap closure phase 4 — v1 milestone cleanup Closes all audit gaps: CLIENT-03 circuit breaker coverage, DOMAIN-03/06 verification gaps, documentation staleness, orphaned code cleanup. Fixes traceability table phase assignments to match actual roadmap phases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(03): commit generated NewsService OpenAPI specs + checkpoint update SummarizeArticle RPC was added during Phase 3 plan 02 but generated OpenAPI specs were not staged with that commit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(04): research phase domain * docs(04): create phase plan * docs(04-01): fix ROADMAP.md Phase 3 staleness and delete .continue-here.md - Change Phase 3 heading from IN PROGRESS to COMPLETE - Check off plans 03-03, 03-04, 03-05 (all were already complete) - Delete stale .continue-here.md (showed task 3/10 in_progress but all 10 done) * feat(04-02): add circuit breakers to seismology, wildfire, climate, maritime - Seismology: wrap listEarthquakes in breaker.execute with empty-array fallback - Wildfire: replace manual try/catch with breaker.execute for listFireDetections - Climate: replace manual try/catch with breaker.execute for listClimateAnomalies - Maritime: wrap proto getVesselSnapshot RPC in snapshotBreaker.execute, preserve raw relay fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(04-02): add circuit breakers to news summarization and GDELT intelligence - Summarization: wrap newsClient.summarizeArticle in summaryBreaker.execute for both tryApiProvider and translateText - GDELT: wrap client.searchGdeltDocuments in gdeltBreaker.execute, replace manual try/catch - Fix: include all required fields (tokens, reason, error, errorType, query) in fallback objects - CLIENT-03 fully satisfied: all 17 domains have circuit breaker coverage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(04-01): create 2L-VERIFICATION.md, fix desktop-readiness.ts, complete service barrel - Create retroactive 2L-VERIFICATION.md with 12/12 must-haves verified - Fix map-layers-core and market-panel stale file refs in desktop-readiness.ts - Fix opensky-relay-cloud stale refs (api/opensky.js deleted) - Add missing barrel re-exports: conflict, displacement, research, wildfires, climate - Skip military/intelligence/news barrels (would cause duplicate exports) - TypeScript compiles cleanly with zero errors * docs(04-02): complete circuit breaker coverage plan - SUMMARY.md: 6 domains covered, CLIENT-03 satisfied, 1 deviation (fallback type fix) - STATE.md: Phase 4 plan 02 complete, position and decisions updated - ROADMAP.md: Phase 04 marked complete (2/2 plans) - REQUIREMENTS.md: CLIENT-03 marked complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(04-01): complete documentation fixes plan - Create 04-01-SUMMARY.md with execution results - Update STATE.md with Plan 01 completion and decisions - Update ROADMAP.md: Plan 04-01 checked, progress 1/2 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(phase-04): complete phase verification and fix tracking gaps ROADMAP.md Phase 4 status updated to Complete, 04-02 checkbox checked, progress table finalized. REQUIREMENTS.md coverage summary updated (27 complete, 0 partial/pending). STATE.md reflects verified phase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(military): migrate USNI fleet tracker to sebuf RPC Port all USNI Fleet Tracker parsing logic from api/usni-fleet.js into MilitaryService.GetUSNIFleetReport RPC with proto definitions, inline Upstash caching (6h fresh / 7d stale), and client adapter mapping. Deletes legacy edge function and _upstash-cache.js dependency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: add proto validation annotations and split all 17 handler files into per-RPC modules Phase A: Added buf.validate constraints to ~25 proto files (~130 field annotations including required IDs, score ranges, coordinate bounds, page size limits). Phase B: Split all 17 domain handler.ts files into per-RPC modules with thin re-export handler.ts files. Extracted shared Redis cache helpers to api/server/_shared/redis.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add ADDING_ENDPOINTS guide and update Contributing section All JSON endpoints must use sebuf — document the complete workflow for adding RPCs to existing services and creating new services, including proto conventions, validation annotations, and generated OpenAPI docs. Update DOCUMENTATION.md Contributing section to reference the new guide and remove the deprecated "Adding a New API Proxy" pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add blank lines before lists to pass markdown lint (MD032) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: disambiguate duplicate city keys in city-coords Vercel's TypeScript check treats duplicate object keys as errors (TS1117). Rename 'san jose' (Costa Rica) -> 'san jose cr' and 'cambridge' (UK) -> 'cambridge uk' to avoid collision. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve Vercel deployment errors — relocate hex-db + fix TS strict-null - Move military-hex-db.js next to handler (fixes Edge Function unsupported module) - Fix strict-null TS errors across 12 handler files (displacement, economic, infrastructure, intelligence, market, military, research, wildfire) - Add process declare to wildfire handler, prefix unused vars, cast types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: convert military-hex-db to .ts for Edge Function compatibility Vercel Edge bundler can't resolve .js data modules from .ts handlers. Also remove unused _region variable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: inline military hex db as packed string to avoid Edge Function module error Vercel Edge bundler can't resolve separate data modules. Inline 20K hex IDs as a single concatenated string, split into Set at runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove broken export { process } from 4 _shared files declare const is stripped in JS output, making export { process } reference nothing. No consumers import it — each handler file has its own declare. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: move server/ out of api/ to fix Vercel catch-all routing Vercel's file-based routing was treating api/server/**/*.ts as individual API routes, overriding the api/[[...path]].ts catch-all for multi-segment paths like /api/infrastructure/v1/list-service-statuses (3 segments). Moving to server/ at repo root removes the ambiguity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: rename catch-all to [...path] — [[...path]] is Next.js-only syntax Vercel's native edge function routing only supports [...path] for multi-segment catch-all matching. The [[...path]] double-bracket syntax is a Next.js feature and was only matching single-segment paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add dynamic segment route for multi-segment API paths Vercel's native file-based routing for non-Next.js projects doesn't support [...path] catch-all matching multiple segments. Use explicit api/[domain]/v1/[rpc].ts which matches /api/{domain}/v1/{rpc} via standard single-segment dynamic routing that Vercel fully supports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove conflicting [...path] catch-all — replaced by [domain]/v1/[rpc] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: widen CORS pattern to match all Vercel preview URL formats Preview URLs use elie-ab2dce63 not elie-habib-projects as the team slug. Broaden pattern to elie-[a-z0-9]+ to cover both. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update sidecar build script for new api/[domain]/v1/[rpc] path Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: trigger Vercel rebuild for all variants Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #106 review — critical bugs, hardening, and cleanup Fixes from @koala73's code review: Critical: - C-1: Add max-size eviction (2048 cap) to GeoIP in-memory cache - C-2: Move type/source/severity filters BEFORE .slice(pageSize) in cyber handler - C-3: Atomic SET with EX in Redis helper (single Upstash REST call) - C-4: Add AbortSignal.timeout(30s) to LLM fetch in summarize-article High: - H-1: Add top-level try/catch in gateway with CORS-aware 500 response - H-3: Sanitize error messages — generic text for 5xx, passthrough for 4xx only - H-4: Add timeout (10s) + Redis cache (5min) to seismology handler - H-5: Add null guards (optional chaining) in seismology USGS feature mapping - H-6: Race OpenSky + Wingbits with Promise.allSettled instead of sequential fallback - H-8: Add Redis cache (5min TTL) to infrastructure service-status handler Medium: - M-12/M-13: Fix HAPI summary field mappings (iso3 from countryCode, internallyDisplaced) Infrastructure: - R-1: Remove .planning/ from git tracking, add to .gitignore - Port UCDP parallel page fetching from main branch (#198) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #106 review issues — hash, CORS, router, cache, LLM, GeoIP Fixes/improvements from PR #106 code review tracking issues: - #180: Replace 32-bit hash (Java hashCode/DJB2) with unified FNV-1a 52-bit hash in server/_shared/hash.ts, greatly reducing collision probability - #182: Cache router construction in Vite dev plugin — build once, invalidate on HMR changes to server/ files - #194: Add input length limits for LLM prompt injection (headlines 500 chars, title 500 chars, geoContext 2000 chars, max 10 headlines) - #195/#196: GeoIP AbortController — cancel orphaned background workers on timeout instead of letting them fire after response is sent - #198: Port UCDP partial-result caching from main — 10min TTL for partial results vs 6hr for complete, with in-memory fallback cache Proto codegen regenerated for displacement + conflict int64_encoding changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: restore fast-xml-parser dependency needed by sebuf handlers Main branch removed fast-xml-parser in v2.5.1 (legacy edge functions no longer needed it), but sebuf handlers in aviation/_shared.ts and research/list-arxiv-papers.ts still import it for XML API parsing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: fix stale paths, version badge, and local-backend-audit for sebuf - ADDING_ENDPOINTS.md: fix handler paths from api/server/ to server/, fix import depth (4 levels not 5), fix gateway extension (.js not .ts) - DOCUMENTATION.md: update version badge 2.1.4 -> 2.5.1, fix broken ROADMAP.md links to .planning/ROADMAP.md, fix handler path reference - COMMUNITY-PROMOTION-GUIDE.md: add missing v2.5.1 to version table - local-backend-audit.md: rewrite for sebuf architecture — replace all stale api/*.js references with sebuf domain handler paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: use make commands, add generation-before-push warning, bump sebuf to v0.7.0 - ADDING_ENDPOINTS.md: replace raw `cd proto && buf ...` with `make check`, `make generate`, `make install`; add warning that `make generate` must run before pushing proto changes (links to #200) - Makefile: bump sebuf plugin versions from v0.6.0 to v0.7.0 - PR description also updated to use make commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: make install installs everything, document setup - Makefile: `make install` now installs buf, sebuf plugins, npm deps, and proto deps in one command; pin buf and sebuf versions as variables - ADDING_ENDPOINTS.md: updated prerequisites to show `make install` - DOCUMENTATION.md: updated Installation section with `make install` and generation reminder Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #106 re-review issues — timeouts, iso3, pagination, CORS, dedup - NEW-1: HAPI handler now returns ISO-3 code (via ISO2_TO_ISO3 lookup) instead of ISO-2 - NEW-3: Aviation FAA fetch now has AbortSignal.timeout(15s) - NEW-4: Climate Open-Meteo fetch now has AbortSignal.timeout(20s) - NEW-5: Wildfire FIRMS fetch now has AbortSignal.timeout(15s) - NEW-6: Seismology now respects pagination.pageSize (default 500) - NEW-9: Gateway wraps getCorsHeaders() in try/catch with safe fallback - NEW-10: Tech events dedup key now includes start year to avoid dropping yearly variants Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #106 round 2 — seismology crash, tsconfig, type contracts - Fix _req undefined crash in seismology handler (renamed to req) - Cache full earthquake set, slice on read (avoids cache pollution) - Add server/ to tsconfig.api.json includes (catches type errors at build) - Remove String() wrappers on numeric proto fields in displacement/HAPI - Fix hashString re-export not available locally in news/_shared.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: loadMarkets heatmap regression, stale test, Makefile playwright - Add finnhub_skipped + skip_reason to ListMarketQuotesResponse proto - Wire skipped signal through handler → adapter → App.loadMarkets - Fix circuit breaker cache conflating different symbol queries (cacheTtlMs: 0) - Use dynamic fetch wrapper so e2e test mocks intercept correctly - Update e2e test mocks from old endpoints to sebuf proto endpoints - Delete stale summarization-chain.test.mjs (imports deleted files) - Add install-playwright target to Makefile Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing proto fields to emptyStockFallback (build fix) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quick-1): fix country fallback, ISO-2 contract, and proto field semantics - BLOCKING-1: Return undefined when country has no ISO3 mapping instead of wrong country data - BLOCKING-2: country_code field now returns ISO-2 per proto contract - MEDIUM-1: Rename proto fields from humanitarian to conflict-event semantics (populationAffected -> conflictEventsTotal, etc.) - Update client service adapter to use new field names - Regenerate TypeScript types from updated proto Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(quick-1): add in-memory cache + in-flight dedup to AIS vessel snapshot - HIGH-1: 10-second TTL cache matching client poll interval - Concurrent requests share single upstream fetch (in-flight dedup) - Follows same pattern as get-macro-signals.ts cache - No change to RPC response shape Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quick-1): stub RPCs throw UNIMPLEMENTED, remove hardcoded politics, add tests - HIGH-2: listNewsItems, summarizeHeadlines, listMilitaryVessels now throw UNIMPLEMENTED - LOW-1: Replace hardcoded "Donald Trump" with date-based dynamic LLM context - LOW-1 extended: Also fix same issue in intelligence/get-country-intel-brief.ts (Rule 2) - MEDIUM-2: Add tests/server-handlers.test.mjs with 20 tests covering all review items Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quick-2): remove 3 dead stub RPCs (ListNewsItems, SummarizeHeadlines, ListMilitaryVessels) - Delete proto definitions, handler stubs, and generated code for dead RPCs - Clean _shared.ts: remove tryGroq, tryOpenRouter, buildPrompt, dead constants - Remove 3 UNIMPLEMENTED stub tests from server-handlers.test.mjs - Regenerate proto codegen (buf generate) and OpenAPI docs - SummarizeArticle and all other RPCs remain intact Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #106 review findings (stale snapshot, iso naming, scoring, test import) - Serve stale AIS snapshot on relay failure instead of returning undefined - Rename HapiConflictSummary.iso3 → iso2 to match actual ISO-2 content - Fix HAPI fallback scoring: use weight 3 for combined political violence (civilian targeting is folded in, was being underweighted at 0) - Extract deduplicateHeadlines to shared .mjs so tests import production code Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add coverage for PR106 iso2 and fallback regressions --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Elie Habib <elie.habib@gmail.com> |