feat(bootstrap): restore rescued GuruRMM/GuruConnect WIP on rebuild

Add restore-at-risk-work.ps1 and wire it into bootstrap Phase 6. Recreates
local-only WIP rescued to the recovery bundle's at-risk-work/: re-applies the
three guru-rmm stash patches back AS stashes (LIFO order preserved) and drops
the guru-connect tmp-spec018.diff back as its untracked working file. Patches
that won't apply cleanly are reported for manual git apply --3way. Updates
RESTORE.md and the session log with the rescue details.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 12:11:08 -07:00
parent 6bb75e9320
commit 7342be1eaf
4 changed files with 87 additions and 2 deletions

View File

@@ -34,6 +34,11 @@ The recovery bundle lives on the removable drives:
- `config\` — Windows Terminal settings, fleet `hosts` file, quote-wizard `.env.production` - `config\` — Windows Terminal settings, fleet `hosts` file, quote-wizard `.env.production`
- `manifests\``installed-tools.txt`, `ollama-models.txt`, `git-global-config.txt`, - `manifests\``installed-tools.txt`, `ollama-models.txt`, `git-global-config.txt`,
`repos.txt`, `user-environment.reg` / `.txt` (incl. `OLLAMA_MODELS`/`OLLAMA_HOST`/`PROTOC`), `scheduled-tasks\*.xml` `repos.txt`, `user-environment.reg` / `.txt` (incl. `OLLAMA_MODELS`/`OLLAMA_HOST`/`PROTOC`), `scheduled-tasks\*.xml`
- `at-risk-work\` — local-only WIP rescued from the submodules (not on any remote):
guru-rmm stashes as `.patch` files + guru-connect `tmp-spec018.diff`. The bootstrap
re-applies these automatically in Phase 6 (`restore-at-risk-work.ps1`) — the guru-rmm
ones are put back **as stashes** (`git stash list`), the guru-connect diff is dropped
back as its untracked working file. See `RESTORE-at-risk-work.txt` for manual steps.
- `data\` (F: only) — large non-Gitea client/project data, repo-relative paths - `data\` (F: only) — large non-Gitea client/project data, repo-relative paths
Everything else (all tracked code, skills, commands, docs, session logs, wiki) comes Everything else (all tracked code, skills, commands, docs, session logs, wiki) comes

View File

@@ -0,0 +1,59 @@
<#
.SYNOPSIS
Restore local-only WIP (stashes + untracked diffs) that was rescued into the
recovery bundle's at-risk-work\ folder. Run AFTER the repos + submodules are cloned.
.DESCRIPTION
guru-rmm : each stashN-*.patch is applied to the working tree and then re-stashed,
faithfully recreating the original `git stash` entries. Patches are
processed highest-N-first so stash0 ends up on top (stash@{0}), matching
the original LIFO order. The working tree is left CLEAN (changes live in
the stash, exactly as before).
guru-connect : tmp-spec018.diff was an UNTRACKED working file, so it is copied back
into the repo as-is (not applied). Apply it yourself if/when you want it.
Non-destructive and re-runnable. If a patch won't apply cleanly (submodule moved on),
it is reported and the .patch file is left in place for manual `git apply --3way`.
.PARAMETER BundlePath Recovery bundle root (auto-detect F:\ then E:\).
.PARAMETER ClaudeToolsRoot Default D:\claudetools.
#>
[CmdletBinding()]
param([string]$BundlePath,[string]$ClaudeToolsRoot='D:\claudetools')
$ErrorActionPreference='Stop'
if (-not $BundlePath) { foreach ($d in 'F:','E:','D:') { if (Test-Path "$d\claudetools-recovery\at-risk-work") { $BundlePath="$d\claudetools-recovery"; break } } }
$aw = "$BundlePath\at-risk-work"
if (-not $BundlePath -or -not (Test-Path $aw)) { Write-Host "[INFO] no at-risk-work folder found in bundle - nothing to restore"; return }
Write-Host "[INFO] restoring at-risk WIP from $aw" -ForegroundColor Cyan
function Have-Git($repo){ Test-Path "$repo\.git" }
# ---- guru-rmm stashes ----
$rmm = "$ClaudeToolsRoot\projects\msp-tools\guru-rmm"
if ((Test-Path "$aw\guru-rmm") -and (Have-Git $rmm)) {
if (git -C $rmm status --porcelain) {
Write-Host "[WARN] guru-rmm working tree is dirty; skipping auto-restore to avoid mixing changes. Apply patches in $aw\guru-rmm manually." -ForegroundColor Yellow
} else {
# highest N first so stash0 lands at stash@{0}
$patches = Get-ChildItem "$aw\guru-rmm" -Filter '*.patch' | Sort-Object Name -Descending
foreach ($p in $patches) {
$check = git -C $rmm apply --check --3way $p.FullName 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host "[WARN] won't apply cleanly, left for manual restore: $($p.Name) ($check)" -ForegroundColor Yellow; continue }
git -C $rmm apply --3way $p.FullName 2>$null
git -C $rmm stash push -u -m "restored WIP: $($p.BaseName)" 2>$null | Out-Null
Write-Host "[OK] re-stashed guru-rmm: $($p.BaseName)" -ForegroundColor Green
}
Write-Host "[INFO] guru-rmm stashes now:" -ForegroundColor Cyan
git -C $rmm stash list
}
}
# ---- guru-connect untracked diff ----
$gc = "$ClaudeToolsRoot\projects\msp-tools\guru-connect"
$diff = "$aw\guru-connect\tmp-spec018.diff"
if ((Test-Path $diff) -and (Test-Path $gc)) {
Copy-Item $diff "$gc\tmp-spec018.diff" -Force
Write-Host "[OK] guru-connect\tmp-spec018.diff restored (untracked working file - 'git apply --3way tmp-spec018.diff' to apply it)" -ForegroundColor Green
}
Write-Host "[DONE] at-risk WIP restore" -ForegroundColor Cyan

View File

@@ -185,8 +185,13 @@ if (Phase 5 'Clone repos') {
} }
# ============================================================ PHASE 6 # ============================================================ PHASE 6
if (Phase 6 'Restore repo-local identity') { if (Phase 6 'Restore repo-local identity + at-risk WIP') {
if ($script:Bundle) { & "$here\restore-secrets.ps1" -BundlePath $script:Bundle -Group repo -ClaudeToolsRoot $ClaudeToolsRoot } if ($script:Bundle) {
& "$here\restore-secrets.ps1" -BundlePath $script:Bundle -Group repo -ClaudeToolsRoot $ClaudeToolsRoot
# Recreate local-only WIP (guru-rmm stashes, guru-connect untracked diff) that
# would otherwise have been lost - faithfully puts the stashes back as stashes.
& "$here\restore-at-risk-work.ps1" -BundlePath $script:Bundle -ClaudeToolsRoot $ClaudeToolsRoot
}
else { Warn "no bundle - you must hand-create .claude/identity.json (see CLAUDE.md multi-user section)" } else { Warn "no bundle - you must hand-create .claude/identity.json (see CLAUDE.md multi-user section)" }
} }

View File

@@ -65,6 +65,22 @@ Full read-only sweep of the environment:
- Bootstrap toolkit committed to repo (3rd copy, fleet-reusable). - Bootstrap toolkit committed to repo (3rd copy, fleet-reusable).
- Part 2 (the actual reset + rebuild) pending — driven by `F:\claudetools-recovery\bootstrap\windows-bootstrap.ps1`. - Part 2 (the actual reset + rebuild) pending — driven by `F:\claudetools-recovery\bootstrap\windows-bootstrap.ps1`.
## At-risk WIP rescued (addendum)
Checked the GuruRMM/GuruConnect repos for local-only work a reset would destroy.
All committed code was already pushed (every submodule HEAD is on origin), but found
uncommitted WIP and preserved it to `at-risk-work\` on both E: and F:
- **guru-rmm** (3 stashes, exported as patches): `stash@{0}` +119 lines `docs/FEATURE_ROADMAP.md`;
`stash@{1}` `CONTEXT.md`; `stash@{2}` `agent/Dockerfile` + `docs/unraid-ca-template.xml`.
- **guru-connect**: untracked `tmp-spec018.diff` (54 KB; `agent/src/install.rs` + more).
- `D:\work\gururmm`: only a local deletion of a legacy file still on remote — no loss.
Original stashes left intact (not dropped). Added `restore-at-risk-work.ps1` and wired it
into bootstrap Phase 6: on rebuild it re-applies the guru-rmm patches **back as stashes**
(LIFO order preserved) and drops the guru-connect diff back as its untracked file. Patches
that won't apply cleanly are reported and left for manual `git apply --3way`.
## Follow-ups ## Follow-ups
- When ready to reset: run the bootstrap from the drive; verify vault decrypts via - When ready to reset: run the bootstrap from the drive; verify vault decrypts via
`vault.sh get-field projects/claudetools/database.sops.yaml credentials.password`. `vault.sh get-field projects/claudetools/database.sops.yaml credentials.password`.