Skip to content

fix(cli): no auto-update across 0.x minors; install skills at the CLI's release tag - #7013

Merged
Vitozhu04 merged 6 commits into
mainfrom
wanbi/cli-update-guard-skills-tag
Nov 3, 2026
Merged

Vitozhu04 merged 6 commits into
mainfrom
wanbi/cli-update-guard-skills-tag

Conversation

@Vitozhu04

@Vitozhu04 Vitozhu04 commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

What

Three fixes to how the CLI updates itself and its skills.

  1. Auto-update stops crossing 0.x minors. The "never auto-install across a major" guard compared only the first number, so on 0.x every release looked like the same major and 0.8.x → 0.9.x installed silently in the background. Before 1.0 the guard now compares major.minor (0.8 vs 0.9 is a major jump: notice only, no install); from 1.0 on it compares the major. Patch updates inside one minor (0.9.2 → 0.9.3) still auto-install.
  2. Skills install at the CLI's own release tag, not main. skills update, bare skills and init now install from https://github.com/heygen-com/hyperframes#v<VERSION>. skills check and the background freshness nudge read skills-manifest.json at the same tag. There is no up-front git ls-remote probe: publish.yml pushes v<VERSION> before npm publish, so a release build reads its tag directly. Only a 404 for the tag's manifest falls back to main; a timeout, missing git or proxy error surfaces as an error (skills update then takes its existing offline path and keeps what is installed; the background nudge writes nothing). The ref is decided once per run: checkSkills returns the ref it read, and skills update installs from that same ref. Dev / pre-release builds use main, as today. Release tags are the npm-published commits (checked: npm view hyperframes@0.9.3 gitHead = v0.9.3 = f6536e9; same for 0.9.2, 0.8.231).
  3. The upstream skills CLI is pinned. npx skills add … → npx skills@1.5.13 add …, the version agentDirs.generated.ts (scripts/sync-agent-dirs.ts SKILLS_REF) and the lock-path notes were checked against, so the pin and the mirror table agree. 1.5.13 parses the #<ref> fragment (parseFragmentRef) and clones with --branch <ref> (cloneRepo); checked in its published dist/cli.mjs, and a real npx skills@1.5.13 add https://github.com/heygen-com/hyperframes#v0.9.2 … --full-depth in a sandbox printed Source: …hyperframes.git @ v0.9.2, wrote lock ref: v0.9.2, and installed hyperframes-cli byte-identical to v0.9.2 (13/13 files; 12/13 match main).

No skill text changes. Docs: docs/packages/cli.mdx, README.md, CLAUDE.md said skills update installs from main; they now say it installs at the tag matching the CLI.

Why

  • (1) A 0.x minor can break (semver), and the code comment already promised "never auto-update across major versions". In practice a 0.8 user got 0.9 with no prompt.
  • (2) An agent reads skills written for whatever main holds today while driving an older CLI from npm; the skills can reference flags the installed CLI does not have. For a 0.9.2 CLI today, 4 skills on main differ from v0.9.2 (hyperframes, hyperframes-cli, media-use, reverse-any-video).
  • (3) An unpinned npx skills runs whatever upstream published last, so an upstream release could change every install with no HyperFrames release.

How

  • autoUpdate.ts: majorOf → breakingLine (0.<minor> before 1.0, <major> after). Unparsable versions keep the old "allow" behaviour.
  • skillsManifest.ts: new cliReleaseTag(). checkSkills takes a cliVersion option and reads the raw manifest at that tag (tags do not move, so no SHA pinning needed); fetchManifest throws a typed ManifestHttpError, and only its 404 falls back to the unchanged main path. SkillsCheckResult gains ref (the ref the manifest was read at; null for a local file or URL). Callers (skills update / skills check / the background nudge) pass VERSION; skillsManifest.ts itself still imports only node builtins, because scripts/clawhub-unlist-retired.mjs loads it from source under plain node.
  • commands/skills.ts: installSkills takes the ref from the caller and appends #<ref> to the clone URL: updateSkills passes check.ref; the offline path (manifest unreachable) passes cliReleaseTag(VERSION) ?? "main". SKILLS_CLI = "skills@1.5.13".

