diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05903bb..8d2c9c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -391,6 +391,7 @@ all optional except where noted: | `CATEGORY` | **Required to appear in the manual at all** — see below. | | `COMPONENT` | Only for functions gated by the [opinionated-component system](#opinionated-components). | | `DEPENDENCIES` | Other functions this one calls that a reader may want to look up. | +| `CLASSIFICATION` | Hazard/shadow-interaction tags — see below. | | `SYNOPSIS` | One-line usage form. | | `DESCRIPTION` | Prose description; can span multiple paragraphs. | | `ARGUMENTS` | Flags/positional args, one per line. | @@ -414,6 +415,9 @@ A full example (`functions/claude.fish`): # DEPENDENCIES # agents-init # +# CLASSIFICATION +# bypasses-shadow(claude) +# # SYNOPSIS # claude [ARGS...] # @@ -443,6 +447,14 @@ If your function genuinely doesn't fit any of these, add a new `docs/manual/05-functions/NN-your-category.md` stub (with frontmatter matching its siblings) rather than force-fitting it into an existing one. +**`CLASSIFICATION` flags hazards and shadow interactions, optional and +omitted when nothing applies:** whether the function calls a +[C1-shadowed command](docs/manual/08-components-reference/01-c1-command-shadows.md) +bare wanting the override (`uses-shadow(ls)`) or bypasses it deliberately +via `command`/`builtin` (`bypasses-shadow(cat)`), and general hazards — +`destructive`, `network`, `blocking-prompt`. Full tag definitions and +placement rule: [`docs/function-classification-schema.md`](docs/function-classification-schema.md). + ### Private/internal helper functions Functions named with a leading `_` (e.g. `_agents_init_ensure_gitignore`, diff --git a/docs/function-classification-schema.md b/docs/function-classification-schema.md new file mode 100644 index 0000000..57a0039 --- /dev/null +++ b/docs/function-classification-schema.md @@ -0,0 +1,74 @@ +# 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. +- **`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. diff --git a/docs/manual/08-components-reference/01-c1-command-shadows.md b/docs/manual/08-components-reference/01-c1-command-shadows.md index a71900c..87356a7 100644 --- a/docs/manual/08-components-reference/01-c1-command-shadows.md +++ b/docs/manual/08-components-reference/01-c1-command-shadows.md @@ -108,6 +108,7 @@ toggle state: editor launch. A function's own doc header records which of these it depends on: see the -`CLASSIFICATION` label (`uses-shadow(...)` / `bypasses-shadow(...)`) in -`AGENTS/functions/CLAUDE.md`. +`CLASSIFICATION` label (`uses-shadow(...)` / `bypasses-shadow(...)`), +documented in full at +[`docs/function-classification-schema.md`](../../function-classification-schema.md).