Skip to content
Development
Skill

/release-ota-promote

Publish signed Electron UI/daemon bundles to a local OTA feed with atomic manifest switch and digest readback, while dry-running the K8s drain_respawn rollout. Prod is refused before writes; feed publication is not client apply.

BOOST
From plugin
prismercloud
1.6k102 skills
Install
$ npx -y skills add Prismer-AI/PrismerCloud --skill release-ota-promote --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/release-ota-promote

Context preview

The summary Claude sees to decide when to auto-load this skill.

Publish signed Electron UI/daemon bundles to a local OTA feed with atomic manifest switch and digest readback, while dry-running the K8s drain_respawn rollout. Prod is refused before writes; feed publication is not client apply.

SKILL.md

release-ota-promote.SKILL.md
name: release-ota-promote
description: Publish signed Electron UI/daemon bundles to a local OTA feed with atomic manifest switch and digest readback, while dry-running the K8s drain_respawn rollout. Prod is refused before writes; feed publication is not client apply.
license: MIT
scope: coding
compatibility:
  - claude-code
allowed-tools:
  - Bash
metadata:
  category: release

release-ota-promote

把一份 runtime OTA 沿**双通道**下发(`apc/01` §2 ota-promote,收口步 7/8/11)。这是**线 3 admin fleet 下发**,交付模式治 **doc01 #8**("promote 用了线 1 kill1 而非线 3 drain_respawn"):

  • **K8s daemon 通道**:交付 **drain_respawn**(排空在飞 task → 优雅 `exit(0)` → supervisor respawn → boot-OTA 换版,用户**无感/无中断**)。正规落点 = admin fleet rollout endpoint `POST /api/admin/runtime-releases/rollout`(`src/im/api/admin.ts`)→ 给每个目标 daemon 下发 `runtime.update.apply` 帧 → daemon **arm drain_respawn**。本 verb **不再 spawn 线 1 `kill 1` 的 `runtime-ota.ts`**(那会打断在飞 task),而是**构造这条 drain_respawn rollout 请求**。本机替身**只 dry-run 出请求、不真 POST**(真 POST 需 approvalId + release row + 在线 daemon = M5)。
  • **桌面 feed 通道**:本机替身**未接线**——诚实标 ⬜(铁律 4)。缺①桌面本地 feed 基座(`apc/05` §3,P0-4 未落)②本机禁写真 Nacos manifest(= M5)。

**什么时候用**:一个新 runtime 版本发布后,要让 fleet 里的 daemon 采用它。

承重纪律(发版 skill 的命根子,先记死)

  • **prod OTA 必拒**(不变量 2)。`--env prod` **先于任何 OTA 动作硬拒**,不做任何下发。**别试 prod**:真 prod promote = M5。
  • **交付模式 = `drain_respawn`,绝非 `kill1`**。线 3 fleet 下发必须排空在飞后再 respawn(`kill 1` 会打断用户在飞 task,那是线 1,仅 dev/紧急)。`k8s.directive` 必须是 `drain_respawn`。
  • **不宣称 `drain_respawn` 已执行**。本机替身**只 dry-run 出 rollout 请求(`k8s.posted:false`),不真 POST**(= M5:approvalId + release row + 在线 daemon)。照实报"已构造 drain_respawn 请求,未真下发",**绝不说"已 drain_respawn 下发"**。
  • **桌面 feed 走真文件副作用**。传 `--desktop-feed-dir` 与 UI/daemon builder metadata;verb 先校验 size + SHA-512,复制版本化 bundle,最后原子切 `manifest.json`,再从磁盘回读摘要。只有 `desktop.status:'applied'` + `readback.verified:true` 才能宣称 feed 已发布。
  • **feed 已发布 ≠ Electron 已应用**。客户端 `check → stage → apply` 需要另取运行态证据,不得从文件写入外推。

Anti-pattern(直接判红)

  • ❌ **`--env prod`** 想 promote prod。命中即 blocked,别绕。
  • ❌ **把交付模式报成 `kill1` / 线 1 `kubectl exec kill 1`**。本 verb 是线 3,交付 `drain_respawn`;报 kill1 = 把在飞打断当成了正规下发。
  • ❌ **把 `k8s.posted:false` 的 dry-run 说成"已 drain_respawn 下发/已 POST"**,或把本地 feed 已发布说成"Electron 已更新"。

工具契约(签名以此为准)

> `apc` **不在 PATH**——在仓库根用 `npx tsx sdk/apc/bin/apc.ts <sub>`。

| 命令 | 作用 | 退出码 | | --- | --- | --- | | `npx tsx sdk/apc/bin/apc.ts release ota-promote [--env dev\|test\|prod] [--json]` | K8s 通道构造 drain_respawn rollout 请求(落点 admin fleet rollout endpoint,本机替身 dry-run 不 POST)+ 桌面通道 ⬜;prod 硬拒 | `0` green(真 rollout 完成,仅 M5)· `1` blocked(本机 dry-run 边界 / 桌面未接线 / prod 拒) |

