---
phase: 11-filterbuilder
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/filter/builder.rs
  - src/filter/mod.rs
autonomous: true
requirements:
  - FILTER-01
  - FILTER-02
  - FILTER-03
  - FILTER-04
  - FILTER-05
  - FILTER-06
  - FILTER-07
  - FILTER-08
  - FILTER-09

must_haves:
  truths:
    - "用户可在 src/filter/builder.rs 内定义的 `FilterBuilder::new()` 上链式调用 14 个字段的所有谓词方法构造组合过滤器"
    - "ts 字段提供 ts_contains / ts_eq / ts_starts_with / ts_ends_with 四个方法（FILTER-01）"
    - "tag 字段提供 tag_is_some / tag_is_none / tag_eq / tag_contains 四个方法（FILTER-02）"
    - "ep 字段（u8）提供 ep_eq / ep_gt / ep_lt / ep_between 四个方法（FILTER-03）"
    - "sess_id / thrd_id / username / trxid / statement / appname / client_ip 七个字段各自提供 _contains / _eq / _starts_with / _ends_with 共 28 个方法（FILTER-04）"
    - "sql 字段提供 sql_contains / sql_eq / sql_starts_with / sql_ends_with 四个方法（FILTER-05）"
    - "exectime 字段（f32）提供 exec_time_gt / exec_time_lt / exec_time_between 三个方法（FILTER-06，不提供 eq）"
    - "rowcount 字段（u32）提供 rowcount_eq / rowcount_gt / rowcount_lt / rowcount_between 四个方法（FILTER-07）"
    - "exec_id 字段（i64）提供 exec_id_eq / exec_id_gt / exec_id_lt / exec_id_between 四个方法（FILTER-08）"
    - "FilterBuilder::build() 返回 Filter，Filter::matches(&Sqllog) -> bool 对所有谓词执行 AND 短路求值（FILTER-09）"
    - "Filter 满足 Send + Sync（为 Phase 12 async 集成铺路）"
    - "src/filter/mod.rs 声明 `pub mod builder;` 并 `pub use builder::{Filter, FilterBuilder};`"
    - "cargo build / cargo test / cargo clippy --all-targets -- -D warnings 全部通过，所有单元测试覆盖每个字段至少一个谓词的 true / false 两种情况"
  artifacts:
    - path: "src/filter/builder.rs"
      provides: "FilterBuilder 链式构建器 + Filter 谓词容器 + 单元测试 #[cfg(test)] mod tests"
      contains: "pub struct FilterBuilder"
    - path: "src/filter/mod.rs"
      provides: "filter 子模块根，声明 builder 并重导出公开类型"
      contains: "pub mod builder"
  key_links:
    - from: "src/filter/builder.rs"
      to: "src/record.rs"
      via: "use crate::record::Sqllog；闭包内直接访问 14 个字段"
      pattern: "use crate::record::Sqllog"
    - from: "src/filter/mod.rs"
      to: "src/filter/builder.rs"
      via: "pub mod builder + pub use"
      pattern: "pub mod builder"
    - from: "src/filter/builder.rs"
      to: "Filter.matches 内部"
      via: "self.predicates.iter().all(|pred| pred(record))"
      pattern: "predicates\\.iter\\(\\)\\.all"
---

<objective>
建立 Phase 11 的核心 API：在 `src/filter/builder.rs` 新建文件，实现 `FilterBuilder`（链式构建器）和 `Filter`（谓词容器），覆盖 14 个 Sqllog 字段全部谓词方法（FILTER-01~08）以及 AND 组合（FILTER-09）。同步更新 `src/filter/mod.rs` 声明并重导出新类型。所有谓词以 `Box<dyn Fn(&Sqllog) -> bool + Send + Sync + 'static>` 形式存储，符合研究 RESEARCH.md 推荐的方案 D。

Purpose: 这是 Phase 11 唯一的"API 表面"plan，所有用户侧公开方法都在这里产生；Plan 11-02 的 wiring 完全依赖本 plan 输出的类型。

