diff --git a/research-wiki/designs/2026-07-14-adversarial-question-gen-phaseB-design.md b/research-wiki/designs/2026-07-14-adversarial-question-gen-phaseB-design.md
new file mode 100644
index 0000000..bfe5901
--- /dev/null
+++ b/research-wiki/designs/2026-07-14-adversarial-question-gen-phaseB-design.md
@@ -0,0 +1,192 @@
+---
+id: adversarial-question-gen-phaseB
+title: 出题质量提升 Phase B — Agent 对抗过滤(独立后置过滤层)
+type: design
+created: 2026-07-14
+status: draft
+---
+
+# Phase B:Agent 对抗过滤(独立后置过滤层)
+
+## 1. 目标与范围
+
+在 Phase A 的 grounded 单题基础上,加两个"用完整 inference agent 揪残余 shortcut"的机制:**作弊者门**(AFLite 式——agent 能秒杀的题剔除)和**配对翻转门**(agent 答案必须随问题翻转,否则揪出偏好蒙答)。
+
+**关键架构决策**:Phase B 是 Phase A 产物 `accepted_questions.json` 之上的**独立后置过滤层**,**不改** Phase A 的逐题 accepted 语义、`on_accept` 逐题持久化、`record_item`/`update_gates` 状态机、`load_progress` 断点续跑。Phase B 有自己的过滤状态表,与 Phase A 的 `final_status` 正交。这个选择有明确代价(§2 权衡),但换来对现有状态机的零改动。
+
+**前置依赖(硬依赖,缺则 Phase B 无法判 flip)**:Phase A([2026-07-14-grounded-question-gen-phaseA-design.md])已落地,且 **`accepted_questions.json` 必须写入 `sub_pattern` 字段**——Phase B 按题的 `sub_pattern` 查其 `supports_flip`/`flip_axis`(Codex 指出当前 `_on_accept` 未写该字段,Phase A 的"sub_pattern 持久化"必须落实,否则 Phase B 无法区分哪些题走翻转门)。
+
+**问题来源**:[2026-07-14-hard-distractor-literature.md](AFLite、TempCompass QuadAcc、DMC3 before/after 问题翻转)。
+
+### 路径归属
+
+| 改动 | 代码位置 | 生效范围 |
+|------|---------|---------|
+| 作弊者门 + 配对翻转门 | 新建 `adversarial_filter.py`(独立过滤层) | **filter 层自己的配置 `filter_task_types`(默认 `[Action Recognition]`)选择过滤哪些题型**——不塞进 Phase A strategy 主流程,保持概念隔离 |
+| supports_flip 声明 | `SubPattern` 数据类加 `supports_flip`/`flip_axis` | 仅声明的 SubPattern(当前只 AR 的 temporal/cross_segment) |
+| 过滤状态表 | `run_store.py` 新增 `adversarial_verdicts` 表 | 公共(新表,不动现有 `question_gen_items`) |
+
+## 2. 两种架构对比与权衡
+
+Codex 审查了"改内部状态机"方案(把 accepted 时机推迟到 agent 门后),指出三个 Critical:`record_item` 只存 question_text 无法从 DB 重建完整 `GeneratedQuestion`;改 accepted 时机与 `update_gates`/`on_accept`/`load_progress` 三处状态机强冲突;`load_progress` 不返回 pending 无恢复入口。
+
+这三个 Critical **不是靠打补丁修**,而是靠**换架构从根上消除**——这正是本设计选独立后置过滤层的理由:
+
+| Critical(改内部状态机方案的问题) | 独立后置层如何从根消除 |
+|-----------------------------------|----------------------|
+| DB 只存 question_text,无法重建 GeneratedQuestion | Phase B 从 `accepted_questions.json` 重建(question/options/answer/source_nodes/video_id 字段完整),**根本不碰 DB 重建这条路** |
+| 改 accepted 时机与三处状态机冲突 | Phase B **不改 accepted 时机**——Phase A 照常逐题 accepted,Phase B 在其产物之上过滤,冲突的前提(改时机)不存在 |
+| load_progress 不返回 pending | Phase B **不引入 pending 状态**,用独立 `adversarial_verdicts` 表管自己的过滤进度,不依赖 load_progress |
+
+**但独立后置层有它自己的代价(诚实标注)**:
+
+| 代价 | 说明 | 缓解 |
+|------|------|------|
+| **两份 JSON** | `accepted_questions.json`(便宜门过,Phase A 原始产物)不再是最终题库,最终是 `accepted_questions_final.json`(agent 门过)。使用者(训练/评估脚本)若读错会用到未过 agent 门的题 | 明确命名 + 文档标注训练脚本必须读 `_final`。**不覆盖写回**——覆盖写回会与补生成的 `on_accept` 追加产生时序打架,故保留两份、各有明确语义。这是本架构无法消除的真实代价 |
+| **补生成多 run** | 被过滤题需再跑 Phase A 补缺额,多次出题 run | 迭代上限 `adversarial_max_rounds` 兜底 |
+| **agent + 镜像成本翻倍** | 每题一次 agent 试答,supports_flip 的题额外一次镜像生成 + 一对试答 | 批次后置只对便宜门过的题跑,不浪费在中途废题 |
+
+净判断:独立后置层用"两份 JSON + 补生成成本"换"现有状态机零改动 + 从完整 JSON 重建",符合"避免过度修改引入 bug"的约束。
+
+## 3. 架构与数据流
+
+```mermaid
+flowchart TD
+ A[Phase A 产物 accepted_questions.json
N 道便宜门过的 AR 题] --> B[adversarial_filter 后置过滤]
+ B --> C[作弊者门: 完整 agent 标准设定试答每题]
+ C -->|agent 答对=太简单| D[verdict=filtered_too_easy]
+ C -->|agent 答错=有效| E{supports_flip?}
+ E -->|否| H[verdict=passed]
+ E -->|是| F[现场生成镜像题 + agent 试答一对]
+ F -->|P/Q 答案不同=随问题翻转| H
+ F -->|P/Q 答案相同=没翻转/瞎选| G[verdict=filtered_no_flip]
+ D --> I[统计缺额]
+ G --> I
+ H --> J[写 final: accepted_questions_final.json]
+ I -->|缺额>0 且 轮次<上限| K[调 run_pipeline_v2 补生成
避开已用节点/已过滤题]
+ K --> A
+ I -->|够数或达轮次上限| L[难度报告 + 告警]
+```
+
+## 4. 组件设计
+
+### 4.1 作弊者门(`adversarial_filter.py`)
+
+对 `accepted_questions.json` 中每道 AR 题,用完整 inference agent(**标准设定**,看完整题;agent 配置 skill_mode/max_steps 从训练配置继承)试答:
+- agent 答对 → `verdict=filtered_too_easy`(EOB/排除法残余,剔除)
+- agent 答错 → 进入配对翻转门
+
+`adversarial_verdicts` 表(schema 补齐以支撑严格续跑):
+
+| 列 | 说明 |
+|----|------|
+| `question_id` | 题标识(跨轮唯一,见 §4.3 ID 方案) |
+| `round` | 过滤轮次 |
+| `stage` | `cheat`(作弊门)\| `flip_original` \| `flip_mirror`(区分同一 pair 的三次 agent 试答) |
+| `question_hash` | 原题 payload(question+options+answer)hash,防 JSON 变动后误用旧 verdict |
+| `agent_prediction` | agent 答案 |
+| `agent_correct` | 是否答对(作弊门用) |
+| `verdict` | `passed` \| `filtered_too_easy` \| `filtered_no_flip` \| `flip_skipped` |
+| `pair_id` | 关联原题与镜像题 |
+| `agent_config` | agent 配置指纹(skill_mode/max_steps/model),固化以保证续跑一致 |
+
+续跑:重启时按 `(question_id, question_hash, stage)` 查已完成的试答,未完成的重跑;`agent_config` 变化则该题全部 verdict 作废重判。每题试答结果**立即落表**(崩溃不丢)。
+
+### 4.2 配对翻转门(现场生成镜像,仅 supports_flip 的 SubPattern)
+
+**声明粒度是 SubPattern 级(不是 strategy 级)**:`SubPattern` 数据类加 `supports_flip: bool` + `flip_axis: str | None`("before/after" | "first/last")。因为 AR 内部 6 个 SubPattern 只有部分支持翻转:temporal_reasoning_failure=before/after、cross_segment_entity_tracking=first/last;其余 4 个=None。Phase B 按题的 `sub_pattern`(从 JSON 读,见 §依赖)查该 SubPattern 的 flip_axis。
+
+**判定语义(修正前版逻辑洞)**:配对翻转**不要求答对,只看答案是否随问题翻转**。前版"两题都答对才 passed"与作弊门"agent 答错才进入"直接矛盾(同一 agent 对原题先错后对判定不稳定)。修正为:
+
+| agent 对 (P, Q) 的答案 | 含义 | verdict |
+|----------------------|------|---------|
+| 两题给**不同**答案 | 答案随问题翻转 → agent 在尝试理解视频(即使都错)| **passed**(有效难题) |
+| 两题给**相同**答案 | 问题明明相反却不翻转 → agent 没看视频瞎选、题诱导瞎选(有偏置)| **filtered_no_flip**(剔除) |
+
+**流程(完全在 Phase B 内,Phase A 不动)**:
+1. 从原题 `source_nodes` 重建素材(Phase B 已持有树);
+2. 现场调 VLM 生成镜像题(同素材、翻转 flip_axis:`"X 之前"`→`"X 之后"`),正解天然相反;
+3. 原题 P 的 agent 预测**复用作弊门那次结果**(不重跑,避免同一 agent 非确定性引入噪声);仅新跑镜像题 Q;同一 `pair_id`。
+4. 按上表判定(比较 P/Q 所选是否指向同一语义内容,不判对错)。
+5. 镜像生成失败(造不出合法 before/after 对)→ `flip_skipped`,该题只经作弊门判定,不误杀。
+
+**判定工程化细则(消除判定噪声)**:
+- **按 canonical 语义比较,不比字母**:镜像题选项会重洗牌,A/B/C/D 字母无语义;比较 agent 所选**选项规范化文本**(canonical option)是否指向同一内容。
+- **无效答案保守处理**:P/Q 任一为拒答/非法答案 → 不判 passed,记 `flip_skipped`(退回只经作弊门),避免"P 拒答、Q 随机给答"被误判为翻转。
+- **镜像正解字面校验**:镜像生成后校验 `canonical_correct(P) != canonical_correct(Q)`(before/after 不总保证正解不同,如列表答案、"无法确定"类);相同则造不出有效镜像对 → `flip_skipped`。
+
+镜像题仅用于翻转检验,**不进最终题库**。
+
+**门顺序(逻辑自洽)**:① 作弊门用完整 agent 试答原题,答对=太简单→剔除;② 存活的"agent 答错"题进翻转门,看 P/Q 答案是否翻转。两门都用完整 agent,串联无矛盾:答对的在作弊门被剔除,答错的在翻转门按"答案是否翻转"区分"有效难题"vs"诱导瞎选的偏置题"。
+
+### 4.3 补生成与迭代
+
+缺额 = 目标数 − passed 数。缺额 >0 且轮次 < `adversarial_max_rounds` 时,调 `run_pipeline_v2`(Phase A)补生成。
+
+**需要的接口改动(明确,非"显式传入即可"一笔带过)**:`run_pipeline_v2` 现每 run 新建空 `embed_pool`/`used_node_ids`(`pipeline_v2.py:793` 附近),补生成需新增两个可选参数:
+- `initial_used_node_ids: set[str] | None` — 传入已用节点,补生成避开(sampler 的 used_node_ids 初值);
+- `initial_embed_pool: list[np.ndarray] | None` — 传入已接受题的 embedding,补生成对它们去重。
+不传时行为与现状完全一致(默认 None → 空初始化),11 题型不受影响。
+
+**question_id 跨轮防撞**:现 `question_id = f"{video_id}_{task_type}_{seq:04d}"`(`generator_v2.py:340`)。补生成新 run 复用 seq 会撞 ID。方案:补生成 run 传入 `seq_offset`(= 上一轮最大 seq),新题从 offset 续编,保证 `accepted_questions.json` 与 `adversarial_verdicts` 中 ID 全局唯一。
+
+**两份 JSON 时序(消除歧义)**:
+- `accepted_questions.json`:Phase A 各 run(含补生成)的 `on_accept` **滚动追加**(保持 Phase A 语义)。
+- `accepted_questions_final.json`:Phase B 每轮结束**全量重写**(tmp+os.replace 原子写),内容 = 当前所有 `verdict=passed` 的题。非增量追加——避免断点在"verdict 已写、final 未重写"之间时状态不一致;final 可随时从 verdicts 表全量重建。
+
+### 4.4 难度报告(`run_store.py`)
+
+- 每轮记录 agent 正确率(=作弊门答对率,越低越难)到 `adversarial_verdicts` 聚合。
+- 批次 agent 正确率 > `difficulty_warn_threshold`(默认 0.85)→ `logger.warning` 告警"出题太简单"。
+- 难度用独立字段(agent_correct 聚合),**不复用** `difficulty_steps`(那是 heavy_check 步数)。
+
+## 5. Prior-Version Audit(Phase A 后的管线行为)
+
+| Phase A 后行为 | Phase B 处置 |
+|---------------|-------------|
+| on_accept 逐题追加 JSON(便宜门过即 accepted) | **保留** — Phase B 不改,只在其产物之上过滤 |
+| record_item/update_gates 状态机(final_status) | **保留** — Phase B 用独立 `adversarial_verdicts` 表 |
+| load_progress 断点续跑 | **保留** — Phase B 有独立过滤进度(未试答题续跑) |
+| run_pipeline_v2 逐 slot 出题 | **保留** — 补生成是标准 Phase A run |
+| embed_pool/used_node_ids 每 run 独立初始化 | **保留** — 补生成新 run 不继承,显式传避开节点 |
+| 四门 gate | **保留** — 作弊门/翻转门是 JSON 产物之后的独立层 |
+
+未发现隐式删除。Phase B 是 additive 层,Phase A 状态机零改动。
+
+## 6. 非功能性需求
+
+| 维度 | 设计 |
+|------|------|
+| **持久化** | 每题 agent 试答结果按 `(question_id, question_hash, stage)` 立即写 `adversarial_verdicts` 表;final JSON 每轮全量重写(tmp+os.replace)。原题 GeneratedQuestion 从 Phase A 的 JSON 完整重建,无需 DB 重建 |
+| **幂等性** | agent 试答非确定性(LLM 固有),但过滤决策幂等(同 verdict 不重复处理);`question_hash` 防 JSON 变动误用旧 verdict;补生成用递增 seed + seq_offset 可复现 |
+| **断点续跑** | `adversarial_verdicts` 按 `(question_id, question_hash, stage)` 记录已完成试答,重启只重跑未完成的;`agent_config` 变则该题 verdict 作废重判。补生成复用 Phase A progress |
+| **原子性** | 每题 verdict 单条 DB 写;final JSON 全量 tmp+os.replace 原子重写(非追加,可随时从 verdicts 表重建) |
+
+## 7. 行为保真检查清单
+
+| # | 行为 | 状态 |
+|---|------|------|
+| 1 | Phase A 出题 + accepted 语义 + on_accept | 保留(零改动) |
+| 2 | 四门 gate / 断点续跑 / record_item 状态机 | 保留 |
+| 3 | 11 个非 AR 题型 | 保留(不在 `filter_task_types` 中,不触发过滤) |
+| 4 | AR 最终题库 | **有意变更** — 经 agent 对抗过滤 + 翻转检验 |
+
+## 8. 配置参数(YAML)
+
+| 参数 | 默认 | 说明 |
+|------|------|------|
+| `filter_task_types` | `[Action Recognition]` | 后置过滤作用的题型(filter 层配置,非 strategy) |
+| `adversarial_max_rounds` | 5 | 后置过滤迭代上限 |
+| `adversarial_agent_max_steps` | 40 | agent 试答步数上限 |
+| `difficulty_warn_threshold` | 0.85 | 批次 agent 正确率告警阈值 |
+
+## 9. 测试策略
+
+- 单元:翻转判定(构造 agent 对 P/Q 的答案 → 验证"答案不同=passed / 答案相同=filtered_no_flip",不判对错);作弊门 verdict(agent 答对=filtered_too_easy);迭代缺额计算 + 轮次上限 + seq_offset 防撞 ID。
+- 集成:AR 后置过滤端到端(mock agent + mock 镜像 VLM),验证 `accepted_questions.json` → 过滤 → `accepted_questions_final.json`;断点续跑(中断后从 adversarial_verdicts 未试答题继续,已判的不重跑)。
+- 回归:11 个非 AR 题型不触发过滤(不在 `filter_task_types` 中,现有测试全绿)。
+
+## 10. 待实现时确认的细节
+
+- 补生成"避开已用 source_nodes"的粒度:整题级去重(已用节点集合传入 sampler 的 used_node_ids)已足够,无需节点级黑名单持久化跨 run(每轮显式传入即可)。
+- 镜像题 VLM 生成失败(无法造出合法 before/after 对)时:该题跳过翻转门、只经作弊门判定(记 flip_skipped,不算 filtered),避免因镜像生成失败误杀有效题。
diff --git a/research-wiki/designs/2026-07-14-grounded-question-gen-phaseA-design.md b/research-wiki/designs/2026-07-14-grounded-question-gen-phaseA-design.md
new file mode 100644
index 0000000..b01a867
--- /dev/null
+++ b/research-wiki/designs/2026-07-14-grounded-question-gen-phaseA-design.md
@@ -0,0 +1,151 @@
+---
+id: grounded-question-gen-phaseA
+title: 出题质量提升 Phase A — Grounded 单题质量(候选池 + VLM 视觉打分 selector + 单维反事实)
+type: design
+created: 2026-07-14
+status: draft
+---
+
+# Phase A:Grounded 单题质量
+
+## 1. 目标与范围
+
+把"干扰项 grounded 约束"从 prompt 文案(求 VLM 主观写得像)下沉到**机制层**:VLM 生成候选池 → VLM 视觉打分 → 按视觉可信度区间选出真实 near-miss 干扰项,从机制上消灭 Easy-Options Bias(负空间干扰项 → 排除法秒杀)。Phase A **完全不依赖 inference agent**,纯出题质量 + 观测。
+
+**问题来源**:[2026-07-14-ar30-too-easy-analysis.md](负空间干扰项 96.7% 正确率、SubPattern 归属跑偏)、[2026-07-14-hard-distractor-literature.md](GroundAttack/TempCompass/VITATECS 范式)。
+
+### 路径归属(严格区分,避免改错路径)
+
+| 改动 | 代码位置 | 生效范围 |
+|------|---------|---------|
+| L0 tree 错配 bug 修复 | `pipeline_v2.py` | 公共(纯 bug,全题型正向) |
+| 候选池 + VLM 视觉打分 selector | 新建 `distractor_selector.py` | 公共层,**strategy 声明开关,默认只 AR 启用** |
+| 单维反事实约束 | `strategy_action_recognition.py` | **仅 AR** |
+| multi_true 门松绑 | `gate_multi_true.md` | 公共,12 类统一(grounded 干扰项过门的前提) |
+| sub_pattern 持久化 + selector 观测 | `tools/generate_questions.py`、`run_store.py` | 公共(纯数据) |
+
+**非 AR 的 11 个 BaseTaskTypeStrategy**:`uses_grounded_selector=False`,走原路径(VLM 直接出 3 个干扰项),行为不变。
+
+## 2. 架构与数据流
+
+```mermaid
+flowchart TD
+ A[sample_material_v2] --> B[VLM 生成正解 + N 个候选干扰项]
+ B --> C{strategy.uses_grounded_selector?}
+ C -->|False 11类| D[取前 3 个候选 = 原行为]
+ C -->|True AR| E[VLM 视觉打分: 每候选 + 正解 打 0-1]
+ E --> F[区间选择: 选 3 个落在 正解分-δ 区间的]
+ F -->|凑不齐 3 个| G[退回重出 prev_reason 引导]
+ F -->|成功| H[组题 candidate]
+ D --> H
+ H --> I[四门 gate 用 current_tree 修复]
+ I -->|通过| J[on_accept 落盘 含 sub_pattern]
+```
+
+## 3. 组件设计
+
+### 3.1 L0:tree 错配 bug 修复
+
+`pipeline_v2.py` `_process_one_slot`:`run_gates(tree=tree, ...)` → `run_gates(tree=current_tree, ...)`。retry 换视频后门控用当前树验证,消除"无 source material"假拒绝。1 行改动。
+
+### 3.2 策略开关与候选池生成(`distractor_selector.py`)
+
+**策略开关(路径隔离的核心)**:`TaskTypeStrategy` protocol 新增 `uses_grounded_selector` property;`BaseTaskTypeStrategy` 默认返回 `False`(11 类走原路径),`ActionRecognitionStrategy` 覆盖为 `True`。`generator_v2._build_v2_prompt` 的公共输出 schema **不变**;候选池 + selector 逻辑放新模块 `distractor_selector.py`,仅当 `strategy.uses_grounded_selector=True` 时 `_process_one_slot` 调用它,其余题型仍走 `generate_one_v2` 直接出 4 选项。
+
+出题拆为两步(**仅 uses_grounded_selector=True**):
+1. **正解生成**(现有 generate_one_v2):VLM 生成 question + 正解 + difficulty。
+2. **候选池生成**(新 distractor_selector):给定 question + 正解 + 素材,VLM 一次生成 `N` 个候选干扰项(默认 N=24),每个都要求 grounded near-miss(真实发生、只在一维不同)。
+
+### 3.3 VLM 视觉打分 Selector(核心,消灭 EOB)
+
+```
+输入: N 个候选干扰项 + 正解 + 采样帧
+VLM(看真帧 + question + 每个选项) → 每个选项打"视觉可信度" score ∈ [0,1]
+ 正解得分 s_correct(应偏高)
+选 3 个 distractor,使 score ∈ [s_correct - δ_high, s_correct - δ_low]:
+ - 上界 s_correct - δ_low: 不能太接近正解(否则真歧义/也对)
+ - 下界 s_correct - δ_high: 不能太低(否则负空间,排除法可解)
+凑不齐 3 个 → 退回重出(reject_reason="grounded 干扰项不足")
+```
+
+`δ_low`、`δ_high` 为配置参数(默认 δ_low=0.05、δ_high=0.35)。核心洞察:干扰项与正解视觉可信度接近 → 排除法("搜不到就排除")失效。
+
+**退化处理(避免 AR 系统性欠产)**:候选池凑不齐 3 个落区间时按序退火:
+1. 扩大候选池再生成一轮(N → 2N);
+2. 仍不足则逐步放宽 δ_high(+0.1,最多 2 次),保证下界不越过 δ_low;
+3. 仍不足才走正常重出(reject_reason="grounded 干扰项不足",retry_limit 兜底)。
+每次 hard-fail(走到步骤 3)记录到 run_store 的 selector 观测,供调参。
+
+### 3.4 单维反事实约束(仅 AR,`strategy_action_recognition.py`)
+
+6 个 SubPattern 的 `instruction` + `distractor_rules` 改为内嵌"只改一维"模板 + few-shot 正例,硬约束"干扰项必须是视频中真实发生的动作/实体,仅在时点/主体/方式/对象**单一维度**上与正解不同,严禁缺席事件":
+
+| SubPattern | 反事实维度 | few-shot 要求 |
+|-----------|-----------|--------------|
+| temporal_reasoning_failure | 事件顺序(同组真实事件错序) | 正解正确排序,干扰项是真实事件的错误排列 |
+| fine_grained_visual_action | 执行方式(同动词的另一种真实做法) | 4 选项为同一动词的真实执行方式变体 |
+| cross_segment_entity_tracking | 主体(另一实体的真实动作) | 干扰项=另一实体在该段真实做的事 |
+| semantic_rigidity | 表述(同义 vs 字面陷阱) | 正解同义改写,1 干扰项复用素材原文但语义错 |
+| premature_evidence_anchoring | 时点(前段真实诱饵) | 前段必须有与某干扰项字面吻合的真实局部匹配 |
+| evidence_gap_confabulation | 因果完整性(诚实 vs 编造) | 正解仅陈述可观测,干扰项编造未展示因果 |
+
+### 3.5 multi_true 门松绑(`gate_multi_true.md`,公共)
+
+rubric 从"≥2 选项 plausibly correct → fail"改为"**仅当 ≥2 选项完全满足题干限定(同主体/时点/方式/对象下都为真)→ fail**"。放行"错误选项有局部真实证据、但在题干限定下为假"的近似干扰项。12 题型统一(用户确认全局松绑)。
+
+### 3.6 sub_pattern 持久化 + selector 观测(公共,纯数据)
+
+- `accepted_questions.json` 每题增 `sub_pattern` 字段:改 `tools/generate_questions.py` 的 `_on_accept`(当前**未写**该字段),从 pipeline 逐题传下的 sub_pattern 写入 JSON。**这是 Phase B 配对翻转门的硬前提**(Phase B 按 sub_pattern 查 supports_flip),务必落实。
+- selector 打分观测:`question_gen_items` 表新增列 `selector_scores TEXT`(JSON:正解分、选中 3 干扰项分、候选池大小、退火轮次、是否 hard-fail),幂等 ALTER TABLE 迁移(与 sub_pattern 列同法)。供 EOB 观测与调参。
+
+## 4. Prior-Version Audit(现有 v2 管线行为清单)
+
+| 现有行为 | Phase A 处置 |
+|---------|-------------|
+| 每 slot 独立重出循环(retry_limit,prev_reason 传回) | **保留** — selector 凑不齐干扰项走同一重出机制 |
+| on_accept 逐题追加 JSON(崩溃最多丢一题) | **保留** — 仅新增 sub_pattern 字段 |
+| progress 断点续跑(slot_id → status) | **保留** — 不动 |
+| 四门 gate 并发 + verbatim 短路 | **保留** — 仅改 multi_true rubric(prompt)+ 修 tree 传参 |
+| embedding 去重 | **保留** |
+| heavy_check 抽检 | **保留** |
+| 11 个 BaseTaskTypeStrategy 出题 | **保留** — uses_grounded_selector=False 走原路径 |
+
+未发现隐式删除。唯一行为变更:AR 出题多一个 selector 步骤;multi_true rubric 放宽(有意,全局)。
+
+## 5. 非功能性需求
+
+| 维度 | 设计 |
+|------|------|
+| **持久化** | 不变 — on_accept 逐题追加,崩溃最多丢一题。selector 打分随题落 run_store |
+| **幂等性** | 不变 — 同 seed → 同 slot;候选池生成与 selector 打分由 rng/VLM 决定,同输入同输出(VLM 非确定性已有,不新增) |
+| **断点续跑** | 不变 — progress 机制在 pipeline 层;selector 无状态,重出即重跑 |
+| **原子性** | 不变 — 逐题落库;JSON 用 tmp + os.replace 原子写(现有) |
+
+## 6. 行为保真检查清单
+
+| # | 行为 | 状态 |
+|---|------|------|
+| 1 | 12 题型 slot 分配 | 保留 |
+| 2 | 重出循环 + 换视频 | 保留 |
+| 3 | 四门 gate | 保留(multi_true rubric 有意松绑 + tree 传参修复) |
+| 4 | 去重/heavy_check/on_accept/断点续跑 | 保留 |
+| 5 | 11 个 BaseTaskTypeStrategy 行为 | 保留(selector 默认关闭) |
+| 6 | AR 出题 | **有意变更** — 候选池 + selector + 单维反事实 |
+
+## 7. 配置参数(YAML)
+
+| 参数 | 默认 | 说明 |
+|------|------|------|
+| `candidate_pool_size` | 24 | VLM 生成的候选干扰项数 N |
+| `selector_delta_low` | 0.05 | 干扰项视觉分与正解的最小差 |
+| `selector_delta_high` | 0.35 | 干扰项视觉分与正解的最大差 |
+
+## 8. 测试策略
+
+- 单元:selector 区间选择(构造候选打分,验证选出落区间的 3 个 / 凑不齐时退回);uses_grounded_selector 分流(AR 走 selector、其余走原路径)。
+- 集成:AR 出题端到端(mock VLM 返回候选池 + 打分)→ 验证干扰项来自候选池且落区间;sub_pattern 写入 JSON。
+- 回归:11 个非 AR 题型出题行为不变(现有测试全绿)。
+
+## 9. 与 Phase B 的衔接
+
+Phase A 产出 grounded 单题;Phase B([2026-07-14-adversarial-question-gen-phaseB-design.md])在其上加"完整 agent 对抗过滤门 + 配对翻转门"揪残余 shortcut。Phase A 的 selector 观测数据为 Phase B 的难度报告提供基线。