Skip to content

fix(studio-server): an edit aimed at a clip never lands on an audio group sharing its id - #5645

Merged
miguel-heygen merged 2 commits into
mainfrom
fix/edit-target-by-hf-id
Oct 15, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
fix/edit-target-by-hf-id

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What changed

An edit aimed at one element can no longer land on a different element that happens to share its id.

A person put an audio group in the film's root, <hf-audio-group id="voice" data-fx-chain="...">, and gave the voiceover clips in a sub-composition data-audio-group="voice". After a later edit in Studio, the group was gone from index.html with no error, so the voice chain stopped applying.

  • Cause. findTargetElement (packages/studio-server/src/helpers/sourceMutation.ts) looks the target up by data-hf-id first. When that finds nothing, it falls back to getElementById(target.id), then to the selector.
    • Say a clip shares the group's id (<audio id="voice" ...>) and the request is resolved in a file where its data-hf-id isn't found: the clip lives in another file, or the copy it was served from is stale.
    • Then the fallback picks the group. A delete removes it, and a move writes the clip's data-start onto it.
    • Every save that routes through findTargetElement (patch, remove, split, wrap, unwrap, duplicate, freeze frame) shared this.
  • Fix, in findTargetElement. A data-hf-id that was already in the file is that element's identity. So when a request names a data-hf-id, a fallback hit pinned in the file to a different one is a different element, and it no longer counts as a match. The routes already answer "not matched" safely.
  • What still lands. A fallback hit whose data-hf-id was only minted by this parse (not yet written to the file) still matches. That is the case the fallback exists for: a save carrying an id served before an outside edit changed what is minted. Its test ("hfId lookup falls through to selector...") passes unchanged.
  • How. parseSourceDocument records which data-hf-ids the raw source carried, keyed by the document it returns. findTargetElement reads that, so no call site changes.

What I measured

  • New test in sourceMutation.test.ts: a root <hf-audio-group id="voice" data-hf-id="hf-oova">, then remove and patch with { id: "voice", hfId: "hf-6egx" }. The file comes back unchanged and the patch reports matched: false.
    • fails without the fix: the group is deleted ("expected '
      <div …class="clip"…' to be '…<hf-audio-group id="voice"…'").
  • src/helpers vitest passes 3 runs of 3 (511 tests). Full @hyperframes/studio-server vitest passes (1328, 2 skipped). tsc --noEmit passes. oxfmt and oxlint are clean on the changed files.
  • End to end, through removeElementFromHtml / patchElementInHtml on a two-file project (clip in compositions/vo.html) and on an inline-template one:
Request Before After
Delete clip, sent to index.html, {id, hfId} group deleted group kept
Move clip, sent to index.html group retimed group kept
Delete clip, inline template, stale hfId group deleted group kept
Move clip, inline template, stale hfId group retimed group kept
Delete clip, sent to its own file clip removed clip removed

What I did NOT exercise

  • A request that sends only id (no hfId), into a file where that id appears twice. It carries no identity to check, so it still resolves to the first element, as before. Studio's timeline always sends both.
  • Studio in a browser: the change is server-side, and I measured it through the helpers the routes call.

@github-actions

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 199 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)

@jerrai-bot-heygen jerrai-bot-heygen 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.

At this head, exact hfId resolution remains first; id and selector fallback now reject an element carrying a different ID pinned in the authored source, while parser-minted IDs retain the legitimate fallback. The new remove/patch regression distinguishes the prior audio-group deletion, and the edit-accuracy and lint checks passed. The separate timeline viewport gate is red on a paused A-roll chroma sample; its fixture loads/plays a film without a source edit and does not establish a defect in this two-file resolver change. That CI/visual gate still needs its own landing decision. — Jerrai

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit 49c4950 Oct 15, 2026
99 of 100 checks passed
@miguel-heygen
miguel-heygen deleted the fix/edit-target-by-hf-id branch October 15, 2026 08:33
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