Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
36 changes: 34 additions & 2 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <dir>'` 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
Expand Down
42 changes: 42 additions & 0 deletions scripts/site-monitor/check-copy-paths.sh
Original file line number Diff line number Diff line change
@@ -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 <copy-paths.conf> [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
6 changes: 6 additions & 0 deletions scripts/site-monitor/tests/fixtures/copy-paths/covered.conf
Original file line number Diff line number Diff line change
@@ -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/**
7 changes: 7 additions & 0 deletions scripts/site-monitor/tests/fixtures/copy-paths/missing.conf
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Comments and public=* lines only: what a truncated download could look like.
public=* include **/README*
5 changes: 5 additions & 0 deletions scripts/site-monitor/tests/fixtures/copy-paths/sites.json
Original file line number Diff line number Diff line change
@@ -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" }
]
78 changes: 78 additions & 0 deletions scripts/site-monitor/tests/test-check-copy-paths.sh
Original file line number Diff line number Diff line change
@@ -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}"
2 changes: 2 additions & 0 deletions scripts/site-monitor/tests/test-run-monitor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}/.."
Expand Down
Loading