主题
scripts/ 索引
本文件只做索引:每个脚本做什么、怎么跑、有没有东西在自动执行它。 🚫 这里不定义任何规则。脚本自身的头注释才是它的判据说明书 —— 本文件只给一行导航。
现状:
scripts/下 29 个条目 = 27 个脚本 +scripts/lib/目录 + 本文件 (现数:ls scripts/ | wc -l;脚本数要去掉这两个非脚本条目,见 §4 的命令)。 ⚠️ 份数与接线状态都会漂,§4 给了现算命令;本文件里的表是 2026-08-29 由那条命令生成的快照。
1. 按用途分组
守门(CI 里跑,或该跑)
| 脚本 | 做什么 | 怎么跑 |
|---|---|---|
scripts/check-no-inline-zh.mjs | 抓 src/ 里绕过 src/lib/strings/ 的中文字面量 | pnpm run check:no-inline-zh |
scripts/check-no-inline-en.mjs | 抓 src/components/ src/pages/ 里硬编码的英文 UI 串(高精确,宁缺勿滥) | pnpm run check:no-inline-en |
scripts/check-schema-frozen.mjs | 红线 #11:已应用迁移的 SQL 本体保持冻结(SHA-384 指纹) | pnpm run check:schema-frozen |
scripts/check-claude-md-paths.mjs | 反引号里的仓根相对路径是否真实存在(且真的入仓 —— 本机存在但不 tracked 的会红:node_modules / 构建产物 / 本地配置写进反引号 = 只在 CI 上炸的雷) | pnpm run check:claude-paths(7 份 CLAUDE.md)· pnpm run check:readme-paths(--readmes:git ls-files 现取全部 tracked README)· 也接受任意 md 作 argv |
scripts/check-doc-links.mjs | 活跃文档的链接悬挂检查,两种模式:A 根侧裸写的 docs/plans/ docs/cross-end/ 全路径;B 全仓(含 rvh/docs/ admin/docs/,2026-09-01 起加 supabase/ tools/ scripts/ rb-debug-mcp/)的 markdown 相对链接,按引用文件所在目录解析 | pnpm run check:doc-links |
scripts/check-kb.mjs | 知识层守卫:frontmatter 完整性 + sourceRefs 路径存在且入仓 + verifiedAt 是真 commit + 索引一致 + 全仓 md 无 VitePress 专有语法(正文,剥代码块)。外加新鲜度报告(源码已变动几次 → 只打印,恒不判失败) | pnpm run check:kb |
scripts/audit-locale-coverage.mjs | src/lib/strings/locales/{en,zh} 覆盖度(MISSING / EQUAL 漏译嫌疑) | pnpm run audit:locale |
scripts/cross-end-check.sh | 三端(RB / RVH / Supabase)结构一致性只读诊断 | bash scripts/cross-end-check.sh --max-skip=2 |
scripts/check-vocab-asset.sh | 预装库资产闸门:体积闸(≥100 MB 退出 1)+ 4 表白名单卫生断言 | bash scripts/check-vocab-asset.sh |
scripts/pipeline-health.sh | 推荐流水线产出健康:T1 产出层(匿名可读,CI 里不需要任何 secret)+ T2 诊断层(需 service_role,缺则 SKIP 不 PASS)。补的是「analyze-articles 静默产出 0 长达 25 天而无人发现」那个缺口 · 已接定时巡检(monitor-prod.yml) | bash scripts/pipeline-health.sh --self-test / --max-skip=3 |
专题验证(只读诊断,配套 docs/verification/ 的清单)
一一对应关系:脚本承担「能用数字或集合表达」的断言,清单只留问句 + 假绿风险 (铁律见
../docs/verification/README.md)。
| 脚本 | 专题 | 配套清单 |
|---|---|---|
scripts/sync-verify.sh | 同步与多端一致性 · CI 已接(只跑得到 N0) | sync-consistency.md |
scripts/privacy-verify.sh | 隐私与鉴权 · P8-P13 已接定时巡检(monitor-prod.yml,按 schedule 不按 commit,见 §2) | privacy-auth.md |
scripts/ops-verify.sh | 运维监控体系(🔴 接不了:无凭据时 PASS=0,见 §2) | ops-monitoring.md |
scripts/release-verify.sh | 发版链路(--deep 才真下载制品验签)· 静态段 R1-R4 已接 CI(--static-only,见 §2) | release-chain.md |
scripts/migration-verify.sh | 数据基线与迁移(零线上凭据,只读本地库)· CI 已接 | data-baseline.md |
scripts/learning-loop-verify.sh | 学习循环(查词 / 生词 / SRS),含 SM-2 黄金向量单副本守卫 L1/L2 · CI 已接 | learning-loop.md |
scripts/verify-rvh-alignment.sh | RB 侧跨端对齐验收(storage_path + reference_words) | — |
scripts/context-budget.sh | 恒加载区体量与趋势的仪表(恒 exit 0,不是闸门) | context-budget.md |
🔑 五个脚本带
--max-skip=N(2026-08-29 T3-1 四个 + 2026-08-30privacy-verify;全部已接自动执行):不传时行为完全不变(本机仍是 「0 全 PASS / 1 有 FAIL / 2 有 SKIP / 3 跑不起来」四态);传了则「≤N 且无 FAIL ⇒ 0,>N ⇒ 1」。 这个参数是假绿的解药,不是宽容:没有它,一个因缺凭据全 SKIP 的脚本会永远显示绿色。🔑 这六个
*-verify.sh的头部都带中文标点陷阱说明:$var,会被 shell 当作变量名的一部分, 静默截断整个脚本剩余部分(2026-08-29 T1-1 修 29 处;四个脚本此前分别死在 R1 / P7 / M3 / D4)。 写新断言时用${var},并跑一次脚本头里的自检命令。
运维与发版
| 脚本 | 做什么 | 怎么跑 |
|---|---|---|
scripts/backup-supabase.sh | Supabase 全量备份(免费版只有 7 天自动备份) | 见 supabase-backup-restore-runbook.md |
scripts/backup-supabase-storage.mjs | Storage bucket 镜像 —— pg_dump 里没有这些字节,dump 只有指针 | 同上 |
scripts/db-audit.sh | 生产数据一致性巡检(只读)。这套系统的事故绝大多数是静默的,它负责「让你知道」(🔴 无静态段,不是 CI 候选) | bash scripts/db-audit.sh |
scripts/reset-dev-data.sh | 清测试数据(本地 / --remote 连 Supabase 10 张同步表 + Storage) | bash scripts/reset-dev-data.sh |
scripts/release.sh | 发版机械步骤封装(next-version / bump / preflight / promote-updater 等子命令),由 /release skill 调用 | bash scripts/release.sh <子命令> |
scripts/set-version.mjs | 三处版本号统一(真相源 = src-tauri/tauri.conf.json) | pnpm run check:version-sync |
scripts/gen-latest-json.mjs | 从 CI 制品生成 Tauri updater 的 latest.json(指向公开仓而非私有镜像仓) | release.yml 的 publish job |
构建与资产生成
| 脚本 | 做什么 | 怎么跑 |
|---|---|---|
scripts/build-content-script.mjs | 把 src-tauri/src/content-script/ 打成单文件 IIFE(Rust include_str! 消费它)。⚠️ 直接改产物 src-tauri/src/content-script.js 无效 | pnpm run build:cs / pnpm run watch:cs |
scripts/gen-word-illustrations.py | 预装库的词 × OpenMoji 匹配 → 「具象名词 → hexcode」净映射 | 手工,见文件头 |
scripts/fetch-openmoji-assets.sh | 按上一步产出的 hexcode 清单拉 SVG 到 public/openmoji/ | 手工 |
2. 接线状态矩阵
列的含义:CI = .github/workflows/ 下提到该文件名的 workflow 数 · pkg = package.json 里出现次数 · skill = 提到它的 SKILL.md 数(根 + rvh + admin)· verif = docs/verification/ 下提到它的文档数。🔴 = CI pkg skill 三列全 0, 即没有任何东西在自动执行它,只有文档在说它存在。
⚠️ 两个读法陷阱(这张表是机械 grep,不是执行图):
CI=0不等于「不在 CI 里」 —— 经package.json间接进 CI 的那批(pkg≥1且ci.yml跑对应的pnpm run)在这一列是 0。实际在ci.yml里跑的有 8 个:check-no-inline-zh·check-no-inline-en·check-schema-frozen·set-version(check:version-sync)·audit-locale-coverage·check-claude-md-paths·check-doc-links·build-content-script(经pnpm build)。CI=1也不等于「在 CI 里跑」 ——scripts/release.sh那个 1 是.github/workflows/release.yml的一行注释提到了它,workflow 并不执行它。 真正被 workflow 直接run:的是 6 个:cross-end-check.sh+check-vocab-asset.sh(ci-cross-end.yml)·migration-verify.sh+sync-verify.sh+learning-loop-verify.sh(ci-verify.yml)·gen-latest-json.mjs(release.yml,仅发版时)。
🔒 因此:写 workflow 注释时别写脚本全名(写
check-schema-frozen,别写带扩展名的全名)。 这张表是判断「还有谁没接」的唯一依据,一次注释提及就能把没在跑的脚本标成CI=1, 等于把剩余工作面藏起来。ci-verify.yml的文件头记着这条纪律 —— 2026-08-29 建它时 头两版注释正好踩中,当场把check-schema-frozen.mjs与cross-end-check.sh的 CI 列刷高了。
| 脚本 | CI | pkg | skill | verif | |
|---|---|---|---|---|---|
scripts/audit-locale-coverage.mjs | 0 | 1 | 1 | 0 | |
scripts/backup-supabase-storage.mjs | 0 | 0 | 0 | 0 | 🔴 |
scripts/backup-supabase.sh | 0 | 0 | 1 | 2 | |
scripts/build-content-script.mjs | 0 | 3 | 0 | 0 | |
scripts/check-claude-md-paths.mjs | 0 | 2 | 0 | 0 | |
scripts/check-doc-links.mjs | 0 | 1 | 0 | 0 | |
scripts/check-no-inline-en.mjs | 0 | 1 | 1 | 0 | |
scripts/check-no-inline-zh.mjs | 0 | 1 | 1 | 0 | |
scripts/check-schema-frozen.mjs | 0 | 1 | 1 | 1 | |
scripts/check-vocab-asset.sh | 1 | 0 | 3 | 0 | |
scripts/context-budget.sh | 0 | 0 | 1 | 1 | |
scripts/cross-end-check.sh | 1 | 0 | 2 | 2 | |
scripts/db-audit.sh | 0 | 0 | 0 | 1 | 🔴 |
scripts/fetch-openmoji-assets.sh | 0 | 0 | 0 | 0 | 🔴 |
scripts/gen-latest-json.mjs | 1 | 0 | 0 | 1 | |
scripts/gen-word-illustrations.py | 0 | 0 | 0 | 0 | 🔴 |
scripts/learning-loop-verify.sh | 1 | 0 | 0 | 2 | |
scripts/migration-verify.sh | 1 | 0 | 0 | 2 | |
scripts/ops-verify.sh | 0 | 0 | 0 | 3 | 🔴 |
scripts/privacy-verify.sh | 1 | 0 | 0 | 2 | |
scripts/release-verify.sh | 1 | 0 | 3 | 2 | |
scripts/release.sh | 1 | 0 | 1 | 1 | |
scripts/reset-dev-data.sh | 0 | 0 | 0 | 1 | 🔴 |
scripts/set-version.mjs | 0 | 1 | 1 | 1 | |
scripts/sync-verify.sh | 1 | 0 | 0 | 5 | |
scripts/verify-rvh-alignment.sh | 0 | 0 | 1 | 0 |
🔴 共 6 个(11 → 9 → 8 → 7 → 6)。分两类,别混为一谈:
接不了 / 不该接(2 个):
ops-verify.sh·db-audit.sh。 2026-08-29 实测,不是没人做,是做了发现前提不成立:脚本 无凭据时 为什么不接 ops-verify.shPASS=0 SKIP=8 CI 里一条都验不了 —— 接了就是 100% 假绿闸门 privacy-verify.shPASS=7 SKIP=8 ✅ 2026-08-30 已接,但走的是 schedule 不是 commit( monitor-prod.yml)。当初判「不该接」指的是不该按 commit 接 —— 那 7 条(P8-P13)全是打admin.lampio.app的线上 HTTP,验的是「已部署的站点」不是「这次改动」;更要命的是 admin 走 Vercel 部署、Vercel 不等 Actions,所以真正会出事的时刻(部署完而没人提交任何东西)恰恰是 commit 触发永远覆盖不到的。⇒ 跟着时间走,不跟着提交走db-audit.sh无凭据直接退出 纯生产数据巡检,压根没有静态段 暂缓、有明确理由(1 个):—— ✅ 2026-08-30 已接(这一类现在是空的)。 当初暂缓的理由是它的 SKIP 数是网络可达性的函数(同机同码连跑两次 SKIP = 4 / 11), 钉任何基线都会得到一道随网络天气变红的门。解法是拆段而不是调基线: 新增release-verify.sh--static-only只跑 R1-R4(本地文件 + node + python3 + grep,零网络零凭据)。 拆完把代理指向关闭端口实测:静态段 0.45s / SKIP=0(完全不受影响), 全量 20.1s / SKIP=10(正是那个病)。CI 里跑--static-only --max-skip=0; R5 起(gh / endpoint / latest.json / landing / 凭据台账)留给人工与/release。刻意手工(4 个):
backup-supabase-storage.mjs·fetch-openmoji-assets.sh·gen-word-illustrations.py·reset-dev-data.sh。它们没有「应该恒真」的断言 (备份 / 拉资产 / 生成映射 / 清测试数据都是动作不是判据),不接 CI 是知情选择。 另一类不在 🔴 里,但同样不是闸门:仪表(1 个,2026-08-30 新增):
context-budget.sh(skill=1所以不计入 🔴)。它恒exit 0, 由/docs-auditStep 0 调用、结果记进docs/verification/context-budget.md的台账。 🔴 别把它当闸门接进 CI —— 它守的东西(恒加载区的回涨速度)没有固定阈值: 任何体量阈值都会在「正常增长 → 下一次治理」之间恒红。判断层是人,仪表只负责让趋势可见。
🔴 量「哪些检查不依赖凭据」时必须先把 gitignored 的
admin/.env.local藏起来。 脚本会set -a; . 它(里面有 service_role + anon key),于是本机沙箱里一大片 「看起来是静态」的检查其实在拿本机凭据读线上库。2026-08-29 第一次没藏,ops-verify报 PASS=19,差点被当成最佳候选接进来;藏掉之后真实数字是 PASS=0。 同理要剥掉psql/gh/ Keychain(security)与$HOME。
已接的 3 个(migration-verify.sh --max-skip=2 · sync-verify.sh --max-skip=1 · learning-loop-verify.sh --max-skip=1)走 .github/workflows/ci-verify.yml。 判据是静态段零网络零凭据 ⇒ SKIP 数在 CI 里是常量 ⇒ 基线钉得住。 ⚠️ sync-verify 在 CI 里只有 N0 一条真的跑 —— 接它的理由不是覆盖面,是 N0 守的东西 只有这里守(那份 shell 表清单是手抄 Rust PENDING_PUSH_TABLES 的第二本账)。
3. scripts/lib/
被上面的脚本 source / import 的共享实现,不单独执行:
| 文件 | 被谁用 |
|---|---|
scripts/lib/migration_digest.py | scripts/migration-verify.sh(迁移指纹) |
scripts/lib/minisign_verify.py | scripts/release-verify.sh(updater 签名验签) |
scripts/lib/mastery_ladder.py | scripts/learning-loop-verify.sh(掌握度阶梯) |
scripts/lib/parse-push-payload.py | scripts/sync-verify.sh(push payload 解析) |
4. 数字现算命令
bash
cd /Users/larry/reading-browser
ls scripts/ | wc -l # 条目数(含 lib/ 与本文件)
ls scripts/ | grep -vE '^(lib|README\.md)$' | wc -l # 脚本数
# 接线矩阵(§2 那张表就是它生成的)
for f in $(ls scripts/ | grep -vE '^(lib|README\.md)$'); do
printf "%-30s CI=%s pkg=%s skill=%s verif=%s\n" "$f" \
"$(grep -rl -- "$f" .github/workflows/ 2>/dev/null | wc -l | tr -d ' ')" \
"$(grep -c -- "$f" package.json)" \
"$(grep -rl -- "$f" .claude/skills/ rvh/.claude/skills/ admin/.claude/skills/ 2>/dev/null | wc -l | tr -d ' ')" \
"$(grep -rl -- "$f" docs/verification/ 2>/dev/null | wc -l | tr -d ' ')"
done
# 中文标点陷阱扫描(应全为 0)
for f in scripts/*.sh; do
printf "%-34s " "$f"; perl -ne 'while(/\$([A-Za-z_]\w*)([^\x00-\x7F])/g){print "x\n"}' "$f" | wc -l
done
pnpm run check:readme-paths # 全部 tracked README 的反引号路径5. 加新脚本时
- 放这里 → 回来加一行(本文件是索引,不是自动生成物),并写清它的接线归属: 进 CI / 进
package.json/ 由某个 skill 调 / 只手工跑。 - 「只手工跑」是合法选择,但要写出来 —— 🔴 行不是罪状,是「知情的选择」与 「忘了接」之间的区分标记。分不出这两者,正是本仓 2026-08-29 盘出 11 个 🔴 的成因。
- 脚本头注释里写清为什么存在(不只是用法)。
*-verify.sh那批的头注释是范式。