用户令逐字:「E:/ProgramData/.workbuddy/skills 提交仓库是指的这里」—— 即本目录就是仓库(2026-10-07 已在本目录建仓,见当日日志 §22),本轮把余下未纳管的 9 个技能一并提交。 本次入库(9 个技能,46 个文件): 1、`AI HOT` 2、`draw-ui` 3、`dsh-diagnose` 4、`dsh-knowledge` 5、`dsh-local-env` 6、`dsh-opensource-release` 7、`dsh-workflow` 8、`oil-motion` 9、`skills-security-check` 提交前核对: · **凭据类扫描**(`*.env` / `*token*` / `*.key` / `*secret*` / `*.pem`)⇒ **零命中** ✓; · 体积合计约 20 MB(`draw-ui` 12M + `oil-motion` 6.5M 是大头,形态为配图/素材 —— 仓库 `.gitignore` 里明写「`assets/*.png` 是内容不是产物」⇒ 属刻意入库); · 运行产物仍按既定规则排除(`__pycache__` / `logs/` / `tmp/` / `.venv/` / `*.egg-info` / `uv.lock` / `.workbuddy/`)。
16 KiB
16 KiB
实测坑(全部踩过,别再踩)
归属:技能
dsh-opensource-release的详情档(按需读,不是每次都要读)。 本档覆盖:实测坑(全部踩过,别再踩)(原行 L621–L703)。 主文件 / 判据与流程主干 =../SKILL.md(§0.5 事故清单 · §0 事实 · §1 硬规则 R-O1–R-O17 · §1.5 用户当场纠正的硬口径 · §5 授权结构 · §6 发布 SOP 主干 · §7 验证八件套 · §相关)。 来源:2026-09-22「技能重组线」把../SKILL.md的 L621–L703 段下沉到本文件;同日按 humanizer 技能做了一轮「人话化」措辞修订,技术标识、判据与条目数未动。 跨档引用:正文里的「§N / 见 §8 坑 N / 见下表」等编号,用../SKILL.md末节「详情索引」的原章节列定位。 维护:本文件与../SKILL.md的指针行成对;改内容时同时核对主文件的指针描述是否仍准确。
8. 实测坑(全部踩过,别再踩)
text被重新赋值导致替换未落盘:脱敏函数里text = text.replace(...)之后再用if text2 != text判「是否需要写盘」⇒ 全局替换永远不落盘(只落行级改动)。必须留orig = text做比较基准。- 行级重写的 key 必须写「全局替换之后的文本」:例:原行
(档案 70 的 anysearch 事故…),key 要写(档案 70 的 该插件 事故…)。用替换前的原文当 key ⇒ 静默不命中,留下半吊子句子。 - 行级重写会丢缩进:按
strip()匹配就必须把原行的 indent 补回去(多行替换值还要给后续行也加 indent),否则 TS 缩进错乱。 - 全局替换顺序敏感:
wake.html的专用规则必须在笼统的ai1net → <baseDomain>之前,否则专用规则永远不命中(会被先替换成<baseDomain>.com这种半成品)。 - 重建会抹掉手写文件:所以有
OVERLAY(先快照_overlay/后恢复)+--force安全闸。验证过一次:重建后 5/5 个 overlay 文件逐字节一致。 .git绝不能带过去:历史里含全部内部信息;必须是全新仓库。- Windows 下
install.sh没有执行位:README 一律写sudo bash install.sh;要执行位就chmod +x后再提交。 - 别信「探针没命中」的直觉:探针必须在重建之后、还原 overlay 之后跑(overlay 里也可能带泄漏)。
- ⛔ 绝不对
_build_export.py本身做批量替换:脚本里同时存在「源模式」与「目标值」(例如规则("dshs", "dsh-web-platform")),盲替会把源模式一起改掉 ⇒ 改名规则静默失效(2026-09-13 实际误伤 4 处)。改脚本只用精确Edit,改完必须重建一次并用 grep 复查。 Dockerfile/Dockerfile.dsh曾被整份跳过脱敏:is_text()按扩展名判断,二者无扩展名(或.dsh不在白名单)⇒ 漏掉。已在is_text()里加特判。package.json属「源派生」而非 OVERLAY:手改 version / description / repository / license 会在下次--force被覆盖。这类项目自有字段必须写成 GLOBAL 规则。dshs.db是无前缀写法:src/config.ts里默认库文件名不带dsh-前缀,只替换dshs会漏掉它;要单独一条规则。- 改名要连导出目录名一起改,并同步两个辅助脚本里的绝对路径(
_build_export.py的DST、_verify_tsc.mjs的EX),改完重跑一次 tsc 验证路径仍对。 - 废弃的旧候选名要进阻断性探针:改名改了两轮时(
dshs→ 中间候选 → 终选),如果探针只查最早那个名字,中间名会静默残留(_overlay里最容易中招)。做法:把所有曾用名都加进LEAK_PROBES。 - 改 overlay 的自指标识可以做批量替换(overlay 里只有本项目自己的标识,上游出处名是另一个字符串),但必须先用 grep 确认「旧名出现处全是自指」再替换;
_build_export.py本身绝不能这么干(见坑 9)。 - 🔴
--claim-exec的输出必须当场看;claim 与 release 绝不能写进同一条命令(2026-09-13 实际闯祸):把 claim 结果重定向到文件、只显示OWNER首行 ⇒ 没发现抢锁失败;命令末尾的--release-exec是rm -rf语义,于是把另一个会话(R1-注入层-1104)的锁删掉了。正确姿势:- 抢锁单独一条命令,且当场看它的输出(
✓ 已持全局执行锁vs✗ 抢锁失败); - 看到「占用者」不是自己就立即停手,不碰文档库/代码库;
- 放锁前再
cat 05-交接单/.exec-lock/OWNER确认首行是自己; - 万一误放:脚本实现是
rm -rf "$LOCKEXEC"、不留档,只能按handoff-guard.sh第 43 行的格式原样重建(OWNER/开始:MM-DD HH:MM/在做:…,值取自抢锁失败时的输出),再用bash 07-scripts/handoff-guard.sh的信息模式验证「占用者」已回到原会话,并在回复里明确告知用户;若该会话其实已结束,请用户自行撤锁(处置权只属于用户)。
- 抢锁单独一条命令,且当场看它的输出(
- 🔴 迁移工作根:先改脚本常量,再搬目录(2026-09-13 实际踩):顺序颠倒(先
mv后改OUT)时,任何一次重建都会在旧位置把整个仓库重新建出来(那次建出 135 个文件),而且因为旧位置没有_overlay,6 个手工层文件(README / LICENSE / NOTICE / PLUGIN-PORTING / install.sh / LICENSE-UPSTREAM-MIT)全部缺席;若此时旧目录被当成交付物,就是一次「文档凭空消失」的事故。 正确顺序:① 改_build_export.py的OUT和_verify_tsc.mjs的EX→ ② 复制/搬运目录 → ③ 在新位置重建 → ④ 核对文件数与手工层 6/6 + 跑tsc→ ⑤ls确认旧位置不存在(没被重建复活)→ ⑥ 同步本技能与项目 MEMORY 里的路径引用。 ⚠️ 附带教训:_build_export.py的安全闸与 OVERLAY 机制都依赖OUT指向真实工作根;OUT指错时它不会报错,只是在别处默默新建一套。 - 🔴
INCLUDE_DIRS/INCLUDE_FILES是白名单:漏了不报错,只是少文件(2026-09-13 实际踩:assets/漏收录 ⇒ 导出的仓库缺注入脚本,平台会 fail-fast、自带校验脚本也会失败)。两条防线:REQUIRED_EXPORT清单(脚本内,26 项):构建时逐个os.path.exists,缺一即return 1;新增"运行时要读的文件"必须加进去。- 用被导出仓库自带的校验脚本验收:
node scripts/verify-inject.cjs(读assets/inject/*.js+ 检查proxy.ts走loadInject)。这比"我看了一眼目录"可靠得多。 ⚠️ 同类风险点:package.json的files(npm/git 安装按它过滤,与INCLUDE_*是两套名单,都要补)。
- 🔴🔴
cp _overlay/<f> <仓>/会把「下一版内容」整份带进当前版(2026-09-18 血证,已误发上公网):_overlay/在改名窗口期是「未来版」,导出仓是「当前版」;为修一处残留引用而cp _overlay/README.md覆盖旧仓,就把 新名 + 新中文名 +git clone …/<新仓>.git+cd <新仓>+systemctl is-active <新仓>一并推了上去。 ⛔ 真实危害不是"名字不对"而是功能性缺陷:README 让用户 clone 的那个仓库当时是空的(只有建仓提交)⇒ 照 README 做的人克隆到空仓库,部署直接断。 ✅ 规矩:_overlay处于下一版状态时绝不整文件cp到导出仓,只准逐处精确Edit,只落本次要发的那几段。 ⚠️ 最阴的一点:这种误覆盖会让「导出仓 vs_overlay的 diff 变空」。diff 为空本身就是"新版被覆盖进去"的证据,不是"一致"的好消息。看到意外的一致要停下来查原因。 ✅ 推前必做两条复核:①grep -rn "<新名>\\|<新中文名>" .⇒ 须为空;② 部署命令自洽 ——git clone指向的仓库真有内容、cd的目录名、systemctl的服务名三者一致。 - 🔴 新增「中英文件对」时必须同步登记(2026-09-18 实证;2026-09-19 扩充为四处):
这份清单是白名单:没登记的对象不会报错,只是静默跳过检查。所以新增
manual/X.md+manual/X.zh-CN.md时,四处一起改: ①_build_export.py的OVERLAY:漏了 ⇒ 静默少带,文件根本不在导出物里; ②_build_export.py的REQUIRED_EXPORT:漏了 ⇒ 缺文件不报错(它只查"清单里的在不在"); ③_check_parity.mjs的MANUAL/PAIRS:漏了 ⇒ 该对静默跳过对等检查(标题数 / 表格行数 / 图数 / 英文汉字数全不检); ④_check_links.mjs的MANUAL:漏了 ⇒ 该文档整份不参与链接与锚点检查。 🔴🔴 ③ 与 ④ 是两份独立实现(不 import 同一常量)⇒ 必须各改一次,改一处不算改。 ✅ 验收判据(别只看 exit code):回读检查器自己报告的文档数有没有 +N,本轮实测links24 → 26、parity输出里多出manual/contributing.md一行(headings 4/4)。 ⚠️ 同类静默跳过已出现四次(坑 18INCLUDE_*/REQUIRED_EXPORT、坑 20 的两份MANUAL)⇒ 判据统一为:凡"清单式"机制,新增对象后必须回读清单计数是否 +N(AST 实读,别数文件)。 COMMERCIAL-LICENSE.*的落位规矩(2026-09-18 立):① 只在四份 README(仓 +_overlay/各中英)授权节尾加一句链接,⛔ 不在正文铺开;② ⛔ 不得把「商用须授权」的句子写进LICENSE或 README 的 AGPL 段落(那才是 §10 追加限制);③ 英文版汉字数仍须恒为 4(只在顶部入口行出现「中文文档」)。- 🔴🔴
sanitize()不作用于OVERLAY,所以「干跑预演通过」≠「构建能过」(2026-09-19 血证)——restore_overlay()排在sanitize()之后 ⇒ 手工层(README / manual / LICENSE / install.sh …)永远不会被脱敏,上面任何字面量都会原样进公开仓。 实测:manual/decisions.{md,zh-CN.md}里一行目录树写着poc/(该目录从未收录)⇒ 重建时被阻断探针抓出blocking hits: 2。 ⇒ 纪律:① 改完手工层必须重建(或至少跑整树探针);② 若写了「干跑预演」脚本,它只覆盖源派生文件,不能当手工层的通行证;③ 命中后按「先改仓库同名文件 → 再Copy-Item进_overlay」的顺序修(反了会被stash_overlay()覆盖回去)。 - 🔴 裸替换「纯字母数字」的内部标识会改坏
package-lock.json(2026-09-19): 内部主机号w106/w47全由 base64 字母表字符组成 ⇒ 裸替会命中integrity哈希,npm ci校验失败,且症状与改动毫无关联、极难排查。 ⇒ 只替带引号或**作对象键(后跟冒号)**的形态;纯数字标识(如15432)也要先扫一遍上下文再决定(本轮实测 11 处全在127.0.0.1:15432这类 URL 里,安全)。 - 🔴 改名之后,新项目名可能与旧脱敏规则「撞名」(2026-09-19):
新名
dsh_ai1net含ai1net,而ai1net同时是真实域名的词根 ⇒ 若照旧写一条笼统的ai1net→<baseDomain>,会把新名打成dsh_<baseDomain>。 ⇒ 只打带.com的域名形态(dsh_ai1net永不含.com)。同理:任何"新名包含旧敏感词根"的情形,规则都必须精确到可区分的形态,并规则 + 探针同时加。 - 内部运维 / 演练 / 诊断脚本族的排除判据(2026-09-19 定型):新增一整批
*-probe/*-drill/*-jitter/*-entropy/bootstrap-*.sh/dshlog.mjs/find-ui*/check-layering*时按四条判: ① README / manual 从不引用它;② 含内部主机号 / 测试机 IP / 内部隧道端口;③ 引用内部文档(交接单/调整方案/poc/);④ 是「把节点接进本平台自研编排」的一次性手册。 ⇒ 四条全中 = 与 K8s 那一族同处置(整批 drop),别试图逐条脱敏(脱完也不可用,且留下一堆<>占位符)。 ⚠️.py这类脚本进来时先看TEXT_EXT:不在白名单的扩展名整份跳过脱敏且不被任何探针扫(本轮.py就踩了,已补)。 - 🔴🔴 只改手工层(
OVERLAY)文字时,不必整树--force重建(2026-09-19 定型,是坑 22 的推论): 坑 22 已证restore_overlay()(纯shutil.copy2)排在sanitize()之后 ⇒ 手工层不过脱敏、逐字节直落。 ⇒ 「改_overlay/<f>+ 跑_sync_overlay.py(方向_overlay→ 导出仓)」得到的产物,与整树重建逐字节相同。 ✅ 收益:风险面从整棵树缩到改的那几个文件,不会顺带把源仓当前状态(别人的未提交改动 / 新提交 /??目录)带进公开仓, 也就不必先过dirty_src_files()那道闸门(省掉一轮「等源仓干净」的阻塞)。 ⚠️ 前提与边界:① 改动必须落在OVERLAY清单内;非手工层文件只能「改规则 + 重建」; ② 仍须_sync_overlay.py --check报**「逐字节一致」再提交(方向搞反 = 事故 #27); ③ 交付前仍跑全套门禁**(_check_dst整树探针 /_check_public两仓 / links / parity / tsc), 其中_verify_tsc.mjs与_check_public.py必须串行(junction 假阳性)。 ④ 若这轮同时还有规则改动,就必须回到整树重建 —— 捷径只适用于「纯手工层文字」。 - 🔴🔴 写文档用词之前,先 grep 整个代码库,确认这个词有没有被别的意思占用(2026-09-19 用户点出):
实测:
manual/project*.md里用「水位」表达「上下文已占用量」⇒ ① 对读者是类比(要先自己换算); ② 更硬的是术语撞车 ——水位在本项目已有两个别的含义:src/net/relay/client.ts:150= WS 出向水位(字节,默认 256 KiB)、src/db/schema.ts:301= worker 容量水位。同一个词在文档 + 代码里指三样东西,这是歧义,不是风格。 ⇒ 改法:贴回同段前一句已经引入的项目词(该段前半句是「上下文被当作有上限的预算」)⇒ ZH其次是**上下文已经占了多少**| ENthen **how much of the context is already used**。⛔ 不另造新比喻。 🔑 为什么必须单独立一条:探针体系对这类问题完全无感,该文件所在manual/没有任何探针,blocking hits: 0、required 全在、两仓 0 命中 ⇒ 门禁全绿,而词依然是坏的。 判据:探针只答「有没有不该出现的敏感信息」,不答「这个词会不会产生歧义」。 ⛔ 同一段里另有 5 处类比,用户只点了一处 ⇒ 一律不动,列条目报上去等拍板(背着这份重量/腐坏/悄悄搭车/淹没/ EN 独有blast radius)。 同段同病也不许自行扩大改动面:报清单的成本是一个回合,擅改的成本是信任。