Output: src/filter/builder.rs 含 ~50 个公开方法 + 14 字段全覆盖单元测试；src/filter/mod.rs 重导出 Filter 与 FilterBuilder。
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/ROADMAP.md
@.planning/REQUIREMENTS.md
@.planning/STATE.md
@.planning/phases/11-filterbuilder/11-RESEARCH.md
@.planning/phases/11-filterbuilder/11-PATTERNS.md
@src/record.rs
@src/filter/mod.rs
@src/filter/adapter.rs
@src/parser/builder.rs
@src/parser/mod.rs
@CLAUDE.md

<interfaces>
本计划开始时（Phase 10 收尾后）：
- `src/filter/mod.rs` 当前仅 1 行：`pub(crate) mod adapter;`
- `src/filter/adapter.rs` 已存在 `filter_by_exec_time(min_ms: u64)` 和 `filter_by_sql_contains(pattern: &str)`，均为 `pub(crate)`
- `src/lib.rs` 当前声明 `pub(crate) mod filter;`（filter 模块对外通过 lib.rs 控制可见性）
- `src/record.rs` 中 Sqllog 是 fully-owned struct（无生命周期），14 字段类型如下：
  - ts: String, tag: Option<String>, ep: u8
  - sess_id / thrd_id / username / trxid / statement / appname / client_ip: String
  - sql: String, exectime: f32, rowcount: u32, exec_id: i64
- `src/parser/builder.rs` 展示了项目链式 setter 风格：`pub fn name(mut self, v: T) -> Self { self.field = Some(v); self }`

本计划结束时输出（Plan 11-02 消费）：
```rust
// src/filter/builder.rs（新文件）
pub struct Filter {
    predicates: Vec<Box<dyn Fn(&Sqllog) -> bool + Send + Sync>>,
}
impl Filter {
    #[inline]
    pub fn matches(&self, record: &Sqllog) -> bool { ... }
}
pub struct FilterBuilder { predicates: Vec<Box<dyn Fn(&Sqllog) -> bool + Send + Sync>> }
impl FilterBuilder {
    pub fn new() -> Self;
    pub fn build(self) -> Filter;
    // 14 字段 × 谓词 ≈ 50 个链式方法
}
impl Default for FilterBuilder { fn default() -> Self { Self::new() } }
```

```rust
// src/filter/mod.rs（修改后完整内容）
pub(crate) mod adapter;
pub mod builder;
pub use builder::{Filter, FilterBuilder};
```

