diff --git a/tests/custom-html-rst-bad/ops.py b/tests/custom-html-rst-bad/ops.py
new file mode 100644
index 0000000..3f3b920
--- /dev/null
+++ b/tests/custom-html-rst-bad/ops.py
@@ -0,0 +1,15 @@
+"""custom-html-rst-bad — lifecycle ops for bad-restore RED canary.
+
+NO pre_backup hook: marker never seeded before backup → snapshot has no ci-marker.txt.
+pre_restore writes "mutated". After restore, marker stays "mutated" (not in snapshot) → FAIL.
+"""
+
+from __future__ import annotations
+
+from harness import lifecycle
+
+MARKER_PATH = "/usr/share/nginx/html/ci-marker.txt"
+
+
+def pre_restore(domain: str, meta: dict) -> None:
+ lifecycle.exec_in_app(domain, ["sh", "-c", f"echo mutated > {MARKER_PATH}"])
diff --git a/tests/custom-html-rst-bad/recipe_meta.py b/tests/custom-html-rst-bad/recipe_meta.py
new file mode 100644
index 0000000..7e5bda7
--- /dev/null
+++ b/tests/custom-html-rst-bad/recipe_meta.py
@@ -0,0 +1,3 @@
+# custom-html-rst-bad — regression fixture for bad-restore canary.
+# BACKUP_CAPABLE=True forces the backup tier to run even though the recipe has no backupbot label.
+BACKUP_CAPABLE = True
diff --git a/tests/custom-html-rst-bad/test_restore.py b/tests/custom-html-rst-bad/test_restore.py
new file mode 100644
index 0000000..a361d65
--- /dev/null
+++ b/tests/custom-html-rst-bad/test_restore.py
@@ -0,0 +1,23 @@
+"""custom-html-rst-bad — RESTORE assertion (bad-restore RED canary).
+
+No pre_backup → backup snapshot has no ci-marker.txt. pre_restore writes "mutated".
+After restore: marker is "mutated" (restore can't recover "original" — wasn't backed up) → FAIL.
+"""
+
+import os
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
+from harness import lifecycle # noqa: E402
+
+MARKER_PATH = "/usr/share/nginx/html/ci-marker.txt"
+
+
+def test_restore_returns_state(live_app):
+ result = lifecycle.exec_in_app(
+ live_app, ["sh", "-c", f"cat {MARKER_PATH} 2>/dev/null || echo MISSING"]
+ ).strip()
+ assert result == "original", (
+ f"restore did not return the pre-mutation (backed-up) state: got {result!r}. "
+ "Expected 'original'. The backup had no marker, so restore cannot recover it."
+ )
diff --git a/tests/regression/test_canaries.py b/tests/regression/test_canaries.py
index 1f54efd..b864f02 100644
--- a/tests/regression/test_canaries.py
+++ b/tests/regression/test_canaries.py
@@ -141,13 +141,13 @@ _BAD_BACKUP = {
_BAD_RESTORE = {
"id": "bad-restore",
- "recipe": "custom-html-bkp-bad",
- "src": "recipe-maintainers/custom-html-bkp-bad",
- # Pin: custom-html-bkp-bad main @ 2026-06-02 (b6fe99de).
- # No pre_backup hook → marker never seeded before backup → snapshot has no ci-marker.txt.
- # pre_restore writes "mutated". After restore, marker is MISSING or "mutated" → FAIL → restore=RED.
- # install+backup PASS; upgrade=skip (no version tags yet).
- "ref": "b6fe99de41601f9e51bc7ea5b6072f0c3f56cdc3",
+ "recipe": "custom-html-rst-bad",
+ "src": "recipe-maintainers/custom-html-rst-bad",
+ # Pin: custom-html-rst-bad main @ 2026-06-02 (9a73a184).
+ # No pre_backup hook → backup snapshot has no ci-marker.txt.
+ # pre_restore writes "mutated". After restore: marker stays "mutated" → FAIL → restore=RED.
+ # install+backup PASS (no test_backup.py in cc-ci dir); upgrade=skip (no version tags).
+ "ref": "9a73a184e739691bc6a621a5f1e6efc799743c5b",
"expected_green": False,
"stages": "install,backup,restore,custom",
"failing_tier": "restore",