diff --git a/FRONTEND_REDESIGN_REPORT.md b/FRONTEND_REDESIGN_REPORT.md
index 5b64e2fa..6914ea8f 100644
--- a/FRONTEND_REDESIGN_REPORT.md
+++ b/FRONTEND_REDESIGN_REPORT.md
@@ -1,262 +1,137 @@
-# 前端重新设计完成报告
+# 前端交付与重构报告(2026-03-12)
-## 概述
+## 1. 报告目的
-按照 `docs/images/demo_map.png` 的设计,已成功重新设计和实现了 PolyWeather 首页核心布局。
+本报告用于说明当前线上前端(`frontend/`)的真实实现状态,替代旧版“单次改版完成报告”。
-## 核心设计
+---
-### 三列布局架构
+## 2. 当前前端架构
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 顶部导航栏 (高度: 48px) │
-├──────────────┬──────────────────────────────┬───────────────┤
-│ │ │ │
-│ 左侧边栏 │ 中央地图展示区 │ 右侧详情面板 │
-│ (宽: 192px)│ (Leaflet全球地图) │ (宽: 288px) │
-│ │ │ │
-│ • Logo │ • 全球地图视图 │ • 大温度显示 │
-│ • 搜索框 │ • 彩色圆形标记 │ • 观测信息 │
-│ • 城市列表 │ • 风险等级标色 │ • 小时趋势图 │
-│ • 温度显示 │ │ • 概率分布 │
-│ │ │ • 多模型预报 │
-│ │ │ • 多日预报 │
-└──────────────┴──────────────────────────────┴───────────────┘
+```mermaid
+flowchart LR
+ B["Browser"] --> N["Next.js App Router (Vercel)"]
+ N --> RH["Route Handlers /api/*"]
+ RH --> F["FastAPI (VPS)"]
+
+ N --> STORE["Dashboard Store"]
+ STORE --> MAP["MapCanvas"]
+ STORE --> SIDEBAR["CitySidebar"]
+ STORE --> PANEL["DetailPanel + Modal"]
```
-x
+### 2.1 组件分层(实际)
-## 实现细节
+- 页面入口:`frontend/app/page.tsx` + `frontend/components/dashboard/DashboardEntry.tsx`
+- 核心容器:`frontend/components/dashboard/PolyWeatherDashboard.tsx`
+- 主要视图:
+ - `MapCanvas.tsx`
+ - `CitySidebar.tsx`
+ - `DetailPanel.tsx`
+ - `FutureForecastModal.tsx`
+ - `HistoryModal.tsx`
+ - `GuideModal.tsx`
+- 状态管理:`frontend/hooks/useDashboardStore.tsx`
-### 1. 左侧城市列表面板 (192px)
+---
-```
-功能:
- • Logo 和标题: "PolyWeather"
- • 搜索输入框: 搜索城市功能
- • 城市列表:
- - 显示所有支持的城市
- - 当前温度显示
- - 风险等级颜色指示器
- - 活跃城市高亮显示
- - 可点击选择切换城市
+## 3. 本轮已落地能力
-样式:
- - 背景: slate-900/50
- - 边框: slate-800
- - 活跃状态: cyan-500 高亮
- - 悬停效果: bg-slate-800/30
+### 3.1 侧栏风险分组折叠(已完成)
+
+- 按 `high / medium / low / other` 分组展示城市。
+- 分组支持折叠/展开。
+- 保留“本地时间”和“峰值时间”显示。
+- 折叠状态持久化到 `localStorage`(`polyWeather_sidebar_groups_v1`)。
+
+### 3.2 选中城市状态持久化(已完成)
+
+- 最近一次选中城市持久化到 `localStorage`(`polyWeather_selected_city_v1`)。
+- 页面刷新后自动恢复。
+
+### 3.3 未来日期分析与市场扫描(已完成)
+
+- 前端通过 `target_date` 调用 `/api/city/{name}/detail`。
+- 未来日期 modal 可展示对应日期的模型概率与市场扫描。
+
+### 3.4 市场概率分布去重保护(已完成)
+
+- 后端温度桶去重后,前端仍保留兜底去重逻辑。
+- 避免“同温度重复四行”导致的可视化误导。
+
+### 3.5 可访问性修复(已完成)
+
+- 解决详情侧栏关闭时 `aria-hidden` 焦点冲突。
+- 方案:`inert` + `activeElement.blur()`。
+
+### 3.6 图标与性能观测(已完成)
+
+- 已接入 favicon/Apple touch icon/manifest。
+- 已集成 Vercel Speed Insights 与 Analytics。
+
+---
+
+## 4. 缓存与性能策略(当前状态)
+
+### 4.1 BFF HTTP 缓存(Vercel)
+
+- `/api/cities`:`ETag` + `s-maxage=300`
+- `/api/city/{name}/summary`:`ETag` + `s-maxage=20`
+- `/api/history/{name}`:`ETag` + `s-maxage=60`
+- `summary?force_refresh=true`:`Cache-Control: no-store`
+
+### 4.2 前端本地缓存
+
+- `sessionStorage`:城市详情缓存(5 分钟 TTL + revision 探测)
+- 请求去重:并发请求合并(pending request map)
+- `localStorage`:选中城市 + 侧栏折叠状态
+
+### 4.3 当前明确未做
+
+- Service Worker Cache API
+- IndexedDB
+
+---
+
+## 5. 验收记录
+
+### 5.1 前端缓存验收脚本
+
+```bash
+./scripts/validate_frontend_cache.sh "https://polyweather-pro.vercel.app"
```
-### 2. 中央地图展示区 (Leaflet)
+当前结果:`PASS (14 passed)`。
-```
-功能:
- • Leaflet 全球地图
- • 动态缩放控制
- • 多个城市标记
+### 5.2 自动化测试
-标记样式:
- • 圆形标记,半径 24px
- • 显示当前温度(度数字)
- • 颜色编码 (基于风险等级):
- - 高风险: 红色 (#ef4444)
- - 中风险: 橙色 (#f97316)
- - 低风险: 绿色 (#10b981)
- - 默认: 青色 (#06b6d4)
- • 发光效果: box-shadow
- • 点击弹窗显示详细信息
+```bash
+.\\venv\\Scripts\\python.exe -m pytest -q
```
-### 3. 右侧详情面板 (288px)
+当前结果:`31 passed`。
-#### a. 大温度显示区域
+---
-```
-┌─────────────────────────────────┐
-│ │
-│ 12.0°C │
-│ (文字大小: 3xl/48px) │
-│ (颜色: 青色 - cyan-400) │
-│ │
-│ 观测时间: 11°C @17:00 │
-│ │
-│ ┌────┬────┬────┐ │
-│ │Obs │DEB │Fcst│ │
-│ │1.0 │6.6 │5.9 │ (三列参考) │
-│ └────┴────┴────┘ │
-│ │
-└─────────────────────────────────┘
-```
+## 6. 风险与改进点
-#### b. 小时趋势图
+1. `frontend/.next` 构建产物在本地可见,需继续确保不进入版本管理。
+2. 前端缓存策略已覆盖 P0+P1,但离线能力仍未建设(无 SW/IndexedDB)。
+3. 多源数据仍依赖后端聚合延迟,前端仅能做缓存与降噪,不可替代后端刷新节奏。
-```
-简化的柱状图:
- • 显示最近 12 小时的温度
- • 动态高度基于温度值
- • 渐变颜色: cyan-500 → cyan-400
- • 高度: 80px
-```
+---
-#### c. 概率分布
+## 7. 结论
-```
-水平条形图:
- • 显示温度概率分布
- • 显示前 3 个最高概率的温度范围
- • 动态条形宽度
- • 渐变背景: cyan-500 → emerald-500
-```
+当前前端已从“单体页面”演进为组件化 dashboard,具备:
-#### d. 多模型预报
+- 风险分组侧栏与状态持久化
+- 未来日期分析与市场扫描联动
+- BFF 标准缓存头(`ETag/304`)
+- 可访问性修复与基础性能观测
-```
-模型列表 (GFS, ECMWF, ICON, GEM, Open-Meteo, DEB):
- • 每个模型显示一条横向进度条
- • 指示器显示预报值在范围内的位置
- • 显示精确温度值
- • 支持动态模型范围计算
-```
+可以支持继续推进商业化接入,但支付相关能力仍需后端与权限体系配套完成。
-#### e. 多日预报
+---
-```
-4 天预报卡片:
- • 网格布局 (4列)
- • 显示日期 (Day 0, Day 1, ...)
- • 显示最高温度
- • 简洁的卡片设计
-```
-
-## 颜色主题
-
-### 暗色主题 (Dark Mode)
-
-```
-背景:
- - 主背景: slate-950 (#030712)
- - 面板背景: slate-900/50
- - 组件背景: slate-800/50
-
-文本:
- - 主文本: slate-200
- - 次文本: slate-400
- - 强调文本: cyan-400 (#06b6d4)
-
-边框:
- - 主边框: slate-800
- - 次边框: slate-700/50
-
-强调色:
- - 主强调: cyan-400 (#06b6d4)
- - 辅助强调: emerald-500 (#10b981)
- - 警告: rose-500 (#ef4444)
-```
-
-## 文件变更
-
-### 新建文件
-
-1. **`frontend/components/dashboard/map-dashboard.tsx`** (新建)
- - 核心三列布局组件
- - 城市列表、地图、详情面板的主容器
- - 包含所有数据处理和渲染逻辑
-
-### 修改文件
-
-1. **`frontend/app/page.tsx`**
- - 重新设计,移除 `TerminalDashboard`
- - 改用新的 `MapDashboard` 组件
- - 简化页面结构,减少代码冗余
-
-2. **`frontend/components/dashboard/map-view.tsx`**
- - 更新地图标记类型定义
- - 改用新的 `createTemperatureMarker` 标记样式
- - 适配新的数据结构 (`color`, `temp` 而非 `value`)
-
-## 技术实现
-
-### 关键技术点
-
-1. **动态导入 (Dynamic Import)**
-
- ```typescript
- const MapView = dynamic(() => import("@/components/dashboard/map-view"), {
- ssr: false,
- loading: () =>
Loading map...
,
- });
- ```
-
- - 解决 Leaflet SSR 兼容性问题
- - 提高首屏加载性能
-
-2. **响应式布局**
- - Flex 布局实现三列设计
- - 固定宽度边栏 (192px, 288px)
- - 弹性地图中央区域
-
-3. **数据可视化**
- - 柱状图 (hourly trend)
- - 条形图 (probability, models)
- - 网格布局 (daily forecast)
-
-4. **交互设计**
- - 城市列表可点击切换
- - 地图标记可悬停显示信息
- - 搜索框输入过滤城市
-
-## 性能优化
-
-1. **编译状态**
- ✅ 编译成功
- ✅ 没有 TypeScript 错误
- ✅ 没有构建警告
-
-2. **打包大小**
- - 首页大小: 8.29 kB
- - 首屏 JS: 121 kB
- - 支持静态预渲染
-
-## 部署检查清单
-
-- [x] 前端编译通过
-- [x] 无 TypeScript 错误
-- [x] 无构建警告
-- [x] SSR 兼容性解决
-- [x] 数据绑定就绪
-- [x] 响应式设计完成
-- [x] 颜色主题应用
-- [x] 交互功能实现
-
-## 下一步工作
-
-1. **数据集成**
- - 从后端 API 动态获取所有城市数据
- - 实现实时数据更新
-
-2. **城市级地图**
- - 为每个城市添加多个测站标记
- - 显示周边观测点信息
-
-3. **高级分析**
- - 实现点击地图标记显示详细分析
- - 添加时间滑块用于历史数据回放
-
-4. **市场数据集成**
- - 在详情面板添加 Polymarket 市场信息
- - 显示市场价格和概率对比
-
-## 总结
-
-✅ **首页设计完全按照 `demo_map.png` 的风格重新实现**
-
-- 三列布局清晰分工
-- 暗色主题统一协调
-- 数据可视化专业美观
-- 交互流畅直观
-- 代码结构清晰可维护
-- 编译部署无误
-
-前端现已准备就绪,可以与后端 API 进行数据集成测试。
+最后更新:`2026-03-12`
diff --git a/README.md b/README.md
index f15be849..5e3bdea4 100644
--- a/README.md
+++ b/README.md
@@ -14,121 +14,99 @@ Official dashboard: [polyweather-pro.vercel.app](https://polyweather-pro.vercel.

-## What This Project Does
+## Core Capabilities
-- Aggregates weather observations and forecasts for monitored cities.
-- Blends multi-model forecasts with DEB (Dynamic Error Balancing).
-- Computes settlement-oriented probability buckets (mu-centered distribution).
-- Maps model view to Polymarket read-only market data for mispricing/risk scan.
-- Delivers the same core logic to web dashboard and Telegram bot.
+- Aggregates real-time observations and forecasts for 20 monitored cities.
+- Uses DEB (Dynamic Error Balancing) to blend multi-model highs.
+- Produces settlement-oriented probability buckets (`mu` + bucket distribution).
+- Maps weather model view to Polymarket read-only quotes for mispricing scan.
+- Serves the same analysis core to web dashboard and Telegram bot.
-## Overview Diagram
+## Architecture (Current)
+
+```mermaid
+flowchart LR
+ U["Users (Web / Telegram)"] --> FE["Next.js Frontend (Vercel)"]
+ U --> BOT["Telegram Bot (VPS)"]
+ FE --> API["FastAPI /web/app.py"]
+ BOT --> API
+
+ API --> WX["Weather Collector"]
+ WX --> METAR["Aviation Weather (METAR)"]
+ WX --> MGM["MGM (Turkey station network)"]
+ WX --> OM["Open-Meteo"]
+ WX --> NWS["weather.gov (US cities)"]
+
+ API --> ANALYSIS["DEB + Trend + Probability + Market Scan"]
+ ANALYSIS --> PM["Polymarket Read-only Layer"]
+```
+
+## Bot Runtime Layout
```mermaid
flowchart TD
- A["PolyWeather Pro"]
-
- subgraph DL["Data Layer"]
- DL1["METAR (Aviation Weather / METAR)"]
- DL2["MGM (Turkey MGM)"]
- DL3["Station 17130 (Ankara Center 17130)"]
- DL4["Open-Meteo"]
- DL5["weather.gov (US cities)"]
- DL6["Polymarket (P0 Read-only)"]
- end
-
- subgraph AL["Analysis Layer"]
- AL1["DEB (Dynamic Error Balancing)"]
- AL2["Probability Engine (mu + buckets)"]
- AL3["Trend Engine"]
- AL4["Risk Profiles"]
- AL5["Mispricing Radar"]
- end
-
- subgraph DEL["Delivery Layer"]
- DEL1["FastAPI"]
- DEL2["Next.js Dashboard"]
- DEL3["Telegram Bot"]
- DEL4["Alert Push"]
- end
-
- subgraph OL["Ops Layer"]
- OL1["Docker Compose (VPS backend + bot)"]
- OL2["Vercel (frontend)"]
- OL3["Cache + force_refresh"]
- OL4["Speed Insights"]
- end
-
- A --> DL
- A --> AL
- A --> DEL
- A --> OL
+ E["bot_listener.py"] --> O["src/bot/orchestrator.py"]
+ O --> H["src/bot/handlers/*"]
+ O --> S["src/bot/services/*"]
+ O --> A["src/bot/analysis/*"]
+ O --> G["src/bot/command_guard.py"]
+ O --> R["src/bot/runtime_coordinator.py"]
```
-## Architecture
+## Source Policy
-```mermaid
-graph TD
- User[Web / Telegram User] --> FE[Next.js Frontend on Vercel]
- User --> Bot[Telegram Bot on VPS]
- FE --> API[FastAPI Service]
- Bot --> API
+| Domain | Current Policy |
+| :-- | :-- |
+| Primary observation | Aviation Weather / METAR |
+| Ankara enhancement | MGM + nearby stations, lead station fixed to `17130` |
+| Forecast baseline | Open-Meteo + multi-model (ECMWF/GFS/ICON/GEM/JMA) |
+| US official context | weather.gov |
+| Market layer | Polymarket P0 read-only discovery + quotes |
+| Removed source | Meteoblue (fully removed from runtime and docs) |
- API --> WX[Weather Data Collector]
- WX --> METAR[METAR / Aviation Weather]
- WX --> MGM[MGM API / nearby stations]
- WX --> OM[Open-Meteo]
- WX --> NWS[weather.gov]
+## Monitored Cities (20)
- API --> DEB[DEB + Trend + Probability Engines]
- API --> PM[Polymarket Read-only Layer]
- PM --> Gamma[Gamma API]
- PM --> CLOB[CLOB / py-clob-client]
-```
+- Europe / Middle East: Ankara, London, Paris, Munich
+- APAC: Seoul, Hong Kong, Shanghai, Singapore, Tokyo, Wellington
+- Americas: Toronto, New York, Chicago, Dallas, Miami, Atlanta, Seattle, Buenos Aires, Sao Paulo
+- South Asia: Lucknow
-## Current Source Policy
+## Major Updates (2026-03-12)
-| Domain | Source Policy |
-| :------------------ | :--------------------------------------------------- |
-| Primary observation | Aviation Weather / METAR |
-| Ankara enhancement | MGM + nearby stations, lead station fixed to `17130` |
-| Forecast baseline | Open-Meteo |
-| US official context | weather.gov |
-| Market layer | Polymarket P0 read-only discovery + quotes |
-| Removed source | Meteoblue (fully removed from code and docs) |
+1. Bot architecture refactor completed:
+ - `bot_listener.py` is now a thin entrypoint.
+ - Core runtime moved to orchestrator + handlers/services/analysis layers.
+ - Startup loops managed by `StartupCoordinator`, with `/diag` diagnostics.
+2. Mispricing radar hardened:
+ - Anchor changed from single Open-Meteo settlement to multi-model highest-high anchor.
+ - Skip non-tradable markets (`closed`, inactive, not accepting orders, or past end time).
+ - Future-date scan supported via `target_date` in detail aggregate endpoint.
+3. Wallet activity watcher upgraded:
+ - Wallet aliases (`POLYMARKET_WALLET_ACTIVITY_USER_ALIASES`) supported.
+ - Telegram link preview toggle (`POLYMARKET_WALLET_ACTIVITY_LINK_PREVIEW`) supported.
+ - Debounce + immediate delta push controls reduce noisy spam bursts.
+4. Frontend P0+P1 cache and UX improvements:
+ - BFF `ETag + 304` on `/api/cities`, `/api/city/{name}/summary`, `/api/history/{name}`.
+ - `force_refresh=true` on summary keeps `Cache-Control: no-store`.
+ - `sessionStorage` city-detail cache + background summary revision probe.
+ - `localStorage` persistence for selected city and risk-group collapse state.
+ - Detail panel accessibility fix (`inert` + active-element blur).
+5. Observability:
+ - Vercel Speed Insights integrated.
+ - Telegram alert/watcher startup diagnostics exposed through `/diag`.
-## Recent Changes (2026-03-12)
+## Repository Layout
-- Removed all Meteoblue API integration and references.
-- Added frontend BFF `ETag + Cache-Control` for:
- - `/api/cities`
- - `/api/city/{name}/summary` (`force_refresh=true` keeps `no-store`)
- - `/api/history/{name}`
-- Added frontend state persistence:
- - selected city in `localStorage`
- - risk-group collapse state in sidebar `localStorage`
- - background summary revision check to silently refresh stale detail cache
-- Mispricing radar safety hardening:
- - skip non-tradable markets (`closed`, inactive, not accepting orders, or past `endDate`)
- - propagate tradable state in `market_scan.primary_market`
-- AI decision guard:
- - peak-window state (`before` / `in_window` / `past`) now explicitly injected into AI context
- - before-peak state now forbids "locked/confirmed floor" style conclusions
-- Fixed market top-bucket rendering path by deduplicating repeated temperature buckets.
-- Added frontend fallback guard when market top buckets collapse to low-quality duplicates.
-- Fixed detail panel accessibility issue (`aria-hidden` focus conflict) using `inert` + active-element blur.
-- Added Vercel Speed Insights integration in `frontend/app/layout.tsx`.
-
-## Repositories and Runtime Paths
-
-- Frontend: `frontend/` (Next.js App Router)
-- Backend API: `web/app.py` and `src/`
-- Telegram runtime: `bot_listener.py` + `src/analysis/*`
+- Frontend: `frontend/`
+- Backend API: `web/app.py`, `src/`
+- Telegram bot runtime: `bot_listener.py`, `src/bot/*`
+- Wallet watchers: `src/onchain/*`
+- Ops scripts: `scripts/`
- Docs: `docs/`
## Quick Start
-### Backend + Bot (VPS / Docker)
+### Backend + Bot (Docker)
```bash
docker compose up -d --build
@@ -142,14 +120,14 @@ npm install
npm run dev
```
-### Frontend production build check
+### Frontend production build
```bash
cd frontend
npm run build
```
-## Operations Verification
+## Ops Verification
### Validate frontend cache headers (`ETag` / `304` / `force_refresh=no-store`)
@@ -157,31 +135,46 @@ npm run build
./scripts/validate_frontend_cache.sh "https://polyweather-pro.vercel.app"
```
-### Watch mispricing radar push decisions
+### Watch mispricing radar decisions
```bash
docker compose logs -f polyweather | egrep "market not tradable|trade alert pushed|mispricing cap"
```
-## Command Surface (Telegram)
+### Watch wallet activity watcher startup and pushes
-| Command | Purpose |
-| :------------- | :---------------------------- |
-| `/city ` | City real-time analysis |
-| `/deb ` | DEB historical reconciliation |
-| `/top` | User leaderboard |
-| `/help` | Help and command usage |
+```bash
+docker compose logs -f polyweather | egrep "wallet activity watcher started|wallet activity pushed|wallet activity cycle failed"
+```
+
+### Check bot startup diagnosis in Telegram
+
+```text
+/diag
+```
+
+## Telegram Command Surface
+
+| Command | Purpose |
+| :-- | :-- |
+| `/city ` | City real-time analysis |
+| `/deb ` | DEB historical reconciliation |
+| `/top` | User leaderboard |
+| `/id` | Show current chat ID |
+| `/diag` | Bot startup diagnostics and loop status |
+| `/help` | Help and usage |
## Documentation Index
+- Chinese overview: `README_ZH.md`
- Chinese API guide: `docs/API_ZH.md`
- Commercial roadmap: `docs/COMMERCIALIZATION.md`
- Tech debt (EN): `docs/TECH_DEBT.md`
- Tech debt (ZH): `docs/TECH_DEBT_ZH.md`
-- Chinese overview: `README_ZH.md`
+- Frontend delivery report: `FRONTEND_REDESIGN_REPORT.md`
## Status
- Version: `v1.3`
+- Test status: `31 passed` (`.\\venv\\Scripts\\python.exe -m pytest -q`)
- Last Updated: `2026-03-12`
-- Runtime: Stable (web + bot + market read-only layer in production)
diff --git a/README_ZH.md b/README_ZH.md
index 9d42f085..8492be95 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -14,120 +14,99 @@

-## 这个项目在做什么
+## 核心能力
-- 聚合监控城市的实测与预报数据。
-- 用 DEB(Dynamic Error Balancing)做动态融合预测。
-- 计算结算导向的温度概率分布(`μ` + 温度桶)。
-- 将模型概率与 Polymarket 只读市场数据对齐,输出错价/风险信号。
-- Web 仪表盘与 Telegram 机器人共用同一套核心逻辑。
+- 聚合 20 个监控城市的实时实测与预报数据。
+- 通过 DEB(Dynamic Error Balancing)融合多模型最高温。
+- 输出结算导向的概率分布(`mu` + 温度桶)。
+- 将模型观点映射到 Polymarket 只读市场,做错价扫描。
+- Web 仪表盘与 Telegram 机器人复用同一套分析内核。
-## 概览图
+## 当前架构
+
+```mermaid
+flowchart LR
+ U["用户(Web / Telegram)"] --> FE["Next.js 前端(Vercel)"]
+ U --> BOT["Telegram Bot(VPS)"]
+ FE --> API["FastAPI /web/app.py"]
+ BOT --> API
+
+ API --> WX["Weather Collector"]
+ WX --> METAR["Aviation Weather(METAR)"]
+ WX --> MGM["MGM(土耳其站网)"]
+ WX --> OM["Open-Meteo"]
+ WX --> NWS["weather.gov(美国城市)"]
+
+ API --> ANALYSIS["DEB + 趋势 + 概率 + 市场扫描"]
+ ANALYSIS --> PM["Polymarket 只读层"]
+```
+
+## Bot 运行分层
```mermaid
flowchart TD
- A["PolyWeather Pro"]
-
- subgraph DL["数据层"]
- DL1["METAR (Aviation Weather / METAR)"]
- DL2["MGM (土耳其 MGM)"]
- DL3["安卡拉主站 (17130 Center)"]
- DL4["Open-Meteo"]
- DL5["weather.gov (美国城市)"]
- DL6["Polymarket (P0 只读)"]
- end
-
- subgraph AL["分析层"]
- AL1["DEB (动态误差平衡)"]
- AL2["概率引擎 (mu + 桶分布)"]
- AL3["趋势引擎"]
- AL4["城市风险档案"]
- AL5["错价雷达"]
- end
-
- subgraph DEL["交付层"]
- DEL1["FastAPI"]
- DEL2["Next.js 仪表盘"]
- DEL3["Telegram Bot"]
- DEL4["预警推送"]
- end
-
- subgraph OL["运维层"]
- OL1["Docker Compose (VPS)"]
- OL2["Vercel (前端)"]
- OL3["缓存 + force_refresh"]
- OL4["Speed Insights"]
- end
-
- A --> DL
- A --> AL
- A --> DEL
- A --> OL
+ E["bot_listener.py"] --> O["src/bot/orchestrator.py"]
+ O --> H["src/bot/handlers/*"]
+ O --> S["src/bot/services/*"]
+ O --> A["src/bot/analysis/*"]
+ O --> G["src/bot/command_guard.py"]
+ O --> R["src/bot/runtime_coordinator.py"]
```
-## 系统架构
+## 数据源口径
-```mermaid
-graph TD
- User[Web / Telegram 用户] --> FE[Vercel Next.js 前端]
- User --> Bot[VPS Telegram Bot]
- FE --> API[FastAPI 服务]
- Bot --> API
+| 领域 | 当前口径 |
+| :-- | :-- |
+| 主观测源 | Aviation Weather / METAR |
+| Ankara 增强 | MGM + 周边站,领先站固定 `17130` |
+| 预报基线 | Open-Meteo + 多模型(ECMWF/GFS/ICON/GEM/JMA) |
+| 美国官方语义层 | weather.gov |
+| 市场层 | Polymarket P0 只读发现 + 报价 |
+| 已移除 | Meteoblue(代码与文档已彻底移除) |
- API --> WX[Weather Collector]
- WX --> METAR[METAR / Aviation Weather]
- WX --> MGM[MGM API / 周边站]
- WX --> OM[Open-Meteo]
- WX --> NWS[weather.gov]
+## 监控城市(20)
- API --> DEB[DEB + 趋势 + 概率引擎]
- API --> PM[Polymarket 只读层]
- PM --> Gamma[Gamma API]
- PM --> CLOB[CLOB / py-clob-client]
-```
+- 欧洲/中东:Ankara、London、Paris、Munich
+- 亚太:Seoul、Hong Kong、Shanghai、Singapore、Tokyo、Wellington
+- 美洲:Toronto、New York、Chicago、Dallas、Miami、Atlanta、Seattle、Buenos Aires、Sao Paulo
+- 南亚:Lucknow
-## 当前数据源口径
+## 本轮主要更新(2026-03-12)
-| 领域 | 当前口径 |
-| :------------- | :-------------------------------- |
-| 主观测源 | Aviation Weather / METAR |
-| Ankara 增强 | MGM + 周边站,领先站固定 `17130` |
-| 预报基线 | Open-Meteo |
-| 美国官方语义层 | weather.gov |
-| 市场层 | Polymarket P0 只读发现 + 报价 |
-| 已移除 | Meteoblue(代码与文档已全部移除) |
-
-## 最近更新(2026-03-12)
-
-- 完整移除 Meteoblue API 及全部引用。
-- 前端 BFF 增加 `ETag + Cache-Control`:
- - `/api/cities`
- - `/api/city/{name}/summary`(`force_refresh=true` 仍保持 `no-store`)
- - `/api/history/{name}`
-- 前端状态持久化优化:
- - 记住上次选中城市(`localStorage`)
- - 记住侧边栏风险分组折叠状态(`localStorage`)
- - 详情命中缓存时做后台 revision 检查,静默更新陈旧数据
-- 错价雷达安全加固:
- - 市场 `closed` / 不活跃 / 不接受下单 / 超过 `endDate` 时跳过推送
- - `market_scan.primary_market` 透传可交易状态字段
-- AI 决策时段约束:
- - 上下文显式注入峰值窗口状态(`before` / `in_window` / `past`)
- - 峰值窗口前禁止“已锁定/已确认底线”结论
-- 修复市场“最热温度桶”重复温度刷屏问题(后端按温度去重 + 前端兜底去重)。
-- 修复详情面板可访问性告警(`aria-hidden` 焦点冲突),改为 `inert + blur`。
-- 集成 Vercel Speed Insights(`frontend/app/layout.tsx`)。
+1. Bot 分层改造完成:
+ - `bot_listener.py` 变为极薄入口。
+ - 运行时迁移到 orchestrator + handlers/services/analysis 分层。
+ - 启动循环由 `StartupCoordinator` 统一编排,并通过 `/diag` 暴露诊断。
+2. 错价雷达口径升级:
+ - 锚点从“单一 Open-Meteo 结算”改为“多模型最高温锚点”。
+ - 不可交易市场硬拦截(`closed` / inactive / 不接单 / 过结束时间)。
+ - 未来日期分析支持 `target_date`(聚合详情接口)。
+3. 钱包异动监听升级:
+ - 支持钱包昵称映射(`POLYMARKET_WALLET_ACTIVITY_USER_ALIASES`)。
+ - 支持 Telegram 链接预览开关(`POLYMARKET_WALLET_ACTIVITY_LINK_PREVIEW`)。
+ - 增加 debounce + 立即推送控制,减少连续下单刷屏。
+4. 前端 P0+P1 缓存与体验优化:
+ - BFF 在 `/api/cities`、`/api/city/{name}/summary`、`/api/history/{name}` 返回 `ETag + 304`。
+ - `summary?force_refresh=true` 保持 `Cache-Control: no-store`。
+ - `sessionStorage` 详情缓存 + 后台 revision 静默探测。
+ - `localStorage` 持久化“选中城市”和“风险分组折叠状态”。
+ - 详情面板可访问性修复(`inert + active-element blur`)。
+5. 可观测性:
+ - 前端集成 Vercel Speed Insights。
+ - Bot 启动和后台循环状态可通过 `/diag` 查看。
## 目录说明
-- 前端:`frontend/`(Next.js App Router)
-- 后端:`web/app.py` 与 `src/`
-- 机器人:`bot_listener.py` + `src/analysis/*`
+- 前端:`frontend/`
+- 后端 API:`web/app.py`、`src/`
+- Telegram 机器人:`bot_listener.py`、`src/bot/*`
+- 钱包监听:`src/onchain/*`
+- 运维脚本:`scripts/`
- 文档:`docs/`
## 快速启动
-### 后端 + 机器人(VPS / Docker)
+### 后端 + Bot(Docker)
```bash
docker compose up -d --build
@@ -141,7 +120,7 @@ npm install
npm run dev
```
-### 前端构建校验
+### 前端生产构建
```bash
cd frontend
@@ -150,37 +129,52 @@ npm run build
## 运维验收
-### 验证前端缓存头(`ETag` / `304` / `force_refresh=no-store`)
+### 校验前端缓存头(`ETag` / `304` / `force_refresh=no-store`)
```bash
./scripts/validate_frontend_cache.sh "https://polyweather-pro.vercel.app"
```
-### 观察错价雷达推送决策日志
+### 观察错价雷达决策日志
```bash
docker compose logs -f polyweather | egrep "market not tradable|trade alert pushed|mispricing cap"
```
-## Telegram 命令
+### 观察钱包异动监听日志
-| 命令 | 用途 |
-| :------------- | :----------- |
+```bash
+docker compose logs -f polyweather | egrep "wallet activity watcher started|wallet activity pushed|wallet activity cycle failed"
+```
+
+### Telegram 启动诊断
+
+```text
+/diag
+```
+
+## Telegram 指令面
+
+| 指令 | 用途 |
+| :-- | :-- |
| `/city ` | 城市实时分析 |
-| `/deb ` | DEB 历史对账 |
-| `/top` | 用户排行榜 |
-| `/help` | 帮助说明 |
+| `/deb ` | DEB 历史对账 |
+| `/top` | 用户积分排行 |
+| `/id` | 查看当前聊天 Chat ID |
+| `/diag` | Bot 启动诊断与后台循环状态 |
+| `/help` | 帮助与用法 |
## 文档索引
+- 英文总览:`README.md`
- API 文档(中文):`docs/API_ZH.md`
- 商业化路线:`docs/COMMERCIALIZATION.md`
- 技术债(英文):`docs/TECH_DEBT.md`
- 技术债(中文):`docs/TECH_DEBT_ZH.md`
-- 英文总览:`README.md`
+- 前端交付报告:`FRONTEND_REDESIGN_REPORT.md`
## 当前状态
- 版本:`v1.3`
+- 测试状态:`31 passed`(`.\\venv\\Scripts\\python.exe -m pytest -q`)
- 最后更新:`2026-03-12`
-- 状态:稳定运行(Web + Bot + 市场只读层)
diff --git a/docs/API_ZH.md b/docs/API_ZH.md
index e295940a..24cf35c9 100644
--- a/docs/API_ZH.md
+++ b/docs/API_ZH.md
@@ -1,130 +1,90 @@
# PolyWeather API 文档(v1.3)
-本文档描述当前后端真实可用接口(`web/app.py`)。
-前端一般通过 Next.js BFF 路由代理访问这些接口。
+本文档基于当前代码(`web/app.py` + `frontend/app/api/*`)整理。
+前端默认通过 Next.js BFF 路由访问后端。
---
## 1. 基础信息
-- 本地地址:`http://127.0.0.1:8000`
-- 生产地址:`http://:8000` 或你绑定的 HTTPS 域名
+- 后端直连:`http://127.0.0.1:8000`
+- 前端 BFF:`https://polyweather-pro.vercel.app/api/*`
- 返回格式:`application/json`
-- 缓存策略:
- - 后端分析缓存:默认 5 分钟(Ankara 特殊口径 60 秒)
- - 前端详情缓存:5 分钟 + revision 检查 + 后台静默刷新
- - 前端 BFF HTTP 缓存(Vercel 层):
- - `/api/cities`:`ETag` + `Cache-Control`(`s-maxage=300`)
- - `/api/city/{name}/summary`:`ETag` + `Cache-Control`(`s-maxage=20`)
- - `/api/history/{name}`:`ETag` + `Cache-Control`(`s-maxage=60`)
- - `summary?force_refresh=true`:`Cache-Control: no-store`
- - 手动刷新:`force_refresh=true` 强制绕过缓存
+- 口径说明:
+ - 结算导向分析以温度最高值和温度桶概率为核心。
+ - Ankara 增强使用 MGM,主站固定 `17130`。
+ - Meteoblue 已移除,不再出现在任何有效字段中。
---
-## 2. API 思维导图
+## 2. 请求链路
```mermaid
-flowchart TD
- A["PolyWeather API"]
-
- subgraph E["接口分组"]
- E1["GET /api/cities"]
- E2["GET /api/city/{name}"]
- E3["GET /api/city/{name}/summary"]
- E4["GET /api/city/{name}/detail"]
- E5["GET /api/history/{name}"]
- end
-
- subgraph O["关键对象"]
- O1["current"]
- O2["forecast"]
- O3["probabilities (mu + distribution)"]
- O4["multi_model / multi_model_daily"]
- O5["market_scan (P0 只读)"]
- end
-
- A --> E
- A --> O
+flowchart LR
+ FE["Browser / Dashboard"] --> BFF["Next.js Route Handlers (/api/*)"]
+ BFF --> API["FastAPI (/web/app.py)"]
+ API --> WX["Weather Collector"]
+ API --> ANA["DEB + Trend + Probabilities + Market Scan"]
+ ANA --> PM["Polymarket Read-only Layer"]
```
---
## 3. 接口总览
-| 接口 | 方法 | 用途 |
-| :------------------------- | :--- | :------------------------------------ |
-| `/api/cities` | GET | 城市清单与地图基础信息 |
-| `/api/city/{name}` | GET | 城市主分析数据(侧栏/今日分析主来源) |
-| `/api/city/{name}/summary` | GET | 轻量摘要(首屏预热/低开销更新) |
-| `/api/city/{name}/detail` | GET | 聚合详情 + Polymarket P0 只读市场层 |
-| `/api/history/{name}` | GET | 历史对账数据 |
+| 接口 | 方法 | 用途 |
+| :-- | :-- | :-- |
+| `/api/cities` | GET | 监控城市列表(地图/侧栏) |
+| `/api/city/{name}` | GET | 城市主分析(今日分析核心数据) |
+| `/api/city/{name}/summary` | GET | 轻量摘要(首屏预热/低成本轮询) |
+| `/api/city/{name}/detail` | GET | 聚合详情(含 `market_scan`) |
+| `/api/history/{name}` | GET | 历史对账数据 |
---
-## 4. 关键接口详解
+## 4. 关键接口说明
### 4.1 `GET /api/cities`
-返回监控城市列表(地图 Marker 与侧边栏基础数据)。
+返回监控城市清单。
-示例:
+关键字段:
-```json
-{
- "cities": [
- {
- "name": "ankara",
- "display_name": "Ankara",
- "lat": 39.9334,
- "lon": 32.8597,
- "risk_level": "medium",
- "risk_emoji": "🟠",
- "airport": "Esenboğa",
- "icao": "LTAC",
- "temp_unit": "celsius",
- "is_major": true
- }
- ]
-}
-```
+- `name`, `display_name`
+- `lat`, `lon`
+- `risk_level`, `risk_emoji`
+- `airport`, `icao`
+- `temp_unit`(`celsius` / `fahrenheit`)
### 4.2 `GET /api/city/{name}`
-主数据接口,前端详情面板和今日分析最常用。
+主分析接口,返回当前实况、预报、概率、趋势、AI 分析等。
可选参数:
- `force_refresh=true|false`
-核心字段:
+关键字段:
-- `name`, `display_name`, `local_date`, `local_time`, `temp_symbol`
-- `risk`
-- `current`
-- `forecast`
+- `current`(温度、今日最高、METAR 观测时间、原始 METAR)
+- `forecast`(今日及多日高温、日出日落、日照时长)
- `mgm`, `mgm_nearby`
- `multi_model`, `multi_model_daily`
-- `deb`
-- `ensemble`
+- `deb`, `ensemble`
- `probabilities`(`mu` + `distribution`)
- `trend`, `peak`
- `hourly`, `hourly_next_48h`
-- `source_forecasts`(当前只保留 `weather_gov`)
-- `market_scan`
-- `updated_at`
-
-说明:
-
-- `current.raw_metar` 是原始 METAR 报文。
-- Ankara 专项增强使用 MGM 站网,领先站固定 `17130`。
-- Meteoblue 已彻底移除,不再出现在接口字段中。
+- `source_forecasts.weather_gov`
### 4.3 `GET /api/city/{name}/summary`
-轻量温度摘要,用于地图首屏预热和低成本刷新。
+轻量摘要接口,适合高频刷新列表。
-典型字段:
+可选参数:
+
+- `force_refresh=true|false`
+
+关键字段:
- `name`, `display_name`, `icao`
- `local_time`, `temp_symbol`
@@ -133,21 +93,17 @@ flowchart TD
- `risk.level`, `risk.warning`
- `updated_at`
-缓存说明:
-
-- 通过前端 BFF 访问时,默认返回 `ETag` 与可缓存 `Cache-Control`。
-- 当 `force_refresh=true` 时,BFF 强制 `no-store`,用于人工排障与即时刷新。
-
### 4.4 `GET /api/city/{name}/detail`
-聚合视图接口,包含天气分析和市场只读层。
+聚合详情接口,市场分析与未来日期分析都依赖该接口。
可选参数:
- `force_refresh=true|false`
-- `market_slug=`(调试/定向市场匹配)
+- `market_slug=`
+- `target_date=YYYY-MM-DD`
-关键结构:
+关键返回块:
- `overview`
- `official`
@@ -158,133 +114,112 @@ flowchart TD
- `risk`
- `ai_analysis`
-`market_scan`(P0 只读)重点字段:
+`market_scan` 重点字段:
-- `primary_market`, `selected_condition_id`, `selected_slug`
-- `yes_token`, `no_token`
+- `available`, `selected_date`, `selected_slug`, `signal_label`
- `yes_buy`, `yes_sell`, `no_buy`, `no_sell`
-- `market_price`, `model_probability`, `edge_percent`
-- `temperature_bucket`
-- `top_buckets`(前端展示前会再去重)
-- `signal_label`(`BUY YES` / `BUY NO` / `MONITOR`)
-- `websocket.asset_ids`, `websocket.condition_ids`(订阅标识,不涉及下单)
-- `primary_market.tradable`(是否可交易)
-- `primary_market.tradable_reason`(不可交易原因)
-- `primary_market.ended_at_utc`(UTC 结束时刻)
-- `primary_market.accepting_orders`(是否仍接收订单)
+- `temperature_bucket`, `forecast_bucket`, `top_buckets`
+- `anchor_model`, `anchor_high`, `anchor_settlement`
+- `open_meteo_settlement`(兼容旧字段,当前与 `anchor_settlement` 同值)
+- `primary_market.tradable`, `primary_market.tradable_reason`
+- `primary_market.accepting_orders`, `primary_market.ended_at_utc`
-注意:
+说明:
-- 后端已做温度桶去重与方向优先(优先与主市场同方向的 `or higher`/`or lower` 桶)。
-- 前端还有二次去重兜底,避免重复温度桶刷屏。
-- 错价雷达推送前会二次校验交易状态,若市场已不可交易(`closed` / inactive / 不接单 / 过 `endDate`)会跳过。
+- 当前错价锚点不是单一 Open-Meteo,而是“多模型最高温锚点”。
+- 推送层会再次校验市场可交易性,不可交易市场会跳过。
### 4.5 `GET /api/history/{name}`
-历史对账数据来源。
+历史对账接口。
-示例:
+关键字段:
-```json
-{
- "history": [
- {
- "date": "2026-03-07",
- "actual": 7.0,
- "deb": 6.5,
- "mu": 7.2,
- "mgm": 8.0
- }
- ]
-}
+- `date`
+- `actual`
+- `deb`
+- `mu`
+- `mgm`
+
+---
+
+## 5. 缓存与刷新策略(当前已实现)
+
+### 5.1 FastAPI 后端缓存
+
+- `_analyze` 结果内存缓存:默认 5 分钟
+- Ankara 特例:60 秒
+- `force_refresh=true`:绕过后端缓存
+
+### 5.2 Next.js BFF HTTP 缓存(Vercel)
+
+- `GET /api/cities`
+ - `ETag`
+ - `Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=1800`
+- `GET /api/city/{name}/summary`
+ - `ETag`
+ - `Cache-Control: public, max-age=0, s-maxage=20, stale-while-revalidate=60`
+- `GET /api/history/{name}`
+ - `ETag`
+ - `Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=300`
+- `summary?force_refresh=true`
+ - `Cache-Control: no-store`
+
+### 5.3 前端本地缓存
+
+- `sessionStorage`
+ - 城市详情缓存(5 分钟 TTL + revision 探测)
+- `localStorage`
+ - 上次选中城市
+ - 侧栏风险分组折叠状态
+
+### 5.4 尚未引入(当前明确未做)
+
+- Service Worker Cache API
+- IndexedDB
+
+---
+
+## 6. 常用调试示例
+
+### 6.1 查询未来日期 `market_scan`
+
+```bash
+curl -s "http://127.0.0.1:8000/api/city/ankara/detail?force_refresh=true&target_date=2026-03-12" \
+| python3 -c "import sys,json; m=json.load(sys.stdin).get('market_scan',{}); print({k:m.get(k) for k in ['available','selected_date','anchor_model','anchor_high','anchor_settlement','yes_buy','no_buy']})"
```
----
-
-## 5. 请求链路(以 `/api/city/{name}` 为例)
-
-```mermaid
-sequenceDiagram
- participant FE as Frontend
- participant API as FastAPI
- participant WX as Weather Collector
- participant PM as Polymarket RO Layer
-
- FE->>API: GET /api/city/{name}?force_refresh=...
- API->>WX: fetch_all_sources(city)
- WX-->>API: METAR / MGM / Open-Meteo / weather.gov / Multi-model
- API->>API: DEB + trend + probability
- API->>PM: build_market_scan(...)
- PM-->>API: market_scan (read-only)
- API-->>FE: merged city payload
-```
-
----
-
-## 6. 数据口径
-
-### 6.1 主观测
-
-- Aviation Weather / METAR 是全局主观测源。
-- Ankara:结算主站仍是 `LTAC`,领先信号强化使用 MGM(`17130`)。
-
-### 6.2 预测源
-
-- Open-Meteo
-- weather.gov(美国城市)
-- 多模型:ECMWF / GFS / ICON / GEM / JMA
-
-### 6.3 概率口径
-
-- `mu`:动态分布中心,不是固定结算值。
-- `distribution`:按温度桶输出概率分布,面向结算决策而非通用天气展示。
-
----
-
-## 7. 常见问题
-
-### 7.1 接口 500
-
-- 先检查容器是否启动:`docker compose ps`
-- 查看日志:`docker compose logs -f polyweather_web`
-
-### 7.2 METAR 看起来“延迟”
-
-优先核对:
-
-- `current.obs_time`
-- `current.report_time`
-- `current.receipt_time`
-
-通常是上游发布节奏,不一定是本地轮询问题。
-
-### 7.3 前端仍显示旧内容
-
-- 确认 Vercel 已部署最新构建
-- 浏览器强刷(`Ctrl+F5`)
-- 检查是否命中前端 5 分钟 TTL
-
-### 7.4 为什么 VPS `:8000` 看不到 `ETag`?
-
-- `:8000` 是 FastAPI 后端直连口径,主要负责分析与数据聚合。
-- `ETag/304` 主要由前端 BFF 路由返回(Vercel 域名下的 `/api/*`)。
-- 验证缓存头请用前端域名,而不是后端直连 IP。
-
----
-
-## 8. 验收脚本
-
-项目内置缓存验收脚本:
+### 6.2 验证前端缓存头
```bash
./scripts/validate_frontend_cache.sh "https://polyweather-pro.vercel.app"
```
-输出 `Result: PASS` 代表以下链路均正常:
+### 6.3 观察错价雷达跳过原因
-- `ETag` 返回
-- `If-None-Match -> 304`
-- `force_refresh=true -> no-store`
+```bash
+docker compose logs -f polyweather | egrep "market not tradable|trade alert pushed|mispricing cap"
+```
+
+---
+
+## 7. 常见问题
+
+### 7.1 VPS `:8000` 为什么看不到 `ETag`?
+
+- `:8000` 是 FastAPI 直连层,主要负责聚合分析。
+- `ETag/304` 在前端 BFF(Vercel 的 `/api/*`)侧实现。
+
+### 7.2 为什么 `target_date` 有时没有市场价格?
+
+- 该日期可能没有可交易市场。
+- 或目标桶在市场里无可用报价(`yes_buy/no_buy` 缺失)。
+- 可先看 `market_scan.available` 与 `primary_market.tradable`。
+
+### 7.3 如何确认 Bot 后台循环是否启动?
+
+- Telegram 里发送 `/diag`。
+- 查看三类循环状态:错价雷达、Polygon 钱包监听、Polymarket 钱包异动监听。
---
diff --git a/docs/COMMERCIALIZATION.md b/docs/COMMERCIALIZATION.md
index 2bccc06b..9563b826 100644
--- a/docs/COMMERCIALIZATION.md
+++ b/docs/COMMERCIALIZATION.md
@@ -6,115 +6,113 @@ Target: make PolyWeather a sustainable paid weather-intelligence product.
## 1. Product Positioning
-PolyWeather is not a generic weather app.
-It is a decision-support layer for temperature-settlement markets:
+PolyWeather is not a generic weather app. It is a settlement decision layer for temperature markets:
- observation-first (METAR/MGM),
-- settlement-aware probability modeling (DEB + mu/buckets),
-- market mapping (Polymarket read-only) for actionable edge detection.
+- settlement-aware modeling (DEB + mu/buckets),
+- market mapping (Polymarket read-only) for actionable mispricing checks.
---
-## 2. Business Overview Diagram
+## 2. Current Monetization Readiness (2026-03-12)
+
+| Capability | Status | Notes |
+| :-- | :-- | :-- |
+| Frontend entitlement gate | Implemented | Next middleware supports token gate + session cookie |
+| Backend entitlement guard | Implemented | `POLYWEATHER_REQUIRE_ENTITLEMENT` + backend token header |
+| Bot command entitlement pre-hook | Implemented | `/city` and `/deb` can be protected (`POLYWEATHER_BOT_REQUIRE_ENTITLEMENT`) |
+| Payment event ingestion | Not implemented | No automated USDC payment reconciliation yet |
+| Subscriber persistence | Not implemented | Still missing managed subscriber DB |
+| Self-serve billing UI | Not implemented | No user billing center yet |
+
+---
+
+## 3. Access Model
```mermaid
-flowchart TD
- A["PolyWeather Monetization"]
+flowchart LR
+ U["User"] --> FE["Frontend (Vercel)"]
+ FE --> MW["Entitlement Middleware"]
+ MW --> BFF["BFF /api/*"]
+ BFF --> API["FastAPI"]
+ API --> G["Backend Entitlement Guard"]
- subgraph P["Product"]
- P1["Telegram Signal Channel"]
- P2["Web Dashboard"]
- P3["VIP Bundle"]
- end
-
- subgraph R["Pricing"]
- R1["Entry 1 USD"]
- R2["Dashboard 5 USD"]
- R3["Bundle 5.5 USD"]
- end
-
- subgraph AC["Access Control"]
- AC1["Manual activation (P1)"]
- AC2["Wallet/USDC detection (P2)"]
- AC3["Entitlement middleware"]
- end
-
- subgraph G["Growth"]
- G1["Accuracy reports"]
- G2["Retention analytics"]
- G3["User preference center"]
- end
-
- A --> P
- A --> R
- A --> AC
- A --> G
+ P["Payment Source (USDC / Wallet)"] --> S["Subscriber State (to build)"]
+ S --> MW
+ S --> API
```
+### Do we need login/register to start charging?
+
+Short answer: **no for phase 1, yes for scale**.
+
+- Phase 1 can run with token/wallet-based entitlement and manual ops.
+- For scale (self-serve renewals, refunds, support, analytics), account identity and subscriber DB become mandatory.
+
---
-## 3. Packaging and Pricing
+## 4. Packaging and Pricing (Draft)
-| Tier | Price | Value |
-| :--------------- | :----------- | :---------------------------------------- |
-| Telegram Channel | $1 / month | Low-noise proactive signal feed |
-| Web Dashboard | $5 / month | Full multi-model context + reconciliation |
-| VIP Bundle | $5.5 / month | Dashboard + signal stream |
+| Tier | Price | Value |
+| :-- | :-- | :-- |
+| Telegram Signal Channel | $1 / month | Low-noise proactive signal stream |
+| Web Dashboard | $5 / month | Full model context + historical reconciliation |
+| VIP Bundle | $5.5 / month | Dashboard + signal stream |
Payment direction:
-- Currency: Polygon USDC
-- Phasing: manual activation first, then automated entitlement sync
+- Settlement/network: Polygon USDC
+- Rollout: manual confirmation first, then automated entitlement sync
---
-## 4. Execution Phases
+## 5. Execution Phases
-```mermaid
-graph LR
- P1[Phase 1 Manual Beta] --> P2[Phase 2 Payment Automation]
- P2 --> P3[Phase 3 Growth and B2B]
-```
+### Phase 1: Manual Paid Beta
-### Phase 1: Manual Beta
-
-- Keep paid channel small, optimize signal quality first.
-- Manual payment confirmation + manual entitlement grant.
-- Invite-gated dashboard while access control hardens.
+- Keep user set small and quality-focused.
+- Manual payment confirmation + manual entitlement issue.
+- Weekly accuracy report as trust anchor.
### Phase 2: Payment Automation
-- Detect wallet payment events (USDC).
-- Auto-issue/refresh entitlement.
-- Enforce route-level and API-level access guards.
+- Ingest payment events (wallet/tx).
+- Auto-issue and auto-expire entitlement.
+- Full parity across frontend middleware, backend API, and bot command guard.
-### Phase 3: Growth and Expansion
+### Phase 3: Growth and B2B
-- Self-serve billing and subscription panel.
-- Operator analytics and feature usage telemetry.
-- Optional B2B API package for quant teams.
+- Self-serve billing and subscriber console.
+- Retention analytics and feature usage telemetry.
+- Optional B2B/API package.
---
-## 5. Technical Dependencies for Revenue
+## 6. P0/P1 Commercial Engineering Backlog
-| Dependency | Why it matters |
-| :------------------- | :--------------------------------------------------------------- |
-| Entitlement guard | Prevents unpaid dashboard/API access |
-| Subscriber store | Persistent paid user state |
-| Audit trail | Explains why each alert fired |
-| Observability | Detects degradation before churn |
-| Frontend performance | Impacts conversion and retention (Speed Insights now integrated) |
+### P0 (before public paid launch)
+
+1. Subscriber store (managed PostgreSQL/Supabase) with entitlement expiry.
+2. Payment event pipeline (idempotent ingest + reconciliation + retry).
+3. Unified entitlement policy matrix (frontend/backend/bot).
+4. Ops audit trail for alerts and entitlement changes.
+
+### P1 (after initial paid users)
+
+1. Billing/entitlement admin console.
+2. User-level support tooling (manual override, extension, refund notes).
+3. Conversion and retention dashboards.
+4. Churn diagnostics linked to alert quality and latency.
---
-## 6. Immediate Commercial Priorities
+## 7. Commercial Risk Controls
-1. Finish robust entitlement middleware in frontend and backend.
-2. Persist subscriber/payment state in managed DB.
-3. Publish transparent monthly accuracy and signal-quality reports.
-4. Add support playbooks for false-alert and stale-data incidents.
+- Revenue leakage: deny by default when entitlement token/state is missing.
+- Signal quality drift: publish monthly transparent accuracy summary.
+- Support load: keep alert evidence standardized in push payloads.
+- Compliance/ops: preserve immutable entitlement and push logs.
---
-Last Updated: `2026-03-11`
+Last Updated: `2026-03-12`
diff --git a/docs/TECH_DEBT.md b/docs/TECH_DEBT.md
index 888411ce..29ef1e73 100644
--- a/docs/TECH_DEBT.md
+++ b/docs/TECH_DEBT.md
@@ -4,91 +4,90 @@ Purpose: keep engineering debt explicit while shipping production features.
---
-## 1. Debt Landscape
+## 1. Debt Snapshot
+
+Current estimate: **90% stable / 10% debt**.
```mermaid
flowchart TD
A["Tech Debt"]
- subgraph AR["Architecture"]
- AR1["Monolithic bot entry"]
- AR2["Shared runtime coupling"]
- end
-
- subgraph PI["Product Infra"]
- PI1["Entitlement hardening"]
- PI2["Subscription persistence"]
+ subgraph PI["Payment Infra"]
+ PI1["Payment event ingestion"]
+ PI2["Subscriber persistence"]
+ PI3["Entitlement parity"]
end
subgraph Q["Quality"]
Q1["Replay harness"]
- Q2["Broader regression tests"]
+ Q2["Mixed integration tests"]
end
subgraph O["Observability"]
- O1["Alert evidence trace"]
- O2["SLO dashboards"]
+ O1["Alert evidence schema"]
+ O2["Ops dashboards"]
end
- A --> AR
A --> PI
A --> Q
A --> O
```
-Current system health estimate: **86% stable / 14% debt**.
-
---
## 2. Recently Closed (2026-03-12)
-- Meteoblue API path fully removed from backend, frontend, config and docs.
-- Market top-bucket duplicate temperature issue fixed (backend dedupe + frontend guard).
-- Detail panel a11y conflict fixed (`aria-hidden` focus conflict resolved with `inert` + blur).
-- Vercel Speed Insights integrated for frontend performance telemetry.
-- Frontend BFF `ETag + Cache-Control` landed for cities/summary/history (`force_refresh` keeps `no-store`).
-- Mispricing radar now hard-skips non-tradable markets (closed/inactive/not accepting orders/past endDate).
-- AI analysis now includes peak-window hard constraints (before-window cannot claim "locked"/"confirmed floor").
+- Bot entry refactor completed:
+ - `bot_listener.py` simplified to thin entrypoint.
+ - Runtime split into orchestrator/handlers/services/analysis/guard/coordinator layers.
+- Startup diagnostics landed:
+ - `/diag` command
+ - loop-level startup status reporting (trade alerts, polygon watcher, polymarket watcher)
+- Multi-model anchor migration completed for mispricing radar (replaced single Open-Meteo anchor).
+- Non-tradable market hard-skip guard completed (closed/inactive/not accepting orders/past end time).
+- Wallet activity watcher upgraded with alias parsing, link preview switch, and anti-spam debounce/immediate controls.
+- Frontend BFF HTTP caching (`ETag`/`304`) completed for cities/summary/history.
+- Meteoblue fully removed from runtime paths and docs.
---
-## 3. High Priority Debt
+## 3. Active High-Priority Debt
| Item | Impact | Suggested Work |
| :-- | :-- | :-- |
-| Monolithic bot entry (`bot_listener.py`) | Hard to test and safely refactor | Split orchestration, IO and analysis modules |
-| Entitlement enforcement consistency | Revenue leakage risk | Align frontend middleware and backend enforcement |
-| Subscriber persistence model | Manual operations do not scale | Move to managed PostgreSQL/Supabase state |
-| Alert explainability | Operator trust and debugging cost | Standardize evidence payload per alert |
+| Payment event ingestion pipeline | Cannot automate paid access reliably | Build idempotent onchain payment ingest + reconciliation worker |
+| Subscriber persistence model | Manual entitlement ops do not scale | Add managed PostgreSQL/Supabase subscriber state |
+| Entitlement parity matrix | Access leaks/false denies across channels | Unify policy across frontend middleware, backend API, and bot guard |
+| Alert evidence contract | Harder to debug false positives quickly | Standardize machine-readable evidence schema for each push |
---
-## 4. Medium Priority Debt
+## 4. Active Medium-Priority Debt
| Item | Impact | Suggested Work |
| :-- | :-- | :-- |
-| Replay simulation harness | Hard to reproduce edge cases | Build deterministic replay over stored records |
-| Chart/UI regression coverage | Visual regressions can slip | Add snapshot + interaction test coverage |
-| Config centralization | Threshold changes are error-prone | Consolidate runtime knobs into structured config |
-| Naming cleanup | Legacy terms reduce clarity | Refactor naming in market/weather boundary layer |
+| Replay simulation harness | Edge-case regressions hard to reproduce | Deterministic replay from stored weather + market snapshots |
+| End-to-end integration coverage | Runtime regressions can slip | Add integration tests for `/api/city/{name}/detail` + push decisions |
+| Config sprawl | Tuning is error-prone | Consolidate env knobs into structured config groups |
+| Naming and data contracts | Boundary confusion persists | Normalize model/market field naming and compatibility aliases |
---
-## 5. Low Priority Debt
+## 5. Active Low-Priority Debt
| Item | Impact | Suggested Work |
| :-- | :-- | :-- |
-| Cold-start behavior | First request latency variance | Add warming strategy for top city routes |
-| Storage abstraction | Local file assumptions remain | Continue moving state to remote services |
+| Cold-start variance | First request latency jitter | Add prewarm strategy for top city routes |
+| Local state files | Harder multi-instance scaling | Continue migration to managed storage |
---
## 6. Next Milestones
-1. Entitlement parity: one policy across frontend and backend.
-2. Subscriber DB integration and migration scripts.
-3. Alert evidence schema + tooling for quick operator audit.
-4. Replay runner for weather/market mixed regression scenarios.
+1. Land subscriber DB + entitlement expiry model.
+2. Ship payment ingest + automatic entitlement sync.
+3. Add replay harness for weather/market mixed scenarios.
+4. Publish alert evidence schema and operator tooling.
---
diff --git a/docs/TECH_DEBT_ZH.md b/docs/TECH_DEBT_ZH.md
index ca875059..5f7784c3 100644
--- a/docs/TECH_DEBT_ZH.md
+++ b/docs/TECH_DEBT_ZH.md
@@ -1,94 +1,93 @@
# 技术债与工程待办
-目标:在持续交付的同时,把关键技术债显式化、可追踪化。
+目标:在持续交付的同时,让技术债可见、可追踪、可关闭。
---
-## 1. 技术债全景
+## 1. 债务快照
+
+当前估计:**90% 稳定 / 10% 技术债**。
```mermaid
flowchart TD
A["技术债"]
- subgraph AR["架构层"]
- AR1["机器人入口过于集中"]
- AR2["共享运行时耦合"]
- end
-
- subgraph PI["产品基础设施"]
- PI1["订阅权限一致性"]
- PI2["付费用户持久化"]
+ subgraph PI["支付基础设施"]
+ PI1["支付事件入库与对账"]
+ PI2["订阅用户持久化"]
+ PI3["权限策略一致化"]
end
subgraph Q["质量保障"]
- Q1["回放测试能力"]
- Q2["UI 回归覆盖不足"]
+ Q1["回放仿真能力"]
+ Q2["端到端集成覆盖"]
end
subgraph O["可观测性"]
- O1["告警证据链"]
- O2["SLO 看板"]
+ O1["告警证据标准"]
+ O2["运维看板"]
end
- A --> AR
A --> PI
A --> Q
A --> O
```
-当前系统健康度估计:**86% 稳定 / 14% 技术债**。
+---
+
+## 2. 近期已关闭(2026-03-12)
+
+- Bot 入口重构完成:
+ - `bot_listener.py` 已收敛为薄入口。
+ - 运行时拆分为编排层/处理层/服务层/分析层/守卫层/协调层。
+- 启动诊断完成:
+ - 新增 `/diag` 指令
+ - 后台循环启动状态可视化(错价雷达、Polygon 钱包、Polymarket 异动)
+- 错价锚点完成升级:从单一 Open-Meteo 改为多模型最高温锚点。
+- 不可交易市场硬拦截完成:`closed`/inactive/不接单/超结束时间全部跳过。
+- 钱包异动监听增强完成:昵称映射、链接预览开关、去抖与即时推送控制。
+- 前端 BFF `ETag/304` 缓存完成(cities/summary/history)。
+- Meteoblue 已从运行链路与文档中彻底移除。
---
-## 2. 最近已关闭项(2026-03-12)
-
-- Meteoblue API 全链路移除(后端/前端/配置/文档)。
-- 市场温度桶重复刷屏问题修复(后端去重 + 前端兜底)。
-- 详情面板可访问性告警修复(`aria-hidden` 焦点冲突改为 `inert + blur`)。
-- 前端已接入 Vercel Speed Insights。
-- 前端 BFF 增加 `ETag + Cache-Control`(cities/summary/history)与 `force_refresh=no-store` 语义。
-- 错价雷达增加“不可交易市场硬拦截”(closed/inactive/不接单/过 endDate)。
-- AI 分析增加峰值时段硬约束(before 状态禁止“已锁定/已确认底线”)。
-
----
-
-## 3. 高优先级技术债
+## 3. 当前高优先级技术债
| 项目 | 影响 | 建议动作 |
| :-- | :-- | :-- |
-| 机器人入口单体化(`bot_listener.py`) | 测试和重构风险高 | 拆分为编排层、IO 层、分析层 |
-| 订阅权限策略不完全统一 | 可能造成付费泄露 | 前后端统一权限校验策略 |
-| 付费用户状态持久化不足 | 人工运营不可扩展 | 迁移到托管 DB(PostgreSQL/Supabase) |
-| 告警可解释性不足 | 运维排障成本高 | 统一告警证据字段(Evidence Schema) |
+| 支付事件采集与对账流水线 | 无法稳定自动开通付费权限 | 构建幂等 payment ingest + reconciliation worker |
+| 订阅用户持久化模型 | 人工开通不可扩展 | 落地 PostgreSQL/Supabase 订阅状态 |
+| 权限策略一致性矩阵 | 存在漏放行/误拦截风险 | 统一前端中间件、后端 API、Bot 守卫策略 |
+| 告警证据协议 | 假阳性排障成本高 | 统一机器可读 Evidence Schema |
---
-## 4. 中优先级技术债
+## 4. 当前中优先级技术债
| 项目 | 影响 | 建议动作 |
| :-- | :-- | :-- |
-| 回放仿真能力不足 | 边缘场景难复现 | 基于历史记录构建可重复 Replay |
-| 图表/UI 回归覆盖不足 | 视觉回归风险 | 增加快照与交互自动化测试 |
-| 阈值配置分散 | 改动成本高且易错 | 统一收口到结构化配置 |
-| 命名历史包袱 | 认知成本高 | 系统化命名治理 |
+| 回放仿真能力不足 | 边缘场景回归难复现 | 基于天气+市场快照构建确定性 Replay |
+| 端到端集成覆盖不足 | 运行时回归难提前发现 | 增加 `/api/city/{name}/detail` 与推送链路集成测试 |
+| 配置项分散 | 阈值调优易出错 | 将关键 env 聚合为结构化配置分组 |
+| 命名与字段兼容历史包袱 | 认知与维护成本高 | 统一模型/市场字段命名与兼容层 |
---
-## 5. 低优先级技术债
+## 5. 当前低优先级技术债
| 项目 | 影响 | 建议动作 |
| :-- | :-- | :-- |
-| 冷启动波动 | 首次请求延迟不稳定 | 热点城市路由预热 |
-| 本地文件状态依赖 | 云端弹性场景受限 | 持续迁移到远程存储 |
+| 冷启动波动 | 首次请求延迟抖动 | 对热点城市路由做预热 |
+| 本地文件状态依赖 | 多实例扩展受限 | 继续迁移到托管存储 |
---
## 6. 下阶段里程碑
-1. 完成前后端订阅权限一致化。
-2. 上线付费用户持久化与迁移脚本。
-3. 建立告警证据标准并接入运维排障流。
-4. 落地天气+市场混合回放回归测试。
+1. 上线订阅用户 DB 与权限过期模型。
+2. 完成支付事件入库与自动授权同步。
+3. 落地天气+市场混合回放回归。
+4. 发布告警证据标准与运维排障工具。
---
diff --git a/frontend/README.md b/frontend/README.md
index 4a990159..b24695cf 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,6 +1,6 @@
# PolyWeather Frontend
-This directory is the only web frontend in production.
+This directory contains the production web frontend.
Production URL:
- https://polyweather-pro.vercel.app/
@@ -8,23 +8,23 @@ Production URL:
## Stack
- Next.js App Router
-- React (component-driven dashboard)
+- React (dashboard component architecture)
- Tailwind CSS
-- Leaflet (map runtime)
-- Chart.js (charts with manual lifecycle wrapper)
-- Typed store + typed data client
+- Leaflet (map)
+- Chart.js
+- Typed dashboard store + typed data client
-## Production Model
+## Runtime Model
-- Vercel serves the web UI and BFF route handlers
-- FastAPI on VPS serves weather APIs only
-- The old FastAPI static website has been removed
-- The production page shell is React-driven (`components/dashboard/*`), with no runtime dependency on `public/static/app.js`
+- Vercel hosts UI + BFF route handlers.
+- FastAPI on VPS provides weather/analysis APIs.
+- Browser never calls backend directly in normal flow.
-Current request flow:
-- Browser -> Vercel frontend
-- React store/client -> Next route handlers
-- Next route handlers -> FastAPI API
+Request path:
+
+1. Browser -> `https://polyweather-pro.vercel.app`
+2. Frontend -> Next route handlers (`/api/*`)
+3. Route handlers -> FastAPI (`POLYWEATHER_API_BASE_URL`)
## Local Development
@@ -38,41 +38,73 @@ npm run dev
Default local URL:
- http://localhost:3000
-## Required Environment Variable
+## Required Environment Variables
```env
POLYWEATHER_API_BASE_URL=https://
```
-Examples:
-- `http://38.54.27.70:8000`
-- `https://api.example.com`
+Optional entitlement variables:
+
+```env
+POLYWEATHER_DASHBOARD_ACCESS_TOKEN=
+POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=
+```
## Route Handlers
-Thin BFF routes currently exposed by Next:
- `GET /api/cities`
- `GET /api/city/[name]`
- `GET /api/city/[name]/summary`
+- `GET /api/city/[name]/detail`
- `GET /api/history/[name]`
-Current frontend behavior:
-- `/` keeps the world overview layout and initial city temperatures preload
-- Marker click: focus map + open right city card + render nearby stations
-- Right-card "今日日内分析": opens modal and freezes map motion
-- Blank-map click: closes right card only, without resetting camera
+Cache behavior:
+
+- `cities` / `summary` / `history` return `ETag` + `Cache-Control`.
+- `summary?force_refresh=true` returns `Cache-Control: no-store`.
+- `city/[name]` and `city/[name]/detail` are dynamic pass-through (no shared HTTP cache).
+
+## Frontend State & Local Cache
+
+- `sessionStorage`:
+ - city detail cache bundle (TTL 5 minutes)
+- `localStorage`:
+ - selected city
+ - sidebar risk-group collapse state
+- in-flight request de-duplication for city detail/summary/history/market scan
+
+## Entitlement
+
+- `frontend/middleware.ts` enforces dashboard/API access when `POLYWEATHER_DASHBOARD_ACCESS_TOKEN` is set.
+- BFF forwards backend entitlement token via `x-polyweather-entitlement` header when configured.
+
+## UI Notes
+
+- Left sidebar supports risk-group collapsible sections.
+- City rows keep local time and peak-time hints visible.
+- Future-date modal requests market scan with `target_date`.
+- Detail panel accessibility uses `inert` + blur when hidden.
+
+## Icons & Manifest
+
+- `frontend/app/favicon.ico`
+- `frontend/app/favicon-16x16.png`
+- `frontend/app/favicon-32x32.png`
+- `frontend/app/apple-touch-icon.png`
+- `frontend/app/site.webmanifest`
## Vercel Deployment
-1. Import the repo into Vercel
-2. Set Root Directory to `frontend`
-3. Set `POLYWEATHER_API_BASE_URL`
+1. Import repo into Vercel
+2. Set Root Directory = `frontend`
+3. Set env vars
4. Deploy
-## Notes
+## Verification
-- Backend CORS must allow `https://polyweather-pro.vercel.app`
-- City detail cache TTL is 5 minutes with revision probe; manual refresh bypasses cache
-- UI layout and sizing remain aligned with the legacy visual contract after React migration
+```bash
+./scripts/validate_frontend_cache.sh "https://polyweather-pro.vercel.app"
+```
-Last updated: 2026-03-09
+Last updated: `2026-03-12`