Skip to content

#1743, part 3: two Render Queue outputs never write the same file, and default names avoid the queue's other outputs - #1830

Draft
nanox333 wants to merge 7 commits into
storytold:mainfrom
nanox333:fix/1743-duplicate-outputs
Draft

nanox333 wants to merge 7 commits into
storytold:mainfrom
nanox333:fix/1743-duplicate-outputs

Conversation

@nanox333

@nanox333 nanox333 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Part of #1743, the third of three parts. The first is #1758, which closes the issue.

Draft: stacked on #1758. This branch contains #1758's five commits, so until #1758 merges the diff below also shows them. This PR's own changes are the last two commits (10 files, +834/-27). I will rebase it and mark it ready for review once #1758 has merged. It does not depend on the second part.

What was wrong

Two queue items that resolve to one output (a comp added twice, a duplicated item) replace each other's file inside one job. Nothing is on disk when the job starts, so the existing-file check said nothing.

What changes

  • A default output name that another queue item writes (any output module or segment) is numbered when the item is added: Main.mp4, Main_1.mp4 (renderQueue.add, render.preRender, comp.saveFrameAs {queue}, file.createProxy). Names a caller gave and names already stored in items are never changed. Sequences are compared by the frames they write, so two stills of one comp at different times keep their names.
  • Outputs of one job that still name the same file (explicit names, renderQueue.duplicate, output modules, segments) are listed by renderQueue.checkOutputs under duplicates, and the whole render is refused before anything is written, naming both items. overwrite does not apply: it is for files that exist, and here the render would replace its own output. Sequences clash only on frames both write; a later item with Skip Existing Files leaves the earlier item's frames alone; items refused for footage take no part.
  • The Replace / Cancel prompt stays closed in that case (the refusal shows in the status bar), and effectcraft-cli render no longer asks for --overwrite first.

Behaviour changes

A job that used to render two items over one file now refuses before writing anything. Default names change only for new items that would collide.

Testing

  • tests_render_overwrite and output_guard unit tests, ui_render_overwrite (two_items_writing_one_file_are_refused_without_asking_to_replace) and a CLI test. They were written to fail without the change; I have not re-run them without it since the rebase onto Fix #1743: protect render outputs from replacing project sources #1758.
  • Passing locally on top of Fix #1743: protect render outputs from replacing project sources #1758 (Windows 11, after a clean rebuild of the workspace crates): cargo fmt --check; cargo test for effectcraft-engine (lib, 700 passed), -project, -host (lib), -automation (45 + 8), -cli (cli.rs, 23 passed) and -ui-egui (lib i18n and overwrite, ui_render_overwrite 8 tests, ui_render_queue, ui_agent_audit); cargo clippy -D warnings on the engine's library and on project, host, ui-egui, automation, script and cli (all targets); cargo xtask layers; cargo check --target wasm32-unknown-unknown for project, engine and ui-egui.
  • Full cargo xtask ci gate not run; quick checks only.

nanox333 and others added 2 commits October 18, 2026 22:18
…le, and replaces other existing files only when told to

A composition made from footage.mp4 is named after the file, so its default output was
<project folder>/footage.mp4: the footage itself. Render opened it with File::create and
truncated it, without asking.

- A render never writes over a file the project reads: footage (every frame of a sequence),
  a proxy, or the project file. Paths are compared through links and, on Windows and macOS,
  without regard to case. The item fails with the reason and the other items render, with
  overwrite or without it.
- Any other file that exists is replaced only with renderQueue.render {overwrite: true}; without
  it the whole render is refused before anything is written and the queue is left as it was.
  renderQueue.checkOutputs reports what would be replaced (existing) or refused (blocked).
- The desktop app asks Replace / Cancel before replacing (Escape cancels; automation ids
  dialog.overwrite.replace and dialog.overwrite.cancel). effectcraft-cli render takes
  --overwrite, scripts take renderQueue.render(true), and a watched folder keeps replacing the
  outputs of a project it renders again.
- A default output name that would be one of those files is numbered when the item is added
  (comp "footage" renders to footage_1.mp4, as After Effects numbers a second item of one comp).
  A name the caller chose is kept, and rendering it is refused.