字段命名约定（来自 RESEARCH.md "方法命名约定"节）：
| 字段 | 方法前缀 |
| ts | ts_ |
| tag | tag_ |
| ep | ep_ |
| sess_id / thrd_id / username / trxid / statement / appname / client_ip | <field>_ |
| sql | sql_ |
| exectime | exec_time_ （不是 exectime_，与现有 filter_by_exec_time 保持一致）|
| rowcount | rowcount_ |
| exec_id | exec_id_ |
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: 创建 src/filter/builder.rs，实现 Filter + FilterBuilder + 14 字段所有链式谓词方法 + Default 实现</name>
  <files>src/filter/builder.rs</files>
  <read_first>
    - src/record.rs（确认 Sqllog 全部 14 字段名和类型，注意 tag 是 Option<String>）
    - src/filter/adapter.rs（理解现有 `Ok(sqllog) => sqllog.field op value, Err(_) => false` 模式；新代码应在 builder.rs 内只关心 &Sqllog，不处理 Result）
    - src/parser/builder.rs（参考 `pub fn name(mut self, ...) -> Self { ...; self }` 链式方法签名风格）
    - .planning/phases/11-filterbuilder/11-RESEARCH.md（重点：第 110-300 行"代码骨架"节、第 470-495 行"方法命名约定"节、第 535-610 行 Pitfalls 节）
    - .planning/phases/11-filterbuilder/11-PATTERNS.md（链式 setter 风格、错误处理位置说明）
    - CLAUDE.md（函数 < 40 行约束）
  </read_first>
  <action>
    1. **建立文件头**（src/filter/builder.rs 新文件）：
       - 顶部模块级 rustdoc 注释 `//! 链式过滤器构建器。`，说明 AND 语义和与 LogParser::iter() 的集成。
       - `use crate::record::Sqllog;`
       - 不需要其他 use（不接触 ParseError、不接触 LogIterator）。

    2. **声明 Filter 结构体**（约 15 行）：
       - `pub struct Filter { predicates: Vec<Box<dyn Fn(&Sqllog) -> bool + Send + Sync>> }`
       - 加 rustdoc：说明 Filter 由 FilterBuilder::build() 产生，AND 短路求值。
       - `impl Filter` 块包含：
         - `#[inline] pub fn matches(&self, record: &Sqllog) -> bool { self.predicates.iter().all(|pred| pred(record)) }`
       - **不要 derive Debug**（Box<dyn Fn> 无法自动 Debug）。

    3. **声明 FilterBuilder 结构体**（约 8 行）：
       - `pub struct FilterBuilder { predicates: Vec<Box<dyn Fn(&Sqllog) -> bool + Send + Sync>> }`
       - 加 rustdoc：说明链式调用风格、所有条件 AND 组合，提供一个完整的多字段示例（ts_contains + exec_time_gt + sql_contains 组合）。

    4. **实现 impl FilterBuilder**：
       a. `pub fn new() -> Self { Self { predicates: Vec::new() } }`
       b. `pub fn build(self) -> Filter { Filter { predicates: self.predicates } }`
       c. **私有 helper**：`fn add<F>(mut self, pred: F) -> Self where F: Fn(&Sqllog) -> bool + Send + Sync + 'static { self.predicates.push(Box::new(pred)); self }` — 所有公开方法都通过 add() 添加谓词，避免重复 Box::new 调用。

    5. **FILTER-01 ts 四方法**（每个方法 ≤ 5 行）：
       - `ts_contains(self, pattern: impl Into<String>) -> Self` — 闭包内 `r.ts.contains(&pattern)`，pattern 在 add() 调用前 .into() 成 String。
       - `ts_eq(self, value: impl Into<String>) -> Self`
       - `ts_starts_with(self, prefix: impl Into<String>) -> Self`
       - `ts_ends_with(self, suffix: impl Into<String>) -> Self`
       - 每个方法之前加单行 rustdoc 注释，示例：`/// 时间戳包含指定子串（FILTER-01）。`

    6. **FILTER-02 tag 四方法**：
       - `tag_is_some(self) -> Self` → 闭包 `|r| r.tag.is_some()`
       - `tag_is_none(self) -> Self` → 闭包 `|r| r.tag.is_none()`
       - `tag_eq(self, value: impl Into<String>) -> Self` → 闭包 `|r| r.tag.as_deref() == Some(&value)`
       - `tag_contains(self, pattern: impl Into<String>) -> Self` → 闭包 `|r| r.tag.as_deref().is_some_and(|t| t.contains(&pattern))`

    7. **FILTER-03 ep 四方法**（u8 数值）：
       - `ep_eq(self, value: u8) -> Self`
       - `ep_gt(self, value: u8) -> Self`
       - `ep_lt(self, value: u8) -> Self`
       - `ep_between(self, min: u8, max: u8) -> Self` → 闭包 `|r| r.ep >= min && r.ep <= max`（闭区间）

    8. **FILTER-04 七个字符串元数据字段 × 4 方法 = 28 个方法**：
       按 sess_id → thrd_id → username → trxid → statement → appname → client_ip 顺序，每字段 4 个方法（_contains / _eq / _starts_with / _ends_with），方法模板：
       ```
       pub fn <field>_contains(self, pattern: impl Into<String>) -> Self {
           let pattern = pattern.into();
           self.add(move |r| r.<field>.contains(&pattern))
       }
       ```
       **手写展开 28 个方法，不引入 paste crate**（RESEARCH.md 决策：避免外部依赖、保证每方法独立 rustdoc）。
       每方法 ≤ 5 行符合 CLAUDE.md 函数 < 40 行约束。每方法 1 行 rustdoc，例如：`/// sess_id 包含指定子串（FILTER-04）。`

    9. **FILTER-05 sql 四方法**：sql_contains / sql_eq / sql_starts_with / sql_ends_with，与 ts 同模式。

    10. **FILTER-06 exectime 三方法**（f32，**不实现 eq**）：
        - `exec_time_gt(self, min_ms: f32) -> Self` → `|r| r.exectime > min_ms`
        - `exec_time_lt(self, max_ms: f32) -> Self` → `|r| r.exectime < max_ms`
        - `exec_time_between(self, min_ms: f32, max_ms: f32) -> Self` → `|r| r.exectime >= min_ms && r.exectime <= max_ms`
        - rustdoc 注明"参数单位为毫秒；f32 精度有限，不提供 eq 方法"。

    11. **FILTER-07 rowcount 四方法**（u32 数值）：rowcount_eq / rowcount_gt / rowcount_lt / rowcount_between。

    12. **FILTER-08 exec_id 四方法**（i64 数值）：exec_id_eq / exec_id_gt / exec_id_lt / exec_id_between。

    13. **实现 Default**：
        ```
        impl Default for FilterBuilder {
            fn default() -> Self { Self::new() }
        }
        ```

    14. **#[cfg(test)] mod tests 单元测试块**（在文件末尾）：
        - 顶部辅助函数 `fn make_record() -> Sqllog { Sqllog { ts: "2024-06-01 10:00:00.000".to_string(), tag: Some("SEL".to_string()), ep: 2, sess_id: "0xABC".to_string(), thrd_id: "123".to_string(), username: "alice".to_string(), trxid: "0".to_string(), statement: "0x1".to_string(), appname: "myapp".to_string(), client_ip: "10.0.0.1".to_string(), sql: "SELECT * FROM users".to_string(), exectime: 150.0, rowcount: 10, exec_id: 999 } }`
        - 每个字段至少 2 个测试（true 路径 + false 路径），覆盖 FILTER-01~08 全部谓词：
          - test_ts_contains / test_ts_eq / test_ts_starts_with / test_ts_ends_with（4 个）
          - test_tag_is_some / test_tag_is_none / test_tag_eq / test_tag_contains（4 个）
          - test_ep_eq / test_ep_gt / test_ep_lt / test_ep_between（4 个）
          - 7 字段 × 1 个代表性测试（每字段一个 contains 测试足够覆盖宏展开正确性）= 7 个
          - test_sql_contains / test_sql_eq / test_sql_starts_with / test_sql_ends_with（4 个）
          - test_exec_time_gt / test_exec_time_lt / test_exec_time_between（3 个）
          - test_rowcount_eq / test_rowcount_gt / test_rowcount_lt / test_rowcount_between（4 个）
          - test_exec_id_eq / test_exec_id_gt / test_exec_id_lt / test_exec_id_between（4 个）
        - FILTER-09 测试：
          - `test_empty_filter_matches_all`：`FilterBuilder::new().build().matches(&make_record())` 返回 true（空谓词链 all() 返回 true）
          - `test_multiple_predicates_all_must_pass`：链式三条件全满足返回 true；其中一条件改为不满足返回 false
          - `test_and_semantics_short_circuit`：链式至少 3 条件，第一条件不满足，验证 matches 返回 false（短路通过 .all() 自动满足）
        - **测试函数总数约 38 个**，每个测试 ≤ 8 行（符合 CLAUDE.md 函数 < 40 行）。

    15. **断言无 Cargo.toml 变更**：本任务不引入新依赖；如需引入需明确指出（默认不需要）。

    禁止：本任务不修改 src/parser/、src/lib.rs、src/record.rs、src/filter/adapter.rs、src/filter/mod.rs（mod.rs 在 Task 2 处理）、examples/、tests/。
  </action>
  <verify>
    <automated>cd /Users/guang/Projects/dm-database-parser-sqllog && cargo build 2>&1 | tail -5 && cargo test --lib filter::builder 2>&1 | tail -10 && cargo clippy --lib -- -D warnings 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - 文件 src/filter/builder.rs 存在：`test -f src/filter/builder.rs && echo OK` 输出 OK
    - 公开类型存在：`grep -c '^pub struct Filter ' src/filter/builder.rs` 返回 1
    - 公开类型存在：`grep -c '^pub struct FilterBuilder ' src/filter/builder.rs` 返回 1
    - Filter::matches 方法存在：`grep -c 'pub fn matches' src/filter/builder.rs` 返回 1
    - FilterBuilder::new 存在：`grep -c 'pub fn new' src/filter/builder.rs` 返回 1
    - FilterBuilder::build 存在：`grep -c 'pub fn build' src/filter/builder.rs` 返回 1
    - Default 实现存在：`grep -c 'impl Default for FilterBuilder' src/filter/builder.rs` 返回 1
    - 谓词存储类型正确：`grep -c 'Box<dyn Fn(&Sqllog) -> bool + Send + Sync>' src/filter/builder.rs` ≥ 2（Filter 与 FilterBuilder 字段各一处）
    - ts 字段 4 方法：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn ts_(contains|eq|starts_with|ends_with)\b'` 返回 4
    - tag 字段 4 方法：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn tag_(is_some|is_none|eq|contains)\b'` 返回 4
    - ep 字段 4 方法：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn ep_(eq|gt|lt|between)\b'` 返回 4
    - 7 字符串元数据字段 × 4 方法 = 28：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn (sess_id|thrd_id|username|trxid|statement|appname|client_ip)_(contains|eq|starts_with|ends_with)\b'` 返回 28
    - sql 字段 4 方法：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn sql_(contains|eq|starts_with|ends_with)\b'` 返回 4
    - exec_time 字段 3 方法（无 eq）：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn exec_time_(gt|lt|between)\b'` 返回 3
    - exec_time 无 eq 方法：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn exec_time_eq\b'` 返回 0
    - rowcount 字段 4 方法：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn rowcount_(eq|gt|lt|between)\b'` 返回 4
    - exec_id 字段 4 方法：`grep -v '^\s*//' src/filter/builder.rs | grep -cE 'pub fn exec_id_(eq|gt|lt|between)\b'` 返回 4
    - AND 语义通过 `.all()` 实现：`grep -c 'predicates.iter().all' src/filter/builder.rs` 返回 1
    - `cargo build` 退出码 0（确认整个 crate 仍能编译，即使 filter/mod.rs 尚未声明 builder，注意 Task 2 完成 mod 声明前 builder.rs 不会被引用，但单独编译应可通过）
    - `cargo test --lib filter::builder 2>&1 | grep -E 'test result' | tail -1` 必须含 `ok. N passed; 0 failed`（N ≥ 35）— **注：本断言需在 Task 2 完成 mod 声明后才能跑通**；如果 Task 1 完成时 mod.rs 还未更新，可放宽为"文件单独通过 rustc 编译"，并在 Task 2 完成后再次校验本断言。
    - `cargo clippy --lib -- -D warnings` 退出码 0（零警告）
    - 文件函数全部 < 40 行：`awk '/^\s*pub fn |^\s*fn /{name=$0; lc=0; depth=0; in_fn=1} in_fn{for(i=1;i<=length($0);i++){c=substr($0,i,1); if(c=="{") depth++; if(c=="}") depth--}; lc++; if(depth==0 && lc>1){if(lc>40) print "TOO LONG:", name, lc; in_fn=0}}' src/filter/builder.rs` 无 TOO LONG 输出
  </acceptance_criteria>
  <done>
    src/filter/builder.rs 文件存在；Filter + FilterBuilder + 50± 链式方法 + Default 实现 + #[cfg(test)] mod tests 单元测试全部就位；cargo build 通过；cargo clippy 零警告；单元测试在 Task 2 完成 mod 声明后能通过 cargo test --lib filter::builder。
  </done>
