# Function CLASSIFICATION schema This is the canonical definition of the `# CLASSIFICATION` function doc-header label. It's referenced from code comments and commit messages — link here, not to anything under `AGENTS/` (that tree is git-ignored local agent state, not part of the repo). See [Public function documentation header](../CONTRIBUTING.md#public-function-documentation-header) in `CONTRIBUTING.md` for where `CLASSIFICATION` fits among the other header labels, and [C1 — Command Shadows](manual/08-components-reference/01-c1-command-shadows.md) for the full list of C1-shadowed commands this schema's shadow tags refer to. ## Format Optional. Comma-separated tags from the closed set below, on the indented body line directly under the label: ```fish # CLASSIFICATION # uses-shadow(ls), destructive ``` Omit the label entirely when nothing applies — omission means "nothing to flag," not "not yet audited," so don't add it speculatively, and don't add it empty as a placeholder. ## Tags - **`uses-shadow(name[,name...])`** — calls a C1-shadowed command (see the C1 doc linked above) bare, deliberately wanting the overridden behavior (e.g. `ls` wanting eza's icons for a human to read). - **`bypasses-shadow(name[,name...])`** — calls `command `, `builtin `, or (for `help` specifically) `__original_help $argv`, deliberately forcing stock behavior because the shadow's override would break this function's logic: timestamps leaking into a parsed capture, `-i` prompting on a path meant to run unattended, structural output changes breaking a `string`/`sed` parse, etc. - **`self-limiting(name[,name...])`** — calls a shadowed command bare, and it's safe not because the caller did anything but because *the shadow's own logic* already neutralizes the override for this call. Verify the actual condition per shadow, it's not the same check for each one: - `rm` falls back to `command rm` for any flag **except** a bare `-r`, `-R`, or `--recursive` (those still route to `trash put`) — so `rm -f`/`rm -rf` qualify, but `rm -r $dir` alone does not. - `mkdir` falls back to `command mkdir -p` for *any* flag at all, no exception. - `--color=auto`/`bat`'s own tty auto-detection (`grep`, `fgrep`, `egrep`, `dir`, `vdir`, `cat` — verified byte-identical to stock when piped or captured, since none of these force color on a non-terminal). Document it explicitly rather than leaving the bare call untagged: if a shadow's bypass condition is ever weakened, narrowed, or removed, every `self-limiting` site is one grep away instead of silently wrong. Don't use this for `ls` — eza's long-format/icon layout is structural, not tty-gated, so it stays different from stock `ls` even piped; a bare `ls` call still needs `uses-shadow(ls)` or a real bypass. - **`destructive`** — can irreversibly delete or overwrite data: `rm -f`, `rm -rf`, truncating or force-overwriting a file, `git push --force`. Routine cleanup of the function's own `$tmpdir`/`$_tmpdir`/`mktemp` output (or other output it just created in this same call) is expected behavior, not a hazard — don't tag it. - **`network`** — makes an outbound network call: `curl`, `wget`, `ssh`, `git fetch`/`pull`/`push`/`clone`, `paru`/`yay` (package-manager network ops), talking to an API, etc. - **`blocking-prompt`** — can block waiting on interactive confirmation with no non-interactive escape hatch: a shadow's forced `-i`, fish's `read` (genuinely waiting on a terminal — not a `string split | read` or `while read` consuming a pipe, which never blocks), a `confirm`-style prompt with no `--yes`/`--force`/`--silent` bypass. Don't tag a function that's only ever meant to be run interactively at a prompt (a keybinding handler, an fzf-driven picker) — the hazard this tag exists for is a script or another function calling it unexpectedly, not a human running it themselves. ## Placement Directly under `# DEPENDENCIES` if the header has one; otherwise directly under `# COMPONENT`; otherwise directly under `# CATEGORY`; otherwise as the first label in the header block (this is the common case for internal `_`-prefixed helpers, which usually carry none of the three). ## Judgment calls `uses-shadow` vs `bypasses-shadow` is the easiest place to get subtly wrong — verify against the actual code, not just whether the name appears in the file. A function that only calls a *helper* which itself interacts with a shadow does not get the tag; the tag belongs on the helper. When generating these tags in bulk (e.g. delegating the sweep to another model), review every result against the source before trusting it — this schema's own rollout caught several false positives this way: a piped `read` misread as an interactive prompt, a documented `--yes` flag missed as an escape hatch, and cleanup of a function's own temp output flagged as `destructive` despite the explicit exclusion above. `rm` specifically has its own internal flag check (any flag other than `-r`/`-R`/`--recursive` falls back to `command rm` *inside the shadow itself*, before it ever touches trash) — a caller writing plain `rm -f` or `rm -rf` is not bypassing anything itself, the shadow is. Only tag `bypasses-shadow(rm)` when the caller explicitly writes `command rm` or `builtin rm`; a bare `rm -f`/`rm -rf` call gets no shadow tag at all.