Skip to content

fix(organize): a file that fails the check after its move fails alone - #124

Merged
iLevyTate merged 3 commits into
mainfrom
fix/organize-move-check-fails-alone
Nov 13, 2026
Merged

iLevyTate merged 3 commits into
mainfrom
fix/organize-move-check-fails-alone

Conversation

@iLevyTate

@iLevyTate iLevyTate commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Follow-up to #122 from the hands-on Mac report (item 2): a file that fails the check after its move is listed as not moved, and the rest of the run stays moved. Before, it undid the whole run.

What changes

The move check (batchOrganizeHandler.js)

  • After each move the batch checks that the file is at its destination and gone from its source. A failed check was a critical error: the batch stopped and every confirmed move was rolled back. The file that failed never joins the completed moves, so the rollback did not bring it back; it only undid the others.
  • A failed check is now a per-file failure. The row is failed, the other files stay moved, and undo covers the confirmed moves.
  • "Destination missing" always carries MOVE_VERIFICATION_DESTINATION_FAILED (it carried the raw ENOENT), and its message ends with where the file is: still at its original location, gone from there too, or not checkable.
  • A destination that cannot be read for another reason keeps the filesystem's code, so EIO still stops the batch and EACCES/EPERM count as permission failures.
  • The message fragment verification failed is no longer critical. On the move path only the move check produced it; checksum mismatch stays critical.

Escalation

  • Failed checks share the rule permission denials already had: three in a row with no verified move in between roll the batch back. It is one counter, one predicate and one threshold (FAILURE_ESCALATION_THRESHOLD), renamed from the permission-only ones. This is a judgment call on top of "fail alone": a destination that keeps losing files is treated as a fault of the destination, not of each file.
  • Strict mode still stops on the first failure.

Renderer (errorMapping.js)

  • Both codes are mapped and show the handler's message. Without an entry, MOVE_VERIFICATION_DESTINATION_FAILED fell through the _FAILED suffix rule to the generic save-failed copy.

Also in this PR

  • test/batchOrganize.moveCheckFailsAlone.realfs.test.js: the same cases on the real filesystem (real handler, move, check and rollback in a temp folder). With the handler from main, three of its four tests fail because the whole run is rolled back.
  • @hono/node-server 2.1.5 → 2.1.6 in the lockfile and the inventory. npm audit --omit=dev started failing on 2026-11-12 (two moderate advisories, transitive through @modelcontextprotocol/sdk), which fails npm run ci on every branch.

Not in this PR

  • The banner text is unchanged; "Files that were not moved are still listed." already describes this case.
  • A file that fails the check because it is still at its source may also be at its destination. It is not removed from either place; the next run treats the destination copy as it treats any existing file there.
  • When another program deletes the temporary copy during a cross-volume move, the row says "File not found: " although the source is still there. That wording predates this PR.

Checks

Windows 11 x64, on this branch's tree:

  • npm run local:gate, every step: 957 test suites, 14143 tests passed, 11 skipped, coverage thresholds met, production build, native modules.
  • With the counter reset removed from the handler, the test "a confirmed move between two failed checks keeps them per-file" fails; with it restored, it passes.
  • The eight Playwright smoke specs on a clean dev build, 101 of 101, on the first commit. Not rerun after the test file and the lockfile bump were added.

A separate process deleting files from the destination while a 60-file batch runs (one-off runs, not in the repo):

Run Deleter takes Result
Handler under Jest, C: to C: 6 named files 5 failed alone, 54 stayed moved, no rollback. The sixth was deleted after its check had passed and is reported as moved.
Handler under Jest, C: to an external drive (cross-volume) 6 named files 6 failed alone, 54 stayed moved, no rollback.
Handler under Jest, C: to C: every file that arrives Rolled back after three failed checks in a row. 10 files destroyed by the deleter, 50 never touched.
Handler under Jest, C: to an external drive every file that arrives, temporary copies included No source file lost (60 of 60 in place); the batch stopped.
Packaged Windows x64 app (win-unpacked built from the first commit), organize called through its own preload bridge 6 named files 4 failed alone, 54 stayed moved, no rollback. Two were deleted after their check had passed.
Packaged Windows x64 app, same call every file that arrives Rolled back after three failed checks in a row. 6 files destroyed by the deleter, 54 never touched.

Not checked: macOS (the new real-filesystem test runs there when main next builds; pull requests run Ubuntu and Windows only), the installer, the Organize screen in the packaged app (the call went through the preload bridge, not the buttons), and a real sync client or antivirus product.

After each move the batch checks that the file is at its destination and
gone from its source. A file that failed the check was a critical error:
the batch stopped and every confirmed move was rolled back. The file that
failed never joins the completed moves, so the rollback did not bring it
back; it only undid the rest.

- A failed check is a per-file failure. The row is listed as not moved and
  the other files stay moved and undoable.
- "Destination missing" now always carries
  MOVE_VERIFICATION_DESTINATION_FAILED (it carried the raw ENOENT) and its
  message ends with whether the file is still at its original location.
  A destination that cannot be read for another reason keeps the
  filesystem's code, so EIO still stops the batch.
- Failed checks share the escalation rule that permission denials already
  had: three in a row with no verified move in between roll the batch
  back. One counter, one predicate, one threshold.
- Strict mode still stops on the first failure.
- The renderer maps both codes and shows the handler's message. Without an
  entry the destination code fell through to the generic save-failed copy.
The existing tests for a file that fails the check after its move run on
a mocked filesystem. This one runs the real batch handler, the real move,
the real check and the real rollback in a temp folder. The only stand-in
is the other program: a real filesystem call made right after a file
lands at its destination.

- A file removed from its destination fails alone; the others stay moved.
- A file put back at its source fails alone and is reported as still
  there.
- A file present at both places fails alone and neither copy is touched.
- A destination that keeps losing files is rolled back.

With the handler from main the first three fail: the whole run is rolled
back.
npm audit --omit=dev started failing on 2026-11-12: versions 2.0.0 to
2.1.5 carry two moderate advisories (GHSA-qv5m-9pm6-hqp3,
GHSA-jpvv-r467-5p4w). It is a transitive dependency of
@modelcontextprotocol/sdk. Only its lockfile entry and its row in the
commercial-use inventory change.

2.1.6 (2026-11-02) is the first patched release; 2.1.8 was published
hours before this commit and is left for a later update.
@iLevyTate
iLevyTate merged commit 82f912e into main Nov 13, 2026
8 checks passed
@iLevyTate
iLevyTate deleted the fix/organize-move-check-fails-alone branch November 13, 2026 01:12
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