From 8d563fbdb6494ff869a5d0f9bf6645cf8cf6c43d Mon Sep 17 00:00:00 2001 From: A Tobey Date: Tue, 1 Sep 2026 09:05:08 -0400 Subject: [PATCH] docs(wrapped): the short form said bypass where the long form says narrower Amy read the 0.17.0 release notes and asked whether wrapped commands bypass allow_external_commands. They do not, and the draft said they did because it was quoting our changelog. The long form has always been right. docs/wrapped_command.md's "What it is for" says the switch has two settings, nothing spawns and everything on PATH spawns, and a wrapped command is the setting between. Every short restatement dropped that and kept only the half that reads as a hole: CHANGELOG.md "Runs with allow_external_commands off." docs/wrapped_command "It runs when allow_external_commands is false." Both sentences are true. Both invert the feature, because a reader who meets a policy switch and a thing that runs regardless concludes the thing defeats the switch. The direction is the load-bearing half and it was the half that kept getting cut for length. The behavior was never in doubt and a test already discriminates it: wrapped_command_exec_tests.rs:373 runs /bin/true two ways in one kernel, once as a registered wrapper and once as a bare command, and asserts the wrapper runs while the bare command is still refused. The module doc gained the statement outright. It is the docs.rs entry point for an embedder deciding whether to register a wrapper, and it described the mechanism while saying nothing about the policy the mechanism serves, which is what an embedder is actually deciding about. The rule going forward: a one-line summary of wrapped commands carries "narrower, not wider," or it teaches the opposite of what the feature does. --- CHANGELOG.md | 3 ++- crates/kaish-kernel/src/tools/wrapped.rs | 8 ++++++++ docs/wrapped_command.md | 10 +++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb1469a7..e7ae98f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,8 @@ breaking entries are marked **BREAKING**. - **Wrapped commands** (`kaish_kernel::tools::wrapped`, `subprocess` feature): register an external program as a tool with a declared grammar. Verbs and flags are deny-by-default, refused with exit 2 before any spawn; the kernel renders - argv. Runs with `allow_external_commands` off. See `docs/wrapped_command.md`. + argv. It narrows rather than widens: `allow_external_commands = false` plus + named wrappers is the intended shape. See `docs/wrapped_command.md`. - **`ValidationIssue::command`** — the command an issue concerns, when one is genuinely known (`UndefinedCommand`'s name, a builtin's own regex/schema failure), so an embedder can route on it instead of parsing `message`. diff --git a/crates/kaish-kernel/src/tools/wrapped.rs b/crates/kaish-kernel/src/tools/wrapped.rs index 6daf61a3..d72fbfed 100644 --- a/crates/kaish-kernel/src/tools/wrapped.rs +++ b/crates/kaish-kernel/src/tools/wrapped.rs @@ -5,6 +5,14 @@ //! renders the argv itself, so a value is never parsed as a flag by the child //! unless the declaration put it in flag position. //! +//! This narrows what a kernel can run; it does not widen it. +//! `allow_external_commands` chooses between "no word spawns anything" and +//! "every program on `$PATH` spawns, with any arguments." A wrapped command is +//! the setting between: the executable is pinned at registration, so a changed +//! `$PATH` changes nothing, and an undeclared verb or flag is refused before +//! any spawn. Registering wrappers with the switch left `false` is the intended +//! shape, not a way around it. +//! //! ```no_run //! use kaish_kernel::tools::wrapped::{Flag, Positional, Verb, WrappedCommand}; //! diff --git a/docs/wrapped_command.md b/docs/wrapped_command.md index 701cddb2..47a521c9 100644 --- a/docs/wrapped_command.md +++ b/docs/wrapped_command.md @@ -430,9 +430,13 @@ and the two names that collide: optional slot without it. - A relative `path_under(root)` — the root must be an absolute path. -A wrapped command is a registered tool, not an external command. It runs when -`allow_external_commands` is `false`. It needs the `subprocess` feature; a -sandbox build has no `wrapped` module. +A wrapped command is a registered tool, not an external command, so +`allow_external_commands` does not gate it. That switch is not a lock a wrapper +picks: it decides whether an arbitrary word becomes a `$PATH` lookup. A wrapper +is the narrower grant beside it — one pinned executable, one declared grammar. +Leaving the switch `false` and registering a wrapper is the point. + +It needs the `subprocess` feature; a sandbox build has no `wrapped` module. ## What the kernel owns, and what the embedder owns