> 进 git 的判据只有一句:明天这个仓库能不能把它重算出来
一条每天早上七点跑的流水线,产出证据快照、模型摘要、收据、周报。 spool/ 本来是被 git 跟踪的,但最后一次提交停在某一天, 之后 23 天的产出全都只在工作树里:管线照写不误, 而没有任何一步会去提交它。
结论先行¶
一条每天早上七点跑的流水线,产出证据快照、模型摘要、收据、周报。
spool/ 本来是被 git 跟踪的,但最后一次提交停在某一天,
之后 23 天的产出全都只在工作树里:管线照写不误,
而没有任何一步会去提交它。
.gitignore 顶上现在只写了一句判据:
如果这个文件今晚被删掉,明天这个仓库能不能把它重算出来? 能,就忽略;不能,就跟踪 —— 哪怕它是"产物",哪怕产物通常不进 git。
照这句话重新分一遍,结果和"产物不进 git"的直觉正好相反:
| 文件 | 进 git | 理由 |
|---|---|---|
spool/*.evidence.json |
✅ | 当天 07:00 各工作树的快照,含从未提交进任何仓库的工作 |
spool/*.digest.json |
✅ | 每天一次模型调用;明年重跑读到的是另一棵树,答的是另一个问题 |
archive/week-*.json |
✅ | 周模型,除 brief 外确定性 |
spool/*.receipt.txt |
✅ | 纸上当时到底写了什么 |
archive/*.detail.txt |
❌ | 从上面两个确定性重算 |
spool/*.escpos |
❌ | 渲染中间产物 |
代价:42 天 2.3 MB,一年约 15 MB,私有仓库。
而我一开始判错了最不可重算的是哪个。 我以为是周模型 week-*.json,
错了 —— 它是派生的,analyze.py 就是从 spool/ 的 evidence + digest
重建它的,真正不可再生的只有里面 brief 那一小段。
链条最上游、也最补不回来的是 evidence.json:它记的是当天工作树的状态,
包括那些从没变成 commit 的工作(digest 里就有
"all uncommitted, no commits" 这样的句子)。
git history 里没有它,因为"不进 git"正是那些工作的定义。
为什么¶
"产物不进版本控制"这条规矩是对的,但它的真正依据不是"产物"这个分类, 而是可重算性。build 产物不进 git,因为源码加工具链能把它造出来; 一旦某个产物的输入里有不会被保留的东西,它就不再可重算, 而分类名没有变。
不可重算的输入有三种,都容易被忽略:
- 时点状态 —— 某一刻的工作树、某一刻的磁盘占用、某一刻外部 API 的回答。 明天重跑得到的是另一个时点的答案。
- 一次模型调用 —— 同样的 prompt 明年跑,模型不同、输入树不同, 答的是另一个问题。它是观测,不是函数求值。
- 当时呈现给人的那个版本 —— 收据和周报严格说可以重渲染, 但只能用今天这版渲染器重渲染。留着它们是因为以后要吵的是 "当时纸上写的是什么",不是"今天的代码会印出什么"。
反过来,也要防判据被滥用:*.detail.txt 确实能从 spool 文件确定性重算,
所以它该被忽略 —— 判据的价值在于它两个方向都给答案,而不是
"重要的东西都存起来"。
异盘备份不能替代这一条。那份每晚 rsync 的备份
--exclude='.git',保住文件,丢掉历史,而且两块盘在同一台机器里。
进 git 补的正是它补不了的两样:一份离机副本,和一段能 diff 的历史
(见 --exclude='.git/objects' 的备份恢复出来的是尸体,不是仓库)。
怎么用¶
把判据写进 .gitignore 的第一行,而不是记在脑子里。
那个文件是每次有人犹豫"这个要不要忽略"时唯一会被打开的地方。
写成一个问句比写成一份清单好:清单会过期,问句不会。
给未来的自己留一条反向警告。 这个仓库里现在有这么一段:
⚠️ 在 git 里看到几 MB 的 JSON,不要"顺手"把它 gitignore 掉。 那不是构建垃圾,那是这个项目唯一的底稿。
值得照抄。"仓库里有大 JSON" 在几乎所有项目里都是坏味道, 所以一个按常识清理仓库的人(或会话)会正好做错事。 凡是违反通行直觉的决定,都得把理由留在它旁边。
不要让 cron 去提交,但要让它报数。 run_weekly.sh 末尾现在数一次
spool/ 和 archive/ 下未提交的文件数,写进 cron log。
一次提交里该放什么不是 cron 该做的决定 ——
但这个失败模式静默了 23 天,不能再静默第二次。
这是"自动化只负责观测、不负责决定"的一个便宜实例,
同 优雅降级设计好了,却没人被告知——退化必须在产物上留痕。
把不可重算的那一段隔离出来,别让重跑覆盖它。 同一个项目里的
配套改动:确定性字段任何时候都能从证据重算,brief 不能。
--no-brief 重跑(或模型不可达的那天)以前会连同 brief 一起覆盖掉
week-*.json;现在沿用已存的那份并打印 carried_from。
一个产物里混着可重算和不可重算的部分时,重算路径必须对后者是只读的。
进 git 之前扫一遍 key / token / .env。 这批文件是扫描产物,
很容易夹带路径、主机名、凭据。(这个项目扫出的唯一命中是一个任务名
里的 task-yaml-pre-slo-calibration-1911 被切成了 sk-... 的假阳性。)
出处¶
~/Engineering/worklog-printer:.gitignore 顶部的判据与
TRACKED/IGNORED 分类,README.md §2「归档进 git:能重算的不留,
不能重算的必须留(2026-09-01)」,weekly.py:797-802(carried_from),
run_weekly.sh 末尾的未提交文件计数。