Skip to content

fix(studio-server): a rename rewrites only references that name the renamed path - #4915

Merged
miguel-heygen merged 9 commits into
mainfrom
fix/rename-references-boundary
Oct 2, 2026
Merged

miguel-heygen merged 9 commits into
mainfrom
fix/rename-references-boundary

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

What changes

Renaming a file or folder rewrote every occurrence of its old path as a plain substring in project text files. Renaming assets to brand also changed assets-backup/b.png to brand-backup/b.png and the words "the assets folder" in prose.

The old path counts as a reference only where it starts and ends at a non-name character, after any ./, ../ or / lead (kept). A folder is matched by what is under it, never by prose. Because a filename can hold any character a delimiter can (a.png&b.png, other assets/), a match inside the longer path of a file or folder that exists in the project is left alone: the project's real paths settle what text cannot. One function owns it (replaceReferences), used by the rename route.

Tests

files.renameReferences.test.ts (new): the pattern on every form a project writes a path, a longer name and prose left alone, and the PATCH route renaming a folder next to assets-backup and prose. files.test.ts and files.pathSafety.test.ts still pass (143 tests together on miga).

Also read as the same path

A root-relative lead (/assets/x), JSON-escaped (assets\/x) and Windows (assets\x) separators are rewritten, and a file's path ends at a delimiter, so a.png@2x.png is not taken for a.png.

Known limits

A bare folder name with no / after it (for example "assets" as a config value) is no longer rewritten when its folder is renamed; it was indistinguishable from prose.

References from a nested file written relative to that file (../assets/x in scenes/css/style.css when scenes/assets is renamed) are still not resolved: the rewrite has no per-file base, as before this change.

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 1216 (base branch 1216), smooth 1123 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 (1)

@miguel-heygen
miguel-heygen marked this pull request as ready for review October 2, 2026 23:00

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

Reviewed at 72d1da82: referenceRewriter, referencePattern, projectPaths/directoryReal, the updateReferences call site in the rename route, and the new tests. Approving.

Is the matching exact?

  • Start of a reference: REFERENCE_START refuses a match after a word character, ., /, \, + or -. So my-assets/, my+assets/, other/assets/, other\assets\ and https://cdn/assets/ are never taken for a root assets. The ./ / ../ / / lead is part of the match, and the lookbehind sits before it, so the regex can't restart inside the lead (other/./assets and other/../assets stay put).
  • End of a folder: it must be followed by a separator, which rules out assets-backup/, assets2/ and bare prose assets.
  • End of a file: FILE_END refuses a word character, - or .x, which rules out a.png2, a.png-old and a.png.bak. ?v=2, #x, ), a closing quote, srcset's 2x and a sentence-ending . are all still taken.
  • Longer paths that exist: a match inside the name of a real file or folder (other assets/, a.png&backup.png) is left alone. The index is built after renameSync, so the old path's own entries are gone and can't shield it. Linked folders count, through directoryReal, with a cycle guard and isSafePath.
  • The replacement is a function, so a $& or $1 in the new name is written literally. I checked that.

Tests I ran (vitest at this head):

  • files.renameReferences.test.ts passes, 16 of 16.
  • The route tests discriminate. With files.ts restored to the base, both "renaming a folder over the route" tests fail. The base wrote brand-backup/, other brand/, empty brand/ and "The brand folder", and rewrote other assets/a.png through the linked folder.
  • In files.test.ts, "rejects a stale semantic no-op after a concurrent file write" timed out in my checkout. It times out on the base code too, and it doesn't involve renames.

Nits (not blocking):

  1. A single \ counts as a separator, so a JSON or JS string escape after a folder's name reads as a path. Renaming the folder assets to brand turns {"label":"The \"assets\" folder","msg":"Loading assets\nnow"} into "The \"brand\" folder" and "Loading brand\nnow". fonts\tready does the same. This is far narrower than the base, which rewrote every assets in the file, but it's the one prose case left. One fix is to treat a single backslash as a separator only when it isn't followed by n, t, r, ", ' or u, or only in .md/.mdx files.
  2. projectPaths walks folders that walkFiles skips (renders, .thumbnails, .transcode-cache). That only costs time on each rename. It doesn't change results, because those paths only ever shield a match. Skipping the same set would keep a project with many render frames quick.
  3. A lead deeper than four levels isn't rewritten: ../../../../../assets/a.png stays as it is. That's safe, but the limit could be noted in the pattern's comment.

Verdict: APPROVE
Reasoning: The pattern takes a path only when it starts at a reference boundary and ends where the name ends. Longer paths that exist are shielded, and the route tests fail on the old split-and-join. What's left is one residual prose case behind JSON/JS string escapes, and two cost and coverage nits.

— Rames Jusso

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 2, 2026
Merged via the queue into main with commit 1168bb7 Oct 2, 2026
94 checks passed
@miguel-heygen
miguel-heygen deleted the fix/rename-references-boundary branch October 2, 2026 23:43
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