diff --git a/01-RESEARCH/029-the-agent-configured-through-its-module/00-overview.md b/01-RESEARCH/029-the-agent-configured-through-its-module/00-overview.md new file mode 100644 index 0000000..3e90486 --- /dev/null +++ b/01-RESEARCH/029-the-agent-configured-through-its-module/00-overview.md @@ -0,0 +1,59 @@ +--- +status: active +initiated: 2026-10-04 +touches: + - 03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md + - 03-DESIGN/00-as-is/15-the-agent-and-its-licences.md + - 02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md + - 02-DECISIONS/0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md + - 02-DECISIONS/0210-a-tools-configuration-is-its-seat-holders-and-every-other-module-extends-it-through-the-seat.md + - 02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md +became: [] +--- + +# 029 — The agent configured through its module + +## What + +Everything about the operator's coding agent that can be configured is configured through the agent +module's tools, and reaches the machines as **one plugin the mesh serves**: + +- subagents, skills, slash commands, hooks, output styles and tool servers; +- the agent's settings and its instructions. + +Each item is registered once, from any machine, and goes to one machine, several, or all of them, +including a machine that joins later. The mechanism is the one the module already uses for tool +servers: the registration is kept in the module's state on the bus, and each machine's instance writes +what applies to it. The operator chose the plugin route on the day this effort opened. + +## Why + +The vendor gives the agent a machine-wide directory for its settings, its tool servers and one +instruction file, and **nothing machine-wide for skills, subagents, commands or hooks**. Those exist +only in a home or a project. So today they are copied into each home by hand, and they drift and go +stale. [01](01-what-is-configured-today.md) measures that on four machines. + +A plugin is the vendor's own unit for carrying all of those at once. A machine-wide setting can name +a marketplace and enable a plugin from it. If the module serves the plugin and its own managed settings +enable it, the mesh gets a machine-wide place for everything the vendor left home-only, and the home +stays the person's ([ADR 0182](../../02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md)). + +## What it touches + +- **The agent module's design** ([to-be 36](../../03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md)): + its managed directory, its state, and its tools. +- **Module state** ([ADR 0201](../../02-DECISIONS/0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md)): + where registrations are kept, and which file content fits in a bucket. +- **Contributions** ([ADR 0210](../../02-DECISIONS/0210-a-tools-configuration-is-its-seat-holders-and-every-other-module-extends-it-through-the-seat.md)): + a module other than the agent's, say the forge's, wanting the agent to have a skill for it. That is a + contribution to the agent's seat, not a file it writes. +- **The managed settings key the operator sets** ([ADR 0213](../../02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md)), + which a settings tool would write rather than a hand-composed settings layer. + +## Documents + +- [01 — What is configured today](01-what-is-configured-today.md): the evidence. +- [02 — What the vendor allows](02-what-the-vendor-allows.md): plugins, marketplaces and managed + settings, as documented, with sources. +- [03 — Options](03-options.md): where each kind of item goes, how it is registered and stored, and + the questions a decision has to answer. diff --git a/01-RESEARCH/029-the-agent-configured-through-its-module/01-what-is-configured-today.md b/01-RESEARCH/029-the-agent-configured-through-its-module/01-what-is-configured-today.md new file mode 100644 index 0000000..50db7db --- /dev/null +++ b/01-RESEARCH/029-the-agent-configured-through-its-module/01-what-is-configured-today.md @@ -0,0 +1,51 @@ +# 01 — What is configured today + +Measured on 2026-10-04 on four machines that run the agent module: two workstations, the control node +and a home server. The figures count what sits in each operator account's agent directory in its home, +outside the module's managed directory. + +## What sits in the homes + +| what | workstation A | workstation B | control node | home server | +|---|---|---|---|---| +| rule files (`rules/`) | 4 | 2 | 2 | 1 | +| skills of the person's own (`skills/`, beside the vendor's synced ones) | 6 | 2 | 2 | 2 | +| subagents (`agents/`) | 0 | 1 | 0 | 0 | +| slash commands (`commands/`) | 0 | 0 | 0 | 0 | +| plugin marketplaces known | 1 | 2 | 1 | 1 | + +## What that shows + +- **Six files the design says the operator removes are still on every machine.** To-be 36 §1 lists the + predecessor's rule files and skills and leaves their removal to the operator, "once, on each + workstation". On all four machines, the two predecessor skills are present, byte-identical to each + other: + - one that switches licences through tools that no longer exist; + - one that names the predecessor's forge. + + So a session can still load a skill whose every instruction fails. +- **One instruction, three versions.** The predecessor's node-identity rule file is on three machines, + with three different contents. It was written per machine and then left alone. +- **Two instruction sets that contradict each other, loaded together.** On a workstation, one session + reads two sets of instructions: + - the module's managed instruction file says to search the mesh's records first; + - the predecessor's rule files in the home say to search the predecessor's knowledge base first, + through tools that are no longer served. + + Both are loaded, and neither says the other is stale. +- **A subagent exists on one machine only.** A reviewer for module definitions was written on one + workstation. The other three machines cannot use it, and nothing says it exists. +- **Settings are per home, and so per machine.** The agent's auto-mode environment, the rules that + decide which actions the agent may take unasked, is written in one home's settings file. It + describes another organisation's cloud, and it answers for this mesh's forge only through a list of + trusted domains. When the agent refused a merge the operator had approved, the only lawful fix was + a managed key ([ADR 0213](../../02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md)). + The agent could not change its own settings, and nothing else in the mesh could either. + +## What already works the way this effort wants + +Tool servers. A server registered through the module's register tool is kept in the module's state +on the bus, keyed `all.` or `.`. Every instance watches that state and writes what +applies to it into the managed tool-server file, and a machine that joins later takes it at its first +start. Today one server is registered there, for one workstation. That is the shape this effort extends +to everything else. diff --git a/01-RESEARCH/029-the-agent-configured-through-its-module/02-what-the-vendor-allows.md b/01-RESEARCH/029-the-agent-configured-through-its-module/02-what-the-vendor-allows.md new file mode 100644 index 0000000..ac20160 --- /dev/null +++ b/01-RESEARCH/029-the-agent-configured-through-its-module/02-what-the-vendor-allows.md @@ -0,0 +1,106 @@ +# 02 — What the vendor allows + +Read from the vendor's documentation on 2026-10-04; the agent installed on the machines measured in +[01](01-what-is-configured-today.md) was a 2.1 release. Each fact names the page it came from. Where the +documentation is silent, this says so. A fact that a design rests on is to be confirmed on one machine +before it is built on (see [03](03-options.md), *What to confirm first*). + +## What a plugin can carry + +A plugin is a directory with a manifest in `.claude-plugin/plugin.json` and, beside it, any of: + +- skills, slash commands and subagents; +- hooks; +- tool servers (`.mcp.json`) and language servers; +- output styles, workflows, themes and monitors; +- a `bin/` directory; +- a `settings.json`. + +Its components are namespaced by the plugin's name, so a subagent `reviewer` in a plugin `mesh` is +`mesh:reviewer`, and it never collides with a person's own of the same name. +— *plugins/manifest-reference, plugins/loading (name conflicts)* + +**What a plugin cannot carry:** + +- **Settings.** Only two keys of a plugin's `settings.json` take effect: the default agent and the + subagent status line. The rest are dropped. — *plugins/manifest-reference, settings* +- **Permission rules.** Not documented as a plugin capability. +- **Instructions.** A `CLAUDE.md` at a plugin's root is not loaded, and the validator warns about it. + Instructions reach a session through skills only. — *plugins/manifest-reference, standard layout* + +## Marketplaces, and a marketplace on the machine's own disk + +A marketplace is a `marketplace.json` listing plugins and where each comes from. Its sources include: + +- a relative path inside the marketplace; +- a forge repository, a git URL or a subdirectory of one; +- a package from a registry; +- an archive over HTTPS; +- the output of a command. + +**A marketplace can be a directory on the machine.** Its plugins with relative paths are **loaded in +place**, not copied into the cache. An edit takes effect at the next session start, or at +`/reload-plugins` in a running session, and the plugin's version need not change. +— *plugins/marketplace-reference (marketplace sources), plugins/loading (in-place and copied plugins)* + +A plugin from any other source is copied into a cache in the home, under +`plugins/cache////`. — *plugins/loading* + +## What managed settings do with plugins + +These keys work in the machine-wide managed settings file — *plugins/org*: + +| key | what it does | +|---|---| +| `extraKnownMarketplaces` | registers a marketplace on every session of the machine | +| `enabledPlugins` | `true` installs and enables a plugin; `false` blocks and hides it at every scope. The managed value outranks every other scope | +| `strictKnownMarketplaces`, `blockedMarketplaces` | allow-list or block-list of marketplace sources | +| `strictPluginOnlyCustomization` | refuses skills, subagents, hooks and tool servers that come from neither a plugin nor managed settings | +| `allowManagedHooksOnly` | runs only the hooks from managed sources | +| `disableSideloadFlags` | blocks loading a plugin from the command line | +| `syncClaudeAiPlugins` | stops plugins synced from the vendor's web account | + +**Installed without anyone being asked.** Once the settings reach a machine, the marketplace is +registered and the plugins installed at the next session start. A non-interactive run installs them in +the background. Managed plugins do not wait for the workspace trust prompt. — *plugins/org* + +## What the managed settings file honours besides + +`permissions` (with its default mode and the switch that disables bypassing it), `autoMode`, `hooks`, +`env`, `model`, `statusLine`, `outputStyle`, `apiKeyHelper`, and the managed-only switches for permission +rules, hooks and tool servers. — *managed-settings* + +That `autoMode` is honoured from the managed file is documented. That it changes what the agent +refuses on these machines is still to be seen ([ADR 0213](../../02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md) +left that open). + +## Tool servers: the exclusive file wins over a plugin's + +When the managed tool-server file is present, as the module writes it, it is **exclusive**: only its +servers load. The vendor's web connectors load too when a managed key allows them. **A plugin's +`.mcp.json` servers are blocked.** — *managed-mcp (exclusive control)* + +So a tool server registered through the module stays in the managed tool-server file. Putting it in +the plugin would silently stop it loading. + +## Variables inside a plugin + +- `${CLAUDE_PLUGIN_ROOT}`: the plugin's directory. +- `${CLAUDE_PLUGIN_DATA}`: a directory that survives updates. +- `${CLAUDE_PROJECT_DIR}`: the project's root. + +These resolve in hook commands, tool and language server configuration, and the content of skills, +subagents and commands. A plugin's declared options (`userConfig`) can be marked sensitive; the agent +asks the person for them and stores them itself. — *plugins/manifest-reference (environment variables)* + +## Reload + +A running session does not see a changed plugin until `/reload-plugins` or a new session. +`/reload-plugins` reloads skills, subagents, hooks and servers. It does not restart monitors. +— *plugins/loading* + +## Not documented + +- a machine-wide directory for bare skills, subagents or commands. Only a plugin enabled by managed + settings puts them machine-wide; +- permission rules or instructions carried by a plugin. diff --git a/01-RESEARCH/029-the-agent-configured-through-its-module/03-options.md b/01-RESEARCH/029-the-agent-configured-through-its-module/03-options.md new file mode 100644 index 0000000..c73e45e --- /dev/null +++ b/01-RESEARCH/029-the-agent-configured-through-its-module/03-options.md @@ -0,0 +1,122 @@ +# 03 — Options + +The route is chosen: a plugin the mesh serves. What is left open is where each kind of item goes, how it +is registered and kept, and what the module does about what it finds in the homes. + +## Where each kind of item goes + +[02](02-what-the-vendor-allows.md) puts a hard limit on the plugin: it carries skills, subagents, commands, +hooks, output styles and language servers, but no settings, no permission rules, no instructions, and +no tool server the exclusive managed file does not list. So there are four places, not one: + +| kind | goes to | why there | +|---|---|---| +| skills, subagents, slash commands, output styles | **the mesh's plugin** | the only machine-wide place the vendor has for them | +| hooks | **the mesh's plugin**, with the scripts beside them | a hook's script can live in the plugin and be named through `${CLAUDE_PLUGIN_ROOT}`. A hook in the managed settings would need its script placed somewhere else | +| tool servers | **the managed tool-server file**, as today | the exclusive file blocks a plugin's servers | +| settings and permission rules | **the managed settings file**, beside the mesh's keys ([ADR 0213](../../02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md)) | a plugin's settings are dropped | +| instructions | **the managed instruction file**, in sections | a plugin's instruction file is not loaded | + +The plugin is reached through two managed keys the module already owns the file for: +`extraKnownMarketplaces`, naming a marketplace directory the module writes, and `enabledPlugins`, set to +`true` for the mesh's plugin. Neither is the operator's to set. Like the attribution key, they are the +mesh's keys and outrank whatever the operator sets. + +### Option A — one plugin + +Everything the mesh serves is in one plugin, `mesh`, so every invocation reads `mesh:`. That is +simple, and the name says where an item came from. + +### Option B — a plugin per source + +One plugin for what the operator registers, and one for what other modules contribute (below). An item +then says in its name whether a person or a module definition put it there. But the operator has two +prefixes to remember, and an item has two owners to ask about. + +*Leaning:* A. Where an item came from belongs in the module's list tool, not in the item's name. + +## Who registers an item + +- **The operator, through the module's tools**, from any machine, for one, several or all of them. The + pattern is the tool-server register tool's, extended to every kind: + - `claude_code__list`, `_register`, `_unregister` for skills, subagents, commands, hooks, output + styles and instruction sections; + - `claude_code_settings_get` / `_set` and `claude_code_permission_allow` / `_deny` / `_ask` / + `_remove` for the managed settings. +- **Another module, through the agent's seat** ([ADR 0210](../../02-DECISIONS/0210-a-tools-configuration-is-its-seat-holders-and-every-other-module-extends-it-through-the-seat.md)). + The forge's module wanting the agent to know how pull requests are made here contributes a skill. It + declares the contribution in its definition, and the controller renders it to the agent's holder on + each machine where both run. That depends on the agent module holding a seat; today it holds none. + +The two meet in the one plugin. A contribution and a registration with the same name are refused at +registration, and the list tool shows the owner of each. + +## Where a registration is kept + +The tool-server registrations live in a key-value bucket the module declares +([ADR 0201](../../02-DECISIONS/0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md)), +keyed `all.` or `.`. Skills differ: a skill is a folder, and it can carry scripts +and reference files beside its main file. Every message on the bus is limited to about a megabyte. + +1. **One value per item.** The item and its files go in one value, refused above a limit well under + the bus's. It is simple, it fits the module's existing state, and every item measured in + [01](01-what-is-configured-today.md) takes 20 KB or less on disk, + the vendor's synced skills aside. But a skill with a large reference file cannot + be registered at all. +2. **The bus's object store for files, the bucket for the item.** Large files are stored in pieces and + the item names them. Nothing in the mesh uses the object store yet, so ADR 0201 would need + extending. +3. **A repository on the forge.** The plugin is built from a repository, and registering an item is a + commit. This is reviewable and versioned. But a register tool would have to write to the forge, + and the forge would sit on the path to every machine. + +*Leaning:* 1 now, with the limit stated and checked at registration. 2 when an item outgrows it. 3 +mixes the operator's configuration into the code review cycle, which it does not need. + +## Where the plugin is written + +The module owns the managed directory, so the marketplace goes under it, written whole by the +module's code: + +- the marketplace file; +- one plugin directory beside it. + +It is loaded in place, so a change takes effect at the next session, or at `/reload-plugins` in a +running one. Nothing is copied into the home. + +## What the module does about the homes + +[01](01-what-is-configured-today.md) found stale predecessor files on every machine. Under +[ADR 0182](../../02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md) +the mesh never writes inside a home except what it owns. So the module can: + +- **report** what it finds: a status tool listing the home's skills, subagents, commands and rule files, + and naming those that duplicate a mesh item or name tools no longer served; +- **import** on request: `claude_code__import` takes an item from one machine's home and registers + it, so a skill written by hand on one workstation becomes the mesh's in one call. Removing the home + copy stays the person's act. + +`strictPluginOnlyCustomization` would make the homes' items stop loading altogether. That is the +operator's choice to make through the managed settings, not a default of the module. + +## What to confirm first, on one workstation + +1. A directory marketplace named in the managed settings, with its plugin enabled there, loads with + no prompt, in place, in an interactive session and in a non-interactive one. +2. The plugin's skills, subagents and commands are offered under `mesh:`, beside the home's own + items, with no collision. +3. A hook in the plugin runs, with its script found through `${CLAUDE_PLUGIN_ROOT}`. +4. The exclusive tool-server file still loads the console, and a server in the plugin does not load, + as documented. +5. `autoMode` in the managed settings changes what the agent refuses (ADR 0213's open point). +6. The account can read the marketplace in the managed directory, which root owns. + +## Questions a decision has to answer + +- One plugin or one per source (leaning: one). +- How a registration is kept, and the size limit (leaning: one value per item, with a stated limit). +- Whether the managed settings are set through tools writing the module's state, or through the + controller's settings layer as ADR 0213 has it. If both, which one wins on the same key. +- Whether the agent module holds a seat, so that other modules can contribute to it. +- Whether the module may say anything about a home beyond reporting, for example a switch the operator + sets to make home items stop loading.