From f495b08f4268855b5c219ae0c1ca291283e64f98 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Fri, 12 Jun 2026 06:26:20 -0700 Subject: [PATCH] harness: gitignore tmp/ + add promotion check to /save and /scc - .gitignore: ignore root tmp/ (temp/ and .claude/tmp/ were already ignored; root tmp/ was not, which is how scratch got committed and needed cleanup). - New .claude/scripts/tmp-promotion-check.sh: advisory, read-only, never blocks. Scans the gitignored scratch dirs (tmp/, temp/, .claude/tmp/) and flags files worth graduating (scripts, substantial docs, session-log-referenced) before they're lost to cleanup. Silent when scratch is empty. - /save (Phase 4) and /scc (new step 2) run the check before sync.sh, pointing at .claude/TEMP_GRADUATION.md for the graduate-vs-delete decision. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/commands/save.md | 12 +++++ .claude/commands/scc.md | 8 +-- .claude/scripts/tmp-promotion-check.sh | 67 ++++++++++++++++++++++++++ .gitignore | 1 + 4 files changed, 85 insertions(+), 3 deletions(-) create mode 100755 .claude/scripts/tmp-promotion-check.sh diff --git a/.claude/commands/save.md b/.claude/commands/save.md index 7f98481..b4313cd 100644 --- a/.claude/commands/save.md +++ b/.claude/commands/save.md @@ -102,6 +102,18 @@ not on every save. ## Phase 4 — Sync +First, run the **promotion check** — the scratch dirs (`tmp/`, `temp/`, `.claude/tmp/`) +are gitignored, so anything in them is invisible to git and lost on cleanup. This is +advisory and never blocks: + +```bash +bash .claude/scripts/tmp-promotion-check.sh +``` + +If it flags `[GRADUATE?]` candidates, graduate the keepers per `.claude/TEMP_GRADUATION.md` +(`git mv` into `scripts/` / `clients//reports/` / `projects/

/tools/`) **before** the +sync sweeps the commit. Pure scratch can be left or deleted. Then sync: + ```bash bash .claude/scripts/sync.sh ``` diff --git a/.claude/commands/scc.md b/.claude/commands/scc.md index 37dcf1b..0a9c171 100644 --- a/.claude/commands/scc.md +++ b/.claude/commands/scc.md @@ -9,14 +9,16 @@ Quick command to save session log, stage everything, and push to Gitea in one sh - **Per-session-unique filename (mandatory)** — concurrent sessions share this worktree, so never use the bare `YYYY-MM-DD-session.md`. Use `YYYY-MM-DD--.md`; collision-guard + same-session-append rules are in `/save` (`save.md`). - Include: summary, credentials (unredacted), infrastructure, commands, files changed, pending tasks -2. **Commit + push (locked, rebase-safe)** - Run `bash .claude/scripts/sync.sh`. This is the single serialized git path: it takes the per-machine sync lock (so it can't interleave with another session's sync/commit), reconciles git identity to `identity.json`, stages changes, commits, fetch + rebase, pushes — ClaudeTools then vault. +2. **Promotion check (advisory)** - Run `bash .claude/scripts/tmp-promotion-check.sh`. The scratch dirs (`tmp/`, `temp/`, `.claude/tmp/`) are gitignored, so anything there is invisible to git and lost on cleanup. If it flags `[GRADUATE?]` candidates, `git mv` the keepers to a permanent home (`scripts/` / `clients//reports/` / `projects/

