fix(#4558): report a byte-identical restore destination as already_present (#4599)

* fix(#4558): report a byte-identical restore destination as already_present

restore-custom-files treated a destination that is byte-identical to its
backup exactly like a missing one: plan reported it as `eligible`, --apply
re-copied the same bytes and reported `restored`, and both counters
included it. Because update.md drives its restore question off
eligible_count, the workflow re-offered the same no-op restore on every
update and accepting it never settled anything.

Emit a distinct `already_present` outcome for that case. It is excluded
from eligible_count and restored_count, --apply writes nothing for it,
and the backup is left intact. A differing destination is still
skipped_destination_exists and a missing one still restores normally.

Regression tests cover the identical-destination plan/apply paths, the
idempotence-after-success cycle (missing -> restored -> silent plan), and
a mixed backup. Docs for the outcome enum are updated to match.

* fix(#4558): tighten already_present wording after review

update.md's RESTORE_ELIGIBLE == 0 branch now also names the already-present
case, CLI-TOOLS.md no longer calls the follow-up plan run "silent" (the
entry is still reported, just never offered), and a test message reads
correctly.

* chore(#4558): add changeset fragment for #4599

* chore(#4558): acknowledge update.md growth from the restore-outcome guidance

Emitted-Drift-Ack-Growth: update.md — added already_present restore-outcome guidance for #4558

---------

Co-authored-by: TwistedRiCen <16397953+TwistedRiCen@users.noreply.github.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Twisted Fate
2026-09-13 10:16:40 +08:00
committed by GitHub
parent d6788a6805
commit 6edd506cc7
5 changed files with 129 additions and 13 deletions

View File

@@ -3293,6 +3293,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
const RESTORE_OUTCOME = Object.freeze({
ELIGIBLE: 'eligible',
RESTORED: 'restored',
ALREADY_PRESENT: 'already_present',
SKIPPED_DESTINATION_MANAGED: 'skipped_destination_managed',
SKIPPED_DESTINATION_EXISTS: 'skipped_destination_exists',
SKIPPED_COPY_FAILED: 'skipped_copy_failed',
@@ -3572,9 +3573,13 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
}
// An identical destination is a no-op restore, not a conflict: re-running
// the restore after a successful one must stay quiet and idempotent.
// the restore after a successful one must stay quiet and idempotent. It
// gets its own outcome so it is excluded from eligible_count — the update
// workflow drives its restore question off that count (#4558).
let destExists = false;
let destDiffers = false;
if (fs.existsSync(destPath)) {
destExists = true;
try {
destDiffers = !fs.readFileSync(destPath).equals(fs.readFileSync(srcPath));
} catch {
@@ -3589,6 +3594,10 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
entries.push({ path: relPath, outcome: RESTORE_OUTCOME.SKIPPED_DESTINATION_EXISTS, warnings });
continue;
}
if (destExists) {
entries.push({ path: relPath, outcome: RESTORE_OUTCOME.ALREADY_PRESENT, warnings });
continue;
}
if (!apply) {
entries.push({ path: relPath, outcome: RESTORE_OUTCOME.ELIGIBLE, warnings });