docs(gateguard): document type filters, path patterns and lookups

- design-notes.md: new Type filters and Path patterns sections; Search
  scope extends the search-of-the-target rule to name lookups; Flag
  parsing lists the new value flags.
- change-profile.md: Unicode line terminators make an edit non-trivial.
- skills/gateguard/SKILL.md and CHANGELOG.md describe the same rules.
- skill-triggers@1.json: refresh registryDigest for the SKILL.md body
  change. The skill's description is unchanged, so triggers and
  triggersDigest are untouched, as in the plan-orchestrate refresh.
This commit is contained in:
montjeffrey
2026-10-10 23:38:48 -04:00
parent 64d8971a64
commit 329ca4e11a
5 changed files with 55 additions and 7 deletions
+2
View File
@@ -23,6 +23,8 @@
- GateGuard keys checked files by canonical path, resolving relative targets against the tool `cwd`, so `a.py`, `./a.py`, and the absolute path (or differently cased and separated Windows spellings) share one first touch. Keys from earlier state files are still honoured.
- GateGuard gates a MultiEdit call as its `tool_input.file_path` when its entries do not name their own path, which is the tool's own shape; such calls were never gated before, including on sensitive files.
- GateGuard gates NotebookEdit as a first-touch edit of its `notebook_path`; notebooks were never gated before.
- GateGuard prior-search credit no longer counts a search that never saw the target. File-type filters (`rg -t`/`-T`/`--type`/`--type-not`, `fd -e`/`--extension`, the Grep tool's `type`), the Glob tool's pattern, shell glob operands, and git pathspecs now filter like include globs, so `rg -t py tokenizer src` or `Glob **/tokenizer.py` no longer credits `tokenizer.js`. A search confined to the target's own file name (`Glob **/tokenizer.js`, `find -name tokenizer.js`, Grep `glob: 'tokenizer.js'`) is a lookup and never credits it. PowerShell searches keep backslashes as path separators.
- GateGuard no longer passes an edit as comment-only when its text contains U+2028, U+2029, or U+0085, which end a JavaScript or C# line comment.
## 2.2.3 - 2026-10-01
+3
View File
@@ -139,6 +139,9 @@ Anything the lexer cannot read with confidence makes the entry non-trivial:
- a line comment ending in `\` (it continues onto the next line in C and
Make), the trigraph `??/`, a `/*` inside a block comment, and an
unterminated block comment or string;
- U+2028, U+2029 or U+0085 anywhere in the text: JavaScript ends a line at
the first two and C# at all three, so code after one inside a `//` comment
runs, while the lexer splits lines only at `\n` and `\r`;
- a code line ending in a line continuation `\` (trailing blanks included):
a C macro, a Python explicit continuation or a string continued onto the
next line joins that line, so adding or removing a comment-only line after
+34 -3
View File
@@ -31,7 +31,8 @@ and trivial edits) is in [change-profile.md](change-profile.md).
[Stdin is not a tree search](#stdin-is-not-a-tree-search),
[Search scope](#search-scope), [Stem matching](#stem-matching),
[Test stems](#test-stems), [Search filters](#search-filters),
[Include filters](#include-filters),
[Include filters](#include-filters), [Type filters](#type-filters),
[Path patterns](#path-patterns),
[Glob matching without RegExp](#glob-matching-without-regexp),
[Flag parsing](#flag-parsing),
[PowerShell parameter binding](#powershell-parameter-binding).
@@ -280,7 +281,12 @@ evidence.
Each search offers stem texts, candidate directories, the scope the target must
lie in, and path operands. The target must lie inside the scope. A search whose
operands are all the target itself read the target rather than searched for it,
and gives nothing. A `Glob` without glob characters is a single-file lookup: it
and gives nothing. The same holds for a search confined to the target's own
file name: when every include glob, or the Glob pattern, or every path pattern
ends in the target's file name with no glob characters (`**/tokenizer.js`,
`find -name tokenizer.js`, `rg -g tokenizer.js`), the search could only find
the target, so its stem never credits; the closest-miss note says it was a
lookup. A `Glob` without glob characters is a single-file lookup: it
names no directory. A `Glob` with an empty literal prefix and no explicit path
names only the implicit cwd, so it gives no directory credit. Directory
evidence from shell operands must name a directory that exists now.
@@ -349,6 +355,30 @@ leading `**/` lets it start at any directory; the other tools match names only,
so such a glob admits nothing. An include glob that is malformed, past the
matching bounds, or one of more than 32 filters admits nothing.
## Type filters
A file-type filter is an include or exclusion in another spelling, so it
filters the same way. `rg -t`/`--type` adds the type's globs as includes and
`rg -T`/`--type-not` adds them as exclusions, using ripgrep 14.1's built-in
globs for common languages. A type name outside that table, `--type-add` and
`--type-clear` redefine what a type means, so they make the search an
unreadable filter that never credits. `fd -e`/`--extension` is an include of
`*.<ext>`. The `Grep` tool's `type` parameter is read like `rg -t`.
## Path patterns
A glob in a path argument limits the files a search sees just as an include
does, so it must match the target for the stem to count. The `Glob` tool's
pattern is matched against the target's path below its root, with `*` inside
one directory and `**` across directories. A shell path operand with glob
characters (`rg x src/*.py`) is expanded by the shell before the search runs;
it admits the target when it matches the target or a directory above it,
since a matched directory is searched recursively. A git pathspec
(`git grep x -- '*.py'`) matches the same way, except that its `*` also
matches `/`. Literal operands keep their scope rule, and one operand that
admits the target is enough. PowerShell escapes with a backtick, so a
PowerShell search is tokenized with its backslashes kept as path separators.
## Glob matching without RegExp
Filter globs are matched by dynamic programming over glob tokens, never by a
@@ -366,7 +396,8 @@ Only path operands can scope a search:
- for grep-family tools, `rg`, `git grep`, `Select-String` and `fd`, the first
positional is the pattern unless `-e`/`-f` (grep family, `rg`, `git grep`
only; fd's `-e` is an extension) supplied it;
- fd's `-E/--exclude` takes a value; rg's `-r/--replace` takes a value.
- fd's `-E/--exclude` and `--extension` take a value; rg's `-r/--replace`,
`--type-not`, `--type-add` and `--type-clear` take a value.
## PowerShell parameter binding
@@ -10,7 +10,7 @@
"id": "hand-seeded",
"source": "manual-curation-pending-regeneration"
},
"registryDigest": "16549b53055bf5c106e51ca7fb2fe68f8a705b5f6bad40d388349320caedfa63",
"registryDigest": "13486e7747e81dcc199ac6475ca42089fcc02457e7a55c86a8fe94cf0bd41fce",
"schemaVersion": 1,
"triggers": {
"skill:agent-runtime-gateway-smoke-test": [
+15 -3
View File
@@ -368,10 +368,21 @@ An Edit, Write, NotebookEdit, or MultiEdit entry is allowed, with an
the hook cannot see, so such a segment never credits. File-name globs that
restrict a search (Grep `glob: '*.py'`, `rg -g '*.py'`, `grep --include`,
`find -name`, `tree -P`, `-Include`/`-Filter`) must match the target's file
name for its stem to count.
name for its stem to count. File types filter the same way: `rg -t`/
`--type` and the Grep `type` include the type's globs, `rg -T`/`--type-not`
exclude them, and `fd -e`/`--extension` includes `*.<ext>`, so
`rg -t py tokenizer src` never credits `tokenizer.js`. An unknown type name,
`--type-add`, or `--type-clear` makes the segment never credit.
- **Lookups are not searches:** a search confined to the target's own file
name — every include glob, the Glob pattern, or every path pattern ends in
that exact name (`Glob **/tokenizer.js`, `find -name tokenizer.js`,
`rg -g tokenizer.js`, Grep `glob: 'tokenizer.js'`) — could only find the
target, so it never credits it. `Glob **/tokenizer.*` still does.
- **Scope:** `Glob`, `Grep`, and `LS` only credit a target strictly inside the
directory they searched (their `path`, else the tool `cwd`, plus a Glob
pattern's literal leading directories). A Glob without glob characters is a
pattern's literal leading directories). A Glob pattern is a filter too: it
must match the target's path for the stem to count, so `Glob **/tokenizer.py`
never credits `tokenizer.js`. A Glob without glob characters is a
one-file lookup: it names no directory and credits only files directly in
its directory, never the looked-up file itself. A search whose path is the target is not a search.
- **Shell segments:** a segment without a path operand counts only when it is
@@ -384,7 +395,8 @@ An Edit, Write, NotebookEdit, or MultiEdit entry is allowed, with an
characters, never credit.
- **Shell scope:** a shell segment only credits a target equal to or inside
one of its path operands (resolved against the tool `cwd`; a glob operand
counts as its literal directory), or inside the `cwd` when it has none:
must match the target or a directory above it, and a git pathspec's `*`
also matches `/`), or inside the `cwd` when it has none:
`rg payment docs` or `grep -rn payment /usr/share/doc` never credit
`src/payment.py`. An input redirection before any operand
(`rg payment < file`) or a `<` inside a word reads stdin and never counts.