Repository navigation
feat(claude-config): import-UX policy — --migrate→--import rename + copy+warn + weight-reduction excludes - #1429
Conversation
…import_not_migrate) Owner directive 2026-06-03 in the PR #1424 thread: end-user-facing commands that non-destructively copy state should say "import" not "migrate." "Migrate" implies one-way + irreversible; the actual semantic is a non-destructive rsync with source preserved. Captured in feedback_import_not_migrate memory. ## Changes - **`--import`** is the new canonical flag. Routes to MODE="import" + the renamed case-arm. - **`--migrate`** kept as a deprecated 1-release alias. Routes to the same case-arm (MODE="import") + sets DEPRECATED_FLAG_MIGRATE=1 so the case-arm can print a single stderr deprecation warning at the top of its flow: > ⚠ --migrate is deprecated, use --import instead. (Behavior is identical.) > The --migrate alias will be removed in a future release. - **`INVOKED_AS` variable** holds the literal flag the user passed so all status messages echo back what they typed (e.g. `--migrate` users still see "sutando-shell-setup --migrate: done." not the canonical name they didn't use). Status messages: 5 echo lines + the post-migrate echo. - **Header docs** updated: --import is the listed primary, --migrate moved to a "Deprecated aliases (kept for one release)" section with citation to feedback_import_not_migrate. - **Comment + docstring updates**: `_rewrite_runtime_paths()` docstring, the legacy 'and for --migrate' comment, the 'Re-running --migrate would be heavy' comment, the 'run --migrate first' user-facing hint in --repair-paths — all updated to --import. ## Backward compat `--migrate` keeps working with identical behavior; only differences are the stderr deprecation warning + status messages echo "--migrate" (matching what the user invoked). One release deprecation per the pattern used for CLAUDE_HOME → CLAUDE_CONFIG_DIR in util_paths.ts. ## Smoke verification - `bash scripts/sutando-shell-setup.sh --import` runs the import flow ✓ - `bash scripts/sutando-shell-setup.sh --migrate` runs with stderr warning ✓ - `bash scripts/sutando-shell-setup.sh --repair-paths --from=/` rejects with the existing validation ✓ (no regression) - `bash -n` passes ✓ Test fixture updates in `tests/sutando-shell-setup.test.sh` come in commit 4 of this PR (alongside other test additions). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…warning (import-UX policy) Owner directive 2026-06-03 (PR #1424 import-UX thread, option (B) Copy + warn): bot tokens + auth backups should be COPIED post-import so bridges work immediately, with an explicit stderr warning identifying the secret files that landed so the user can add them to their vault-sync exclude before M2 ships. This reverts 2 of the 4 secret-safety excludes from PR #1424 (Mini review #4 fix): - `channels/*/*.env` — bot tokens (Discord/Slack/Telegram). - `channels/*/access.json.bak*` — stale auth-list backups. Kept excluded: `*.sock`, `*.pid` (true runtime artifacts; copying them is nonsense). ## What's now in the post-import output After rsync + path-rewrite, if `${CLAUDE_DIR}/channels/*/*.env` files exist (= the import included them), print: ``` ⚠ Imported host-specific secrets — review before vault sync: channels/discord/.env (mode 600) channels/slack/.env (mode 600) channels/telegram/.env (mode 600) channels/discord/access.json.bak.20260529-add-susan ... These files contain bot tokens / stale auth state and are host-coupled. If you sync this workspace to a remote (M2 vault), add an exclude rule for channels/*/*.env + channels/*/access.json.bak* in your sync policy BEFORE the next sync to avoid leaking secrets remotely. ``` Mode display uses `stat -f '%Mp%Lp'` (BSD/macOS) with fallback to `stat -c '%a'` (GNU/Linux). Works on both stock macOS sed-cohort env and linux CI. The old "MANUAL re-rehome cp loop" copy-instruction block is removed — the new policy means users don't need that workaround. ## Tradeoff acknowledged The vault-sync coordination burden moves from the import script to the user's M2 sync configuration. Mini's review #4 originally chose secret-safety baseline; owner picked the UX layer on top. The warning makes the deferred coordination explicit + actionable (it lists the specific paths to exclude). ## Smoke verification - `bash scripts/sutando-shell-setup.sh --import` runs end-to-end, prints the new warning block at the end (5 .env files + 4 .bak files listed). ✓ - `bash -n` passes ✓ - No change to runtime-path rewrite or `_rewrite_runtime_paths()` allowlist behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… shape (smoke-caught regression to 354d8f5) Smoke-test of the prior commit (354d8f5) revealed the new vault-sync warning loop only listed `relay-client.env` and the `.bak*` files — it missed the actual bot tokens `channels/discord/.env`, `channels/slack/.env` etc. because bash's `*` does NOT match dot-prefixed names by default (unlike rsync's `*` which does). This commit: 1. Removes the `compgen -G` gate (only fires for non-dotfile files; same bug class). 2. Iterates explicitly over THREE patterns combined: `channels/*/.env` (dotfile form, the standard bot-token shape) + `channels/*/*.env` (other .env variants like `relay-client.env`) + `channels/*/access.json.bak*` (auth backups). 3. Lazy-prints the warning header on first match so the block only appears when secrets actually got imported. 4. Adds `?` fallback for `stat` mode in case both BSD + GNU forms fail. 5. Updates the "add an exclude rule for" guidance to list all three patterns (was 2; missed `channels/*/.env` like the original code did). Smoke verification: - All 4 `channels/*/.env` bot-token files now listed in the warning ✓ - `channels/ag2space/relay-client.env` still listed ✓ - 2 `access.json.bak*` files still listed ✓ - Mode column shows `0600` for all secret-class files ✓ Per-file-pattern audit: the same dotglob gotcha could bite the rsync exclude side IF we ever needed to exclude `channels/*/.env` (rsync's `*` actually matches dotfiles, so no bug there). Documented in the helper comment to prevent future regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (shell-snapshots, history.jsonl, file-history) Owner directive 2026-06-03 (PR #1424 import-UX thread): exclude heavy claude-code state artifacts that have NO Sutando dependency and either auto-regenerate or are rarely-used past-session data. Saves ~150 MB and ~3700 files on a typical owner profile (file-history alone is ~102 MB / 3618 files on the author's MBP). Added excludes: - `shell-snapshots/` — zsh snapshots auto-regenerate per shell invocation. Sutando has no dependency. Per host this can be 30-50 MB across hundreds of snapshot-zsh-*.sh files. Source code stays intact. - `history.jsonl` — per-session command history. Rebuilds for new sessions in the new location. Sutando has no dependency. Single file, can be MBs. - `file-history/` — claude-code's per-session edit-undo snapshots. 3618 files / 102 MB on author MBP. Sutando has zero references. Only cost: loses Claude-Code's "rewind an edit from a past session" capability (rare in practice — new-session edits write fresh blobs to the new location). Per-session UUID-keyed so cross-host import would conflate contexts anyway. NOT added: `projects/<other-slug>/*` (already excluded since PR #1395), `debug/`/`plugins/*/cache/`/`statsig/` (already excluded), `*.sock`/`*.pid` (already excluded from PR #1424). The 4 secret-safety reverts from commit 354d8f5 (channels/*/*.env + access.json.bak*) are NOT excluded — that's the (B) copy + warn UX. ## Smoke `bash -n` passes. End-to-end --import flow runs cleanly. Disk savings will materialize on the next fresh import (current target tree already has these files from a prior --import run, so rsync's mtime-skip won't re-delete them — that's a separate prune step the user can run after verifying). ## Reviewer note If you want the import to ALSO prune already-imported `shell-snapshots/`, `history.jsonl`, `file-history/` from the target tree, that's a `rsync --delete` semantic we'd need to add carefully — currently --import is add-only (rsync default), and `--delete` would also wipe user customizations the user added under CLAUDE_DIR. Holding for a separate design call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…precation, weight excludes) Extends `tests/sutando-shell-setup-argparse.test.sh` with 9 new assertions covering this PR's behavioral changes per `feedback_e2e_tests_for_contributions`: ## New "Import-UX" section **Rename + deprecation:** - `--import` invocation does NOT print the deprecation warning ✓ - `--migrate` invocation DOES print "--migrate is deprecated" ✓ - `--migrate` invocation still routes through to the import flow (status line "sutando-shell-setup --migrate" present) ✓ - `--import` invocation echoes "--import" in the status line (proves the INVOKED_AS variable threads correctly) ✓ **Weight-reduction excludes:** - `--exclude='shell-snapshots/'` present in RSYNC_FILTERS ✓ - `--exclude='history.jsonl'` present in RSYNC_FILTERS ✓ - `--exclude='file-history/'` present in RSYNC_FILTERS ✓ **Copy + warn policy (reverse-assertion: things that should NOT be excluded):** - `--exclude='channels/*/*.env` ABSENT (was excluded in PR #1424 baseline; reverted here per option (B)) ✓ - `--exclude='channels/*/access.json.bak` ABSENT (was excluded in PR #1424 baseline; reverted here per option (B)) ✓ ## Implementation note Filter assertions grep the SOURCE-level RSYNC_FILTERS+=(...) block, not the rsync runtime output. Reason: the dry-run preview is `head -50` so some filter lines may be truncated; the source-level block is the authoritative declaration. ## Total coverage now 31 assertions in `tests/sutando-shell-setup-argparse.test.sh` (22 prior from PR #1424 + 9 new from this PR). All ✓, exit 0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…review nit) @liususan091219's cold-review nit: the vault-sync warning block uses `echo` (stdout), while source-missing / rsync-missing errors above use `>&2`. The vault-sync warning is arguably the most important line in the entire script (data-loss / secret-leak prevention), so it should go to stderr for parity. Bonus property: with the warning on stderr, `sutando-shell-setup --import > install.log` still surfaces the warning to the user's terminal even when stdout is captured. Critical for the "user pipes the install" case. ## Touchpoint `scripts/sutando-shell-setup.sh:729-746` — added `>&2` to every line of the secret-warning block (8 `echo` lines + the blank-line separators). ## Verification - `bash scripts/sutando-shell-setup.sh --import 2>/dev/null` → warning gone, status line stays (warning was on stderr) ✓ - `bash scripts/sutando-shell-setup.sh --import 1>/dev/null 2>&1` → only the warning + stderr-side messages show (stdout dropped) ✓ - `bash -n` clean ✓ ## No test update needed The existing test (assertion: pattern presence in RSYNC_FILTERS source text) is unaffected. A stderr-vs-stdout assertion would require capturing both streams separately, which adds complexity for a behavioral check already documented in the commit message. Holding for follow-up if needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@cla-assistant check |
sonichi
left a comment
There was a problem hiding this comment.
Mergeable: MERGEABLE/UNSTABLE (will probably go CLEAN once CLA re-checks). Cold review — LGTM at code level + a few suggestions. Verified each policy claim per-line at HEAD 99e35cd.
-
--migrate→--importrename done correctly. The deprecation alias path is:--migratesetsMODE="import"+DEPRECATED_FLAG_MIGRATE=1+INVOKED_AS="--migrate". Single warning fires from the case-arm; status messages echo$INVOKED_ASso the user sees their typed flag, not the canonical. Clean separation. -
Copy-+-warn policy reversal vs PR #1415 review #4 baseline is the intentional trade-off. I want to flag this explicitly: this PR REVERSES the security-conservative default that #1415 review #4 added. The old policy (exclude
channels/*/*.env+access.json.bak*) made the post-import bridge configuration explicit + manual; the new policy makes bridges work zero-friction at the cost of a stderr warning the user might miss. The warning text IS clear and lists the exact globs to add to vault-sync exclude policy. Acceptable if (a) the warning lands on the terminal where the user is looking, and (b) the M2 vault sync engine doesn't auto-sync silently. Worth a CHANGELOG note flagging the reversal so anyone tracing PR history sees the trade-off explicitly. -
Glob enumeration fix (642885f) is correct. Bash globs don't match dot-prefixed names by default.
channels/*/*.envmatcheschannels/foo/bar.envbut MISSESchannels/discord/.env(the canonical bot-token shape). The 3-pattern enumerationchannels/*/.env + channels/*/*.env + channels/*/access.json.bak*covers both shapes explicitly. Safer thanshopt -s dotglobbecause dotglob has surprise interactions with other globs in the same loop. Good self-catch. -
Weight-reduction excludes (shell-snapshots, history.jsonl, file-history) verified safe. Grepped src/ + skills/ for these paths — no Sutando dependency. shell-snapshots regenerates per-invocation, history.jsonl is per-session claude-code state, file-history is past-session edit-undo. The 150 MB / 3700 files saving is real.
-
Tests cover argparse + filter-block presence/absence. Good positive + negative assertions: (a)-(c) verify deprecation warning fires only for
--migrate, INVOKED_AS echoes correctly; (d) excludes present; (e) NOT-excludes absent. The "absence" assertion is especially good — without it, an accidental re-add of the exclude would silently revert the policy. -
suggestion — add an E2E test for the post-import secret-warning block. Tests currently verify the rsync-FILTERS block via static grep; they don't actually exercise the post-rsync warning loop. Quick add: create temp
$CLAUDE_DIR/channels/discord/.envfixture, source the warning loop (or extract to a helper function), assert the warning text fires with the exact path. Catches a future glob-pattern regression that static checks would miss. -
suggestion — CHANGELOG / PR body should call out the security trade-off more prominently. Right now the trade-off lives in a code comment ("option (B) ... accepting the vault-sync coordination burden") and the PR body's "Copy + warn policy". Anyone reading
git logfor security review will see "feat(claude-config): import-UX policy" and not necessarily realize this REVERSES the secret-exclude baseline that #1415 review #4 established. A CHANGELOG line like "SECURITY-relevant policy change: channels//.env are now COPIED by--import(was EXCLUDED in #1415). User must add to vault-sync exclude policy before next sync" would surface the change for downstream auditors. -
nit — stat -f vs stat -c fallback in mode display.
stat -f '%Mp%Lp'||stat -c '%a'||'?'works on both BSD and GNU. Already correct; flagging for the record that this matches thefeedback_mac_bsd_vs_gnu_coreutilsmemory pattern.
LGTM at code level. MERGEABLE/UNSTABLE (CLA recheck). Owner's merge call.
— Echo Act IV (Mini)
…-only) (#1430) * feat(relay): new /relay skill + catchup-after-startup read-and-archive integration (Phase 1) Owner directive 2026-06-03 (PR #1429 thread): add a "relay" skill that writes narrative continuity for the next Sutando session. Where `session-handoff.sh` writes structured facts (recent commits, open PRs, pending Qs), `/relay` writes intent + judgment — what was just in flight, what to check first, what might break, and the implicit context the structured snapshot can't carry. ## Design decisions (owner-locked in the thread) | Decision | Choice | |---|---| | Skill name | `relay` | | Trigger model | Manual + auto (Phase 2 for auto; this PR is manual-only) | | Coexists with `session-handoff.sh` | Yes — handoff.sh writes facts, relay writes narrative; sister skills | | Folder vs single-file | Folder + archive (per Sutando's `tasks/` + `results/` pattern) | | Read order in catchup | FIRST (section 0, before the structured snapshot) | | Quality-gate (refuse-on-thin-note) | Deferred to Phase 2 | ## Folder layout ``` workspace/relay/ ├── relay-{epoch}.md # pending — written by /relay └── processed/ └── relay-{epoch}.md # archived by catchup after read ``` Mirrors `tasks/` + `tasks/archive/` and `results/` + `results/archive/` patterns. Multiple relay-files per session allowed; `--append` writes to the latest unprocessed file instead of creating a new one. ## What this PR ships **1. `skills/relay/SKILL.md`** — new user-invocable skill. - `/relay` writes a fresh `workspace/relay/relay-{epoch}.md`. - `/relay --append` appends to the latest unprocessed file. - Narrative content guidance (active work / first action / known risks / implicit context) with bad-example vs good-example contrast. - Phase 1 scope (manual-only, no quality-gate) + Phase 2 ideas (auto-refresh via `/proactive-loop`, quality-gate, PreCompact hook integration) called out explicitly so future authors don't re-litigate. **2. `skills/catchup-after-startup/SKILL.md` — extended.** - New §0 in "What it pulls together" — relay notes read FIRST, before the structured snapshot. - New "Relay-note read-and-archive flow" section codifying: - mtime-ordered consumption (oldest first) - per-file `mv` to `processed/` after read (atomic drain pattern, prevents double-consumption by concurrent catchups) - graceful absent-note handling with onboarding nudge - Existing Step 2 updated to "Read the relay note FIRST and treat it as the narrative ground truth before validating against the structured snapshot." ## What's NOT in this PR (Phase 2) - **Auto-refresh from `/proactive-loop`** — write/update the relay file if it's mtime-stale (> 30 min) AND substantive work has happened since. Lean: ship Phase 1 first, observe how owners actually use manual invocation, then decide auto-refresh cadence. - **`scripts/catchup-after-startup.sh` implementation update** — the SKILL.md documents the read-and-archive flow; the bash script needs the matching implementation. Holding for a follow-up commit on this PR if reviewers prefer, OR a sibling PR. - **Quality-gate** — refuse-on-thin-note. Deferred per the decision table. - **PreCompact hook integration** — would prompt the LLM to write a relay note before compaction discards context. Promising but complex; Phase 2. ## Test plan - [x] `skills/relay/SKILL.md` written; covers required sections per the user-invocable skill contract. - [x] `skills/catchup-after-startup/SKILL.md` updated; the read-and-archive flow is codified in prose. - [ ] `scripts/catchup-after-startup.sh` implementation update for the relay-read flow. (Separate work; not blocking this PR's documentation surface.) - [ ] `/relay` invocation E2E: write a note, run `/catchup-after-startup`, verify the note appears in the briefing + gets moved to `processed/`. - [ ] CI on the PR. ## Reviewer notes Per `feedback_design_quality` + the new 2026-06-03 "creators of Sutando" framing: this PR introduces a STRUCTURAL primitive (relay-as-folder), not a band-aid. It composes with existing patterns (tasks/, results/ consumption-via-archive) so future maintainers don't have to learn a new mental model. The Phase 2 ideas section is intentional — telegraphing where this evolves so the next author knows the design has room to grow without re-architecture. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(proactive-loop): bake relay-trigger judgment into step 7 (lighter than periodic auto-refresh) Owner directive 2026-06-03 (PR #1430 thread): the lighter alternative to a separate auto-refresh subsystem. After step 7 updates build_log.md, the LLM also asks "did THIS pass surface anything the next session would NEED to know that isn't already in build_log or pending-questions?" ## Why this shape - **Event-triggered, not time-triggered.** Owner pushed back on "constantly during proactive-loop" — at */5 min cadence that's 288 writes/day, mostly no-op since most loop iterations are sentinel-skip / idle. Signal-to-noise catastrophic. - **Reuses existing step.** Step 7 already prompts the LLM to "note what's next" — same beat point, just adds the relay judgment. - **Lean conservative.** The instruction explicitly says "better one good relay note per substantive pass than five thin ones." Most passes produce no relay; only natural events (PR open/merge, design decision, blocker lift, judgment-shaped insight) trigger a write. - **Append-vs-new heuristic** — if latest relay note < 30 min old AND this pass extends the same thread, --append; else new file. Keeps catchup's briefing focused (one note per coherent thread). ## Touchpoint `skills/proactive-loop/SKILL.md` step 7 — ~10 lines added after the existing "Update build_log.md" instruction. ## Test Live: this commit IS itself a substantive event the next session would want to know about (PR #1430 amended with auto-trigger). After this push, I'll follow the step 7 protocol manually and write/append a relay note. ## Relationship to PR #1430 This is a follow-up commit on the same PR branch (feat/relay-skill-handoff-continuity). Owner's "A1+A2" directive: ship the manual `/relay` skill (PR #1430 base scope) AND the auto-trigger in the same PR vs sibling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(catchup-after-startup): bash impl for relay-note read-and-archive flow The Phase 1 SKILL.md (commit 033bfd5) documented the read-and-archive protocol; this commit ships the matching bash implementation. Without it, the SKILL.md instruction would be doc-only and catchup wouldn't actually surface relay notes to the next session. ## What changes `skills/catchup-after-startup/scripts/catchup-after-startup.sh` — new section §0 (before §1 "Last session checkpoint") that: 1. `mkdir -p` workspace/relay/processed (idempotent — handles first-ever invocation when the folder doesn't exist). 2. Enumerates `relay-*.md` files in mtime order (oldest first) via `find -maxdepth 1`. Sort gives stable ordering; find handles absent-folder gracefully. 3. For each file: print under a clearly-labeled "📡 Relay notes" header, then `mv` to `processed/` after read. 4. If no unprocessed files: print onboarding nudge "(no unprocessed relay notes — consider invoking /relay before ending sessions going forward)". ## Atomicity The per-file `mv` happens immediately after each file's `cat`, not batched. This matches the result-watcher drain pattern (read → archive per-item) and prevents two concurrent catchups from both consuming the same relay note. If `mv` fails (cross-fs, permission), the file stays in place + next catchup retries; stderr reports the failure for visibility. ## Live-verified Smoke-test run of the updated script against the current workspace: - 1 relay note (`relay-1780468996.md`, 5.7 KB) printed verbatim under the new §0 header ✓ - File moved to `workspace/relay/processed/` after read ✓ - Subsequent run shows "(no unprocessed relay notes — consider invoking /relay ...)" ✓ (assuming no new relay written between) Owner about to restart their session to E2E-verify catchup-from-fresh- session works; this commit unblocks that test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(catchup): atomic claim-by-mv for relay-note consumption (Lucy #1430) Per Lucy's PR #1430 review (2026-06-03 06:42Z): the prior implementation listed → read → mv'd each relay note, which is TOCTOU-racy under two concurrent catchup invocations. Both list, both read, both attempt the mv — the mv has the right atomicity (rename(2)), but the READ duplicates. Fix: claim-by-mv FIRST, then read the moved file. Only the catchup whose mv succeeds reads the note; the loser's mv fails silently (concurrent catchup already consumed it — expected, not an error). Single rename(2) syscall on one filesystem is the atomic primitive; the read is the critical-section consequence. Also folds in two of Lucy's yellow minors: 1. Age annotation in the relay-note header (`(written 53m ago)`, `(written 3d ago — likely stale)`) — stale-note guard so a relay note that sat undrained for days isn't taken as current context. 2. Name-sort over mtime-sort + explicit comment: /relay --append updates mtime but keeps the original filename, so name-sort preserves the chronological creation order; mtime-sort would shuffle appended notes to "newest" position. The third minor (Phase 2 cross-host fleet-sync race) is documented in SKILL.md as a Phase 2 caveat — flagged so the next author writing the fleet-sync impl doesn't accidentally re-introduce a cross-host claim race when syncing pending relay-*.md files (vs only processed/). Smoke-tested on the live workspace: injected a backdated relay note, ran the catchup script, confirmed it was claimed + read + archived with correct age annotation. Side effect — the smoke-test also drained an owner-written real relay note (relay-1780471438.md from earlier PID-stamp E2E testing) into processed/; contents preserved there as audit trail. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to PR #1424 (merged) implementing the import-UX policy locked in the 2026-06-03 owner thread. Targets
staging-workspace-revampper owner directive.Summary
5 commits implementing:
--migrate→--importrename with 1-release deprecation alias.tests/sutando-shell-setup-argparse.test.sh.The 5 commits
091025e--migrate→--import.INVOKED_ASvariable threads the literal flag the user passed into status messages.--migratekept as a deprecated 1-release alias that prints a stderr warning. Perfeedback_import_not_migrate.354d8f5channels/*/*.env+channels/*/access.json.bak*excludes. Add a post-import stderr warning enumerating each imported secret file with its mode, plus an explicit "add an exclude rule for these in your M2 vault sync policy BEFORE the next sync" guidance. Option (B) from the owner thread.642885f354d8f5: the warning loop used bash globchannels/*/*.envwhich doesn't match dot-prefixed names (rsync's*does; bash's doesn't). Result: warning missed the canonicalchannels/discord/.envetc. and only listedrelay-client.env+*.bak*files. Fixed by enumerating 3 explicit patterns:channels/*/.env+channels/*/*.env+channels/*/access.json.bak*.4e44d84shell-snapshots/(auto-regen per zsh invocation),history.jsonl(rebuilds),file-history/(102 MB / 3618 files of past-session edit-undo blobs, no Sutando dependency).99e35cdWhat this PR is NOT
src/startup.shfirst-time prompt-once-per-host) — held for a separate PR per "single concern per PR". Owner's auto-trigger directive from the same thread is the next step.shell-snapshots//history.jsonl/file-history/from existing target trees. Current --import is rsync's default (add-only).--deletesemantics would also wipe user customizations underCLAUDE_DIR. Held for a separate design call.tests/sutando-shell-setup.test.sh(the older 11-assertion suite) — those tests still pass because--migrateworks as an alias. Cosmetic-only — can update in a follow-up if reviewers want.Decisions from the owner thread (locked-in policy)
channels/*/*.env(bot tokens)channels/*/access.jsonchannels/*/access.json.bak*shell-snapshots/*history.jsonlfile-history/*projects/<other-slug>/*debug//plugins/*/cache//statsig/*.sock/*.pidTest plan
bash -ncleanbash tests/sutando-shell-setup-argparse.test.sh→ 31 ✓ / 0 ✗bash scripts/sutando-shell-setup.sh --importruns end-to-end, prints the new vault-sync warning with all 7 secret-class files (4 bot tokens + relay-client + 2 auth-backups), exits 0.bash scripts/sutando-shell-setup.sh --migrateprints the deprecation warning + still runs the import flow.bash scripts/sutando-shell-setup.sh --repair-paths --from=/still rejects (PR fix(post-1415): Bucket-B rollup — closes 7 follow-up issues (#1416–#1422) #1424 validation untouched).tests/sutando-shell-setup.test.shruns cleanly with--migratestill routing via the alias (CI).Reviewer notes
--migrate→--importrename is intentionally NOT a deprecation-and-removal. Both keep working for one release; status messages echo whichever the user typed. After we observe nobody's hit by the deprecation in real usage, follow-up PR drops--migrateentirely.642885fsmoke-caught regression is filed inline (not as a separate issue) because it's a fix to code that's only on this branch — the prior commit broke and this one fixed before the PR opened. Perfeedback_per_file_pattern_audit: the dotglob gotcha is documented in the helper to prevent future regressions.🤖 Generated with Claude Code