lotus-land-story: clear stale restic locks before each run
An OOM-killed run on Oct 7 left a lock in the primary that failed every later forget --prune.
Assisted-by: Claude Opus 5.5 via Claude Code
diff --git a/docs/backup.md b/docs/backup.md
index 31f37cf..208cd19 100644
--- a/docs/backup.md
+++ b/docs/backup.md
@@ -14,11 +14,15 @@ sends its own output to the journal with the `backup` tag. Each run:
1. Pings healthchecks.io `/start`, distinguishing a run that died early from
one that never fired.
-2. Backs up `/mnt/lotus-land-story/`, excluding `docker/`.
-3. Backs up `pg_dumpall` output via stdin as `lotus-land-story.sql`.
-4. Runs `forget --prune` against the primary with retention of daily for one
+2. Removes stale locks from the primary, so a run killed mid-flight (for
+ example, by the OOM killer) doesn't block every later prune. The
+ secondary gets the same treatment just before the copy, so a down lolk
+ can't stop the primary backup.
+3. Backs up `/mnt/lotus-land-story/`, excluding `docker/`.
+4. Backs up `pg_dumpall` output via stdin as `lotus-land-story.sql`.
+5. Runs `forget --prune` against the primary with retention of daily for one
month, weekly for one year, monthly forever.
-5. Mirrors every snapshot onto the secondary with
+6. Mirrors every snapshot onto the secondary with
`restic copy --from-repo <primary>`.
An exit trap pings healthchecks.io with success or failure and pushes
diff --git a/lotus-land-story/templates/backup.sh b/lotus-land-story/templates/backup.sh
index c7045db..86c94d3 100644
--- a/lotus-land-story/templates/backup.sh
+++ b/lotus-land-story/templates/backup.sh
@@ -66,6 +66,10 @@ trap report EXIT
# call is distinguishable from one that never fired at all.
ping_healthcheck /start
+# A run killed mid-flight (e.g., by the OOM killer) leaves its lock behind,
+# which blocks every later prune. unlock only removes stale locks.
+restic unlock
+
restic --verbose backup \
/mnt/lotus-land-story/ \
--exclude=/mnt/lotus-land-story/docker
@@ -87,6 +91,7 @@ restic --verbose forget \
# it explicitly; the repos share one.
export RESTIC_REPOSITORY=${SECONDARY_REPOSITORY}
export RESTIC_FROM_PASSWORD=${RESTIC_PASSWORD}
+restic unlock
restic --verbose copy --from-repo "${PRIMARY_REPOSITORY}"
# Snapshots forgotten above are never removed from the secondary; a one-time