Skip to content

feat(cli): reverse start records a reverse run; cloud render carries its session - #7016

Merged
Vitozhu04 merged 4 commits into
mainfrom
wanbi/cli-reverse-start-record
Nov 2, 2026
Merged

Vitozhu04 merged 4 commits into
mainfrom
wanbi/cli-reverse-start-record

Conversation

@Vitozhu04

@Vitozhu04 Vitozhu04 commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

What

Reverse funnel telemetry, CLI side, part 1 of 2 (records only; the block branch is the stacked PR on top of this one).

  • New command npx hyperframes reverse start <video> [--dir <project>]. It probes the video (the same JSON as measure probe, plus a reverse field), then, when signed in, calls POST /v1/hyperframes/reverse/start with {surface, session_id?} and saves the returned session_id in the project's hyperframes.json as reverseSessionId (also seeds authoringSkill: "reverse-any-video" if unset). On a resume it sends the saved id back as session_id.
  • It never blocks. Not signed in → no call. Network error, 404 (server not deployed yet), 5xx, or a malformed answer → no session, probe still printed, exit 0. When the reverse route cannot answer, reverse.plan falls back to the existing subscription read (readPlan), or null if unknown. A server answer of plan: "unknown" is a valid session: saved, recorded: true, printed as plan: null, never blocks, no fallback read.
  • Purchase credit: on Free, reverse.upgrade_url is printed: the server's upgrade_url, else the plans page with feature=reverse&rs=<session> (the fields EF checkout reads). The skill (references/start.md) passes that exact link on; billing subscribe is only the fallback for a CLI without reverse start.
  • cloud render sends reverse_session_id only when the project's hyperframes.json holds one. Only a successful /reverse/start writes it, so a server that 404s the route never receives the unknown field (the render DTO is extra: forbid → it would 422).
  • reverse-any-video skill: the auth status Pro check and the measure probe step are replaced by reverse start; the soft Pro check keeps its meaning ("go on only when signed in on a paid plan, not Free"). The min-CLI note + fallback for older CLIs (auth status + measure probe) is in SKILL.md, with the field details in references/start.md.

Why

The reverse funnel (start, split Free/Pro → rendered → paid with feature=reverse → started again after paying) is built only from server events. This PR gives the server a start event per run and ties the cloud render to it. Whether Free users are blocked is decided by the server (gate: "record" | "block"); this PR only reads gate and prints it, the stacked PR acts on it.

Related work

  • Depends on the EF PR that adds POST /v1/hyperframes/reverse/start and the optional reverse_session_id on POST /v3/hyperframes/renders. Deploy EF first. Before that, this CLI keeps working: the route 404s, no session is saved, and cloud render sends nothing new.
  • Pairs with fix(cli): no auto-update across 0.x minors; install skills at the CLI's release tag #7013 (skills installed at the CLI's release tag): the min-CLI note says 'a CLI without reverse start'; no version number until the release that contains it is cut.
  • Stacked: the block PR is based on this branch.

How

  • packages/cli/src/heygen/reverse.ts: startReverseSession() (api2 base, same as plan.ts; 10 s timeout; never throws). surface is "desktop" when HYPERFRAMES_CLIENT is set (the CLI was launched by Desktop), else "cli".
  • packages/cli/src/commands/reverse.ts + commands/reverse/start.ts: the command. Probes first, so a bad path fails like measure probe before any server call. Without --dir, the session is saved only when the current folder already is a project (no stray hyperframes.json in a random folder).
  • packages/cli/src/commands/measure/probe.ts: the probe body is extracted as probeReport() and shared; measure probe output is unchanged.
  • packages/cli/src/utils/projectConfig.ts: reverseSessionId field, accepted only when it matches the render endpoint's pattern from EF #63633 (^rvs_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$); a saved value that does not match is ignored, so cloud render never sends it (no 422) and reverse start opens a fresh session, recordProjectReverseSession() (creates the folder + default config when absent, else patches in place keeping unknown keys); the shared in-place write is extracted as rewriteConfig(). Schema docs/schema/hyperframes.json gets the key.
  • packages/cli/src/commands/init.ts: a folder holding only hyperframes.json counts as empty, so reverse start --dir x then init x works and keeps the session (any other file still refuses).
  • packages/cli/src/commands/cloud/render.ts: reverse_session_id added to the body from the local project dir only (not for --asset-id / --url). The generated _gen/types.ts is not hand-edited; the body type is widened locally until the EF client is regenerated.
  • Docs: docs/packages/cli.mdx ("Find your command" + a reverse section), root help.

Test plan

  • Unit tests added/updated (targeted only): heygen/reverse.test.ts (success, resume id, not signed in → no call, 404 + plan fallback, network error, malformed answers, 500, surface), commands/reverse/start.test.ts (saves to new --dir, patches existing config + resumes, no write outside a project, never stops when signed out / server unavailable, bad video fails before the server call), commands/cloud/render.reverseSession.test.ts (field sent only with a saved session; not for a missing/malformed id or --asset-id), utils/projectConfig.test.ts, projectConfigSchema.test.ts, init.test.ts (scaffolds into a folder holding only hyperframes.json and keeps it; still refuses a folder with other files).
  • Manual testing performed — not against a real server: the EF route is not deployed yet.
  • Documentation updated
  • Comments follow CONTRIBUTING.md "Comments"

