## File: README.md

# 主动基金 / 基金经理？请看 [基佬skills](https://github.com/wbh604/jilao-skills)

**UZI-Skill 负责个股深度分析；基佬skills 负责主动基金和基金经理行为鉴定。**  
它用真实公开数据逐笔验证基金经理的买卖行为，评的是行为，不是净值。在线示例：[兴全合宜 163417 · 谢治宇行为报告](https://wbh604.github.io/jilao-skills/assets/fund-163417.html)

---



---

## 🚀 30 秒上手

**任何 agent 里丢一句话 · 装好就能用**。详细装法见 [安装](#安装)。

| 你用的 agent | 直接丢这句 |
|---|---|
| **Claude Code** | `/plugin marketplace add wbh604/UZI-Skill` 然后 `/plugin install stock-deep-analyzer@uzi-skill` |
| **Codex / OpenAI CLI** | "按 https://raw.githubusercontent.com/wbh604/UZI-Skill/main/.codex/INSTALL.md 装 UZI-Skill，分析 600519" |
| **Cursor** | `/add-plugin stock-deep-analyzer` |
| **Gemini CLI** | `gemini extensions install https://github.com/wbh604/UZI-Skill` |
| **Hermes** | ⚠️ `hermes skills install` 当前被 [Skills Guard 误判](https://github.com/NousResearch/hermes-agent/issues/1006) · 用一键脚本绕过：`curl -fsSL https://raw.githubusercontent.com/wbh604/UZI-Skill/main/install-hermes.sh \| bash` · 详见 [INSTALL-HERMES.md](INSTALL-HERMES.md) |
| **OpenClaw / 龙虾** | "装 https://github.com/wbh604/UZI-Skill 这个股票分析技能" |
| **CLI 直用** | `git clone https://github.com/wbh604/UZI-Skill.git && cd UZI-Skill && pip install -r requirements.txt && python run.py 贵州茅台` |

装好后最常用 4 条命令（任何 agent 里直接说）：

```
/stock-deep-analyzer:analyze-stock 贵州茅台    ← 完整 22 维 × 66 评委分析（5-8min）
/stock-deep-analyzer:quick-scan 002217         ← 30 秒速判
/stock-deep-analyzer:scan-trap 002217          ← 杀猪盘排查
/stock-deep-analyzer:dcf 600519                ← DCF 估值专项
```

> 💡 **当前最新稳定版 v3.9.4** · 完整演进见 [更新日志](#-更新日志)：
> - **66 位评审团 · 9 大流派**（v3.7 新增 a16z Andreessen / Naval / 黄仁勋 / 马斯克 / 高瓴张磊 / Burry / Chanos 等 13 位 + 独立 I 组 Serenity AI 卡位猎手）· 242 条量化规则
> - **Serenity 严谨化**（v3.8）：8 罚分因子 + 3 级证据阶梯（"有定点量产"≈90 分 vs "仅题材"≈60 分）+ 供应链 8 层分层
> - **Tier-1 五方法**（v3.8）：`/ai-readiness` `/earnings-preview` `/model-update` `/returns` `/rebalance`
> - **多股对比 & 组合**（v3.6）：`--versus` 2-4 只横向对决 · `--portfolio` CSV 组合健康度 · 暗色模式 + sticky TOC（v3.9.1 起可一键折叠）+ 术语悬浮
> - **流派视角锁定**（v3.5）：`--school A-I` 只看一派的判断 · 报告带 SCHOOL LOCK banner
> - **架构**：v3.0 pipeline 默认主干 · 685 tests 全过 · v2.x API 100% 向后兼容（`UZI_LEGACY=1` 回老路径）
>
> **Hermes 用户**：`hermes skills install` 被上游 Skills Guard 误判 · 用一键脚本装：`curl -fsSL https://raw.githubusercontent.com/wbh604/UZI-Skill/main/install-hermes.sh | bash` · 详见 [INSTALL-HERMES.md](INSTALL-HERMES.md).

---


## 💬 没有群！虽然我也不知道为什么交流群也没涉及什么东西会被封号。。。

我再说明一下，没有群，也不拉群。



> 添加人过多，请备注好事宜，我才知道出什么问题了。。

---

---
## 鸣谢
学AI，上L站！
感谢 [Linux.do](https://linux.do/) 社区支持。

## 这是啥

一句话：输入一只股票，Claude 变成你的私人分析师，跑完 22 个维度的数据、调 22 种华尔街分析模型、让 66 个投资风格完全不同的大佬各自打分，最后吐出一份 600KB 的 Bloomberg 风格报告。

```
/stock-deep-analyzer:analyze-stock 国盾量子
```

5-8 分钟后你会得到：
- **一份 HTML 报告** — 可以直接用浏览器打开，自包含，离线也能看
- **一张朋友圈竖图** — 1080×1920，直接发
- **一张微信群战报** — 1920×1080
- **一段话摘要** — 复制粘贴就能发群里

## 为什么做这个

之前看一只票的流程：东方财富翻基本面 → 同花顺看 K 线 → 雪球刷大 V 说了啥 → 研报系统找卖方观点 → Excel 算个 DCF → 结果买进去还是亏。

这些活儿本质上就是"搜集信息 → 多角度分析 → 给个结论"，让 AI 全干了不行吗？

市面上看了一圈，要么是输出三段废话的 GPT wrapper，要么是用不起的机构终端。Anthropic 出了个 [financial-services-plugins](https://github.com/anthropics/financial-services-plugins)，方法论很好（DCF / Comps / LBO 那套），但完全是美股视角 + 全要付费数据源。

所以自己搓了一个。**全免费数据源，零 API key，A 股直接能跑。**

---

## 安装

不管你用什么 agent，**都是丢一句话过去就行**：

### Claude Code

```
/plugin marketplace add wbh604/UZI-Skill
/plugin install stock-deep-analyzer@uzi-skill
```

装好后说 `/stock-deep-analyzer:analyze-stock 贵州茅台`。

> ⚠️ **必须带 `stock-deep-analyzer:` 命名空间前缀**
>
> Claude Code 装 plugin 后，所有 skill/command 都以 `stock-deep-analyzer:` 开头。
> 部分环境下短名（`/analyze-stock`）不会被自动解析——稳妥起见请一律用全名：
>
> - `/stock-deep-analyzer:analyze-stock <ticker>`
> - `/stock-deep-analyzer:quick-scan <ticker>`
> - `/stock-deep-analyzer:scan-trap <ticker>`
> - `/stock-deep-analyzer:dcf <ticker>`
> - `/stock-deep-analyzer:ic-memo <ticker>`
> - `/stock-deep-analyzer:investor-panel <ticker>`
> - `/stock-deep-analyzer:trap-detector <ticker>`
> - `/stock-deep-analyzer:deep-analysis <ticker>`
> - 等全部 14 条
>
> Cursor / Gemini CLI / Codex 同理：**一律用 `/stock-deep-analyzer:<cmd>` 全名**，
> 避免短名解析失败。

### Codex

直接对 Codex 说：

> 请按照 https://raw.githubusercontent.com/wbh604/UZI-Skill/main/.codex/INSTALL.md 的指引安装 UZI-Skill，然后帮我深度分析 贵州茅台。

### OpenClaw / 龙虾

对龙虾说：

> 帮我安装 https://github.com/wbh604/UZI-Skill 这个股票分析技能，装好后分析 贵州茅台。

### Cursor

```
/add-plugin stock-deep-analyzer
```

然后说"分析 贵州茅台"。

### Gemini CLI

```bash
gemini extensions install https://github.com/wbh604/UZI-Skill
```

### OpenCode

对 OpenCode 说：

> 请按照 https://raw.githubusercontent.com/wbh604/UZI-Skill/main/.opencode/INSTALL.md 安装并分析 贵州茅台。

### Windsurf / Devin / 其他 Agent

丢这句话进去：

> 克隆 https://github.com/wbh604/UZI-Skill ，读 AGENTS.md 了解怎么用，帮我深度分析 贵州茅台。

### 📱 不在电脑前？

对任何 agent 说：

> 分析 贵州茅台，用远程模式，生成一个公网链接让我手机能看。

agent 会用 `--remote` 启动 Cloudflare Tunnel，给你一个 `https://xxx.trycloudflare.com` 链接。若本机没有 `cloudflared`，默认只提示安装方式；确认要自动安装时再加 `--install-cloudflared`。

---

## 用法

### 完整深度分析（5-8 分钟）

```
/stock-deep-analyzer:analyze-stock 水晶光电
/stock-deep-analyzer:analyze-stock 002273
/stock-deep-analyzer:analyze-stock 00700.HK
/stock-deep-analyzer:analyze-stock AAPL
/stock-deep-analyzer:analyze-stock 7203.T
/stock-deep-analyzer:analyze-stock 005930.KS
```

### 专项命令

> 都要加 `/stock-deep-analyzer:` 前缀才保证执行得通。

| 命令 | 干嘛的 |
|---|---|
| `/stock-deep-analyzer:dcf 600519` | DCF 估值 · WACC + 5×5 敏感性表 |
| `/stock-deep-analyzer:comps 002273` | 同行对标 · PE/PB 分位分析 |
| `/stock-deep-analyzer:lbo 600519` | LBO 测试 · PE 买方能赚多少 IRR |
| `/stock-deep-analyzer:initiate 002273` | 机构首次覆盖报告 · JPM/GS 格式 |
| `/stock-deep-analyzer:ic-memo 002273` | 投委会备忘录 · 三情景回报 |
| `/stock-deep-analyzer:earnings 002273` | 财报解读 · beat/miss 检测 |
| `/stock-deep-analyzer:catalysts 002273` | 催化剂日历 · 未来 60 天 |
| `/stock-deep-analyzer:thesis 002273` | 投资逻辑追踪 · 5 支柱监控 |
| `/stock-deep-analyzer:screen 002273` | 5 套量化筛选 · value/growth/quality |
| `/stock-deep-analyzer:dd 002273` | 尽调清单 · 5 工作流 21 项 |
| `/stock-deep-analyzer:quick-scan 002273` | 30 秒速判 |
| `/stock-deep-analyzer:panel-only 600519` | 只看 66 评委投票 |
| `/stock-deep-analyzer:scan-trap 002273` | 杀猪盘排查 |
| `/stock-deep-analyzer:segmental-model 300308` | 分业务收入 bottom-up 建模 · 3 情景 × 3 年 projection · 对 DCF 反向校验 |
| `/stock-deep-analyzer:ai-readiness 002273` | 🆕 v3.8 · 单票 AI 就绪度/卡位评估 · 3 道 gate → Go/Wait + 评级 |
| `/stock-deep-analyzer:earnings-preview 002273` | 🆕 v3.8 · 财报**前**预览 · 一致预期 + Bull/Base/Bear + 隐含波动 |
| `/stock-deep-analyzer:model-update 002273` | 🆕 v3.8 · 新财报/指引增量更新模型 · 假设 delta → DCF/thesis 影响 |
| `/stock-deep-analyzer:returns` | 🆕 v3.8 · 组合收益归因 · 按持仓/行业拆解 + Top 贡献/拖累 |
| `/stock-deep-analyzer:rebalance` | 🆕 v3.8 · 逐持仓再平衡 · 漂移 + 交易清单 + A股印花税/佣金换手成本 |

### CLI 直跑进阶玩法（git clone 用户）

```bash
python run.py 600519.SH --depth lite --no-browser   # 30-60s 快速档
python run.py 300394.SZ --school I                  # 只看 Serenity 卡位视角（A-I 九派任选）
python run.py --versus 茅台 五粮液 002594.SZ         # 2-4 只票横向对决 · ★WIN 高亮
python run.py --portfolio holdings.csv             # CSV 组合 · 加权评分 + 健康度
python run.py 600519.SH --output-dir /tmp/out      # SaaS 集成 · index.html + meta.json
python run.py 600519.SH --remote                   # 公网链接 · 缺 cloudflared 时默认不改系统
```

---

## 🎯 评分校准（v2.11）

用户反馈"茅台 47 分"、"没超过 65 分"—— 诊断发现两处公式偏严苛，v2.11 校准：

| 改动 | 旧 (v2.9.1) | 新 (v2.11) | 影响 |
|---|---|---|---|
| **verdict 阈值** | 85/70/55/40 | **80/65/50/35** | 从未有股能 ≥85（"值得重仓"档空设），下调 5 分让白马/真强股进"可以蹲一蹲"档 |
| **consensus neutral 权重** | 0.5（半权） | **0.6** | （v2.11 校准）价值派+游资 35 人偏保守，neutral 权重 0.5 让白马 consensus 仅 37，0.6 更贴近"不坑但不是心头好"的真实语义 |

公式（未变）：`overall = fund_score × 0.6 + consensus × 0.4`

典型白马（如茅台）预期：
- v2.9.1：`fund=62 consensus=45 → overall 55 → 观望优先`
- v2.11：`fund=62 consensus=50 → overall 57 → 观望优先`（但更接近"可以蹲一蹲"边界，白马行情启动时容易进 65）

两档合计影响 ~5-8 分。**真正的坑仍会 < 35 → 回避**，分数辨识度不降反升。

诊断字段 `panel.json::consensus_formula.version = "v2.11 · (bullish + 0.6*neutral) / active"` 可审计。

回归测试：`tests/test_v2_11_scoring_calibration.py` 8 个用例。

完整校准记录见 [BUGS-LOG.md v2.11.0 章节](docs/BUGS-LOG.md#v2110-2026-04-18--评分校准--用户反馈驱动)。

---

## 🎚️ 三档思考深度（v2.10.3 新增）

给用户自己选择分析力度——快想 / 正常 / 深挖：

```bash
python run.py 600519 --depth lite     # ⚡ 速判模式（1-2 分钟）
python run.py 600519                   # 📊 标准分析（5-8 分钟）· 默认
python run.py 600519 --depth deep      # 🔬 深度研究（15-20 分钟）
```

或通过环境变量：

```bash
export UZI_DEPTH=lite       # 或 medium / deep
python run.py 600519
```

### 三档差异一览

| 维度 | ⚡ **lite** 速判 | 📊 **medium** 标准 | 🔬 **deep** 机构级 |
|---|---|---|---|
| **预计耗时** | 1-2 分钟 | 5-8 分钟 | 15-20 分钟 |
| **fetcher 维度** | 核心 7 维 | 全 22 维 | 全 22 维 + 强化 fallback |
| **评委数量** | 10 位代表 | 66 位完整 | 66 位 + **Bull-Bear 结构化辩论** |
| **机构方法** | 只 DCF | 全 22 种 | 全 22 种 + **Segmental Build-Up** |
| **ddgs 定性查询** | **全 skip**（省 token）| 按需 · 预算 30 次 | 跑满 · 预算 60 次 |
| **fund_holders** | Top 5 完整业绩 | Top 20 完整 + 其余清单 | Top 100 完整 |
| **自查 gate** | critical block | critical block · warning 可 ack | 两级都 block |
| **Playwright 兜底**（v2.13.1） | ❌ 完全禁用 | opt-in · `UZI_PLAYWRIGHT_ENABLE=1` · **6 维**（4_peers/8_materials/15_events/17_sentiment/7_industry/14_moat） | ✅ 默认启用 · **10 维**（medium 6 + 3_macro/13_policy/18_trap/19_contests）· 首次 y/n 交互装 Chromium |
| **Token 消耗（Codex）** | 最省 | 中等 | 最大 |
| **适用场景** | 随手看 / 老板临时问 / 预判 ETF 成分股 | 日常深度分析 · 写研报 | 投委会备忘录 · 建仓前深挖 |

### 自动降级策略

- **第一次安装** / `.cache/_global` 空时 → 自动切 lite（省首次冷启动时间）
- **网络预检 3+ 域不通** → 自动切 lite（避免卡死）
- 手动 `--depth` 始终覆盖自动判定

### 实战选择

| 问题 | 推荐档位 |
|---|---|
| "帮我看看这只票能不能买" | `medium`（默认） |
| "15 分钟内给我个结论" | `lite` |
| "老板明天投委会要看" | `deep`（含 Bull-Bear 辩论 + bottom-up segmental） |
| "ETF 代码输进去了（系统会提示选成分股）" | `lite`（成分股快速预判）|
| "Codex 环境 / 首次安装" | 不用管 · 自动 lite |

### 命令映射（隐式档位）

| 命令 | 隐式档位 |
|---|---|
| `/stock-deep-analyzer:quick-scan 600519` | lite |
| `/stock-deep-analyzer:panel-only 600519` | lite |
| `/stock-deep-analyzer:analyze-stock 600519` | medium（默认）|
| `/stock-deep-analyzer:ic-memo 600519` | deep |
| `/stock-deep-analyzer:initiate 600519` | deep |

---

## 🎭 66 位评审团

不是模板话术。每个人有自己的**量化规则集**（共 236 条），给出的建议必须引用具体命中了哪条。
v3.7.0 起新增 **13 位新晋科技大佬** + 独立的 **I 组 Serenity（AI 卡位/瓶颈猎手）**；v3.9.0 再添从十年实盘交割单蒸馏的 **股海贼王**，覆盖 9 大流派：

| 组 | 风格 | 人数 | 代表人物 |
|---|---|---|---|
| A | 经典价值 | 6 | 巴菲特 · 格雷厄姆 · 芒格 · 费雪 · 邓普顿 · 卡拉曼 |
| B | 成长投资 | 9 | 林奇 · 木头姐 · 蒂尔 · **Andreessen (a16z)** · **Gurley (Benchmark)** · **Naval** · **Gerstner (Altimeter)** · **Chamath** |
| C | 宏观对冲 | 7 | 索罗斯 · 达里奥 · 霍华德马克斯 · 德鲁肯米勒 · 罗伯逊 · **Burry（大空头）** · **Chanos（做空猎手）** |
| D | 技术趋势 | 4 | 利弗莫尔 · 米内尔维尼 · 达瓦斯 · 江恩 |
| E | 中国价投 | 7 | 段永平 · 张坤 · 朱少醒 · 谢治宇 · 冯柳 · 邓晓峰 · **张磊（高瓴）** |
| F | A 股游资 | 24 | 章盟主 · 赵老哥 · 炒股养家 · **股海贼王 🆕**（淘股吧十年实盘蒸馏）· 北京炒家 … |
| G | 量化系统 | 4 | 西蒙斯 · 索普 · 大卫·肖 · **Asness (AQR)** |
| H | 科技领袖派 🆕 | 4 | **黄仁勋 (NVIDIA)** · **马斯克 (Tesla)** · **Sam Altman (OpenAI)** · **Saylor (MSTR)** |
| I | AI 卡位/瓶颈猎手 🆕 | 1 | **Serenity（@aleabitoreddit）** |

> v3.7.0（2026-06）新晋 13 人加粗。**H 组**是带着自家产业链视角的科技 CEO；**I 组 Serenity** 是单人特殊评委（见下）。

**举个例子**：

> **巴菲特** 给水晶光电打 62 分 · 中性
> "观望：护城河 27/40 可见；但 ROE 5 年最低 6.7%，达标率仅 0/5"
> ✅ 资产负债率 30% 保守 · ❌ ROE 5 年最低 6.7%

> **黄仁勋** 给某 CPO 光模块股打 100 分 · 看多
> "在 AI 算力链上 · 数据中心 Capex 直接受益 · 毛利率 ≥50% 定价权强——这是光速摩尔定律的受益方。"
> ✅ AI 算力需求强相关 · ✅ CUDA/生态绑定深

> **卡拉曼** 给水晶光电打 0 分 · 看空
> "看空核心：无 30% 安全边际"

---

## 🧠 I 组 · Serenity · AI 卡位/瓶颈猎手

> **重磅角色**：2026 年 X（推特）爆火的海外散户研究员 [@aleabitoreddit](https://x.com/aleabitoreddit)。
> 单独成组、单独评分——因为她的打法极度集中、极度逆共识，跟任何机构大佬都不一样。

### 她是谁

- 自述背景：**前 AI 研究科学家 · Nature 论文作者 · 前 RISC-V 基金会成员 · 半导体/光通信工程师**
- 二次元头像、匿名、不露脸、不卖课、不跟单，研究**全部免费公开**，X 粉丝 30 万+
- 成名战：提前约一年押中 InP 磷化铟衬底瓶颈股 **$AXTI（$12 → $70+，最高 $115–140）**，2026 Q1 被 IntelliEPI CEO 公开印证"InP 短缺是整个 AI 基建的瓶颈"

> ⚠️ 身份与收益均为**自述/媒体转述、未经第三方审计**，各来源数字互相矛盾。本项目仅蒸馏其**方法论**作为一个分析视角，不代表认可其真实战绩。详见 [`docs/serenity-research-dossier.md`](docs/serenity-research-dossier.md)（全网 20+ 来源逐条档案）。

### 她在 UZI-Skill 里的作用

把她的「**AI 产业链卡脖子/瓶颈点理论（Chokepoint Theory）**」做成一个可量化的评委——
**不买 AI 龙头**（英伟达这种已被充分定价的票），而是沿供应链往上游拆，找那个"全世界都绕不过去、又最容易供给见底"的二三线上游小盘，抢在市场定价前埋伏。

```
龙头被买爆 → 沿供应链往上拆 → 找最难替代的环节 → 找该环节里供给最紧的小盘 → 提前埋伏
```

**核心打分逻辑「卡位决定态度」**——不看估值便宜、不看成长快，只看一个变量：**这家公司的产品在当前 AI 浪潮里有没有卡住别人的脖子**。

| 判定 | 态度 |
|---|---|
| 卡住了（不可替代 + 供给瓶颈 + 没被定价） | 🟢 **看多 / 可能重仓** |
| 在 AI 链上但卡位不硬（能被替代 / 产能充足） | ⚖️ 中性 · 待验证 |
| 没卡到位 / 只是蹭概念 / 不在 AI 链 | 🔴 **直接 skip**（白酒、银行护城河满分也给 0 分） |

判一个环节是不是"卡位点"看三件事：① **难替代**（换供应商/材料/工艺要多久，越久越好 → `14_moat` 切换成本）② **供给紧**（产能跟不跟得上 AI 需求曲线，越跟不上越好 → `7_industry`）③ **没被定价**（市场还在用"周期股/老半导体/小众材料"旧叙事看它 → `5_chain` + `15_events`）。

### 怎么单独跑 Serenity 视角

```bash
python run.py 300394.SZ --school I       # 只看 Serenity 的"卡没卡位"判断
python run.py NVDA --school H             # 只看 H 组科技领袖派（黄仁勋/Musk/Altman/Saylor）
```

> 方法论六步法 + alpha 5 维详见 [`skills/deep-analysis/references/fin-methods/serenity-bottleneck.md`](skills/deep-analysis/references/fin-methods/serenity-bottleneck.md)；
> 语气库 + 评分规则见 [`skills/investor-panel/references/group-i-serenity.md`](skills/investor-panel/references/group-i-serenity.md)。

---

## 📐 22 种机构级方法

从 [anthropics/financial-services-plugins](https://github.com/anthropics/financial-services-plugins) 移植方法论，适配了 A 股参数（rf=2.5% / ERP=6% / 税率 25% / 终值 g=2.5%）。首批 17 种 + v3.8.0 Tier-1 续引 5 种（`/ai-readiness` `/earnings-preview` `/model-update` `/returns` `/rebalance` · 见 [专项命令](#专项命令)）：

**估值建模**
- DCF（WACC 拆解 + 两段 FCF + Gordon Growth 终值 + 5×5 敏感性热力图）
- Comps 同行对标（PE / PB / EV-EBITDA 分位 + 隐含目标价）
- 三表预测（5 年 IS / BS / CF 联动）
- Quick LBO（PE 基金视角 IRR 交叉校验）
- 并购增厚/摊薄模型

**研究工作流**
- 首次覆盖报告（JPM/GS/MS 格式 · 评级 + 目标价 + 论点 + 风险）
- 财报 beat/miss 解读
- 催化剂日历（真实事件提取 + 未来预排 + 影响分级）
- 投资逻辑追踪（5 支柱健康度）
- 晨报 · 量化筛选 · 行业综述

**深度决策**
- IC 投委会备忘录（8 章节 · Bull/Base/Bear 三情景）
- Porter 五力 + BCG 矩阵
- DD 尽调清单（5 工作流 21 项 · 自动标注完成状态）
- 单位经济学 · 价值创造计划 · 组合再平衡

---

## 📸 报告长什么样

> 以下截图全部来自水晶光电（002273.SZ）的真实分析结果。

### 综合评分 + 核心结论



### 多空大分歧 · The Great Divide

费雪 100 分 vs 卡拉曼 96 分，三轮互喷，每轮引用具体数字。



### 65 位评审团 · 审判席

每个人一盏灯——绿色看多、红色看空、灰色中性。



### 聊天室模式

评委们用自己的语言风格发言，引用命中的具体规则。



### DCF 估值 · 5×5 敏感性热力图

WACC 6.96% · 内在价值 ¥20.73 · 安全边际 -28.6%，颜色从深绿（低估）到深红（高估）。



### IC 投委会备忘录 · 三情景回报

Bull ¥26.95 / Base ¥20.73 / Bear ¥14.51，每个情景有概率和假设。



### 22 维深度卡

每个维度有独立可视化——K 线蜡烛图 / PE Band / 雷达图 / 供应链流程图 / 温度计 / 环形图。



### 朋友圈竖图 · 一键分享



---

## 🔧 数据源

全部免费，零 API key：

| 数据 | 主源 | 备用 |
|---|---|---|
| 实时行情 / PE / 市值 | 东方财富 push2 | 雪球 → 腾讯 → 新浪 → 百度 |
| 财报历史 | akshare | 雪球 f10 |
| K 线 / 技术指标 | akshare | yfinance |
| 龙虎榜 / 北向 / 两融 | akshare | 东财 |
| 研报 / 公告 | 巨潮 cninfo + akshare | 同花顺 |
| 港股 | akshare hk | yfinance |
| 美股 | yfinance | akshare us |
| 全球同行 | Yahoo Equity Screener + Fundamentals Timeseries | 本地行业同行 + 24h 缓存 |
| 全球汇率 | Yahoo Chart FX | 原币值始终保留 |
| 宏观 / 政策 / 舆情 / 杀猪盘 | DuckDuckGo web search | — |
| **社交热榜**（v2.12 新增） | **微博 / 知乎 / 百度 / 抖音 / 头条 / B 站 · 各平台官方 JSON API** | 5min 文件缓存 · 单平台失败不影响其他 |

多层 fallback 链 — 一个源挂了自动切下一个。

### 全球同行业绩对比

分析单只股票时，`4_peers` 会先保留本地市场同行，再按细分行业发现全球上市公司候选，
只为相关度最高的 8 家补齐年度财务。报告展示全球同行中位数、目标公司分位、
营收规模/毛利率散点和跨市场明细表；Comps 估值也会复用有效全球同行。

已识别中国、香港、美国、日本、韩国、台湾、新加坡、印度、加拿大、澳大利亚、
英国及欧洲主要交易所，并支持 Yahoo 可识别的其他交易所代码作为通用全球标的。

口径约束：

- 年报、季度和 TTM 不混在同一序列。
- 营收、利润等金额保留原币值，另写基准币换算值；原始值不会被覆盖。
- 不同币种的原始市值不会直接用于规模相似度评分。
- 负利润和负 ROE 是有效数据，不会改写成零。
- 有效同行少于 3 家时只展示数据，不输出全球分位结论。
- 单个同行或汇率源失败不会中断主报告，失败代码会保留在结果中。

可选配置：

```bash
# 完全关闭全球同行，只保留原有本地同行
export UZI_DISABLE_GLOBAL_PEERS=1

# 调整最终补全数量，范围 3-12，默认 8
export UZI_GLOBAL_PEER_LIMIT=10
```

### 📱 6 平台社交热榜（v2.12 新增）

散户情绪和杀猪盘题材经常先在抖音/小红书/微博发酵，DuckDuckGo 扫不到。v2.12 起 `17_sentiment` 维度自动查：

- **微博热搜** · 抓 `weibo.com/ajax/side/hotSearch` · 50 条实时热搜
- **知乎热榜** · 抓 `zhihu.com/api/v3/feed/topstory/hot-list-web` · 50 条
- **百度热搜** · 抓 `top.baidu.com/api/board` · 实时榜单
- **抖音热点** · 抓 `douyin.com/aweme/v1/web/hot/search/list/` · 搜索热点
- **头条热榜** · 抓 `toutiao.com/hot-event/hot-board/` · 热点事件
- **B 站热搜** · 抓 `s.search.bilibili.com/main/hotword` · 全站热搜

股票名（含简称，如"贵州茅台"→"贵州"/"茅台"）在热榜标题里命中 → 计入情绪热度 + 记录具体条目。

数据结构：synthesis 的 `17_sentiment.data.hot_trend_mentions`：
```json
{
  "stock_name": "贵州茅台",
  "platforms_ok": 6,
  "total_hits": 3,
  "by_platform_count": {"weibo": 2, "zhihu": 1, ...},
  "mentions": { "weibo": [{"rank":3, "title":"茅台 1499 回归", ...}], ... }
}
```

> 致谢：本模块设计参考了 [run-bigpig/jcp](https://github.com/run-bigpig/jcp) (韭菜盘 AI) 的 `hottrend` 服务实现。

### 🔑 可选：东方财富妙想 Skills API（v2.3 新增）

2026 年 `push2.eastmoney.com` 在大陆网络经常被反爬拦截。若设置
`MX_APIKEY`，UZI-Skill 会优先走官方 NLP API：

- **中文名纠错**："北部港湾" → 自动识别为 "北部湾港(000582.SZ)"
- **行情快照**：绕过 push2 直接拿到最新价/市值/PE/PB/行业

配置：
```bash
cp .env.example .env
# 编辑 .env 填入 MX_APIKEY（免费申领：https://dl.dfcfs.com/m/itc4）
```

无 key 时全部回退到 XueQiu/akshare 链，现有用户零感知。

### 🔓 需登录的数据源（v2.7.1 新增）

部分数据源 2026 年起加了登录鉴权，UZI-Skill 默认**不主动弹登录窗**（保持无人值守）。
用户可按需启用：

| 数据源 | 维度 | 启用方式 | 影响 |
|---|---|---|---|
| **XueQiu cubes_search.json** | `19_contests` 实盘比赛持仓 | `export UZI_XQ_LOGIN=1` 然后 `python -m lib.xueqiu_browser login`（一次性弹浏览器登录） | 不启用：报告 19_contests 显示"⚠️ XueQiu 需登录，0 cube"；启用后能看到雪球 50+ 个实盘组合持有本股 |

#### XueQiu 登录步骤

```bash
# 1. 启用环境变量（一次性，可加进 .zshrc）
export UZI_XQ_LOGIN=1

# 2. 一次性登录（首次跑会弹有头浏览器，登录后回到终端按回车）
python -m lib.xueqiu_browser login
# → 浏览器弹出，手动账密 / 微信扫码 / 短信登录
# → 登录成功后回终端按回车，cookie 持久化到 ~/.uzi-skill/playwright-xueqiu/

# 3. 后续跑分析自动复用登录态（cookie 通常有效 ≥ 30 天）
python run.py 贵州茅台 --no-browser
# 19_contests 维度会显示真实雪球组合数 + 收益率分布

# 4. 如果直接跑 run.py 想启用，加 flag
python run.py 贵州茅台 --enable-xueqiu-login
```

#### 跳过登录（默认行为）

不想登录？什么都不用做。XueQiu 维度会清晰标注 `⚠️ 需登录，0 cube`，
其他 21 个维度照常工作。

#### 状态查询
```bash
python -m lib.xueqiu_browser status
# 显示：profile dir / cookie 是否存在 / 是否启用
```

### 🚨 数据缺口怎么处理（v2.3）

若某些字段脚本拿不到（网络限制 / 新股 / 停牌），pipeline **不会塞默认值糊弄**：

1. 生成 `_data_gaps.json` 列出每个缺口的建议恢复动作（浏览器 / MX / WebSearch / 推导）
2. Agent 按 [HARD-GATE-DATAGAPS](skills/deep-analysis/SKILL.md) 逐条尝试补齐
3. 真的补不到 → 在 `agent_analysis.json` 里 `data_gap_acknowledged` 显式承认
4. HTML 报告顶部显示橙色 banner + 相关字段显示 "—" 并划线

这样你永远能分辨"这只股真的不适合买" vs "只是数据没拿到"。

### 🌐 网络受限环境（v2.4 新增）

UZI-Skill 在大陆和海外都能跑，但瓶颈不同，建议对号入座：

**大陆网络 · `pip install` 失败怎么办？**

`run.py` 和 `setup.sh` 会自动尝试国内镜像（清华 → 阿里云 → 中科大），
所以常见情况你什么都不用做。若要手动指定：

```bash
pip install -r requirements.txt \
    -i https://pypi.tuna.tsinghua.edu.cn/simple \
    --trusted-host pypi.tuna.tsinghua.edu.cn
```

**Codex / 海外 agent · 数据源访问慢怎么办？**

国内数据源（尤其 `push2.eastmoney.com`）从海外访问经常超时。**强烈建议
设置 `MX_APIKEY`**（免费申领 → https://dl.dfcfs.com/m/itc4），它走
`mkapi2.dfcfs.com` 境内外都通，同时天然具备中文名纠错能力。

```bash
cp .env.example .env
# 编辑 .env 填入 MX_APIKEY
python run.py 贵州茅台
```

**双端都不通**：agent 应保留 `_data_gaps.json` / `_resolve_error.json`，
等网络恢复后直接跑 `stage2()` 可以复用已采集数据，不用从头来过。

详见 [AGENTS.md · 网络受限环境](AGENTS.md) 的场景 A/B/C 速查。

---

## 📁 项目结构（v3.x 架构）

```
/* Detailed source-code truncated for AI context efficiency. */
```

**v3.2 重构分层亮点**：

| 层 | 文件 | 职责 |
|---|---|---|
| 入口 | `run.py` | CLI 主入口 · `UZI_LEGACY=1` 回退老路径 |
| 管道 | `lib/pipeline/*` | v3.0 主干 · collect / score / synthesize |
| 纯函数 | `lib/pipeline/score_fns.py` | v3.1 · score_dimensions / generate_panel / generate_synthesis |
| 渲染 | `lib/report/*` | v3.2 · 5 个子模块 · svg / viz / inst / panel / special |
| Legacy | `run_real_test.py` + `assemble_report.py` | v2.x 向后兼容层 · re-export 所有迁移函数 |

---

## 🧠 设计理念

**Agent 驱动分析，脚本只是工具。**

整个流程分两段——中间 agent 必须介入，**最后必须自查**（v2.9 起机械强制）：

```
Stage 1 (脚本)          → 数据采集 + 模型计算 + 规则引擎骨架分
        ⏸️ Agent 介入   → 读数据 → role-play 66 评委 → 写判断 → 审查假设
Stage 2 (脚本)          → 综合研判 + 自动跑 13 条自查 → 报告生成
                         ↑ v2.9 核心：critical 不过 → 拒绝出 HTML
```

**66 个评委不是跑公式出分数**——agent 要真正站在每个人的角度思考：

- 巴菲特分析苹果 → agent 知道这是伯克希尔第一大持仓 → override 看多
- 赵老哥分析美股 → agent 知道游资不做美股 → skip
- 木头姐分析白酒 → agent 知道她只看颠覆创新 → "不在平台里"
- 格雷厄姆看到 PE 33 → 不需要复杂推理 → 看空

每个判断都可以覆盖规则引擎的机械得分，但必须给出理由。

**三层评估**：真实持仓 → 行业亲和度 → 量化规则。真金白银比任何公式都有说服力。

### 🛡 机械级自查 gate（v2.9 起）

过往 `HARD-GATE-FINAL-CHECK` 是"软要求"——agent 可能跳过、可能忘、可能做半截。BUG#R10（云铝被归为"农副食品加工"）就是全流程跑完报告发出去，才被用户发现行业分类错了。**软 gate 不够，v2.9 起机械强制。**

**`lib/self_review.py`** 13 条自动检查覆盖所有历史 BUG 经验：

| severity | 抓什么 | 背后 BUG |
|---|---|---|
| 🔴 critical | 行业碰撞（工业金属→农副食品加工） | BUG#R10 |
| 🔴 critical | 维度缺失 / 空 data / 占位符 | wave2 timeout、fetcher 崩溃 |
| 🔴 critical | HK kline 0 根 / HK 财报空 | BUG#R7 / R8 |
| 🔴 critical | panel 全 skip / coverage < 60% | 数据灾难 |
| 🔴 critical | agent_analysis 缺 / 未 review | agent 偷懒 |
| 🟡 warning | DCF 全 0 / 金属股 materials 空 | v2.8.x coverage gap |
| 🟡 warning | 编造"苹果产业链"无 raw_data 证据 | 联想编造 |

**`assemble_report::assemble()` 入口自动跑 review**，critical > 0 → `raise RuntimeError("⛔ BLOCKED by self-review")`，**物理上无法出报告**，直到 agent 修完。

```bash
# agent 迭代流程
loop:
  python review_stage_output.py <ticker>
  读 .cache/<ticker>/_review_issues.json
  对每条 critical 执行 suggested_fix
  直到 critical == 0 才出 HTML
```

每次新 BUG 修完，对应的 `check_*` 规则都会加到 self_review，**下次同类问题跑完就自动抓到，不再靠用户反馈**。

---

## ❓ FAQ

**Q: 跑一次要多久？**
A: 5-8 分钟，主要是数据采集慢（22 个维度要调十几个 API）。纯计算的机构建模部分 < 1 秒。

**Q: 需要付费数据源吗？**
A: 不需要。全部免费源（akshare / yfinance / DuckDuckGo / 巨潮 / 东方财富 / 雪球），零 API key。

**Q: 港股美股能用吗？**
A: 能。`/stock-deep-analyzer:analyze-stock 00700.HK` 或 `/stock-deep-analyzer:analyze-stock AAPL`。

**Q: 数据准不准？**
A: 实时数据走东方财富 / 雪球，财报走巨潮 / akshare，和你在东方财富 App 上看到的一样。但 web search 质量不稳定（DuckDuckGo 中文搜索有时会返回无关结果），所以 Claude 会做二次审查。

**Q: 能当投资建议吗？**
A: 不能。这是工具不是神仙，66 个大佬的意见都是规则引擎模拟的，不代表真人观点。买不买你自己决定。

**Q: 怎么知道这次报告数据是否可信？**
A: v2.9 起**强制**机械自查。报告生成前跑 13 条检查，critical 不过物理上发不出报告。`.cache/<ticker>/_review_issues.json` 里能看到本次跑有没有 warning，每条都带 `suggested_fix`。每次新 BUG 修完都加对应检查 → 下次同类问题自动抓到，不靠用户反馈。

**Q: 怎么升级到新版本？自动提示吗？**
A: 会。v2.14.0 起每次启动 CLI 或 agent 会话都会后台检测 GitHub 最新 release：
- 有新版本 → 弹三选一提示（是 / 跳过本版 / 否）+ 改动摘要
- 选"是"→ 按你装的方式执行对应命令：
  - Claude Code: `/plugin update stock-deep-analyzer`
  - git clone: `cd UZI-Skill && git pull`
  - Hermes: `hermes skills update wbh604/UZI-Skill/skills/deep-analysis`
- 选"跳过本版"→ 该版本不再提示，下一个新版本出来时才再弹
- 选"否"→ 下次启动再问
- 网络慢 / 关掉检查：`export UZI_NO_UPDATE_CHECK=1`（CI / Codex 环境推荐）
- 缓存 6 小时 · 不会每次都打 GitHub API

**Q: 之前报告里有 BUG 怎么办？**
A: 2026-04-17 前跑过"工业金属 / 工业母机 / 工业机械"相关股票的用户，cache 里的 `7_industry` 维度是错的（云铝被归为农副食品加工的那个 bug）。清 cache 重跑即可：
```bash
rm -rf skills/deep-analysis/scripts/.cache/<ticker>/raw_data.json
python run.py <ticker> --no-resume
```

---

## 📋 更新日志

| 版本 | 日期 | 主要变化 |
|---|---|---|
| **v3.9.4** | 2026-08-08 | **前端显示修复 + 美化（kimi-k3）** · ① None 泄漏清零：政策面（方向/补贴/监管/反垄断）与资金面"主力资金 20日"在数据源返回 null 时曾直接显示 `None`，现统一兜底为 "—"/"数据暂缺"。② 移动端图表裁切：sparkline SVG 固定 220px 宽度撑破容器（LBO 的 EBITDA 路径/债务偿还、维度卡迷你图），改为 `width:100%` 自适应 + 移动端单列堆叠；敏感性表/机构建模表移动端横向可滚动。③ 治理面措辞："关联交易/违规"此前仅因搜过查询词就显示"未发现"（`qualitative_search` 存的是搜索词非结果），改为中性"暂无公开违规记录"。④ 抄作业卡片"时间框架/仓位风格/翻盘条件"标签与正文粘连，改为标签不换行+留白+正文右对齐。⑤ 美化：维度卡顶部常驻分级色条（高绿/中金/低红）+ hover 高亮、全局平滑锚点滚动、文本选择色、`:focus-visible` 聚焦环、细滚动条、section/卡片进入视口轻浮现（尊重 `prefers-reduced-motion`）、TOC 锚点 `scroll-margin` 防遮挡。⑥ 版本对齐：3 个子 skill（investor-panel/lhb-analyzer/trap-detector）原停在 3.9.2 与主 manifest 不一致，全部统一 bump 到 3.9.4。全量 685 passed |
| **Unreleased** | 2026-08-05 | **全球同行业绩对比 + 数据完整性 hotfix** · 自动按全球细分行业发现候选并补全 Top 8 年度财务，覆盖日/韩/台/新/印/加/澳/英/欧洲等 Yahoo 交易所后缀；原币与 USD 标准化值分离，跨币种原始市值不混算，同行失败隔离、后备候选递补并缓存 24h。报告新增目标高亮、同行中位数、全球分位、规模/毛利率散点和明细表，结果接入 Comps 与维度评分。同步包含 issue #87/#90 的行情 scale、自身同行剔除和美股 TTM 修复。22 个全球同行专项测试 · 全量 685 passed |
| **v3.9.3** | 2026-08-07 | **茅台深度实测驱动的数据/渲染修复** · ① 现金流误判：`fcf_positive` 从 OCF/净利比改为真实 FCF，茅台(FCF 658亿)不再显示"现金流为负"；`_ic_risks` 的"行业周期下行"不再无条件追加，仅行业判定衰退时才算。② 同行修复：push2 被反爬时 4_peers 曾只剩自己一行("暂无可比股")——新增 INDUSTRY_PEERS 硬编码同行兜底(白酒→五粮液/泸州老窖/洋河/山西汾酒)，用 `stock_financial_analysis_indicator_em`(不走 push2)拉 ROE。③ 渲染修复：评委 pass/fail dict 不再泄漏成 Python 字面量(报告曾出现 162 处 `{'msg':...}`)；4_peers 对比条 `abs(None)` 崩溃；一致目标价不再显示"(None)"。④ deep 档强制 agent 介入 role-play：`--depth deep` 不再 CLI 一把梭，须 stage1 → 读 persona → 写 agent_analysis.json(`per_investor_override` 已能真正合并)。⑤ Codex 评审反馈(P1/P2)：`fcf_positive` 优先用真实经营现金流(净利×0.8 仅作缺失回退)；同行兜底补别名匹配(集成电路→半导体 / 工业金属→有色金属)且被分析股不在同行列表时补 self 行；HK rank-only 的 `peer="—"` 不再被渲染成 0 对比条。45+ 回归测试 |
| **v3.9.2** | 2026-07-07 | **流程与数据契约 hotfix（issue #82/#83）** · ① `fetch_financials` 显式输出 `ocf` / `ocf_history` / `ocf_to_net_income_ratio`，不再只把经营现金流藏在 `fcf` 字段里；`stock_features` 读入 OCF/净利比，避免 trap-detector 默认 1.0 误判。② `industry=None` 时 `fetch_peers` 返回 self-only fallback + reason，`fetch_valuation` 用 cninfo 市场加权 PE 兜底并标明原因。③ pipeline registry 字段契约对齐 legacy 输出（`financial_health` / `pe_quantile` 等），避免假 data_gaps。④ `agent_analysis.json` 结构性 schema error 现在真正 fallback 到脚本骨架，不再污染 synthesis。⑤ `run.py` 统一 fund summary / `--versus` / `--portfolio` 的浏览器、`--output-dir`、`--remote` 后处理；`cloudflared` 缺失时默认不自动安装，需显式 `--install-cloudflared`。8 个新回归测试 |
| **v3.9.1** | 2026-06-23 | **HTML 报告导航栏可折叠（issue #79 · @QKioi）** · v3.6.0 加的左侧 sticky 章节导航栏会略微遮挡正文，本次按社区建议加折叠按钮：展开态 `◀`，点一下收起成一个 `☰` 小把手（items 全藏 · 不再压住正文），再点一下展开 · 状态写入 `localStorage`(`uzi-toc-collapsed`) 刷新记忆 · 全程安全 DOM(无 innerHTML) + `aria-expanded` 可访问性. 7 个新回归 · 总 649 passed |
| **v3.9.0** | 2026-06-11 | **新评委「股海贼王」· 首位从真实交割单蒸馏的评委 (65→66)** · 数据源：淘股吧十年实盘帖 (2016-02 开贴) · 3898 张持仓截图 OCR → **8951 笔反推交割单** + **5069 条发言**. 定量画像：33 万→3131 万 (~95 倍/10 年) · 持仓中位 1 天/P75 3 天 · 同时 3-5 只 · 第一重仓中位 51% · 10 年 2010 只票题材轮动 (鸿博/川能/人民网/大众交通). 方法论蒸馏（风格提炼·不逐字转载原帖）：复盘三问(为啥涨停/板块地位/大盘地位) · 弱转强快速板才算超预期 · 逻辑硬的低位票爆发力足 · 格局票=时代的情绪载体(三五倍格局论) · 反复强调不跟单. 落地：F 组 flagship · 6 条数据驱动规则 (阈值来自其真实行为统计) · 台词按风格原创撰写 · `docs/ghzw-dossier.md` 蒸馏档案. **原始交割单/截图/发言均为本地数据 · 未入库.** 实测：鸿博式妖股 bullish 100 (他真做过 22 次) · 茅台 bearish 9.5 · 美股 skip. 10 个新回归 · 总 642 passed |
| **v3.8.1** | 2026-06-09 | **skills 全面体检 · H/I 两组配套层 6 处补齐** · 体检发现 v3.6.3/v3.7.0 加 14 位评委时配套层漏更新（全部静默降级所以没暴露）：① 14 评委缺头像 SVG → 报告破图（gen_pixel_avatars 补齐 · 总 65）② `render_school_scores` order=[A..G] → H/I 两派分数永远不渲染 ③ GROUP_LABELS ×3 处缺 H/I → 显示裸字母 ④ `GROUP_DEFAULT` 缺 H/I → profile 全 "—" ⑤ `STYLE_GROUP_WEIGHTS` 缺 H/I → v2.7 风格加权对两组失效（现 I 在红利股 0.2/科技成长 1.5）⑥ 13 新评委补显式 MARKET_SCOPE + PERSONAS voice 台词（之前群聊全是 generic 套话）. 文档同步 ~35 处（52→65 评委 / 7→9 流派 / 180→236 规则 / --school A-G→A-I）. 10 个新体检回归 · 总 632 passed |
| **v3.8.0** | 2026-06-08 | **Tier-1 五方法 + Serenity 严谨化 + 技术指标/杜邦扩展**（参考 `anthropics/financial-services` + `lolifamily/ashare-mcp` + `muxuuu/serenity-skill`）. **① Tier-1 5 方法**（新包 `lib/tier1/`）：`/ai-readiness`（AI 就绪度/卡位 · 复用 `ai_chokepoint_score`）· `/earnings-preview`（财报前预览 · Bull/Base/Bear + 隐含波动）· `/model-update`（增量更新模型 · 假设 delta + 对 DCF/Comps/thesis 影响）· `/returns`（组合收益归因）· `/rebalance`（逐持仓再平衡 + 本地化换手成本，A股无资本利得税故不做 TLH）. **② Serenity 严谨化**（不再只会看多）：8 罚分因子（炒作无订单/微盘流动性/杀猪盘/治理/周期/替代设计/地缘/稀释 · 封顶 60% 折扣）+ 3 级证据阶梯（强公告财报×1.0 > 中媒体卖方×0.85 > 弱叙事×0.70 · 同卡位"有定点量产"≈90 vs"仅题材"≈60）+ 供应链 8 层分层（材料→...→下游 · 越上游瓶颈分越高）. **③ 指标扩展**：DuPont 杜邦分解（ROE 质量来源 margin_driven/leverage_driven · 价值派用）+ KDJ/OBV/Williams%R（K 线卡片新增副指标徽章 · 技术派用）. 61 个新回归测试 · 总 622 passed |
| **v3.7.1** | 2026-06-04 | **首页补 Serenity 介绍 + `--school H/I` 放开** · 用户反馈"README 没把 Serenity 写清楚". 修复：(1) 评审团章节重写 52→65 人 · 7→9 组完整表 (A–I)· 规则 180→236; (2) 新增专门 `## 🧠 I 组 · Serenity · AI 卡位/瓶颈猎手` 介绍块（她是谁 + 未审计免责 + Chokepoint Theory 作用 + "卡位决定态度"打分表 + `--school I/H` 用法）; (3) **bug fix** · v3.7.0 起 evaluator `SCHOOL_LABELS` 已含 H/I 但 `run.py` argparse `choices` 还停在 A-G · 文档说能用实跑报错 · 现 choices 扩到 A-I + `_SCHOOL_NAMES` 补中文名; (4) README/CLAUDE/AGENTS/GEMINI 当前状态计数 52→65 评委同步（历史 changelog 不动）. 533 passed |
| **v3.7.0** | 2026-06-03 | **13 位新晋科技大佬集体入团 · 52→65 评委** · 用户反馈"评委库新晋科技 / AI / VC 视角覆盖不足". 新增分布：**B 成长派 +5** (Marc Andreessen a16z / Bill Gurley Benchmark / Naval Ravikant AngelList / Brad Gerstner Altimeter / Chamath Palihapitiya Social Capital) · **C 宏观派 +2** 做空猎手 (Michael Burry Big Short / Jim Chanos Kynikos) · **E 中国价投 +1** (张磊 高瓴 · "做时间的朋友") · **G 量化派 +1** (Cliff Asness AQR 价值×质量×动量三因子) · **H AI 卡位/瓶颈猎手 +4** (黄仁勋 NVIDIA / Musk TSLA / Sam Altman OpenAI / Saylor MSTR). 每位评委 ≥4 条规则 (测试守护) · `_render_school_lock_banner` THEMES 全派代表评委更新为真实在册成员 · H 派配色紫色 🔗. NVDA 跑分实测 Andreessen/Gerstner/Huang/Altman 全 100 · 茅台跑分 Andreessen 38 (industry filter ✓ 白酒不在 software/AI) · 张磊 80 (✓ 长跑道龙头). 18 个新回归测试 · 总 532 passed |
| **v3.6.3** | 2026-06-03 | **重磅角色 Serenity · AI 卡位/瓶颈猎手（新增第 52 位评委 · 独立 I 组）** · 把 X 爆火的 AI 供应链「卡脖子/瓶颈点」投资人 Serenity ([@aleabitoreddit](https://x.com/aleabitoreddit)) 接入评审团 · 参考 [serenity-alpha skill](https://github.com/haskaomni/serenity-skill/tree/main/serenity-alpha) 方法论 + 爬 X 真实言论建语气库. **核心打分逻辑「卡位决定态度」**：新增派生特征 `ai_chokepoint_score`（AI 链命中 × 不可替代性 × 中小市值弹性 × 需求拐点）· `SERENITY_RULES` 全 weight 5 · 产品卡在 AI 瓶颈（光模块/CPO/HBM/CoWoS/InP 衬底…）+ 上游不可替代 + 小市值 → **bullish 重仓**；没卡到位/不在 AI 链上 → **bearish 不碰**（白酒/银行护城河满分也 score=0）；在链但卡位不硬 → neutral 待验证. 改动 `investor_db`(独立 I 组)/`investor_criteria`/`investor_evaluator`(`--school I`)/`investor_personas`/`investor_profile`/`stock_features`/`agents/investor-panel.md`/`investor-cards.json`(置顶高亮). 新增 `group-i-serenity.md` + `fin-methods/serenity-bottleneck.md`（六步法 + alpha 5 维 + $AXTI 范例）+ `serenity-voice.md` 语气库 + `docs/serenity-research-dossier.md`（全网 20 条评价档案）. 8 个新回归 + 实测水晶光电(neutral 59) · 总 533 passed |
| **v3.6.2** | 2026-06-03 | **cninfo 翻页长尾修复 + Hermes 脚本 pip 探测** · 两个社群 issue 同发. **#68** ([@xy2yp](https://github.com/wbh604/UZI-Skill/issues/68))：`--versus 000958 600406 --depth lite` 卡在 15_events cninfo 公告 · `0/854 [01:53<6:11:58]` · 根因 `akshare.stock_zh_a_disclosure_report_cninfo` 内部翻完全部 854 页才返回 · 我们 `.head(30)` 是事后截取没用. 修法：新增 `_cninfo_direct_api` 直连 cninfo `/new/hisAnnouncement/query` HTTP · `pageSize=30 + pageNum=1 + 15s 硬超时` · akshare 慢路径默认禁用（`UZI_AK_CNINFO_FALLBACK=1` opt-in）· 几小时 → ≤15s. **#69** ([@FrankHuy](https://github.com/wbh604/UZI-Skill/issues/69))：Linux + Py3.11 跑安装脚本 `pip: command not found` · akshare 装不上. 修法：脚本启动加 Python 版本预检（≥3.10）· pip 五层级联探测（venv/.venv/pip/pip3/python -m pip）· 完全失败时给 apt/yum/ensurepip 三套指令 · install 失败给镜像源 + 升级 pip 建议. 12 个新回归 · 总 507 passed |
| **v3.6.1** | 2026-05-29 | **Hermes Skills Guard 假阳性绕过** · [issue #66](https://github.com/wbh604/UZI-Skill/issues/66) (@zodiacg) 反馈：`hermes skills install` 报 `Verdict: DANGEROUS · 168 findings` · `--force` 也覆盖不了. 诊断：Hermes Skills Guard 模式匹配假阳性 · 把我们读 `os.environ.get("UZI_DEPTH")` 当 "exfiltration" · `subprocess.run(["brew", "install", "cloudflared"])` 当 "privilege_escalation" · HTML 注释当 "injection". Hermes 团队已知问题 ([#1006](https://github.com/NousResearch/hermes-agent/issues/1006)/[#7072](https://github.com/NousResearch/hermes-agent/issues/7072))· 官方 builtin skills 也被自家扫描器拦. 修法：新增 `install-hermes.sh` (96 行 · `set -euo pipefail`) · `curl ... \| bash` 一键 clone + symlink + venv pip · 完全等价 Hub 安装但跳过扫描. `INSTALL-HERMES.md` 头部重写解释假阳性原因. 11 个新回归测试守护 (script 存在/bash 语法/严格模式/4 skill 覆盖/venv fallback/文档链接) · 总 495 passed |
| **v3.6.0** | 2026-05-29 | **视觉/交互大升级 + 多股横向对比 + 组合分析** · 三 Phase 拼合发布. **Phase A 视觉**：(1) 暗色模式 toggle（右上角 🌙 · `localStorage` 持久化 + `prefers-color-scheme` 自动初始化）· (2) 左侧 sticky TOC + IntersectionObserver scroll-spy · (3) 大评分 count-up 动画 · (4) PE/PB/ROE/DCF/IRR/WACC/EV-EBITDA/LBO/YTD/TTM/PEG/LHB 自动包 `.jargon` + 悬浮 tooltip · (5) 报告底部 QR 码（扫码直达完整报告）· 🔒 全部用 `createElement + textContent` 安全 DOM · 不用 innerHTML (XSS 防御). **Phase B `--versus`**：2-4 只票横向对比 · `lib/versus_runner.py` (380 行)· ★ WIN 高亮 12 个核心指标 · 单 HTML 自包含 · 复用 cache. **Phase C `--portfolio`**：用户上传 CSV (ticker/weight/note) · 容错解析（header/无 header/中英文列名/0-1 vs 0-100 权重）· 权重自动归一 · 输出排名 + KPI（加权评分 + 集中度 + 行业分散）+ metadata.json · `lib/portfolio_runner.py` (370 行). Phase D（`--sector` / `--as-of`）需 fetcher date-aware · 留到 v3.7. 39 个新回归测试 · 总 484 passed |
| **v3.5.0** | 2026-05-29 | **单一流派视角锁定 (`--school A-G`) + SaaS 集成 (`--output-dir`)** · 社群反馈"我只想看 F 派游资视角 · 不想 51 评委一起 vote". (1) `run.py` 加 `--school A/B/C/D/E/F/G` · 价值/成长/宏观/技术/中国价投/游资/量化 七选一 · `investor_evaluator.evaluate` 入口检查 `UZI_SCHOOL` env · 非该派评委直接 `_skip_result(reason="用户锁定 X 派视角")` 不进规则引擎. (2) `synthesis.school_lock={group,label}` 编码进 syn · `_render_school_lock_banner` 7 派各自配色（A=深绿/F=深红/G=青）渲染在报告顶部 · 让分享者一眼看出本次仅看了该派 · 避免被误读为 51 评委结论. (3) `SKILL.md` 加 HARD-GATE · agent role-play 时严格只 role-play 该派 · `panel_insights` 不写跨派对比. (4) 同时合入早先准备的 `--output-dir DIR` SaaS 集成参数 · 把 `reports/{ticker}_{date}/` 拷到外部路径 + 写 `index.html` / `report.meta.json` 供 Celery worker 落库. 11 个新回归测试 · 总 445 passed |
| **v3.4.5** | 2026-05-12 | **F 派游资 LHB 反查 + low-confidence banner** · 社群 codex agent 跑京东方 (000725) 实测发现两点：① F 派 23 人全 skip（市值 2000 亿超射程）· 但 LHB 实际有 3-5 个游资席位参与涨停博弈 · 评委逻辑与数据脱节. ② 规则引擎 fund_score 37.6 但 agent 重评 65/100 · 报告无任何"score 不可信"警告 · 用户误判. 修法：(1) `_is_youzi_out_of_range` 加 LHB 反查 · features.matched_youzi 含该游资昵称时强制不 skip. (2) `_render_data_gap_banner` 新增 syn 参数 · stock + fund_score<50 + cov<60% → 渲染 low-confidence 红色 banner · 文案明确引导看 agent 重评估. 10 个新回归测试 · 总 407 passed |
| **v3.4.4** | 2026-05-12 | **data quality banner UX 优化** · 社群反馈两点：① ETF 报告 "覆盖率 17%" 让用户误判可信度（实际 ETF 本就无 ROE/PE 个股字段）· ② 橙色 banner 上橙色字（`#f59e0b/#fbbf24`）看不清. 修法：(1) banner 检测 ETF/LOF/mutual_fund · 切换 `fund-type` 蓝色调 banner · 文案明确"基金类型预期缺字段·不影响可信度"+ 引导看持仓股报告. (2) CSS 对比度大改 · title→#92400e 深棕 · subtitle strong→#7c2d12 + 加粗 800 · chip 文字→#7c2d12 · subtitle 正文→#1f2937 深灰. 11 个新 CSS + 行为回归测试 · 总 397 passed |
| **v3.4.3** | 2026-05-12 | **开放式基金分类修复 + 字段级 fallback gate** · (1) [#60 复议](https://github.com/wbh604/UZI-Skill/issues/60) (@SchrodingerBarbatos) · 用户输 110011 易方达优质 → 被错判 convertible_bond early-exit. 修法：`classify_security_type` 在判 cb 前用 `akshare.fund_name_em` 二次校验 · 加 `mutual_fund` 类型 · run.py + preflight 路由到 fund_holdings_runner · 005xxx 等股票前缀外的基金也识别. (2) [PR #63](https://github.com/wbh604/UZI-Skill/pull/63) (@Wood Letitia · 313+137 行) · 字段级 fallback gate · 修 source-level 整块切换导致 name 永远缺的问题 · 主源拿到 price/PE/PB 但 name 空时 · 自动调 tencent_qt → baostock → ak_code_name 补全 · 仅填空不覆盖. 386 tests passed |
| **v3.4.2** | 2026-05-11 | **Windows + Clash + Schannel TLS 兼容** · 社群反馈"Clash 国内规则 DIRECT 还是 Schannel · 东方财富彻底不可用 · 只有 baostock 能用". 修法：(1) `lib/data_sources.fetch_basic` 在所有 eastmoney 链路（xueqiu / push2 / baidu / tencent_qt）后追加 baostock fallback · `query_history_k_data_plus` 拿 peTTM/pbMRQ/close · `query_stock_basic` 拿 name/listed_date; (2) `fetch_financials._fetch_a_share` 当 ROE/营收/净利率全空时调 baostock `query_profit_data` 拉 5 年季报 · 解析 ROE history / 营收 history / 净利率 / 毛利率. 实测茅台 baostock 拿到 ROE=19.25% / 净利率 52.6% / 营收 893.5 亿. 6 个新回归 test (含真机烟雾) · 总 374 passed |
| **v3.4.1** | 2026-05-11 | **verdict 粒度细化** · 社群反馈"神剑股份(58分) / 博云新材(60分) verdict 都是观望优先 看不出差异". 修法：(1) verdict 50-65 拆三档（观望偏空 50-55 / 观望中性 55-60 / 观望偏多 60-65）· 65-70 加"可以蹲（偏弱）"; (2) verdict label 追加"X 派看多 / Y 派看空"; (3) synthesis 加 `verdict_detail = "基本面 X · 共识 Y"` · assemble_report 渲染时拼到 verdict 后. 改后两只票 verdict 仍同段（55-60）但 detail 让基本面 +2 分差异显式可见. 5 个新回归 test · 总 368 passed |
| **v3.4.0** | 2026-05-10 | **基金/ETF 持仓循环分析 + baostock ≥0.9.1** · ETF/LOF 不再 early-exit · 改为列出前 10 持仓 + 估算耗时 + **二次确认**（y / 数字 / N） · 确认后循环跑 stock-analyze + 生成 fund-holdings-summary.html. partial failure 容忍 · `UZI_FUND_AUTO_YES=1` 跳过 prompt（CI/agent）. 可转债/指数仍 early-exit. 同时 `requirements.txt` 锁 `baostock>=0.9.1` (社群通知 2026-04-22 起服务端要求). 新建 `lib/fund_holdings_runner.py` (240 行) · 7 个回归测试 · 总 362 passed. 真机：510300 持仓正确拉到（茅台 5.89% top1） |
| **v3.3.4** | 2026-05-10 | **mini_racer V8 crash escape hatch** ([#61](https://github.com/wbh604/UZI-Skill/issues/61)) · 用户 @dragonforai 报 `python run.py SEHK.03690 --depth deep` → `[FATAL:address_pool_manager.cc(67)] Check failed`. 根因：macOS Py 3.12/3.13 下 mini_racer V8 isolate pool 即使串行化仍可能双重初始化 SIGTRAP（进程级崩 · Python `try/except` 抓不到）. 多重修法：① `UZI_DISABLE_MINI_RACER=1` 显式禁用. ② **Sentinel 文件机制（核心创新）**：调 mini_racer fetcher 前 arm `~/.uzi-skill/_minirackercrash.sentinel` · 成功后 disarm · 进程崩则 sentinel 留下 · 下次启动自动 disable + 提示用户. ③ `UZI_FORCE_MINI_RACER=1` 强制启用. legacy + pipeline 双路径都加保护. 新增 7 个回归 test · 总 355 passed |
| **v3.3.3** | 2026-05-06 | **社区 PR 4 合 1**（[#52](https://github.com/wbh604/UZI-Skill/pull/52) / [#54](https://github.com/wbh604/UZI-Skill/pull/54) / [#55](https://github.com/wbh604/UZI-Skill/pull/55) / [#59](https://github.com/wbh604/UZI-Skill/pull/59)） · #52 [LHB akshare 1.18+ "近一月" 失效](https://github.com/wbh604/UZI-Skill/pull/52) (@qdby26) · 改 YYYYMMDD 日期循环 + 6 mock test。#55 [agent_analysis schema docs](https://github.com/wbh604/UZI-Skill/pull/55) (@DragonQuix) · SKILL.md + analyze-stock.md 文档化 12 条 validator 校验规则。#54 (@DragonQuix) cherry-pick svg_radar import · #59 (@Charlson852) cherry-pick Python 3.11 嵌套 f-string SyntaxError 修复（**避开了 #59 原版的 items.append 缩进 bug**）· 新增 5 个回归 test 守护 · 总 348 passed |
| **v3.3.2** | 2026-04-28 | **GitHub issue #50 + #51 hotfix**（社区驱动）· #50 [Stage 2 总是超时](https://github.com/wbh604/UZI-Skill/issues/50)：v3.2 拆分时 `lib/report/institutional.py` 漏 import `svg_sparkline` · `_render_lbo_block` 触发 NameError → stage2 崩 · 加进 import 块即修复（致谢 @chenxiang-bj 报告 + agent 诊断）。#51 [XueQiu 登录验证失败](https://github.com/wbh604/UZI-Skill/issues/51)：`/cubes/cubes_search.json` endpoint 已下线 · 改用 `/query/v1/search/cube/stock.json?q={code}` (致谢 @bilieebiliee1-design 报告 + @Kylin824 提供 fix)。3 处文件同步换 endpoint。新增 5 个回归 test · 总 337 passed |
| **v3.3.1** | 2026-04-28 | **Hermes 兼容回归修复 (hotfix)** · 群友反馈"更新后不支持 hermes 报错"。根因：v3.0/v3.1/v3.2 重构期 main 上从未包含 hermes 兼容代码（`INSTALL-HERMES.md` / `skills/deep-analysis/run.py` / `requirements.txt` / 4 个 SKILL.md hermes metadata 全缺）· 但 README 仍叫 hermes 用户装 main · 装下来缺文件就崩。修复：从 `hermes-compat` 分支 cherry-pick 5 项核心适配到 main + `run.py` 加双 layout 探测。**hermes 用户重装一次 skill 即恢复**（`hermes skills uninstall` + install 4 个）|
| **v3.3.0** | 2026-04-23 | **分支大整合**：唯一未合 feature（v2.10 segmental 渲染层 · 228 行 + 222 CSS）cherry-pick 到 v3.2 架构 · 新建 `lib/report/segmental.py` (555 行)。同时清理 22 个 stale 分支（refactor/* / docs/* / feature/v2.14-v2.15.3 都已 superseded）· 仅保留 main + hermes-compat · 单一开发主干. |
| **v3.2.0** | 2026-04-23 | **assemble_report.py 深度拆分 (-80%)**：2964 → 587 行 · 拆 5 个子模块 `lib/report/svg_primitives` (602) / `dim_viz` (742) / `institutional` (532) / `panel_cards` (183) / `special_cards` (544)。v2.x 所有 API 保持 re-export · 332 tests 全过 · e2e 零差异 · 加 `CODEX.md` + `AGENTS.md::Repository Layout` 给 codex 准确入口指引 |
| **v3.1.0** | 2026-04-23 | **run_real_test.py 瘦身 65%**：2105 → 735 行。1228 行纯函数 (`_f/score_dimensions/generate_panel/generate_synthesis/_autofill_qualitative_via_mx`) → `lib/pipeline/score_fns.py`；166 行 preflight/resolve/ETF guard → `lib/pipeline/preflight_helpers.py::prepare_target()`。rrt 做 re-export 保持完整向后兼容 · 332 tests 全过 · 002217 resume 10s 出报告 |
| **v3.0.0** | 2026-04-23 | **pipeline 架构默认启用**：`python run.py <ticker>` 默认走 `pipeline.run_pipeline` · `UZI_LEGACY=1` 强制回老路径 · pipeline 异常自动 fallback。Phase 6c 解耦：`pipeline.score` 直接调 rrt 纯函数（不再走 stage1 重复 collect）· 002217 score_from_cache 从 180s → 10.6s。Pipeline 预检 guards (中文名 / ETF / LOF / 可转债 → fallback legacy) |
| **v2.15.5** | 2026-04-23 | **评分公式重校准**：`panel_consensus` 从单一 vote 公式升级为混合公式 `0.65*score_mean + 0.35*vote_weighted` + 极化拉伸 (k=1.3) · 解决"大多数分在一个区间徘徊"问题 · 7 流派 consensus 分歧更清晰 · 002217 F 游资 51→43.7（vote 高估修正）· G 量化 50→59.3（实分低估修正） |
| **v2.15.4** | 2026-04-22 | **按流派打分 school_scores**：7 大流派 (A 价值 / B 成长 / C 宏观 / D 技术 / E 中式 / F 游资 / G 量化) 各自产出 consensus/avg_score/verdict · 报告新增"SCHOOL SCORES"卡片 · 同一只票可见不同哲学的分歧（宏观派买入 vs 成长派回避） |
| **v2.15.3** | 2026-04-21 | **性能 hotfix** · fetch_capital_flow 每股重抓全 A 大宗/解禁/融资数据集 → 每股 3+ min · 新增 `_universe_*()` 4 个 helper + `cached("_universe", ...)` 24h TTL · 全股共享 · 二次跑 universe 部分 0.01s（**100+ 倍加速**）· 6 专项测试 |
| **v2.15.2** | 2026-04-21 | **GitHub issue hotfix**：#36 Gemini CLI 安装报错（gemini-extension.json 加 version + 纳入 version-bump）· #30 网络自检增强（Clash 本地代理端口侦测 + 数据源分组诊断 + 多行修复建议 · 写进 `.cache/_global/network_profile.json` 供 agent 读）· 10 专项测试 |
| **v2.15.1** | 2026-04-20 | **报告质量 2 bug hotfix**（实测 300470 发现）· Bug 1: fund-card 一堆 "5Y +0.0%" 假数据 · 修 `_build_row_full` + `render_fund_managers` 让 lite 行降级 · Bug 2: 14_moat 护城河被贵州茅台数据污染（DDGS 对生僻公司返超级股票结果）· 加 `_SUPERSTAR_POLLUTERS` 过滤 · 11 专项测试 |
| **v2.15.0** | 2026-04-20 | **YAML persona 接入 agent role-play**（借鉴 augur · 取长补短）：新增 `personas/` 目录 51 个 YAML 文件（12 flagship 手写 + 39 stub 自动生成）· `lib/personas.py` 加载 + prefix-stable system message（prompt cache 省 50-90%）· `lib/i18n.py` zh/en 语言开关 · `HARD-GATE-PERSONA-ROLEPLAY` 强制 agent 读 YAML · 双盲测试（3 股 × 5 投资者）显示 YAML 14/15 vs Rules 8/15 方向准确率 · 修复 Rules 4 类"历史立场错位"硬伤 · 14 专项测试 |
| **v2.14.0** | 2026-04-20 | **自动检测 GitHub 新版本**：每次启动 CLI 或 agent 会话，插件会检测 GitHub latest release · 有更新则弹 `y / s / n` 三选一（是 / 跳过本版 / 否）· 跳过本版后直到下一版才再弹 · 6h 缓存防 API 限流 · 非 TTY / `UZI_NO_UPDATE_CHECK=1` / 网络异常 silent skip 不阻塞 · `HARD-GATE-UPDATE-PROMPT` 让 agent 主动展示 · 13 专项测试 |
| **main** | 2026-04-20 | **Segmental Revenue Build-Up 落地**（`lib/segmental_model.py` 408 行 + `compute_segmental.py` CLI + `/segmental-model` slash command）· deep 档 `enable_segmental_model` flag 之前只在 profile 声明，实现缺失——本次从老分支 cherry-pick 3 个新文件，零冲突接入 · 同步新增 `CONTRIBUTORS.md` · 清理 14 个已合入幽灵分支 |
| **v2.13.7** | 2026-04-19 | **16 新源真正接入 fetcher**：v2.13.4/6 登记但未用的 16 源全部接通——新建 `lib/news_providers.py`（jin10/em 快讯/em 公告/同花顺 4 源聚合）接入 `fetch_events` + `fetch_sentiment`；`_yahoo_v8_chart` 直连 HTTP 接入 US/HK K 线链（绕 yfinance cookie）；cfachina 期货协会源接入 `fetch_policy`（期货/商品 industry 专用）。实测 4/4 新闻源通 · A 股 15_events 密度 3-5 → 10-30 条 · pytest 217 passed |
| **v2.13.6** | 2026-04-19 | **新增 6 个经 curl 验证的期货 + 财经新闻源**（SOURCES 64 → 70）：jin10_flash（类财联社零 Key 替代）/ em_kuaixun / em_stock_ann / ths_news_today / 99qh / cfachina · 8 专项测试 |
| **v2.13.5** | 2026-04-19 | **NetworkProfile 自适应 + agent HARD-GATE 主动触发 Playwright**：9 目标 3 组网络预检（domestic/overseas/search）+ 代理检测 + 5min cache · SKILL.md / AGENTS.md 加 `HARD-GATE-PLAYWRIGHT-AUTOFILL` 让 agent 主动 FORCE · 15 专项测试 |
| **v2.13.4** | 2026-04-19 | **新增 10 个经 curl 验证的无 Key 公开数据源**（SOURCES 54 → 64）：Yahoo Chart v8 / 腾讯 qt HK / 加密货币（CoinGecko/Binance/CoinCap）/ ECB / World Bank 等 · 11 专项测试 |
| **v2.13.3** | 2026-04-19 | **51 评委规则全员历史立场还原**：林奇 PE<40 Rolls Royce 红线、索罗斯 long/short 拆分、木头姐 CPO/光模块 whitelist、段永平/张坤/邓晓峰 PE 硬红线、游资 range 校验 · 10 专项测试 |
| **v2.13.2** | 2026-04-19 | **Playwright 触发逻辑升级 · 数据质量感知 + FORCE flag**：`_dim_quality_score` 公开字段真空检测 · `UZI_PLAYWRIGHT_FORCE=1` 强制全维兜底 · 8 专项测试 |
| **v2.13.1** | 2026-04-18 | **Playwright 全 10 维覆盖**（开源研究场景扩展）：v2.13.0 Codex 保守排除的 5 维（7_industry 百度搜索 / 14_moat 百度百科 / 13_policy 证监会 / 18_trap 小红书 / 19_contests 雪球组合）全部加回 · medium 4→6 维 · deep 5→10 维 · 22 专项测试 |
| **v2.13.0** | 2026-04-18 | **Playwright 通用兜底 · 按三档 profile 分级**：lite `off` / medium `opt-in` (4 维) / deep `default` (5 维 · 首次 y/n 自动装 Chromium)。新增 `lib/playwright_fallback.py` · 抽离 `lib/junk_filter.py` |
| **v2.12.1** | 2026-04-18 | **4 个报告板块空数据/错数据修复**（中际旭创实测驱动）：4_peers 三层 fallback + 雪球 Playwright opt-in · 7_industry regex 上下文感知 · core_material 垃圾过滤 · BCG 真实算 market_share + 阈值调整 · 16 专项测试 |
| **v2.12.0** | 2026-04-18 | **6 平台社交热榜聚合**：微博/知乎/百度/抖音/头条/B站 官方 API + 5min 文件缓存 + 单平台失败不影响其他 · `17_sentiment` 维度新增 `hot_trend_mentions` 字段补 DuckDuckGo 盲区 · 抄 jcp/hottrend 设计 · 17 个专项测试 |
| **v2.11.0** | 2026-04-18 | **评分校准（用户反馈驱动）**：论坛+微信反馈"茅台 47 分"、"没超过 65 分" → verdict 阈值 `85/70/55/40 → 80/65/50/35`；consensus neutral 权重 `0.5 → 0.6`（A 股白马结构性偏低问题）；`stock_style` 同步对齐 |
| **v2.10.7** | 2026-04-18 | **Codex 审查 3 处修复**：`raw.market` 硬编"A"污染 HK/US · `resume` 对别名输入失效（中文名/三位港股 cache 不命中）· AGENTS.md 强制全量流程与 CLI/lite 降载设计冲突 → 深浅两路径决策树 |
| **v2.10.6** | 2026-04-18 | **Providers 框架实际落地**：v2.10.3 建的 5 provider 链（akshare/efinance/tushare/baostock/direct_http）实际接入 `data_sources.py` K 线链 · Tushare kline 补齐 · `hermes skills install` 风格 health CLI |
| **v2.10.5** | 2026-04-18 | **v2.10.4 遗漏补丁**：`check_coverage_threshold` profile-aware（lite 不再结构性偏低）· `run.py` 自动 `UZI_CLI_ONLY=1`（medium/deep CLI 直跑也出报告）· `render_fund_managers` None 字段兜底 |
| **v2.10.4** | 2026-04-17 | **Codex 测试反馈 3 bug**：lite 模式 self-review 9 critical 误报 · `agent_analysis.json` 缺失 CLI 直跑误报 critical · ETF 早退 `RuntimeError: Stage 2 缺少数据` |
| **v2.10.3** | 2026-04-18 | **三档思考深度**：`lite` (30s-2min · 7 维 + 10 投资者) / `medium` (2-4min · 默认 · 22 维 + 51 投资者) / `deep` (15-20min · 含 Bull-Bear 辩论 + Segmental) · `--depth` CLI arg · direct_http provider（腾讯/新浪/etnet 直连脱离 akshare） |
| **v2.10.0-2** | 2026-04-18 | 首次安装 + Codex 机器耗时/token 优化 · `lib/net_timeout_guard` 4 层网络超时保护（代理/GFW 不通快速 fail） · Fund holders 双层策略（Top N full + rest lite） · 首次运行 10-15min → 2-4min |
| **v2.9.x** | 2026-04-17 | **机械级 agent 自查 gate**：13 条自动检查 + `assemble_report` 入口强制 block critical → 修完再出 HTML · fetch_industry 动态 `search_trusted`（236 个行业不再是"—"）· HK industry_pe fallback · consensus 半权公式 |
| **v2.8.x** | 2026-04-17 | **BUG#R10 修复** 行业分类碰撞（工业金属→农副食品加工）· 134 条申万→证监会硬映射 · 22 位海外人物真实原话 + URL 溯源 · 每评委自己方法论回答 3 字段 · English README · Munger/Alibaba hook |
| **v2.7.x** | 2026-04-17 | HK financials 实现（BUG#R7）· HK kline fallback 链（BUG#R8）· wave2 flush（BUG#R9）· 风格动态加权 7+1 · 量化结构性识别（top-1<2%）· XueQiu 登录 opt-in · 14 权威数据源 + search_trusted |
| **v2.6.x** | 2026-04-17 | agent 闭环写回 · `agent_analysis.json` 合并 · dim_commentary · 22 维覆盖 |
| **v2.5** | 2026-04-16 | 数据源注册表 54 条 · HK AASTOCKS 支持 · 3 层 tier 分类 |
| **v2.0–v2.3** | 2026-04-16 | 17 种机构分析方法 · 51 评委 180 规则 · 两段式 pipeline · MX 妙想 API · 多平台支持 |
| **v1.0** | 2026-04-14 | 初版 · 19 维 + 50 评委 + 杀猪盘检测 |

完整更新日志见 [RELEASE-NOTES.md](RELEASE-NOTES.md)

---

## 🤝 致谢

- [anthropics/financial-services-plugins](https://github.com/anthropics/financial-services-plugins) — 机构级分析方法论
- [obra/superpowers](https://github.com/obra/superpowers) — 多平台架构 / HARD-GATE / hooks / sub-agent 设计
- [akshare](https://github.com/akfamily/akshare) — A 股数据引擎
- [titanwings/colleague-skill](https://github.com/titanwings/colleague-skill) — Skill 架构参考
- [virattt/ai-hedge-fund](https://github.com/virattt/ai-hedge-fund) — Pydantic Signal 模式
- [TauricResearch/TradingAgents](https://github.com/TauricResearch/TradingAgents) — 多空辩论循环

---


## ⚠️ 免责声明

本工具由 AI 模型基于公开数据生成分析报告。所有评分、建议、模拟评语均为算法输出，不代表任何真实投资者的实际观点。**不构成投资建议**，投资有风险，入市需谨慎。

---

## ⭐ Star History

实时 stars：

<a href="https://star-history.com/#wbh604/uzi-skill&Date">
 
   
   
   
 
</a>

> 注：star-history.com 服务端有 24h 缓存，增长很猛的前几天图可能滞后（想看当前真实数字看上面的 shields.io badge，或点图进 star-history 官网会触发刷新）。

---

---

## File: docs/superpowers/specs/2026-06-03-serenity-role-design.md

# 重磅角色 Serenity 接入 UZI-Skill 评审团 — 设计文档

- 日期：2026-06-03
- 状态：待用户审阅
- 参考：[serenity-alpha skill](https://github.com/haskaomni/serenity-skill/tree/main/serenity-alpha)、X @aleabitoreddit

## 1. 背景与目标

Serenity（X @aleabitoreddit）是 2026 年爆火的海外散户/研究员，前 AI 研究科学家、前 RISC-V
基金会成员、光通信工程师。核心方法论是 **「AI 产业链卡脖子/瓶颈点」投资法**：不直接买 AI 龙头，
而是拆解供应链、找到**最难被替代、最容易出现供给瓶颈的二三线上游小盘**，抢在市场定价之前埋伏。
代表战是提前一年押中 InP 衬底瓶颈股 AXTI（$12 → $70+，10 倍+）。

目标：把 Serenity 作为**重磅角色**接入 UZI-Skill 的投资大佬评审团，覆盖 4 件事：
1. 新增一位评委（独立流派）；
2. 实现「卡位决定态度」的打分逻辑；
3. 提供独立的「瓶颈点投资法」分析模块；
4. 模仿其真实语气 + 沉淀其底层知识库；
并额外交付一份**全网研究方法评价档案**。

## 2. 现状架构（接入点）

评审团是**双层架构**：
- **规则引擎层**（确定性）：`skills/deep-analysis/scripts/lib/`
  - `investor_db.py` — 每位评委元数据（id/name/group/fields/avatar_seed），`assert_count` 现为 51。
  - `investor_criteria.py` — `INVESTOR_RULES[id]` = `Rule(check=lambda features)` 列表，出「骨架分」。
  - `investor_evaluator.py` — 跑规则 → score/signal/confidence；`SCHOOL_LABELS` 现为 A–G；
    `--school` 可锁单一流派；F 组有射程检查。
  - `investor_personas.py` — `PERSONAS[id]` = bullish/bearish/neutral 的 signature lines（拟人台词）。
- **LLM agent 层**（拟人）：`agents/investor-panel.md` — 按 Group Profile 复盘，**可覆盖**规则骨架分，
  产出最终 `{signal, score, headline, reasoning}` JSON。
- **展示层**：`skills/investor-panel/assets/investor-cards.json`（HTML 渲染卡片）、
  `skills/investor-panel/references/group-{a..g}.md`（方法论）、`quotes-knowledge-base.md`（语料）。
- 特征来源：`stock_features.py`，已有 `5_chain`（供应链）、`14_moat`（护城河 4 维）等维度。

现状注意点：
- 无 `build_cards.py`（`_meta` 注释提到但不存在）；`investor-cards.json` 现有 50 条 vs db 51 条，
  **已存在漂移**。本次手动同步，把 Serenity 补成第 52 条。
- `fin-methods/` 目录现仅 `README.md`。

## 3. 设计决策

| 决策 | 选择 | 理由 |
|---|---|---|
| 卡位判断放哪层 | **双层都做** | 规则引擎出 `ai_chokepoint_score` 骨架分（可追溯），LLM agent 层用人设做最终裁决，与现有哲学一致 |
| 流派归属 | **新建 H 组**「AI 卡位/瓶颈猎手」 | 方法论独特，塞进 A–G 任一组都失真；独立组可被 `--school H` 单独锁定 |
| 显示名 | **Serenity** + 中文 tagline「AI 卡脖子猎手」 | 保留推特 ID 辨识度 |
| 票权 | **仅高亮置顶，不加票权**（一人一票） | 不破坏现有共识分公平性；重磅感由高亮卡片 + 独立方法论专栏体现 |
| 语气来源 | 爬 @aleabitoreddit X 原帖 + Tracker/Substack/媒体直接引用 | 真实语料，避免编造 |

## 4. 实现方案（5 块）

### 块 1 · 新增第 52 位评委（H 组）
- `investor_db.py`：在末尾新增 H 组与条目：
  ```python
  {"id": "serenity", "name": "Serenity", "en": "Serenity (@aleabitoreddit)",
   "group": "H", "tier": "flagship",
   "fields": ["5_chain", "7_industry", "14_moat", "13_policy", "15_events"],
   "source": "serenity-alpha skill / X @aleabitoreddit", "avatar_seed": "Serenity-Chip"}
  ```
  `assert_count` 51 → 52；`__main__` 自检按组计数保持通过。
- `investor_evaluator.py`：`SCHOOL_LABELS["H"] = "AI 卡位/瓶颈猎手"`（使 `--school H` 生效）。
- `investor_personas.py`：新增 `PERSONAS["serenity"]`（见块 5）。
- `skills/investor-panel/references/group-h-serenity.md`：H 组方法论文档。
- `skills/investor-panel/assets/investor-cards.json`：手动新增 Serenity 卡片，`_meta.count`/groups 同步，
  `tier:"flagship"` 字段用于前端置顶高亮。

### 块 2 · 卡位打分逻辑（核心）
新增派生特征 `ai_chokepoint_score`（0–100），在 `stock_features.py` 计算，四因子合成：
1. **AI 链命中**：行业/`5_chain` 文本命中关键词库（光模块/CPO/光芯片/HBM/CoWoS/先进封装/InP 衬底/
   PCB/液冷/铜连接/电源/交换机/AI-ASIC/RISC-V/铜缆/PCB 载板…）。
2. **不可替代性**：`14_moat` 的 switching（切换成本）+ scale 分位高 → 上游卡点。
3. **弹性**：中小市值（市值越小弹性越大）。
4. **需求拐点**：`13_policy` / `15_events` 出现需求/扩产/缺货信号。

`SERENITY_RULES`（全部 weight 5，体现强信念）：
- 看多：`ai_chokepoint_score ≥ 70` 且不可替代性达标 → bullish。
- 中性：在 AI 链但卡位不明确（score 40–70）→ neutral，列出待验证项。
- **看空/skip：`ai_chokepoint_score < 40`（产品在 AI 浪潮里没卡到位）→ 他直接不碰**
  （= 用户强调的「卡位决定态度」）。
- LLM agent 层（块 4）H 组 profile 一句话定调：
  *「我只看一件事——这家的产品在当前 AI 浪潮里卡没卡住脖子。卡住=重仓，没卡=不看。」*

### 块 3 · 独立「瓶颈点投资法」分析模块
- 新增 `skills/deep-analysis/references/fin-methods/serenity-bottleneck.md`，落地 serenity-alpha 六步法：
  ① 信号去噪 ② 映射财务科目 ③ 猎misclassified 小盘 ④ 验证错误定价 ⑤ 建验证链 ⑥ alpha 评分。
  alpha 5 维评分：**确定性 / 清晰度 / 纯度 / 弹性 / 时间窗**。
- 报告输出**独立专栏**：供应链瓶颈地图 → 卡位评级 → 验证链（财报/backlog/ASP/产能/客户 roadmap）
  → 仓位建议。挂到 `task2.5-qualitative-deep-dive.md` 与 `task3-investor-panel.md` 流程。

### 块 4 · LLM agent 接入
- `agents/investor-panel.md` 增加 `### Group H · AI 卡位/瓶颈猎手 (Serenity)` profile：
  焦点 = 供应链不可替代性、二三线上游、需求拐点；忽略 EPS/估值传统指标；
  卡位裁决规则 + 上面那句定调；可覆盖规则骨架分。
- `task3-investor-panel.md` / `task3-agent-evaluation.md`：H 组纳入分派与 schema。

### 块 5 · 语气模拟 + 底层知识库
- 实现阶段爬取/汇总 @aleabitoreddit 原帖 + Tracker(semiconstocks.com)/Substack/PANews/Futu 等直接引用，
  产出：
  - `skills/investor-panel/references/serenity-voice.md` — 底层知识库（方法论 + 典型案例 AXTI 等 +
    口头禅/术语表）。
  - 并入 `quotes-knowledge-base.md` — 全局语料复用。
- `PERSONAS["serenity"]` signature lines 用其真实口吻，示例：
  - bullish：「别盯着 EPS——问这家在 AI 基建里可不可被替代。不可替代，就是印钞机。」
  - bearish：「{name} 在供应链里没有卡点，对我没意义。」
  - neutral：「卡位还没被验证，等客户 roadmap / 缺货信号坐实再说。」
  风格标签：技术流、供应链偏执、鄙视盯短期 EPS、断言式、点名具体节点、略带战斗性。

### 交付物 · 全网研究方法评价档案
- 新增 `docs/serenity-research-dossier.md`：汇总知乎/雪球/PANews/区块周刊/东方财富/腾讯/网易/
  Futu/KuCoin/HTX/Substack/semiconstocks 等所有帖子与评价。
- 每条 = 来源 + 核心观点摘要 + 对其方法的评价（正面/质疑，含「从不公开认错」「选择性展示」
  「收益不可审计」等批评）+ 原文链接。实现阶段再扩一轮搜索做全。

## 5. 测试与验收
- `investor_db.assert_count()` → 52 通过；`python investor_db.py` 分组计数含 H=1。
- 新增 `test_serenity_rules.py`：
  - AI 卡点小盘（如模拟 AXTI 类）→ Serenity bullish 且 score 高；
  - 非 AI/无卡位标的（如白酒/银行）→ skip 或 bearish；
  - `--school H` 锁定时仅 Serenity 参与、其余 skip。
- 现有 `test_v2_13_3_investor_rules.py` 等回归不破。
- 报告渲染：卡片置顶高亮 + 瓶颈点投资法专栏正常出现。

## 6. 影响文件清单
- 改：`investor_db.py`、`investor_evaluator.py`、`investor_criteria.py`、`investor_personas.py`、
  `stock_features.py`、`agents/investor-panel.md`、`investor-cards.json`、
  `task2.5/task3` 两个 reference、`quotes-knowledge-base.md`。
- 增：`references/group-h-serenity.md`、`fin-methods/serenity-bottleneck.md`、
  `references/serenity-voice.md`、`docs/serenity-research-dossier.md`、`tests/test_serenity_rules.py`。

## 7. 非目标（YAGNI）
- 不做实时抓取 X API（用静态汇总语料）。
- 不改共识分聚合算法（不加票权）。
- 不重构现有 build_cards 缺失问题（仅手动同步本次卡片）。

---

## File: docs/BUGS-LOG.md

# BUGS-LOG · 防回归记录

每个 bug 修完都登记到这里。**未来改这些代码区域时，必须回看本文件确保不引入回归。**
对应单元测试在 `skills/deep-analysis/scripts/tests/test_no_regressions.py` + `tests/test_v2_10_4_fixes.py` + `tests/test_v2_11_scoring_calibration.py` + `tests/test_v2_12_1_data_fixes.py` + `tests/test_v2_13_playwright_strategy.py` + `tests/test_v3_9_2_flow_bugfixes.py` + `tests/test_issue87_em_direct_and_comps.py` + `tests/test_issue90_us_financials_ttm.py`。

**登记规范**：每条必含 症状 / 位置 / 根因 / 影响 / 修法 / 验证 / 回归测试 / "未来改该区域注意事项"

---

## Unreleased (2026-07-18 · 数据完整性 hotfix · issue #87/#90)

### BUG · 东财直连字段/单位误读、Comps 自引用、美股财报只看年报

- **症状**：
  1. [#87](https://github.com/wbh604/UZI-Skill/issues/87) · A 股基础数据走 EastMoney push2 直连 fallback 时，把 `f47` 成交量错误当作 `change_pct` 兜底，导致涨跌幅异常；同时 `f116` 市值原始单位是元，下游按“亿”读会把 DCF/市场份额等派生指标放大 1e8 倍。
  2. [#87](https://github.com/wbh604/UZI-Skill/issues/87) · Comps 同行估值在只有目标公司自身样本时仍继续计算分位与估值结论，报告可能出现“自己和自己对标”的假结论。
  3. [#90](https://github.com/wbh604/UZI-Skill/issues/90) · 美股财务数据只读取 `yfinance.Ticker.financials` 年报，未合并更新的 quarterly financials，财报季后会继续展示旧年报口径，无法暴露 TTM 最新收入/净利。
- **位置**：
  - `lib/data_sources.py::_fetch_basic_a` / `_fetch_financials_impl`
  - `lib/stock_features.py::extract_features`
  - `lib/fin_models.py::build_comps_table`
  - `fetch_financials.py::_fetch_us`
- **根因**：
  - EastMoney push2 字段 scale 混杂：`f43/f60` 是 price * 100，`f170` 是涨跌幅 * 100，`f47` 是成交量，`f116/f117` 是元；旧代码没有集中解析契约。
  - Comps 模型只检查 `peers` 是否为空，没有剔除 `is_self` / 同 ticker / 同 name，也没有最低有效同行数 gate。
  - 美股路径把年报列直接作为最新历史序列，没有读取 `quarterly_financials` 做最近 4 季 TTM，也没有 `financial_basis` / `financial_period` 告诉报告当前口径。
- **影响**：
  - A 股 fallback 下涨跌幅、市值、市占率、DCF per-share 和估值结论可能严重失真。
  - 同行样本不足时仍给出估值判断，用户会把无样本报告误读成有效 peer comp。
  - 美股在最新季报后仍显示旧年报趋势，尤其对周期股/半导体/高波动成长股会低估或高估盈利拐点。
- **修法**：
  1. 新增 `_parse_em_direct_payload` 集中归一化 push2 字段：只用 `f170` 或 `price/prev_close` 计算 `change_pct`，`f47` 只保留为 `volume`，`f116/f117` 转成 `xx亿` 并保留 raw。
  2. `stock_features._market_cap_to_yi` 识别原始元单位并转成“亿”，市值、市占率走同一转换函数。
  3. `build_comps_table` 剔除目标公司自身样本；有效同行少于 2 家时直接返回“同行样本不足 · 无法对标”，不输出分位和隐含价。
  4. `_fetch_us` 读取 quarterly financials 最近 4 季 TTM；季度期末晚于年报时追加 `revenue_ttm` / `net_profit_ttm`，写 `financial_basis=TTM` 和 `financial_period`；最新口径超过 180 天时写 staleness warning。
  5. `_fetch_financials_impl` 美股原始源暴露 `quarterly_income`，让数据排查能看到季度输入。
- **验证**：
  - `tests/test_issue87_em_direct_and_comps.py` 覆盖东财字段不把成交量当涨跌幅、原始元市值转“亿”、self-only Comps 被拒绝。
  - `tests/test_issue90_us_financials_ttm.py` 覆盖 quarterly TTM 追加、旧财报时效 warning、raw data source 暴露 `quarterly_income`。
  - 全量 `pytest tests -q`：663 passed。
- **未来改该区域注意事项**：
  - 新增东财 push2 字段时必须先写字段 scale 注释和解析测试，不要在 fetcher 内临时 `(field or other_field) / 100`。
  - 下游模型的市值统一使用“亿”口径，raw 元只能作为溯源字段存在。
  - Comps 估值必须有真实 peer universe；少于 2 家同行只能展示数据缺口，不能生成估值结论。
  - 美股报告展示历史财务时必须同时写 `financial_basis` 和 `financial_period`，避免用户不知道当前是 annual 还是 TTM。

---

## v3.9.2 (2026-07-07 · 流程与数据契约 hotfix · issue #82/#83)

### BUG · OCF 缺失、industry=None、CLI report 后处理早退、agent_analysis 坏结构继续合并

- **症状**：
  1. [#82](https://github.com/wbh604/UZI-Skill/issues/82) · `fetch_financials` 只把经营现金流写成 `fcf`，没有显式 `ocf` / `ocf_history` / `ocf_to_net_income_ratio`，A 股 trap-detector 的现金利润匹配规则会读不到真实 OCF。
  2. [#83](https://github.com/wbh604/UZI-Skill/issues/83) · `basic.industry=None` 时 `fetch_peers` 整个 A 股分支跳过，返回空同行表且 `fallback=False`；`fetch_valuation` 也直接丢行业/市场 PE。
  3. fund/ETF/LOF 持仓汇总、`--versus`、`--portfolio` 生成 HTML 后直接 `sys.exit(0)`，绕过 `--output-dir` / `--remote` / 浏览器打开。
  4. `agent_analysis.json` schema error 只打印 `_agent_analysis_errors.json`，但仍传给 `generate_synthesis`，坏结构可能污染报告或触发 `.get()` 异常。
- **位置**：
  - `fetch_financials.py` 现金流段
  - `fetch_peers.py` A 股 `industry` 分支
  - `fetch_valuation.py` cninfo 行业 PE 段
  - `run.py` CLI 分支和 remote 后处理
  - `run_real_test.py::stage2`
  - `lib/pipeline/fetchers/registry.py`
- **根因**：
  - 数据契约漂移：legacy fetcher 输出 `financial_health` / `pe_quantile`，registry 却期待顶层 `debt_ratio/current_ratio` / `pe_ttm/pe_percentile`。
  - 控制流分散：多报告模式各自早退，没有共享 report post-process。
  - schema validator 只写错误清单，没有把 error 级问题转成 fallback。
- **影响**：
  - 现金流质量被默认值掩盖，可能把 OCF/净利大幅背离的股票误判为通过。
  - 行业缺失时报告同行/估值区块空白但不标 fallback。
  - SaaS/远程查看模式在 fund/versus/portfolio 下失效。
  - 非 Claude/Codex 生成的坏 `agent_analysis.json` 可能导致 stage2 崩溃或错用用户覆盖字段。
- **修法**：
  1. `fetch_financials._apply_operating_cash_flow` 显式输出 OCF 字段；`stock_features` 读取 `ocf_to_net_income_ratio`。
  2. `fetch_peers` 在 industry 缺失时 self-only fallback，并写 `fallback_reason`。
  3. `fetch_valuation` 在 industry 缺失/未匹配时用 cninfo 市场加权 PE 兜底，并写 `industry_pe_fallback_reason`。
  4. pipeline registry 对齐 legacy 输出字段。
  5. `run.py` 抽出 direct report path + shared post-process，fund summary / versus / portfolio 均复用 `--output-dir` / `--remote`；`cloudflared` 缺失时默认只提示，显式 `--install-cloudflared` 才自动安装。
  6. `run_real_test._validate_agent_analysis_or_fallback` 对 error 级 schema issue 直接丢弃 payload，回退脚本骨架。
- **验证**：新增 `tests/test_v3_9_2_flow_bugfixes.py`，覆盖 8 个回归。
- **未来改该区域注意事项**：
  - 新增/改名 fetcher 字段时，同步更新 `lib/pipeline/fetchers/registry.py`，并加行为测试，不只 grep 源码。
  - 所有“生成 HTML 的 CLI 模式”都必须返回 report path 并进入统一 post-process；不要再在 runner 分支里直接 `sys.exit(0)`。
  - `cloudflared` / `brew` / `sudo` 属于系统变更，默认只提示，必须显式 opt-in。

## v3.8.1 (2026-06-09 · 全面体检 · H/I 两组配套层 6 处补齐)

### BUG · 加评委没加配套 · 6 个静默降级缺陷

- **症状**：v3.6.3 (Serenity I 组) / v3.7.0 (13 位科技大佬 H 等组) 上线后 · 报告中
  ① 14 位新评委头像破图 ② 流派评分卡永远只显示 7 派（H/I 静默消失）③ H/I 组标签
  显示裸字母 ④ H/I 评委的 time_horizon/position_sizing 全 "—" ⑤ 风格动态加权对
  H/I 失效 ⑥ 新评委群聊台词全是 generic 套话
- **根因**：加评委只改了 investor_db + investor_criteria · 但仓库里有 **6 处按组
  遍历/查表的硬编码 A-G 配套层** · 全部用 `.get(g, default)` 优雅降级 → 不崩 ·
  所以 CI 全绿 · 视觉缺陷一直没暴露
- **修法**：
  1. `gen_pixel_avatars.py` 重跑 → 补 14 头像（脚本本身就支持增量 · 之前没人跑）
  2. `special_cards.render_school_scores` order A-G → A-I
  3. `panel_cards.GROUP_LABELS` + `special_cards` 内联副本 → 补 H/I
  4. `investor_profile.GROUP_DEFAULT` → 补 H/I 流派档案
  5. `stock_style.STYLE_GROUP_WEIGHTS` 8 风格 × 补 H/I 列
  6. `MARKET_SCOPE` 13 人显式登记 + `PERSONAS` 13 人 voice 台词
- **验证**：10 个体检回归测试 (`test_v3_8_1_audit_fixes.py`) · 632 passed
- **未来改该区域注意事项**（防再犯 · 关键）：
  - **加新评委的 checklist**（缺一不可）：investor_db → investor_criteria →
    `gen_pixel_avatars.py` 重跑 → MARKET_SCOPE → PERSONAS 台词 → 若新增组：
    GROUP_LABELS ×2 / render_school_scores order / GROUP_DEFAULT /
    STYLE_GROUP_WEIGHTS / SCHOOL_LABELS / run.py --school choices /
    _render_school_lock_banner THEMES / score_fns GROUP_META
  - 体检测试 `test_v3_8_1_audit_fixes.py` 已把以上大部分变成硬断言 ·
    新加组时这些测试会先红 · 跟着修就不会漏
  - 不要依赖 `.get(g, 1.0)` 这类优雅降级当"没问题"的证据 —— 它恰恰是
    本次 6 个缺陷能潜伏两个版本的原因

---

## v3.6.3 (2026-06-03 · 重磅角色 Serenity · AI 卡位/瓶颈猎手 I 组)

### BUG · Serenity 卡位关键词库漏掉 AR/消费光学，把光学股误判成「不在 AI 链」

- **症状**：实测 `python run.py 002273 --depth lite`（水晶光电，真实行业「光学光电子」· AR/VR + 车载光学 + iPhone 相机模组 · 市值 404 亿）· Serenity 给出 `bearish / 0`，headline「不在 AI 产业链上 —— 对我没有意义」。但水晶光电明显踩在 AR/AI 光学链上，应识别为「在链但卡位不够硬 → neutral」，而非一票否决。
- **位置**：`skills/deep-analysis/scripts/lib/stock_features.py` · 派生特征 `ai_chokepoint_score` 的 `_AI_CHOKEPOINT_KW` 关键词库
- **根因**：关键词库只覆盖了**数据中心光**（光模块/CPO/光通信/光器件/激光器）+ 先进封装 + 化合物半导体 + 互连 + 算力，**漏了 AR/消费/车载光学族**（光学/光电子/光学元件/光波导/滤光片/镀膜/棱镜/镜头/相机模组/AR-VR/近眼显示/车载光学）。`光学光电子` 行业整段命中 0 词 → `ai_chain_hit=False` → 直接腰斩到 score≈0。
- **影响**：所有 AR/VR/消费光学/车载光学方向的真实卡位候选（水晶光电、蓝特光学、舜宇、长光华芯等）会被 Serenity 误判为「不在 AI 链」而错杀，丧失「在链→再按不可替代性/市值判断」的分级能力。
- **修法**：`_AI_CHOKEPOINT_KW` 扩充一组 AR/光学终端侧词条。**注意刻意不加裸词 `ar`/`vr`/`mr`**（会在 lowercase 的 JSON blob 里匹配到 market/margin/warrant 等英文子串造成全局误命中）· 改用中文词（增强现实/虚拟现实/混合现实/头显/近眼显示）+ 带斜杠的 `ar/vr` + `ar眼镜`。
- **验证**：
  - 水晶光电真实数据重评 → `ai_chain_hit=True`（命中 光学/光电子/相机模组/ar-vr/车载光学）· 卡位分 70.1 · 但不可替代=False(切换5+规模6=11<12) + 市值404亿>300 → **neutral / 59**「命中 AI 链但可替代性偏高、市值偏大，不是真瓶颈」（地道的 Serenity 视角）
  - `test_serenity_rules.py` 7 项不破：白酒(高粱酿造)/银行(存款贷款) 仍 `ai_chain_hit=False` → bearish（无 AR/光学词误命中）
  - 全量 532 passed
- **未来改该区域注意事项**：
  - **永远不要在 `_AI_CHOKEPOINT_KW` 加 2 字母以内的裸英文词**（ar/vr/mr/ic/ai 单独）· blob 是 lowercase 拼接的中英文 + JSON · 短英文子串必然误命中。要加英文必须够长够特异（waveguide / micro-led / cowos）。
  - 扩词只影响 `ai_chain_hit` 这道**门槛**；是否 bullish 仍由 `chokepoint≥70 + 不可替代 + 中小市值` 三条 weight-5 规则把关。所以扩词宁可**宽进严出**，不会让普通光学股变成 Serenity 重仓。
  - 改词库后必跑 `test_serenity_rules.py::test_bearish_on_non_ai_regardless_of_moat`（确保非 AI 股仍被否）。

---

## v3.6.2 (2026-06-03 · cninfo 翻页长尾 #68 + install-hermes.sh pip 探测 #69)

### BUG #68 · cninfo 公告分页 854 页拖几小时

- **症状**：用户 [@xy2yp](https://github.com/wbh604/UZI-Skill/issues/68) 反馈 `python run.py --versus 000958 600406 --depth lite` 卡在 15_events 维度 · 进度条 `0/854 [01:53<6:11:58, 26.44s/it]` · 单股 4-6 小时
- **位置**：`skills/deep-analysis/scripts/fetch_events.py::_cninfo_disclosures`
- **根因**：调 `akshare.stock_zh_a_disclosure_report_cninfo` · 该 akshare 函数内部用循环翻完全部分页（cninfo 一只票常有 800+ 页公告）才把 DataFrame 返给我们 · 后续 `.head(30)` 截取已无用 · 翻页时间已经花掉了
- **修法**：
  1. 新增 `_cninfo_direct_api(code, page_size=30, timeout=15)` · 直接 `requests.post` 到 `http://www.cninfo.com.cn/new/hisAnnouncement/query` · `pageNum=1 + pageSize=30` · 一次 HTTP ≤15s · 永远不翻全部页
  2. 板块路由：`000/001/002/3xx → szse` · `6xx/688 → sse` · `8xx → bse`
  3. 响应解析：`announcements[*].announcementTime` (毫秒) / `announcementTitle` / `adjunctUrl` (拼 `http://static.cninfo.com.cn/` 前缀)
  4. `_cninfo_disclosures` 优先调直连 API · 直连失败时**默认不调 akshare**（防长尾）· 仅 `UZI_AK_CNINFO_FALLBACK=1` 显式启用时才走 akshare 慢路径
- **验证**：
  - mock `requests.post` ConnectionError → 返 [] · 不抛
  - mock 200 + 合法 JSON → 解析正确 / 路由正确
  - 网络失败 + 未设 fallback env → 不调 akshare（关键 · 防止再次踩坑）
- **未来改该区域注意事项**：
  - **永远不要回去用 `ak.stock_zh_a_disclosure_report_cninfo`** · 这是 akshare 实现的死结 · 它会翻完所有页
  - 直连 API 的 pageSize 上限 cninfo 文档说 30 · 别贪心设大数字（会被服务端拒）
  - cninfo 的时间戳是 **毫秒** · 不要忘了 `/ 1000` 再 `fromtimestamp`
  - 板块路由 prefix 列表要保持齐：未来 cninfo 加新板块要更新（如果 北交所 8xx 后还有新代码段）

### BUG #69 · install-hermes.sh 在 Linux 找不到 pip

- **症状**：用户 [@FrankHuy](https://github.com/wbh604/UZI-Skill/issues/69) 在 CentOS-like + Python 3.11 跑一键脚本：`line 95: pip: command not found` + akshare 装不上
- **位置**：`install-hermes.sh` line ~94（装依赖段）
- **根因**：很多 Linux 发行版（Debian/Ubuntu/CentOS/RHEL）**默认不提供 plain `pip` 命令** · 用户必须用 `pip3` / `python3 -m pip` · 我们脚本只试 `pip` 直接报错 · 后续 akshare 报"找不到 wheel"其实是因为底层 pip 不存在 · 不是真的 wheel 不兼容
- **修法**：
  1. 启动加 Python 版本预检（`python3` → `python` 探测 + 版本 ≥3.10 检查）· 警告而非阻断 · 给三种系统的安装命令
  2. pip 探测改为 5 层级联：`$HERMES/venv/bin/pip` → `$HERMES/.venv/bin/pip` → `pip` → `pip3` → `$PY_BIN -m pip`
  3. 全部探测失败 → exit 4 + 给 apt / yum / ensurepip / get-pip 四种安装路径
  4. `pip install` 失败 → exit 5 + 提示版本/镜像源/升级 pip
- **验证**：
  - `bash -n install-hermes.sh` 语法过
  - 测试 grep 验证脚本含 pip3 / -m pip / tuna.tsinghua / upgrade pip 等关键字
- **未来改该区域注意事项**：
  - 不要在主体代码里假设 `pip` 命令存在 · 用 `command -v` 探测
  - `set -euo pipefail` 严格模式下任何 fail 立即退出 · 必须 *先* 探测再 *用*
  - 镜像源 fallback 只在文案里建议 · 不要默认走清华源（部分海外用户访问慢）· 让用户主动加 `-i`

---

## v3.6.1 (2026-05-29 · Hermes Skills Guard 假阳性绕过 · issue #66)

### BUG · `hermes skills install` 报 DANGEROUS · `--force` 覆盖不了

- **症状**：用户 @zodiacg ([#66](https://github.com/wbh604/UZI-Skill/issues/66)) 反馈 `hermes skills install wbh604/UZI-Skill/skills/deep-analysis` 失败 · scanner 168 findings · DANGEROUS verdict
- **位置**：Hermes Skills Guard 模式匹配扫描器（NousResearch/hermes-agent · 上游 bug · 不是 UZI-Skill 的问题）
- **根因**：Skills Guard 是 v0.x 纯模式匹配 (`r'os\.environ\b'`) · 不区分"读自己配置"vs"窃取用户敏感 env" · 也不识别 docstring / HTML 注释 / opt-in 用户功能
- **影响**：community 源任何 finding 都会 BLOCK · `--force` 设计上不能覆盖 DANGEROUS · 用户 Hub 装不下来
- **修法**：提供 `install-hermes.sh` 一键脚本 · `git clone + ln -sfn` 到 `~/.hermes/skills/` · 跳过 Hub quarantine 扫描 · Hermes 跑时只看目录 layout · 完全等价
- **绝不能做的事**：
  - ❌ 用 dynamic import + 字符串拼接绕 Skills Guard 检测（这是上游 issue #7072 提到的恶意绕过 · 我们绝不走这条路 · 那是窃取信任模型）
  - ❌ 删除合法的 `os.environ.get` 代码来降 findings · 那是把功能砍了
  - ✅ 只提供 clone+symlink 路径 · 用户主动决定信任我们 · 而不是欺骗 Hub 让它判 "safe"
- **验证**：
  - `bash install-hermes.sh` 在干净环境跑通 · 4 个 skill symlink + venv pip 装包 + SKILL.md 版本验证
  - 11 个回归测试 (`test_v3_6_1_install_hermes.py`)
- **未来改该区域注意事项**：
  - 若 Hermes Skills Guard 升级到 allowlist 模型 · `hermes skills install` 重新可用时 · 在 INSTALL-HERMES.md 顶部加 "Skills Guard 已修 · 直接 hub 装" 提示
  - 但 install-hermes.sh 应该保留作为 dev 路径（clone + symlink 让 git pull 立刻生效 · Hub 装是 snapshot）
  - 改 skill 目录结构时 · 必须同步更新脚本里的 `SKILLS=(deep-analysis ...)` 数组
  - 永远不要"为了 Skills Guard 评分好看"砍合法功能 · `--remote` cloudflared 是用户主动 opt-in · 默认不跑

---

## v3.6.0 (2026-05-29 · 视觉升级 + 多股对比 + 组合分析)

### FEATURE A1 · 暗色模式 toggle

- **位置**：`assets/report-template.html`（CSS `:root` + `[data-theme="dark"]` 块 · 末尾 `<script>` toggle 段）
- **改动**：
  1. 新增 `[data-theme="dark"]` 块 · 30+ 个 CSS 变量重定义（slate-900 风 + 提亮 neon）
  2. topbar 加 `` · 默认 🌙 / 切换后 ☀️
  3. JS 初始化优先级：`localStorage.getItem('uzi-theme')` > `prefers-color-scheme: dark` > light
  4. 持久化：每次 toggle 都写 `localStorage` · 跨刷新生效
- **未来改该区域注意事项**：
  - 加新 CSS 变量时 · 必须同时在 `:root` 和 `[data-theme="dark"]` 都定义 · 否则暗色模式回退到 light 默认值
  - 不要硬编码 `#000000 / #ffffff` · 必须用 `var(--text-bright)` 等抽象

### FEATURE A2-3 · sticky TOC + count-up

- **位置**：`assets/report-template.html`（CSS `.toc-rail`/`.toc-item` · HTML `<nav class="toc-rail">` · 8 个 `id="section-X"` 锚点 · JS 末尾段）
- **改动**：
  1. 给 8 个 section-head div 加 `id="section-{core/clash/jury/chat/scan/modeling/risks/zones}"`
  2. 左侧 `.toc-rail` fixed 50% · `@media (max-width:1280px)` 隐藏
  3. JS 用 IntersectionObserver `rootMargin: '-30% 0px -50% 0px'` 命中段加 `.active` class
  4. count-up：找 `.score-giant, .sc-score-giant, .hero-score-num, .confidence` · 进入视口动画
- **未来改该区域注意事项**：
  - 增减章节时必须同步：HTML section id + TOC `<a href="#X">` + 顺序 · 不一致会出现 active 跳到错位置
  - count-up selector 用类名匹配 · 改样式时不要把 `.score-giant` 重命名 · 否则动画失效

### FEATURE A4 · 金融术语悬浮 tooltip · 🔒 XSS 安全加固

- **位置**：`assets/report-template.html`（JS 末尾 `TERMS` dict + `tooltipify` 函数）
- **改动**：
  1. `TERMS` dict 含 12 个核心术语（PE/PB/ROE/DCF/IRR/WACC/EV-EBITDA/LBO/YTD/TTM/PEG/LHB）
  2. 扫描 `.panel-insights / .dim-card .body / .friendly-layer / .risk-box / .punchline / .dim-row / .school-scores .desc / .round-bull / .round-bear / .qr-desc` 内 text node
  3. 用 `createDocumentFragment + createElement('span') + textContent + setAttribute('data-tip', ...)` 安全构造 · 不用 `innerHTML`
  4. 词条按长度降序匹配 · `\\b` 边界 + RegExp 防止 PE 误匹配 PEG
- **安全要点**：
  - **永远不要用 `node.innerHTML = ...` 处理动态术语 / 用户数据**（XSS 风险）
  - `textContent` 自动转义 · `setAttribute` 也安全 · 是首选
  - 测试 `test_tooltipify_uses_safe_dom_no_innerhtml` 守护这一点
- **未来改该区域注意事项**：
  - 加新术语只需扩 `TERMS` dict
  - 不要为了"省事"用 innerHTML 拼接 — 这是 security hook 检查项
  - 长术语在前（`EV/EBITDA` 在 `PE` 前）· 防止短词先匹配吞掉长词

### FEATURE A5 · 报告 QR 码

- **位置**：`assets/report-template.html`（CSS `.share-qr-card` · HTML 在 buy-zones 之后 share button 之前 · JS `drawQR` 函数）
- **改动**：
  1. `<canvas id="report-qr-canvas">` 200×200 · 用 Google Chart API (`api.qrserver.com`) 远程生成
  2. file:// 协议下不调 API · 用 `canvas.getContext('2d').fillText` 提示用 `--remote`
  3. 网络失败 fallback · 显示 "QR offline"
- **未来改该区域注意事项**：
  - QR 服务商可换 · 但接口签名 `data=` 通用 · 不要硬编码尺寸到 URL
  - 不要 innerHTML 注入 URL · 用 canvas 2D 绘图安全

### FEATURE B · 多股横向对比 `--versus`

- **位置**：`lib/versus_runner.py` (新文件 380 行) · `run.py` argparse + 早返回段
- **核心函数**：
  - `_load_cache(ticker)` → bundle{syn, raw, panel}
  - `_extract_metrics(bundle)` → 12 个核心字段 dict
  - `_winner(values, higher_is_better)` → winner index · 全空返 -1 · 跳过 0
  - `_render_comparison_grid(stocks)` → table HTML · ★ WIN 标注
  - `_render_html(stocks, depth)` → 完整 HTML（复用主模板 CSS）
  - `run_versus(tickers, depth, auto_open)` → dispatch
- **不破坏现有行为**：
  - `--versus` 早返回 · 不影响单股分析路径
  - 输入校验 2-4 只 · 其他长度 invalid_input
- **未来改该区域注意事项**：
  - 改 `ROWS` 加新对比维度时 · winner 高低需明确（`higher_is_better` 字段）· None=不比
  - 模板 CSS 路径用 `ASSETS_DIR` 常量 · `.parents[1]` 推算 scripts dir · 不要硬编码
  - `_render_html` 复用主模板 `<style>` 块 · 主模板改 CSS 时这里自动跟随
  - dark-toggle JS 简化版 (无 TOC/jargon) · 未来如需统一 · 抽 `assets/shared-theme.js`

### FEATURE C · 组合批量分析 `--portfolio CSV`

- **位置**：`lib/portfolio_runner.py` (新文件 370 行) · `run.py` argparse + 早返回段
- **核心函数**：
  - `_parse_csv(path)` → list[{ticker, weight, note}] · 容错 header / 中英文 / 0-100
  - `_normalize_weights(holdings)` → 归一化到 sum=1.0 · 缺失均分 / 部分缺剩余均分 / 总和 > 1 重归一
  - `_portfolio_health(metrics)` → {weighted_score, max_weight, n_industries, verdict}
  - `_render_html` → 排名表 + KPI grid + metadata.json
  - `run_portfolio(csv, depth, auto_open, portfolio_name)` → dispatch
- **CSV 容错**：
  - header 兼容：ticker / code / symbol / 股票 / 代码 · weight / 权重 / 仓位 / pct / 比例
  - weight 0-1 视为比例 · >1 自动除以 100
  - 无 header 时假设单列 ticker · weight 平均
- **未来改该区域注意事项**：
  - CSV header 加新别名时 · 必须加到 `tk_keys / wt_keys / note_keys` 列表
  - 健康度门槛是 hardcode（weighted_score>=70, max_weight<0.40, n_industries>=3）· 改阈值要同时改测试
  - metadata.json schema 是 SaaS 契约 · 加字段 OK · 改既有字段必须升 schema 版本
  - 失败容忍：单只 fail 进 `failed[]` 不阻断 · 但全失败返 insufficient_data

### Phase D 暂缓说明（v3.7 范围）

`--sector LED` 板块全扫 + `--as-of 2024-Q3` 历史复盘 都需要：
- fetcher 接受 `as_of_date` 参数 · 走历史接口（baostock query_history_k_data_plus 已支持但 akshare 大多不支持）
- 板块扫需 industry → tickers 映射表 · 跨数据源对齐

工作量大 + 涉及数据层重构 · 留到 v3.7 单独迭代。

---

## v3.5.0 (2026-05-29 · 单一流派视角锁定 `--school` + SaaS 集成 `--output-dir`)

### FEATURE · `--school A/B/C/D/E/F/G` 单一流派视角锁定（社群反馈）

- **背景**：用户反馈 "我只想看 F 派游资视角 · 不想 51 评委一起 vote"。51 评委 vote 出来的共识在某些场景下不是用户想要的视角（如纯游资打板 / 纯价值长线）
- **位置**：
  - `lib/investor_evaluator.py::evaluate` 入口段
  - `lib/pipeline/score_fns.py::synthesize` return 段
  - `lib/report/institutional.py::_render_school_lock_banner` 新函数
  - `run.py::main` argparse 段
  - `SKILL.md` HARD-GATE-SCHOOL-LOCK
- **改动**：
  1. `run.py` argparse 新增 `--school` choices=[A,B,C,D,E,F,G] · 触发后 set `os.environ["UZI_SCHOOL"]`
  2. `investor_evaluator.get_locked_school()` 读 env + 大小写归一 + 合法性校验
  3. `evaluate()` 顶部检查 · `inv_group != locked` → `_skip_result("用户锁定 X 派视角")` 不进规则引擎/persona
  4. `score_fns.synthesize` 把 `school_lock={group, label}` 编码进 synthesis · 让报告层无需读 env
  5. `_render_school_lock_banner(syn)` · 7 派各自配色 + 代表评委提示 · 渲染在 data_gap_banner 上方
  6. `SKILL.md` 加 HARD-GATE · agent role-play 时只 role-play 该派 5-8 人 · `panel_insights` 不写跨派对比 · `great_divide_override.bull_say_rounds/bear_say_rounds` 限于该派内
- **兼容 v3.4.5**：锁定 F 派 + 京东方 (2000 亿) · F 派 23 人按 LHB 反查机制 · 实际上榜的赵老哥/孙哥仍参与评分（不会因射程超限而二次 skip）
- **未传 `--school` 时行为 100% 兼容**：`get_locked_school()` 返 "" · 全 51 评委正常 vote · banner 不渲染（_render_school_lock_banner 返 ""）
- **验证**：
  - `UZI_SCHOOL=F python3 -c "from lib.investor_evaluator import evaluate; print(evaluate('buffett', {...}))"` → signal=skip · reason 含"锁定 A 股游资 派视角"
  - `_render_school_lock_banner({"school_lock":{"group":"F","label":"A 股游资"}})` → 包含 "SCHOOL LOCK" + "赵老哥" + 深红配色
  - 11 个回归测试 (`test_v3_5_0_school_lock.py`) 全过
- **未来改该区域注意事项**：
  - `UZI_SCHOOL` env 大小写归一在 `get_locked_school()` 内完成 · 不要在其他位置再做 upper() 否则双重转换会失效
  - 非 A-G 的 group 字符串（实验派 / 未分类）也会被 skip · 不要假设所有评委都有 group · 必要时检查 `_INVESTOR_GROUP_MAP.get(id, "")`
  - `_render_school_lock_banner` 的 THEMES dict 必须包含所有 7 派 · 加新派时记得同步 SCHOOL_LABELS + THEMES + 测试
  - synthesis.school_lock 是 dict 或 None · 不要写 ""（空字符串）· 测试 `if syn.get("school_lock")` 会过 None 但不会过 dict（即使 group=""）
  - agent role-play 时 · 即使 panel.json 里非该派评委有 skip 状态 · 也不要 override 他们的 signal · 用户预期就是只看该派

### FEATURE · `--output-dir` CLI flag · 供 uzi-platform 集成
- **背景**：把 UZI-Skill 包装成 SaaS（`uzi-platform/`），后端 Celery worker 需要稳定产物路径挂载到 web 报告页
- **位置**：`run.py::main` argparse 段 + 报告找到后的拷贝段（≈ line 530）
- **改动**：
  1. argparse 新增 `--output-dir DIR`
  2. 报告路径确定后，把整目录（含 avatars / share-card.png / war-report.png / one-liner.txt）拷到该目录
  3. `full-report-standalone.html` 额外复制一份为 `index.html`，便于平台直接服务
  4. 生成 `report.meta.json`（schema=1 · ticker / depth / generated_at / one_liner / size_kb），供后端落 DB
  5. 拷贝失败不影响本地报告生成（silent fallback + warning）
- **不破坏现有行为**：未传 `--output-dir` 时与改动前完全一致；--remote / --no-browser / browser open 路径全部保留
- **验证**：
  - 不传 `--output-dir`：本地 `reports/{ticker}_{date}/` 仍正常生成（既有测试覆盖）
  - 传 `--output-dir /tmp/x`：`/tmp/x/index.html` + `/tmp/x/report.meta.json` 存在且 size > 0
- **未来改该区域注意事项**：
  - `report.meta.json` 的 schema 字段是 SaaS 后端契约 · 加字段 OK · **改/删既有字段必须升 schema 版本号**
  - 拷贝用 `shutil.copytree` 已存在目录会先删 · 不要把用户的 `--output-dir` 设为非空业务目录
  - `index.html` 永远是 `full-report-standalone.html` 的副本（self-contained · 不依赖 avatars/） · 不要改为 `full-report.html`（会拉 share-card.png CDN）

---

## v3.4.5 (2026-05-12 · F 派 LHB 反查 + low-confidence banner)

### BUG 1 · F 派游资遇大盘股 100% skip · 与 LHB 实际数据脱节
- **症状**：用户跑京东方 000725（约 2000 亿市值）· 51 评委里 F 派 23 人全 skip · 但 LHB 实际显示 3-5 个游资参与涨停博弈
- **位置**：`lib/investor_evaluator.py::_is_youzi_out_of_range`
- **根因**：v2.13.3 加的市值射程检查没考虑 LHB 实际数据 · 只看 SEATS[nickname] 的 fit_rules max_mcap · 超出就硬 skip
- **修法**：检测 `features["matched_youzi"]`（fetch_lhb.match_seats_in_lhb 反查的 30 天上榜游资昵称 list）· 若该游资在里面 · 即使市值超射程也不 skip
- **回归测试**：4 个测试覆盖 (无 LHB skip / 有 LHB active / 射程内 / 非 F 派)
- **未来注意**：
  - `matched_youzi` 是 list[str]（nickname）· 不是 list[dict]
  - 仅对 F 派（_INVESTOR_GROUP_MAP）生效 · A/B/C/D/E/G 派不受影响
  - 加新游资到 SEATS 时 · 用 list 兼容（不是 set）· 否则 nickname in matched 会失败

### BUG 2 · fund_score 偏低但缺数据时无警告 · 用户误判可信度
- **症状**：京东方实测 fund_score=37.6 · 但 agent 重评 65/100 · 报告无任何"评分不可信"提示 · 用户读到 0/24 票数会误以为应清仓
- **位置**：`lib/report/institutional.py::_render_data_gap_banner`
- **根因**：score_dimensions 对缺数据维度给默认中性 5-6 分 · 多维缺失时会人为拉低 fund_score · 但 banner 没区分"评分可信"vs"评分受缺数据干扰"
- **修法**：`_render_data_gap_banner(data_gaps, raw, syn)` 新增 syn 参数 · 检测 stock + fund_score<50 + cov<60% → 渲染 low-confidence 红色 banner · 引导用户看 agent 重评
- **未来注意**：
  - 阈值 fund_score<50 + cov<60% 是经验值 · 京东方/类似缺数据股的实测 · 调整需更新测试
  - low-confidence banner 只对 stock 触发 · ETF/LOF 走 fund-type banner（v3.4.4）
  - 不传 syn 时向后兼容 · 走老 banner
  - 用红色调 #7f1d1d/#b91c1c · 区别于橙色（普通缺数据警告）+ 蓝色（基金类型）

---

## v3.4.4 (2026-05-12 · data_gap_banner UX 优化)

### UX BUG 1 · ETF 17% 覆盖率让用户误判可信度
- **症状**：用户反馈"ETF 报告数据覆盖率 17% · 会不会影响可信度"
- **位置**：`lib/report/institutional.py::_render_data_gap_banner`
- **根因**：原 banner 不区分 stock vs ETF/基金 · 对基金来说"缺 ROE/PE"是字段本身没有 · 不是采集失败 · 但 banner 用同样的"⚠️ DATA QUALITY 警告"措辞让用户误以为不可信
- **修法**：
  1. `_render_data_gap_banner(data_gaps, raw=None)` 新增 raw 参数
  2. 检测 `basic.security_type` 或 `raw.security_type` · 或 ticker 反推 (调 `classify_security_type`)
  3. ETF/LOF/mutual_fund → 渲染 `data-gap-banner fund-type` 蓝色调 banner · 文案"FUND-TYPE NOTE · 字段差异属预期·不影响可信度·去看前 10 大持仓股"
  4. 普通 stock 走老 banner · 向后兼容
- **未来注意**：
  - 新增 sec_type 时 · 更新 `is_fund_like` 检测逻辑
  - raw 传入是可选参数 · 不传时走老 banner（不要 break 现有 caller）
  - ticker 反推依赖 `lib.market_router.classify_security_type` · 改前缀规则要回归测试

### UX BUG 2 · banner 橙底橙字对比度不足
- **症状**：banner subtitle `` 用 `#f59e0b` 浅橙 · 在 12% 橙色背景上几乎看不清；chip 用 `#fbbf24` 亮橙更糟
- **位置**：`assets/report-template.html` `.data-gap-banner` 系列 CSS
- **修法**：
  - title `#f59e0b` → `#92400e` 深棕
  - subtitle strong `#f59e0b` → `#7c2d12` 深棕红 + font-weight 800
  - chip text `#fbbf24` → `#7c2d12` + font-weight 600
  - subtitle 正文 `var(--text-bright)` → `#1f2937` 深灰（避免主题色干扰）
  - 左边条 `#f59e0b` → `#b45309` 深棕色（更醒目）
  - 新加 `.data-gap-banner.fund-type` 蓝色调 CSS (#0369a1 + #0c4a6e)
- **回归测试**：`tests/test_v3_4_4_banner_ux.py` 11 个测试 · 含 CSS 颜色字符串断言（守护回归）
- **未来注意**：
  - 任何 banner 文字颜色都必须在浅橙背景上 WCAG AA 通过 · 不要用 `#fXXXXX` 系列浅橙做文字
  - 信息性提示用蓝色调 · 警告/错误才用橙红色 · 维持视觉语义一致

---

## v3.4.3 (2026-05-12 · 开放式基金分类修复 + 字段级 fallback gate)

### BUG #60-followup · 开放式基金被误判为 convertible_bond
- **症状**：用户输 110011（易方达优质混合）→ classify_security_type 返 `convertible_bond` → early-exit · v3.4.0 加的 fund_holdings_runner 无机会触发
- **位置**：`lib/market_router.py::classify_security_type`
- **根因**：仅按前缀规则分类 · 110xxx 既是 SH 老转债前缀 也是开放式基金代码 · 同样 005xxx 不是股票前缀但是基金
- **修法**：
  1. 新增 `SecurityType` literal `mutual_fund`
  2. 在判 `convertible_bond` 之前 · 用 `akshare.fund_name_em()` 二次校验（懒加载 + 全表 set 缓存）· 基金代码优先识别为 mutual_fund
  3. 对不在 stock 前缀的码段（如 005xxx）· 也查一下基金清单
  4. run.py + preflight_helpers 把 mutual_fund 路由到 fund_holdings_runner
- **验证**：110011 → mutual_fund ✅ · 113008 真转债 → cb ✅（不误伤）
- **回归测试**：`tests/test_v3_4_3_mutual_fund_classification.py` (6 tests)
- **未来改该区域注意事项**：
  - `_is_mutual_fund_code` 是懒加载 module-level 缓存 · 单进程内只下载一次 · 不要在 hot path 调
  - `fund_name_em` 失败时 silent fallback 到老前缀规则 · 不抛异常（保证向后兼容）
  - 新增 sec_type 时 · run.py 两处分支 + preflight + fetch_basic 四处都要加路由
  - `akshare.fund_portfolio_hold_em` 对 ETF/LOF/mutual_fund 都 work · 不需为 mutual_fund 单独写持仓拉取

### REFACTOR (PR #63) · A 股 basic 字段级 fallback gate
- **症状**：xueqiu 拿到 price/PE/PB 但 name 空 → early return 跳过后续 fallback · 报告 name 永远显示为空
- **位置**：`lib/data_sources.py` · 新增 `_ensure_a_share_basic_fields` gate
- **根因**：之前 fallback 是 source-level（一个源 ok 就 early return）· 没做字段级 patch
- **修法**：新增 `_merge_missing_basic_fields` · 仅填空不覆盖 · 4 个早 return 点全部走 `_ensure_a_share_basic_fields` 字段级 gate · 备用源链 tencent_qt → baostock → ak_code_name → known_industry
- **验证**：茅台 600519 拿到 name=贵州茅台 / price / pe_ttm / pb / industry=白酒Ⅱ / listed_date 全字段
- **未来改该区域注意事项**：
  - `_merge_missing_basic_fields` 默认只填指定字段集 · 加新字段需更新 `fields` 元组
  - `_fallback_snap` 标记会去重 · 别在 hot loop 里反复 append 同 marker
  - `_ensure_a_share_basic_fields` 调用应在所有主 fallback 之后 · 否则被覆盖

---

## v3.4.2 (2026-05-11 · Windows + Clash Schannel TLS 兼容 · baostock 双 fallback)

### BUG · Windows Python + Schannel TLS 与东财不兼容 · PE/PB/ROE 全空
- **症状**：Windows + Clash 用户跑分析 · 所有 eastmoney 链路（xueqiu / push2 / baidu / tencent_qt）全挂 · 报告里 PE/PB/ROE 全 `—`
- **位置**：`lib/data_sources.fetch_basic` 链 + `fetch_financials.py::_fetch_a_share`
- **根因**：Windows Python 默认用 Schannel（系统 TLS）· 东财接口与 Schannel 兼容性差. Clash 国内规则 DIRECT → 仍走 Schannel · 代理救不了
- **不能修的事**：Schannel 本身没法修（需要换 Python TLS backend 或改 Clash 规则让 eastmoney 走代理）
- **能修的事**：增加 baostock fallback · 它走自有协议（非 HTTPS）· 完全绕过 SSL 兼容性
- **修法**（2 处 fallback）：
  1. `fetch_basic` · 在 tencent_qt 之后追加 baostock · `query_history_k_data_plus` 拿 peTTM/pbMRQ/close · `query_stock_basic` 拿 code_name/ipoDate
  2. `fetch_financials._fetch_a_share` · 当 ROE/revenue_history/net_margin 都空时触发 baostock · 拉 5 年季报 + 解析 roeAvg/MBRevenue/npMargin/gpMargin
- **验证**：baostock 茅台 sh.600519 实测拿到 peTTM=20.4 / pbMRQ=7.15 / ROE=19.25% / 净利率 52.6% / 营收 893.5 亿
- **回归测试**：`tests/test_v3_4_2_baostock_fallback.py` (6 tests · 含真机烟雾测试)
- **未来改该区域注意事项**：
  - **不要在 baostock fallback 段直接覆盖正常数据**：fetch_financials 检查 `needs_fallback = not roe and not revenue_history and not net_margin` · 仅当核心字段全空才触发 · 否则会让正常拿到数据的票被 baostock 覆盖（baostock 季报较慢 · 可能不是最新）
  - baostock `query_profit_data` 返回 ROE/Margin 都是**小数**（0.19 = 19%）· 渲染前必须 ×100
  - 不要把 baostock 当主源 · 它季度数据更新比 akshare 慢 1-3 天 · 仅当主源失败时启用
  - baostock 服务端要求 ≥0.9.1（v3.4.0 已锁版本）

---

## v3.4.1 (2026-05-11 · verdict 粒度细化 · 相近股票可区分)

### BUG · 50-65 verdict 段过宽 · 神剑(58) + 博云(59.9) 都判 "观望优先" 看不出差异
- **症状**：用户反馈"神剑股份、博云新材 这两支其实买入逻辑也不一样，但评分一致"
- **位置**：`lib/pipeline/score_fns.py::generate_synthesis` line 980+ verdict ladder
- **根因**：v2.11 verdict 5 档 (80/65/50/35) · 50-65 "观望优先" 跨度 15 分 · 神剑 58 + 博云 59.9 都在里面 · 用户感知一致
- **真实差异**：流派分上有差距（成长派 +13 · 中式价投 +15）· overall 也差 1.9 · 但被 verdict 段掩盖
- **修法**（3 层细化 · 不动核心 fund_score 公式以避免破坏白马评分）：
  1. verdict 7 档：80/70/65/60/55/50/35 (50-65 拆三档 · 65-70 加偏弱)
  2. verdict label 追加 "X 派看多 / Y 派看空"
  3. synthesis 加 verdict_detail = "基本面 X · 共识 Y" · assemble_report 渲染时追加显示
- **回归测试**：`tests/test_v3_4_1_verdict_granularity.py` (5 tests) + 更新 `test_v2_11_scoring_calibration` 阈值
- **未来改该区域注意事项**：
  - 不要直接改 score_dimensions 来放大差异 · 它是 v2.11 校准过的 · 改会破坏白马评分
  - 真正的根因是 score_dimensions 给缺数据维度默认 5-7 (中性) · 抹平了 ROE/营收差异 · 未来 v3.5+ 可考虑 "active-weighted"
  - verdict 阈值增加时 · 同步更新 `test_v2_11_scoring_calibration::_verdict_for` ladder

---

## v3.4.0 (2026-05-10 · 基金/ETF 持仓循环分析 + baostock ≥0.9.1)

### FEATURE · ETF/LOF 持仓循环分析（v2.10.4 early-exit 改为 opt-in 批量）
- **背景**：v2.9.2 引入 ETF/LOF 早退（避免对非个股标的跑 51 评委）· 但用户期望"分析整只 ETF" · 之前手动跑 10 次 stock-analyze 太麻烦
- **位置**：`lib/fund_holdings_runner.py`（新）+ `run.py`（两处分支接入 runner）
- **用户体验**：检测到 ETF/LOF → 列持仓 + 估算耗时 → 二次确认（y / 数字 / N） → 循环跑 stock-analyze + 生成 summary HTML
- **安全设计**：
  - 默认取消（除非用户输入 y）
  - 数字输入只跑前 K 只
  - 单只崩不中断（partial failure 容忍）
  - 非交互环境必须 `UZI_FUND_AUTO_YES=1` 显式确认
  - 可转债 / 指数仍 early-exit · 只对 ETF/LOF 启用
- **回归测试**：`tests/test_v3_4_0_fund_holdings.py` (7 tests · runtime 估算 / 取消 / auto_yes / partial failure / summary HTML 链接)
- **未来改该区域注意事项**：
  - **不要把 ETF/LOF pipeline 强行塞进主 22 维 stock pipeline** · 它们没有 ROE/护城河字段 · 这次设计意在循环复用 stock pipeline 而不是新建 fund pipeline
  - 默认取消逻辑必须保留 · 否则 agent 误传 ETF 会循环 10 次浪费 token
  - `UZI_FUND_AUTO_YES=1` 应用于 CI / agent 编排场景 · 不要默认开启
  - top_holdings 必须有 rank/code/name 三个字段 · weight_pct 可选

### CONFIG · baostock 锁版本 ≥0.9.1
- **背景**：社群通知 2026-04-22 起 baostock 服务端要求 ≥0.9.1
- **位置**：`requirements.txt`
- **修法**：`baostock>=0.8.9` → `baostock>=0.9.1`
- **验证**：本地 0.9.1 实测 login + 茅台 K 线 query 全过
- **未来改该区域注意事项**：baostock 服务端版本要求未来可能继续上调 · 看到 login() 大面积失败时优先升 baostock

---

## v3.3.4 (2026-05-10 · mini_racer V8 crash escape hatch · issue #61)

### BUG #61 · macOS Py 3.12/3.13 下 mini_racer V8 SIGTRAP（@dragonforai）
- **症状**：`python run.py SEHK.03690 --depth deep` → `[FATAL:address_pool_manager.cc(67)] Check failed: !pool->IsInitialized()` · Python 进程被 SIGTRAP 杀掉
- **位置**：`run_real_test.run_fetcher` (legacy 路径) + `lib/pipeline/collect.py::_run` (pipeline 路径)
- **根因**：
  - `mini_racer` 是 V8 isolate 的 ctypes 封装 · 非进程内 thread-safe
  - v2.6 加 `_MINI_RACER_LOCK` 串行化 · 但 macOS Python 3.12+ 下 libffi cross-thread ctypes call 时序仍可能让 V8 isolate pool 被多次初始化
  - SIGTRAP 是进程级 signal · Python `try/except` 抓不到 · 整个 process 被 kill
- **影响**：HK 港股 + deep 模式特别容易触发（因为 deep 启用更多 fetcher · 增加 mini_racer 调用频次）· 用户无法生成报告
- **修法**（多重 layer）：
  1. **显式 escape hatch**：`UZI_DISABLE_MINI_RACER=1` env var · 跳过 3 个 fetcher graceful 降级
  2. **自动恢复**（核心创新）：sentinel 文件机制
     - 调 mini_racer fetcher 前写 `~/.uzi-skill/_minirackercrash.sentinel`
     - 成功后删
     - 进程崩则 sentinel 留下 · 下次启动自动 disable
  3. **强制启用**：`UZI_FORCE_MINI_RACER=1` 覆盖 sentinel（debug 用）
  4. legacy + pipeline 两条路径都做了同样保护
- **验证**：UZI_DISABLE_MINI_RACER=1 e2e 跑通 · 614 KB HTML 仍生成
- **回归测试**：`tests/test_v3_3_4_minirackerguard.py` (7 tests)
- **未来改该区域注意事项**：
  - 不要试图用 Python `try/except` 抓 V8 SIGTRAP · 抓不到（进程级 signal）
  - sentinel 文件路径在 `~/.uzi-skill/_minirackercrash.sentinel` · 不要改路径
  - 调 mini_racer fetcher 时 **必须先 arm sentinel** · 成功后 disarm · 否则 auto-recovery 失效
  - 普通 Python 异常时也要 disarm sentinel · 否则误判 V8 crash 让用户每次都跑 fallback
  - **加新的 mini_racer 触发函数时**（akshare 升级可能引入更多 V8 调用）· 务必加进 `_MINI_RACER_FETCHERS` 集合
  - 长期方案：考虑用 subprocess 隔离 mini_racer call · 或换 cninfo HTTP API 不依赖 mini_racer

---

## v3.3.3 (2026-05-06 · 社区 PR · 4 项 hotfix)

### BUG #52 · LHB akshare 1.18+ "近一月" 字符串失效（@qdby26）
- **症状**：所有股票 `lhb_count_30d=0` / `matched_youzi=[]` / `inst_vs_youzi` 全 0 · 龙虎榜模块永远空
- **位置**：`lib/data_sources.py::_fetch_lhb_impl`
- **根因**：akshare 1.18+ 改了 API · `stock_lhb_stock_detail_em(symbol, date="近一月")` 返 `None` → `TypeError` → `except: return []` 静默吞掉
- **修法**：用 `stock_lhb_stock_detail_date_em` 拿历史日期 + 按 days 过滤 + 逐日调 YYYYMMDD 格式 + 列名归一化 `交易营业部名称 → 营业部名称`
- **验证**：6 mock 回归测试全过
- **未来改该区域注意事项**：
  - akshare 任何字符串简写参数（"近 X 月" / "今年" / "全部"）都不可信 · 优先用 YYYYMMDD/YYYY-MM-DD 数值格式
  - `except Exception: return []` 这种静默吞异常的写法是 anti-pattern · 必须至少 print warning

### BUG #54 · institutional.py 缺 svg_radar import（@DragonQuix）
- **症状**：报告里 BCG/Porter 5 forces 块缺失（_render_competitive_analysis 静默返空）
- **位置**：`lib/report/institutional.py:393`
- **根因**：v3.2 拆分时只 import 了 `svg_gauge / svg_progress_row` · 漏了 `svg_radar`（v3.3.2 已修过 svg_sparkline · 但忘了 svg_radar）
- **修法**：import 块加 `svg_radar`
- **验证**：`test_institutional_imports_svg_radar` + `test_render_competitive_analysis_does_not_raise_nameerror`
- **未来改该区域注意事项**：
  - **任何 lib/report/* 子模块用的 svg_* 函数必须 import** · v3.2 拆分时漏了 svg_sparkline (修过 #50) 又漏了 svg_radar · 已加回归测试守护 · 若再加 svg_xxx 也要更新 import
  - 推荐：CI 加 `python -c "from lib.report.institutional import *; ar.assemble('TEST.SH')"` 烟雾测试

### BUG #59 · Python 3.11 嵌套 f-string 反斜杠 SyntaxError（@Charlson852）
- **症状**：Python 3.11 import `lib.report.special_cards` 直接 `SyntaxError: f-string expression part cannot include a backslash`
- **位置**：`lib/report/special_cards.py::render_school_scores` · 第 500 行
- **根因**：Python 3.11 不允许 f-string 表达式部分有反斜杠 · `f"{f'...\\\"...\\\"...' if skip else ''}"` 这种嵌套 f-string + 反斜杠 attr 引号会触发 SyntaxError（Python 3.12+ 才允许）
- **影响**：所有 Python 3.11 用户（Debian 13 默认）完全无法 import · stage2 全崩
- **修法**：把内嵌 f-string 提取为独立变量 `skip_display` · 主 f-string 只插入变量 (无反斜杠)
- **PR #59 原版 bug 警告**：作者修这个的同时把 `items.append(...)` 从 for-loop **内**（8 缩进）错移到 for-loop **外**（4 缩进）· 会导致 7 流派只渲染最后一个 · 我们 cherry-pick 仅修复部分 · 保持原缩进
- **验证**：`test_school_scores_uses_skip_display_variable` + `test_render_school_scores_renders_all_seven_groups`
- **未来改该区域注意事项**：
  - **永远不要在 f-string 表达式部分用反斜杠**（即使 Python 3.12+ 允许 · 也会让 3.11 崩）· 把 HTML 等需要引号的部分提取为独立变量
  - 重写 render_school_scores 时 · `items.append` **必须**在 for-loop 内 · 否则只渲染最后一个流派 · `test_render_school_scores_renders_all_seven_groups` 守护这条
  - 缩进改动看似无害但语义巨变 · review PR 时关注控制流缩进

---

## v3.3.2 (2026-04-28 · GitHub issue #50 + #51 hotfix)

### BUG #50 · institutional.py 漏 import svg_sparkline · NameError 卡死 stage2
- **症状**：用户报"Stage 2 总是超时"· 实际是 `NameError: name 'svg_sparkline' is not defined`
- **位置**：`lib/report/institutional.py:211-212` · `_render_lbo_block` 函数内
- **根因**：v3.2 拆分时 institutional.py 的 import 块只列 `svg_gauge / svg_progress_row` · 漏了 svg_sparkline · 但 _render_lbo_block 实际调用了它
- **触发条件**：dim20.lbo.ebitda_path 或 debt_schedule 非空时（绝大多数股票都会触发）
- **影响**：stage2 整个崩 · HTML 不出 · 用户感知"卡住超时"
- **修法**：import 块加 `svg_sparkline`
- **验证**：手动构造 dim20 跑 _render_lbo_block · 应生成 2 个 SVG sparkline
- **回归测试**：`tests/test_v3_3_2_issue_fixes.py::test_institutional_imports_svg_sparkline` + `test_render_lbo_block_does_not_raise_nameerror`
- **未来改该区域注意事项**：
  - 任何在 lib/report/* 子模块用的 SVG 函数必须在文件顶部 import block 显式列出
  - v3.2 拆分时容易漏 import · 抽块前先 grep 该块用了哪些函数 · 全部加进 import 列表
  - 推荐：每个新 lib/report/*.py 文件都在 CI 跑 `python -c "import lib.report.X"` 烟雾测试

### BUG #51 · XueQiu cubes_search.json endpoint 已下线
- **症状**：用户报"XueQiu 登录成功但验证失败" · cookie 已保存但 endpoint 仍 400
- **位置**：`lib/xueqiu_browser.py:32` (LOGIN_TEST_URL) · `lib/xueqiu_browser.py::fetch_cubes_via_browser` · `fetch_contests.py::fetch_xueqiu_cubes`
- **根因**：XueQiu 把 `/cubes/cubes_search.json` endpoint 完全下线 · 未保留兼容
- **社区修法**：@Kylin824 提供新 URL `/query/v1/search/cube/stock.json?q={xq_symbol}&count={limit}&page=1`
- **修法**：3 处同步换新 endpoint
- **验证**：grep 项目无 `cubes/cubes_search.json` API 调用残留
- **回归测试**：`test_xueqiu_login_url_uses_new_endpoint` + `test_xueqiu_browser_fetch_uses_new_endpoint` + `test_fetch_contests_uses_new_endpoint`
- **未来改该区域注意事项**：
  - XueQiu API 不稳定 · 历史上多次改 endpoint · 任何报登录验证失败时优先怀疑 endpoint 失效
  - 改 LOGIN_TEST_URL 必须同步 fetch_cubes_via_browser 和 fetch_contests · 三处保持一致
  - `query/v1/search/cube/stock.json` 参数是 `q=` 不是 `code=` · category 参数也已废弃

---

## v3.2.0 (2026-04-23 · assemble_report.py 拆分 80%)

### REFACTOR · assemble_report.py 从 2964 → 587 行
- **症状**：v2.15.x 多个 hotfix（fund / moat / school_scores）都落在 assemble_report.py · 68 函数单文件耦合严重
- **位置**：`assemble_report.py` · 新增 `lib/report/` 5 个子模块
- **根因**：所有 render 相关代码堆一个文件 · 改 fund_managers 的人可能不小心碰到 dim_viz 的 SVG · 2964 行导致定位困难
- **修法**（4 次 commit 物理拆分）：
  1. `svg_primitives.py` (602 行) · 19 个 svg_* + 9 个 COLOR_* · 纯渲染无业务
  2. `dim_viz.py` (742 行) · 19 个 _viz_xxx + DIM_VIZ_RENDERERS dispatch
  3. `institutional.py` (532 行) · DCF/LBO/IC/catalyst/competitive/style_chip
  4. `panel_cards.py` (183) + `special_cards.py` (544 行) · 51 评委相关 + 特殊卡
- **验证**：332 tests 全过 · 002217 assemble() 0.0s 出 608KB HTML · 格式 byte-level 一致
- **回归测试**：4 处 grep 式 test 扩展为拼接 assemble_report + 对应子模块源码
- **未来改该区域注意事项**：
  - 新增 render 函数要选对目标文件：`svg_xxx` → svg_primitives · `_viz_xxx` → dim_viz · `render_dim_card` 核心 → assemble_report · panel 相关 → panel_cards · 深度卡片 → special_cards · 机构建模 → institutional
  - assemble_report.py 对所有抽离函数做 re-export · 改 API 时保持 re-export 完整否则外部调用崩
  - 循环依赖：每个子模块都重新定义 `_safe` 避免 import assemble_report（防止循环）
  - grep 式测试脆弱 · 再拆模块时记得扩展 src 拼接列表
  - `DIM_META` / `CAT_GROUPS` 仍在 assemble_report · 因为 render_dim_card 依赖它们

---

## v3.1.0 (2026-04-23 · rrt 瘦身 65% · 纯函数 + preflight 抽离)

### REFACTOR · run_real_test.py 从 2105 → 735 行
- **症状**：v2.x 连续 5 个 hotfix 都落在 rrt 屎山里 · 函数互相耦合 · 改动困难
- **位置**：`run_real_test.py` · `lib/pipeline/score_fns.py`（新）· `lib/pipeline/preflight_helpers.py`（新）
- **根因**：rrt 同时承担 collect + scoring 纯函数 + stage1/stage2 CLI 编排 · 职责混杂 · 2105 行单文件
- **影响**：改任何一处需要扫全文件找依赖 · 测试 grep 式脆弱 · v3.0 发完仍有这个债
- **修法**（2 步物理搬迁）：
  1. 搬 1228 行纯函数到 `score_fns.py` · rrt 做 re-export 保持兼容
  2. 搬 166 行 preflight/resolve/ETF 到 `preflight_helpers.py` · stage1 改调 `prepare_target()`
- **验证**：332 tests 全过 · 002217 resume e2e 10s 出报告 · 格式 100% 兼容
- **回归测试**：18 处 grep 式测试批量 patch 读 rrt + score_fns + preflight_helpers 三文件拼接
- **未来改该区域注意事项**：
  - 纯函数的 canonical 位置现在是 `score_fns.py` · rrt 只做 re-export · 新函数添加走 score_fns
  - preflight 任何新增检查都加到 `prepare_target()` · 不要回到 stage1 里堆逻辑
  - `collect_raw_data` 仍在 rrt（283 行）· legacy stage1 还用它 · 新 collector 是 `pipeline/collect.py`
  - 如果 stage1 签名变化（加/减参数）· 需同步 `preflight_helpers.prepare_target` 参数
  - grep 式测试（`(X / "run_real_test.py").read_text()`）脆弱 · 如果再搬函数记得扩展测试的文件列表

---

## v3.0.0 (2026-04-23 · pipeline 架构默认启用 + Phase 6c 解耦)

### REFACTOR · pipeline 成为主干 · legacy 转作 fallback
- **症状**：v2.x 连续 5 个 hotfix 都落在 assemble_report.py (2964 行) + run_real_test.py (2105 行) 两个巨文件 · 屎山深重
- **位置**：`run.py::main`（默认路径切换）+ `lib/pipeline/score.py`（Phase 6c 解耦）+ `lib/pipeline/run.py`（pipeline 编排）
- **根因**：v2.15.x 的 Phase 6a delegate 模式下 `pipeline.score_from_cache` 直接调 `rrt.stage1(ticker)` · 但 stage1 内部会重新跑 22 fetcher collect · 和 pipeline 刚做完的 collect 重复 · 每股多耗 5-10 分钟
- **影响**：pipeline dark-launch 两周下来 opt-in 用户反馈"比 legacy 慢" · 原因是重复 collect
- **修法**（3 步）：
  1. `pipeline/score.py::score_from_cache` 改为调 rrt 纯函数（`score_dimensions` / `generate_panel` / `generate_synthesis` / `_autofill_qualitative_via_mx`）· 不再调 `stage1`
  2. `pipeline/run.py::run_pipeline` 加 `_preflight_guards(ticker)` · 中文名 / ETF / LOF / 可转债 → 抛 `ValueError` 让 run.py fallback legacy（legacy 有完整交互）
  3. `run.py::main` 默认走 pipeline · `UZI_LEGACY=1` 强制走老路径 · pipeline 异常自动 fallback · 附 traceback
- **验证**：002217 resume 模式 `run_pipeline` 46.9s 出报告 · 以前约 120s+（score_from_cache 从 180s → 10.6s）
- **回归测试**：332 tests 全过（253 legacy + 79 pipeline）· 含 score/synthesize/collect/run_pipeline 的模块单测
- **未来改该区域注意事项**：
  - `pipeline.score_from_cache` 依赖 rrt 的 4 个函数签名 · 任何一个签名变 · pipeline 同步改（现状：`score_dimensions(raw)` / `generate_panel(dims_scored, raw)` / `generate_synthesis(raw, dims_scored, panel, agent_analysis=None)` / `_autofill_qualitative_via_mx(raw, ticker)`）
  - `pipeline/synthesize.py` 仍是 delegate · 调 `rrt.stage2(ticker)` · stage2 只读 cache 不 collect · 安全（改 stage2 时注意保持"只读 cache"原则）
  - `_preflight_guards` 里的异常类型必须是 `ValueError` · 其他异常 run.py 不会 fallback（视为 crash）
  - 如果 legacy 某个纯函数名变（如 `generate_panel` → `build_panel`）· pipeline/score.py 必须同步改 · 否则 AttributeError
  - `UZI_LEGACY=1` 是保险开关 · 绝不删 · 出问题时用户能临时回退

---

## v2.15.5 (2026-04-23 · 评分聚集在一个区间 · 区分度不足)

### BUG · consensus 公式把连续分压成三分类 + 规则严苛导致结构性居中
- **症状**：用户反馈"现在评分大多数都在一个区间内徘徊" · 采 7 股数据 consensus 聚集 40-55 · 流派间分歧 stdev 常 < 15
- **位置**：`run_real_test.py::generate_panel`（v2.11 原公式）+ `lib/investor_evaluator.py:69`（BULLISH_THRESHOLD=65 / BEARISH_THRESHOLD=35）
- **根因 1（最主要）**：v2.11 公式 `(bullish + 0.6*neutral)/active*100` 只看 signal 计数 · 单 investor score 虽然 stdev=30 信息丰富 · 但被压成 3 分类后"打 55 分"和"打 40 分"一样算 neutral 贡献 · 程度丢失
- **根因 2**：A 成长派规则严苛 · 平均 score 35 · D 技术派 51 · G 量化 58 · 跨流派结构性偏差 20 分
- **影响**：7 流派 consensus 分布挤在 30-55 区间 · 用户看不出"宏观派买入 vs 价值派回避"这种真正的分歧信号
- **修法**（3 步）：
  1. `generate_panel` 引入 `SCORE_WEIGHT=0.65 + VOTE_WEIGHT=0.35` 混合公式 · `raw = 0.65*score_mean + 0.35*vote_weighted`
  2. 加 `_polarize(c, k=1.3)` helper · `final = clip(50 + (raw-50)*1.3, 0, 100)` · 以 50 为中心把两端拉开
  3. 总盘 + `school_scores` 每个流派同步升级 · 新增 `score_mean` / `vote_consensus` 分量字段让用户看到"实分 vs 投票"的拆解
- **验证**：
  - 002217 F 游资 51→43.7（vote 机制高估修正·实分 42）· G 量化 50→59.3（实分 61 低估修正）
  - 总盘 range 从 62 → 68 分（两端更极端）
  - 7 股样本没有再聚集 40-55 区间
- **回归测试**：`tests/test_v2_15_4_school_scores.py`（9 tests · 含 `test_mixed_formula_polarizes_extremes` 校验数学 · `test_consensus_formula_in_panel_has_mixed_components` 守护常量）
- **未来改该区域注意事项**：
  - `SCORE_WEIGHT + VOTE_WEIGHT` 必须 == 1.0 · 否则 raw 会偏置
  - `POLARIZE_K` 调大（>1.5）会让 consensus 容易贴 0/100 · 不建议 · 想进一步拉开优先改 BULLISH_THRESHOLD
  - 若修改 `NEUTRAL_WEIGHT`（0.6→其他）· 必须同步 `stock_style.py::apply_style_weights` 里的 `neutral_w += w * 0.6`（v2.11 耦合）
  - `_polarize` 对 50 分不变 · 如果改变中心点需要同步更新 `_consensus_to_verdict` 阈值
  - 新增流派时 `school_scores` 聚合会自动处理 · 但 `GROUP_META` 要同步加 label + desc

---

## v2.15.4 (2026-04-22 · panel 只有总分看不到流派分歧)

### FEATURE · 按流派打分 (school_scores)
- **症状**：用户"打分系统我觉得可能还要优化一下，我们现在有几个流派，那么除了有一个最终分数，还要有不同流派各自给出的分数"· 51 位评委的分歧被聚合掉看不出来
- **位置**：`run_real_test.py::generate_panel`（~line 740）+ `assemble_report.py::render_school_scores` + `assets/report-template.html`
- **根因**：原设计只有一个 `panel_consensus` / `vote_distribution` / `signal_distribution` · 没有按 investor.group 分组聚合
- **影响**：结构性矛盾票（譬如宏观友好但成长性差）看总分只是中性 · 用户无法快速判断到底是"共识中性"还是"各派互相抵消"
- **修法**（3 改动）：
  1. `generate_panel` 末尾加 `by_group` 聚合 · 每个流派用和总盘一致的 `(bullish + 0.6*neutral)/active * 100` 公式生成 consensus · active 成员 score 均值生成 avg_score · `_consensus_to_verdict` 阈值 80/65/50/35 与综合分对齐
  2. `synthesis.json` 携带 `school_scores` · 报告层无须回拉 panel.json
  3. `render_school_scores` 渲染 7 卡片网格（配色按 verdict 语义）· 注入 `` 锚点
- **验证**：002217 · 宏观派 68 买入 vs 成长派 25 回避 · 分歧 43 分可见 · 总分 45.5 单看无法识别
- **回归测试**：`tests/test_v2_15_4_school_scores.py`（7 tests · 聚合数学 / 阈值一致 / 模板锚点 / render 函数 / 空数据兜底）
- **未来改该区域注意事项**：
  - 如果修改 `NEUTRAL_WEIGHT` 或 consensus 公式 · 必须同步改 `generate_panel` 下面的流派聚合段 · 两处必须保持公式一致
  - 如果新增/删除 investor · 需确认其 `group` 字段在 A-G 范围 · 否则 `school_scores` 会出现 `?` key
  - 如果改 verdict 阈值（如 65→70）· 必须同步 `_consensus_to_verdict` 和 overall 的 `verdict_label` 两处

---

## v2.15.3 (2026-04-21 · fetch_capital_flow 严重性能 bug)

### BUG · 每股重抓全 A 大宗/解禁/融资数据（3+ min/股）
- **症状**：用户"数据源不稳定"反馈 · 12_capital_flow 维度每股卡 3-5 min
- **位置**：`fetch_capital_flow.py::main()` · 4 个调用涉及全市场数据
- **根因**：`stock_dzjy_mrtj` / `stock_restricted_release_summary_em` / `stock_restricted_release_detail_em` / `stock_margin_detail_szse/sse` 都返回全市场整年数据（几千到几万行），原 main 每股分析都重下一遍后再 filter
- **修法**：
  - 新增 4 个 `_universe_*()` helper · 用 `cached("_universe", key, ttl=24h)` 做 module-level cache
  - `main()` 里从已 cache 的 universe 数据 filter 本股记录
  - cache key 用 `"_universe"` 作 ticker · 跨股共享
- **实测**：首次 382s（正常 · 建 cache）· 二次 cache 命中 universe 部分 0.01s
- **回归测试**：`tests/test_v2_15_3_capital_flow_cache.py` · 6 case
- **未来注意事项**：
  - 所有"全市场数据集 + 本股 filter" 模式都要走 universe cache · 不能 per-stock 重抓
  - 其他可能有同类问题的 fetcher：fetch_industry.py 的 cninfo 全行业数据 · 应审查
  - universe cache 统一放 `.cache/_universe/api_cache/` · 跟股票 cache 分开便于清理
  - TTL 24h · 如果是交易时间敏感的大宗/融资 · 可以缩短到 2h（但性能 vs 新鲜度要权衡）

---

## v2.15.2 (2026-04-21 · GitHub issue #36 + #30 hotfix)

### BUG · Gemini CLI 安装报错（#36）
- **症状**：`gemini extensions install ...` 失败 · 报 `missing "version"`
- **位置**：`gemini-extension.json`（仓库根目录）
- **根因**：Gemini CLI 硬校验 `version` 字段 · 我们没给
- **修法**：加 `"version": "2.15.2"` · 并把该文件纳入 `.version-bump.json::files` · 未来 bump 会自动同步
- **未来注意**：每次 bump version 时确认 `gemini-extension.json` 也被更新 · 否则 Gemini CLI 用户会装到过期版本

### FEATURE · 网络自检增强（#30）
- **需求**：Clash 用户偶尔代理配错 · 希望 plugin 能诊断 + 给具体修复建议
- **位置**：`lib/network_preflight.py`
- **实现**：
  1. `_detect_local_proxy()` · 扫常见代理端口（7890/7891/7897/10808/1080/8888）· 检到本地代理但 env 没 HTTPS_PROXY → 给 export 建议
  2. `diagnose_source(profile)` · 按 domestic/overseas/search 3 组独立诊断 · 每组列 affected_fetchers + multi-line fix
  3. `NetworkProfile` 新增 `local_proxy: dict` + `diagnostics: list` 字段
  4. `run_preflight` 写 cache 时把这两个新字段一起落盘 · agent 可读
  5. verbose 模式输出 · Clash hint + 每组 fix 多行
- **未来注意**：
  - 若加新代理工具（如 Quantumult X / Surge 一键），在 `_LOCAL_PROXY_PORTS` 里加新端口
  - `diagnose_source` 里的 `affected_fetchers` 列表要跟实际 fetcher 保持同步（加新 fetcher 时检查）
  - cache 文件 schema 变了 · 老 cache 读回要用 `NetworkProfile.from_dict` 的容错逻辑

---

## v2.15.1 (2026-04-20 · 报告质量 2 bug hotfix · 实测 300470 发现)

### BUG 1 · fund-card 渲染 0.0% 假数据
- **症状**：用户看到"每次都一大堆基金持有，看着就很不对劲" · 报告里 15-30 张 fund-card 中第 5/6 张起 5Y/年化/回撤/夏普全是 0.0%
- **位置**：
  - `fetch_fund_holders.py::_build_row_full` 
  - `assemble_report.py::render_fund_managers`
- **根因**：双层故障链
  1. fetch_fund_holders · compute_fund_stats 返 `{}` 时用 `stats.get("return_5y", 0)` 写 0（应该 None）· fund.eastmoney.com SSL 封或新基金 NAV 不足 50 条都会触发
  2. assemble_report · `INITIAL_SHOW = 6` 硬编码 · 所有 manager 都过 for 循环生成 fund-card · 即便 return_5y=None 也被 `m.get("return_5y") or 0` fallback 成 0
- **修法**：
  - fetch_fund_holders: stats 空时降级为 `_row_type="lite"` + 所有数值字段 None · 有 has_real_stats 判断
  - assemble_report: for 循环里 `is_lite` 跳过 full-card 生成 · `INITIAL_SHOW = min(6, len(cards))` 动态 · lite 去重（按 fund_code）+ cap 30 · 余量"另有 N 家"文案
- **回归测试**：`tests/test_v2_15_1_fund_lite_rendering.py` · 7 case
- **未来改该区域注意事项**：
  - 任何 fetcher 返"有字段但数值是 0"的场景都要考虑 render 端是否会误判为"实测数据"
  - 同理 8_materials / 3_macro / 7_industry 的数值字段如果网络失败默认返 0 会误导报告
  - `_build_row_full` 的 has_real_stats 判断必须保留 · 未来加新 stat 字段也要纳入判断
  - lite 去重是按 `fund_code` · 富国天惠 A/B/C/D 虽是同一产品不同份额，但在报告里应合并看
  - LITE_CAP=30 可调 · 太少看不到小仓机构 · 太多撑爆报告

### BUG 2 · 14_moat 污染成贵州茅台数据
- **症状**：中密控股 300470 报告 14_moat 四个字段（intangible/switching/network/scale/rd_summary）全部显示"贵州茅台表示，技术创新在公司发展历程中始终扮演关键角色... 成立研究院公司..."
- **位置**：`fetch_moat.py::main` 的 search 结果过滤环节
- **根因**：DDGS 对生僻公司（中密控股）查 "上市公司 专利 核心技术 品牌壁垒"时返回的是热门股（茅台）的高相关文章 · 原 filter 只做 `_is_garbage`（字典/百科）检测，没做"结果是否真含目标公司名"检测
- **修法**（`fetch_moat.py`）：
  - `_SUPERSTAR_POLLUTERS` 列表 · 15 个易污染股（茅台/五粮液/宁德/腾讯等）
  - `_result_mentions_company()` · 结果 title+body 不含目标公司名就丢（含 polluter 更是硬 drop）
  - polluter 集合动态排除目标自身（分析茅台时茅台自己的结果保留）
- **回归测试**：4 case
  - polluter 结果被丢
  - 真含目标公司保留
  - 无关结果保守过滤
  - 目标本身是 polluter 时自己不被误伤
- **未来改该区域注意事项**：
  - 其他用 search_trusted + keyword 评分的 dim（4_peers / 7_industry / 13_policy / 17_sentiment / 18_trap）也可能有同类污染 · 后续要逐个加这个 filter
  - `_SUPERSTAR_POLLUTERS` 名单要定期更新 · 2026 年茅台/宁德仍是顶级，但 2027+ 可能换人
  - 不要用 DDGS 对生僻公司做基本信息抓取 · 要靠 akshare / xueqiu API

---

## v2.15.0 (2026-04-20 · YAML persona 层 · 修 Rules 4 类历史立场硬伤)

### FEATURE · YAML persona 接入 agent role-play（取长补短 augur）
- **背景**：xgzlucario/augur 对比测试发现当前 Rules 引擎有 4 类"投资者历史立场错位"硬伤：
  1. 合力泰 × 木头姐 Rules 说"必须重仓"（她不会买 OEM 显示模组）
  2. 合力泰 × 赵老哥 Rules 说"观望"（这恰恰是他最爱的低价题材）
  3. 茅台 × 巴菲特 Rules 说"买入"（他公开说过"不懂中国白酒"）
  4. 中际旭创 × 段永平 Rules 说"强买"（PE 63 超他 40 红线）
  Rules 是"触发某条规则就按模板出话"，没有 persona 历史 context，所以写出的话没法对齐本人立场。
- **实现位置**：
  - `skills/deep-analysis/personas/` · 51 YAML 文件（12 flagship 手写 + 39 stub 自动生成）
  - `skills/deep-analysis/scripts/lib/personas.py` · 加载 + prefix-stable system message
  - `skills/deep-analysis/scripts/lib/i18n.py` · language_instruction（zh/en）
  - `skills/deep-analysis/SKILL.md::HARD-GATE-PERSONA-ROLEPLAY` · agent 必须读 YAML
- **混合架构设计**：
  - Rules 引擎保留（确定性兜底 · agent 失败仍可出报告）
  - YAML persona 补充（flagship 优先级 > Rules headline · stub Rules 优先）
  - 如冲突 · flagship persona 可覆盖 panel.json signal/score（记 `_rules_override`）
- **augur 吸收的具体设计**：
  - YAML persona 格式（philosophy/key_metrics/avoids/voice）
  - prefix-stable system message → prompt cache 命中 → input token 省 50-90%
  - language_instruction() i18n helper
- **保留的自有优势**：
  - 22 维真实 fetcher（vs augur 只靠 LLM web search）
  - 51 投资者含游资派（vs augur 仅 18 位西方人）
  - 17 机构方法 · HTML 报告 · 机械自查 gate
- **验证**（双盲 · 3 股票 × 5 投资者 = 15 格）：
  - 准确性 YAML 14/15 vs Rules 8/15
  - 入戏感 YAML 15/15 vs Rules 2/15
  - 明显错误 YAML 0 vs Rules 4
- **回归测试**：`tests/test_v2_15_0_persona_layer.py` · 14 case
  - 51 个 YAML 全部存在 · 12 flagship 身份正确 · 39 stub 标记正确
  - flagship 必填字段（philosophy + key_metrics + voice + a_share_view）
  - YAML id 跟 panel.json investor_id 1:1
  - `build_system_message` prefix 稳定（prompt cache 前提）
  - i18n zh 默认 / en opt-in / env override / unknown 回退
- **未来改该区域注意事项**：
  - **每次 panel.json 新增 investor_id 都必须同步建对应 persona YAML**（否则 agent role-play 会 silent fall through 到 Rules）
  - flagship 的 12 个 YAML 是"质量基线"· 修改要审慎，要 diff 对比历史 headline 看语气是否跑偏
  - stub 的 39 个 YAML 是占位 · 每当用户反馈"某评委说话不像本人"就把那个 stub 手写升级为 flagship · 长期目标所有 51 个都变 flagship
  - `_parse_minimal_yaml` 是零依赖 parser · 不支持嵌套 list / 复杂 YAML · 保持 YAML 文件简化风格（不要加锚点 / 不要加 >< 折叠）
  - `build_system_message` 必须 prefix-stable · 任何改动要先跑 `test_build_system_message_is_prefix_stable` 确认
  - prompt cache 对 Anthropic / OpenAI 都有效，但对其他 LLM provider 可能无加速 · 文档里不能承诺"一定省钱"
  - agent 走 sub-agent 调 role-play 时要注意：sub-agent 没有 persona YAML context，必须通过 Agent tool prompt 显式传入

---

## v2.14.0 (2026-04-20 · 自动检测 GitHub 新版本 · interactive prompt)

### FEATURE · 自动更新检查
- **需求**：用户每次使用插件时自动检测 GitHub 有没有新版本 · 有更新先提示用户是否更新 · 三选一（是/跳过本版/否）· 跳过本版后直到下一个更新版本出来之前不再跳弹窗
- **实现位置**：
  - `skills/deep-analysis/scripts/lib/update_check.py` · 核心模块（check_for_update + mark_skipped + cache）
  - `run.py::_maybe_prompt_update()` · CLI 直跑 interactive
  - `hooks/session-start` · agent 会话后台写 `.cache/_global/update_prompt.md`
  - `SKILL.md::HARD-GATE-UPDATE-PROMPT` · agent 展示规则
- **设计要点**：
  - 6h cache 防 GitHub API 限流（60 req/h 未认证）
  - semver 严格匹配 · 仅正式 tag 比较
  - 3 态 skip 逻辑：`skipped_version == latest` 才跳过；一旦有更新版立刻再弹
  - 非 TTY / `UZI_NO_UPDATE_CHECK=1` / 网络异常 全部 silent skip · 不阻塞主流程
  - 状态文件 `.cache/_global/update_check.json`
  - agent 环境走 `.cache/_global/update_prompt.md` 文件通道（因为 hook 无法直接跟用户 prompt）
- **防滥用**：timeout 5s + exception 全 catch · 永远不因检查挂主流程
- **回归测试**：`tests/test_v2_14_0_update_check.py` · 13 个 case
  - parse_semver 边界 / newer 比较 / env 禁用 / 同版本不弹 / 新版本弹
  - skip 后同版不弹 · 更新版本再弹
  - 网络失败 silent skip · cache 不重复打 API
  - handle_answer y/s/n 三路径 · format_prompt 含三选项
- **未来改该区域注意事项**：
  - `UZI_NO_UPDATE_CHECK=1` 在 CI / Codex 环境必须默认设 · 防阻塞
  - `check_for_update` 内部任何异常都要 catch 不能抛 · 主流程绝不被 update 检查影响
  - `mark_skipped` 只记 version · 不记时间 · 因为"跳过本版"语义与时间无关（除非 latest 变了）
  - GitHub API 未来可能变 schema · 只读 `tag_name` 和 `body` 两个字段 · 兼容性最大
  - hook 写 prompt 文件走 async `&` · 阻塞 session-start 会拖慢 agent 启动

---

## v2.13.7 (2026-04-19 · wire new sources · registry 登记但 fetcher 没用的 16 源接入)

### BUG · v2.13.4 / v2.13.6 新增源只登记未接入 · 数据流通失效
- **症状**：v2.13.6 加了 `jin10_flash` / `em_kuaixun` / `em_stock_ann` / `ths_news_today` 到 registry，但 `fetch_events.py` / `fetch_sentiment.py` 没调它们，数据源对实际报告 zero 影响
- **位置**：
  - `skills/deep-analysis/scripts/fetch_events.py` (15_events · A 股)
  - `skills/deep-analysis/scripts/fetch_sentiment.py` (17_sentiment)
  - `skills/deep-analysis/scripts/fetch_policy.py` (13_policy)
  - `skills/deep-analysis/scripts/lib/data_sources.py::_kline_us_chain` / `_kline_hk_chain`
- **根因**：
  - `data_source_registry.py` 只是声明清单（tier/markets/dims/health 元数据）· 真正的调用必须在 fetcher 里显式写代码
  - 之前添加 registry entry 时没同步改 fetcher，导致"注册但未使用"
  - 对外看：`SOURCES 已 70` 但用户报告里新闻还是 3-5 条（来自老 cninfo + ak.stock_news_em）
- **影响**：
  - 15_events 数据密度 3-5 条 · 应有 10-30 条
  - 17_sentiment heat 分数偏低 · 没利用金十/东财快讯的实时信号
  - 13_policy 期货/商品类 industry 没有权威协会源信号
  - 美股/港股 K 线在 yfinance 挂掉时（2026 年常见 cookie 失败）无 HTTP 兜底
- **修法**：
  1. 新建 `lib/news_providers.py`（160 行）· 4 新闻源统一聚合 · 10 min cache
  2. `fetch_events.py::main()` A 股路径调 `get_news_multi_source` 合并结果
  3. `fetch_sentiment.py` 调 `get_news_multi_source` 做情绪增强 + heat bonus
  4. `fetch_policy.py` 加 `_fetch_cfachina_titles` · 期货相关 industry 才触发
  5. `data_sources.py::_yahoo_v8_chart()` · 429 自动 retry · US/HK kline chain 都接入
- **验证**：
  - `python3 lib/news_providers.py "" ""` → sources_ok: 4/4, total_hits: 31 ✓
  - `_yahoo_v8_chart("AAPL", "1mo")` → 22 rows ✓
  - `_yahoo_v8_chart("0700.HK", "1mo")` → 21 rows ✓
  - pytest 全量 217 passed（baseline 205 + 12 新）
- **回归测试**：`tests/test_v2_13_7_wire_new_sources.py`
  - `news_providers` 模块 API 存在性 + dataclass
  - `fetch_jin10` / `fetch_em_kuaixun` 正则解析 · 特别 em_kuaixun 无尾 `;` 格式
  - `fetch_events` / `fetch_sentiment` 接入 news_providers 调用路径
  - `_yahoo_v8_chart` 解析 + 429 retry
  - `_kline_us_chain` yf/ak 全败时兜底到 v8
  - `fetch_policy` 期货 industry 调 cfachina · 非期货跳
- **未来改该区域注意事项**：
  - **每次往 `data_source_registry.SOURCES` 加新源，都必须同时改对应的 fetcher 去实际调用这个源**。registry 是静态声明，不是活调度
  - `news_providers.py` 是"ddgs 盲区"补充 · 不是全量替代 · 老的 cninfo / akshare 路径保留
  - `_yahoo_v8_chart` 的 User-Agent 用 Windows Chrome · 测试过 macOS UA 偶尔 429
  - em_kuaixun 响应格式是 `var ajaxResult={...}` 无尾 `;` · 正则用 `\s*;?\s*$` 兼容
  - cfachina 大部分列表 JS 渲染 · 只能抓首页静态标题链接 · 深度内容需 Playwright
  - 加新 news provider 时，要在 `_is_noise_news` 检查 title 是否会被 `_NOISE_KWS` 误过滤

---

## v2.13.5 (2026-04-19 · NetworkProfile 自适应 + agent HARD-GATE 主动触发 Playwright)

### BUG · agent role-play 阶段不主动调 Playwright · 低质量数据未被兜底
- **症状**：用户反馈"我使用下来，并没有遇到模型主动使用 Playwright 的问题"
- **位置**：SKILL.md / AGENTS.md / commands/analyze-stock.md 的 agent 工作流指引
- **根因**：
  - stage1 末尾 `autofill_via_playwright` 自动跑一次 OK，但 data 有字段但全是 "—" 时 `_dim_needs_fallback` 判"不需要兜底" → 跳过
  - agent 介入阶段只做 role-play，不碰数据补充
  - SKILL.md 只在多处散句提及 "Chrome/Playwright MCP"，**没有** HARD-GATE 明确要求 agent 主动调 autofill
- **影响**：
  - 每次 agent role-play 出报告时，某些维度仍空
  - 用户看到 "数据缺失" 的 commentary 模板话术
  - Playwright 基础设施已全 · 但 agent 层**被动**不用
- **修法**（三层）：
  1. `lib/network_preflight.py` 升级 NetworkProfile（9 目标 3 组 + 代理检测 + 写 cache）· 提供 agent 决策输入
  2. SKILL.md 加 `HARD-GATE-PLAYWRIGHT-AUTOFILL` · 明确 3 step 流程：读 net profile → 读 review_issues.json → 主动 FORCE 跑 autofill
  3. `lib/playwright_fallback.DIM_NETWORK_REQUIREMENTS` 每维声明网络能力 · `_filter_dims_by_network` 自动过滤
- **验证**：
  - agent 按 SKILL.md 指引跑 · 看到 `_review_issues.json` warning 主动调 Playwright
  - 测试构造 `NetworkProfile(domestic_ok=False)` → `_filter_dims_by_network` 全跳
  - `NetworkProfile(search_ok=False)` → 7_industry / 18_trap 跳 · 其他维度保留
- **回归测试**：`tests/test_v2_13_5_preflight_adaptive.py` 14 用例
  - Layer 1：代理检测 / recommendation 变化 / cache 读写 / stale 重测（6 用例）
  - Layer 3：domestic offline 全跳 / search offline 部分跳 / 全通保留（3 用例）
  - Layer 2：SKILL.md / AGENTS.md / commands 文档检查（3 用例）
  - 基本数据类：NetworkProfile 字段 / DIM_NETWORK_REQUIREMENTS 完整性（2 用例）
- **若未来改 agent 工作流**：
  - HARD-GATE-PLAYWRIGHT-AUTOFILL 不能删（SKILL.md line ~125 附近）· agent 读了就会主动用
  - `.cache/_global/network_profile.json` schema 不能破（agent 依赖 domestic_ok/overseas_ok/search_ok 三字段）
  - `DIM_NETWORK_REQUIREMENTS` 需覆盖 DIM_STRATEGIES 所有 key（有测试护栏 `test_dim_network_requirements_complete`）
  - 新增维度要同步加网络声明

---

## v2.13.3 (2026-04-19 · 51 评委规则全员历史立场还原)

### BUG · 19 位评委给中际旭创 100 分 · 立场与历史严重不符
- **症状**：用户截图报告 "彼得·林奇 100 分 · 看多 · PEG ≈ 63.253/60 < 1.5" 质疑"林奇历史上不会这么激进"。扫描面板发现 19 人 100 分（13 位游资 + 林奇 + 索罗斯 + 段永平 + 张坤 + 邓晓峰 etc），木头姐 13 分看空（CPO 是她赛道）
- **位置**：`lib/investor_criteria.py` 多处规则 · `lib/investor_evaluator.py` · `lib/seat_db.py`
- **5 处根因**：

  1. **F 组游资射程反向判定**：`_youzi_base_rules` 把 `market_cap > min_mcap` 当打分依据 · 9456 亿对所有游资都"在射程" · 13 位游资误打 100/25。`is_in_range` 定义了但 evaluator 从未调用

  2. **索罗斯反身性方向错**：`abs(upside_to_target) > 10` · 目标价 -63%（看空信号）也判"反身性差 = 看多 100"。`abs` 是 bug

  3. **林奇 PEG 过松 + 无 PE 红线**：`peg_reasonable PEG < 1.5` 太松 · 无 PE 上限。但林奇原话 "PE should approximately equal growth rate" (PEG ≤ 1) 和 "PE > 40 like Rolls Royce"（历史持仓 Taco Bell 0.6/Hanes 0.2/Fannie Mae 0.6 均 PEG < 1）

  4. **木头姐双 bug**：(a) `check=lambda f: f.get("industry_growth_pct", 0) > 20` · 但 stock_features v2.12.1 设的是 `industry_growth`（不带 _pct）· 中际旭创 40% 读成 0 · 判"增长太慢"看空。(b) 白名单缺 CPO/光模块/算力/数据中心/HBM · AI 基建本是 ARK 核心赛道

  5. **中国价投派无 PE 红线**：段永平/张坤/邓晓峰 rules 只看 `pe_quantile_5y < 50` 不看绝对 PE · 高估值成长股也 100 分。历史：段买苹果 PE 18 / 茅台 PE 30；张坤重仓 PE 15-35 区间；邓晓峰偏左侧

- **影响**：所有高 PE 成长股（CPO/光模块/AI/新能源等）的评委分布都被扭曲 · 5 大类评委立场都与历史不符 · 核心卖点"51 评委量化投票"的可信度崩塌

- **修法**：
  1. `seat_db.is_in_range` 加隐式 500 亿大市值上限 + `_MEGA_CAP_ALLOWLIST = {"章盟主"}`
  2. `investor_evaluator.evaluate` 加 `_is_youzi_out_of_range` 前置检查 · F 组超射程 skip
  3. `_youzi_base_rules` 移除 min_mcap/max_mcap 作为 Rule
  4. SOROS_RULES 拆 `sentiment_long_reflex`（只 upside > +10 pass）+ `sentiment_short_reflex_penalty`（upside < -15 扣分）
  5. LYNCH_RULES 6 条：`peg_ideal (PEG<1, 5分)` + `peg_acceptable (PEG 1-1.5, 3分)` + `pe_not_rolls_royce (PE<40, 3分)` + `fast_grower_zone (20-50%, 3分)` + `understandable (2)` + `research_support (2)`
  6. WOOD_RULES 字段兼容 `industry_growth` · 白名单加 CPO/光模块/算力等 12 词
  7. DUAN_RULES/ZHANGKUN_RULES/DENGXIAOFENG_RULES 各加 `pe_not_expensive`（PE<40/40/35）

- **验证**（300308.SZ 实测）：
  | 评委 | v2.13.2 | v2.13.3 |
  |---|---|---|
  | 林奇 | 100 bullish | 38 neutral |
  | 索罗斯 | 100 bullish | 42 neutral |
  | 段永平 | 100 bullish | 84 bullish |
  | 张坤 | 100 bullish | 78 bullish |
  | 邓晓峰 | 100 bullish | 76 bullish |
  | 木头姐 | 13 bearish | 80 bullish |
  | F 组 22 位游资 | 全打分 | 全 skip（射程外） |
  - 100 分从 19 人 → 5 人 · skip 从 1 → 23 人

- **回归测试**：`tests/test_v2_13_3_investor_rules.py` 15 用例
  - F 组：out_of_range / allowlist 章盟主 / small cap in range / 非游资不受影响
  - 索罗斯：+30 bullish / -63 bearish / +5 neutral 三方向
  - 林奇：PEG < 1 / PE 63 reject / PEG 1-1.5 临界
  - 木头姐：CPO 识别 + industry_growth 新字段兼容
  - 段永平/张坤：PE 63 扣分 + PE 30 高分护栏
  - 全量 173 passed（v2.13.2 158 + 新 15）

- **若未来改评委规则**：
  - **核心护栏 · 林奇的 PE 40 红线不能去掉**（历史持仓数据支撑 · Rolls Royce 原话）
  - **索罗斯反身性 abs() 绝对值是已知反向 bug**（目标价 -63% 不是 "反身性差 = 看多"）
  - **F 组游资必须 is_in_range 前置 skip**（大市值股游资不玩 · 9000 亿拉不动）
  - **中国价投派 PE 红线对应各自历史风格**（段 40 / 张 40 / 邓 35）· 改动需说明历史出处
  - **木头姐字段名用 `industry_growth` 口径**（与 stock_features 保持一致 · v2.12.1 字段）
  - 加新评委规则要同时加历史依据（书/年报/访谈 URL）到 `lib/investor_knowledge.py`

---

## v2.13.2 (2026-04-19 · Playwright 触发逻辑升级 · 数据质量感知 + FORCE flag)

### BUG · 维度有 data 但值都是 "—"/空时 Playwright 兜底未触发
- **症状**：用户反馈"有很多网站爬不到内容，也没拉起 Playwright"。中际旭创 cache 里 `7_industry.data` 有 12 个 key 但 `growth`/`tam`/`penetration` 都是 `"—"`，Playwright 却判定"已有数据"直接跳过
- **位置**：`lib/playwright_fallback.py::_dim_needs_fallback`
- **根因**：原版只看 `len(data)`：
  ```python
  if not data or not isinstance(data, dict): return True
  if dim.get("fallback") and len(data) < 4: return True
  return False
  ```
  数据 12 keys 但全是垃圾也判"不需要兜底" · 用户期望落空
- **修法**：
  1. 新加 `_dim_quality_score(data)` 计算有效值占比（排除 `_` 前缀诊断字段）
  2. `QUALITY_THRESHOLD = 0.5` · 低于 50% 触发兜底
  3. `dim.fallback=True` 总是触发（不再看 len）
  4. 返 tuple `(needs, reason)` 让日志可看
- **附加**：
  - 加 `UZI_PLAYWRIGHT_FORCE=1` 环境变量 · 用户强制 kill switch · 忽略 quality 判定
  - `autofill_via_playwright` 加清晰日志 · 每个维度 skip/run 原因可见 · 禁用时明确 `disabled_reason`
- **验证**：
  - `_dim_quality_score({"a":"—","b":"","c":None,"d":[],"e":"真实"})` → 20%
  - 12 keys 含 3 有效 → 触发 ✅
  - FORCE=1 · 质量 100% 的 dim 也触发
- **回归测试**：`test_v2_13_playwright_strategy.py` 新增 5 用例
  - `test_dim_quality_score_detects_mostly_empty`
  - `test_dim_quality_score_skips_ignoring_underscore_keys`
  - `test_autofill_triggers_on_low_quality_data`
  - `test_force_flag_ignores_quality_check`
  - `test_autofill_summary_has_disabled_reason_when_off`
- **若未来改 Playwright 触发**：
  - `_dim_needs_fallback` 返 tuple `(needs, reason)` 契约不能破（调用方日志依赖 reason）
  - `QUALITY_THRESHOLD` 如需调整必须跑 `test_autofill_triggers_on_low_quality_data` 确保低质量用例仍触发
  - `_` 前缀字段不计 quality 这个语义不能改（避免 `_autofill`/`_debug` 被误当有效数据）
  - `UZI_PLAYWRIGHT_FORCE` 是用户 kill switch · 不能移除

---

## v2.13.1 (2026-04-18 · Playwright 全 10 维覆盖 · 策略契约修订)

### 改进 · 扩展 DIM_STRATEGIES 到全 10 维（策略调整，非 bug 修复）
- **背景**：v2.13.0 Codex review 出于反爬/合规/信噪比担忧，明确排除 5 维（7_industry/14_moat/13_policy/18_trap/19_contests）。用户明确反馈："反爬合规不是问题，这个是开源研究项目受保护的"
- **位置**：
  - `lib/playwright_fallback.py::DIM_STRATEGIES` · 5 → 10 entry
  - `lib/analysis_profile.py` · `_PLAYWRIGHT_MEDIUM_DIMS` 4→6 维，`_PLAYWRIGHT_DEEP_DIMS` 5→10 维
  - `tests/test_v2_13_playwright_strategy.py` · 更新 count 断言 + 移除排除护栏
- **策略变化**：
  | 维度 | v2.13.0 | v2.13.1 | 目标页 |
  |---|---|---|---|
  | 7_industry | ❌ | ✅ medium + deep | 百度搜索 `{行业}+景气度` |
  | 14_moat | ❌ | ✅ medium + deep | 百度百科 `/item/{name}` |
  | 13_policy | ❌ | ✅ deep | 证监会 `csrc.gov.cn` |
  | 18_trap | ❌ | ✅ deep | 小红书 `/search_result?keyword={name}+老师+推荐` |
  | 19_contests | ❌ | ✅ deep | 雪球 `/cube/rank/list` 匿名组合排行 |
- **新增 5 个 parser**（`lib/playwright_fallback.py`）：
  - `_strategy_7_industry` · 抓百度 `<h3>` 标题 + `.content-right` 描述
  - `_strategy_14_moat` · 抓 `.lemma-summary` + `.basicInfo-item` 键值对
  - `_strategy_13_policy` · 抓证监会动态 `<a title="...">` 列表
  - `_strategy_18_trap` · 抓小红书 `"title":"..."` 帖子标题 + 命中数统计（风险信号）
  - `_strategy_19_contests` · 抓雪球组合 JSON 的 `name` + `total_gain`
- **回归测试**：
  - `test_dim_strategies_has_10_entries` (替代 `has_5_entries`)
  - `test_all_parsers_callable_and_return_none_on_empty_html` · 10 个 parser 都 mock `fetch_url` 返 None · 验证不抛
  - `test_medium_dims_subset_of_deep` · 护栏：medium 必须是 deep 子集
- **护栏测试移除**：
  - `test_excluded_dims_not_in_strategies` 移除（反爬合规不再是契约禁令）
- **v2.13.0 契约修订**：
  - ❌ 旧：v2.13.0 BUGS-LOG 说"不能不经 Codex review 加回排除维度"
  - ✅ 新：**开源研究场景不受合规限制** · 维度扩展由用户需求驱动即可
- **若未来改 Playwright 层**：
  - 10 个维度的 parser 如遇 HTML 变化 → 返 None 即可，不要 raise（已在 `fetch_url` 层 try/except）
  - `_PLAYWRIGHT_MEDIUM_DIMS ⊆ _PLAYWRIGHT_DEEP_DIMS` 关系必须保持（`test_medium_dims_subset_of_deep` 护栏）
  - 加新维度需同步：(1) `DIM_STRATEGIES` dict (2) `_PLAYWRIGHT_*_DIMS` 白名单 (3) 更新 `test_dim_strategies_has_10_entries` count

---

## v2.13.0 (2026-04-18 · Playwright 通用兜底 · 按三档 profile 分级)

### 改进 · Playwright fallback 通用化 + 按 profile 分级
- **背景**：v2.12.1 给 `fetch_peers.py` 加了雪球 Playwright Tier 3。用户提出"所有爬不到数据的都用 Playwright + 自动装"。直接做全量会导致 lite 用户也要背 150MB Chromium，且某些维度（小红书/抖音/微博）反爬 + 合规风险大
- **位置**：
  - 新增 `lib/playwright_fallback.py`（~320 行）· 通用模块
  - 新增 `lib/junk_filter.py` · 抽离 v2.12.1 的 `_is_junk_autofill`
  - `lib/analysis_profile.py` · AnalysisProfile 加 `playwright_mode` + `playwright_dims` 字段
  - `run_real_test.py:1750+` · 在 `_autofill_qualitative_via_mx` 之后调用
- **策略**（Codex review 后收敛）：
  | profile | playwright_mode | 覆盖维度 | 自动装行为 |
  |---|---|---|---|
  | lite | off | 无 | 不涉及（保持 30s-1min 快扫）|
  | medium | opt-in · 需 `UZI_PLAYWRIGHT_ENABLE=1` | 4 维（4_peers/8_materials/15_events/17_sentiment） | 未装时打印命令让用户手动装，本次跳过 |
  | deep | default 默认启用 | 5 维（medium 4 + 3_macro 官方权威） | 未装时 y/n 交互确认 → 同意自动装 → 失败 graceful degrade |
- **Codex review 排除的维度**：
  - `7_industry` → 百度搜索页信噪比差（保持 `search_trusted` site: 方案）
  - `14_moat` → 百度百科质量差
  - `13_policy` → `search_trusted` site: 限权威域已够
  - `18_trap` → 小红书/抖音反爬严 + UGC 合规风险
  - `19_contests` → `lib/xueqiu_browser` 已有专用登录路径
- **自动装流程**：
  1. 检测 `playwright` 包 + Chromium executable
  2. deep `auto=True` → `_confirm_install_interactive()` y/n 询问
  3. 同意 → `pip install playwright` 复用 `run.py::PYPI_MIRRORS` 清华/阿里云/中科大 fallback
  4. `playwright install chromium` 下载 ~150 MB · stdout 可见
  5. 任何环节失败 → 返 False · 调用方跳过 Playwright · **不阻塞主流程**
- **反爬 / 合规原则**：
  - 只抓官方权威页：`xueqiu.com/S/{sym}` public / `cninfo.com.cn` / `em.eastmoney.com` F10 / `stats.gov.cn`
  - 每次请求随机 0.5-1.5s sleep
  - 不抓 UGC 平台（小红书/抖音/微博）
- **验证**：
  - `is_playwright_enabled()` · lite False · medium opt-in + env True · deep 永远 True
  - `ensure_playwright_installed(auto=False)` 未装不跑 subprocess · 只打印命令
  - `ensure_playwright_installed(auto=True)` + user n → 不装 · 返 False
  - `autofill_via_playwright` 尊重 `profile.playwright_dims` 白名单
  - Playwright 返垃圾（"类型；类型"）被 `junk_filter` 过滤不写入
- **回归测试**：`tests/test_v2_13_playwright_strategy.py` · 21 个用例
  - 3 档 profile 字段检查 · `is_enabled` 三场景 · `ensure_installed` 5 种路径 · autofill 白名单 + 垃圾过滤 + 已有数据跳过 · `DIM_STRATEGIES` 5 维 · 排除维度护栏 · `junk_filter` 模块导出 · `run_real_test._is_junk_autofill` BC delegate
  - 全 mock · 无真实浏览器依赖 · CI 可跑
- **若未来改 Playwright 层**：
  - **不能把 lite 的 playwright_mode 改为 opt-in/default**（lite 设计上就是快扫 · 加浏览器破坏档位语义）
  - **不能静默自动装 Chromium**（150MB 下载用户必须知情 · deep 档已有 `_confirm_install_interactive`）
  - **安装失败必须 graceful degrade**（不能 raise 阻塞主流程 · 现有 `try/except` 不能删）
  - **不能把排除的 4 维（14_moat/13_policy/18_trap/19_contests/7_industry）加回 DIM_STRATEGIES 而不经 Codex 级 review**（反爬/合规/信噪比问题有先例）
  - **BC 契约**：`run_real_test._is_junk_autofill` 必须保留（现 delegate 到 `lib/junk_filter`）· 老代码可能直接 import

---

## v2.12.1 (2026-04-18 · 4 个报告板块空数据 / 错数据修复)

用户实测中际旭创（300308.SZ）发现 4 个板块问题 · 一次性修完.

### BUG · `4_peers` 东财 push2 挂了同行表空白
- **症状**：报告"同行对比"板块完全空 · `peer_table: []` · `peer_comparison: []`
- **位置**：`fetch_peers.py::main`（A 股分支 line 72-121 原版）
- **根因**：现有 try/except 只 catch 异常到 `peers_raw`，主链路 `ak.stock_board_industry_cons_em`（走 push2）挂了后没切换到 fallback 源
- **影响**：任何 push2 被反爬/限流的网络环境（国内代理、Codex 沙箱）同行表必空
- **修法**（三层 fallback + 一层保底）：
  1. Tier 1 主链（不变）
  2. Tier 2 · 2.5s 后 retry 一次（网络抖动兜底）
  3. Tier 3 · 雪球 Playwright 登录态（用户 opt-in `UZI_XQ_LOGIN=1`）· 复用 `lib/xueqiu_browser.py::fetch_with_browser` + 新加 `fetch_peers_via_browser(code)`
  4. Tier 4 · 最低保底：`_build_self_only_table` 返回公司自己一行 + `fallback: True` + `fallback_reason` 字段
- **验证**：中际旭创 E2E `peer_table` 至少有公司自己一行（不再空） + `fallback_reason` 明确说明降级原因
- **回归测试**：`test_v2_12_1_data_fixes.py::test_fetch_peers_has_self_only_fallback` / `test_fetch_peers_tier_chain_documented` / `test_fetch_peers_fallback_reason_surfaced` / `test_xueqiu_browser_has_fetch_peers_function` / `test_xueqiu_browser_peer_fn_respects_opt_in`
- **若未来改 fetch_peers**：Tier 4 `_build_self_only_table` 保底必须保留（不能回到"整表空"）· `data.fallback_reason` 字段 agent 依赖识别降级· 雪球 opt-in 必须保留 `is_login_enabled()` 检查不能改成默认启用（headless 环境会卡）

### BUG · `7_industry.growth/tam/penetration` 永远 `—`
- **症状**：行业景气板块的增速/TAM/渗透率 3 个字段永远是 `—`，即便 `dynamic_snippets` 已抓到 9 条 search 结果
- **位置**：`fetch_industry.py::_dynamic_industry_overview` (line 110-165) + `main` (line 185-188)
- **根因**：
  1. 原 growth regex `r"([+\-]?\d{1,3}(?:\.\d+)?)\s*%"` 不带上下文 · 容易被 `PE 25%` / `失业率 5%` 抢先匹配 · 且不匹"涨超40%"这类中文财经常见表达
  2. `penetration` 完全没 regex 抽取路径
  3. `main` line 187 `penetration` 没 fallback 到 dynamic（只 est 一条路径）
  4. `all_bodies` 只拼 `body` 不含 `title` · 关键数字常在 title 里（"净利齐涨超40%"）
- **影响**：所有未被 INDUSTRY_ESTIMATES 硬编码覆盖的 236+ 行业（包括通信设备）三字段全空 · 同时导致 Bug 4 BCG 缺 growth 输入
- **修法**：
  1. growth regex 上下文感知 · 关键词含 `增长/增速/CAGR/复合增长/同比/增幅/年均增长/涨超/涨幅/暴涨/翻倍/提升/上升/上涨/净利齐涨` + 0-20 字符 + %
  2. 加 `tam_context_pat` 优先匹"市场规模/规模达/产业规模/TAM/行业规模" 附近的"XX亿"
  3. 加 `penetration_heuristic` · 匹"渗透率 XX%" / "XX% 渗透率"
  4. main line 228 · `penetration = est.get("penetration") or dynamic.get("penetration_heuristic") or "—"` 补兜底
  5. `all_bodies` 改为拼 `title + body`
- **验证**：mock search_trusted 返含"增速 42% 预计 CAGR 30%" 的 snippets · `growth_heuristic` 抓到值不是 `—`
- **回归测试**：`test_industry_growth_regex_picks_context_aware` / `test_industry_penetration_regex_extracts` / `test_industry_penetration_fallback_wired_in_main`
- **若未来改 fetch_industry**：
  - 不能去掉上下文关键词直接裸匹 `%`（会被 PE/失业率等噪音抢先）
  - `penetration_heuristic` 在返回 dict 里必须保留（main 依赖）
  - `all_bodies` 必须同时拼 title + body（关键数字常在 title）

### BUG · `8_materials.core_material = "类型；类型"` (MX 垃圾数据无过滤)
- **症状**：原材料板块 `core_material` 显示为"类型；类型"这种 MX prompt 残留噪音
- **位置**：`run_real_test.py::_autofill_qualitative_via_mx` (line 1047-1068 原版)
- **根因**：后处理 `_autofill_qualitative_via_mx` 调 MX 妙想 API 填 6 个定性维度时，**直接把 MX 返回 `text` 写入 `data[字段]` 没做质量校验**。MX 偶尔会返回 prompt 模板残留（"类型；类型" / "抱歉，无法回答" / 重复片段）
- **影响**：6 个定性维度（3_macro/7_industry/8_materials/9_futures/13_policy/15_events）都可能被垃圾数据污染，比空还糟（显示错误数据）
- **修法**：
  1. 加 `_is_junk_autofill(text)` 函数 · 检测长度 < 5 / 黑名单短语（类型；类型/抱歉/无法回答/暂无数据/XXX/TODO/null）/ 分号分隔全同片段
  2. `_AUTOFILL_JUNK_PATTERNS` 模块级常量便于扩展
  3. MX 和 ddgs 返回后分别过滤 · 垃圾数据 `text = ""` 不写入
  4. 保留 `_autofill_failed` 标记让 agent/UI 明确知道是"数据不足"
- **验证**：中际旭创 E2E `core_material` 不再是"类型；类型"（可能是真实 ddgs 结果或 `—`）· 真实数据如"高端光通信收发模块"不被误伤
- **回归测试**：`test_junk_autofill_catches_type_duplication` / `test_junk_autofill_catches_refusal` / `test_junk_autofill_lets_real_text_through`
- **若未来改 _autofill_qualitative_via_mx**：
  - 写入 `data[字段]` 前必须先跑 `_is_junk_autofill(text)` 过滤
  - 遇到新的 MX 垃圾 pattern（如"未找到"/"NaN"）加到 `_AUTOFILL_JUNK_PATTERNS` 常量
  - 不能为了"省一次判断"就写无过滤版本 - 比空还糟的数据误导用户

### BUG · BCG 矩阵所有股都归为 "Dog (瘦狗)"
- **症状**：报告"BCG 矩阵定位"永远显示 Dog 瘦狗 + "考虑剥离/收缩" · 中际旭创作为 CPO 全球龙头被归 Dog 明显错误
- **位置**：
  - 计算：`lib/deep_analysis_methods.py::build_competitive_analysis` (line 488-503)
  - features 源头：`lib/stock_features.py` (line 340-341)
- **根因**：
  1. `stock_features.py:340-341` 写死 `f["industry_growth"] = _f(industry.get("growth"), default=10)` 和 `f["market_share"] = _f(industry.get("market_share"), default=10)` · 但 `industry.market_share` key 从未被任何 fetcher 写入 · 永远 default 10 · `industry.growth` 也因 Bug 2 永远 `—` → `_f("—")` = 0 或 default 10
  2. BCG 阈值 `market_share > 15` + `market_growth > 10` · 默认 10/10 不满足任何 `> 15` 条件 → 必落 Dog
  3. 阈值 `> 15 市场份额` 对 A 股单股非现实（很少有单股过 15% 市占率）
- **影响**：所有股票（茅台/中际旭创/宁德时代 etc）BCG 都是 Dog · "Star/Cash Cow/Question Mark"三档形同虚设
- **修法**：
  1. `stock_features.py` · 真实计算 `market_share = 公司市值 / 行业总市值 × 100`（数据源：`basic.market_cap_yi` / `industry.cninfo_metrics.total_mcap_yi`）
  2. `stock_features.py` · `industry_growth` 从 `industry.growth` 字符串 regex 解析 `[+\-]?\d+(?:\.\d+)?%`（Bug 2 修复后 growth 字段有真实值）
  3. `deep_analysis_methods.py` · BCG 阈值调整：Star `share>3 AND growth>15`、Cash Cow `share>3 AND growth≤15`、Question Mark `share≤3 AND growth>15`、Dog `share≤3 AND growth≤15`
  4. `default=10` 硬编改为 `default=0` · 让数据缺失时明确落 Dog（而不是假数据误导为 Dog）
- **验证**：
  - 中际旭创市值 9455 亿 / 通信设备行业 171648 亿 ≈ 5.5% > 3 · E2E 实测 BCG 升为 `Cash Cow (现金牛)`（growth 若抓到 >15% 则升 Star）
  - features market_share 5.5 + growth 25 → 单元测试验证归 Star
  - features share 0.5 + growth 2 → 归 Dog（回归护栏）
- **回归测试**：`test_stock_features_market_share_real_computation` / `test_bcg_thresholds_updated_for_realistic_a_share` / `test_bcg_classifies_zhongji_as_star` / `test_bcg_classifies_low_growth_small_share_as_dog` / `test_bcg_question_mark_for_high_growth_small_share`
- **若未来改 BCG / features**：
  - `stock_features.market_share` 必须真实计算 · 不能回退到 `default=10`
  - BCG 阈值 A 股上下文下 `share > 3` 是合理线（15% 是非现实）· 不能无理由拉回 15
  - `default=0` vs `default=10` 语义差异大 · 前者代表缺失（让 agent 识别），后者是假数据（历史 bug 根因）
  - 依赖链：Bug 4 需 Bug 2 先修好（growth 字段要有真实值）

---

## v2.11.0 (2026-04-18 · 评分校准 · 用户反馈驱动)

### BUG · 白马股被评"谨慎"、从未有股能拿"值得重仓"
- **症状**：
  - @崔越（微信）："测了几只股票，没有超过 65 分的"
  - @W.D（微信）："茅台 47 分"
  - @睡袍布太少（微信）："目前只测到天孚通信超过 65"
  - 观察：线性评分完全没有 ≥ 85 的股票，"值得重仓"档位形同虚设
- **位置**：
  - `run_real_test.py::generate_panel` 的 consensus 公式
  - `run_real_test.py::generate_synthesis` 的 verdict 阈值
  - `lib/stock_style.py::apply_style_weights` 的 neutral 权重（需同步）
- **根因**：
  1. 51 评委里价值派 6 + 中国价投 6 + 游资 23 = 35 人对大多数股偏保守 → bullish 常仅 5-15 人
  2. v2.9.1 consensus 公式 `(bullish + 0.5×neutral) / active` neutral 权重 0.5 过低，把 neutral 当"半空头"处理。实际语义是"不坑但不是我心头好"，应接近中位数
  3. verdict 阈值 85/70/55/40 太严 · 茅台白马实测 fund=62/consensus=37 → overall 47 → 谨慎
- **影响面**：所有 A 股 / 港股 / 美股。白马股结构性偏低 → 用户失去信心 → 卸载
- **修法**：
  1. `generate_panel` · consensus `NEUTRAL_WEIGHT 0.5 → 0.6` + 加 `consensus_formula.version` 诊断字段
  2. `generate_synthesis` · verdict 阈值 `85/70/55/40 → 80/65/50/35`
  3. `stock_style.apply_style_weights` · neutral 权重 `w*0.5 → w*0.6`（与 generate_panel 对齐）
- **验证**（模拟茅台典型 12/20/16/3 分布）：
  - 旧 consensus = (12 + 10) / 48 × 100 = 45.8 · overall = 62×0.6+45.8×0.4 = 55.5 → 观望
  - 新 consensus = (12 + 12) / 48 × 100 = 50.0 · overall = 62×0.6+50×0.4 = 57.2 → 观望优先
  - 对比茅台 47 实测 → 新公式提升 ~10 分，verdict 从 "谨慎" 升到 "观望优先"
- **回归测试**：`tests/test_v2_11_scoring_calibration.py` 8 个用例 · 护栏 `test_no_regressions.py::test_consensus_neutral_weighted_formula` 兼容 0.5/0.6 两种权重
- **若未来改 consensus**：
  - `NEUTRAL_WEIGHT` 必须同时改 `generate_panel` 和 `stock_style.apply_style_weights` 两处（否则加权前后分数不一致）
  - verdict 阈值任一改动必须跑 `test_verdict_thresholds_are_v2_11_calibrated`
  - 把 bullish-only 公式改回 `bullish / active` = **禁止**（forum 反馈已明确该公式导致白马结构性偏低）

---

## v2.10.7 (2026-04-18 · Codex 整体审查发现执行链路 3 处)

### BUG · `raw["market"]` 硬编 "A" 污染 HK/US 路径
- **症状**：`python run.py 00700.HK --depth lite` Self-Review 显示 `(A)`，应为 `(H)`；后续市场分支判断全错
- **位置**：`skills/deep-analysis/scripts/run_real_test.py::collect_raw_data` 入口 + post-fetch_basic 回填逻辑
- **根因**：
  1. 初始化时硬编码 `raw["market"] = "A"`
  2. post-fetch_basic 回填只在 `resolved_ticker != ticker` 分支里触发（用户直接输入 `00700.HK` 时 resolved == input，不触发）
  3. 回填读的是 `dims["0_basic"].get("data", {}).get("market", "A")`，但 fetch_basic 实际把 market 放在**顶层**（见 fetch_basic.py:80 `"market": ti.market`），不在 `.data` 里
- **影响**：所有 HK/US 直输 + 所有 resume cache 走 raw 的场景，`raw.market` 都被污染为 A
- **修法**：
  1. 入口用 `parse_ticker(ticker).market` 预填（非中文名即可拿到 H/U）
  2. post-fetch_basic 改为**无条件**从 `dims["0_basic"].get("market")` 顶层回填
  3. resume 从 cache 复用时也回填 `raw["market"]`
- **验证**：`python run.py 00700.HK --depth lite` Self-Review 显示 `(H)` ✅
- **回归测试**：`test_v2_10_4_fixes.py::test_raw_market_initialized_from_parse_ticker`
- **若未来改 collect_raw_data**：不能把 market 硬编码回 "A"；不能把顶层 market 改回读 `.data.market`；新增 resume 路径必须同步回填 market

### BUG · `resume` cache 对别名输入失效
- **症状**：用户用中文名 "贵州茅台" 或三位港股 "700" 输入时，`.cache/600519.SH/raw_data.json` 已存在也不命中缓存，重跑 Stage 1 耗时 + token 双爆
- **位置**：`run_real_test.py::collect_raw_data` 的 resume cache 加载块（line ~107-114）
- **根因**：注释写"尝试用原始 ticker 和 resolved ticker 都查"，实际只 `_read_cache(ticker, "raw_data")` 调了一次，发生在 fetch_basic 解析之前
- **影响**：别名输入下 resume 形同虚设；Codex 等 agent 环境反复耗 token 重跑
- **修法**：双重查询——先 `_read_cache(ticker)` 原样查；未命中 + 非中文名则 `_read_cache(parse_ticker(ticker).full)` 兜底
- **验证**：`python run.py 00700.HK`（cache 存在）→ 命中 15/15 维
- **回归测试**：`test_v2_10_4_fixes.py::test_resume_cache_tries_resolved_ticker`
- **若未来改 resume 路径**：不能移除 `parse_ticker.full` 兜底查询；中文名输入走 fetch_basic resolver 不在 resume 范畴内

### BUG · AGENTS.md 强制全量 agent 流程 · 抵消 CLI/lite 降载设计
- **症状**：v2.10.4/5 已把 `agent_analysis.json` 缺失降 warning 允许 CLI 直跑出报告，但 AGENTS.md 仍让 agent 看到"分析 XXX"就无条件 role-play 51 评委 + 写 agent_analysis.json，token 浪费
- **位置**：`AGENTS.md` Step 1-5 + `CLAUDE.md` "工作流" 段落
- **根因**：v2.10.4/5 是代码侧改，文档没同步更新
- **修法**：加"深浅两路径"决策树：
  - 快速路径（默认）：`python3 run.py <ticker> --depth lite/medium --no-browser` → 30s-4min 出完整报告，**不需要** role-play
  - 深度路径：仅当用户明确要 DCF / IC memo / 首次覆盖等深度产物时走两段式
- **若未来改 agent 流程**：run.py 的 CLI 直跑路径必须保持"缺 agent_analysis.json 降 warning 继续出 HTML"；文档里必须保留深浅两路径说明

---

## v2.10.5 (2026-04-18 · v2.10.4 遗漏补丁)

### BUG · `check_coverage_threshold` 非 profile-aware 阻塞 lite 出报告
- **症状**：`python run.py 600519.SH --depth lite --no-browser` 跑出 `coverage=17% (3/18)` → critical → `RuntimeError: BLOCKED by self-review`，HTML 生成失败
- **位置**：`skills/deep-analysis/scripts/lib/self_review.py::check_coverage_threshold:254`
- **根因**：分母用全 18 项 `CRITICAL_CHECKS`，lite 只启用 7 维，结构性偏低；CLI 直跑模式又没 agent 可补数据，critical 把流程卡死
- **影响**：任何 lite 模式 + 网络稍差的组合 → 报告 block
- **修法**：
  1. Profile-aware 分母：只算 `profile.fetchers_enabled` 里的 CRITICAL_CHECKS 项
  2. CLI-only/lite 模式下 `< 40%` 的 critical 降为 warning（允许继续出 HTML 供参考）
- **验证**：600519.SH lite → `critical=0 warning=2`，HTML 生成 ✅
- **回归测试**：
  - `test_v2_10_4_fixes.py::test_coverage_critical_downgrades_in_lite`
  - `test_v2_10_4_fixes.py::test_coverage_critical_preserved_in_medium`（回归护栏 · medium 仍 critical）
  - `test_v2_10_4_fixes.py::test_coverage_profile_aware_denominator`
- **若未来改 self_review**：分母必须读 profile，不能退回硬编码 18；CLI 模式下 critical 降级逻辑不能删

### BUG · `run.py` 直跑模式未自动标记 UZI_CLI_ONLY
- **症状**：`python run.py 002273.SZ --depth medium` → `agent_analysis` 缺失仍 critical → block HTML
- **位置**：`run.py::main()` 环境变量设置区
- **根因**：CLI 降级逻辑依赖 `UZI_DEPTH=lite / UZI_LITE=1 / UZI_CLI_ONLY=1 / CI=true` 四个信号；medium 模式都不命中
- **修法**：run.py main() 开头加 `os.environ.setdefault("UZI_CLI_ONLY", "1")` — run.py 是 CLI 直跑入口（agent 流程走 stage1/stage2 直接调用，不经 run.py）
- **验证**：002273.SZ medium → HTML 生成 ✅
- **回归测试**：`test_v2_10_4_fixes.py::test_run_py_sets_cli_only_env`
- **若未来改 run.py**：不能删 UZI_CLI_ONLY=1 setdefault；若新增 agent 专用入口必须另设标志区分

### BUG · `render_fund_managers` None 字段 TypeError
- **症状**：`TypeError: '>' not supported between instances of 'NoneType' and 'int'` in `assemble_report.py:1844`
- **位置**：`skills/deep-analysis/scripts/assemble_report.py::render_fund_managers`（5 处字段）
- **根因**：v2.10.2 fund_holders 双层策略（Top N full + rest lite）下，rest lite 基金的 `return_5y/annualized_5y/max_drawdown/sharpe/peer_rank_pct` 为 **显式 None**，但 `m.get("return_5y", 0)` 只处理 key 缺失、不处理值为 None
- **影响**：所有 lite + fund holders ≥ N+1 的场景 → 报告组装崩溃
- **修法**：`m.get("return_5y") or 0` 统一兜底（既处理缺失又处理 None）
- **验证**：`run.py 002273.SZ --depth medium` 正常生成 HTML
- **若未来改 fund_holders schema**：数值字段保持"None = 未计算"语义；新增数值字段 render 时必须用 `or 0` 不能用 `.get(k, 0)`

---

## v2.10.4 (2026-04-17 · Codex 测试反馈 3 bug)

### BUG · lite 模式与 self-review 冲突（9 critical 误报）
- **症状**：`UZI_DEPTH=lite` 跑完 gate 报 9 个 critical（维度缺失、data 为空）
- **位置**：`lib/self_review.py::check_all_dims_exist` + `::check_empty_dims`
- **根因**：硬编码检查全 20 维，不看 profile；lite 只启用 7 维，其余 13 维被误报 critical
- **修法**：两函数都读 `analysis_profile.get_profile().fetchers_enabled`，只检查启用的维度
- **回归测试**：
  - `test_check_all_dims_lite_respects_profile`
  - `test_check_empty_dims_lite_respects_profile`
  - `test_check_all_dims_medium_still_reports_missing`（护栏）
- **若未来加 self-review check**：新 check 涉及维度遍历必须 profile-aware

### BUG · agent_analysis.json 缺失在 CLI 直跑误报 critical
- **症状**：`python run.py` 直跑（无 agent 介入）必定 critical 阻止 HTML
- **位置**：`lib/self_review.py::check_agent_analysis_exists`
- **修法**：`UZI_DEPTH=lite / UZI_LITE=1 / UZI_CLI_ONLY=1 / CI=true` 任一命中 → 降 warning
- **回归测试**：`test_agent_analysis_missing_downgrades_in_lite` + `test_agent_analysis_missing_critical_in_medium`（护栏）
- **若未来改：** 正常两段式流程 agent_analysis 缺失仍是 critical，不能一刀切降级

### BUG · ETF 早退 RuntimeError（stage1 已识别非股，stage2 仍被调用）
- **症状**：`python run.py 512400.SH` → stage1 写 `_resolve_error.json` 识别为 ETF，但 `run_real_test.main()` 仍调 stage2 → `RuntimeError: Stage 2 缺少数据`
- **位置**：`run_real_test.py::main()` + `run.py::main()` 两处
- **修法**：
  1. `run_real_test.main()` 加 `status == "non_stock_security"` 分支，跳过 stage2 并 return
  2. `run.py::main()` 捕获 `run_analysis()` 返回的 `non_stock_security` dict，打印成分股提示后 `sys.exit(0)`
  3. 中文名输入路径同样捕获
- **回归测试**：`test_main_returns_early_on_non_stock_security` + `test_main_returns_early_on_name_not_resolved`
- **若未来加新的"非个股"类别**：早退 status 加到 `main()` 的分支列表里，不要让 stage2 被白白调用

---

## v2.8.3 (2026-04-17 critical · 行业分类碰撞错误)

### BUG#R10 · 申万行业被误映射到证监会"农副食品加工业"（严重）
- **症状**：用户分析云铝股份（000807.SZ），属于工业金属铝行业，但报告里 `7_industry` / `10_valuation` 两维都把它归类为**农副食品加工**
- **位置**：`fetch_industry.py::_cninfo_industry_metrics:90` + `fetch_valuation.py:122`
- **根因**：两处都用 `df["行业名称"].str.contains(industry_name[:2])` 做 fuzzy 匹配。证监会行业分类里含"工业"子串的有 4 个行业，其中农副食品加工业排第一，`iloc[0]` 盲选它
- **影响面**：所有带"工业 / 加工 / 制造"字样的申万行业（工业金属/工业母机/工业机械/工业气体 etc）全受影响；报告的 industry_pe、公司数量、行业景气度文本全是错的
- **修法**：新 `lib/industry_mapping.py`：
  1. `SW_TO_CSRC_INDUSTRY` 134 条申万 → 证监会硬映射
  2. `HIGH_COLLISION_TOKENS` 黑名单 12 个通用前缀
  3. `resolve_csrc_industry()` 4 策略解析：硬映射 → 整名子串 → 去前缀 fuzzy → 返 None
  4. **绝不再盲选 `iloc[0]`**，匹配不到明确返 None
- **验证**：云铝股份 → 工业金属 → 有色金属冶炼和压延加工业 PE 32.97 ✓
- **回归测试**：
  - `test_industry_mapping_blocks_high_collision_substring`
  - `test_resolve_csrc_industry_on_mock_df`（mock 6 个证监会行业，用工业金属查询必须选到有色金属加工业不能选到农副食品）
  - `test_fetch_industry_and_fetch_valuation_use_mapping`
- **若未来改 fetcher**：`resolve_csrc_industry` 是 single source of truth，不许退回裸 `str.contains(ind[:2])` pattern
- **若未来加新申万行业**：优先加到 SW_TO_CSRC_INDUSTRY 硬映射；不行再靠 fallback，不要用 iloc[0] 盲选

---

## v2.8.1 (2026-04-17 quotes expansion · 海外人物真实原话)

### 增强 · quotes-knowledge-base.md 补齐 22 位海外代表人物
- **动机**：v2.8.0 做完 investor_profile 后发现 quotes-knowledge-base（agent 必读语料）只覆盖中国投资者，海外 20+ 人物原话空白。用户："还有很多你要去找他们的言论，去找一下，收集一下"
- **方法**：4 个并行 research agent 按流派取证；严格要求真实可验证、不 fabricate
- **产出**：KB 306 → 639 行；人物 23 → 45；每人 3-5 条带 URL 原话
- **溯源标准**：优先原版书（Principles / Margin of Safety / One Up on Wall Street / Zero to One / Reminiscences）、官方年报（berkshirehathaway.com / oaktreecapital.com / ARK）、经过验证的 Goodreads / Farnam Street / 雪球 / WSJ / CNBC
- **发现的副作用**：`chengdu` 被写进 PROFILES 但 KB 把它归类为"席位集合体·无个人原话" → 移出 PROFILES 走 group F fallback（席位集合体不应冒充个人人物）
- **回归测试**：
  - `test_quotes_knowledge_base_covers_authored_personas`（每个 authored 必须在 KB 有段落）
  - `test_quotes_knowledge_base_has_source_urls`（抽查必须带 URL）
- **若未来改 investor_profile**：新增 authored 人物必须同步加 KB 段落，否则测试 fail
- **若未来改 KB**：不能删海外人物 URL（下游 agent 依赖可点击溯源）

---

## v2.8.0 (2026-04-17 persona profile · 因地制宜)

### 增强 · 每个评委用自己方法论回答 3 个问题
- **动机**：Codex 建议把评审升级成"流派 + 人物 + agent 写回"。实地审计发现这些 80% 已有；真正缺的是每个评委的 `time_horizon` / `position_sizing` / `what_would_change_my_mind`
- **关键原则**：**不是给所有人加 3 个同样的字段**，而是每人按自己方法论填 authentic 内容（Buffett 10 年 vs 赵老哥 T+2 vs Simons <2 天）
- **已落地**：`lib/investor_profile.py` 22 人手写 + 7 群 fallback
- **接入**：evaluator.evaluate / _skip_result / _unknown_result 三处返回 · generate_panel 写入 panel.json · assemble_report 新增「🧭 我的方法论」UI 区块
- **回归测试**：
  - `test_investor_profile_authentic_per_persona`（buffett/zhao_lg/simons 必须体现差异）
  - `test_investor_profile_group_fallback`（未注册投资者走 group fallback）
  - `test_evaluator_carries_profile_fields`
  - `test_panel_carries_profile_fields`
- **若未来加/改投资者**：不能把 authentic 人物换成 group fallback（退化）；新增投资者优先加到 PROFILES 而不是只塞进 investor_db
- **若改 panel 输出 schema**：不能删 3 个字段，报告 UI 已依赖

---

## v2.7.3 (2026-04-17 data-source expansion)

### 增强 · 权威域 site: 搜索 + 14 个 Codex 建议源
- **动机**：Codex 建议补"权威媒体 + 官方宏观 + 银行间利率 + 社区舆情"四块源
- **已落地**：14 个 DataSource（cnstock/cs_cn/stcn/nbd/pbc/safe/stats_gov/
  chinamoney/chinabond/ine/guba_em_list/jisilu/fx678/cmc）
- **核心机制**：`lib/web_search.py::search_trusted(query, dim_key=...)` 自动
  prepend `(site:d1 OR site:d2 ...)` 把 ddgs 限定在 dim 对应权威域白名单
- **接入 fetcher**：fetch_policy（全切）/ fetch_macro（部分）/
  fetch_events（权威+通用兜底）/ fetch_moat（权威+通用兜底）
- **不接入**：fetch_trap_signals（需要命中小红书/抖音风险信号，强制权威域
  反而漏；设计上保留现状）· fetch_sentiment（已有按平台 site: 设计）
- **回归测试**：`test_trusted_domains_covers_qualitative_dims` /
  `test_qualitative_fetchers_use_search_trusted` /
  `test_registry_contains_codex_authority_sources`
- **若未来改 web_search**：保持 TRUSTED_DOMAINS_BY_DIM 覆盖至少 5 个核心
  定性维度（3_macro/13_policy/15_events/14_moat/17_sentiment）
- **若未来改 registry**：cnstock/cs_cn/stcn/nbd/pbc/safe/stats_gov/chinabond/
  ine/guba_em_list 10 个权威源不得删除

---

## v2.7.2 (2026-04-17 hotfix)

### BUG#R7 · HK `1_financials` 永远空（stub 从未实现）
- **症状**：所有港股 `1_financials` 返回 `data={}`；ROE / 营收 / 净利 /
  毛利率 / 负债率 / ROIC 全缺；agent 盲评 → 报告完整性掉到 56%
- **位置**：`scripts/fetch_financials.py::main` HK 分支
- **根因**：旧代码 `else: data = {}`（HK 走这里），注释承认 "akshare has
  stock_financial_hk_abstract but field names differ" 但 stub 从未补上
- **修法**：新 `_fetch_hk(ti)` 调用 `ak.stock_financial_hk_analysis_indicator_em`，
  把 ROE_AVG / ROE_YEARLY / ROIC_YEARLY / OPERATE_INCOME / HOLDER_PROFIT /
  DEBT_ASSET_RATIO / CURRENT_RATIO / GROSS_PROFIT_RATIO + YoY 映射到 A 股
  一致的字段；额外保留 HK 特有 `eps` / `bps` / `currency`
- **验证**：`00700.HK` → `roe=21.1%` · `roe_history=[28.1, 29.8, 24.6, 15.1, 21.8, 21.1]` ·
  `revenue_history` 6 年亿元 · `financial_health` 完整
- **若未来改 fetch_financials**：HK 分支必须返回 ROE + 6 年历史，否则
  港股技术面/基本面评委全部盲评

### BUG#R8 · HK 2_kline 只有 1 条路径，GFW 一丢包就 0 根
- **症状**：港股 `kline_count=0`、`stage='—'`、所有技术指标 None；
  `ds.fetch_kline` 在东财 push2his 被代理丢包时直接失败无兜底
- **位置**：`scripts/lib/data_sources.py::_fetch_kline_impl` HK 分支
- **根因**：HK 只有 `ak.stock_hk_hist` 一条路径；A 股已有 6 路 fallback 链，
  但 HK 从未对齐
- **修法**：新 `_kline_hk_chain()` 三层 fallback：
  1. `ak.stock_hk_hist`（东财 push2）
  2. `ak.stock_hk_daily`（新浪, 返 5366 rows IPO-至今）
  3. `yfinance 0700.HK`（海外兜底；自动 `00700` → `700.HK`）
  所有路径返回结果归一到东财中文列（日期/开盘/收盘/最高/最低/成交量）
- **验证**：mock 东财失败后 Sina fallback 正常返 561 rows, stage='Stage 1 底部'
- **若未来改 HK kline**：必须保留至少 2 路以上 fallback；返回前归一到中文列

### BUG#R9 · Wave2 结束未 flush，timeout 标记会丢
- **症状**：跑完 465s 后 `raw_data.json` 里某维度**完全消失**（不是 OK 也不是
  timeout），agent 无法辨别"没跑过"还是"跑挂了"
- **位置**：`scripts/run_real_test.py::collect_raw_data` wave2 末尾
- **根因**：`_persist_progress()` 每 3 个 fetcher 落盘一次；wave2 整体 300s
  超时后把未完成 fetcher 标记 `_timeout=True` 写入 `dims` **仅在内存**；
  wave3 再跑 160s 期间若 Ctrl+C / crash，wave2 的 timeout 标记全丢
- **修法**：wave2 结束立即 `_persist_progress()` + stage1 收尾再 flush 一次。
  raw_data 始终反映最新完整状态。
- **若未来改 wave2/wave3**：任何新 wave 结束必须强制 flush，不要指望增量
  持久化覆盖 wave 结束的关键状态

---

## v2.7.1 (2026-04-17 hotfix)

### BUG#R5 · 19_contests xueqiu_cubes 全空（XueQiu 登录政策变化）
- **症状**：实盘比赛维度始终 0 个 cube，无任何雪球组合显示
- **根因**：`xueqiu.com/cubes/cubes_search.json` 2026 年起强制登录，HTTP 直访
  返 `400 + error_code: "400016"`（"遇到错误，请刷新页面或者重新登录"）
- **修法**：
  - 新 `lib/xueqiu_browser.py` Playwright + 持久化 cookie
  - `fetch_contests` HTTP fail → 检查 UZI_XQ_LOGIN → Playwright fallback
  - 未登录 → 透明标 `_login_required: True` + commentary 显示"⚠️ XueQiu 需登录"
  - run.py 加 `--enable-xueqiu-login` flag，README 说明登录步骤
- **回归测试**：`test_no_regressions.py::test_contests_login_required_marked`
- **若未来改 fetch_contests**：必须保留 `xueqiu_meta.login_required` 标记

### BUG#R6 · 18_trap signals 全 0（ddgs cache 残留）
- **症状**：杀猪盘 8 信号扫描永远命中 0/8（`signals_hit_count: 0`）
- **根因**：v2.6.1 之前 ddgs 未装时 `_ddg_search` 返 [] 被 cache 缓存了 12h；
  装 ddgs 后 cache 仍有效 → 永远返空
- **修法**：清 `.cache/_global/api_cache/ws__*.json` cache（一次性）
  + 改 `_auto_summarize_dim` 让 18_trap 显示 "已扫 ddgs 24 条搜索结果" 透明状态
- **若未来 lib/web_search 改依赖**：必须 bump cache_key_prefix 强制失效

---

## v2.7.0 (2026-04-17)

### BUG#R1 · `detect_style` 漏掉负 ROE 的困境股
- **症状**：ST 股（roe_5y_min < 0）被错判为 `small_speculative`（小盘投机），不是 `distressed`（困境反转）
- **位置**：`lib/stock_style.py:detect_style` 第 1 个判定分支
- **根因**：旧条件 `0 < roe_5y_min < 5` 排除了负值
- **修法**：改为 `roe_5y_min < 5`（去掉下界，允许负值）
- **回归测试**：`test_no_regressions.py::test_distressed_negative_roe`
- **若未来改 detect_style**：必须保留"负 ROE 也是困境"逻辑

### BUG#R2 · `fund_managers` 只显示 6 个（v2.4 修复后又出现的"假回归"）
- **症状**：报告里只显示 6 个基金经理，即便股票被几百家基金持有
- **位置**：`run_real_test.py:_fund_holders` 函数（wave3）
- **根因**：v2.4 把 `fetch_fund_holders.main()` 默认 limit 改成 None，但调用方
  `run_real_test.py:264` 一直写死 `limit=6` —— 修改 fetcher 默认值不会影响显式传参
- **修法**：把 `limit=6` 改为 `limit=None`
- **回归测试**：`test_no_regressions.py::test_fund_managers_no_cap`
- **若未来改 wave3 fetcher**：默认 limit 必须保持 None，render 端已支持 >6 紧凑展开

### BUG#R4 · fetch_fund_holders 并行调 akshare 触发 mini_racer V8 crash
- **症状**：Py3.13 macOS 跑 `fetch_fund_holders.main()` 默认 workers=3 → 致命 crash
  `Check failed: !pool->IsInitialized()`
- **根因**：v2.6 给 `_MINI_RACER_FETCHERS` 加了锁，但 fetch_fund_holders 不在
  wave2 列表里（它是 wave3 + 内部自己开 ThreadPoolExecutor）。其内部并行调
  `ak.fund_open_fund_info_em` 触发 mini_racer 同样问题。
- **修法**：fetch_fund_holders 默认 `UZI_FUND_WORKERS=1`（serial）；同样修
  `lib/quant_signal.py` 内部并发 → 默认 `UZI_QUANT_WORKERS=1`
- **若未来引入新模块调 akshare fund/portfolio 接口**：必须 default workers=1，
  或显式 import `_MINI_RACER_LOCK`

### BUG#R3 · 数据缺口 agent 没主动补齐就出报告
- **症状**：stage2 完成后直接发链接给用户，没检查 22 维定性 commentary 是否完整
- **位置**：原 SKILL.md 没有"输出前最后核查" 的 HARD-GATE
- **根因**：HARD-GATE-DATAGAPS 要求 agent 补数据，但没说"最后还要再核一遍"
- **修法**：新增 HARD-GATE-FINAL-CHECK，强制 agent 在发链接前打开 synthesis.json
  + raw_data.json 检查覆盖率 / commentary 完整性 / detected_style 合理性
- **若未来改 SKILL.md**：必须保留 FINAL-CHECK 这一节

---

## v2.6.1 (2026-04-17 hotfix)

### BUG · 直跑模式定性维度全空
- **症状**：浙江东方报告里宏观/政策/原材料/期货/事件 5 维 missing
- **根因 1**：`dim_commentary` 的 `dim_labels` 只覆盖 9/22 维
- **根因 2**：fallback 是 "[脚本占位]" 废话
- **根因 3**：`ddgs` 不在 requirements.txt（lib/web_search 静默返 0）
- **修法**：`_auto_summarize_dim` 全 22 维 + `_autofill_qualitative_via_mx` MX/ddgs 兜底 + 加 ddgs 到 requirements.txt
- **回归测试**：`test_no_regressions.py::test_22_dims_all_have_commentary`

---

## v2.6.0 (2026-04-17)

### BUG · KeyError 'skip'（论坛 #2）
- **位置**：`preview_with_mock.py:322`
- **根因**：`sig_dist = {"bullish": 0, "neutral": 0, "bearish": 0}` 漏 'skip' key
- **修法**：加 'skip' + 用 `.get()` 防御
- **回归测试**：`test_no_regressions.py::test_sig_dist_has_skip_key`

### BUG · per-fetcher hang 导致 pipeline 卡死（论坛 #11）
- **位置**：`run_real_test.py:collect_raw_data` ThreadPoolExecutor
- **根因**：`as_completed()` 没 timeout，单 fetcher 网络 hang 卡死整个流水线
- **修法**：`as_completed(futures, timeout=300)` + `fut.result(timeout=90)` + 长尾 fetcher 例外
- **若未来改 collect_raw_data**：必须保持双层 timeout

### BUG · OpenCode 跑到 60% 停止不能续（论坛 #9）
- **修法**：`collect_raw_data(resume=True)` 默认 + 增量保存 + `--no-resume` flag
- **若未来改 stage1**：resume 默认必须 True

### BUG · Python 3.9 `str | None` 语法报错（Codex blocker A）
- **修法**：所有新 .py 文件加 `from __future__ import annotations`
- **回归测试**：`test_no_regressions.py::test_all_modules_import_on_py39`

### BUG · mini_racer V8 thread crash on A 股（Codex blocker B）
- **位置**：`run_real_test.py:run_fetcher`
- **根因**：akshare 的 stock_industry_pe / stock_individual_fund_flow / stock_a_pe_and_pb
  内部用 mini_racer 解 JS 反爬，V8 isolate 不是 thread-safe
- **修法**：`_MINI_RACER_LOCK` 串行化这 3 个 fetcher
- **若未来加新 fetcher**：若它调用 mini_racer 相关 akshare 函数，必须加进 `_MINI_RACER_FETCHERS`

### BUG · 报告 banner 显示 v2.2（Codex blocker C）
- **修法**：`run.py:_get_version()` + `assemble_report.py:_get_plugin_version()` 动态读 plugin.json
- **若未来 bump 版本号**：只改 plugin.json 即可，banner 自动同步

### BUG · render_share_card / render_war_report 缺 main()（Codex blocker E）
- **修法**：`main = render` alias
- **若未来重命名函数**：必须保留 main alias

---

## v2.5.0 (2026-04-17)

### BUG · 港股 11 个 dim 全是 A-only stub
- **修法**：`lib/hk_data_sources.py` 解锁 50+ akshare HK 函数；HK 5 维（basic / peers / capital_flow / events + 原 kline）真实数据
- **若未来改 fetch_*.py**：HK 分支必须独立 try/except，不能让 HK 错误污染 A 股链路

---

## v2.4.0 (2026-04-17)

### BUG · 大佬抓作业 limit=50 截断
- **修法**：`fetch_fund_holders.main(limit=None)` 默认改无上限
- **回归**：v2.7 又因 wave3 调用层写死 `limit=6` 部分回归 → BUG#R2

### BUG · 6 维定性维度无方法论指引
- **修法**：`task2.5-qualitative-deep-dive.md` (~400 行) + HARD-GATE-QUALITATIVE
- **若未来改 SKILL.md**：必须保留 HARD-GATE-QUALITATIVE

### BUG · pip 直接挂掉无国内镜像 fallback
- **修法**：`run.py:check_dependencies` 4 级镜像 fallback
- **若未来改 dependencies**：保持 4 级 fallback 链

---

## v2.3.0 (2026-04-17)

### BUG · 中文名输错（"北部港湾" vs "北部湾港"）解析挂掉、22 fetcher 全炸
- **修法**：`lib/name_matcher.py` Levenshtein + `lib/mx_api.py` MX NLP 三层 fallback
- **若未来改 fetch_basic.py**：name_resolver 必须返回结构化 error，不能 fallback 当 ticker 用

### BUG · 关键字段缺失时 pipeline 不 abort 也不警示
- **修法**：`data_integrity.generate_recovery_tasks` + `_data_gaps.json` + HTML 橙色 banner
- **回归测试**：`test_no_regressions.py::test_data_gaps_banner_renders`

---

## 通用 Don't 清单（任何改动都不能违反）

1. ❌ `sig_dist` 字典少 `skip` key
2. ❌ `as_completed()` 不带 timeout
3. ❌ ThreadPoolExecutor 跑 mini_racer-using fetcher 不加锁
4. ❌ 改 fetcher 默认参数后忘记同步调用层
5. ❌ 加 fund 持仓数据流时硬编码 limit
6. ❌ `dim_commentary` 用 "[脚本占位]" 字符串而不是 raw_data 综合
7. ❌ 写 .py 文件用 `str | None` syntax 但忘 `from __future__ import annotations`
8. ❌ `run.py` banner 硬编码版本号
9. ❌ `lib/web_search` 改用其他依赖但不更新 requirements.txt
10. ❌ 把第一次 stage2 输出当最终报告（必须 agent FINAL-CHECK）

## 流程要求

- 每改 `lib/stock_style.py` 必须跑 `test_no_regressions.py::test_*_style*`
- 每改 `run_real_test.py` 必须跑 `test_no_regressions.py` 全套
- 每改 `lib/data_sources.py` `_fetch_basic_*` 必须 smoke test 三市场
- bump 版本号时 4 个 manifest（`.claude-plugin/`、`.cursor-plugin/`、`package.json`、`.version-bump.json`）必须同步

---

## File: docs/DATA-PROVIDERS.md

# 数据源 Providers 指南

UZI-Skill 采用多数据源 + 自动 failover 架构 (v2.10.3 起)。本文档列出**所有能接入**的 providers 及配置方法。

**v2.10.6**：providers chain 正式被 `data_sources._kline_a_share_chain` 调用（此前 0 采用）。诊断工具：
```bash
python -m lib.providers              # 看所有 provider 健康度
python -m lib.providers chain A      # 看 A 股每个维度的优先级链
UZI_PROVIDERS_KLINE=baostock,akshare python -m lib.providers chain A kline
```

## 优先级模型

每个 fetcher 请求时，按这个顺序尝试：

```
主源 akshare (0 key, 默认)
  ↓ 挂了
并行冗余 efinance (0 key, pip install efinance)
  ↓ 挂了
官方 API tushare (需 TUSHARE_TOKEN)
  ↓ 挂了
兜底 baostock (0 key, 已装)
```

可用 `UZI_PROVIDERS_<DIM>` 覆盖单维度优先级，如：
```bash
# 财务数据强制优先走 tushare（忽略 akshare）
export UZI_PROVIDERS_FINANCIALS=tushare,akshare
```

## 已实现的 Providers

### 🟢 `akshare` · 零配置主源

**状态**: 已随 `requirements.txt` 安装
**特色**: 覆盖最广（A/港/美股 + 基金 + 期货 + 宏观）
**限制**: 爬虫聚合，字段偶有不稳；GFW 代理时挂

### 🟢 `baostock` · 零配置兜底

**状态**: 已随 `requirements.txt` 安装
**特色**: 官方合作，A 股历史财报 + K 线稳定
**限制**: 仅 A 股 · 需要 `login()`

### 🟡 `efinance` · 零 key 并行冗余（推荐装）

**启用**:
```bash
pip install efinance
```

**特色**:
- 内部走东财 + 同花顺 + 新浪聚合爬虫
- 国内网络稳定性 ≥ akshare
- 覆盖 A/港/美股 + ETF + 基金 + 可转债
- 与 akshare 字段格式不同，作为失败冗余

**建议**: akshare 冷启动慢 / 代理不通时，efinance 常能救场

### 🟡 `tushare` · 官方 API（推荐注册）

**启用**:
```bash
# 1. 访问 https://tushare.pro 注册（免费 120 积分）
# 2. 个人中心 → 接口 TOKEN 复制
pip install tushare
export TUSHARE_TOKEN=your_token_here
```

**特色**:
- **字段质量最高** · ISO 清洗流程 · 官方授权
- **财报深度 5-10 年** · 利润表 / 资产负债表 / 现金流 三表齐全
- **机构级衍生数据** · 龙虎榜席位细节 / 北向实时 / 期货逐笔
- **国内网络稳定** · 走 tushare.pro HTTPS（不是 GFW 风险路径）

**限制**: 免费 120 积分够跑 50 次左右；常用接口需 2000+ 分（贡献数据 / 充值）

## 未实现但国内能用的 Providers

按 ROI 排，想加随时欢迎 PR：

### 零 key · 开源库

| Provider | pip 包 | 覆盖 | 亮点 |
|---|---|---|---|
| **adata** | `pip install adata` | A 股多源聚合 | 新兴开源，国内稳 |
| **AkTools HTTP** | `pip install aktools` | 同 akshare | HTTP 网关模式，可做远程共享 cache |
| **stockstats** | `pip install stockstats` | 技术指标 | 计算库，不是数据源 |

### 需要账号 · 券商 OpenAPI（免费）

| Provider | 账号条件 | 特色 |
|---|---|---|
| **富途 Futu OpenAPI** | 富途开户 | 港美股 + A 股实时 L2 盘口 |
| **长桥 Longbridge OpenAPI** | 长桥开户 | 港美股 · SDK 免费 |
| **老虎 Tiger API** | 老虎开户 | 美股为主 |
| **华泰 iQuant** | 华泰开户 | A 股量化环境 |

### 需要账号 · 研究平台（免费额度）

| Provider | 免费额度 | 特色 |
|---|---|---|
| **聚宽 JQData** | 100 次/日 | A 股研究，本地跑 |
| **米筐 RiceQuant** | 200 次/日 | 覆盖期货期权 |
| **QuantAxis** | 本地部署 | 量化框架 · 自建数据源 |

### 境外 · 美股专用（代理/VPN 才能用）

| Provider | 免费配额 | 特色 |
|---|---|---|
| **Financial Modeling Prep** | 250 次/日 | 美股 5 年财报 + 隐含 DCF |
| **Alpha Vantage** | 500 次/日 | 美股实时 + 技术指标 + 外汇 |
| **Polygon.io** | 5 次/分钟（历史） | 美股 tick-level |
| **OpenBB Platform** | 自身整合 10+ 源 | Python SDK `openbb` |
| **stooq.com** | 无限（直连 HTTP） | 美股/欧股日级历史 |

### 付费（不默认接，用户有授权时可自接）

Wind / 万得 · Choice · iFinD · Bloomberg · Refinitiv — 价钱不合理

## Health Check

查看当前所有 provider 的可用性：

```bash
cd skills/deep-analysis/scripts
python -c "from lib import providers; import json; print(json.dumps(providers.health_check(), ensure_ascii=False, indent=2))"
```

输出示例：
```json
{
  "akshare":  {"available": true,  "markets": ["A","H","U"], "requires_key": false, "status": "ok"},
  "efinance": {"available": false, "status": "unavailable"},
  "tushare":  {"available": false, "markets": ["A"], "requires_key": true, "status": "unavailable"},
  "baostock": {"available": true,  "markets": ["A"], "requires_key": false, "status": "ok"}
}
```

## 建议配置（国内用户）

**极简**（默认）：
```
akshare + baostock + DDGS
```

**标准**（推荐）：
```bash
pip install efinance
# akshare + efinance + baostock（+ DDGS）
```

**最稳**：
```bash
pip install efinance tushare
export TUSHARE_TOKEN=xxx
# 四层冗余，GFW 挂也能跑
```

**含境外**（需代理）：
```bash
pip install efinance tushare openbb
export TUSHARE_TOKEN=xxx
export FMP_API_KEY=xxx
```

---

## File: docs/serenity-research-dossier.md

# Serenity（@aleabitoreddit）研究方法 — 全网评价档案

> 整理日期 2026-06-03 · 用途：UZI-Skill 重磅角色底层资料
> 说明：本档案系全网公开来源二手整理。Serenity 身份与收益均为**自述/媒体转述、未经第三方审计**；文中所有收益数字均按「据某文称」逐条标注，**各来源数字互相矛盾，请勿当作事实采信**。

---

## 一、人物与方法论速览

Serenity（X handle **@aleabitoreddit**，Reddit 旧 ID **u/AleaBito**）是 2026 年爆火的海外散户研究员，使用二次元女性头像、匿名、不露脸、不接受采访、不卖课、不做付费跟单，研究全部免费公开。自述背景为**前 AI 研究科学家、Nature 论文作者、前 RISC-V 基金会成员、半导体与光通信工程师**，并称曾在英伟达股价约 $6 时拒绝其 AI 团队 offer（据 [Odaily](https://www.odaily.news/en/post/5210924)、[KuCoin](https://www.kucoin.com/news/insight/SOL/6a1bc052ea4a6c0007f96493)）。2022 年因在 r/WallStreetBets 发布 $AXTI 研究被版主以「拉高出货」为由永久封禁，遂转战 X，不到一年聚集 30 万+（部分文称 36 万/40 万/47.3 万）粉丝。

核心方法论是「**AI 产业链卡脖子/瓶颈点（Chokepoint Theory）**」投资法：不直接买 AI 龙头（英伟达等），而是自下而上逆向拆解供应链，找物理层面最难替代、被单一或极少数企业垄断、市场尚未定价的二三线上游小盘股，抢在市场定价前埋伏。代表战是提前约一年押中 InP 磷化铟衬底瓶颈股 **$AXTI（$12→$70+，后达 $115–140）**，2026 年 Q1 被 IntelliEPI CEO 公开印证「InP 短缺是整个 AI 基建的瓶颈」。争议集中在：从不公开认错、收益不可审计、选择性展示战绩、有「高智商学术包装的拉高出货」嫌疑、幸存者偏差、微盘流动性风险、技术单一路径依赖。

---

## 二、方法论拆解（综合多源）

### 1. 卡脖子/瓶颈点理论
- 核心信条：**避开英伟达等大盘龙头，去买它们必须依赖、却不为人知、垂直整合度极高的关键供应商**（据 [PANews 英文](https://www.panewslab.com/en/articles/019e7d30-5a0b-7721-8dfb-ff74096ba255)）。
- 标志性比喻：「**正如全球 20% 的石油要经过霍尔木兹海峡**」，AI 光通信基建同样依赖控制关键咽喉的单一供应商或双寡头（多源，含 [semiconstocks](https://semiconstocks.com/zh)、[singularityresearchfund](https://singularityresearchfund.substack.com/p/inside-the-mind-of-serenity-aleabitoreddit)）。其本人称 $AXTI 为「AXTI 海峡」。
- 「**紫苏叶理论**」（据 [PANews 对比文](https://www.panewslab.com/en/articles/019e69f3-28a3-72ca-9edc-409b4fbb4a50)）：寿司店里金枪鱼最贵，但特定农场的紫苏叶不可或缺——瞄准在关键制程上有「绝对技术垄断」的小盘制造商。
- 比喻为「河床最窄处」：无论上游水量多大，所有水都得挤过那个点（据 [capitalblueprint](https://capitalblueprint.substack.com/p/serenitys-axti-trade-when-a-tiny)）。

### 2. 五因子 / 多步研究流程（各文表述略异，合并如下）
据 [网易](https://www.163.com/dy/article/KU5K019B0556BWUL.html)、[BlockWeeks](https://blockweeks.com/view/238485)、[KuCoin](https://www.kucoin.com/news/insight/SOL/6a1bc052ea4a6c0007f96493) 等：
1. **确认确定性大趋势**（AI 算力扩张、光互连、CPO 等已被验证的超级趋势）；
2. **绘制产业链地图**，逐层向下解构（从英伟达 GPU 集群层层往上游拆）；
3. **识别真正瓶颈**——难扩产、难替代、被垄断的环节（第一层瓶颈→再找第二、第三层瓶颈）；
4. **寻找证据链支持**（订单、认证、产能、Capex、补贴数据）；
5. **等催化剂后下注**（财报、量产、政策、出口管制等）。

[KuCoin](https://www.kucoin.com/news/insight/SOL/6a1bc052ea4a6c0007f96493) 把它概括为「**贝叶斯框架**」：先验研究（读论文+拆 BOM 建立初始概率）→ 不确定下行动 → 证据更新（尽调验证、加减仓）→ 动态调仓（把资金转向「瓶颈确定性」最高的标的）。
[GitHub serenity-reply](https://github.com/leslieyeo/serenity-reply/tree/main) 蒸馏出 **5 大心智模型 + 8 条决策准则**，其中含「读英伟达信号（追踪其合作与投资）」「优先欧洲小盘」「反 meme 股标签」「机构验证确认」「反杠杆铁律」「DYOR 验证」「地缘政治折价评估」。

> 注意：「反杠杆铁律」（GitHub 框架）与多篇文称其实际「1.3–1.4 倍杠杆操作」相互矛盾，应视为其口头主张 vs 实际操作的出入。

### 3. 供应链拆解的具体维度
- **拆解光通信链为 7 层**（据 [PANews 英文](https://www.panewslab.com/en/articles/019e7d30-5a0b-7721-8dfb-ff74096ba255)）：原材料（镓/铟/砷）→ 设备（热解氮化硼坩埚）→ InP 衬底加工（「最受限环节」）→ 连续波激光器（CPO 光源）→ 光模块组装 → 测试/验证 → 光缆光纤。
- **CPO 硅光五大物理壁垒**（据 [PANews 中文](https://www.panewslab.com/zh/articles/019e674b-724f-736c-8077-b2221cf24e39)）：光纤阵列单元 FAU/微透镜→FOCI(台湾)；外部光源/DFB 激光器→SIVE(瑞典)；分子束外延 MBE 设备→Riber(法国)；高纯度红磷(6N-7N)→日本化学工业；SOI 衬底→Soitec(法国)。
- **不看财报，看产业链节奏信号**（据 [PANews 英文-不看财报那代](https://www.panewslab.com/en/articles/019e6cf0-c247-707e-b151-dce5f3c3f3c3)）：不算 ROE/现金流/负债率，而是看「**财报电话会措辞、客户认证周期、供应链节奏、上游材料是否被垄断、技术是否从论文走向量产**」。
- **对抗性 AI 论证**：用 AI（Gemini）扮演「魔鬼代言人」反复挑战自身逻辑（多源）。
- **多维整合**：技术专利壁垒 + 地缘政治风险 + 出口管制政策，构建「卡脖子物理+地缘地图」（[PANews 中文](https://www.panewslab.com/zh/articles/019e674b-724f-736c-8077-b2221cf24e39)）。

### 4. 选股口径
- **只看 AI 链**：光模块、硅光子、存储、CPO、化合物半导体、衬底材料。
- **只找二三线瓶颈**：「隐形冠军」「沉默的齿轮」，机构盲区。
- **只选中小/微市值**：AXTI 建仓时市值仅约 $2 亿、股价约 $12。公开点过 23–38 个标的（各文数字不一）。

---

## 三、全网来源逐条（核心交付）

### 中文来源

#### 1. 知乎《5个月32倍，一个超级散户的AI投资方法论》—— [zhuanlan.zhihu.com/p/2039045698369361597](https://zhuanlan.zhihu.com/p/2039045698369361597)
- 核心：标题即给出「**据该文称 5 个月 32 倍**」的战绩框定，系中文圈深度拆解其方法论的代表帖之一。（注：本次抓取时该页返回 403，内容据搜索摘要与标题，未能逐字校验。）
- 评价：标题导向正面（「超级散户」「方法论」），属推崇向。

#### 2. 知乎《Serenity 一个跑赢机构的海外散户，把美股AI牛市拆成了一张供应链瓶颈地图》—— [zhuanlan.zhihu.com/p/2039362476144341796](https://zhuanlan.zhihu.com/p/2039362476144341796)
- 核心：把其方法形象化为「**一张供应链瓶颈地图**」，强调跑赢机构。（抓取返回 403，据搜索结果收录标题与定位。）
- 评价：正面，突出「降维打击机构」。

#### 3. 知乎问答《如何看待最近推特爆火的散户 Serenity?》—— [zhihu.com/question/2043660509396865080](https://www.zhihu.com/question/2043660509396865080)（含[「玩转A股量化」回答](https://www.zhihu.com/question/2043660509396865080/answer/2044184997146400599)）
- 核心：中文圈最集中的**质疑场**。网友指出：其自述背景（前 AI 科学家、Nature 作者、RISC-V 成员）**无法核实**；**从不公开认错**，应对质疑三板斧——「甩更大收益数字 / 嘲讽对方持仓 / 说对方不懂」；雪球等社区已有人**抄作业并借其名义卖课**。
- 评价：**质疑为主**。幸存者偏差、收益不可验证、追随资金推高价格。

#### 4. 区块周刊 BlockWeeks《一文拆解"股神 Serenity"投资方法论》—— [blockweeks.com/view/238485](https://blockweeks.com/view/238485)
- 核心：系统给出**五因子模型**（确定需求/受限供给/低关注度/价值捕获/催化剂）与「先定价大叙事→再定价二级供应商→最后才意识到真正短缺的材料」逻辑。据该文称 **YTD 4502.45%**，公开 25 标的涨幅 100%–1000%。
- 评价：**正反兼具**。正面：逻辑严密、信息拼图能力强。质疑：推断易过拟合、难辨信号噪音、早期财报差缺估值锚、已成市场变量（追随资金推高价）、幸存者偏差。

#### 5. 网易《YTD暴赚4500%后，爆火股神Serenity真正厉害的，不是选股》—— [163.com/dy/article/KU5K019B0556BWUL.html](https://www.163.com/dy/article/KU5K019B0556BWUL.html)
- 核心：据该文称 **YTD 4502.45%**；强调「真正厉害的不是选股，是方法论」——确认趋势→画产业链地图→识别瓶颈→找证据→等催化剂；持仓含 AXTI、AAOI、LITE、RPI、SIVE、XFAB。（抓取返回 403，内容据搜索摘要。）
- 评价：**偏正面**，但点出「赌未来技术路线，若 CPO 延期/替代技术/巨头自研，逻辑可能全面崩塌」的风险。

#### 6. PANews 中文《2年225倍收益？揭秘神秘研究员 Serenity 的 AI"卡脖子"投资术》—— [panewslab.com/zh/articles/019e674b...](https://www.panewslab.com/zh/articles/019e674b-724f-736c-8077-b2221cf24e39)
- 核心：详述 WSB 永封→转 X 改名经过；自下而上逆向工程；CPO 五大物理壁垒（FOCI/SIVE/Riber/日本化学/Soitec）；对抗性 AI 论证。据该文称 **2 年 22,561.99%**。代表战 AXTI 市值约 $2 亿、股价约 $12，后飙至 $70，「超 1000% 浮盈」「立名之战」。
- 评价：**正反兼具**。正面：「用极客深度降维打击金融广度」「解构系统找沉默齿轮」。质疑：微盘流动性枯竭/踩踏风险；资深空头批其为「**带高智商学术包装的拉高出货**」；单一技术路径依赖（若英伟达转向薄膜铜缆，「整个帝国瞬间瓦解」）；背景均自述未经验证。

#### 7. PANews《散户"带头大哥"Serenity vs 新晋股神 Leopold》—— [panewslab.com/en/articles/019e69f3...](https://www.panewslab.com/en/articles/019e69f3-28a3-72ca-9edc-409b4fbb4a50)
- 核心：以「紫苏叶理论」概括 Serenity（瞄准关键制程绝对垄断的小盘）；与 Leopold 对比——Serenity 攻微观元件瓶颈、战绩未经核实，Leopold 管 $100 亿已验证资本、攻宏观基建约束。
- 评价：**正反兼具**。正面：精准技术分析、押中 AXTI $12→$70。质疑：微盘低流动性易急跌、身份/背景/历史业绩未经核实、标的高 Capex 薄利润+客户流失风险。

#### 8. HTX 行情资讯《Who is Serenity》—— [htx.com/news/Trading-Dsb4qeq8/](https://www.htx.com/news/Trading-Dsb4qeq8/)
- 核心：自述拒绝英伟达 2018 offer；流程为先验论文研究→落地交易计划→后续尽调→上涨庆祝；常用 AI 协助拆产业链挖供应商。据该文称 **2026 YTD 3840.39%、过去 2 年 2256.99%**，21 只主力持股均盈利 >100%；AXTI 称「利润超 10,000%」。
- 评价：**正反兼具**。正面：机构入场前发现标的、「最热股票预测之王」。质疑：只公布百分比不公布实际头寸、疑收益造假或涉微盘价格操纵；本人回应称免费分享是「信息民主化」，文末坦言「目前我们不知道答案」。

#### 9. 财联社 / cn.investing.com《社媒帖子点燃欧洲半导体妖股 晶圆厂股票突遭资金疯抢》—— [cn.investing.com/news/stock-market-news/article-3388643](https://cn.investing.com/news/stock-market-news/article-3388643)
- 核心：其帖子直接点燃 **X-FAB**（盘中暴涨 77% 至 €15.88、收涨 33.56%、成交量约均量 17 倍、数次熔断），并带动 Raspberry Pi、Soitec、Sivers。单帖 >75 万浏览，X-FAB 成 Boursorama 论坛最热股，收盘价已是分析师均价 €5.5 的两倍多。
- 评价：**质疑/风险为主（含反身性实锤）**。Berenberg 下调买入→持有建议获利了结；Bernstein 给「与大盘持平」称业绩能见度有限；**X-FAB CEO 表示并不知道任何需披露的重大未公开事项**——暗示上涨缺基本面支撑。

#### 10. semiconstocks.com（Serenity Tracker 中文）—— [semiconstocks.com/zh](https://semiconstocks.com/zh)
- 核心：第三方追踪其公开持仓与论点。据该页：**1 年 +122%（2026 年 4 月自报）、YTD 峰值 +501% 后回撤、约 1.4 倍杠杆**；追踪 AXTI/SIVE/AAOI/AEHR，「多个标的首次提及后涨 100–400%」；列两处「验证」：AXTI 被 IntelliEPI CEO Q1 2026 印证、RPI 预测营收 +55% 实际 +58%（共识仅 +14%）。
- 评价：**中性偏审慎**。明确提示：无监管披露义务、过往业绩存幸存者偏差、持仓可能随时无预警变动。

#### 11. moomoo 社区《@aleabitoreddit 背景起底》—— [moomoo.com/community/feed/...116401268326405](https://www.moomoo.com/community/feed/here-s-the-backstory-on-the-twitter-account-aleabitoreddit-x-116401268326405)
- 核心：复述 WSB 封禁（其归因于版主不满散户获利）、RISC-V/Nature 自述、AXTI $12→$70 传奇、从 meme 交易转向供应链瓶颈长文。据该帖称 **YTD +1,116%**。
- 评价：**正反兼具**。正面：高透明度分享论点细节。质疑：波动风险、社区炒作放大、过往 AI 牛市顺风未必保证未来。

### 英文来源

#### 12. PANews 英文《Who is Serenity? Godfather of AI Supply Chain》—— [panewslab.com/en/articles/019e7d30...](https://www.panewslab.com/en/articles/019e7d30-5a0b-7721-8dfb-ff74096ba255)
- 核心：47.3 万粉丝；霍尔木兹海峡比喻；光通信链拆 7 层（InP 衬底为「最受限环节」）；AXTI ~$96M TTM 营收、仍亏损却交易于 $115.70；提前约一年于 IntelliEPI CEO Q1 2026 公开承认前识别 InP 瓶颈。据该文称 **年化 +122%、YTD 峰值 +501% 后回撤**；公开 38+ 标的（光通信/存储/AI 云/能源/加密/杠杆反向 ETF）。
- 评价：**正反兼具**。正面：能读懂衬底级技术文献、机构入场前布局。质疑：收益「自发布、缺独立验证」、头寸规模未知、无审计；有人认为他「**只是个未能验证收益的牛市幸存者**」。

#### 13. PANews 英文《This generation of gurus no longer look at financial statements》—— [panewslab.com/en/articles/019e6cf0...](https://www.panewslab.com/en/articles/019e6cf0-c247-707e-b151-dce5f3c3f3c3)
- 核心：以 Serenity 为「**不看财报新一代**」典型——绕开资产负债表，盯财报电话会措辞、客户认证周期、供应链节奏、上游是否被垄断、技术是否量产。AXTI 目标 $15→$150，后达约 $140.83（~1000%）。
- 评价：**风险为主**。结构性脆弱：订单不确定+客户集中、验证周期长、低流动性可能出现「meme 币式」叙事仍在但买盘蒸发、信息差在主流报道后被抹平。

#### 14. Substack · singularityresearchfund《Inside the Mind of Serenity》—— [singularityresearchfund.substack.com/p/inside-the-mind-of-serenity...](https://singularityresearchfund.substack.com/p/inside-the-mind-of-serenity-aleabitoreddit)
- 核心：深挖其思维：瓶颈映射、等财报验证胜过追噪音（「got tired of all the noise so just waited for earnings to validate my thesis」）、二三阶效应、用 AI 压力测试；AXTI「一条供应链下垂直整合 4 个 chokepoint」；据该文称 **YTD 峰值 +501.24%**，约 1.4 倍杠杆集中持仓。
- 评价：**正反兼具**。承认「拉小盘」指控，但举其已验证战绩（光子轮动、AXTI 卡点价值早于机构共识）；其本人对批评者态度好斗。

#### 15. Substack · capitalblueprint《Serenity's AXTI Trade — When a Tiny...》—— [capitalblueprint.substack.com/p/serenitys-axti-trade-when-a-tiny](https://capitalblueprint.substack.com/p/serenitys-axti-trade-when-a-tiny)
- 核心：复盘 AXTI——逆向问「系统在哪断裂」而非「谁在赢」；AXTI 与 Sumitomo 近双寡头、替代难；映射「美国 InP→瑞典激光→台湾 PCB→日本玻纤→韩国 HBM」。据该文称 **YTD 3,152.77%（≈5 个月 32 倍）**，AXTI 单票一处引「8436%」谷峰值；>70% 未实现、1.3–1.4x 杠杆、无审计。
- 评价：**最均衡的批判**。正面：瓶颈框架智识自洽、反映真实供给约束（黄仁勋亦认可）。质疑：单边牛市 5 个月**无法验证可复制性**；30 万粉造成**反身性（其帖本身推动股价）**；高集中+杠杆+微盘流动性=「天生爆炸性」下行风险；无审计/无头寸权重披露。**结论金句：可借鉴的是方法论而非收益神话——「供应链瓶颈思维是共享工具，不是个人传奇」。**

#### 16. Futu(futunn)《Who is Serenity? Investment logic of the Godfather》—— [news.futunn.com/en/post/73854711/...](https://news.futunn.com/en/post/73854711/who-is-serenity-understanding-the-investment-logic-of-the-godfather)
- 核心：标题定位「AI 供应链教父」，介绍其瓶颈投资逻辑。（抓取时正文为空/未加载，仅收录标题与定位，内容与 PANews 同源系列高度重合。）
- 评价：偏推崇向（「教父」称谓）。

#### 17. Odaily 英文《Refusing an NVIDIA offer at a $6 stock price》—— [odaily.news/en/post/5210924](https://www.odaily.news/en/post/5210924)
- 核心：拒绝英伟达 offer 故事；自述 AI 半导体产业链研究员/Nature/RISC-V；逆向从英伟达回溯找稀缺材料与垄断供应商。战绩举例：AXTI（1000% 未实现）、RPI（2026.2 看多致两日 +90%，实际营收 +58% vs 其预测 +55% vs 共识 +14%）、SIVE 单日 +73.78%、Soitec +16%。据该文称 **上 X 前 630% 年化、今年 YTD 一度 >500%**，均未经审计。
- 评价：**质疑为主**。顾虑：完全匿名、背景未核实、自报业绩、持仓集中于低流动性微盘、15 万跟单文化致散户高位接盘风险；但肯定其目前不卖课/不收费、核心研究公开。

#### 18. KuCoin《Who is Serenity @aleabitoreddit?》—— [kucoin.com/news/insight/SOL/6a1bc052...](https://www.kucoin.com/news/insight/SOL/6a1bc052ea4a6c0007f96493)
- 核心：贝叶斯四步框架（先验研究→不确定下行动→证据更新→动态配置）。据该文称 **2025 下半年 630%、2026 至 5/28 达 4,502.45%（45x）、2 年累计 225x**；35 只荐股仅 4 只下跌，其余涨 1–19.6x。
- 评价：**纯正面**，全文无批评或反方观点（应警惕其立场）。

#### 19. GitHub · leslieyeo/serenity-reply（蒸馏思维框架）—— [github.com/leslieyeo/serenity-reply](https://github.com/leslieyeo/serenity-reply/tree/main)
- 核心：基于 1700+ 推文与三方分析蒸馏出 **5 大心智模型 + 8 条决策准则 + 研究流程**（含读英伟达信号、优先欧洲小盘、反 meme 标签、机构验证、反杠杆铁律、DYOR、地缘折价）。
- 评价：方法论复刻向工具，**透明列出信息源以便核验**；本身不评判收益真伪。

#### 20. Serenity 本人 X 帖（一手）—— [x.com/aleabitoreddit](https://x.com/aleabitoreddit)
- 关键帖：① [$AXTI $150 PT from $12-15…YTD over 600%…identify the biggest chokepoints before anyone else](https://x.com/aleabitoreddit/status/2036089423990042630)；② [InP Chokepoint 解释：NVDA/META/GOOGL/MSFT 量产系于 $700M 小盘 AXTI 与 SMTOY](https://x.com/aleabitoreddit/status/2004936335702753729)；③ [IntelliEPI CEO 印证我去年的 InP 瓶颈判断](https://x.com/aleabitoreddit/status/2053270948414099915)；④ [发帖 6 周 AXTI 再涨 60%，从 $700M 变 $1.4B 上机构与政府雷达](https://x.com/aleabitoreddit/status/2019946788233617526)；⑤ [回应抄作业者：别盲目跟我买 AXTI/LPK，这就是我不做跟单 app、不告诉别人何时卖的原因](https://x.com/aleabitoreddit/status/2046749502326071427)。
- 评价：一手语料。对批评态度好斗（「多数人根本不懂自己在说什么」）；但确有主动劝阻盲目跟单的表态。

---

## 四、正面评价汇总

1. **方法论智识自洽**：瓶颈/卡脖子框架反映真实供给约束，连黄仁勋都公开认可 InP 等约束（capitalblueprint、singularityresearchfund）。
2. **技术降维打击**：能读懂衬底级/材料科学文献，深入散户与多数分析师不碰的层面，「用极客深度打击金融广度」（PANews、moomoo）。
3. **抢跑定价能力**：机构入场前布局——AXTI 早于 IntelliEPI CEO 公开承认约一年、RPI 营收预测 +55% 精准命中 +58%（semiconstocks、Odaily）。
4. **验证链思维**：等财报/认证/量产验证胜过追噪音，二三阶效应推演，用 AI 做对抗性论证（singularityresearchfund、HTX）。
5. **供应链拆解系统化**：7 层光通信链、CPO 五大物理壁垒、跨国地缘地图，可被结构化复刻（GitHub、PANews）。
6. **透明且免费**：不卖课、不收费、不做付费跟单、研究公开，并主动劝阻盲目跟单（Odaily、本人 X 帖）。

## 五、质疑与风险汇总

1. **身份背景全凭自述、无第三方验证**：前 AI 科学家/Nature/RISC-V/拒绝英伟达 offer 均无法核实（几乎所有批评向来源）。
2. **收益不可审计且数字互相矛盾**：YTD 见 +501% / +600% / 1,116% / 3,152.77% / 3,840.39% / 4,502.45%，2 年见 225x / 22,561.99%——只报百分比不报实际头寸与权重，>70% 未实现（capitalblueprint、HTX、知乎问答）。
3. **从不公开认错**：应对质疑三板斧「甩更大数字 / 嘲讽对方持仓 / 说对方不懂」（知乎问答）。
4. **「高智商学术包装的拉高出货」嫌疑**：资深空头直指其本质为 pump；其单帖即令 X-FAB 暴涨 77%、X-FAB CEO 称不知任何需披露重大事项（PANews 中文、财联社）。
5. **反身性陷阱**：30 万+ 粉丝使其帖子本身推动股价，「自我实现」后高位接盘风险转嫁散户（capitalblueprint、Odaily）。
6. **幸存者偏差 + 单边牛市**：5 个月单边 AI 牛市无法证明方法可复制；赢家发声、输家沉默（capitalblueprint、BlockWeeks）。
7. **微盘流动性风险**：标的日均成交极低，浮盈未必能真实兑现，可能出现「meme 币式」叙事仍在但买盘蒸发（PANews、Odaily）。
8. **估值纪律缺失**：以 85x 营收倍数推荐小公司，早期财报差、缺有效估值锚（知乎、BlockWeeks）。
9. **技术单一路径依赖**：整个「帝国」建立在「CPO 是唯一演进路线」假设上，若英伟达转向薄膜铜缆/巨头自研/CPO 延期，逻辑可能瞬间瓦解（PANews 中文、网易）。
10. **言行不一处**：口头主张「反杠杆铁律」却被多文记为 1.3–1.4x 杠杆操作（GitHub vs capitalblueprint 等）。

## 六、结论：方法论可借鉴之处 vs 不可复制之处

**可借鉴（工具层面，多源共识）**
- 「**供应链瓶颈思维是共享工具，不是个人传奇**」（capitalblueprint）——自下而上逆向拆解、定位物理不可替代+垄断+市场未定价的咽喉环节，是一套可学习、可结构化（见 GitHub 框架）的研究范式。
- **验证链/抢跑定价**：用财报电话会措辞、客户认证周期、Capex/扩产/补贴/招聘/专利等「软信号」，在主流报道抹平信息差之前建立判断——这套信号体系本身有方法论价值。
- **对抗性论证 + 二三阶效应**：主动用 AI 当魔鬼代言人压测自身逻辑，是值得复制的纪律。

**不可复制（个体与环境层面）**
- **收益数字不可复制也不可采信**：未经审计、互相矛盾、含巨大未实现浮盈与杠杆，且高度依赖 2025–2026 单边 AI 牛市顺风（幸存者偏差）。
- **反身性是其独有优势同时是散户的陷阱**：其 30 万+ 影响力本身能推动股价，跟随者无此势能，只能高位接盘——这部分「战绩」不可迁移。
- **微盘流动性与单一技术路径风险不可外包**：浮盈兑现难、CPO 路线一旦被证伪「帝国瞬间瓦解」，跟单者承担的尾部风险远大于其展示的收益。

**一句话**：把 Serenity 当作「**一套值得拆解学习的供应链瓶颈研究方法**」来研究是合理的；把他当作「**收益可复制的导师/跟单对象**」来追随是危险的。

---

## File: hooks/README.md

# Hooks · v2.6

## 文件清单

| 文件 | 用途 |
|---|---|
| `hooks.json` | Claude Code 的 SessionStart hook 配置（直接调 session-start，不走 polyglot） |
| `session-start` | bash 脚本 · 在 SessionStart 时打印 skill 列表 + 工作流提醒，输出到 Claude additionalContext |
| `hooks-cursor.json` | Cursor IDE 用的 hook 配置（保留兼容） |
| `run-hook.cmd` | 历史 polyglot bash/batch 调度器（v2.6 起 hooks.json 已不再用，仅保留供外部调用） |

## 安装

`session-start` 需要可执行权限。`setup.sh` 自动做了：

```bash
chmod +x hooks/session-start hooks/run-hook.cmd
```

如果手动 clone：

```bash
chmod +x hooks/session-start
```

## v2.6 论坛 bug 修复说明

论坛反馈 "Claude plugin 执行不了"，原因是旧版 `hooks.json` 调用
`run-hook.cmd` 中转脚本，而 `.cmd` 在 macOS Claude Code 安全策略下：
1. 权限检查未通过（Claude Code 可能拒绝执行 `.cmd` 后缀脚本）
2. polyglot bash/batch 用 `: <<'BATCH_SCRIPT'` heredoc 体操，对解释器有要求

v2.6 修复：`hooks.json` 改为**直接调** `session-start`（标准 sh 脚本，已有 shebang），
跳过 `run-hook.cmd` 中间层。Windows 用户若依赖该中转，仍可手动调用。

## 调试

若 SessionStart 没有触发：

1. 确认 `session-start` 有 `+x` 权限：`ls -l hooks/session-start`
2. 手动测试输出：`./hooks/session-start | head -5`（应看到 JSON 含 `additionalContext`）
3. 检查 Claude Code 日志（`Cmd+Shift+P → Developer: Open Logs`）
4. 若 Claude Code 报路径错误，确认 `${CLAUDE_PLUGIN_ROOT}` 正确解析

---

## File: skills/deep-analysis/references/fin-methods/README.md

# 机构级财务分析方法库

> 改编自 `anthropics/financial-services-plugins`，适配 A 股 / 港股 / 美股散户深度分析场景。

本目录记录 22 种机构级分析方法的**方法论**与**A 股落地参数**（18 种核心 + 5 个 Tier-1 续引入，见文末）。每种方法都有对应的 Python 计算模块：

| 方法 | Python 模块 | 源 SKILL |
|---|---|---|
| DCF 估值 | `lib/fin_models.py :: compute_dcf` | financial-analysis/dcf-model |
| Comps 相对估值 | `lib/fin_models.py :: build_comps_table` | financial-analysis/comps-analysis |
| 三表预测 | `lib/fin_models.py :: project_three_stmt` | financial-analysis/3-statement-model |
| LBO 快速测试 | `lib/fin_models.py :: quick_lbo` | financial-analysis/lbo-model |
| 并购增厚/摊薄 | `lib/fin_models.py :: accretion_dilution` | investment-banking/merger-model |
| Porter 5 Forces + BCG | `lib/deep_analysis_methods.py :: build_competitive_analysis` | financial-analysis/competitive-analysis |
| 首次覆盖报告 | `lib/research_workflow.py :: build_initiating_coverage` | equity-research/initiating-coverage |
| 财报业绩解读 | `lib/research_workflow.py :: build_earnings_analysis` | equity-research/earnings-analysis |
| 催化剂日历 | `lib/research_workflow.py :: build_catalyst_calendar` | equity-research/catalyst-calendar |
| 投资逻辑追踪 | `lib/research_workflow.py :: build_thesis_tracker` | equity-research/thesis-tracker |
| 晨报 | `lib/research_workflow.py :: build_morning_note` | equity-research/morning-note |
| 量化选股筛选 | `lib/research_workflow.py :: run_idea_screen` | equity-research/idea-generation |
| 行业综述 | `lib/research_workflow.py :: build_sector_overview` | equity-research/sector-overview |
| 投委会备忘录 | `lib/deep_analysis_methods.py :: build_ic_memo` | private-equity/ic-memo |
| 单位经济 | `lib/deep_analysis_methods.py :: build_unit_economics` | private-equity/unit-economics |
| 价值创造计划 | `lib/deep_analysis_methods.py :: build_value_creation_plan` | private-equity/value-creation-plan |
| 尽调清单 | `lib/deep_analysis_methods.py :: build_dd_checklist` | private-equity/dd-checklist |
| 组合再平衡（大类配置） | `lib/deep_analysis_methods.py :: build_portfolio_rebalance` | wealth-management/portfolio-rebalance |

## Tier-1 续引入（v3.8.0 · `lib/tier1/` 包）

> 2026-06-04 第二批从 `anthropics/financial-services` 续引入的 5 个与个股研究强相关的方法，
> 各自有 slash 命令（`commands/`）+ 方法论文档（本目录）+ 纯函数模块（`lib/tier1/`）+ pytest。

| 方法 | Python 模块 | 命令 | 源 SKILL |
|---|---|---|---|
| AI 就绪度/卡位评估 | `lib/tier1/ai_readiness.py :: build_ai_readiness` | `/ai-readiness` | private-equity/ai-readiness（适配单票 + 复用 `ai_chokepoint_score`） |
| 财报前预览 | `lib/tier1/earnings_preview.py :: build_earnings_preview` | `/earnings-preview` | equity-research/earnings-preview |
| 模型增量更新 | `lib/tier1/model_update.py :: build_model_update` | `/model-update` | equity-research/model-update |
| 组合收益归因 | `lib/tier1/returns_attrib.py :: build_returns_attribution` | `/returns` | private-equity/returns-analysis（适配二级市场组合） |
| 组合再平衡（逐持仓+换手成本） | `lib/tier1/rebalance.py :: build_rebalance` | `/rebalance` | wealth-management/portfolio-rebalance（A 股适配：去 TLH + 印花税/佣金本地化） |

说明：`/rebalance`（逐持仓 + A 股印花税/佣金换手成本）与既有 `build_portfolio_rebalance`（资产大类配置漂移）分工互补，前者出**调仓交易清单**、后者看**大类配置偏离**。

## 设计原则（全部来自原 SKILL.md 的 CRITICAL CONSTRAINTS）

1. **公式 over 硬编码**：所有派生值都是"函数调用"，不是预计算的数字。改变假设，全链条联动更新。
2. **Step-by-step 可审计**：每个模块返回的 dict 里都带 `methodology_log`，完整记录"每一步在算什么"。
3. **敏感性内置**：DCF 强制 5×5 敏感性表，中心格必须等于基础案例的每股内在价值（自检机制）。
4. **数据源优先级**：真数据 > 代理估算 > 默认值。所有默认值都显式标记为 DEFAULT_*。
5. **情景分析**：IC memo 强制 Bull / Base / Bear 三情景 + 概率 + 假设。

## A 股落地参数

| 参数 | 默认值 | 来源 |
|---|---|---|
| 无风险利率 (rf) | 2.5% | 10Y 中国国债 |
| 股权风险溢价 (ERP) | 6.0% | A 股历史 |
| 标准税率 | 25% | 企业所得税 |
| 高新税率 | 15% | 高新技术企业 |
| 终值永续增长 | 2.5% | 长期 GDP 名义 |
| Beta 默认 | 1.0 | 中性 |
| 债务比例 | 30% | A 股中位数 |
| 税前债务成本 | 4.5% | LPR + 0.5-1pp |

## 报告集成

- 新增 dim **`20_valuation_models`** — DCF / Comps / 3-stmt / LBO 打包
- 新增 dim **`21_research_workflow`** — Initiating / Earnings / Catalyst / Thesis / Morning / Screen / Sector
- 新增 dim **`22_deep_methods`** — IC Memo / Unit Economics / VCP / DD / Competitive / Rebalance

每个新 dim 都通过 `compute_deep_methods.py` 生成，不走 web 请求（纯计算），所以 Task 1 之后 Task 2 里就能直接跑出来。

## 已有 / 新增对照

**之前我们有**：19 维数据采集 + 51 评委量化规则
**这次新增**：机构级财务建模层 (DCF/Comps/LBO/3-stmt) + 研究工作流产物 (首次覆盖/财报解读/催化剂/逻辑追踪) + 深度决策方法 (IC memo/DD/Porter/单位经济/VCP/再平衡)

改动后：**19 维 + 3 新 dim + 51 评委（规则引擎引用新特征） + 18 种分析方法**