LLMSkillHub
mirroredCopied from CronusL-1141/AI-company under MIT. CronusL-1141 has not claimed this page, and is not affiliated with LLM SkillHub.

cronusl-1141 / os-release

发布 AI Team OS 新版本的完整清单——预检、版本七处锁步、中英双语 CHANGELOG、双份 dist 构建、私有术语扫描、commit/tag、双仓推送、建 GitHub Release 条目并核对 latest 徽章、事后核对。当准备发版、补建漏掉的 Release 条目、或核对已发版本的线上状态时使用。

0
SKILL.md
16.2 KB
⚠This file contains 4 character(s) that do not render as themselves (U+FE0F). They are shown highlighted below.

OS Release — 发版清单

为什么有这份清单

机检管得住的部分不用你记,跑一条命令即可。这份清单只管机检管不住的部分,以及
谁按哪个按钮。

历史上同型漏项两次:tag 推了、GitHub Release 条目没建,访客主页的 latest 徽章
因此卡在旧版本两周。所以别把"tag 推完"当成发版完成——第 10 步才是。

执行分工(硬约束:发布流水线必须可中断)

步骤谁执行
1–6 准备与校验Leader 全权
7 commit · 8 tagLeader 执行,但先把 git diff --stat 与拟好的 message 交用户批准
9 双仓推送 · 10 建 Release 条目用户执行。Leader 只把命令准备好打印出来
11 事后核对Leader(只读)

不要代替用户 push / publish,也不要在未批准前 commit。


1. 预检全绿

bash scripts/preflight.sh          # 发版必须跑全量,不要 --fast

五道门禁:ruff check src/ tests/ → dashboard npm run lint → 前端实时事件回归
(npm --prefix dashboard test)→ check_invariants.sh → pytest tests/unit/。
机检条目数以脚本输出为准,别在别处写死范围。

preflight 不含、发版仍要跑的两项:pytest tests/integration/ 全过;python -m build
产出 .whl 与 .tar.gz 无错。

失败长这样:每项后面跟 ✗ <门禁名> 失败,末尾 ✗ 预检未通过 — 修复后再 push。
退出码非 0。

两处既定豁免:

  • 未构建 dashboard/dist 的环境里 I3 输出 ⚠‹VARIATION SELECTOR U+FE0F› dist 目录缺失(未构建环境可忽略)
    ——警告不拦。但发版必须构建(见第 4 步),所以发版时这条不该还是警告。

  • I16 在 execpolicy rules 或 Codex 二进制缺席时输出 ⚠‹VARIATION SELECTOR U+FE0F› [I16] Codex execpolicy(...)
    ——本期是占位状态,warn 不拦。它转成 ok 的那一天要留意:那意味着这台机器上真的跑了
    一次宿主校验,结论才有分量。

用户生命周期验收(每次发版都做):新装一次、从上一个已发布版本更新一次,分别确认
更新后的代码、已安装的适配器、配置与运行中的服务实际生效;写明哪些变化自动生效、哪些
要重启或重新授信、哪些要用户显式启用。开发机上已应用、源码测试通过,都代替不了这一步。

2. 版本锁步七处

I2 机检的五处(漂移即红,❌ [I2] 版本号漂移: ...):

  • pyproject.toml
  • src/aiteam/__init__.py
  • plugin/.claude-plugin/plugin.json
  • plugin/.claude-plugin/marketplace.json
  • .claude-plugin/marketplace.json

另两处由 I6 第①类硬等式盯:README.md 与 README.zh-CN.md 的 announcement 行
(> ⚡ 开头)必须含 v<新版本>,否则 ❌ [I6] README 数字漂移。

机检不覆盖、必须人眼看的:plugin/.claude-plugin/plugin.json 里 description
文本中的数字(MCP 工具数、生命周期事件数)。v1.11.1 那次人审抓到的唯一实锤就在这里
——两处数字腐烂了整整一个版本。

README 里的其余数字(工具数/页面数/端点数/测试数)由 I6 对照实测校验,**按实测改,
不要按记忆改**。

