From 4f4f1f43e8b52a1a906f255cc60adee4590bbbbb Mon Sep 17 00:00:00 2001 From: cproudlock Date: Wed, 29 Apr 2026 14:49:38 -0400 Subject: [PATCH] Restore-UDCData: handle ArchivedData-only backups (no CurrentData.json) Production case: bay 3207 had ArchivedData\ on the share with full production records but no CurrentData.json at the bay root. The previous Restore logic treated CurrentData.json as the marker for "valid backup" and exited early when absent, so the script silently no-op'd every cycle even though there was real archive data ready to restore. Asymmetric with Backup-UDCData.ps1, which already handles missing CurrentData.json gracefully (it copies whatever exists). Possible causes of CurrentData.json absence in a backup: source PC had no live UDC session at backup time (UDC inactive / not recording), backup partially failed for that one file (no Backup-side log to confirm without rerun). Either way, an ArchivedData-only backup is still a valid backup. Behavior change: - Early-exit only when BOTH CurrentData.json AND ArchivedData\ are absent. Otherwise proceed with whatever exists. - Copy step for CurrentData.json wrapped in srcCurExists guard. - consumeOk now requires: every present source successfully copied, AND at least one thing was actually copied. - Move-to-migrated wraps CurrentData.json move in Test-Path guard (was already guarded for ArchivedData). - restore.manifest.json gains CurrentDataPresent and ArchivedDataPresent booleans so future audits can see which side actually restored. - UDC relaunch now fires when EITHER copy succeeded (was only on CurrentData.json copy). Verbose logs now distinguish three cases at the early-exit: - Both absent: "no work to do this cycle" (the 99% path) - Only ArchivedData\: WARN with explanation, proceed - Only CurrentData.json: WARN with explanation, proceed Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Standard/Restore-UDCData.ps1 | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/playbook/shopfloor-setup/Standard/Restore-UDCData.ps1 b/playbook/shopfloor-setup/Standard/Restore-UDCData.ps1 index ace258e..30650e2 100644 --- a/playbook/shopfloor-setup/Standard/Restore-UDCData.ps1 +++ b/playbook/shopfloor-setup/Standard/Restore-UDCData.ps1 @@ -163,11 +163,17 @@ Log " CurrentData.json src: $(if ($srcCurExists) { 'present' } else { 'absent' $srcArcExists = Test-Path -LiteralPath $srcArc Log " ArchivedData/ src: $(if ($srcArcExists) { 'present' } else { 'absent' }) - $srcArc" -if (-not $srcCurExists) { - Log "No backup waiting for bay $mn - no work to do this cycle." +if (-not $srcCurExists -and -not $srcArcExists) { + Log "No backup waiting for bay $mn (neither CurrentData.json nor ArchivedData\ at bay root) - no work to do this cycle." Log 'Exit 0.' exit 0 } +if (-not $srcCurExists) { + Log "Partial backup waiting (ArchivedData\ present, CurrentData.json absent). Will restore ArchivedData\ only. Source PC may have had no live UDC session at backup time, or backup partially failed." 'WARN' +} +if (-not $srcArcExists) { + Log "Partial backup waiting (CurrentData.json present, ArchivedData\ absent). Will restore CurrentData.json only." 'WARN' +} # -- We have a backup. Restore. ------------------------------------------ Log "Backup waiting at $bayDir - proceeding with restore" @@ -203,19 +209,23 @@ if (-not (Test-Path -LiteralPath $UdcDataDir)) { $localCur = Join-Path $UdcDataDir 'CurrentData.json' $localArc = Join-Path $UdcDataDir 'ArchivedData' -# Copy CurrentData.json +# Copy CurrentData.json (only if present at source) $copiedCur = $false -Log "Copying CurrentData.json" -Log " src: $srcCur" -Log " dst: $localCur" -try { - Copy-Item -LiteralPath $srcCur -Destination $localCur -Force -ErrorAction Stop - $copiedCur = $true - $sz = (Get-Item -LiteralPath $localCur).Length - Log " OK ($sz bytes)" -} catch { - Log " FAILED" 'ERROR' - LogErr $_ +if ($srcCurExists) { + Log "Copying CurrentData.json" + Log " src: $srcCur" + Log " dst: $localCur" + try { + Copy-Item -LiteralPath $srcCur -Destination $localCur -Force -ErrorAction Stop + $copiedCur = $true + $sz = (Get-Item -LiteralPath $localCur).Length + Log " OK ($sz bytes)" + } catch { + Log " FAILED" 'ERROR' + LogErr $_ + } +} else { + Log "CurrentData.json not present in backup - skipping that copy step" } # Copy ArchivedData/ @@ -245,9 +255,14 @@ if ($srcArcExists) { Log "ArchivedData/ not present in backup - skipping that copy step" } -# One-shot consumption: only move backup -> migrated/ if everything required succeeded -$consumeOk = ($copiedCur -and ($copiedArc -or -not $srcArcExists)) -Log "consumeOk=$consumeOk (copiedCur=$copiedCur, copiedArc=$copiedArc, srcArcExists=$srcArcExists)" +# One-shot consumption: only consume when every present source has been +# successfully copied. If a source was absent we don't fault on it; if a +# source was present but copy failed, we leave the live backup for retry. +# Must have copied at least one thing to consume. +$consumeOk = (($copiedCur -or -not $srcCurExists) -and ` + ($copiedArc -or -not $srcArcExists) -and ` + ($copiedCur -or $copiedArc)) +Log "consumeOk=$consumeOk (copiedCur=$copiedCur, copiedArc=$copiedArc, srcCurExists=$srcCurExists, srcArcExists=$srcArcExists)" if ($consumeOk) { try { @@ -258,8 +273,10 @@ if ($consumeOk) { if (-not (Test-Path -LiteralPath $migDir)) { New-Item -ItemType Directory -Path $migDir -Force | Out-Null } if (-not (Test-Path -LiteralPath $migStamp)) { New-Item -ItemType Directory -Path $migStamp -Force | Out-Null } - Move-Item -LiteralPath $srcCur -Destination (Join-Path $migStamp 'CurrentData.json') -Force -ErrorAction Stop - Log " moved CurrentData.json" + if (Test-Path -LiteralPath $srcCur) { + Move-Item -LiteralPath $srcCur -Destination (Join-Path $migStamp 'CurrentData.json') -Force -ErrorAction Stop + Log " moved CurrentData.json" + } if (Test-Path -LiteralPath $srcArc) { Move-Item -LiteralPath $srcArc -Destination (Join-Path $migStamp 'ArchivedData') -Force -ErrorAction Stop Log " moved ArchivedData/" @@ -275,7 +292,9 @@ if ($consumeOk) { DestinationHostname = $env:COMPUTERNAME DestinationUser = $whoami MachineNumber = $mn - CurrentDataBytes = (Get-Item -LiteralPath $localCur).Length + CurrentDataPresent = $copiedCur + CurrentDataBytes = if ($copiedCur) { (Get-Item -LiteralPath $localCur).Length } else { 0 } + ArchivedDataPresent = $copiedArc ArchivedDataFiles = $arcFiles ArchivedDataBytes = $arcBytes RestoredVia = 'Restore-UDCData.ps1 (manifest engine, on logon)' @@ -295,7 +314,7 @@ if ($consumeOk) { # Relaunch UDC with the current machine number args. UDC's vendor autostart in # HKLM\Run will also fire on the next user logon, so this is belt-and-suspenders # for the same-session case (e.g. tech is at the keyboard during the restore). -if ((Test-Path -LiteralPath $UdcExePath) -and $copiedCur) { +if ((Test-Path -LiteralPath $UdcExePath) -and ($copiedCur -or $copiedArc)) { Log "Relaunching UDC.exe: `"$Site`" -$mn" try { Start-Process -FilePath $UdcExePath -ArgumentList @("`"$Site`"", "-$mn")