diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index d0dcd7c..30ad478 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -26,3 +26,11 @@ jobs:
- name: Run site-monitor tests
run: bash scripts/site-monitor/tests/run-tests.sh
+
+ # Not hermetic: reads claude-config's conf at main (claude-config#725).
+ # curl -f and the checker both fail on a failed or empty download.
+ - name: Check every site has a copy-paths.conf entry
+ run: |
+ curl -fsSL --retry 3 -o "${RUNNER_TEMP}/copy-paths.conf" \
+ https://raw.githubusercontent.com/smartwatermelon/claude-config/main/copy-paths.conf
+ bash scripts/site-monitor/check-copy-paths.sh "${RUNNER_TEMP}/copy-paths.conf"
diff --git a/docs/STATUS.md b/docs/STATUS.md
index d93bf5f..eb46645 100644
--- a/docs/STATUS.md
+++ b/docs/STATUS.md
@@ -2,7 +2,7 @@
**As of 2026-09-16, with 2026-10-01, 2026-10-02 (three), 2026-10-05
(three), 2026-10-06, 2026-10-06/07, 2026-10-07 evening, 2026-10-08,
-2026-10-12 and 2026-10-13 to 10-16 updates below.**
+2026-10-12, 2026-10-13 to 10-16 and 2026-10-19 updates below.**
Point-in-time snapshot of the infrastructure backlog
(`docs/superpowers/specs/2026-09-01-infrastructure-backlog-design.md`). The
design doc is authoritative on *what* each item is and why; this file records
@@ -512,7 +512,39 @@ or be explicitly parked with a resume note, by then — see the tracker.
- **Auto mode pilot on ASIAGO** (`claude-wrapper#139`, open), with
per-machine settings layered over the shared file (`claude-wrapper#138`).
- **Open:** `claude-config#731` (the secret hook misses
- `op item get --format json`).
+ `op item get --format json`). Closed 2026-10-19, below.
+
+**2026-10-19 update (Pacific):**
+
+- **Hooks, all live on ASIAGO:**
+ - `claude-config#745` closes `#731`: the secret-leak hook denies
+ `op item get` forms that print values (`--format json`, `OP_FORMAT=json`,
+ `--reveal`, `--fields`, `--otp`) and `op document get` without
+ `--out-file`; the redactor replaces such output. No rotation needed for
+ the 10-15 exposure (prefixes only).
+ - `#746` closes `#742`: a quoted `cd` in a literal `printf`/`echo`
+ argument no longer denies.
+ - `#749` closes `#637`: commits on main through `env -C` or `xargs` are
+ blocked. Remaining parse gaps: `#750`.
+ - `#751` closes `#744`: the Pangram copy guard accepts the synced
+ `personify~gN` path (the `~gN` suffix is the sync generation and will
+ change) and no longer reads `sort -k2` as a kind flag.
+ - `#748` closes `#681`: codebase review runs with `--tools`,
+ `--allowedTools` and `--strict-mcp-config`, like the arbiter.
+ - `dotfiles#442` closes `#434`: the `gh` wrapper finds the `gh api`
+ endpoint past value-taking flags and blocks when it cannot.
+- **github-workflows auto-merges its own Dependabot PRs** (`#197`), pinned to
+ `@dependabot-auto-merge-v2`. Every fleet caller pins a hand-moved tag, so a
+ merge there reaches no other repo.
+- **CodeQL default setup on 28 more repos** (`dev-env#166`, open for two
+ decisions: Code Security for the 8 private NOS repos, and a digest check).
+- **"Require branches to be up to date" is off on all 37 protected repos**,
+ by decision: it cost a CI re-run per merge. This reverses the W0 exemplar's
+ `strict: true`. Required checks are unchanged.
+- **dev-env CI checks `sites.json` against claude-config's
+ `copy-paths.conf`** (`dev-env#210`, closes `claude-config#725`).
+- **Open:** `claude-config#747` (a quoted `'; cd
'` moves the gate's
+ route), `#750`, `#732` (fast CI for `known-runtime.txt`-only PRs).
**Kanban tracker: GitHub Issues + a Project board in `smartwatermelon/dev-env`**,
using the org's `Priority`/`Target date`/`Effort` issue fields. Board columns
diff --git a/scripts/site-monitor/check-copy-paths.sh b/scripts/site-monitor/check-copy-paths.sh
new file mode 100755
index 0000000..9901946
--- /dev/null
+++ b/scripts/site-monitor/check-copy-paths.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Fails when a sites.json repo has no include line in copy-paths.conf. Exit 0 ok, 1 repo missing, 2 bad input.
+set -uo pipefail
+unset CDPATH
+
+HERE="$(CDPATH='' cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+CONF="${1:-}"
+SITES="${2:-${HERE}/sites.json}"
+
+die() {
+ echo "check-copy-paths: $1" >&2
+ exit 2
+}
+
+[[ -n "${CONF}" ]] || die "usage: check-copy-paths.sh [sites.json]"
+[[ -r "${CONF}" ]] || die "cannot read ${CONF}"
+[[ -r "${SITES}" ]] || die "cannot read ${SITES}"
+
+# Fail closed: a truncated or wrong download must not pass, so the conf needs at least one owner/name include line.
+included="$(awk '$1 ~ /^[^#=\/]+\/[^\/]+$/ && $2 == "include" { print $1 }' "${CONF}" | sort -u)"
+[[ -n "${included}" ]] || die "${CONF} has no owner/name include lines; refusing to pass on an empty or unreadable conf"
+
+if ! jq -e 'type == "array" and length > 0 and all(.[]; (.repo | type) == "string")' "${SITES}" >/dev/null 2>&1; then
+ die "${SITES} is not a non-empty array of objects with a string .repo"
+fi
+repos="$(jq -r '[.[].repo] | unique | .[]' "${SITES}")" || die "cannot list repos in ${SITES}"
+
+missing=0
+while IFS= read -r repo; do
+ [[ -n "${repo}" ]] || continue
+ if ! grep -qxF -- "${repo}" <<<"${included}"; then
+ echo "check-copy-paths: ${repo} is in ${SITES##*/} but has no include line in ${CONF##*/}" >&2
+ missing=1
+ fi
+done <<<"${repos}"
+
+if [[ "${missing}" -ne 0 ]]; then
+ echo "check-copy-paths: add include lines for the repos above to smartwatermelon/claude-config copy-paths.conf" >&2
+ exit 1
+fi
+echo "check-copy-paths: every repo in ${SITES##*/} has a copy-paths.conf entry"
+exit 0
diff --git a/scripts/site-monitor/tests/fixtures/copy-paths/covered.conf b/scripts/site-monitor/tests/fixtures/copy-paths/covered.conf
new file mode 100644
index 0000000..49705b2
--- /dev/null
+++ b/scripts/site-monitor/tests/fixtures/copy-paths/covered.conf
@@ -0,0 +1,6 @@
+# Every repo in sites.json has an include line.
+public=* include **/README*
+
+owner-a/site-a include index.html
+owner-a/site-a exclude vendor/**
+owner-b/site-b include src/**
diff --git a/scripts/site-monitor/tests/fixtures/copy-paths/missing.conf b/scripts/site-monitor/tests/fixtures/copy-paths/missing.conf
new file mode 100644
index 0000000..32aa2a7
--- /dev/null
+++ b/scripts/site-monitor/tests/fixtures/copy-paths/missing.conf
@@ -0,0 +1,7 @@
+# owner-b/site-b is missing. Its only lines are an exclude, a comment, and a near-miss name, none of which count.
+public=* include **/README*
+
+owner-a/site-a include index.html
+owner-b/site-b exclude vendor/**
+# owner-b/site-b include index.html
+owner-b/site-b-old include index.html
diff --git a/scripts/site-monitor/tests/fixtures/copy-paths/no-entries.conf b/scripts/site-monitor/tests/fixtures/copy-paths/no-entries.conf
new file mode 100644
index 0000000..bb83e1b
--- /dev/null
+++ b/scripts/site-monitor/tests/fixtures/copy-paths/no-entries.conf
@@ -0,0 +1,2 @@
+# Comments and public=* lines only: what a truncated download could look like.
+public=* include **/README*
diff --git a/scripts/site-monitor/tests/fixtures/copy-paths/sites.json b/scripts/site-monitor/tests/fixtures/copy-paths/sites.json
new file mode 100644
index 0000000..93335ce
--- /dev/null
+++ b/scripts/site-monitor/tests/fixtures/copy-paths/sites.json
@@ -0,0 +1,5 @@
+[
+ { "url": "https://a.example", "repo": "owner-a/site-a" },
+ { "url": "https://b.example", "repo": "owner-b/site-b" },
+ { "url": "https://b2.example", "repo": "owner-b/site-b" }
+]
diff --git a/scripts/site-monitor/tests/test-check-copy-paths.sh b/scripts/site-monitor/tests/test-check-copy-paths.sh
new file mode 100755
index 0000000..4019489
--- /dev/null
+++ b/scripts/site-monitor/tests/test-check-copy-paths.sh
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+# check-copy-paths.sh against fixture confs: covered, missing, and each input that must fail closed.
+set -uo pipefail
+unset CDPATH
+
+HERE="$(CDPATH='' cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+SCRIPT="${HERE}/../check-copy-paths.sh"
+FIX="${HERE}/fixtures/copy-paths"
+WORK="/tmp/site-monitor-copy-paths-test-$$"
+mkdir -p "${WORK}"
+trap 'rm -f "${WORK}"/*; rmdir "${WORK}"' EXIT
+
+fail=0
+_pass() { echo " PASS: $1"; }
+_fail() { echo " FAIL: $1" >&2; fail=1; }
+check() {
+ local name="$1"
+ shift
+ if "$@"; then _pass "${name}"; else _fail "${name}"; fi
+}
+check_not() {
+ local name="$1"
+ shift
+ if "$@"; then _fail "${name}"; else _pass "${name}"; fi
+}
+# expect_exit WANT ARGS...: run the checker with ARGS and compare its exit status.
+expect_exit() {
+ local want="$1" rc=0
+ shift
+ "${CHECK_TEST_BASH:-bash}" "${SCRIPT}" "$@" >"${WORK}/out" 2>"${WORK}/err" || rc=$?
+ if [[ "${rc}" -eq "${want}" ]]; then _pass "exit ${want}"; else _fail "exit ${rc}, want ${want}"; fi
+}
+
+echo "-- every repo has an include line: exit 0"
+expect_exit 0 "${FIX}/covered.conf" "${FIX}/sites.json"
+check "says all covered" grep -q 'every repo' "${WORK}/out"
+
+echo "-- a repo with only an exclude, a comment, or a near-miss name: exit 1, named once"
+expect_exit 1 "${FIX}/missing.conf" "${FIX}/sites.json"
+check "names the missing repo" grep -qF 'owner-b/site-b is in' "${WORK}/err"
+named="$(grep -cF 'owner-b/site-b is in' "${WORK}/err")" || named=0
+check "named once despite two sites" test "${named}" -eq 1
+check_not "covered repo not named" grep -qF 'owner-a/site-a' "${WORK}/err"
+
+echo "-- a conf with no owner/name include lines fails closed: exit 2"
+expect_exit 2 "${FIX}/no-entries.conf" "${FIX}/sites.json"
+check "says why" grep -q 'no owner/name include lines' "${WORK}/err"
+
+echo "-- an empty conf (a failed download) fails closed: exit 2"
+: >"${WORK}/empty.conf"
+expect_exit 2 "${WORK}/empty.conf" "${FIX}/sites.json"
+
+echo "-- a missing conf file: exit 2"
+expect_exit 2 "${WORK}/absent.conf" "${FIX}/sites.json"
+check "says why" grep -q 'cannot read' "${WORK}/err"
+
+echo "-- no conf argument: exit 2"
+expect_exit 2
+
+echo "-- a malformed sites.json: exit 2"
+printf '{"repo":"owner-a/site-a"}\n' >"${WORK}/bad-sites.json"
+expect_exit 2 "${FIX}/covered.conf" "${WORK}/bad-sites.json"
+
+echo "-- an empty sites.json array: exit 2"
+printf '[]\n' >"${WORK}/empty-sites.json"
+expect_exit 2 "${FIX}/covered.conf" "${WORK}/empty-sites.json"
+
+echo "-- the default sites.json is the real one next to the script"
+expect_exit 1 "${FIX}/covered.conf"
+check "reads real sites.json" grep -qF 'sites.json but has no include line' "${WORK}/err"
+
+# MIMOLETTE runs this directory under macOS /bin/bash 3.2, so repeat every case there when this host has it.
+if [[ -z "${CHECK_TEST_BASH:-}" ]] && /bin/bash -c '[[ ${BASH_VERSINFO[0]} -lt 4 ]]' 2>/dev/null; then
+ echo "-- every case again under /bin/bash"
+ CHECK_TEST_BASH=/bin/bash bash "${BASH_SOURCE[0]}" || fail=1
+fi
+
+exit "${fail}"
diff --git a/scripts/site-monitor/tests/test-run-monitor.sh b/scripts/site-monitor/tests/test-run-monitor.sh
index 7b7cb3e..124658e 100755
--- a/scripts/site-monitor/tests/test-run-monitor.sh
+++ b/scripts/site-monitor/tests/test-run-monitor.sh
@@ -3,6 +3,8 @@
set -uo pipefail
unset CDPATH
unset BASH_ENV GH_TOKEN GH_HOST GITHUB_TOKEN NTFY_TOPIC
+# An exported gh function (the dotfiles wrapper) beats the stub on PATH in run-monitor.sh, so drop it.
+unset -f gh
HERE="$(CDPATH='' cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DIR="${HERE}/.."