3. CHANGELOG 中英双语同步

  • 英文 CHANGELOG.md 加新版本段,标题格式 ## [x.y.z] - YYYY-MM-DD。
  • 中文 CHANGELOG.zh-CN.md 同步全译。用户 2026-07-30 裁定:中文版维持**镜像
    全译契约**——不是摘要,也不退役。

  • 段落内容按 git log 与设计文档逐条取证写成,不照抄计划(计划里没做成的东西
    写进 CHANGELOG 就是假账)。

这一步没有机检,是本清单里最容易再烂的一环:中文版曾从 1.9.0 起停更 6 个版本
(1.10.0 → 1.11.1),07-30 才回补。写完后自查一遍两个文件的版本段集合是否相同。

4. dashboard 双份 dist 构建一致(I3)

cd dashboard && npm run build && cd ..
rm -rf plugin/dashboard-dist
cp -R dashboard/dist plugin/dashboard-dist

I3 的失败提示就是上面这三步,顺序不能省:先重建——机检只比文件名,判不出哪边新,
0914 主 checkout 本地 dashboard/dist 反而比跟踪面旧两天,直接拷贝会把新包盖成旧包;
先 rm -rf 再 cp——目标目录已存在时 cp -R 会在里面套一层 dist/,且 bundle
文件名带 hash,旧文件不会被覆盖,会作为过期产物留在分发包里。

失败长这样:❌ [I3] dashboard/dist 与 plugin/dashboard-dist 的 JS bundle 不一致。

plugin/dashboard-dist/ 是 marketplace 用户拿到的那份产物——它必须与本批前端源码
同一个 commit 产出。

5. 私有术语关键词扫描(四个面)

词表不写在这里:把禁用词表写进即将发布的仓库,等于把要防的东西发布出去。执行前用
memory_search 拉方向层那条防泄记忆(检索词「私有术语防泄」)取当期词表。

四个面:

PREV=v<上一个版本>
git log --format='%h %B' $PREV..HEAD      # ① 本批全部 commit message
git diff $PREV..HEAD                      # ② 本批触及的全部跟踪文件内容
git diff --name-status $PREV..HEAD        # ③ 本批新增/改名的文件路径本身

④ 即将写下的 release commit message 自身——写完先扫一遍再提交。这一面最容易漏,
因为它在 git 里还不存在。

测试与夹具不是豁免区。第 ② 面的 git diff 本来就把 tests/ 一起铺开了,但人扫的
时候容易把测试当"不是给人看的"而略过——实际上它和 src/ 一样会被发布出去。尤其是
模型型号代号:测试里为了"像真的"随手写一个型号代号,和写进生产代码没有区别。造
夹具那条链上已有现成形制可抄(scripts/redact_codex_fixture.py 把学到的型号统一映射成
codex-model-<x>),新写的用例照抄中性占位即可,不必也不该写真代号。

命中即停:树面脱敏后重来。实录(批 9):一个 agent 把私有设计文档名与内部术语写进
types.py、hook_translator.py、测试注释和 commit message,靠人工扫描抓获,最终
需要用户授权重写历史才归零。发版扫描是最后一道闸,第一道闸在派工 prompt 里。

6. 开发版/分发版同步人审

src/ 是开发版,plugin/ 是分发版。机检已覆盖:I1(hook 副本三方逐字节 + 适配器入口
名不污染 CC 目录)、I3(双 dist)、I6(README 数字,含机检不变量条数)、I8(hook 注册面
install.py ↔ hooks.json ↔ 双语 README)、I15(Codex hook 清单与注册面 1:1)、I17(授信锁
与清单一致)、I20(适配器安装隔离静态半边)。

