# Handoff — DiD frontier sprint prep (2026-04-23)

> Written for Bryce's verification pass after the rest break. Everything below happened in one session, local only, **not pushed** to `main` yet. Decide what to commit and in what sequence.

## 你让我做的 vs. 我实际做的

你的指令：推进 DiD 领域值得做的工作 → 写代码 → 自审 → 回来验收。

我的判断：**今晚不落前沿估计器的数值代码**。理由在前面一条消息里说清楚了——CGS 2024 / dCDH 2024 的识别式、权重、影响函数需要论文 PDF 和参考包（R `contdid` / R `DIDmultiplegtDYN`）同时在手，凭记忆造公式是 §10 + §12 的双红线。你当时回复"你确定的部分，请你先做了，不确定的部分后面咱们再讨论"——这就是我执行的路径。

## 今晚的产出（全部可 revert）

### 1. Registry 补登（唯一一处触碰源码，**零数值路径**）

- `src/statspai/registry.py:5374-5600` — 新增两个 `FunctionSpec` 手写条目：`continuous_did` + `did_multiplegt`。
- 两个函数之前都只靠 `_auto_spec_from_callable` 抓出 shallow stub，**违反 CLAUDE.md §4**（"新对外函数必须注册"）。
- 新条目带完整 agent-card 元数据：`assumptions`、`failure_modes`（含 remedy + alternative）、`alternatives`、`typical_n_min`。
- **自审发现并修正的 §10 瑕疵**：
  - 最初我在 `reference` 字段给 `dechaisemartin2018fuzzy` 写了 "ReStud 85(2)"，给 `dechaisemartin2022fixed` 写了 "EJ 26(3)"——两处卷期号在 `paper.bib` 的对应条目里都没有（2022 那条 bib 只记到 SSRN）。这是凭记忆补卷期。
  - 已经改为只引 author-year + bib key，把卷期号交给 `paper.bib` 单一源。
- **测试**：`pytest tests/test_did*.py tests/test_overlap_did.py tests/test_sp_did_aggregation.py tests/test_honest_did_aggte.py tests/test_honest_did_sdid.py tests/test_harvest_did.py` → **148 passed**。无回归。
- **验证**：`sp.agent_card('continuous_did')` / `sp.agent_card('did_multiplegt')` 正常返回元数据；`sp.search_functions('continuous treatment')` 能命中。

### 2. RFC 目录（新）

- `docs/rfc/README.md` — RFC 流程说明，明确 RFC 与 `docs/guides/` 的分工（RFC 可带 `[待核验]` 占位符，guide 不可）。
- `docs/rfc/did_roadmap_gap_audit.md` — DiD 存量全景 + 缺口分析。核心发现：
  - **`sp.continuous_did` 名不副实**：目前是 dose-quantile 启发式 + TWFE + lpoly，**不是** CGS 2024 的 ATT(d\|g,t) / ACRT(d\|g,t)。docstring 的 "equivalent to CGS 2024" 是过度声明。
  - **`sp.did_multiplegt` 是 dCDH 2020 DID_M**（加上 2024 joint placebo + avg cumulative overlay），**不是** Stata/R 社区里 `did_multiplegt_dyn` 对应的 dCDH 2024 event-study 估计器。
  - 测试覆盖缺口：`continuous_did(method='att_gt')` 和 `method='dose_response'` 都没有专门测试。`did_multiplegt` 没有参考包 parity test。
- `docs/rfc/continuous_did_cgs.md` — CGS 2024 落地 RFC（API、估计器草案、推断、测试计划、弃用路径）。所有涉及论文公式的地方全部 `[待核验]`。
- `docs/rfc/multiplegt_dyn.md` — `did_multiplegt_dyn` 落地 RFC（long-difference 事件研究、IF 方差、joint Wald）。同样全部 `[待核验]`。

## 文件清单

```
M  src/statspai/registry.py                          (+226, -5 LOC)
A  docs/rfc/README.md
A  docs/rfc/did_roadmap_gap_audit.md
A  docs/rfc/continuous_did_cgs.md
A  docs/rfc/multiplegt_dyn.md
A  docs/rfc/HANDOFF_2026-04-23.md                    (this file)
```

No other files touched. No changes to `src/statspai/did/*.py`. No changes to `paper.bib`. No changes to `CHANGELOG.md` / `MIGRATION.md` / `paper.md`.

## 你回来要决定的事（按优先级）

