From f20a8d9df8a4fabba84749a8416e82a8863005f1 Mon Sep 17 00:00:00 2001 From: vk Date: Thu, 3 Sep 2026 00:45:27 +0530 Subject: [PATCH 1/6] feat(overlay): add --check for read-only drift detection vstack overlay copies claude/ into a repo's .claude/ once; nothing reports when that repo drifts behind the template afterward. overlay.sh --check (also `vstack overlay --check `) diffs the destination against every unconditionally-copied file, the two seed_tmpl targets, and the .conductor/settings.toml pin, writes nothing, and exits 1 if anything is stale or missing. --- README.md | 8 ++++ bin/vstack | 4 ++ overlay.sh | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 133 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c10f9f..502f4fc 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,14 @@ only lane a cloud sandbox without your home directory can reach): | `claude/security.yml.tmpl` | `.github/workflows/security.yml` | seeded if absent | | `claude/dependabot.yml.tmpl` | `.github/dependabot.yml` | seeded if absent | +A repo overlaid at commit X keeps that commit's hooks, agents, commands, skills and scanner +until someone re-runs overlay — nothing else reports the staleness. `vstack overlay --check +` (or `./overlay.sh --check `) diffs the destination against the "always +overwritten" file list above plus the two seeded templates and the `.conductor/settings.toml` +pin, writes nothing, and prints one line per drifted file followed by `overlay --check: N +stale, M repo-owned diffs, K missing`. Exit 0 only when N and K are both 0; repo-owned template +edits (M) never fail the check. + ## Day to day | Command | What it does | diff --git a/bin/vstack b/bin/vstack index 10d2750..869ef26 100755 --- a/bin/vstack +++ b/bin/vstack @@ -14,6 +14,10 @@ commands: install [args...] run the repo's install.sh (args passed through, e.g. --dry-run) doctor [args...] run ~/.config/agents/bin/doctor (args passed through, e.g. --drift) overlay run the repo's overlay.sh against + overlay --check + read-only: report drift against a repo already overlaid (stale + copies, repo-owned template edits, missing files, conductor pin + behind), write nothing, exit 1 if any file is stale or missing verify run the repo's .claude/verify.sh test run tests/auto-trigger.sh (live skill-firing regression; needs claude auth, ~15 min) trust [repo-dir] [--yes] diff --git a/overlay.sh b/overlay.sh index 8df3095..7ff820e 100755 --- a/overlay.sh +++ b/overlay.sh @@ -6,11 +6,26 @@ # `.claude/` overlay is the ONLY config lane that reaches it. Run this in every repo you # dispatch work to from your phone. # -# Usage: ./overlay.sh [target-repo-dir] (default: $PWD) +# Usage: ./overlay.sh [--check] [target-repo-dir] (default: $PWD) +# --check: report drift against a repo already overlaid, write nothing, exit 1 if stale set -euo pipefail SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -DEST="${1:-$PWD}" + +# --check may come before or after the dest arg (`overlay.sh --check repo` and the vstack +# dispatcher's `overlay --check` both need to work), so it is pulled out of the +# positional list rather than assumed to be $1. Every other positional arg — today there is +# only ever one — passes through untouched, which is what keeps a plain `./overlay.sh ` +# byte-identical to before this flag existed. +CHECK=0 +ARGS=() +for a in "$@"; do + case "$a" in + --check) CHECK=1 ;; + *) ARGS+=("$a") ;; + esac +done +DEST="${ARGS[0]:-$PWD}" # -e, not -d: inside a git worktree .git is a file pointing at the real git dir. The -d test # rejected every Conductor workspace, which is precisely where this needs to run — Conductor @@ -18,6 +33,110 @@ DEST="${1:-$PWD}" [ -e "$DEST/.git" ] || { echo "error: $DEST is not a git repo or worktree" >&2; exit 1; } [ -f "$SRC/claude/settings.json" ] || { echo "error: run from the vstack repo" >&2; exit 1; } +# --check stops here, before anything below that writes. It walks the exact same file lists +# overlay would copy — unconditional copies, seed_tmpl targets, the conductor pin — but only +# ever reads, so a repo can be checked in CI or a cron without risking the write path neither +# of those callers reviewed. +if [ "$CHECK" -eq 1 ]; then + STALE=0 + OWNED=0 + MISSING=0 + + #