---
name: site-self-heal
description: Detect and heal avenlost.pet (the search hub for Aven) when it breaks — media 404, apex down, an incomplete deploy went live. Read-only liveness check; on a real break, restore the known-good snapshot in one call and verify. The restore target is READ from the site's own record, never carried in this file. v2, 2026-09-12.
---

# site-self-heal · v2 — the anchor is a reference, not a value

The search hub must stay up: a stranger holding Aven reaches it at 3 a.m. This skill keeps it
up without waiting for a human. It NEVER rebuilds and NEVER blind-deploys; it RESTORES an
existing, immutable, already-verified snapshot.

**What changed in v2 and why.** v1 carried a hardcoded "known-good" deploy id. Twelve deploys
later it still pointed at the first one; had it fired it would have undone three weeks of
work. That is `failure:self-heal-anchor-went-stale` in the household's own record. So v2
carries NO anchor. It reads one. The order is fixed and every step is written down:

Site: Netlify site `9f85499e-7ea8-4bf8-b2d6-533c645e40ee` · project `chimerical-mermaid-54bff8` · apex `avenlost.pet`.

## 1 — DETECT (read-only; always safe; silent when healthy)
Fetch, cache-busted (`?cb=<unix time>`):
- apex `https://avenlost.pet/` → 200, body contains the recovery path: phone `(260) 337-3747`, the report form, "MISSING SINCE".
- the load-bearing media, all must serve (HEAD 200): `/episodes/ep02/ep02-hero-720.mp4`, `/media/aven-real.mp4`, `/media/aven-wheel-story-poster.jpg`, `/episodes/ep06/ep06.mp4`.
- `/havens.head` → 200 and parseable JSON.
HEALTHY = all of the above. Healthy → do nothing, say nothing.

## 2 — RESOLVE THE ANCHOR (in this order; stop at the first that answers)
1. **The site's own record.** `GET https://avenlost.pet/havens.head` → `known_good_deploy`. This value was derived by the deploy pipeline from the host's published-deploy record at the moment the graph was cut, and it changes every deploy without a hand touching it. If the apex is broken but this file still serves, it is authoritative.
2. **The host's record.** If `havens.head` cannot be read: `GET https://api.netlify.com/api/v1/sites/9f85499e-7ea8-4bf8-b2d6-533c645e40ee/deploys?per_page=20` (bearer token). Take deploys with `state == "ready"` and a non-null `published_at`, newest first. If the newest is the one currently published and the site is broken, take the one before it. Never take anything without `published_at` — an unpublished draft is not a known-good.
3. **The last resort.** Only if both are unreachable: the anchor recorded in the household memory (`site-anchor` in the project's memory, or `site-restore-manifest`). Say out loud that you fell back to a hand-carried value, and run `tools/anchorcheck.py` against it before trusting it.

Never restore to `6a99a047` (withdrawn prose cards) or to anything from before `6aa5c4b9b29ebe406a131092` (2026-09-12) without the owner's live word — those predate Episode Six, Episode Four's return and the topology page.

## 3 — HEAL (the one write this skill makes)
```
POST https://api.netlify.com/api/v1/sites/9f85499e-7ea8-4bf8-b2d6-533c645e40ee/deploys/<ANCHOR>/restore
Authorization: Bearer <NETLIFY_TOKEN>
```
Re-points production at an existing snapshot — zero uploads, nothing erased, reversible in one
more call. If a token is present in the session, run it. If not, send ONE notification carrying
the exact call with the resolved anchor filled in, and stop. Do NOT store a standing key; the
household norm is ephemeral, rotated tokens.

## 4 — VERIFY
Re-run §1. Additionally: `/episodes/` first `.btn` and first `.card` both point at
`/episodes/ep06/ep06.mp4`, and the ep06 card appears before the ep04 card in the DOM. Log one
line: anchor used, where it was resolved from (§2 step 1/2/3), published_at after restore.
If it did not take, escalate to the owner with the log line.

## Hard guards (never violate)
- Health checks are READ-ONLY. Never deploy to test deploying.
- Never a full-replace / whole-tree deploy without the FULL verified tree — the host snapshots
  the whole tree and deletes what is not in it (this wiped the site on 2026-08-29).
- Fix a break by RESTORE, never by rebuild from a crawl.
- The anchor is resolved at run time, in the order above. If you find yourself typing a deploy
  id into this file, stop: that is the defect this version exists to remove.
- Publishing is the owner's. Auto-restore is a bounded exception that may only republish a
  snapshot the host already served as production.

## Companion instruments (published at avenlost.pet/tools/)
- `anchorcheck.py` — is the anchor a heal would use still fresh? Run it whenever this skill is edited.
- `draft_then_promote.sh` — how every deploy ships: draft, verify, then one promote.
- `graph.py` / `graphcheck.py` — regenerate and verify `havens.jsonld` + `havens.head`; the pipeline
  feeds `KNOWN_GOOD_DEPLOY` in from the host so the anchor lands in the record automatically.
