feat(plugin): add UserLevel plugin capability layer (PR 2/5)#30
Open
shaun0927 wants to merge 1 commit into
Open
feat(plugin): add UserLevel plugin capability layer (PR 2/5)#30shaun0927 wants to merge 1 commit into
shaun0927 wants to merge 1 commit into
Conversation
Introduces the Ourocode.Plugin.UserLevel namespace that lets the runtime discover installed Ouroboros UserLevel plugins and treat their commands as first-class registry entries without reimplementing trust or storage semantics. What is added: - Ourocode.Plugin.UserLevel.Capability + .Capability.Command Normalized identity and command surface (plugin_id, source, version, install scope, trust scopes, manifest digest, declared commands, expected artifacts, continuation hints). Identity stability via (plugin_id, version, manifest_digest) tuple so re-discovery without manifest changes returns the same struct. - Ourocode.Plugin.UserLevel.Discovery Behaviour with a Discovery.run/2 helper that normalizes raw descriptors into Capability structs and surfaces per-descriptor validation errors separately so one bad command never loses a whole plugin. - Ourocode.Plugin.UserLevel.Discovery.OuroborosCLI First discovery adapter; invokes `ouroboros plugin list --json` via a pluggable command runner. Tests inject a stub runner to avoid spawning real processes. Failure modes (exit != 0, runner unavailable, malformed JSON, unexpected shape) all surface as structured errors. - Ourocode.Plugin.UserLevel.Registry Small Agent that caches the latest discovery snapshot with a 60 s TTL, explicit refresh, and identity-preserving merge. Discovery failure degrades the snapshot but preserves last good capabilities, so missing/broken ouroboros CLI never blocks boot. - Ourocode.Plugin.UserLevel.RegistryEntry Projects Capability into the existing Command.Registry plugin-source entry shape (mirrors PluginSurfaceEntry's metadata so the existing CapabilityPreflight.Trust and Projection modules apply unchanged). What is NOT changed in this PR: - No supervision wiring — the registry is standalone and ships dead code until PR 4 wires it into application_services.ex alongside the dispatch adapter that needs it. This keeps PR 2 boot-safe. - No new slash command — `/plugins refresh` ships with PR 4. - No router/dispatcher changes — those land in PR 3. Tests: 5 ExUnit files (1255 LOC total with lib code) cover capability shape, identity, command lookup, discovery normalization, OuroborosCLI parsing of the superpowers fixture, registry TTL + degraded handling, identity-stable merge, and registry projection into the existing plugin-source entry shape. Closes Q00#5 Closes Q00#8 Closes Q00#9 Closes Q00#18 Closes Q00#27 Closes Q00#29 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6599c4c to
9769f52
Compare
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.
Summary
PR 2 of the five-PR plan under SSOT #25 (see PR #3 /
docs/userlevel-plugin-dispatch.md).Adds the
Ourocode.Plugin.UserLevel.*namespace so the runtime can discover installed Ouroboros UserLevel plugins and treat their commands as first-class capabilities — without reimplementing trust, installation, or plugin-internal storage semantics.What ships
Ourocode.Plugin.UserLevel.Capability+.Capability.Command— normalized identity and command surface (plugin_id, source, version, install scope, trust scopes, manifest digest, declared commands, expected artifacts, continuation hints). Identity stability via(plugin_id, version, manifest_digest)so re-discovery without manifest changes returns the same struct.Ourocode.Plugin.UserLevel.Discovery— behaviour +run/2helper that normalizes raw descriptors intoCapabilitystructs and reports per-descriptor errors separately so one bad command never loses the whole plugin.Ourocode.Plugin.UserLevel.Discovery.OuroborosCLI— first discovery adapter; callsouroboros plugin list --jsonthrough a pluggable command runner (tests inject a stub runner; no real processes spawned). Failure modes — exit ≠ 0, runner unavailable, malformed JSON, unexpected shape — all surface as structured errors.Ourocode.Plugin.UserLevel.Registry— small Agent that caches the most recent discovery snapshot with a 60 s TTL, explicitrefresh/2, and identity-preserving merge. Discovery failure degrades the snapshot but preserves the last good capability list; missing/brokenouroborosCLI never blocks boot.Ourocode.Plugin.UserLevel.RegistryEntry— projectsCapabilityinto the existingCommand.Registryplugin-source entry shape (mirrorsPluginSurfaceEntry's metadata so the existingCapabilityPreflight.TrustandProjectionmodules apply unchanged).test/fixtures/user_level_plugins/superpowers.jsonmatching theouroboros plugin list --jsonshape with four superpowers commands.What is intentionally NOT in this PR
ApplicationServicesalongside the dispatch adapter that actually consumes it. This keeps PR 2 boot-safe — missingouroborosCLI cannot regress startup./plugins refreshslash command. Ships with PR 4 once the registry is supervised.:user_level_pluginroute) and PR 4 (UserLevelPluginInvocationadapter).Why this shape
ourocodealready has a strong slash-command preflight surface (Ourocode.Command.CapabilityPreflight). UserLevel plugin entries projected byRegistryEntryreuse that surface as-is — the only thing missing for the slash path was the discovery feed itself. Theooo-prefixed dispatch path remains the work of PRs 3-5.Trust defaults are conservative: capabilities without declared trust scopes are marked
requires_explicit_approval: trueandtrusted: false. This means existingCapabilityPreflight.Trust.boundary/1returns:requires_approval, keeping the safe path even when Ouroboros has not yet surfaced an explicit grant.Closes
Testing
async: true) covering capability shape + identity + command lookup, discovery normalization with valid/invalid descriptors, OuroborosCLI parsing + runner failure modes, registry TTL + degraded handling + identity-stable merge + fetch, and projection into the existing plugin-source registry entry shape.mix test test/ourocode/plugin/user_levelis green before approving.Stacking note
PR 3 will branch off
feat/plugin-user-level-capability; review this PR first to keep downstream diffs minimal.🤖 Generated with Claude Code