</task>

<task type="auto">
  <name>Task 2: 更新 src/filter/mod.rs 声明 builder 子模块并重导出 Filter / FilterBuilder，验证整体 crate 通过测试和 clippy</name>
  <files>src/filter/mod.rs</files>
  <read_first>
    - src/filter/mod.rs（当前内容仅 `pub(crate) mod adapter;`）
    - src/filter/builder.rs（Task 1 产物，确认 pub struct Filter 与 pub struct FilterBuilder 已存在）
    - src/parser/mod.rs（参考 `pub mod builder; pub use builder::LogParserBuilder;` 风格）
    - .planning/phases/11-filterbuilder/11-PATTERNS.md（"src/filter/mod.rs 更新"章节，PATTERNS 中给出 lib.rs 与 mod.rs 两种导出选项；选项 A：模块保持 pub(crate)，只重导出顶层符号 — Plan 11-02 任务里处理 lib.rs 重导出，本任务只需让 filter/mod.rs 在 crate 内可访问 builder::Filter / FilterBuilder）
  </read_first>
  <action>
    1. **修改 src/filter/mod.rs 为完整的两节内容**（覆写整个文件 3 行）：
       ```
       pub(crate) mod adapter;
       pub mod builder;

       pub use builder::{Filter, FilterBuilder};
       ```
       说明：
       - `pub mod builder;` —— builder 作为公开 API 子模块；与 parser/mod.rs 的 `pub mod builder;` 风格一致。
       - `pub use builder::{Filter, FilterBuilder};` —— filter 模块内重导出。**Plan 11-02 将在 lib.rs 顶层再次 pub use 暴露给 crate 外用户**（注意：当前 lib.rs 中 filter 模块本身是 pub(crate)，所以本 mod.rs 的 pub 在 crate 边界外不可见 — 这是有意为之，避免本 plan 改动 lib.rs，避免与 Plan 11-02 文件冲突）。
       - `pub(crate) mod adapter;` 保持不变（内部使用）。

    2. **不修改 src/lib.rs**：lib.rs 的顶层 `pub use filter::FilterBuilder;` 重导出由 Plan 11-02 Task 1 处理（避免 wave 1 与 wave 2 文件冲突）。

    3. **验证整个 crate 编译与测试**：
       - `cargo build` —— 整个 crate（包括新增的 filter::builder）应编译通过。
       - `cargo test --lib filter::builder` —— Task 1 内嵌的单元测试现在可被 cargo test 发现并执行（mod 声明已就位）。
       - `cargo clippy --all-targets -- -D warnings` —— 零警告。
       - **如果 Filter / FilterBuilder 仅在 src/filter/builder.rs 内部测试中使用、外部尚未消费**：可能出现 `dead_code` 警告。
         缓解策略：本 Plan 不引入 lib.rs 导出，所以从 crate 内部链路看 Filter / FilterBuilder 暂时未被使用。如果 clippy 因此报 dead_code：
         - 选项 1（首选）：在 builder.rs 文件顶部为 `pub struct Filter` 与 `pub struct FilterBuilder` 暂时各加 `#[allow(dead_code)]` 标注，并附注释 `// allow until Plan 11-02 wires in lib.rs exports`。Plan 11-02 完成 lib.rs 重导出后将这两处 `#[allow(dead_code)]` 删除。
         - 选项 2：本 Plan 直接把 `pub mod builder;` + `pub use builder::{Filter, FilterBuilder};` 写入 mod.rs，然后让 lib.rs 也跟着重导出。但这会破坏 wave 1 / wave 2 的文件分工（与 Plan 11-02 共用 lib.rs）。**首选选项 1**。

    4. **决策记录**：如果选用选项 1 的 `#[allow(dead_code)]`，在 Task 2 完成时的 SUMMARY 里明确指出，方便 Plan 11-02 删除。
  </action>
  <verify>
    <automated>cd /Users/guang/Projects/dm-database-parser-sqllog && cargo build 2>&1 | tail -3 && cargo test --lib filter 2>&1 | tail -10 && cargo clippy --all-targets -- -D warnings 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - `grep -c '^pub mod builder;' src/filter/mod.rs` 返回 1
    - `grep -c '^pub(crate) mod adapter;' src/filter/mod.rs` 返回 1
    - `grep -c '^pub use builder::{Filter, FilterBuilder};' src/filter/mod.rs` 返回 1
    - src/filter/mod.rs 总行数 ≤ 6（精简：mod 声明 + 重导出，不含其他逻辑）
    - `cargo build` 退出码 0
    - `cargo test --lib filter 2>&1 | grep -E 'test result: ok\. [0-9]+ passed' | tail -1` 匹配 `[0-9]+ passed`，且 N ≥ 35（覆盖 builder.rs 全部单元测试）
    - `cargo test --lib filter 2>&1 | grep 'test result' | tail -1` 含 `0 failed`
    - `cargo clippy --all-targets -- -D warnings` 退出码 0
    - 如果选择选项 1：`grep -c '#\[allow(dead_code)\]' src/filter/builder.rs` ≥ 2（应在 Filter 与 FilterBuilder 各一处，Plan 11-02 删除）
  </acceptance_criteria>
  <done>
    src/filter/mod.rs 含 3 行声明 + 重导出；`cargo build` / `cargo test --lib filter` / `cargo clippy --all-targets -- -D warnings` 全部通过；filter/builder.rs 全部单元测试通过；Plan 11-02 可以引用 `crate::filter::builder::{Filter, FilterBuilder}`。
  </done>