Test plan

  • Unit tests added/updated (vitest run on the touched files: 148 passed; node --test scripts/clawhub-unlist-retired.test.mjs 7 passed; tsc --noEmit clean; oxlint/oxfmt clean)
  • Manual testing performed (E2E below)
  • Documentation updated
  • Comments follow CONTRIBUTING.md "Comments"

Fails without the fix (the PR's tests run against the main source files: 15 failed):

  • fix 1 — autoUpdate.test.ts > does NOT schedule across a 0.x minor jump (main schedules the 0.8.231 → 0.9.3 install)
  • fix 2 — skillsManifest.releaseTag.test.ts > reads the manifest at the CLI's release tag without asking git first (main fetches the main manifest) and skills.test.ts > skills update installs at the ref its check read, through the pinned skills CLI. The review follow-up does not report skills outdated against main when the git lookup fails fails at 7e1587f with expected 1 to be +0 (a failed ls-remote compared a v0.9.2 install against main).
  • fix 3 — skills.test.ts > uses linux-compatible npx command for preflight and the full install (main spawns skills, not skills@1.5.13)

E2E (pod sandbox: own HOME / npm prefix / npm cache / XDG dirs, env -i, telemetry off; real TTY via script; CLI built from main f6536e9 and from this branch at 7e1587f, which still pinned skills@1.7.4 and probed the tag with ls-remote; the 1.5.13 pin was re-checked at 275ba72 with the real install above):

Journey Before (main f6536e9) After (this branch)
J1 0.8.231, cache says latest 0.9.3, real TTY, hyperframes info detached installer launched npm install -g hyperframes@0.9.3; next run printed hyperframes auto-updated to v0.9.3, --version = 0.9.3 auto-update.log: [skip] major-bump 0.8.231 -> 0.9.3; no install after 120 s; terminal shows Update available: 0.8.231 → 0.9.3 / Run: npm install -g hyperframes@0.9.3; still 0.8.231
J1 0.9.2 → 0.9.3 (same minor; real npm registry, real published patch) auto-installed 0.9.3 still auto-installs 0.9.3 (auto-updated to v0.9.3)
J2 CLI 0.9.2, fresh HOME: skills update then skills check 4 current / 0 outdated, but compared with main: 3 of 4 core skills differ from v0.9.2 (hyperframes, hyperframes-cli, media-use); lock ref: null skills CLI shows Source: …hyperframes.git @ v0.9.2; lock ref: v0.9.2; 4 current / 0 outdated; all 220 installed files byte-match v0.9.2
J2 cross: skills installed by the old CLI, checked by the fixed 0.9.2 — skills check: 1 current, 3 outdated (exit 1); skills update reinstalls those 3 at #v0.9.2; re-check 4 current
J2 untagged build (0.9.99) — falls back to main (no #ref), 0 outdated, files == main
J3 spawned argv (npx shim) npx skills add https://github.com/heygen-com/hyperframes … npx skills@1.7.4 add https://github.com/heygen-com/hyperframes#v0.9.2 …

Sandbox: env -i, own HOME / npm prefix / cache / XDG, HYPERFRAMES_NO_TELEMETRY=1 DO_NOT_TRACK=1 DISABLE_TELEMETRY=1; the built dist is swapped into the published 0.9.3 package and its baked VERSION relabelled (0.8.231 / 0.9.2 / 0.9.99). 0.9.2 is used for J2 because main was still equal to v0.9.3 at the time.

Draft — please don't add to the merge queue; owner (Wenbo) will review first.

— wanbi

🤖 Generated with Claude Code

…'s release tag

- autoUpdate: before 1.0 a minor bump counts as a major one, so 0.8.x -> 0.9.x
  is a notice, not a silent background install. From 1.0 on, the major alone.
- skills: `skills update` / bare `skills` / `init` install from
  heygen-com/hyperframes#v<VERSION>, and `skills check` plus the background
  freshness nudge read the manifest at the same tag. A build the repo has no
  tag for (dev build) falls back to main, as before.
- skills: run the upstream installer as `npx skills@1.7.4` instead of an
  unpinned `npx skills`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Nov 2, 2026, 4:55 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

Vitozhu04 and others added 2 commits November 2, 2026 21:13
Comment ratchet: trim the comments added in the previous commit. No code change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ting it

scripts/clawhub-unlist-retired.mjs loads skillsManifest.ts from source under
plain node, which cannot resolve a relative `../version.js` import. Callers now
pass `cliVersion` (and skillsSourceRef takes it as an argument), so the module
keeps importing only node builtins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 103 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (2)

  • seqplainkeys-none-center-r0-nested-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true
  • seqrepeat-none-center-r0-root-z100: tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: COMMENT (draft — no blockers) · 0 blocker · 2 important · 0 nits · head 7e1587f

The auto-update guard (breakingLine) looks right: 0.8→0.9 and 0.9→1.0 skip, patches still install, and unparsable versions keep the old allow behaviour. The two findings below are about the skills path.

Scope

Read the full diff. Checked that publish.yml pushes v<VERSION> before npm publish, so every published build has its tag, and that skills@1.5.13 and @1.7.4 both parse #ref. Actions at this head are green. There were no earlier reviews.

Comment thread packages/cli/src/commands/skills.ts Resolved
Vitozhu04 and others added 2 commits November 2, 2026 22:25
…5.13

- A release build reads the manifest at its own tag directly (publish pushes
  the tag before npm publish). Only a 404 for that manifest falls back to main;
  a timeout, missing git or proxy error now surfaces instead of quietly
  comparing against main, so the background nudge can't cache a false
  "outdated" count.
- checkSkills returns the ref it read, and skills update installs from that
  same ref, so check and install can't disagree within one run.
- Pin skills@1.5.13 (it parses `#<ref>` too) so the pin matches the version
  agentDirs.generated.ts and the lock-path notes were checked against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fallow flagged fetchRemoteManifest's CRAP score (31.6 > 30). The tag fetch
(404 is the only fallback to main) and the SHA-pinned main fetch are now their
own functions. No behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: COMMENT (draft, no blockers) — 2 resolved, 0 open, 0 new · 7e1587f → 5f5752f

@Vitozhu04
Vitozhu04 marked this pull request as ready for review November 2, 2026 15:27

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE · 0 blocker · 0 important · 0 nits · 5f5752f. This approval covers this sha only. Both earlier findings are resolved, and every required check is green or path-skipped at this head.

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent review at 5f5752f0a814f5be002eac26b89c1d58436590ed, covering all 11 changed paths and their consumers. The 404-only tag fallback (skillsManifest.ts:663–675) and passing the checked ref into the pinned installer (skills.ts:410) resolve Genesis's earlier findings.

Blocker — the new breaking-line guard still permits a silent Homebrew minor jump. autoUpdate.ts:249–253 checks the registry target, but the supported Homebrew path at autoUpdate.ts:268–274 uses installerDetection.ts:182–185, which ignores that target and emits brew upgrade hyperframes. The detached child executes those arguments unchanged. With installed 0.8.231 and cached latest 0.8.232, the new guard passes; if the tap already contains 0.9.3, or advances while the child waits, the actual silent installation crosses the protected minor boundary. The completion marker also records the requested patch rather than the installed version.

The unversioned command predates this PR. This is an incomplete fix of the PR's new safety contract across a supported installer, not a newly introduced command. Skip Homebrew background installs until the eventual target can be enforced, retaining explicit manual upgrades. Add a scheduler-to-installer regression test using the real invocation helper; checking the formula before the detached wait alone leaves the race open.

Evidence: exact-source controlled probes confirm the allowed patch schedules unversioned Brew arguments and a simulated newer tap bypasses the boundary. No real Brew/install operation ran. Hosted logs witness all 90 cases in the four changed test files passing and a real tagged skills clone through skills@1.5.13; those runs used synthetic merge 7f254365, and do not test this target drift. No queue or merge action.

Verdict: REQUEST CHANGES
Reasoning: The registry predicate does not constrain the actual Homebrew install target, so the advertised silent-update boundary remains bypassable.

— Magi (Home)

`brew upgrade hyperframes` installs whatever formula the tap holds, so a
background run scheduled for an allowed patch could still cross a 0.x minor.
Homebrew installs now only get the update notice naming `brew upgrade`.
A new test drives the scheduler through the real installer detection and
installer script: no spawn on Homebrew, a versioned npm install for a patch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE · 0 resolved, 0 open, 0 new · 5f5752f → 144e889. Covers this sha only. My approval at 5f5752f missed the Homebrew path; @miguel-heygen's blocker is fixed here. The background installer now skips brew, and hyperframes upgrade still runs brew. autoUpdate.invocation.test.ts fails against 5f5752f and passes 24/24 here, which I reproduced locally. Required CI is green; attempt 1 failures (coreRuntimeBrowser.test.ts and a cli 2/2 timeout) are outside the diff and passed on rerun.

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent re-review at 144e889627b97ba521920e31287321e2026a5059, superseding my changes request at 5f5752f.

The Homebrew blocker is fixed: autoUpdate.ts:259–283 refuses Brew before writing pending state or launching a child. npm/bun/pnpm retain exact-version arguments; the unchanged notice and manual-upgrade paths retain brew upgrade hyperframes. The docs now describe this boundary accurately. The new real-helper invocation test checks both Brew refusal/notice and pinned npm installation.

Read all current peer bodies and audited the three-file delta plus the full 12-path PR, verifying unchanged consumer bytes against the prior audit. Genesis's current approval also acknowledges the previous gap. Fourteen independent exact-source scheduler/detection probes pass; no real child or package manager ran locally. Hosted logs show the two new invocation tests and 22 existing auto-update tests passing.

Initial CI was not all green: the producer browser test failed a retained-node assertion, and CLI 2/2 was cancelled. The original whole logs are preserved; the cancellation cause is not independently established. The producer rerun passes 267 cases and CLI 2/2 passes 3,354, with 89 skipped. Hosted execution used synthetic merge f4a2e97a into c14d92a2, rather than an isolated PR tree or the current main base. No live Homebrew/Windows updater acceptance is claimed.

Verdict: APPROVE
Reasoning: Homebrew can no longer bypass the silent-update boundary, and the allowed installers retain pinned targets. No remaining code blocker found. No queue, merge, or release action.

— Magi (Home)

@Vitozhu04
Vitozhu04 added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit b2acef5 Nov 3, 2026
138 of 141 checks passed
@Vitozhu04
Vitozhu04 deleted the wanbi/cli-update-guard-skills-tag branch November 3, 2026 13:38
@WaterrrForever WaterrrForever mentioned this pull request Nov 3, 2026
1 task done
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 4, 2026
Adopted with tests that fail on the old code (except the link change):
- offload: rewrite media inside <template>, keep same-named files from
  other folders (heygen-com#6842, offload part only)
- autoUpdate: no background upgrade across a 0.x minor; Homebrew installs
  only notify (heygen-com#7013, autoUpdate part only)
- engine: script load failures carry origin/code; aborted scripts are not
  load failures (heygen-com#7088, heygen-com#7093)
- community Discord invite links (heygen-com#7097)
- studio: write media duration to the store once per file (heygen-com#6745)
- check: restart the idle watchdog on each driver call during analysis
  (heygen-com#6919, adapted to this line's fixed 120s idle window)
- check: default wait raised from 10s to 45s, matching snapshot/render
  (heygen-com#6947)
- image cancellations no longer ignored by extension (heygen-com#6928)
- capture: system sleep no longer counts as a stall (heygen-com#7096, hand-ported;
  the sequential branch also uses the shared stall clock)

Commit watermark advances to 183614b (28 consecutive commits with
file:line evidence); PR and issue watermarks are unchanged. The rest of
the review is in docs/UPSTREAM_REVIEW_2026-11-04_CONTINUED.md.

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-authored-by: WaterrrForever <miao.yang@heygen.com>
Co-authored-by: Wenbo Zhu <wenbo_zhu@berkeley.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
staging - docs — 144e8896 Deployed Nov 2, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants