Repository navigation
Conversation
JeanMertz
force-pushed
the
rfd-072
branch
2 times, most recently
from
October 1, 2026 12:09
c198cc3 to
62ee739
Compare
RFD 072 routed a command by the `command` field of a plugin's `describe`
answer, but also required admission before every spawn, `describe`
included. On a fresh install the two could not both hold: to learn
which binary answers `jp serve web`, JP had to run binaries nobody had
approved.
Each plugin binary now carries a manifest, one line of JSON (`jp-plugin/v1
{...}`) that JP reads from the file without running it. It holds only
what JP needs before it decides to run a plugin: the protocol version,
a one-line description, and the claimed command path. The `describe`
answer repeats those fields and adds `help`, and the two must agree.
The file name keeps naming the plugin's config and approval, and two
binaries with one name conflict.
RFD 077 changes to match. `unattended` becomes `allow`, and installing
is no longer configured: official plugins install on first use, and
third-party plugins only through `jp plugin install` or by putting a
binary on `$PATH`. Every plugin defaults to `run = "ask"`, answered
without a prompt by a registry-matching official binary or by an entry
in a user-local approval store that never reaches config or a
conversation. Running an installed plugin makes no network request;
`jp plugin update` updates official plugins. RFD 114 reads a plugin's
workspace scope from its manifest, so admission happens once, under the
configuration that governs the run.
Signed-off-by: Jean Mertz <git@jeanmertz.com>
A config enum variant can now list retired spellings with
`#[variant(deprecated_aliases("unattended"))]`. `FromStr` still parses
them to the variant, and logs a warning naming the replacement, once
per spelling per process so a value read from many stored documents
does not repeat it.
Unlike `aliases`, deprecated spellings stay out of the schema and
`variants()`, so nothing offers a spelling that is on its way out, and
a parsed value always displays and serializes under its canonical
name.
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Tool and label settings that skip the confirmation prompt now spell it `allow`: a tool's `run`, `result`, and `format`, and a label's `run`. `unattended` described how the tool runs, when the setting is really the decision not to ask; a tool at `unattended` could still ask the user a question through an inquiry. ```toml [conversation.tools.'*'] run = "allow" ``` `unattended` still works everywhere it did, in config files, `--cfg`, and conversations stored before the rename, and logs a deprecation warning once per run. Config and conversations are written back as `allow`, and the schema and generated config offer only `allow`. Signed-off-by: Jean Mertz <git@jeanmertz.com>
`--quiet` silences the chrome channel on stderr, and every stderr write
went through it, so there was no way to report an error while chrome
was off. `Printer::error_println` writes a line to stderr whether or not
chrome is silenced, wrapped as a `{"message": ...}` record under a JSON
output format like `eprintln`. `error_println_raw` does the same for an
error that is already a JSON record of its own.
Signed-off-by: Jean Mertz <git@jeanmertz.com>
A command plugin now says where it attaches to `jp` in a manifest, one line of JSON embedded in its binary, and JP reads it from the file without running the plugin. A binary named `jp-webui` whose manifest claims `["serve", "web"]` answers `jp serve web`, and `jp -h` lists every plugin's command and description without spawning any of them. When several binaries claim one command, the longest claim wins, a third-party binary replaces an official command, and two claimants that cannot be told apart are an error naming both. Every plugin now defaults to `run = "ask"`, and nothing runs before it is admitted, `describe` for `jp <plugin> -h` included. An official binary matching its registry checksum, or one recorded in the approval store, runs without a prompt. The prompt shows the binary's path, says when it changed or when another file is the approved one, and marks a third-party binary that replaces an official command. Without a terminal JP refuses and prints the `jp plugin approve <path>` command that would admit it. Official plugins install the first time their command is typed; third-party plugins install only through `jp plugin install`, which asks first and installs what the plugin requires. Running an installed plugin never reaches the network: `jp plugin update` updates the official plugins JP installed, and says why it leaves any other alone. The new `jp plugin approve`, `revoke`, and `uninstall` manage approvals and installed binaries, and every `jp plugin` command works outside a workspace. A plugin's `print` output now follows `--quiet` and `--format json`, and stopping a plugin kills everything it started. BREAKING CHANGE: `plugins.auto_install` and `plugins.command.<name>.install` are removed Installing is no longer configured, and a config file that still sets either key fails to load; delete the key. A plugin binary without a manifest no longer claims a command: rebuild it against this `jp`, or record its claims with `jp plugin approve <path>`. A plugin that ran without asking because it was installed now needs an approval, or `plugins.command.<name>.run = "allow"`. Signed-off-by: Jean Mertz <git@jeanmertz.com>
Ticket 0sz2taf is fixed: `jp -h` no longer spawns plugins, and `jp <plugin> -h` is admitted before `describe` runs. A test proves an unapproved binary is never run. The RFD 072 tracking ticket records that every phase is implemented, along with RFD 077 Phases 3 to 5. Signed-off-by: Jean Mertz <git@jeanmertz.com>
`plugins.auto_install` and `plugins.command.<name>.install` were removed, and conversations stored before that still carry them in their base config and config deltas. The test proves such a conversation still loads through the config recovery path, and that a `run = "deny"` stored beside a dropped `install` key survives rather than being lost with its entry. Signed-off-by: Jean Mertz <git@jeanmertz.com>
`jp plugin approve` now names a plugin by the file it is given, the way discovery names it. A link such as `/usr/local/bin/jp-titles` into `/opt/titles/jp-titles-1.2` is the plugin `titles`: its deny setting applies, and its approval is stored where dispatch looks for it. Approve also refuses a binary whose contents do not match a pinned checksum before running it for `describe`, and hashes the binary once, before it runs, so the approval covers the contents that were checked. Plugin paths found on a relative `$PATH` entry are made absolute, so a plugin run from another workspace's directory (`jp -w …`) is the same file admission checked. Asking a plugin to describe itself now owns its process tree the way a run does: a plugin that keeps running after answering no longer blocks `jp <plugin> -h` or `jp plugin approve`, and a worker it started does not outlive it. An approval store written before approvals carried a timestamp still loads, instead of being read as empty and overwritten on the next approval. The discovery tests use platform file names, so they pass on Windows. Signed-off-by: Jean Mertz <git@jeanmertz.com>
Pressing Ctrl-C while a plugin is starting up for `jp plugin approve` or `jp <plugin> -h` now stops the plugin and everything it started, and `jp` exits as interrupted. Since describing a plugin moved it into a process group of its own, the terminal's Ctrl-C no longer reached it, and `jp` exited without the cleanup that would have: the plugin and its workers kept running, holding ports or files, with nothing to say so. A command published under a command group after the registry cache was written, such as `jp serve http-api`, now refreshes a stale cache like any other command nothing on this machine claims. Before, the cached group answered for it and reported that no plugin provides it, however old the cache was. `jp serve` and `jp serve -h` still answer from the cache. Signed-off-by: Jean Mertz <git@jeanmertz.com>
`CommandPluginConfig::options` became a `MergeableMap` on `main` (#1218), so the test for stored conversations carrying the removed plugin install keys no longer compiled after the rebase. It now builds an empty map, and its imports move to the top of the file. Signed-off-by: Jean Mertz <git@jeanmertz.com>
A plugin that exits on its own no longer leaves the workers it started running, and the kill can no longer reach an unrelated process. On Unix, JP now waits for the plugin to exit without reaping it, kills its process group, and only then reaps it: while the plugin is unreaped its pid, and with it the group id, cannot be reused. Before, JP reaped the plugin first, leaving a short window in which the group id could belong to someone else. On Windows, asking a plugin to describe itself, for `jp <plugin> -h` and `jp plugin approve`, now starts it suspended and resumes it only once it is in its job object, as a run already does. A worker it started right away could otherwise escape the job, and survive the plugin. Closes: T-0vmsq99 Signed-off-by: Jean Mertz <git@jeanmertz.com>
The doc comment on each plugin's public `MANIFEST` linked to its private `REQUIRED_PROTOCOL`, which rustdoc cannot resolve from a public item. The reference is now a plain code span, as the formatter writes it. Signed-off-by: Jean Mertz <git@jeanmertz.com>
`Utc`, `ApprovedPlugin`, and `Globals` are only used by dispatch tests that run on Unix, so on Windows they were unused imports, which CI treats as errors. They are now behind `#[cfg(unix)]`, like the tests that use them. Signed-off-by: Jean Mertz <git@jeanmertz.com>
A plugin that exits before reading `init` makes the host's write fail with a broken pipe. That error returned before any cleanup ran, so a worker the plugin had started kept running after `jp` reported the failure. The `init` write is now part of the run's result, and the plugin's process tree is stopped on that path like on every other. `jp plugin approve` stopped the plugin it was asking to describe on Ctrl-C only. A SIGTERM, from `timeout` or a CI runner cancelling a step, ended `jp` at once and left the plugin and its workers running in their own process group. It now listens for the same signals the signal router treats as a graceful shutdown: Ctrl-C and SIGTERM, or Ctrl-C and Ctrl-Break on Windows. Signed-off-by: Jean Mertz <git@jeanmertz.com>
A test's doc comment and the tool progress window fixture still wrote `run = "unattended"`. Both kept working, since `unattended` is a deprecated spelling of `allow`, but loading the fixture logged the deprecation warning on every manual run. Signed-off-by: Jean Mertz <git@jeanmertz.com>
RFD 072 is marked Implemented, with every phase of its plan done. Signed-off-by: Jean Mertz <git@jeanmertz.com>
`plugins.shutdown_timeout_secs` was never read: a plugin that ignored `Shutdown` was always killed after five seconds, whatever the setting said. The setting now decides how long JP waits after a Ctrl-C or SIGTERM before it kills the plugin and everything it started. A plugin stopped because its run failed gets the same grace, capped at a second. A checksum pinned with `algorithm = "sha1"` refused every binary, because JP always compared the pin with the binary's SHA-256. Admission and `jp plugin approve` now hash the binary with the algorithm the pin names, so a correct SHA-1 pin admits the binary, and a mismatch reports the binary's SHA-1 as the actual value. Signed-off-by: Jean Mertz <git@jeanmertz.com>
RFD 077 described `options` as a single JSON value sent in `init`'s `config` field. It is a table that merges key by key across config layers, sent in `init.options`. Its example, and the one in the config reference, used `web.port` and `web.host`, which `serve-web` never read: it reads `bind` and `port`. The shutdown grace period kills the plugin's whole process tree, not only the process JP spawned, and the RFD now says so. Signed-off-by: Jean Mertz <git@jeanmertz.com>
`jp serve metrics`, where `serve` is an official command group and
`metrics` a third-party plugin the registry lists, reported that no
plugin provides the command. The group's prefix matched, so the
command never reached the hint an unknown command at the root gets.
It now names the plugin and the command that installs it:
error: unrecognized subcommand 'metrics'
`jp serve metrics` is provided by the third-party plugin
`metrics` (https://…). Install it with `jp plugin install metrics`.
The registry entry still only feeds the error: the command routes to
the group, so a catalog entry never decides which plugin runs.
Signed-off-by: Jean Mertz <git@jeanmertz.com>
RFD 072 showed `jp conversation export` and `jp conversation stats` as plugin commands, and its first routing rule said an unknown child of an extensible built-in group could still resolve to a plugin. No built-in group accepts unknown subcommands, so clap rejects `export` before any plugin routing runs. The RFD now says plugins attach at the root or under a group a plugin or the registry provides, drops the two examples and the clause, and lists plugin children of built-in groups under Non-Goals for a later RFD. Signed-off-by: Jean Mertz <git@jeanmertz.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Command plugins now declare where they attach to
jpin a manifest, aline of JSON embedded in the binary that JP reads without running it.
A binary named
jp-webuiwhose manifest claims["serve", "web"]answers
jp serve web, andjp -hlists every plugin's command anddescription without spawning any of them. When several binaries claim
one command, the longest claim wins, a third-party binary replaces an
official command, and claimants that cannot be told apart are an error
naming both.
No plugin runs before it is admitted,
jp <plugin> -hincluded. Everyplugin defaults to
run = "ask": an official binary matching itsregistry checksum, or one recorded in the approval store, runs without
a prompt. Anything else prompts, showing the binary's path, whether it
changed since it was approved, and whether it is a third-party plugin
replacing an official command. Without a terminal, JP refuses and
prints the
jp plugin approve <path>command that would admit it.Official plugins install the first time their command is typed;
third-party plugins install only through
jp plugin install, whichasks first and installs what the plugin requires. Running an installed
plugin never reaches the network, and
jp plugin updateupdates theofficial plugins JP installed. The new
jp plugin approve,revoke,and
uninstallcommands manage approvals and binaries, and everyjp plugincommand works outside a workspace. A plugin'sprintoutput follows
--quietand--format json, and stopping a pluginkills everything it started.
BREAKING CHANGE:
plugins.auto_installandplugins.command.<name>.installare removedInstalling is no longer configured. A config file or
--cfgargumentthat still sets either key fails to load; delete the key. Stored
conversations are unaffected: the keys are dropped when a conversation
loads, and the plugin settings beside them are kept. A plugin binary
without a manifest no longer claims a command: rebuild it against this
jp.jp plugin approve <path>records the claims of a binary whosemanifest cannot be read, such as one compressed with UPX, but only for
a binary built against this
jp. A plugin that ran without askingbecause it was installed now needs an approval, or
plugins.command.<name>.run = "allow".DEPRECATED: the
unattendedrun mode is renamed toallowTool (
run,result,format), label, and plugin settings that skipthe prompt now spell it
allow.unattendedstill works in configfiles,
--cfg, and stored conversations, and logs a warning; replaceit with
allow.