上一篇:不会还有人修完解析失败就散会吧?两周后变种又复发 | 从失败提炼 Skill
不会还有人三处拷贝 Rule 还手改吧?漂移事故都是这么来的 | Pack 与 SSOT
系列:《从 Rule 到落地:AI 工程化实践》第 10 篇
标签:SSOT · Pack · 版本管理 · 工程化
专栏:从小白到AI落地大师
序言
Skill 和 Rule 写得再好,若散落各仓库、手工拷贝、随手改,过三个月一定漂。童鞋们可能已经历过:A 仓改了禁止项,B 仓还是旧的,出事时两边都说「我们有规则」。本篇解决 Pack 怎么进业务仓:拷贝、子模块、链接、包版本怎么选,以及 SSOT 原则。
说句自嘲的:我亲手制造过三份 acme-rules 拷贝,每一份都有「小小的本地修正」。后来修一个安全条款要改三处,漏了一处就漏出一个洞。那不是勤奋,是给未来挖井。
记住一句话:复印越多,真相越少——SSOT 的敌人不是 overlay,是那份「偷偷可写的 vendor」。
概念
Pack,是可安装的规则或技能包单元。SSOT,Single Source of Truth,权威只在一处编辑。锁文件,固定 Pack 版本与校验哈希,便于回滚。Overlay,仓库专属覆盖层,不改权威包正文。
可以理解为:权威包是出版社的书,业务仓是书架。你可以夹便签(overlay),不该在书页上直接涂改再复印三份——复印越多,真相越少。
问题在哪
漂移长这样:三角拷贝三份 Rule,改了一处漏两处;出事故时无法回答「到底哪份生效」。更糟的是,每个人都觉得自己那份是对的。
安装方式若只图快,会把未来升级路堵死。纯拷贝最简单,也最易手改。符号链接省事,但 Windows 权限与开发者模式会坑人。子模块与包版本有学习成本,却换来可锁版本、可回滚。没有银弹,但有原则:必须能说出权威在哪,必须能回滚。
举例
常见方式对比,务实选:复制适合原型;git submodule 或 subtree 适合多仓共享;包版本适合正式分发;符号链接适合本机并联开发。无论哪种,请写 PACKS.md 声明 SSOT:
# PACKS.md 权威来源:packages/acme-rules@1.4.2 本仓库通过锁文件引用,禁止直接修改 vendor 拷贝 例外:仅可在 overlays/ 放置仓库专属覆盖锁文件示意:
{"packs":{"acme-rules":{"version":"1.4.2","hash":"sha256:…"},"fix-parse-failure":{"version":"0.3.1","hash":"sha256:…"}}}仓库落位建议:例如.acme/packs/只读引用,根目录放说明。升级看 changelog,破坏性变更写迁移笔记。Windows 符号链接注意权限与开发者模式;无法链接时降级为只读拷贝加校验,而不是「随便拷一份可写的」。覆盖层容纳仓库个性——命名特例、额外门禁——不要回头去改权威包正文「只此一次」。
「防漂移演练」不用等人自觉,一个哈希对比脚本就够,四步:
步骤总览
- 从锁文件取权威哈希(对权威正文算 sha256);
- 逐个落位拷贝重算哈希,与权威比对;
- 声明为 overlay 的文件豁免——本仓个性,天然不等于权威;
- 只要有一个非 overlay 拷贝对不上,拒绝启动。
# ssot_guard.py —— SSOT 漂移检测:铅封(哈希)对不上就别想蒙混启动# 纯标准库,复制即跑。用法:python ssot_guard.pyimporthashlibdefsha(text):return"sha256:"+hashlib.sha256(text.encode("utf-8")).hexdigest()[:12]# 权威源:packages/acme-rules@1.4.2 的 hard-constraints.md 正文AUTHORITATIVE="禁止把密钥明文提交进仓库\n未经确认禁止改 adaptor/public 入口\n"LOCK=sha(AUTHORITATIVE)# 锁文件里登记的权威哈希# 各落位拷贝:路径 -> (正文, 是否声明为 overlay)COPIES=[(".acme/packs/acme-rules/hard-constraints.md",AUTHORITATIVE,False),("vendor/acme-rules/hard-constraints.md","禁止把密钥明文提交进仓库\n未经确认禁止改 adaptor/public 入口 ←本地加了例外\n",False),# 偷偷可写的 vendor:漂移动事故源("overlay/acme-rules.local.md","本产品线额外禁止改 billing/\n",# overlay:仓库个性,本就不该等于权威True),]defguard(lock,copies):print(f"权威哈希(锁文件登记):{lock}\n")drift=0forpath,content,is_overlayincopies:h=sha(content)ifis_overlay:print(f" [overlay 合法]{path}")print(f" 个性内容,不参与权威校验;随产品仓走,不回流上游")continueifh==lock:print(f" [OK ]{path}")print(f"{h}== 权威,铅封完好")else:drift+=1print(f" [漂移]{path}")print(f" 实算{h}!= 权威{lock}")print(f" <- 有人手改 vendor 拷贝;要改规则请回权威仓提 PR 再升版本")returndriftdefmain():drift=guard(LOCK,COPIES)print(f"\n== 结论 ==")ifdrift:print(f" 检出{drift}处漂移 -> 拒绝启动(比『先用着明天再说』少一场事故)")print(" 回滚 = 把 version 改回上一版重装,而不是在 vendor 里手工改字。")else:print(" 全部铅封完好,可以启动。")print("\n一句话:复印越多,真相越少;SSOT 的敌人不是 overlay,是偷偷可写的 vendor。")if__name__=="__main__":main()跑起来长这样:
权威哈希(锁文件登记):sha256:e96fa0212b34 [OK ] .acme/packs/acme-rules/hard-constraints.md sha256:e96fa0212b34 == 权威,铅封完好 [漂移] vendor/acme-rules/hard-constraints.md 实算 sha256:0538250e4e47 != 权威 sha256:e96fa0212b34 <- 有人手改 vendor 拷贝;要改规则请回权威仓提 PR 再升版本 [overlay 合法] overlay/acme-rules.local.md 个性内容,不参与权威校验;随产品仓走,不回流上游 == 结论 == 检出 1 处漂移 -> 拒绝启动(比『先用着明天再说』少一场事故) 回滚 = 把 version 改回上一版重装,而不是在 vendor 里手工改字。 一句话:复印越多,真相越少;SSOT 的敌人不是 overlay,是偷偷可写的 vendor。输出你看什么
- 三个落位三种判法:symlink 拷贝哈希相同 → OK;vendor 拷贝被「加了个本地例外」→ 实算对不上,当场爆;overlay 内容完全不同却不算漂移——它声明过是仓库个性,豁免校验;这正是「允许 overlay、禁止偷改 vendor」那条边界跑成了代码;
- 权威哈希
sha256:e96fa0212b34是 hashlib 真算出来的,任何人把权威正文原样跑一遍都会得到同一串——这就是上文说的「带铅封的复印件」:复印件一旦被人涂改,铅封号立刻变; - 结论只有一句人话:
检出 1 处漂移 -> 拒绝启动。上文讲过「让它爆,是特性不是麻烦」——这个脚本存在的意义,就是把「明天再说」提前堵死在启动那一刻; - 顺带回扣第 8 篇那条
refactor@2.0+ 根本没装:版本对不上就拒绝,和哈希对不上就拒绝,是同一套「锁 + 校验」的心法,下篇编排、第 23 篇夹具哈希都还会再见到它。
自检
# Pack / SSOT 自检 - [ ] 能否一句话指出每个 Pack 的权威来源? - [ ] 是否有锁版本与哈希,可回滚? - [ ] 是否禁止直接修改 vendor 拷贝? - [ ] 仓库个性是否只存在 overlays? - [ ] Windows 链接失败时是否有降级策略?两问。第一,今天改一条安全禁止项,要改几个仓库里的文件?大于一,就有漂移债。第二,回滚到上周 Pack 版本,步骤写下来超过五分钟能完成吗?不能,锁文件可能只是摆设。
SSOT 不是教条清教徒:允许 overlay,允许紧急热修,但热修必须回流权威源并打版本。只热修不回流,就是漂移的精英写法。
把 PACKS.md 放进新人 onboarding。新人第一周若不知道权威在哪,三个月后你就会多出一份「本地优化版规则」。
Windows:符号链接失败时的降级三步
开发者模式没开、或公司策略禁 symlink 时,别卡死在「必须链过去」。按这三步降级,SSOT 仍成立:
- 探测:
New-Item -ItemType SymbolicLink ...失败就记一条install_mode: copy到本地pack.lock.json,不要默默改权威包。 - 只读拷贝:把 Pack 版本目录整份拷到
vendor/acme-rules/<version>/,拷完立刻算校验(例如Get-FileHash -Algorithm SHA256对hard-constraints.md),哈希写入 lock。 - 防漂移演练:CI 或本地脚本对比 lock 与权威源;不一致就失败,禁止「随手改 vendor」。要改规则,回权威仓提 PR,再升版本重装。
可以理解为:链得上用链(省空间、必同步);链不上就用「带铅封的复印件」。复印件没有铅封,三周后一定漂。
Pack 的覆盖层(overlay)只放本仓个性:路径别名、额外禁止项、团队联系人。权威包保持干净,个性不回流污染上游。可以理解为:出版社出书,书店可以贴自己的荐购条,但不能用马克笔改正文。
锁版本的最小文件可以长这样(示意):
{"pack":"acme-rules","version":"1.4.2","install_mode":"symlink","hashes":{"hard-constraints.md":"sha256:…"}}install_mode为copy时,哈希必填;为symlink时,CI 仍建议抽查权威源是否可解析。回滚就是把 version 改回上一版并重装,而不是在 vendor 里手工改字。
读到这里,不妨立刻做一个五分钟动作:打开你们仓库,找到与本篇对应的那个目录(没有就新建),把文中最小示意落成真实文件名。文件在,讨论才有坐标;文件不在,下周还会从「我们觉得」开始。第 10 篇的验收不是「读完」,是「仓库里多了一份可指出的东西」。
举例加长:覆盖层与权威包怎么分工
权威包packs/acme-rules:硬约束、加载顺序、通用禁止项。
本仓 overlay:overlay/acme-rules.local.md只写「本产品线额外禁止改billing/」。
安装后助手两者都读,但 PR 只允许改权威包的上游仓库;overlay 随产品仓走。
有人图省事直接改 vendor 拷贝——铅封(哈希)一校验就爆。让它爆,是特性,不是麻烦。
PACKS.md 再补两行给 Windows 同事:
链接策略:优先 SymbolicLink;失败则 copy+hash(见安装脚本) 禁止:在 .acme/packs/** 内直接改规则正文锁文件的 hash 对不上就拒绝启动——比「先用着明天再说」少一场事故。SSOT 的敌人不是 overlay,是偷偷可写的 vendor。
思考
小结三句。Pack 进仓第一原则是 SSOT,避免手工漂移。安装方式可以务实选择,但必须锁版本可回滚。覆盖层容纳仓库个性,不改权威包。
可以理解为:书架可以多,出版社只有一个。下篇看编排骨架:意图如何进入流水线并交给 Worker——把门禁与确认做成一等节点。
下篇预告:《不会还有人拿单次工具调用撑多步骤业务吧?没有编排就没有纪律 | 意图流水线 Worker》——单次工具调用撑不起多步骤业务。
觉得有用的话,欢迎点赞、留言说说你们 Pack 用的哪种安装方式;有「三处拷贝漏改一处」的事故,也欢迎对照 SSOT 拆。
专栏:从小白到AI落地大师|《从 Rule 到落地:AI 工程化实践》