Skip to content

feat(plugins): plugin composability P1 — composition API, compose engine, controller - #616

Draft
ilackarms wants to merge 9 commits into
mainfrom
ilackarms/plugin-composability
Draft

feat(plugins): plugin composability P1 — composition API, compose engine, controller#616
ilackarms wants to merge 9 commits into
mainfrom
ilackarms/plugin-composability

Conversation

@ilackarms

Copy link
Copy Markdown
Contributor

Description

P1 of plugin composability — makes Plugin a composable bundle, per the design in #615 (design-docs/PLUGIN_COMPOSABILITY_SPIKE.md).

Motivation: today a Plugin is a pointer at a single git repo, materialized 1:1. Product wants plugins declaratively composed from registry artifacts (Skills, MCP servers, Prompts) overlaid on an optional base source, compiled deterministically into one flattened bundle — while the API represents the plugin as a collection of reusable objects.

What changed:

  • API: new kindless ComponentRef{Namespace,Name,Tag} (the holding field determines the kind — no kind defaulting/mismatch surface); PluginSpec.Source becomes optional; new Skills/MCPServers/Commands/Instructions composition refs; PluginStatus.ResolvedComponents records the controller's pin of every ref (Skill → commit; inline kinds → spec content hash). Validation requires source and/or composition, rejects duplicate names per field; Plugin.ResolveRefs existence-checks refs at admission.
  • Compose engine (internal/registry/plugins/compose): pure, byte-deterministic flatten. Skills → skills/<name>/** (whole-directory overlay-wins), commands → commands/<name>.md, MCP servers → keyed .mcp.json merge (overlay replaces same-named entry; unrelated base content preserved), instructions → AGENTS.md append. Base plugin.json passes through untouched; a minimal manifest is generated only for pure compositions. Every replacement is recorded in a provenance Report. Bundle ceilings apply to the composed result. MCPEntryFromSpec maps registry MCPServer specs to .mcp.json entries (Remote → remote entry; npm/pypi package → stdio entry; OCI package → terminal invalid).
  • Controller: resolves composition refs after the (now optional) base resolve — gating on a referenced Skill's own pin (ComponentsPending, retryable) vs missing (ComponentMissing, terminal) vs unrepresentable (ComponentInvalid, terminal) — then composes in memory and computes Manifest/Inventory over the composed bundle, so governance sees the true surface. Pin set freezes at spec change (ObservedGeneration gating unchanged).
  • Fingerprint: a Plugin dependency's material now includes ResolvedComponents, so deployments redeploy exactly when a composed pin set changes (byte-identical material for existing source-only plugins — no hash churn).
  • Marketplace compat: composed plugins are skipped in the catalog (ErrComposed) — serving just the base URL would silently drop overlays. Serving composed plugins is gated on the git-backed marketplace (tracked in agentregistry-enterprise#1195).

Tests: table-driven compose suite (overlay-wins, .mcp.json merge/replace, traversal guard, ceilings, 10× determinism), hermetic controller reconcile tests (pure composition, pending/missing/invalid components, base+overlay inventory), fingerprint material tests, admission/ResolveRefs tests, marketplace skip test. make verify green; make lint 0 issues; full unit suite (1388 tests) green.

Change Type

/kind feature

Changelog

Plugins can now be composed from registry artifacts: `PluginSpec` gains `skills`, `mcpServers`, `commands`, and `instructions` refs overlaid on an optional base `source`, resolved to a pinned component set in `PluginStatus.resolvedComponents` and compiled deterministically at consumption time.

Additional Notes

Design of record: #615. P2 (consumption: arctl plugin pull, harness runner compose-at-start) follows separately.

Aligns with the BYO composition contract (enterprise #1265) and the Agent
Skills spec, which requires skills/<name>/ to match the frontmatter name.
Missing/invalid declared names and cross-ref declared-name collisions are
terminal ComponentInvalid; the status pin keeps the registry ref identity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants