CLAUDE_INGEST_SKIP_PROJECTS takes filepath.Match globs tested against the project directory NAME, and a matching directory is skipped during the directory WALK — so its files are never opened and their content never enters the process. That is a stronger property than filtering after reading, and the difference is observable: without SkipDir, WalkDir lists the directory's entries, and an unreadable one aborts the entire walk, silently stopping ingestion for every other project. Globs rather than exact names because a project directory is derived from the session's working directory, so a git worktree yields an ephemeral name no exact-match list could keep up with. An unparseable pattern refuses to run. filepath.Match returns an error ALONGSIDE a false match, so a bad pattern checked per-file would ingest the directory while the log complained — the failure nobody notices. Validated once, up front, in both entry points. This complements the client-name scrubber rather than replacing it. The scrubber drops individual turns whose content matches a client identity; this excludes whole workspaces whose content is confidential in ways no pattern reliably recognises. Learnings from excluded sessions are captured deliberately through the session-close ritual instead. Recorded caveat: the project directory reflects the session's working directory, NOT the repositories it touches. A session run from ~/dev that reads an excluded repo is not excluded by this. The scrubber remains the control for content; this is a control for workspaces. A mutation test earned the strongest assertion here: the original tests could not distinguish SkipDir from filtering after the walk, because neither opens files. The unreadable-directory test can. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>