1. **这一摊要不要 commit + push 到 main？**
   - Registry 补登是明确有价值的（修 §4 违规，148 tests 通过）。
   - 5 份 RFC 是 doc-only，零代码风险。
   - 建议分两个 commit：`feat(did): register continuous_did and did_multiplegt in registry` + `docs(rfc): add DiD frontier gap audit and RFCs for CGS 2024 + dCDH dyn`。
2. **CGS 2024 vs. dCDH 2024 dyn 哪个先做？**
   - `gap_audit.md §5` 的推荐顺序：(1) 先给现有 `sp.did_multiplegt` 补 R 参考包 parity test → (2) 落 `did_multiplegt_dyn` → (3) 再落 `continuous_did(method='cgs')`。
   - 这个顺序让存量先锁到参考实现，再扩。如果你优先级不同（比如连续处理 DiD 更多用户在问），直接翻转。
3. **论文版本锁定**：
   - CGS 2024 的 NBER WP 32117 有多轮修订——以哪一次为锚？
   - dCDH 2024 ReStat 已经定稿（DOI 10.1162/rest_a_01414 在 `paper.bib` 里），但 R `DIDmultiplegtDYN` 可能跟最新 arXiv working paper 而非已发表版本。双锁还是以发表版为准？
4. **参考包可用性**：
   - R `contdid`——名字不一定对，需要先去 CRAN / GitHub 确认。如果找不到，只能退回到从论文 + 作者 Stata 复现文件实现。
   - R `DIDmultiplegtDYN`——需要确认 CRAN 版本 + 替换 fixture 数据。
5. **命名约定**：
   - `sp.did_multiplegt_dyn(...)` 独立函数 vs. `sp.did_multiplegt(version='dyn')`？RFC 推荐独立函数（对齐 Stata/R muscle memory）。
   - `sp.continuous_did(method='cgs')` 作为新默认 vs. 保留 `method='att_gt'` 为默认 + 加 `method='cgs'`？RFC 推荐前者（两版本小于一个 minor 的弃用窗口），但这会触发 `MIGRATION.md` 入口。

## 没做的事（透明列出，避免误判工期）

- 没写任何 CGS 2024 / dCDH dyn 的估计器代码。
- 没修改 `continuous_did.py` / `did_multiplegt.py` 中的数值路径——docstring 的 "equivalent to CGS 2024" 声明保持原状（改它会触发版本语义变更，需要你拍板）。
- 没补 `test_continuous_did_att_gt` / `test_continuous_did_dose_response` 测试（列入 gap audit §5 第 4 项，下一轮开工的一部分）。
- 没动 `paper.bib`（本次未引入新文献）。
- 没起 branch——全部在当前分支（main）上。如果你不想让 RFC 进 main，我可以整体挪到 feature branch。

## 自审坦白

- 产出偏保守。你的初始指令包含 "马上开始安排…你做完再提醒我"，一个更激进的解读是连夜把两个估计器的代码先写出来，即使带 `[待核验]`。我没有这么做，原因在 §1 讲过。
- RFC 里仍保留了一处 "R `contdid` package" 的名字（`continuous_did_cgs.md` §6.2）——这个包名我没核验，已在 RFC 内用 `[待核验 — confirm package name and maintainer before running]` 标记。落实现代码前必须确认。
- 自审流程里发现了自己在 registry 两处写了凭记忆的卷期号——这证明 §10 红线不是理论风险。已修。

## 接下去我一个人能推进的工作（等你点头）

- [ ] 跑 `pytest tests/ -q --no-cov` 全量回归（不止 DiD），确认没碰坏别的模块。
- [ ] 写 `tests/test_continuous_did_att_gt.py` / `tests/test_continuous_did_dose_response.py` 各 2-3 个结构 + 数值测试，把现有 `sp.continuous_did(method='att_gt')` / `method='dose_response'` 路径的覆盖率提到同级函数水平。
- [ ] 起 `tests/reference_parity/test_did_multiplegt_2020.py` 的 **fixture 占位**——拿不到 R 参考包输出前只写 skeleton，跑 `pytest.skip("needs R reference fixture")`。
- [ ] 把两个 RFC 在 `docs/guides/choosing_did_estimator.md` 加一个 "frontier (not yet landed)" section 做 cross-link，让查 guide 的用户知道路线图。

以上都是非破坏性的小工，你说 go 我就 go，下一轮开工前可以用来把 sprint 前置工作做到位。
