12 Files In, 4 Out: The Secret-Scanning Gate Between Claude Code's Audit Memos and My Obsidian Wiki A developer built wiki-sync-improvements.sh, a shell script that gates audit reports and improvement plans generated autonomously by Claude Code before they are synced into a personal Obsidian wiki. The script filters 12 Markdown files down to 4 eligible audit/plan/curation documents and aborts the entire sync if any candidate matches an API-key pattern such as sk-* or ghp_*, rather than dropping only the offending file. It defaults to a dry-run preview and writes conflicting files as timestamped copies instead of overwriting existing notes. Last time I wrote about handing a monitoring script exactly one root command https://zenn.dev/bokuwalily/articles/dasd-guard-scoped-sudo . This post is the same question ― what do you let unattended automation touch? ― aimed at a different target: the plumbing that carries audit reports and improvement plans written by Claude Code itself into my human-facing Obsidian wiki. The gate I ended up with takes 12 Markdown files as input, lets 4 through, and refuses to write anything at all if a single one of them contains something that looks like an API key. ~/.claude/improvements/ accumulates audit reports and improvement plans that Claude Code writes during autonomous operation. Look inside and you find entries like this excerpt from audit-2026-05-29.md : | project | 状態 | 推奨アクション | |---|---|---| | seo-affiliate-site 🔴 | 63 files 未コミット(Like / コメント / AdSense 等のマネタイズ実装が宙吊り) | 機能単位で分割コミット | | closet-os 🟢 | clean / 直近活発 | .env.production.local 1.9KB は gitignore 済だがバックアップ運用注意 | That's personal project names, implementation progress, and even the existence of .env files, all spelled out. Separate from the pipeline that turns conversation logs into long-term memory, there is a distinct risk here: documents the AI generates on its own can carry secrets and personal information . A fragment of an API key pasted during debugging ending up quoted in an audit memo is an entirely plausible accident. I needed something mechanical that would stop these memos before they got poured into the wiki. That's wiki-sync-improvements.sh . The comment block at the top of the script summarizes the whole design. wiki-sync-improvements.sh ~/.claude/improvements/ の個別 plan/audit/curation docs を ~/Documents/claude-obsidian/wiki/meta/improvements/ に同期する。 対象: audit- .md / -plan- .md / -curation- .md 除外: log.md 巨大 , README.md, next-session-todo.md など 秘密スキャン: sk- / ghp が含まれていれば sync 中止 使い方: wiki-sync-improvements.sh dry 変更プレビューのみ wiki-sync-improvements.sh apply 実際に書き込む The reason log.md is excluded is visible in the numbers. On my machine it's 2,316 lines and roughly 228 KB ― a chronological log, not something that fits the wiki's one-note-per-file granularity. Target filtering is a shell case pattern. for f in "$SRC"/ .md; do base="$ basename "$f" " case "$base" in audit- .md| -plan- .md| -curation- .md candidates+= "$f" ;; esac done The secret scan looks at every candidate first, and only then decides whether to stop. SECRET RE=' sk- A-Za-z0-9 - {16,}|ghp A-Za-z0-9 {20,} ' secret hits= for f in "${candidates @ }"; do if grep -E -q "$SECRET RE" "$f"; then secret hits+= "$f" fi done if ${ secret hits @ } -gt 0 ; then echo " abort secrets detected — sync stopped" &2 for h in "${secret hits @ }"; do echo " - $h" &2; done exit 2 fi Note: This is deliberately not "drop only the files that contain a secret." A single hit halts every candidate. Allowing partial syncs would force the script to make the call "skip the one file with the secret, pass the rest through" ― and a mistake in that call is the scariest failure mode here. So the threshold has exactly one setting: stop everything. Writing defaults to dry. MODE="${1:-dry}" means running with no argument writes nothing and only prints a preview. In apply mode, if a file with the same name already exists at the destination, the contents are compared cmp -s , effectively a hash comparison ; a match is skipped, and a difference is written out as a separate timestamped file ― coexistence, not overwrite. if cmp -s "$tmp" "$out"; then rm -f "$tmp" skipped=$ skipped+1 continue fi stamped="$DST/${base%.md}.$ ts suffix .md" mv "$tmp" "$stamped" Running dry locally produced this: $ ~/.claude/scripts/wiki-sync-improvements.sh dry dry would write timestamped: audit-2026-05-29.md - meta/improvements/audit-2026-05-29.