Skip to content

Add registry-centric plugins RFD with sub-RFDs#253

Open
nikomatsakis wants to merge 3 commits into
symposium-dev:mainfrom
nikomatsakis:register-centric-plugins-rfd
Open

Add registry-centric plugins RFD with sub-RFDs#253
nikomatsakis wants to merge 3 commits into
symposium-dev:mainfrom
nikomatsakis:register-centric-plugins-rfd

Conversation

@nikomatsakis

Copy link
Copy Markdown
Member

What does this PR do?

Introduces an umbrella RFD for generalizing Symposium's plugin system around registries, with child RFDs covering: plugin model, where clause syntax, discovery policy, plugin defaults, user-managed plugins, fixed-point resolution, and custom registries.

Introduces the umbrella RFD for generalizing Symposium's plugin system
around registries, with child RFDs covering: plugin model, where clause
syntax, discovery policy, plugin defaults, user-managed plugins,
fixed-point resolution, and custom registries.

Co-authored-by: Claude <claude@anthropic.com>
@nikomatsakis nikomatsakis marked this pull request as ready for review June 30, 2026 01:17
nikomatsakis and others added 2 commits June 30, 2026 20:16
Replace the previous registry/source-based design with a PM-centric model:
- Plugins identified by canonical `pm:name:version` triples
- PMs implement four operations: resolve, search, fetch, list-deps
- Three-layer state: config (requirements) → cache (resolved) → agent dirs (active)
- Flat predicates replace namespaced `where.{cargo,predicates}` tables
- Enterprise control via crate-level PM overrides

Delete old sub-RFDs (plugin-model, where-clauses, discovery, plugin-defaults,
user-managed-plugins, fixed-point-resolution, custom-registries, preview docs)
and plan three new ones: PM interface, discovery & sync, user-managed plugins.

Co-authored-by: Claude <claude@anthropic.com>
Restructure main README as user-facing (As a user / As a crate author)
followed by Key concepts (Plugins / Package managers) and FAQ.

Add five sub-RFDs aligned with the new design:
- PM interface: JSON-RPC over stdio, separate binaries, only path built-in
- Cargo PM: resolve format, fetch via cargo toolchain, list-deps from lockfile
- Plugin model: Symposium.toml structure, defaults, predicates, chained plugins
- Discovery & sync: list-deps → search two-phase algorithm, prompt UX
- User-managed plugins: symposium use/remove/status, config format, scoping

Key design decisions captured:
- Package-id is a tuple (pm, name, version), no mandated string format
- PMs communicate via JSON-RPC over stdio (agent-client-protocol SDK)
- symposium CLI is the primary command (cargo agents is an alias)
- depends-on shorthand reuses PM's resolve format
- Symposium.toml is the only config mechanism (no Cargo.toml metadata)
- Recommendations PM participates in discovery via search, not list-deps

Co-authored-by: Claude <claude@anthropic.com>
| `resolve` | opaque TOML value (from `source.<pm>`) | set of package-ids | manifest processing |
| `search` | partial query string | set of package-ids + metadata | `symposium use` |
| `fetch` | package-id | directory with plugin content | sync/install |
| `list-deps` | workspace directory | set of package-ids | auto-discovery |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: We also need something for scanning installed packages and checking whether there are available updates.

* `[[hooks]]` identifies hooks. Symposium allows you to define vendor-neutral hooks that work for any vendor or vendor-specific hooks that target a particular agent (e.g., Claude Code or Codex).
* `[[installable]]` identifies *installable content*, which can be referenced by MCP servers or hooks (which need an executable). An easy option is to package your content as a cargo package that will be cargo-install'd and managed by Symposium, but there are other options.

#### Predicates

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I still think custom predicates are useful, it's possible they should be rolled into the package manager interface, though (and maybe even namespaced, e.g., cargo:foo(...) would invoke "custom predicate foo with ... on the cargo package manager".


### Package managers