`--json` 字段(**名以此为准**):

  • 顶层:`{ verb:'ota-promote', decision, env, deliveryMode, k8s, desktop, blockers:[], notes:[] }`
  • `k8s`:`{ channel:'admin-fleet-rollout', endpoint, directive:'drain_respawn', frame:'runtime.update.apply', request:{channel,version,target}, posted:false, boundary }`(`directive` = 交付模式,**必须 drain_respawn**;`posted:false` = 本机替身未真 POST)
  • `desktop`:成功时 `{ status:'applied', deliveryMode:'local-file-feed', plan:{manifestPath,...}, readback:{verified:true,...}, boundary }`;校验/写入/回读失败时 `{status:'blocked', blockers:[...]}`。
  • `deliveryMode`:`'drain_respawn(线 3 admin fleet rollout · 本机替身 dry-run)'`——**不是** kill1

Workflow

1. K8s 通道(构造 drain_respawn rollout 请求)

npx tsx sdk/apc/bin/apc.ts release ota-promote --env dev --json > /tmp/ota.json; O=$?
echo "ota-promote exit=$O"
  • 读 `k8s.directive`——必须是 `drain_respawn`(线 3,排空在飞后 respawn),**不是** `kill1`。
  • 读 `k8s.posted`——`false` = 本机替身**只 dry-run 出请求、未真 POST**(真 POST = M5)。
  • 本机替身 → `decision:blocked`(exit 1)+ blocker 说明"真 admin rollout POST = M5"。**如实报**,这是本机替身边界(release-common.ts 本机替身边界),不冒充已下发。

2. 桌面通道(诚实 ⬜)

使用隔离目录与现有已签名 builder metadata 发布并回读:

npx tsx sdk/apc/bin/apc.ts release ota-promote --env dev \
  --desktop-feed-dir .e2e-tmp/apc-ota-feed \
  --desktop-ui-meta apps/desktop/dist-ui/ui.manifest.json \
  --desktop-daemon-meta apps/desktop/dist-ui/daemon.manifest.json --json

核验 `desktop.status=applied` 、`desktop.readback.verified=true`,并独立检查 `desktop.plan.manifestPath` 及版本化 bundle 存在。

3. prod 一律拒(验证人闸)

npx tsx sdk/apc/bin/apc.ts release ota-promote --env prod --json; echo "prod exit=$?"
# 预期:decision=blocked, exit 1, k8s.skipped=true, blockers 含 "prod 人闸"

Failure / 边界

  • 本机替身 → K8s 通道 dry-run 出 drain_respawn 请求但不真 POST → `blocked`。这是**预期的真行为**(本机替身边界),不是 skill 的红。
  • 桌面通道 = ⬜(feed 基座未落 + 本机禁写真 Nacos)。
  • 真 drain_respawn fleet 下发(真 POST admin rollout endpoint + approvalId + release row + 在线 daemon)= M5;本 verb 只把交付模式**纠正为 drain_respawn** 并 dry-run 出请求。

输出契约(机器判据按这个复算,别自由发挥格式)

本 skill 的判据**不是**「报告里出现了 `drain_respawn` 这个词」——旧判据正是一条 `match:"drain[\s_-]?respawn"`,一份宣称 `posted:true`、`decision:green`、"fleet 已换版"的**纯编造**报告在它下面满分。新判据**重解析你贴的两份 `--json` 产物**,把 `k8s.channel/directive/frame/posted` 逐字段钉死、从 `decision` 反推退出码、并把 rollout body 的 `version` 与磁盘上真 `/VERSION` 比对(`json-claim`)。

报告必须带这组行 + **两份原样 JSON**(`--env dev` 在前,`--env prod` 在后):

DIRECTIVE: drain_respawn
POSTED: false
DESKTOP: applied
DESKTOP-VERIFIED: true
RUN-EXIT: 1
PROD-DECISION: blocked
PROD-K8S-SKIPPED: true
PROD-RUN-EXIT: 1

判据会判红的情况(任一):

  • 没有可解析的 fenced JSON;
  • `k8s.directive` 不是 `drain_respawn`(写成 **`kill1`** 直接红——线 1 会打断在飞 task,把它当交付模式正是本 verb 存在的理由)、或 `k8s.channel != admin-fleet-rollout`、`k8s.frame != runtime.update.apply`;
  • **`k8s.posted != false`**,或 `posted:false` 却 `decision:green`——**什么都没 POST,就不许把任何东西报成已下发**;
  • `desktop.status != applied` 或 `desktop.readback.verified != true`,或把 feed 发布冒充为 Electron 客户端已 apply;
  • `DIRECTIVE` / `POSTED` / `DESKTOP` 与产物字段不一致,或 `RUN-EXIT` 不是 `decision` 推出来的;
  • rollout body 的 `k8s.request.version` **不等于磁盘上 `/VERSION`**;
  • prod 产物不是 `blocked` + **`k8s.skipped:true`**(连 rollout 请求都没构造)+ `blockers` 非空。

**诚实边界**:桌面通道有真文件副作用与摘要回读;K8s 通道仍只构造请求、不 POST。因此顶层仍应 `blocked`,直到真 admin rollout 产生送达证据。

产出(副作用 oracle,报告里必须给)

1. **decision + 真退出码**:`green`/`blocked` ⇄ `0`/`1`。 2. **交付模式证据**:`k8s

Read more
Ships withprismercloud

Prismer Cloud

Get the whole plugin
Stats
1,554
Stars
17
Forks
Active
Maintenance
TypeScript
Language
MIT
License
2d ago
Last commit
6mo ago
Created

Repo: Prismer-AI/PrismerCloud

Other skills on prismercloud.