/gateguard
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
Claude Code
$ npx -y skills add affaan-m/ECC --skill gateguard --agent claude-code
Codex
$ npx skills add affaan-m/ECC --skill gateguard -a codex -y
Cursor
$ npx skills add affaan-m/ECC --skill gateguard -a cursor -y
Gemini CLI
$ npx skills add affaan-m/ECC --skill gateguard -a gemini-cli -y
GitHub Copilot
$ npx skills add affaan-m/ECC --skill gateguard -a github-copilot -y
OpenCode
$ npx skills add affaan-m/ECC --skill gateguard -a opencode -y
Windsurf
$ npx skills add affaan-m/ECC --skill gateguard -a windsurf -y
SKILL.md
gateguard.SKILL.mdname: gateguard
description: "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."
metadata:
origin: community
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:**
1. DENY — block the first Edit/Write/Bash attempt
2. FORCE — tell the model exactly which facts to gather
3. ALLOW — permit retry after facts are presented
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](#questions-by-target-class)). For **code** targets the Edit gate asks:
Before editing {file_path}, present these facts:
1. List ALL files that import/require this file (search the tree — Glob/Grep, or find/grep via Bash)
2. List the public functions/classes affected by this change
3. If this file reads/writes data files, show field names, structure,
and date format (use redacted or synthetic values, not raw production data)
4. Quote the user's current instruction verbatimThese four are the full set, asked whenever the change cannot be read with confidence. When it can (see [Questions fit the change](#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:
Before creating {file_path}, present these facts:
1. Name the file(s) and line(s) that will call this new file
2. Confirm no existing file serves the same purpose (search the tree — Glob/Grep, or find/grep via Bash)
3. If this file reads/writes data files, show field names, structure,
and date format (use redacted or synthetic values, not raw production data)
4. Quote the user's current instruction verbatimItem 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](#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
Read more
name: gateguard description: "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." metadata: origin: community
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:**
1. DENY — block the first Edit/Write/Bash attempt 2. FORCE — tell the model exactly which facts to gather 3. ALLOW — permit retry after facts are presented
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](#questions-by-target-class)). For **code** targets the Edit gate asks:
Before editing {file_path}, present these facts:
1. List ALL files that import/require this file (search the tree — Glob/Grep, or find/grep via Bash)
2. List the public functions/classes affected by this change
3. If this file reads/writes data files, show field names, structure,
and date format (use redacted or synthetic values, not raw production data)
4. Quote the user's current instruction verbatimThese four are the full set, asked whenever the change cannot be read with confidence. When it can (see [Questions fit the change](#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:
Before creating {file_path}, present these facts:
1. Name the file(s) and line(s) that will call this new file
2. Confirm no existing file serves the same purpose (search the tree — Glob/Grep, or find/grep via Bash)
3. If this file reads/writes data files, show field names, structure,
and date format (use redacted or synthetic values, not raw production data)
4. Quote the user's current instruction verbatimItem 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](#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
Your agent can write code, but ECC gives it a coordinated engineering system and toolbox: it plans before it builds, verifies changes with tests, reviews its own work from a fresh context, remembers what matters, and turns repeated wins into reusable skills
Other skills on ecc.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
act-on-feedback
Act on user feedback attached to the current session. Use when the user submits feedback on the session's changes via the Submit…
Questions about gateguard.
- Which coding agents can use gateguard?
- gateguard installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