/tools/`) per `.claude/TEMP_GRADUATION.md` before committing. Never blocks — pure scratch can be left or deleted. + +3. **Commit + push (locked, rebase-safe)** - Run `bash .claude/scripts/sync.sh`. This is the single serialized git path: it takes the per-machine sync lock (so it can't interleave with another session's sync/commit), reconciles git identity to `identity.json`, stages changes, commits, fetch + rebase, pushes — ClaudeTools then vault. - **Do NOT** run raw `git add -A` / `git commit` / `git push origin main` here — that bypasses the lock AND the fetch+rebase (the old flow raced and would reject on a stale push). - If `sync.sh` **exits 75**, another sync is in progress: report "sync deferred — your log is saved locally and will sync on the next run"; do not claim pushed. - Note: the discrete `scc:`-prefixed message is dropped in favour of one locked git path (commit lands under `sync.sh`'s auto message). If a custom message matters, revisit later (e.g. a `-m` arg on `sync.sh`). -3. **Report** - Confirm what was saved, committed, and pushed (or deferred) +4. **Report** - Confirm what was saved, committed, and pushed (or deferred) -4. **Reaffirm roles** - After push, briefly restate: +5. **Reaffirm roles** - After push, briefly restate: - You are a COORDINATOR, not an executor - Delegate: DB -> Database Agent, code -> Coding Agent, git -> Gitea Agent, tests -> Testing Agent - Do yourself: simple responses, reading 1-2 files, planning, decisions diff --git a/.claude/scripts/tmp-promotion-check.sh b/.claude/scripts/tmp-promotion-check.sh new file mode 100755 index 0000000..aecd472 --- /dev/null +++ b/.claude/scripts/tmp-promotion-check.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# Purpose: Advisory pre-save/commit check. The scratch dirs (tmp/, temp/, .claude/tmp/) +# are gitignored, so anything in them is INVISIBLE to git and will be lost on +# cleanup. Before a /save or /scc, surface what's sitting there and flag the +# files worth GRADUATING to a permanent home (per .claude/TEMP_GRADUATION.md). +# Usage: bash .claude/scripts/tmp-promotion-check.sh +# Behavior: read-only, never blocks. Always exits 0. Prints nothing when scratch is empty. +# Origin: added 2026-06-12 (wired into /save + /scc). See feedback in TEMP_GRADUATION.md. + +set -u + +ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" +cd "$ROOT" || exit 0 + +SCRATCH_DIRS=(tmp temp .claude/tmp) + +# Collect files across the scratch dirs (skip the dirs themselves; ignore .gitkeep). +mapfile -t FILES < <( + for d in "${SCRATCH_DIRS[@]}"; do + [ -d "$d" ] || continue + find "$d" -type f ! -name '.gitkeep' 2>/dev/null + done +) + +[ "${#FILES[@]}" -eq 0 ] && exit 0 # nothing in scratch — stay silent + +echo "[INFO] Promotion check: ${#FILES[@]} file(s) in scratch dirs (gitignored — NOT committed)." +echo " Graduate anything worth keeping before it's lost. Guide: .claude/TEMP_GRADUATION.md" + +candidates=0 +for f in "${FILES[@]}"; do + base="$(basename "$f")" + reason="" + + # Script-like files: reusable automation worth a permanent home. + case "$base" in + *.py|*.sh|*.ps1|*.psm1|*.js|*.rb|*.pl) reason="script" ;; + esac + + # Substantial docs (audit reports, dossiers) — size threshold ~4 KB. + if [ -z "$reason" ]; then + case "$base" in + *.md|*.csv) + sz=$(wc -c < "$f" 2>/dev/null || echo 0) + [ "${sz:-0}" -ge 4096 ] && reason="doc ($((sz/1024))KB)" + ;; + esac + fi + + # Referenced in a session log → clearly load-bearing. + if grep -rqlF "$f" session-logs/ clients/ projects/ 2>/dev/null; then + reason="${reason:+$reason, }referenced" + fi + + if [ -n "$reason" ]; then + echo " [GRADUATE?] $f ($reason)" + candidates=$((candidates + 1)) + fi +done + +if [ "$candidates" -eq 0 ]; then + echo " No graduation candidates (looks like pure scratch — safe to leave or delete)." +else + echo " -> $candidates candidate(s). Move with: git mv /reports/|projects/

/tools/>" +fi + +exit 0 diff --git a/.gitignore b/.gitignore index d0db723..eae03c9 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ tmp-remediation/ *.log *.bak .claude/tmp/ +tmp/ # Live secrets / tokens — never commit .token