Skip to content

scripts/ 索引

本文件只做索引:每个脚本做什么、怎么跑、有没有东西在自动执行它。 🚫 这里不定义任何规则。脚本自身的头注释才是它的判据说明书 —— 本文件只给一行导航。

现状:scripts/29 个条目 = 27 个脚本 + scripts/lib/ 目录 + 本文件 (现数:ls scripts/ | wc -l;脚本数要去掉这两个非脚本条目,见 §4 的命令)。 ⚠️ 份数与接线状态都会漂,§4 给了现算命令;本文件里的表是 2026-08-29 由那条命令生成的快照。


1. 按用途分组

守门(CI 里跑,或该跑)

脚本做什么怎么跑
scripts/check-no-inline-zh.mjssrc/ 里绕过 src/lib/strings/ 的中文字面量pnpm run check:no-inline-zh
scripts/check-no-inline-en.mjssrc/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--readmesgit 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.mjssrc/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.ymlbash 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.shRB 侧跨端对齐验收(storage_path + reference_words)
scripts/context-budget.sh恒加载区体量与趋势的仪表(恒 exit 0不是闸门context-budget.md

🔑 五个脚本带 --max-skip=N(2026-08-29 T3-1 四个 + 2026-08-30 privacy-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.shSupabase 全量备份(免费版只有 7 天自动备份)supabase-backup-restore-runbook.md
scripts/backup-supabase-storage.mjsStorage 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.jsonpnpm run check:version-sync
scripts/gen-latest-json.mjs从 CI 制品生成 Tauri updater 的 latest.json(指向公开仓而非私有镜像仓)release.yml 的 publish job

构建与资产生成

脚本做什么怎么跑
scripts/build-content-script.mjssrc-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,不是执行图)

  1. CI=0 不等于「不在 CI 里」 —— 经 package.json 间接进 CI 的那批(pkg≥1ci.yml 跑对应的 pnpm run)在这一列是 0。实际在 ci.yml 里跑的有 8 个: check-no-inline-zh · check-no-inline-en · check-schema-frozen · set-versioncheck:version-sync)· audit-locale-coverage · check-claude-md-paths · check-doc-links · build-content-script(经 pnpm build)。
  2. CI=1 也不等于「在 CI 里跑」 —— scripts/release.sh 那个 1 是 .github/workflows/release.yml 的一行注释提到了它,workflow 并不执行它。 真正被 workflow 直接 run: 的是 6 个:cross-end-check.sh + check-vocab-asset.shci-cross-end.yml)· migration-verify.sh + sync-verify.sh + learning-loop-verify.shci-verify.yml)· gen-latest-json.mjsrelease.yml,仅发版时)。

🔒 因此:写 workflow 注释时别写脚本全名(写 check-schema-frozen,别写带扩展名的全名)。 这张表是判断「还有谁没接」的唯一依据,一次注释提及就能把没在跑的脚本标成 CI=1, 等于把剩余工作面藏起来。ci-verify.yml 的文件头记着这条纪律 —— 2026-08-29 建它时 头两版注释正好踩中,当场把 check-schema-frozen.mjscross-end-check.sh 的 CI 列刷高了。

脚本CIpkgskillverif
scripts/audit-locale-coverage.mjs0110
scripts/backup-supabase-storage.mjs0000🔴
scripts/backup-supabase.sh0012
scripts/build-content-script.mjs0300
scripts/check-claude-md-paths.mjs0200
scripts/check-doc-links.mjs0100
scripts/check-no-inline-en.mjs0110
scripts/check-no-inline-zh.mjs0110
scripts/check-schema-frozen.mjs0111
scripts/check-vocab-asset.sh1030
scripts/context-budget.sh0011
scripts/cross-end-check.sh1022
scripts/db-audit.sh0001🔴
scripts/fetch-openmoji-assets.sh0000🔴
scripts/gen-latest-json.mjs1001
scripts/gen-word-illustrations.py0000🔴
scripts/learning-loop-verify.sh1002
scripts/migration-verify.sh1002
scripts/ops-verify.sh0003🔴
scripts/privacy-verify.sh1002
scripts/release-verify.sh1032
scripts/release.sh1011
scripts/reset-dev-data.sh0001🔴
scripts/set-version.mjs0111
scripts/sync-verify.sh1005
scripts/verify-rvh-alignment.sh0010

🔴 共 6 个(11 → 9 → 8 → 7 → 6)。分类,别混为一谈:

  • 接不了 / 不该接(2 个)ops-verify.sh · db-audit.sh。 2026-08-29 实测,不是没人做,是做了发现前提不成立

    脚本无凭据时为什么不接
    ops-verify.shPASS=0 SKIP=8CI 里一条都验不了 —— 接了就是 100% 假绿闸门
    privacy-verify.shPASS=7 SKIP=82026-08-30 已接,但走的是 schedule 不是 commitmonitor-prod.yml)。当初判「不该接」指的是不该按 commit 接 —— 那 7 条(P8-P13)全是打 admin.lampio.app 的线上 HTTP,验的是「已部署的站点」不是「这次改动」;更要命的是 admin 走 Vercel 部署、Vercel 不等 Actions,所以真正会出事的时刻(部署完而没人提交任何东西)恰恰是 commit 触发永远覆盖不到的。⇒ 跟着时间走,不跟着提交走
    db-audit.sh无凭据直接退出纯生产数据巡检,压根没有静态段
  • 暂缓、有明确理由(1 个)release-verify.sh —— ✅ 2026-08-30 已接(这一类现在是空的)。 当初暂缓的理由是它的 SKIP 数是网络可达性的函数(同机同码连跑两次 SKIP = 4 / 11), 钉任何基线都会得到一道随网络天气变红的门。解法是拆段而不是调基线: 新增 --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.shskill=1 所以不计入 🔴)。它exit 0, 由 /docs-audit Step 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.pyscripts/migration-verify.sh(迁移指纹)
scripts/lib/minisign_verify.pyscripts/release-verify.sh(updater 签名验签)
scripts/lib/mastery_ladder.pyscripts/learning-loop-verify.sh(掌握度阶梯)
scripts/lib/parse-push-payload.pyscripts/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 那批的头注释是范式。