A package manager (PM) is a pluggable backend that knows how to find, resolve, fetch, and enumerate plugins from a particular ecosystem. Each PM is a separate binary that Symposium invokes — installed as an `[[installable]]` from either the recommendations repository or the user's root config. The `path` PM is built into the Symposium binary itself (since it just reads local directories), but `cargo`, `git`, and any future PMs (npm, pypi, etc.) are separate binaries.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to define where the package managers come from. I am imagining that we always load symposium-recommendations and that it can define a set of additional package managers (which would be another operation in the list below). Those too can define add'l package managers recursively.

Comment thread md/rfds/registry-centric-plugins/README.md

### Dependency discovery

Symposium will automatically scan the dependencies of your project to find relevant plugins. This scan is done by executing `symposium sync`. Users can also configure Symposium to automatically sync every time an agent executes in their workspace.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symposium to automatically sync every time an agent executes in their workspace.

Hmm, yes, with the caveat that we don't do work when we don't need to (like if the Cargo.toml hasn't changed).

@@ -0,0 +1,339 @@
# Registry-centric plugin distribution

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked on Zulip, but I wonder if it's worth mentioning somewhere (maybe a separate RFD) that cargo agents more or less forwards to symposium


See the [discovery & sync sub-RFD](./discovery-sync/README.md) for the detailed algorithm.

### Workspace-local extensions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I'm thinking. We're fetching crate sources to get the plugins? If you have a workspace, the uploaded source is the sub-directories, right? So, if you have skills in the top-level directory, those aren't uploaded to crates.io? Unless you add them?

(I might be completely wrong here - I actually don't know.)

In either case: the (pm, name, version) canonicalization means that the name is the crate name, not workspace name (but this means that two crates in the same workspace might resolve the same plugin).

SKILL.md
```

### Explicit use

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We never really settled whether symposium use means that the plugin is always enabled or not.

I think the answer should be no, and that the predicates ultimately define when the plugin is enabled.

Thinking about it, the depends-on predicate is actually more accurate than the crates predicate:

Let's say you have crateX with a plugin. There are two cases where this gets activated:

  1. If you depend on crateX
  2. You use crateX

Now, if the plugin has a depends-on(crateY) predicate, I wouldn't now expect use crateX to turn that on if you don't also depend on crateY.

For recommendations, I think we want to have a depends-on(*) for default activation, and then no depends means that you need a manual activation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a little hard to explain, but I think it would just work pretty naturally.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My expectation is that: predicates always apply.

I do not think we need something quite so so complex for your crateX scenario. I think if you add a cargo/crateX/Symposium.md file with no predicates into the recommendations repository, then...

  • people can do symposium use crateX and it will work because we'll find it when we search
  • if crateX appears in their dependencies, we will also search and find it

In other words, the default when you put something into the recommendations repository at cargo/foo is that it is activated if foo appears in the dependencies or it is explicitly used.

If you have content that you only want to have when it is explicitly used, you can gate that particular content under a used() predicate (and similarly for the case where you only want the content when it appears in your dependencies, then you would put in-dependencies() or whatever I called that... maybe we want to rename it).

```
symposium-recommendations/
...
cargo/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a thought about conflicting registries, but I think that the recommendations repo is going to be some set of known registries.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it's going to be fine in practice, I decided not to worry about it.


### Publishing a plugin not associated with a crate

The `symposium-recommendations` repository can also be used to publish centralized plugins that don't have an associated crate or whatever. For example, this might be used to distribute a collection of skills from a github repository or to distribute a tool whose installation is not managed by Symposium. To do that, you simply add to the directory called `symposium`:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering: I think that this can just be abstracted into being able to support any path or git repo as a package manager.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I imagine so. We could make it configurable in your config?


The plugin itself and each of its subsections can be gated with a `predicates = [...]` field. When a plugin is installed, the content is only *activated* if the predicate matches.

Common predicates include:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some new ones here; I'd like to know their use-cases.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

source.path = "skills"

[[skills]]
predicates = ["workspace()"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's what this is for...

Well, you would seemingly want a "not-workspace" predicate then? I don't think you want to enable skills for users when developing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do that today because we support not(workspace()) or whatever but I'm unconvinced you ever want this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants