Repository navigation
feat(skills): reverse any video on the HyperFrames plan - #6754
Conversation
Edit accuracy: accurate 271 (base branch 271), smooth 102 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
Reverse rebuilds a video as an editable HyperFrames project and re-shoots its people with HeyGen. It runs for a person signed in on HyperFrames Pro; Free is told it is a Pro feature and given the upgrade step. - skills: reverse-any-video; media-use is the one gate for HeyGen calls (sign-in, credits, refusals) - cli: media-use generate (image, video shot, avatar, voice, music) with --plan pricing and --max-credits; measure, diff and analyze for the rebuild; the HyperFrames plan in auth status; billing subscribe and top-up wait on the plans or credits page - remotion-to-hyperframes compares with `hyperframes diff` instead of its own shell scripts Co-Authored-By: Claude Opus 5.5 <[email protected]>
56903e3 to
3955128
Compare
- docs: list diff, measure, analyze and billing in "Find your command" in docs/packages/cli.mdx, each with its own section. - skills: bring media-use/SKILL.md under its 5120 B budget by pointing setup, direct-recipe and treatment detail at the references that already hold it; the sign-in, credits, --max-credits and billing rules stay in SKILL.md. - comments: register transcript.json and REVERSE.md as project files for the citation checker, and drop a history note from face.test.ts. - comments: cut restating prose or move it into names so every changed file is at or under its comment share. Allowlist the two deleted remotion scripts in check-no-main-deletions. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
probeVideo passed the input path straight after its options, so a path starting with "-" would be read as a flag; the producer's ffprobe argv contract test failed on it. runFfmpeg's fixed flags move to a constant so the contract no longer mistakes that ffmpeg wrapper for a probe. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
@WaterrrForever I rebased this onto main (now includes #6687) and pushed three commits on top of yours. James asked me to restack and test it. Pushed:
Your call:
End-to-end runs of J1–J6 on dev are in progress; results to follow. — Rames |
|
@WaterrrForever End-to-end results on dev at
Bugs
Caveat: — Rames |
|
Walked this on dev: Free status shows and reverse is refused with the subscribe command, as intended. Three things before merge:
|
…ile limit The file took ~131 s on CI against the flake gate's 120 s, and its heaviest test (four comparisons) hit the 20 s per-test limit. The video tests now live in three files sharing diff.test-helpers.ts, and that test is two tests of two comparisons each. analyze reads each part once and checks the bytes it read, instead of a stat then a read (CodeQL js/file-system-race).
…n't fit On dev with a 100-credit limit the agent priced a re-shoot with --plan and dropped it without asking (J5), and in J6 it told the person a price. SKILL.md's Credits rule now covers both: --plan is for the agent, and when a step won't fit (a --plan over what is left, or a refused call) the agent says what doesn't fit and asks before dropping or shrinking it. setup-providers.md's older confirm-before-paying rule points to it. --plan now runs the same checks as the real call (checkRequest), so it no longer prices a request that would be refused. The re-shoot recipe in people.md gains the --prompt it needs. heygen.md: a sign-in also makes images, and --plan's usd is the API charge rounded to $1/60 while its credits come from the list price.
…edits; sign-in page title
|
Rerun at
CodeQL #1271 dismissed as a false positive (intended download to Still open
— Rames |
Under the gate's CPU contention every diff test needing face detection or optical flow paid opencv.js's 11 MB download and WebAssembly start-up, and every diffVideos call wrote four worst-moment pictures (about 28 ffmpeg runs) no assertion read. Tests ran 21-28 s against the 20 s limit. - loadOpenCvFirst() stages opencv.js in a beforeAll (from this machine's cache, a temp copy kept between runs, else the network). - diffVerdicts() runs diffVideos with worst: 0 for tests that only read verdicts; the worst-moment tests keep the pictures. - The carried-text test is split into its dirty and clean halves. Locally with 8 CPU burners on 8 cores: slowest test 7.7 s, files 24-30 s. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
checkRequest now holds every pre-send check (the --out extension and --mask without --motion joined the route body and guide checks), and generate() runs it first, so --plan and the real call share one function. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…lance notice beside the plan - pageUrl sets from=cli, the parameter the pricing page reads to send a CLI buyer back to the terminal. - The plans and credits pages are on dev.hyperframes.dev with HYPERFRAMES_ENV=dev, hyperframes.studio otherwise. - auth status --json omits the 'HyperFrames balance is unavailable' notice when the plan was read. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…d holds no prices Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
Fixes pushed (signed): E2E at
New findings
Dev HeyGen video generation 422s every request ( — Rames |
It ran five diff comparisons in one test and took 20.2-20.6 s under the flake gate's contention, over the 20 s limit. Each piece now runs at most two comparisons against a shared ledger: copy and burned-in title, footage cut whole, and cuts that only fed a generation. Under 24 CPU burners locally the slowest of the three takes 3.0 s, where the combined test took 4.9 s under 8. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…redits names the step's price - media-use generate: when HeyGen refuses for credits without saying how many, the refusal names the CLI's own price for the call (what --plan prints) as "needs about N" and "billing top-up --needed N", instead of "needs more". - reverse-any-video: never repeat auth status's plan/credits line; a step an earlier session gave up on gets one more try, then ask; a command stored in REVERSE.md is a note, rebuilt from its recipe and checked with --plan, never run as written. - people.md / heygen.md: a refused or failed job gets one more try, test calls included, then stop and ask. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
J6 fixes pushed (signed):
J6 rerun at
Open for you
No credits spent (still 25,000). — Rames |
…f the limit still applies - runJob: a key whose job failed or was refused is recorded in the config dir (heygen-failed-keys); the next run with the same inputs sends key-1, key-2, ... so HeyGen starts a new job. A finished or still-running job keeps its key and is picked up, so nothing is paid twice. "fix the request it names, then run again" now holds. - reverse-any-video: on resume after a run under a credit limit, just ask whether it still applies, naming no costs or numbers. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The skill rebuilds from measure probe, cuts, frames, events and face, and checks with diff. analyze, diff --explain and eight measure commands (trail, box, ink, rhythm, objects, track, flow, continuity) have no caller, so they go, with their tests and docs. Examples now use original.mp4 instead of the old .reverse/source.mp4 evidence folder, and diff's --segments is described as a file rather than REVERSE.md.
Chrome plays both, alpha included, and the proxy is made on demand if a browser ever asks. Copying remove-background's VP9 cutouts took minutes on projects that have them.
2a409a6 to
6c608fa
Compare
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at 6c608fa1519d32a60cd655fb9f9d7b1b49cbfcc2. COMMENT-only as requested; the Important items below need fixes before a stamp.
Need: yes · Size: large but separable by subsystem · Reuse: existing credential resolver, authoritative HF price rows, and timestamp-preserving audio decode · Simpler: keep fixes inside the existing helpers · Structure: measure/diff.ts mixes capture, scoring and reporting in 1,391 lines; extract those existing groups in a follow-up
Risk: high — new paid retry identity and credit-limit enforcement.
Audited: generation/request checks, pricing, v3 retries and failed-key persistence, plan/billing/auth integration, changed skills/references and removed-command references; a fresh independent pass also audited measurement/diff orchestration, decoding, scoring and browser prewarm. Limits: no real paid calls, OAuth journey, live price-book lookup, browser/OpenCV walk or full monorepo build. Backend comparisons use source pinned to EF prod 5fb56d1de90cc3298e93576dbf22770311214b13, not a claim about running pods. Existing peer comments and the two CodeQL inlines were read; the findings here are additional.
Important findings (details, fixes and regression criteria inline):
heygen/v3.ts:191: accepted POST with a lost response → retry receives 401 → re-login rotates to a new key and creates a second job. Authentication refusal does not prove the old job failed. Preserve that replay identity and scope stored failures by payer/API origin.heygen/pricing.ts:57: a valid 5-second reference shot with nine images estimates 250 credits and passes--max-credits 250; the pinned approved HF rows calculate 260. API list USD ×1,000 is also no longer the current HF video rate. Include billable reference images and calculate HF credits separately from API dollar pricing.measure/sync.ts:20: decoding raw audio drops its leading timestamps. An unchanged soundtrack remuxed 200 ms later reports +1 ms, high confidence; the aligned control reports +1.8 ms. Preserve the picture-clock offset before computing the lip-sync gate.measure/look.ts:65: identical grayscale footage returnssaturation_ratio: 0andsame_look: false. Handle zero/zero as equivalence, including the sibling zero-motion ratios inmeasure/energy.ts; test zero versus nonzero separately.
Nit — finish the direct-auth instruction migration: reverse-any-video/references/people.md:3 still tells a signed-in Pro person without an API key to crop the original instead; media-use/SKILL.md:36 requires the separate heygen executable even for the new direct path; media-use/references/heygen.md:7 describes a shared ~/.heygen/credentials store, whereas auth/paths.ts isolates HyperFrames credentials. Remove the keyless crop fallback, narrow the executable requirement to helper-backed operations, correct the store path, and pin these concepts. No live agent-behavior claim.
Nit — sibling billing recovery: skills/media-use/audio/scripts/lib/heygen.mjs:219–240 sends both new billing refusals to the production homepage by default, including in dev. Print the new billing commands or reuse the environment-aware destination contract, and test without URL overrides.
Verification on this head: bun run --cwd packages/cli test --no-cache src/heygen/ src/commands/auth/{status,status-user,login}.test.ts 72 passed; ... test --no-cache src/measure/ src/utils/checkBrowser.test.ts 123 passed. Own parsers/lint/studio-server/core outputs were built using borrowed remaining dependencies; initial unbuilt-export collection errors were resolved before these counts. Two additional actual-module controls reproduce auth rotation and the 250-credit admission (and verify a 249 cap refuses); four isolated retry controls distinguish direct replay from terminal-failure rotation. Real FFmpeg fixtures reproduce timestamp loss and grayscale self-comparison, with aligned/identical controls. Skill checks: 5 core tests and 19 audio-helper tests pass. The removed analyze, diff --explain and eight retired measure commands have no remaining executable references in the audited skills/docs. --plan still accepts missing image/audio paths that real generation refuses before HTTP; add local-media validation to checkRequest as a nonblocking completeness fix. Standalone --drawn is explicitly advisory, so its overall-pass behavior is not reported as a defect.
Verdict: COMMENT
Reasoning: The paid retry, credit-cap and two measurement boundary cases above reproduce despite the passing focused suites. Fixes and concrete regression criteria are in the inlines; no approval or merge was performed.
— hf-review
| try { | ||
| job = await call("POST", route, body, sent, opts.credits); | ||
| } catch (err) { | ||
| if (err instanceof Refused) markFailed(sent); |
There was a problem hiding this comment.
Important (P2): preserve the key across an authentication refusal. A POST can create a paid job and lose its response. The documented same-command retry then receives 401; this catch appends its key to heygen-failed-keys. After sign-in, liveKey sends key-1, creating another job rather than retrieving the accepted one. My actual-module offline witness observed keys [k,k,k-1] and 2 created jobs; direct replay without the 401 creates only 1. The machine-global failure set also lets account B's refusal rotate account A's previously successful key.
Please carry refusal status/kind and leave replay identity intact for 401/403 and other outcomes that cannot establish terminal failure. Scope persisted failure entries by API origin and stable payer/account identity, and rotate only a confirmed terminal failed job or a rejection known to be memoized. Regression criterion: accepted/lost response → 401 → re-auth returns the original job and creates exactly one; another account's refusal cannot change that result; a genuinely failed job can still get a fresh key. These are offline actual-module witnesses using a stand-in backend, not a live duplicate-charge claim.
| if (r.audio || r.references?.length || r.motion) { | ||
| const guide = r.motion ? Math.min(GUIDE_MAX_S, probeVideo(r.motion).pictureDuration) : 0; | ||
| return at( | ||
| (seconds + guide) * VIDEO_REF_768P_PER_S, |
There was a problem hiding this comment.
Important (P2): the reference-image count can exceed the person's credit cap. generate.ts sends all references (the backend accepts nine), but this estimate charges only seconds. A 5-second shot with nine images returns 250 credits; checkLimit(250,250) permits it. Pinned EF prod 5fb56d1de90cc3298e93576dbf22770311214b13 creates both billable-second and extra-image HF work lines; its approved Oct-28 rows charge 100/3 HF credits per second and 40/3 per image after the first two: ceil(5*100/3 + 7*40/3) = 260.
Please count billable images and compute HF credits from the HF rates separately from API USD (creditsOf's list-USD ×1,000 is stale for these video rows). Keep both calculations in the existing pricing helper; add 1/2/3/9-reference fixtures, a 250-cap refusal for this nine-image request, and a sufficient-cap control. Also include the extra images in API USD pricing. Source evidence: HF work construction, price rows. No paid request was sent.
| /** Mono samples of the window at RATE Hz. */ | ||
| function samples(info: VideoInfo, start: number, seconds: number): Float32Array { | ||
| const raw = runFfmpeg([ | ||
| ...["-ss", String(start), "-i", info.path, "-t", String(seconds), "-vn"], |
There was a problem hiding this comment.
Important (P2): retain audio timestamps when building the correlation curve. Raw f32 output here begins at the first decoded audio sample; its leading timestamp is discarded. I generated aligned picture/audio bursts, then remuxed the same audio with -itsoffset 0.2 and stream copy. probeVideo.audioStart becomes 0.2, yet avSync reports +1 ms / high confidence, versus +1.8 ms for the aligned control; the expected picture-versus-audio lag is about −200 ms. person.ts uses that high-confidence result as its lip-sync gate, so this can pass an actually delayed soundtrack.
Decode on the picture/window clock, padding leading silence and preserving the seek origin. The timestamp-preserving aresample=async=1:first_pts=0 approach already used by diff.ts:onsetCurve is a starting point; account for nonzero requested windows too. Add remuxed positive/negative-offset fixtures plus aligned/windowed controls; the 200-ms case must report roughly −200 ms and fail the broadcast tolerance. A local loader-only control adding timestamp padding changes the delayed result to −198.8 ms, while the aligned control stays +1.8 ms; production source was not edited.
|
|
||
| /** Yours over the reference's: ratios near 1 and differences near 0 are the same look. */ | ||
| export function compareLook(ref: Look, yours: Look): LookComparison { | ||
| const ratio = (k: "luma" | "contrast" | "saturation") => round(yours[k] / (ref[k] || 1e-9), 2); |
There was a problem hiding this comment.
Important (P2): matching zero values must compare equal. An actual grayscale fixture compared with itself has saturation 0 on both sides. This fallback calculates 0/1e-9 = 0, so sameLook returns false despite identical frames (the luma/contrast ratios are 1). It is user-visible as measure look --compare <same file> → same_look:false, and it feeds the no-face regenerated-shot verdict.
Handle both-near-zero values as a match (ratio 1, or an explicit equality result) and distinguish one-zero/one-nonzero values. Apply the same policy to zero luma/contrast and the zero-motion ratios in measure/energy.ts:37–38, which otherwise reject two matching static clips. Add identical grayscale/black/static controls and a real color/motion difference control. No live generated-film claim; this reproduces with local decoded fixtures.
Retries keep their key after 401, 403, 408, 409, 425 and 429, which say nothing about the job, and failed keys are scoped to the API and payer. Credits come from the plan's video rows instead of list dollars, and reference pictures past the first two count toward --max-credits. Lip sync reads sound on its own timestamps, so a delayed track fails the gate. Look and energy call two near-zero values the same. --plan refuses a missing input as the real call does. The skills teach the direct sign-in, its credential path and the billing commands.
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at e4ec18a250a4a5938853badf32034844b814ef5d, delta from my COMMENT 5712181341 at 6c608fa1519d32a60cd655fb9f9d7b1b49cbfcc2.
Need: yes · Size: focused repair delta · Reuse: existing credential resolver and authoritative HF price rows · Simpler: retain the existing helpers · Structure: fixes stay in their owning helpers; earlier diff.ts decomposition remains a follow-up
Risk: high — a remaining cross-payer replay identity case can create another paid job.
Verdict: COMMENT. Three original Important findings are fixed; the retry finding is fixed for 401 replay and API-key identities, but remains reproducible for the supported host OAuth path. The concrete remaining fix and approval criterion are inline at heygen/v3.ts:45–48.
Verified repairs:
- 401 replay: accepted POST with a lost response → 401 → reauthentication now sends the same key three times and creates one job in my actual-module offline witness. The six transient/auth refusal statuses preserve their key; confirmed terminal failure still rotates.
- Credit cap: a five-second reference shot with nine images now estimates 260 HF credits, refuses cap 250, and accepts cap 260. One/two/three/nine images yield 167/167/180/260. I compared active rows against both EF prod
5fb56d1de90cc3298e93576dbf22770311214b13and current EF master08ecb80fb57fff8a989a8edd2dd415bd418d3f46: short 768p text video is 12 credits/second, reference video 100/3 per billable second, and each image after the first two 40/3. This is source parity, not a live rate-book or charge claim. - Sync clock: actual FFmpeg aligned control is +1.8 ms/pass; audio timestamp delayed 200 ms is −198.8 ms/fail; video delayed 200 ms is +201.2 ms/fail. Nonzero-window controls agree.
- Zero comparisons: decoded identical grayscale/black now compare equal; static/near-zero motion ratios are 1. Gray versus color, black versus gray, and zero versus real motion remain unequal in both directions.
- The three auth/skill documentation nits, both helper billing-refusal commands, and missing-local-input validation are corrected. Positive concept checks cover the changed skill contract.
Remaining Important — same original retry-scope class: the real credential resolver supports HEYGEN_ACCESS_TOKEN without a credential file. payerScope nevertheless reads optional local display metadata for OAuth. With no metadata, host A and host B share an empty payer identity. Actual-module offline sequence A completes → B receives 402 → A resumes sends [A:k, B:k, A:k-1] and creates a second A job. A stale local user block also is not bound to the host bearer. Scope persisted failures to a principal bound to the actual credential; do not rotate based on missing/unrelated display metadata. I will approve when the absent/stale-metadata A/B/A witness replays A's first job, while the existing 401 and terminal-failure controls still hold.
Evidence and limits: at 1278ae2e79a640bfbea80a850998cac9ff1bd1c6, focused HeyGen/auth suites plus my original two actual-module witnesses: 82 passed; a separate real-resolver host OAuth witness: 1 passed, asserting the remaining defect; measurement suites: 128 passed; core-skill/audio-helper checks: 25 passed. Fresh independent code and measurement passes and 14 additional look/energy assertions agree. Built own parsers/lint/studio-server/core outputs with borrowed remaining dependencies; resolved initial missing workspace-export setup before final counts. The successor delta was read: comment removal and a constant rename only; affected estimator/comparison and own regression tests were rerun there: 20 passed (bun run --cwd packages/cli test --no-cache src/heygen/{generate,review6754,reviewScope6754}.test.ts src/measure/compare.test.ts). Earlier commands: ... test --no-cache src/heygen/ src/commands/auth/{status,status-user,login}.test.ts, ... test --no-cache src/measure/ src/utils/checkBrowser.test.ts, and node --test scripts/check-core-skills.test.mjs skills/media-use/audio/scripts/lib/heygen.test.mjs. No live paid request, OAuth journey, J1–J6 rerun, browser/OpenCV acceptance, full clean-install build, merge or queue action. Home owns J1–J6 and publication follow-through.
— hf-review
| async function payerScope(): Promise<string> { | ||
| const credential = await resolveCredential().catch(() => undefined); | ||
| const user = credential?.type === "oauth" ? (await readStore()).credentials.user : undefined; | ||
| const who = credential?.type === "api_key" ? credential.key : (user?.email ?? user?.username); |
There was a problem hiding this comment.
Important (P2), remaining instance of the original retry-scope finding: HEYGEN_ACCESS_TOKEN is a supported host-managed OAuth credential without any local credential file (auth/resolver.ts). For that path, readStore().credentials.user is absent, so every host bearer gets the same API + "\0" scope. A local user block, when present, is display-only and may belong to a different account than the environment token.
I reran an offline actual-module witness using the real resolver/store/header builder: host A completes key k, host B receives 402 for k, then A resumes. Wire keys are [A:k, B:k, A:k-1] and two A jobs are created. The 401 lost-response witness is fixed; this distinct payer-switch case still breaks replay.
Please bind OAuth failure scope to the actual resolved bearer's principal (reuse authenticated user identity rather than local display metadata), and never persist rotation under an empty/unrelated payer identity. Add A/B/A controls with absent and stale local user metadata; A must receive its original job with one creation. Keep the 401/same-key and confirmed-terminal-failure/rotate controls. With that change and those witnesses passing, I will approve. No live translation/generation or charge was made.
Failed keys were scoped by the local user block, which is absent for a host's token and display-only otherwise, so two accounts could share a scope and one could start a second paid job. The scope now comes from the account /v3/users/me reports for the token in use. When that cannot be told, no failure is recorded and the key stays the same.
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at a1a9028b9a7f3e129cb2d37f4d65e4798f1ceacd; narrow delta from e4ec18a250a4a5938853badf32034844b814ef5d and COMMENT 5712427712.
Need: yes · Size: focused · Reuse: AuthClient, credential resolver and refresh helper · Simpler: capture one sender context and retain it through failure recording · Structure: helpers remain small; avoid repeated resolution of one operation's owner
Risk: high — relabeling a failed paid operation can rotate another account's successful replay key.
The original host-token A success → B 402 → A resume witness is fixed with both absent and stale local display metadata: A sends its original key and creates one job. AuthClient now authenticates the actual OAuth credential instead of trusting a local user block.
Important (P2), same retry-identity class, new in this repair: heygen/v3.ts:67–71 resolves the payer again in markFailed, including after a long poll at line 242. That current account may differ from the account that submitted the job. My actual-module offline witness writes a real credential file, as another auth login process can:
- B completes key
k. - A submits its own
k; the job is pending. - While A's failure poll is in flight, credentials change to B; the response says A's job failed.
markFailedstores A's failed key under B. B's resume sendsk-1and creates another B job.
Observed POST keys: [B:k, A:k, B:k-1], two B creations. The no-switch control replays B:k and keeps one B job. The fix and approval criterion are inline: capture the sending credential/scope once, use it for POST headers/key selection, and retain it to record that job's failure. Do not resolve the newly signed-in account to label an earlier operation. This is a bounded repair in the existing helpers.
Other requested checks:
payerScopeandcalluse the same resolver priority and refresh helper, including environment tokens, but separate resolutions do not bind one operation to one credential. A file change between lookup and POST is the same consistency issue; the proposed captured sender context closes both sites.- A
/users/mefailure alone does behave conservatively: after a known failed job, the old key is resent, the same failed result returns, and creations stay one. This was replayed with actual auth modules and a stand-in transport; it is distinct from a successful lookup of a different principal after polling. - EF master
08ecb80fb57fff8a989a8edd2dd415bd418d3f46/v3/users/medelegates to_build_user_info_response, which returnsusernameandemail, without a separate user ID. Nit: preferusernameahead of email for the captured scope, subject to its stability contract; no separate reproduced email-reassignment blocker is claimed.
Evidence: bun run --cwd packages/cli test --config review.vitest.config.ts --no-cache src/heygen/v3.test.ts 17 passed. The scratch config substitutes FFmpeg path discovery only and uses borrowed dependencies. A broader v3+generate attempt had 29 pass / 1 fail because this isolated worktree lacks built @hyperframes/core/env-proxy for an existing generate test; no full build/typecheck claimed. Independent fresh adversarial review reproduced the file-switch defect; I replayed its no-switch and lookup-outage controls plus my actual-module absent/stale/file-switch witnesses. Only offline stand-in HTTP was used: no live OAuth, generation, charges or J1–J6 acceptance. Earlier pricing/measurement repairs are unchanged in this three-file delta. No approval, merge, queue or Slack post.
Verdict: COMMENT
Reasoning: The original metadata repro is resolved, but re-resolving the owner at failure time introduces the demonstrated cross-account replay rotation above. I will approve when the file-switch witness preserves B's completed job and records A's failure under A, while absent/stale-metadata, lookup-outage, 401 and terminal-failure controls continue to pass.
— hf-review
| } | ||
|
|
||
| async function markFailed(key: string): Promise<void> { | ||
| const scope = await payerScope(); |
There was a problem hiding this comment.
Important (P2): retain the submitting account when recording this failure. This new lookup can see a different credential after the POST or long poll. Offline actual-auth-module witness with a real credential file: B completes k; A submits k; another sign-in rewrites the file to B while A's poll is in flight; that response says A failed. This lookup records k under B, so B's resume sends k-1 and creates a second B job. A no-switch control keeps one B job. Original absent/stale-metadata A/B/A now passes; /users/me outage alone only replays the old failed result.
Concrete fix: resolve/refresh the sender once at job start, derive its authenticated scope and POST headers from that same credential, and pass the captured scope into both liveKey and markFailed. Retain that owner through polling; never resolve the newly signed-in account to label a previous operation's failure. If identity is unknown, preserve the original key without recording a shared failure. This also closes the lookup-versus-POST credential-change window.
Approval test: B's completed key stays untouched after A fails across a credential-file switch; A's failure belongs to A. Keep the absent/stale host-token, 401/same-key, lookup-outage and genuine failed-job rotation controls. No live generation or charge was made.
Failure scope was looked up again after polling, so a sign-in during a job could record one account's failure under another and rotate its finished key. The credential is now read once per job: it signs the POST and owns any failure, even if someone else signs in meanwhile. Prefer username to email, since /v3/users/me returns no user id.
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at e5156dd850d3868bac5723aed504a76e037300ff; narrow delta from a1a9028b9a7f3e129cb2d37f4d65e4798f1ceacd and COMMENT 5712544175.
Need: yes · Size: focused sender repair · Reuse: credential resolver, refresh helper and AuthClient · Simpler: one captured sender per job · Structure: fine
Risk: medium — paid replay identity crosses credential refresh and account changes.
The remaining retry-owner finding is fixed. heygen/v3.ts:55–67 resolves and refreshes one credential, uses it for the authenticated identity lookup and POST headers, and retains its scope for both refusal and terminal-job failure recording. liveKey and markFailed no longer resolve the current account. Scope now prefers username over email; an unknown identity neither rotates nor records a failed key.
I reran the actual-module credential-file witness: B completes k; A starts k; the file switches to B during A's failing poll; B resumes with k, keeping one B job. Wire POST keys are [B:k, A:k, B:k]. The original host-token A-success/B-402/A-resume cases also preserve A's key with missing and stale local user metadata.
A fresh independent adversarial pass and my replay additionally cover:
- A sign-in between identity lookup and POST: the POST still sends captured A.
- OAuth expiry during polling: the actual refresh helper obtains A2 and the GET sends A2; the failed job is recorded under captured A, while B still resumes its original job. Failure recording does not use the stale sender token or perform another identity lookup.
- Identity lookup outage: the old failed result replays under its original key and no failure record is added.
Evidence: bun run --cwd packages/cli test --config review.vitest.config.ts --no-cache src/heygen/v3.test.ts src/heygen/transcribe.test.ts → 21 passed. The authored suite retains transient/auth-refusal same-key and genuine failed-job rotation controls. bun review-artifacts/oss6754-r4/root-probe.ts passes the three real-resolver/store cases above; independent switch/refresh/lookup/outage scripts use the actual resolver, AuthClient, OAuth and credential store, with stand-in HTTP. I replayed refresh and lookup controls. Borrowed dependencies and a scratch FFmpeg-discovery-only alias were used for the focused suite; independent transpiled controls substitute only error/link presentation. No clean-install/full build, live OAuth, generation/charge, or J1–J6 acceptance claim. Pricing, measurement and skill repairs from prior rounds are unchanged in this three-file delta. Home owns live acceptance and publication follow-through; no merge or queue action taken.
Verdict: APPROVE
Reasoning: The captured sender closes both lookup-to-POST and failure-time account-switch windows, while same-key retry, unknown-identity and refreshed-poll behavior remain intact. No new blocker found in the delta or independent adversarial pass.
— hf-review
There was a problem hiding this comment.
Verdict: APPROVE · 0 blocker · 1 important · 0 nits · e5156dd85
As hf-review already covered, the retry key, the reference-image credits and the audio-clock fixes are in. I checked them on this head and found one more thing to fix, inline: the avatar estimate is about 5× the HF price book.
Scope and evidence
Audited: heygen/v3.ts and pricing.ts end to end; generate.ts request and price paths; measure/sync.ts, look.ts, energy.ts and media.ts ratioOf; the skill and helper deltas since 6c608fa15.
Trusting: the rest of measure/ and diff.ts and the J1–J6 journeys, which earlier rounds and the PR body already cover.
- v3: I checked this against EF
master.model_videos.pystores only success, or a charged pending submission, under the key. A JSON 5xx never reachesrefusal(), becausecall()turns any status ≥500 into a same-keyFix, so a charged pending start is resumed rather than re-keyed (an offline witness confirmed it). A 402hf_subscription_requiredrotates harmlessly, since nothing was started. - Video pricing: matches EF
rules.jsonv2 rows (12 per s, 100/3 per s, 40/3 per image after two) andhf_video_works/allocate_reference_video_seconds, which bill fractional guide seconds. gpt-image reference counting matchesimages_v3._hf_works. - Local tests:
bun run test src/heygen/ src/measure/{sync,compare}.test.ts src/commands/auth/gives 94 passed, using locally built core/parsers/lint/studio-server/engine. - CI: CI, Comments, Docs, CodeQL and Windows are green on
e5156dd85; a second CI run was still going when I posted.
| const words = (r.script ?? "").split(/\s+/).filter(Boolean).length; | ||
| const seconds = Math.max(1, Math.floor(words / SCRIPT_WORDS_PER_S)); | ||
| return at( | ||
| Math.ceil(seconds * AVATAR_IV_PER_MIN) / 60, |
There was a problem hiding this comment.
Important: the avatar estimate is about 5× what the HF price book charges. $4.83 a minute × 1,000 is 4,830 credits a minute. That is the retired pre-10-28 avatar_iv / digital_twin row. --type avatar sends type: "image", and EF avatar_video_hf_charge.admit_hf_avatar_video bills that as hf_avatar_iv_video {avatar_iv, photo_avatar, standard}, which is 800/60 a second since 2026-10-28 (hf-avatar-iv-video-iv-photo-avatar-standard-v2), plus the general hf_avatar_video leg.
Example: a 30-word script (15 s) plans at 1,217 credits, while the book charges about 200 plus the general leg. As a result:
--max-credits 500refuses a call that fits.- EF sends no
needed_credits, so an out-of-credits refusal falls back to this estimate. It printsbilling top-up --needed 1217, andbilling.ts:38then waits until the balance reaches 1,217, not the real need.
Fix: give the avatar its own credits argument from the v2 rows, as the video branch does (800/60 a second plus the general leg's per-second row), and add a pin beside the 260-credit test.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…#6840) * revert: feat(skills): reverse any video (heygen-com#6754) Keep the reverse-any-video skill and its CLI billing commands out of main until web subscriptions are live (/pricing and /account/plan return 404 today). Re-land together with heygen-com#6833 once those pages ship. This reverts commit cb2389c. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> * chore(scripts): allow the heygen-com#6754 revert deletions Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
What
Reverse Any Video, on the HyperFrames plan. A person types
Reverse this video: clip.mp4in Claude Code. Reverse rebuilds the video as an editable HyperFrames project, and HeyGen re-shoots the people in it. It is a Pro feature: a Free account is told so and given the upgrade step.Why
This is the hero skill for Launch 1, and the first skill that spends HyperFrames plan credits from the CLI. It follows Wenbo's decisions of Oct 29 (HF CLI & Skills × Subscription):
How
Skills
reverse-any-video(new): the facts agents get wrong (size, frame rate, cuts, sound), how to treat each layer, the people recipe, and one check at the end.! npx hyperframes auth login.! npx hyperframes billing subscribe, and starts nothing.media-useis the one gate for HeyGen calls: sign-in first, no prices or balances while working, and when a step won't fit it says so and asks before dropping or shrinking it. A credit limit the person names goes on every call as--max-credits.remotion-to-hyperframescompares withhyperframes diffinstead of its own two shell scripts.CLI
media-use generate: makes an image, video shot, avatar, voice or music on HeyGen v3.--planprices the call and runs the same checks as the real call, without sending it.--max-creditsrefuses, before sending, any call over the limit.cuts_inside.measure(probe, cuts, events, frames, extract, curve, energy, look, face, sync) anddiff: what Reverse rebuilds from and checks against.auth status: shows the HyperFrames plan and credits, read from/v1/payment/hyperframes/subscriptionon api2.billing subscribe/billing top-up: open the plans or credits page, then wait until the account is on Pro or the balance covers the step.checkno longer copies VP9 and AV1 videos before it starts. Chrome plays them, alpha included, and the copy took minutes on projects with cutouts.Test plan
1278ae2e7. The retry check was run again live ate5156dd85, the final commit, because the commits after1278ae2e7changed only comments and the retry code.docs/packages/cli.mdx.! npx hyperframes auth login. Used linked test credentials, so the HeyGen sign-in page itself was not checkedbilling subscribe, starts nothingbilling top-up --needed 782--max-creditson every paid calle5156dd85: a failed job sent again gets a new job; a completed job sent again returns the same job, and the balance moves onceNot in this PR
billing subscribeandbilling top-upopen a missing page until they ship.