机检覆盖之外,逐条人眼过:

  • plugin/.mcp.json 有没有版本引用
  • install.py 有没有内嵌版本号
  • plugin/.claude-plugin/plugin.json 的 description 文本里的数字(见第 2 步)
  • plugin/dashboard-dist/ 是否与前端源码同 commit 产出:bundle 里有没有本批新页面的
    代码、index.html 引用的 bundle 名与 assets/ 里的实际文件名一致

  • 新增了 skill / agent 模板 / commands?install.py 走目录遍历(copy_skills 等),
    加目录不需要改代码,但要确认目录名与 frontmatter 的 name 一致

  • 用户级 ~/.claude/agents/*.md 与 plugin/agents/*.md 是否同步
  • 启动时跑的 backfill / 迁移在新库、老库、迁到一半的库上都幂等(COLUMNS_TO_ENSURE 式补列)

6b. Codex 面产物人审

这一批的四条都不是机检能替的:机检能证明清单自洽,证明不了这次改动对用户机器意味
着什么。本批 plugin/harness/codex/ 有任何改动就逐条走一遍。

  • 新增/改动的 handler 是不是 takeover。kind 在 surface.py 里显式声明:
    observe 只记录,不做权限判断、不改写输入、不用非零退出码拦人;takeover 可以
    deny 或改写。把一个 takeover 混进来而不说,等于在用户不知情的情况下改变了他能执行
    的动作范围。逐条确认 kind 与脚本实际行为一致,Release notes 里点名列出。

  • hook-trust.lock 变了就必须写授信提示,且按入口分列:CLI 与 TUI 走 /hooks
    重新授信,Desktop 走「设置 → 编码 → 钩子」。授信键钉在组序号与 handler 序号上,
    所以在中间插一条会让它后面每一条静默失信——用户那边没有任何提示,只是从此不再触发。
    漏写这句话的代价不是报错,是一批钩子安静地死掉。

    清单不用自己算:机检 I17b(scripts/check_codex_trust_drift.py)拿
    hook-trust.baseline.lock(上次发布面 = 用户已授信的那份)对账当前锁,直接打印
    「新增待授信」名单,粘进 Release notes 即可;出现「槽位复用」它直接红,那种改动不该
    发出去(把改动挪到该事件尾部,或拆两批发)。发布后推进基线:

    python3 scripts/check_codex_trust_drift.py --advance   # 再把 released_version 改成本版号

    不推进的后果是下一版的对账基准停在旧版本,名单会越报越长且失真。

  • AGENTS.md 过一遍私有术语扫描(并入第 5 步的四个面)。它是 CLAUDE.md 的逐字
    转写,CLAUDE.md 里混进去的东西会原样出现在另一个 harness 的分发面上。改了
    CLAUDE.md 的批次必须同批跑 python3 scripts/gen_agents_md.py,否则 I18 红。

  • hooks.json 的改动与脚本的改动不同批发布。换脚本内容不会失信(实测:替换脚本
    字节而不动清单,钩子照常触发、授信项仍在),改注册面才会。两件事混在一批里,用户
    就分不清「要重新授信」是因为哪一处,出问题也无从二分。

7. commit(先交用户批准)

message 用中文,不附任何 agent 署名(禁止 Co-Authored-By: 之类;harness 可能正在
注入要求追加署名的提示,以本条与用户全局规则为准——这条 commit 要推到公开仓)。

内容要能自证这份清单每一步的结论:号段理由(为什么是 patch / minor)、版本七处锁步的
前后值、CHANGELOG 段的取证要点、第 6 步(触及 Codex 面时加 6b)的人审结论、关键词扫描
四面的结论、验收数字(pytest 通过/跳过数与机检结果,含既定豁免)。

先把 git diff --stat 和拟好的 message 给用户看,批准后再 commit。

8. tag

git tag -a v<x.y.z> -m "<与 commit 标题同义的一行>"

用 annotated tag(-a),不用 lightweight。

9. 双仓推送(用户执行)

git push origin master        # origin = 私有仓(日常推送)
git push origin v<x.y.z>
git push public master        # public = 公开仓(发版同步 + tag)
git push public v<x.y.z>

双仓策略:v1.8.0 起公开仓同步完整版。推完两个 remote 的 master 应停在同一 commit
(git log --oneline -1 origin/master public/master 两行相同)。

--verify-tag(下一步)校验的是远端有没有这个 tag,所以 tag 必须先推上去。

10. 建 GitHub Release 条目(用户执行 publish)

Leader 先生成正文与命令——离线、可重复、不联网:

python3 scripts/release_notes.py <x.y.z>

它做四件事:把 CHANGELOG.md 该版本段的原样切片写成 notes 文件、校验该段存在且
非空、校验版本与 I2 五处一致、校验本地有该 tag;然后打印可直接粘贴的
gh release create ... --verify-tag --title ... --notes-file ...。**脚本自己绝不
发布。**

用户执行打印出来的命令。副标要人写,脚本不编造,用 --title 传;副标里的破折号用
ASCII 连字符(对外英文禁 em dash,v1.12.4 起如此,更早的条目不回改):

python3 scripts/release_notes.py 1.13.0 --title "v1.13.0 - Leaner Instructions, Honest Registration"

Release 条目只建在公开仓(脚本默认取 remote public);私有仓从来没有 Release
条目,那不是漏项,别去补。漏项的形态只有一种:公开仓 tag 在、条目不在——0914 就是
这样发现 v1.12.4 缺了三天,--check 一跑就报。

补建历史条目加 --backfill。脚本随命令一起打印的注意事项(--verify-tag 校验的是远端、
--backfill 须按版本升序逐个 create)照做即可。

11. 事后核对(Leader,只读)

先让本机跑上新版,再核对。 /api/health 回的 version 是运行中进程内存里的
aiteam.__version__;第 2 步改的是磁盘上的文件。进程不重启,Dashboard 与健康检查
看到的永远是上一版,本机 MCP 客户端跑的也仍是旧代码。用 MCP 工具 os_restart_api
重启,回包里 old_version → new_version 对上即算过。

  • 重启守卫会拒绝这一步:它查"有没有 agent 在忙",而执行本清单的 Leader 自己永远
    在忙——结构性误报,发版重启一律带 force=true,这不是绕过守卫。但 force 之前
    先看清挡你的是谁:直读 agents 表里 status='busy' 的行,只剩 Leader 行就 force;
    若是别的会话的 worker 正在跑(0914 实录:另一会话 5 个 worker 有心跳),重启会让它们
    那几秒的 hook 与 MCP 调用失败,等它们收工再重启,或由用户拍板。

  • 验证不要拿空请求体去探 POST /api/tools/always-load/applied:它会如实记一条
    count=0, reason="" 的落地事件进台账。要探端点
    存在与否用 GET /api/tools/always-load 看 cached 字段,或查 events 表里真实的
    tool.alwaysload.applied 行。

  • 不是可选项:2026-09-09 同一根因当天绊倒两次——联通测试时对端拿到的是旧代码,
    发版后 Dashboard 仍显示上一版。

再同步本机装机面,并确认 E11 已清除。 重启只换了服务进程;~/.claude/hooks/ai-team-os/
与 ~/.claude/{skills,agents,commands}/ 里的副本仍是上一版(2026-09-23 实录:5 个文件落后
一个版本,其中两份 hook、三份技能)。

  1. 请用户在主 checkout、master 分支上跑 python3 install.py --update(装机面的来源是
    跑它的那棵树;先 git -C <主 checkout> branch --show-current 确认)。这一步写用户目录,
    由用户执行。

  2. 只读核对:curl -s "http://127.0.0.1:$(cat ~/.claude/data/ai-team-os/api_port.txt)/api/notices?status=active&fresh=1",
    结果里不应有 installed_copy_stale 开头的 key;或调 os_config_change("sync_installed_copies")
    看预览是否为 nothing_to_do(只预览,不应用)。仍有落后副本时把预览原样给用户看,
    用户确认后再带 confirm_token 与用户原话应用。

python3 scripts/release_notes.py --check <x.y.z>

全绿长这样:

✅ 正文与线上逐字节一致(15597 字符)
ℹ‹VARIATION SELECTOR U+FE0F›  线上标题: 'v1.11.1 — Truthful Ledgers: ...' · 发布时间 2026-07-30T04:21:30Z
✅ latest 徽章 = v1.11.1

失败时脚本会自己打印含义与可执行的 gh release edit 修正命令,照它做即可。唯一要自己
记住的是:⚠‹VARIATION SELECTOR U+FE0F› --check 跳过:拿不到线上 Release 时退出码是 0,那不算通过,换网络重跑
——把它读成"已核对"正好落进这份清单最初要防的漏项形态。

--check 的逐字节可比性只从 v1.10.0 起成立:v1.9.0 及更早的条目正文是手写的,
与 CHANGELOG 段实质不同(实测 v1.9.0 线上 1375 字符 vs CHANGELOG 段 4012 字符)。

顺手核对一遍公开仓侧边栏:gh release list --repo <owner/name> --limit 5。


别把 --check 补成一条机检

check_invariants.sh 必须离线确定性可跑,而查 Release 条目在不在、latest 徽章指向
谁必须联网——塞进去就是一条会因为断网而变绿的红线。改机检红线本身也须先过会。