Restore the contracts the split dropped and correct the record

A split moves code; it does not get to rewrite what the code promises. The
first pass collapsed three multi-line docstrings into one-liners and the
line counts in the checklist were measured before that happened.

- store_search.py: search_atoms keeps its filter-shape rule and the
  as_holder sharing rule, FtsPage keeps why the cursor is an offset
- bench/run.py: the module docstring says what the five arms are again
- CHECKLIST: 116/341/83 lines, 11 call sites over 7 methods, and the
  migration ladder now cites 181/186/248/250 instead of the parent's lines

Repo-wide multi-line docstrings are back to 114, the parent's count.
This commit is contained in:
ZacharyZhang-NY committed 2026-10-08 10:02:24 -04:00
1 parent 9cb46c18d3
commit 28959c51c6
3 files changed
+24 -7

No files matched your search

+3 -3
View File
@@ -321,7 +321,7 @@
- [x] **p1-6** 代码卫生
- [x] p1-6.1 每个文件 ≤500 行
- 验收命令 `python -m bench.filesize_check` → `OK: no tracked source file exceeds 500 lines`。这个脚本是本轮加的:500 行规则之前只靠记忆执行
- `evals.py` 2413 行在 p0-6 已拆成 `proofs/*`(最大 430)。本轮拆的:`db.py` 503→396(BM25 检索搬进 `store_search.py` 105)、`bench/run.py` 609→351(`query_set.py` 80 + `report.py` 211)、`benchmarks.py` 619→424(`benchmark_scoring.py` 262)、`training.py` 1630→94(`training_{common,encode,metrics,datasets,lora,retrieval}.py` = 108/177/307/387/408/358,`TrainingService` 留成薄壳喂 `cli.py` 的 10 个调用点)、`schemas.py` 534→469(p1-5 拆出 `status_schemas.py` 74)
- `evals.py` 2413 行在 p0-6 已拆成 `proofs/*`(最大 430)。本轮拆的:`db.py` 503→396(BM25 检索搬进 `store_search.py` 116)、`bench/run.py` 609→341(`query_set.py` 83 + `report.py` 211)、`benchmarks.py` 619→424(`benchmark_scoring.py` 262)、`training.py` 1630→94(`training_{common,encode,metrics,datasets,lora,retrieval}.py` = 108/177/307/387/408/358,`TrainingService` 留成薄壳喂 `cli.py` 的 11 个调用点、7 个方法)、`schemas.py` 534→469(p1-5 拆出 `status_schemas.py` 74)
- 「拆分不改行为」的证据:脚本把每个搬走函数的**函数体去缩进后与 HEAD 逐字节比对**——training 侧 53 个、benchmarks 侧 21 个,**全部相同,0 处不同,0 个缺失**。方法转函数只重写 def 头,`self.settings`→`settings` 这类替换是正文里唯一的改动
- 新增 `backend/tests/test_training_split.py`(6 条):CPU 能跑的纯函数逐个测;最后一条用 `inspect.signature` 断言薄壳参数表 = 模块函数参数表去掉注入的两个参数——这条就是抓 `self` 泄漏那类 bug 的
- [x] p1-6.2 删 dead code 与未调用函数
@@ -329,10 +329,10 @@
- 两个为什么是死的:`memory.py` 靠读每颗卡带自己的 `sources.json` 回答归属(导出的卡带可以不在库里);`set_mounts(database, [], stamp)` 换集合本来就能清空挂载表
- 顺手删 `backend/build/`(712KB 的 `python -m build` 残留,gitignore 内,0 个跟踪文件)——它让 grep 撒谎
- [x] p1-6.3 删 v1/v2/v3 废弃端口
- 量完的结论是**没有可删的**,不是没做:每个 `v1/v2/v3` 都承重——磁盘格式标签(`ely-eye-memory-capsule-v1` `proofs/checks_cartridge.py:128`、`ely-eye-{text,visual}-vectors-v1`、`ely-eye-kivi-replay-v1` `cartridge_assets.py:100,127,158`)、迁移阶梯(`migrations.py:185,190,243`)、矛盾语料 fixture 名(`proofs/contradictions.py:114-135`)、第三方名字(MiniLM-L12-v2、ScreenSpot-v2、LongBench v2)、OpenAI 兼容的 `/v1` URL、缓存键里的 `compiler-v2` salt。没有废弃端口、别名或双代码路径
- 量完的结论是**没有可删的**,不是没做:每个 `v1/v2/v3` 都承重——磁盘格式标签(`ely-eye-memory-capsule-v1` `proofs/checks_cartridge.py:128`、`ely-eye-{text,visual}-vectors-v1`、`ely-eye-kivi-replay-v1` `cartridge_assets.py:100,127,158`)、迁移阶梯(`migrations.py:181,186,248,250`)、矛盾语料 fixture 名(`proofs/contradictions.py:114-135`)、第三方名字(MiniLM-L12-v2、ScreenSpot-v2、LongBench v2)、OpenAI 兼容的 `/v1` URL、缓存键里的 `compiler-v2` salt。没有废弃端口、别名或双代码路径
- [x] p1-6.4 注释压到 ≤1 行
- 口径:`tokenize` 找连续 `#` 行块。全仓 **39 块 / 86 行 → 0 块**,每块压成一行,压的时候留承重那句,实测数字与错误原文不删(`Unable to allocate 582. MiB for an array with shape (5632, 9024, 3)`、`288,266 rows for 165,973 atoms`、`[^[^\W\s]` 在 3.14 会吃掉 CJK)
- docstring 不动(全仓多行 docstring 114 条,与父提交同数):docstring 是模块与函数的契约,不是注释;p1-4 review 用的口径也是「新增多行 docstring 数不得增加」
- docstring 不动(全仓多行 docstring 114 条,与父提交同数):docstring 是模块与函数的契约,不是注释;p1-4 review 用的口径也是「新增多行 docstring 数不得增加」。第一轮 review 抓到搬家途中塌了 3 条——`store_search.py` 的 `search_atoms`(那条「每个过滤条件都是同一条字面 SQL 里的 `? IS NULL OR` 谓词」规则和 `as_holder` 的共享规则)和 `FtsPage`、`bench/run.py` 的模块 docstring——已按原文补回,行数仍全部在 500 以内
- 门禁:`pytest -q` **269 passed**;`ruff check backend/src/ely_eye backend/tests bench` All checks passed;mypy 冷缓存 **24 errors / 9 files**(基线 24/6,training.py 那 6 条随拆分落到 4 个新文件,净新增 0);`pyright` 查 46 个改动文件 → **4 errors**,与 HEAD 单个 `training.py` 在包副本里量到的 4 条同源(qwen_vl_utils 元组返回、两处 `save_pretrained(Path)`、`bitsandbytes` 未解析);go build/vet/test + gofmt 干净;`npm run build` + eslint 干净
---
+13 -3
View File
@@ -12,14 +12,15 @@ from .text_index import fts_expression, fts_terms, rare_terms
@dataclass(frozen=True)
class FtsPage:
"""A page of (atom_id, score) plus whether more matches exist."""
"""One page of BM25 hits plus whether the corpus holds more: the cursor is an offset, and a
ranking this small re-reads rather than keeps a server-side position."""
rows: list[tuple[str, float]]
has_more: bool
def search_fts(database: Database, query: str, limit: int) -> list[tuple[str, float]]:
"""Best BM25 matches over the whole corpus, no filters."""
"""Best BM25 matches over the whole corpus, no filters: the ranking the product needs most days."""
return search_atoms(database, query, limit=limit).rows
@@ -39,7 +40,16 @@ def search_atoms(
cartridge_ids: list[str] | None = None,
as_holder: str | None = None,
) -> FtsPage:
"""BM25 over the FTS index joined to memories that are open right now and readable by the asker."""
"""BM25 over the FTS index joined to memories that are open right now and readable by the asker.
Every filter is a `? IS NULL OR` predicate in one literal statement: the ranking never
depends on string-built SQL, and the planner sees one shape. A memory with no event time
cannot answer a time filter, so it drops out when one is asked for.
`as_holder` applies the sharing rule: a memory is readable by its owner, by anyone named
in its visible_to list, by everyone when it is public, and by anyone when nobody owns it.
A memory with no holder cannot be private to a holder that does not exist. Without an
asker the query runs over the whole library, which is what an operator debugging wants."""
with database.connect() as connection:
expression = fts_expression(rare_terms(connection, fts_terms(query)))
rows = connection.execute(
+8 -1
View File
@@ -1,4 +1,11 @@
"""Run the retrieval ablation over the frozen corpus and write the report: python -m bench.run [--arms bm25_only] [--limit 200] [--no-live]."""
"""Run the retrieval ablation over the frozen corpus and write the baseline report.
Five arms, one database: sparse only, dense only, graph only, the product's weighted
fusion, and fusion plus a cross-encoder reranker. Every number in the report is either
measured or explicitly marked as chance.
Run: python -m bench.run [--arms bm25_only] [--limit 200] [--no-live] [--out bench/reports]
"""
from __future__ import annotations