* ci(studio): measure drag smoothness on a quiet runner, with a blank-page control
* ci(studio): gates read the base the run measured, not the base branch tip
* style(ci): format the base-file test
* fix(cli): commands exit without waiting on update checks or telemetry
The update and skills checks run in a detached child that refreshes the
caches for the next run; the parent only reads the caches. finalizeCli no
longer awaits the telemetry flush: the exit handler flushSync() delivers
what is queued. The events beacon, feedback and figma error paths stop
awaiting flush() too.
* fix(cli): retry a failed background check at most once an hour
Before starting the background worker, the CLI stamps a per-check attempt
time and skips a check it attempted within the hour, so a dead network no
longer starts a worker on every command. The stale-pin notice writes onto a
fresh read, detached spawns set windowsHide, and a test ties the worker path
to its build entry.
* fix(cli): a failed-check stamp dated in the future no longer silences update checks
Extends scripts/ci/cli-telemetry-e2e.mjs (already used to verify the `add`
command's telemetry) to spawn the built `render` command against two
generated fixture compositions and assert on the captured PostHog batch,
rather than a unit fixture that stubs the browser or ffmpeg.
One fixture sets every probed property (audio and image elements present,
root/body CSS size deliberately mismatched from the declared composition
size, a skill flag, one injected environment override) and its pair sets
none, so each property is asserted on both its present-and-correct side and
its absent-or-zero side in the same pair of runs: audio_count, image_count,
root_body_mismatch and its delta bucket, authoring_skill_source,
hf_env_overrides, and ffmpeg/browser_version_major.
Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
* feat(cli): track which registry items `add` installs
`cli_command` records that `add` ran and nothing about what it installed, and
the registry is served from raw.githubusercontent.com, which gives no per-item
counter either — so there is no way to tell which block or component people
actually pull, and no way to know what is worth building more of.
Emit one `registry_item_added` event per item written into a project, from
`runAdd` after the install succeeds. That is the single choke point: the bulk
`add <tag>` path re-enters it per item, and a failed or compatibility-refused
install throws before it, so a refused install is never counted as a download.
`requested` separates the item the user named from the transitive
`registryDependencies` pulled in behind it; without it a popular dependency
outranks everything that depends on it.
Item names are public registry identifiers, never user content or project data,
and the event goes through `trackEvent` — an install that opted out via
`hyperframes telemetry disable`, `HYPERFRAMES_NO_TELEMETRY` or `DO_NOT_TRACK`
sends nothing.
* test(cli): cover `add` telemetry end to end against the built CLI
The unit tests assert the emit seam and nothing past it. `shouldTrack()`
short-circuits whenever `isDevMode()` is true, and that is true for any `.ts`
entry, so under vitest a real event and no event are indistinguishable and the
transport is never exercised at all.
Drive the built CLI instead and assert on the HTTP body it actually produces:
one event per installed item, the dependency reported with `requested: false`,
an opted-out install sending no request at all (not merely one without this
event), and a refused install counting nothing.
Two fixtures, because neither case is reachable through the real registry. The
registry origin is a first-class project setting, so a local one supplies the
`registryDependencies` edge that no shipped catalog item declares today; and
`globalThis.fetch` is wrapped to capture the batch rather than send it. The
faked 200 is load-bearing: only a failed flush leaves events queued, and only a
non-empty queue spawns the detached `flushSync` child that would bypass the
hook and reach production analytics.
Verified the check can fail — forcing `requested: true` for every item turns it
red on exactly the dependency assertion.