Repository navigation
feat(cli): reverse start records a reverse run; cloud render carries its session - #7016
Conversation
…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>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Edit accuracy: accurate 271 (base branch 271), smooth 104 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
There was a problem hiding this comment.
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.
…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
left a comment
There was a problem hiding this comment.
Approving. The code does what it says, and Genesis's findings are fixed at this head:
- an
unknownplan is kept as a session (printed asplan: null), so a re-run resumes it; - Free prints
upgrade_urlwithfeature=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>
WaterrrForever
left a comment
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 }); |
There was a problem hiding this comment.
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.
What
Reverse funnel telemetry, CLI side, part 1 of 2 (records only; the block branch is the stacked PR on top of this one).
npx hyperframes reverse start <video> [--dir <project>]. It probes the video (the same JSON asmeasure probe, plus areversefield), then, when signed in, callsPOST /v1/hyperframes/reverse/startwith{surface, session_id?}and saves the returnedsession_idin the project'shyperframes.jsonasreverseSessionId(also seedsauthoringSkill: "reverse-any-video"if unset). On a resume it sends the saved id back assession_id.reverse.planfalls back to the existing subscription read (readPlan), ornullif unknown. A server answer ofplan: "unknown"is a valid session: saved,recorded: true, printed asplan: null, never blocks, no fallback read.reverse.upgrade_urlis printed: the server'supgrade_url, else the plans page withfeature=reverse&rs=<session>(the fields EF checkout reads). The skill (references/start.md) passes that exact link on;billing subscribeis only the fallback for a CLI withoutreverse start.cloud rendersendsreverse_session_idonly when the project'shyperframes.jsonholds one. Only a successful/reverse/startwrites it, so a server that 404s the route never receives the unknown field (the render DTO isextra: forbid→ it would 422).reverse-any-videoskill: theauth statusPro check and themeasure probestep are replaced byreverse 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 inSKILL.md, with the field details inreferences/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 readsgateand prints it, the stacked PR acts on it.Related work
POST /v1/hyperframes/reverse/startand the optionalreverse_session_idonPOST /v3/hyperframes/renders. Deploy EF first. Before that, this CLI keeps working: the route 404s, no session is saved, andcloud rendersends nothing new.reverse start'; no version number until the release that contains it is cut.How
packages/cli/src/heygen/reverse.ts:startReverseSession()(api2 base, same asplan.ts; 10 s timeout; never throws).surfaceis"desktop"whenHYPERFRAMES_CLIENTis 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 likemeasure probebefore any server call. Without--dir, the session is saved only when the current folder already is a project (no strayhyperframes.jsonin a random folder).packages/cli/src/commands/measure/probe.ts: the probe body is extracted asprobeReport()and shared;measure probeoutput is unchanged.packages/cli/src/utils/projectConfig.ts:reverseSessionIdfield, 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, socloud rendernever sends it (no 422) andreverse startopens 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 asrewriteConfig(). Schemadocs/schema/hyperframes.jsongets the key.packages/cli/src/commands/init.ts: a folder holding onlyhyperframes.jsoncounts as empty, soreverse start --dir xtheninit xworks and keeps the session (any other file still refuses).packages/cli/src/commands/cloud/render.ts:reverse_session_idadded to the body from the local project dir only (not for--asset-id/--url). The generated_gen/types.tsis not hand-edited; the body type is widened locally until the EF client is regenerated.docs/packages/cli.mdx("Find your command" + areversesection), root help.Test plan
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 onlyhyperframes.jsonand keeps it; still refuses a folder with other files).Ready for review — please don't add to the merge queue.
— wanbi
🤖 Generated with Claude Code