affaan-m/everything-claude-code/gateguard
- community
Checking that the SKILL.md file is still in the repository.
PreToolUse fact-forcing gate that denies the first Edit/Write/Bash (including MultiEdit and NotebookEdit) attempt until the agent presents concrete facts (importers, data schemas, verbatim user instruction), then allows retry; A/B-tested at +2.25 quality points. Use when enabling or configuring the GateGuard hook, exempting paths via env vars, or handling first-touch denials.
Install
npx vskill@latest install affaan-m/everything-claude-code/gateguardSKILL.md
GateGuard — Fact-Forcing Pre-Action Gate
A PreToolUse hook that forces Claude to investigate before editing. Instead of self-evaluation ("are you sure?"), it demands concrete facts. The act of investigation creates awareness that self-evaluation never did.
When to Activate
- Working on any codebase where file edits affect multiple modules
- Projects with data files that have specific schemas or date formats
- Teams where AI-generated code must match existing patterns
- Any workflow where Claude tends to guess instead of investigating
Core Concept
LLM self-evaluation doesn't work. Ask "did you violate any policies?" and the answer is always "no." This is verified experimentally.
But asking "list every file that imports this module" forces the LLM to run Grep and Read. The investigation itself creates context that changes the output.
Three-stage gate:
No competitor does all three. Most stop at deny.
Evidence
Two independent A/B tests, identical agents, same task:
| Task | Gated | Ungated | Gap | |
|---|---|---|---|---|
| Analytics module | 8.0/10 | 6.5/10 | +1.5 | |
| Webhook validator | 10.0/10 | 7.0/10 | +3.0 | |
| Average | 9.0 | 6.75 | +2.25 |
Both agents produce code that runs and passes tests. The difference is design depth.
Gate Types
Edit / MultiEdit Gate (first edit per file)
MultiEdit is handled identically — each file in the batch is gated individually; entries without their own file_path are gated as the call's file_path. NotebookEdit is gated as an Edit of its notebook_path: same class, checked key, credit and cap rules, the full questions (the cell change is not read), and never a comment-only pass.
The questions depend on the target's class (see Questions by target class). For code targets the Edit gate asks:
These four are the full set, asked whenever the change cannot be read with confidence. When it can (see Questions fit the change), an edit that touches no public declaration asks instead for the call sites in the file or its module that rely on the changed behaviour, and item 3 is dropped when the change handles no data.
Write Gate (first new file creation)
For code targets the Write gate asks:
Item 3 is dropped when the new content handles no data.
Questions fit the change
For code targets the gate reads the change it is about to allow (Edit old_string/new_string, every MultiEdit entry for the file, Write content) and decides three things:
- Public surface: a line on either side declares or exposes a public name.
JS/TS: export, exports, public, declare (.d.ts files always); Python: def/class not starting with _, dunders, __all__, a module-level public assignment such as BASE_URL = ... (__init__.py always); Go: capitalised func/type/var/const names, package, lines starting with a capital; Rust: pub, impl, trait, extern, #[macro_export], #[derive], #[repr], #[no_mangle]; shell: function definitions, export, declare -x; PowerShell: function, filter, param, Export-ModuleMember, [CmdletBinding. A member of an exported container counts too, judged from the file: lines inside an exported interface, enum, type, object or export list, member declarations of an exported class, __all__ entries, public class attributes and dataclass fields, and members of a pub enum, struct, trait or use list. Java, Kotlin, C#, C, C++ and batch files, and every Write, always count as touching it. Touching it keeps questions 1 and 2; otherwise both are replaced by "List the call sites in this file or its module that rely on the changed behaviour".
- Data: words for file I/O, formats (
json,csv,yaml, …),
serialisation, encodings, schemas, SQL, data stores, browser storage, dates and clock reads on either side; in shell scripts also redirection to a file, curl, wget, jq and PowerShell file and web cmdlets. Without them the data question is dropped. With GATEGUARD_EXTERNAL_CONTRACT_QUESTION on, a change that keeps the data question is also asked what outside the repository decides its format or semantics.
- Trivial: see Comment and whitespace-only edits.
Supported extensions: .js .mjs .cjs .jsx .ts .tsx .mts .cts .py .pyi .go .rs .java .kt .kts .cs .c .h .cc .cpp .cxx .hpp .hh .hxx .sh .bash .zsh .ps1 .psm1 .bat .cmd. Any other extension, a missing or non-string field, more than 64 MultiEdit entries, a side over 64 KiB, or 256 KiB in total means the full questions, word for word: the original four (importers, public API, data schemas, the user's instruction), or five with GATEGUARD_EXTERNAL_CONTRACT_QUESTION on. Sensitive and hard-linked targets and NotebookEdit calls always get the full questions. Other classes keep their class questions. Each question has a stable id (importers, public-api, local-callers, callers, no-duplicate, data-schema, external-contract when opted in, quote-instruction, and one per class question), listed in docs/gateguard/change-profile.md.
Questions by target class
The code questions carry no signal for a SKILL.md, a README, a test, or a YAML file, so the first-touch gate classifies the target first.
Classification uses the target's path relative to the project root (CLAUDE_PROJECT_DIR, then the payload cwd, then the process directory), so a project that lives under, say, ~/work/tests/ does not turn every file into a test. A leading .claude/worktrees/<name>/ is stripped, since a worktree mirrors the project, but only when <root>/.claude/worktrees/<name>/.git exists; otherwise the path stays under .claude. On Windows-style paths (and any segment containing ::$), trailing dots/spaces and a :stream suffix are dropped first, so CLAUDE.md. and CLAUDE.md::$DATA classify as CLAUDE.md. Targets outside the root are classified by their absolute path. The path is lowercased and /-separated, and the first match wins:
- instruction — basename
CLAUDE.md,AGENTS.md,AGENT.md,
GEMINI.md, SKILL.md, copilot-instructions.md, .cursorrules, or .windsurfrules; any .mdc file; a .md/.mdx/.txt file under a .claude, agents, commands, skills, rules, hooks, .cursor, .codex, or .opencode directory; or a *instructions*.md file under .github/
- test —
*.test.*,*.spec.*,test_*.py,*_test.py,*_test.go,
or anything under a tests, test, or __tests__ directory
- prose —
.md,.mdx,.txt,.rst,.adoc - config —
.json,.jsonc,.yaml,.yml,.toml,.ini, or a
.env / .env.* file that does not end in a code extension (.env.local is config; .env.example.ts and .envrc are code)
- code — everything else (a
.jsunderhooks/orskills/is code)
Every class ends with "Quote the user's current instruction verbatim", keeps the batch-sibling warning, and uses the same questions for Edit, MultiEdit, and Write unless noted. Items marked (search) carry the suffix "(search the tree — Glob/Grep, or find/grep via Bash)".
| Class | Questions | |
|---|---|---|
| instruction | Name the harness/loader that reads this file (Claude Code, Codex, Cursor, OpenCode, …) and when it loads it · Describe what agent behaviour changes as a result · Confirm no existing instruction, skill, or agent file already covers this (search) | |
| test | Name what behaviour is under test and which module/function it exercises · Name the existing test file(s) covering this module, or confirm none exist (search) | |
| prose (Write) | Name any existing doc this supersedes or duplicates (search) · State where it will be linked or referenced from · Explain why a new file rather than editing an existing one | |
| prose (Edit) | List other docs or code that reference the section being changed (search) · State what the change corrects or adds | |
| config | Name which process/tool reads this file and when · Describe the effect of the change · Confirm no secrets or credentials are being written in plain text |
Condensed denials (after GATEGUARD_FACT_FORCE_FULL_DENIALS) carry a one-line hint for the same class. For code targets the hint names the same questions as the full denial, one short phrase each; an Edit whose change cannot be read keeps the fixed hint (importers/callers, affected API, data schemas if any) when the external-contract question is off. Opting in adds its external-contract phrase. Besides the message text, the class only decides sibling collapse: which files may collapse, and that a sibling must share the first file's class.
Destructive Bash Gate (every destructive command)
Triggers on: rm -rf, git reset --hard, git push --force, drop table, etc.
Routine Bash Gate (once per session)
Read-only introspection does not use this gate up: until it fires, a command whose every ;, |, && or || segment is ls, pwd, cat, head, tail, wc, grep, rg, find, fd, tree, or git status/log/ diff/show/ls-files/rev-parse/branch with read-only options (in PowerShell: Get-ChildItem, Get-Content, Select-String, Get-Location, their aliases, rg, git) passes and is counted in routine_readonly_passes. The first other command is still gated. Any redirection, tee, substitution, backtick, variable, environment assignment, wrapper (env, sudo, xargs, sh -c), unknown command, find -exec/-delete, fd -x, rg --pre/-z, tree -o, git global option or write/execute option, or PowerShell $, @, (, { or --% makes the command ordinary. Destructive detection runs first, unchanged.
Session state expires after 8 idle hours when it is keyed by a session id or transcript path, and after 30 idle minutes when it falls back to the project directory.
Parallel Batches and Partial Application
The first-touch gate evaluates each tool call independently. When several edits to a file that has not been touched yet are sent in one parallel batch, the first call is denied and the denial marks the file as checked, so the sibling edits in that batch are applied. Nothing is rolled back: the file can end up holding the sibling edits without the denied one.
The denial message names the file and warns that batch siblings may already have been applied. Treat it literally:
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 96 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/gateguard/SKILL.md
Popularity
- Repository stars
- 169,799
- Repository forks
- 26,314
- 7-day trend score
- 60
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 28 Apr 2026, 5 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/affaan-m/everything-claude-code/gateguard)