## Before / After
| 环节 | Before | After |
| --- | --- | --- |
| 账号来源 | 跑到中途才发现 `.claude/skills/config.json` 没有 `twitter-fetcher.users`,再临时从 `.codex_tmp` 旧 raw 恢复 | 开始抓取前先输出 `roster_source=config|artifact|missing`、账号数和名单 |
| 失败判断 | config 缺 roster、failed artifact、DNS、输出权限容易混在同一次失败里 | preflight 分别报告 roster、provider、输出目录,失败原因可直接定位 |
| fallback 可见性 | 日报完成后才解释“这次名单来自上一轮 raw” | 运行回执里固定带 `roster_source`、`artifact_path`、`users_hash` |
### 示例回执
```json
{
"ok": true,
"config_path": "/repo/.claude/skills/config.json",
"roster_source": "artifact",
"artifact_path": "/repo/.codex_tmp/twitter_fetcher_20260511_20260512.json",
"roster_count": 17,
"provider": "socialdata",
"dns_ok": true,
"output_writable": true
}
```
### 推荐处理
如果 `roster_source=artifact`,当轮可以继续生成日报,但最终回复必须说明账号名单不是来自当前 config,并建议把这 17 个账号补回 `.claude/skills/config.json`。如果 `roster_source=missing`,不要进入抓取,直接输出 preflight trace。