Ready for review — please don't add to the merge queue.

— wanbi

🤖 Generated with Claude Code

…nder carries its session

`npx hyperframes reverse start <video> [--dir <project>]` probes the video (the same JSON
as `measure probe`), then, when signed in, calls POST /v1/hyperframes/reverse/start and saves
the returned session id in the project's hyperframes.json. It never stops the run: signed
out, offline, a 404 from a server without the route, or a malformed answer all continue
with the probe and no session. `cloud render` sends `reverse_session_id` only when the
project holds one, which only a successful /reverse/start writes.

`init` now treats a folder holding only hyperframes.json as empty, so `reverse start --dir x`
followed by `init x` works and keeps the session. The reverse-any-video skill opens with
`reverse start` instead of `auth status` + `measure probe`, with a fallback for older CLIs.

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, 3:10 PM

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

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 104 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 (1)

  • seqplainkeys-none-px-r0-root-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

@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) · 0 blocker · 2 important · 1 nit · 049331b

Never-blocks holds: every failure path returns unrecorded and the probe still prints. Two contract gaps with experiment-framework#63633 ↓.

Scope

Read the full diff. Checked against EF #63633 @904163b (reverse_session.py, render DTO). Ran a fetch-stubbed probe of startReverseSession for a plan: "unknown" answer. CI green at this head.

Comment thread packages/cli/src/heygen/reverse.ts Resolved
Comment thread packages/cli/src/utils/projectConfig.ts Resolved
…buted upgrade link, takes only rvs ids

- A `plan: "unknown"` answer is a valid session: saved, `recorded: true`, printed as
  `plan: null`; it never blocks and never falls back to the client-side plan read.
- On Free, `reverse.upgrade_url` is printed: the server's link, else the plans page with
  `feature=reverse&rs=<session>` (the fields checkout reads). The skill passes that exact
  link on instead of `billing subscribe`.
- Session ids must match the render endpoint's pattern (`rvs_<lowercase uuid>`). A saved
  value that does not is ignored: not sent on `cloud render`, and `reverse start` opens a
  fresh session.

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

@WaterrrForever WaterrrForever 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.

Approving. The code does what it says, and Genesis's findings are fixed at this head:

  • an unknown plan is kept as a session (printed as plan: null), so a re-run resumes it;
  • Free prints upgrade_url with feature=reverse&rs=<session>;
  • only rvs_<uuid> ids are saved or sent, so a stale or hand-edited id never reaches the render.

Checked locally: the 7 touched test files pass (132 tests). oxlint and the skill budgets are clean. Against today's server, which doesn't have the route yet, reverse start falls back to the subscription read: it prints plan: "pro", recorded: false, saves nothing, and doesn't block.

One line to fix before merge: the version. 0.9.4 went out at 15:12Z today without reverse start, so "CLI 0.9.4+" and "older than 0.9.4" are wrong. Skills go live from main before the CLI ships, so I'd drop the number rather than guess the next one. In SKILL.md: "(a CLI without it: auth status, measure probe)". In references/start.md: "A CLI that does not know reverse start yet: …". The same line is in #7017.

0.9.4 shipped without `reverse start` and skills go live from main before the CLI release,
so the fallback is described as "a CLI without it" instead of a version number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Vitozhu04
Vitozhu04 marked this pull request as ready for review November 2, 2026 15:36

@WaterrrForever WaterrrForever 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.

Re-stamping at a302d70. The only change since my approval is the version line, which now names no CLI version. Both heads are clear of 0.9.4.

@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 · 0 blocker · 0 important · 1 nit · 049331b → a302d70

All three earlier findings are fixed. unknown keeps the session and prints plan: null. upgrade_url is printed and the skill passes it on, so purchases now carry feature=reverse&rs=. The id format matches EF's rvs_<uuid> (schema too). CI is green. A deploy note: this is mergeable now, so the EF-first order depends only on the merger.


/** The link that credits a purchase to this run: the server's, else the plans page with the same fields. */
export function reverseUpgradeUrl(session?: { sessionId: string; upgradeUrl?: string }): string {
return session?.upgradeUrl ?? pageUrl("plans", { feature: "reverse", rs: session?.sessionId });

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.

nit — EF answers upgrade_url: null on purpose while HF purchases are closed to the caller (hyperframes_pricing_url → None). parseSession drops the null and this falls back to the CLI's own plans link, so the person gets a buy link the server withheld. With #7017 under block, that becomes a refusal that points at a page where they can't buy. Suggest keeping null from a recorded session, and using the fallback only when unrecorded.

@Vitozhu04
Vitozhu04 added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 7d3c225 Nov 2, 2026
133 checks passed
@Vitozhu04
Vitozhu04 deleted the wanbi/cli-reverse-start-record branch November 2, 2026 17:04
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