</task>

</tasks>

<verification>
Plan 11-01 端到端验收：

1. **新增文件存在**：
   - `test -f src/filter/builder.rs && wc -l src/filter/builder.rs` 行数应在 350-600 区间（14 字段约 50 公开方法 + Default + 30+ 单元测试 + rustdoc）
2. **mod.rs 状态正确**：
   - `cat src/filter/mod.rs` 输出包含 `pub mod builder;` 和 `pub use builder::{Filter, FilterBuilder};`
3. **API 表面完整**（每字段 grep 验证已在 Task 1 acceptance_criteria 中列出）
4. **测试覆盖**：
   - `cargo test --lib filter::builder 2>&1 | grep 'test result'` 应显示 N ≥ 35 passed; 0 failed
5. **编译验证**：
   - `cargo build` 退出码 0
   - `cargo build --tests` 退出码 0
   - `cargo clippy --all-targets -- -D warnings` 退出码 0
6. **不破坏现有测试**：
   - `cargo test 2>&1 | grep 'test result' | tail -1` —— 整体测试数应等于 Phase 10 收尾时基线数 + 本 Plan 新增单元测试数（约 35-40）；0 failed
</verification>

<success_criteria>
- FILTER-01 ~ FILTER-08：14 个字段全部对应的链式方法实现（约 50 个方法）；每方法对应至少一个单元测试覆盖 true / false 路径
- FILTER-09：Filter::matches 通过 `predicates.iter().all()` 实现 AND 短路；单元测试 test_multiple_predicates_all_must_pass + test_and_semantics_short_circuit + test_empty_filter_matches_all 全部通过
- Filter / FilterBuilder 类型在 src/filter/builder.rs 公开；src/filter/mod.rs 重导出
- cargo test / cargo clippy --all-targets -- -D warnings / cargo build 全部通过
- 函数全部 < 40 行（符合 CLAUDE.md）
- 不引入新外部依赖（Cargo.toml 不变）
- FILTER-10 不在本 Plan 范围（由 Plan 11-02 实现）
</success_criteria>

<output>
完成后创建 .planning/phases/11-filterbuilder/11-01-SUMMARY.md，记录：
- src/filter/builder.rs 行数与方法计数（按 FILTER-01~08 分类）
- src/filter/mod.rs 最终内容
- cargo test 新增单元测试数量（应 ≥35）和整体测试通过数
- cargo clippy 输出（应零警告）
- 是否使用了 `#[allow(dead_code)]` 临时标注（如有，列出位置，由 Plan 11-02 移除）
- 留给 Plan 11-02 的衔接点：Filter / FilterBuilder 已 `pub` 公开；Filter 满足 Send + Sync（可在 spawn_blocking 中传递）
</output>
</content>
</invoke>