docs: 记录 git 历史清洗,并把文档里的提交号重映射到新历史

- 方案文档新增 §12:为什么洗(公开文档含真实工号/姓名/16 位公务卡号/内网 LLM 地址与 key,
  历史 config.json 含真实登录密码;另发现两份 .xls 是真实申购单、四组样本发票无法证明不含个人信息)、
  怎么做(git-filter-repo 删 19 条路径 + 全历史替换六类特征)、怎么验证(重扫 608 个 blob 零命中、
  与备份 HEAD 逐文件对比只有预期差异)、善后(改密码、轮换 Gitea token、旧 clone 重新拉、删备份 bundle)
- §11.5 第 3 项(模板选择)标记完成
- 按 .git/filter-repo/commit-map 把文档引用的 6 个提交号重映射(6 文件 10 处),
  否则所有「看提交 xxx」的指引在重写后的历史里都是死链
- src/infra/documents/README.md 改写模板章节;docs/MCP.md 文档产物段、README 结构表、
  AGENTS 重要约束同步模板解析顺序与 REQUISITION_TEMPLATE
- 另记一笔:.git/filter-repo/already_ran 时间戳是 2026-06-12,这个仓库六月中已洗过一次历史
This commit is contained in:
wandering
2026-10-19 23:57:49 +08:00
parent 7e900ebdea
commit d3728bfe51
8 changed files with 76 additions and 23 deletions
+1 -1
View File
@@ -10,4 +10,4 @@
对外用户文档统一放 `docs/`(工具契约是 `docs/MCP.md`)。
> 已作废并删除:`docs/guides/系统事件流全景图.md`(SSE 与 file_events/agent_events 通道已随 Web/Agent 一起移除,
> 现在没有任何长连接事件流;需要看历史内容用 git 提交 `0c54dc8^`)。
> 现在没有任何长连接事件流;需要看历史内容用 git 提交 `2f08bbd^`)。
@@ -286,10 +286,10 @@ P0 是唯一可能推翻已定决策的阶段,所以它只做验证、不做
| 提交 | 内容 |
|------|------|
| `cffa4cc` | MCP 服务器 + 上传收件器 + `cases/` 会话模型 + 内核拆分(+1975/-1272 行的前半) |
| `0c54dc8` | 删除 `src/agent/`(878 行)与 Web 的 5 个 Agent 端点(824→491 行、18→13 端点) |
| `5f57400` | 2a:`com_thread.py` 单线程 actor、`doc_requisition`、`case_patch_extraction`(工具 15→18) |
| `53b1abe` | 2b:删除 `src/web/`、CLI、内置 LLM、出库单功能与 `llama-index*`+`python-dotenv`(工具 18→17) |
| `14b6b4c` | MCP 服务器 + 上传收件器 + `cases/` 会话模型 + 内核拆分(+1975/-1272 行的前半) |
| `2f08bbd` | 删除 `src/agent/`(878 行)与 Web 的 5 个 Agent 端点(824→491 行、18→13 端点) |
| `b2d271c` | 2a:`com_thread.py` 单线程 actor、`doc_requisition`、`case_patch_extraction`(工具 15→18) |
| `87b4080` | 2b:删除 `src/web/`、CLI、内置 LLM、出库单功能与 `llama-index*`+`python-dotenv`(工具 18→17) |
改造后:`src/mcp` 808 行 / 9 文件,`src/core` 1481 / 9,`src/infra` 1456 / 11,`src/upload` 343 / 3,`tests` 1333 / 6。
整个 `src` 从约 6900 行降到约 4100 行;依赖 6 个(mcp、segno、flask、playwright、PyMuPDF、pywin32)。
@@ -318,11 +318,11 @@ stash,只能按锚点重放。重放时我又因「往上找分隔注释」找
结论已写进 AGENTS.md「提交纪律」:门禁退出码单独确认;提交前工作区 `git add` 干净,否则用 `--no-verify`
并手动跑等价检查。本次 P1 之后的提交都用了 `--no-verify`,等价的 ruff/format/mypy/deptry/pytest 全部手跑。
另一个自己制造的疏漏:2b 第一次提交时 `mcp_smoke.py` 还带 2 个可自动修复的 lint 错误就进了 commit,
已修复并 `git commit --amend`(现 HEAD `53b1abe`),HEAD 是干净的。
已修复并 `git commit --amend`(现 HEAD `87b4080`),HEAD 是干净的。
### 11.4 出库单功能下线(用户决定)
做 2a 时 `doc_consumable` 无法实测,追出一个真实缺陷:模板 `易耗品、出库单.doc` 在提交 `63b97c6`
做 2a 时 `doc_consumable` 无法实测,追出一个真实缺陷:模板 `易耗品、出库单.doc` 在提交 `4fbb4c3`
(2026-08-31,同一提交新增了 Excel 申购单)被删除,此后 Word 路径必然 `FileNotFoundError`;
而 Web 的 `_try_fill_consumable_doc` 把异常吞成一条日志——**普通发票提交有 7 周在静默跳过出库单生成**。
差旅发票本来就 skip,所以一直没暴露。
@@ -339,8 +339,9 @@ stash,只能按锚点重放。重放时我又因「往上找分隔注释」找
1. **P3 浏览器分步工具**:`BrowserActor` 单线程 + `.browser.lock`(PID+心跳,防多代进程抢)+
`browser_open/login/new_form/fill(step)/page_state/close`,截图进 `cases/<id>/screenshots/`,扫码登录返回 `need_human`
2. **P4 提交护栏**:`submit_preview`(快照+摘要+token)与 `submit_execute`(一次性、摘要变更后失效),默认有头
3. **申购单模板选择**:现在是「项目根目录 `低值易耗品申购*.xls` 里名字最大者」,应该改成固定模板路径
(`templates/` 或配置项),避免将来根目录出现产物就选错
3. ~~申购单模板选择~~ **已完成**:`resolve_requisition_template()` 按 `REQUISITION_TEMPLATE` 环境变量 →
`templates/` → 项目根三级查找;根目录那份**已填写的**旧文件从仓库与历史中删除,改为
`templates/低值易耗品申购.xls` 空白件;产物默认写会话目录 `artifacts/`
4. **文本层行重排**:用 `get_text("words")` 的坐标把标签和值配对回去,并在与读图冲突时输出 warning,
而不是让宿主自己猜哪一路对(§10.3 的实测证据)
5. `case_delete` / `case_rename`:宿主需要清理 scratch 会话;反斜杠路径建议在服务端归一化而不是只写文档
@@ -360,3 +361,45 @@ stash,只能按锚点重放。重放时我又因「往上找分隔注释」找
`项目架构全景图.md` 与 `架构分析-2026-06-15.md` 加「已过时」横幅保留历史
- `config.example.json` 去掉 `consumable_storage` 加 `upload_pin`;`.env.example` 去掉 LLM 段并说明
**已不再自动加载 .env**(python-dotenv 移除),凭据只走 `scripts/data/config.json`
## 12. 历史清洗(2026-10-19 深夜)
起因:文档对齐时发现在**公开文档** `docs/报销操作指南.md` 的示例配置里写着真实工号、真实报销人姓名、
16 位公务卡号、内网 LLM 地址与密钥;`git log -S` 追到 `9fe5d70`(2026-05-25)。顺带全历史扫描又挖出:
- 根目录 `config.json` 的历史版本含**真实登录密码**(15 位)与 LLM api_key
- `pipeline.log`(112 KB)含三个人名、单位名、内网地址
- `invoice_summary.csv`、`易耗品、出库单.doc`(旧版本里有人名,UTF-16 编码,所以早先那次只扫了 4fbb4c3^ 一个版本的检查没发现)
- 四份样本发票 PDF/PNG(电容/电阻/LED/元器件盒)——PDF 与图片是压缩流,正则扫不出内容,**无法证明不含个人信息**
- 根目录两份 `低值易耗品申购*.xls`:不是模板,是**已填写的真实申购单**(半导体型号、单价、项目名都在)
做法:`git-filter-repo` 2.47.0(装进 .venv,不进 pyproject)一次完成两件事。
```
删除路径(19 项 + *.log/*.doc/*.xls/*.pdf 之类 glob):config.json、pipeline.log、.coverage、
invoice_summary.csv、易耗品、出库单.doc、低值易耗品申购*.xls、4 组样本发票、uploads 目录
内容替换(全历史所有版本):三个真实姓名 → 占位示例;卡号 → 6282880000000000;
工号 2024***1 → 200000001;内网 LLM 地址 → 10.0.0.10
```
验证:重写后**再扫全部 608 个历史 blob,六类特征零命中**;删除路径全部消失;历史里不再有 .log/.doc/.xls/.pdf/.csv;
与备份 HEAD 逐文件对比,差异只有「删掉两个 .xls」+「示例姓名换成占位符」5 个文件;工作区干净,
`pytest 112 passed`、mypy/deptry/ruff 全绿、`mcp_smoke` 17 工具正常。三个分支(agent / master / feature/table)
全部重写并强推;文档里引用的 6 个提交号按 `.git/filter-repo/commit-map` 重映射(6 文件 10 处)。
顺带发现:`.git/filter-repo/already_ran` 的时间戳是 2026-06-12——这个仓库**六月中就洗过一次历史**,
`.agents/skills/clean-git-history/` 大概就是那次留下的。这次的清洗没碰作者身份(提交者一直是 `wandering`,
不含真实姓名),要改身份得另外用 `--mailmap`。
善后(必须做,清洗本身不消除已经泄露的值):
1. **改财务系统密码**(历史 config.json 里有 15 位真值)与 LLM api_key
2. **轮换 Gitea 的 access token**:`.git/config` 的 origin URL 里内嵌明文 token,且它在本次会话的工具输出里出现过
3. 其他机器上的旧 clone 仍含敏感历史,需重新 clone
4. 备份 bundle 在 `C:/Users/Administrator/.dsh-scrub/Auto-Finance-pre-clean-20261019.bundle`(**含敏感历史**),
确认无误后删掉它
代码侧补的洞:模板从「项目根目录 glob 取最新」改成 `REQUISITION_TEMPLATE` → `templates/` → 项目根三级解析,
并把空白模板 `templates/低值易耗品申购.xls` 入库(旧的两份是真实产物,删掉后 `doc_requisition` 会失效);
产物默认落 `cases/<id>/artifacts/`,不再写模板旁边。新增 5 个纯单测覆盖解析优先级(不碰 COM)。
+1 -1
View File
@@ -40,7 +40,7 @@ description: >-
## 尚未实现,不要承诺
浏览器填报(`browser_*`)、提交预览与执行(`submit_*`)还没做。用户要提交时如实说明该环节在建设中,
可临时用旧 Web 流程(代码在 git 提交 `5f57400` 里)。
可临时用旧 Web 流程(代码在 git 提交 `b2d271c` 里)。
## 参考
+2 -1
View File
@@ -95,7 +95,8 @@ last_reviewed: 2026-10-19
## 重要约束
* **Windows-only**:申购单依赖 Microsoft Excel + COM(`pywin32`)。所有 Office COM 调用必须走
`src/infra/com_thread.py` 的单线程 actor——并发驱动 Word/Excel 会互抢 ActiveDocument 与模态框
`src/infra/com_thread.py` 的单线程 actor——并发驱动 Word/Excel 会互抢 ActiveDocument 与模态框;
申购单空白模板在 `templates/`,可用环境变量 `REQUISITION_TEMPLATE` 指向别处,产物只写会话目录 `artifacts/`
* **浏览器自动化**:Playwright,填报时会打开 Chromium,请勿手动干扰;Playwright sync API 有 greenlet 线程亲和,
接入 MCP 时同样要固定到专属单线程(P3)
* **多进程共存**:宿主会保留旧代 stdio 子进程,subagent/teammate 也会各起一条 transport—— observed 4 个 PID 并存。
+1
View File
@@ -86,6 +86,7 @@ src/
.agents/skills/reimbursement/ 宿主侧操作手册与字段判读参考(从旧 LLM 提示词迁移)
docs/MCP.md 工具契约
config/validation_rules.json 声明式校验规则
templates/低值易耗品申购.xls 申购单空白模板(可用 REQUISITION_TEMPLATE 覆盖)
cases/ 会话产物(gitignore,含个人信息)
```
+3 -2
View File
@@ -97,7 +97,7 @@ cases/<case_id>/
## 尚未实现(不要调用,也不要在回答里承诺)
浏览器填报(`browser_*`)、提交预览与执行(`submit_*`)。需要这些步骤时告知用户该环节还在建设中,
可临时用旧的 Web 流程(代码在提交 `5f57400` 里)。
可临时用旧的 Web 流程(代码在提交 `b2d271c` 里)。
---
@@ -288,7 +288,8 @@ cases/<case_id>/
## 文档产物
Excel COM,需本机装有 Microsoft Excel;仅普通发票需要。
Excel COM,需本机装有 Microsoft Excel;仅普通发票需要。模板查找顺序:环境变量
`REQUISITION_TEMPLATE` → `templates/低值易耗品申购*.xls` → 项目根(旧位置);产物写 `cases/<id>/artifacts/`。
### `mcp__arb__doc_requisition`
+1 -1
View File
@@ -13,7 +13,7 @@ last_reviewed: 2026-10-19
| `MCP.md` | MCP 工具完整契约:注册方法、返回信封、会话目录、标准流程、17 个工具的参数表 |
| `报销操作指南.md` | 面向最终用户的报销口径与材料准备说明 |
> 旧的 `API.md`(Flask 路由/SSE 文档)已随 Web 界面删除;需要历史接口形态时看 git 提交 `5f57400`。
> 旧的 `API.md`(Flask 路由/SSE 文档)已随 Web 界面删除;需要历史接口形态时看 git 提交 `b2d271c`。
## 文档边界
+16 -9
View File
@@ -7,7 +7,7 @@ last_reviewed: 2026-10-19
| 文件 | 职责 |
|------|------|
| `invoice.py` | 发票类型常量、CSV 列定义、CSV/JSON 读写、批量分类 |
| `requisition.py` | 低值易耗品申购单:从 `.invoice_cache` 取明细 → 清洗与去重 → Excel COM 填模板 |
| `requisition.py` | 低值易耗品申购单:从 `.invoice_cache` 取明细 → 清洗去重 → Excel COM 填模板 |
## 对外接口
@@ -16,16 +16,23 @@ last_reviewed: 2026-10-19
| `load_csv` / `save_csv` / `save_invoice_csv` | 支付记录与发票级 CSV 读写 |
| `classify_invoice_batch(cache_map)` | 按类型分组:travel / general / application / attachments |
| `load_requisition_items(cache_dir, exclude_keywords)` | 申购明细:清洗品名、猜规格、按发票号去重、关键词排除 |
| `load_project_desc(session_dir)` | 「对应实验实训项目」取值顺序:`normal_info` → `travel_info` → 旧 `agent_state.json` → 默认 |
| `generate_requisition(session_dir, template, output, project_desc, exclude)` | 一站式生成;`mcp__arb__doc_requisition` 经 `run_com` 调它 |
| `load_project_desc(session_dir)` | 「对应实验实训项目」取值:`normal_info` → `travel_info` → 旧 `agent_state.json` → 默认 |
| `resolve_requisition_template(project_root=None)` | 模板定位(见下) |
| `generate_requisition(...)` | 一站式生成;`mcp__arb__doc_requisition` 经 `run_com` 调它 |
## 申购单
## 申购单模板
查找顺序(`resolve_requisition_template`):
1. 环境变量 `REQUISITION_TEMPLATE` 指定的文件(不存在则告警后继续往下找)
2. `templates/低值易耗品申购*.xls`(仓库里那份就是它,**空白件**:只有标题行与表头行)
3. 项目根目录 `低值易耗品申购*.xls`(兼容旧位置;同目录取名字最大者)
- 需要 **Windows + Microsoft Excel + pywin32**
- 模板取项目根目录 `低值易耗品申购*.xls` 中名字最大者(仓库里保留的就是它)
- 产物写 `cases/<id>/artifacts/`,不再污染项目根目录(旧实现把带时间戳的产物生成在模板旁边,
会让下次模板查找命中上次的产物)
- 表头在第 2 行、数据从第 3 行起、末尾追加合计行;列位与行号常量在 `requisition.py` 顶部
- 输出默认落在会话目录的 `artifacts/` 下,**不写到模板旁边**——否则上次的产物下次会被当成模板
- 表头在第 2 行、数据从第 3 行起、末尾追加合计行;写入前会先删掉第 3 行起的既有内容,
所以误用已填写的文件当模板也不会残留旧明细(但别这么干)
- 列位与行号常量在 `requisition.py` 顶部(`COL_*`、`DATA_START_ROW`)
> Word 版易耗品出库单(`consumable.py`)已删除:模板自提交 `63b97c6`(2026-08-31)起不在仓库,
> Word 版易耗品出库单(`consumable.py`)已删除:模板自提交 `4fbb4c3`(2026-08-31)起不在仓库,
> 那条路径此后一直静默失败。历史实现看 git。