Tests: engine (tests_render_overwrite, output_guard), project (sequence_frame, numbered_template),
UI (ui_render_overwrite), CLI end to end, host (script render(true)).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The prompt that asks before a render replaces an existing file had no rows in
the panel catalogs, so it read in English whatever the language. Nine strings
now have a row in each of the seven catalogs (ja, zh-hans, zh-hant, uk, pt-br,
cs, es), using each catalog's own words for file, render and frame. The sums
that carry a count are written so they read correctly for any number.

In Japanese and Chinese the question sentences in the body end with an ASCII
question mark: the full-width one drew in a heavier fallback font in body text.
The titles keep the full-width mark, which renders correctly there. I looked at
the prompt in every language, for one file and for several, and none overflows
the dialog.

A test keeps it that way: it fails if any of the strings has no row in any
language, and checks that the file name still comes before its folder.

The translations were written without a native speaker; a review by one is
welcome.

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

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

nanox333 and others added 5 commits October 19, 2026 00:58
The sequence line of the Replace / Cancel prompt kept the English word, while the
rest of the Portuguese catalog says "quadros". Found in review.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The prompt holds the render until Replace is pressed, and the answer then went
with `overwrite: true` for whatever the queue's outputs were at that moment. An
agent can run `renderQueue.setOutput` over the control channel while the prompt
is open, so a file the prompt never showed could be replaced by the same answer.

Replace now checks the queue's outputs again. If they are the ones the prompt
listed, the render runs with `overwrite: true` as before. If they changed, the
render stays pending and the prompt asks again about what is there now. If
nothing is left to replace, or the check fails, the render runs without
permission to replace and the engine decides. Found in review.

Tests: a test that points the queue at another existing file while the prompt is
open (nothing is replaced, it asks again, and answering replaces that file; it
fails without the change), and one for the case where the listed file is gone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The check compared an output only with footage that has the same file name, so
an existing hard link to footage under another name was found as an ordinary
file that exists. With overwrite, the exporter's File::create then truncated the
one file both names lead to, and the footage was gone. Found in review.

A render output that exists, and has the size of some footage, is now compared
with it by the volume's own identity (the `same-file` crate: the device and inode
on Unix, the volume and file index on Windows). Nothing is asked of a file that
does not exist, so rendering to a new file costs one look, and the footage sizes
are read once, when first needed. `same-file` was already in the lock file
(through walkdir) and is Unlicense or MIT; it is a dependency of the engine on
native targets only.

Tests: a hard link to the footage is refused and left alone (it fails without the
change), and a copy of the footage, which has its size and bytes but is another
file, is still replaceable.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…efault names avoid the queue's other outputs

Two queue items that resolve to one output (the same comp added twice, a duplicated item) replaced
each other's file inside one job. Nothing is on disk when the job starts, so the check for existing
files said nothing.

- A default output name (renderQueue.add, render.preRender, comp.saveFrameAs {queue},
  file.createProxy) that another item of the queue writes, in any output module or segment, is
  numbered like a footage collision: Main.mp4, Main_1.mp4, Main_2.mp4 (as After Effects numbers a
  second item of one comp). Sequences are compared by the frames they write, so two stills of one
  comp at different times keep their names. Names a caller gave and names already stored in items
  are never changed; the stored name keeps it stable when the item it avoided is removed.
- Outputs of one job that still name the same file (explicit names, renderQueue.duplicate,
  output modules, segments) are listed by renderQueue.checkOutputs under `duplicates` and the whole
  render is refused before anything is written, naming both items. Sequences clash only on frames
  both write; a later item with Skip Existing Files leaves the earlier one's frames alone; items
  refused for footage and items outside the job take no part.

Why refuse the whole render, regardless of `overwrite`: that flag means "replace files that exist
on disk"; here the render would replace its own output, so there is nothing for the user to
approve, and which of two items should keep the file is theirs to say. Failing only the later item
would leave a half-rendered queue found after the user left; refusing tells them at the click and
writes nothing. renderQueue.duplicate stays a verbatim copy so the clash is reported, not hidden.

The Replace / Cancel prompt stays closed when duplicates exist; the engine's message reaches the
status bar.

Tests: engine (tests_render_overwrite, output_guard unit tests), ui-egui (ui_render_overwrite).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… request for --overwrite

With existing files and two queue items writing one file, the CLI's own check said 'pass
--overwrite', and the flag then changed nothing. It now leaves duplicates to the engine's refusal,
which names both items.

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

This branch has not been deployed

No deployments
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.

1 participant