Liking cljdoc? Tell your friends :D

hive-addon.capability

Capability contract vocabulary: the declared, machine-readable description of what one MCP tool's commands are, what they accept, and how stable they are.

Contract: a manifest is DATA. It holds no handlers and never dereferences a :schema — schema refs are registry keys, resolved by hive-spi.schema.capability. Deps: hive-dsl + malli only.

Capability contract vocabulary: the declared, machine-readable description of
what one MCP tool's commands are, what they accept, and how stable they are.

Contract: a manifest is DATA. It holds no handlers and never dereferences a
:schema — schema refs are registry keys, resolved by hive-spi.schema.capability.
Deps: hive-dsl + malli only.
raw docstring

hive-addon.cli

CLI-style subcommand dispatch for a consolidated tool.

make-handler turns a handler tree into a single fn dispatching on the :command parameter, supporting n-depth paths ("status list") alongside single-word commands.

CLI-style subcommand dispatch for a consolidated tool.

`make-handler` turns a handler tree into a single fn dispatching on the
:command parameter, supporting n-depth paths ("status list") alongside
single-word commands.
raw docstring

hive-addon.cli.response

The tool-result shape a CLI handler returns.

A host may enrich these — attaching recovery hints or pending agent instructions — by passing its own builders to hive-addon.cli/make-handler.

The tool-result shape a CLI handler returns.

A host may enrich these — attaching recovery hints or pending agent
instructions — by passing its own builders to `hive-addon.cli/make-handler`.
raw docstring

hive-addon.cli.tree

Command-path parsing and handler-tree resolution.

A handler tree maps keyword segments either to a handler fn or to a nested tree. A nested tree may carry :_handler as the value used when a path stops there, or when no deeper segment matches.

Command-path parsing and handler-tree resolution.

A handler tree maps keyword segments either to a handler fn or to a
nested tree. A nested tree may carry :_handler as the value used when a
path stops there, or when no deeper segment matches.
raw docstring

hive-addon.hot

Hot-reload for IAddon instances — the public surface a host consumes.

Reloading a namespace updates VARS. It does not update the addon INSTANCE the host registered at mount time, nor the sibling instances that were injected into its dependents' configs. Those objects were captured when they were constructed and are frozen; a namespace reload leaves the system holding old objects that call new code, which is the silent-corruption shape. What this namespace does is reconstruct them from the one thing a reload cannot invalidate: the MountSpec, which carries :addon/init-ns + :addon/init-fn as data. Remount is therefore the ordinary mount pipeline re-run over the affected slice, not a second registry.

Layout: hive-addon.hot.source — where an addon's source lives (:local/root vs jar) hive-addon.hot.cascade — pure: which addons a reload touches, in what order hive-addon.hot.strategy — OCP: how each addon is reloaded hive-addon.hot — this facade: wiring, triggering, reporting

hive-hot is a SOFT dependency, resolved through the var at call time and never required. Without it every function here still works — reload-addon! falls back to require :reload — and hot! degrades to a report saying :hot/available? false instead of throwing. Consumers that want file-watching add io.github.hive-agi/hive-hot themselves.

THE ONE HARD RULE: never reload hive-addon.protocol. Reloading a protocol-defining namespace mints a new protocol var, and every live addon instance — same class NAME, different class OBJECT — stops satisfying it, so dispatch misses with an error that reads as if the method were never implemented. no-reload is that rule as data; feed it to hive-hot's :no-reload and the hazard cannot be tripped.

Hot-reload for IAddon instances — the public surface a host consumes.

Reloading a namespace updates VARS. It does not update the addon INSTANCE the
host registered at mount time, nor the sibling instances that were injected
into its dependents' configs. Those objects were captured when they were
constructed and are frozen; a namespace reload leaves the system holding old
objects that call new code, which is the silent-corruption shape. What this
namespace does is reconstruct them from the one thing a reload cannot
invalidate: the MountSpec, which carries `:addon/init-ns` + `:addon/init-fn`
as data. Remount is therefore the ordinary mount pipeline re-run over the
affected slice, not a second registry.

Layout:
  hive-addon.hot.source    — where an addon's source lives (:local/root vs jar)
  hive-addon.hot.cascade   — pure: which addons a reload touches, in what order
  hive-addon.hot.strategy  — OCP: how each addon is reloaded
  hive-addon.hot           — this facade: wiring, triggering, reporting

hive-hot is a SOFT dependency, resolved through the var at call time and never
required. Without it every function here still works — `reload-addon!` falls
back to `require :reload` — and `hot!` degrades to a report saying
:hot/available? false instead of throwing. Consumers that want file-watching
add io.github.hive-agi/hive-hot themselves.

THE ONE HARD RULE: never reload hive-addon.protocol. Reloading a
protocol-defining namespace mints a new protocol var, and every live addon
instance — same class NAME, different class OBJECT — stops satisfying it, so
dispatch misses with an error that reads as if the method were never
implemented. `no-reload` is that rule as data; feed it to hive-hot's
:no-reload and the hazard cannot be tripped.
raw docstring

hive-addon.hot.cascade

Which addons a reload actually touches — the pure Pipeline stratum of the hot-reload bridge. No IO, no var resolution, no host.

Reloading addon A is not enough. Every addon that received A's INSTANCE at mount time (through the mounter's :mount/dependencies sibling injection) still holds the pre-reload object; leaving them alone leaves the system half old and half new, which is the same silent-corruption shape as a partial namespace reload. So a reload seeds at the changed addons and closes forward over the dependency graph.

The graph is NOT recomputed here: edges and solve come from hive-addon.mount.solve, so the order a reload uses is the same order the original mount used, produced by the same rule chain (and extended by the same custom :rules).

Which addons a reload actually touches — the pure Pipeline stratum of the
hot-reload bridge. No IO, no var resolution, no host.

Reloading addon A is not enough. Every addon that received A's INSTANCE at
mount time (through the mounter's `:mount/dependencies` sibling injection)
still holds the pre-reload object; leaving them alone leaves the system half
old and half new, which is the same silent-corruption shape as a partial
namespace reload. So a reload seeds at the changed addons and closes forward
over the dependency graph.

The graph is NOT recomputed here: `edges` and `solve` come from
hive-addon.mount.solve, so the order a reload uses is the same order the
original mount used, produced by the same rule chain (and extended by the
same custom `:rules`).
raw docstring

hive-addon.hot.inject

Inject an addon that was NOT on the classpath when the host booted.

inject! is the mount pipeline run over a slice that did not exist yet: put the addon's paths on the live classpath, discover the manifests under them (and ONLY under them — an addon already on the classpath that the composer chose not to mount must not be resurrected by a scan), solve the new specs against the peers already mounted, tear down the mounted dependents that now have a new sibling to receive, mount the slice through the ordinary IMountDriver, and register the new addons with hive-hot so they reload like the rest.

Classpath extension is a JVM concern: the URL is added to the highest DynamicClassLoader above the calling thread's context loader, which is the loader every later require resolves through in an nREPL-hosted image. A thread with no DynamicClassLoader in its chain cannot extend the classpath; inject! then REFUSES with :hot/no-dynamic-classloader rather than mounting code the next require would fail to find.

Maven dependencies of the injected addon are NOT resolved by default; pass :resolve-deps? true to hand the project's deps.edn :deps to clojure.repl.deps/add-libs first (needs a tools.deps basis in the image).

Inject an addon that was NOT on the classpath when the host booted.

`inject!` is the mount pipeline run over a slice that did not exist yet:
put the addon's paths on the live classpath, discover the manifests under
them (and ONLY under them — an addon already on the classpath that the
composer chose not to mount must not be resurrected by a scan), solve the
new specs against the peers already mounted, tear down the mounted
dependents that now have a new sibling to receive, mount the slice through
the ordinary IMountDriver, and register the new addons with hive-hot so
they reload like the rest.

Classpath extension is a JVM concern: the URL is added to the highest
DynamicClassLoader above the calling thread's context loader, which is the
loader every later `require` resolves through in an nREPL-hosted image. A
thread with no DynamicClassLoader in its chain cannot extend the classpath;
inject! then REFUSES with :hot/no-dynamic-classloader rather than mounting
code the next require would fail to find.

Maven dependencies of the injected addon are NOT resolved by default; pass
`:resolve-deps? true` to hand the project's deps.edn :deps to
clojure.repl.deps/add-libs first (needs a tools.deps basis in the image).
raw docstring

hive-addon.hot.mount-driver

JVM adapter: IMountDriver over hive-addon.mount.boundary.

boundary reaches the classpath, JarFile, URL and the context classloader, so it is the host-bound half of a remount and lives behind the port rather than inside the portable core.

JVM adapter: IMountDriver over hive-addon.mount.boundary.

boundary reaches the classpath, JarFile, URL and the context classloader, so
it is the host-bound half of a remount and lives behind the port rather than
inside the portable core.
raw docstring

hive-addon.hot.port

Ports through which the portable hot-reload core reaches host-specific work.

The core — schema, cascade, strategy — is portable .cljc and names only these protocols. One adapter namespace per host implements them; a per-host wiring namespace constructs the adapters and injects them into the reload context.

Adapters are constructed by FUNCTIONS, never held in a def or defonce: an instance built at load time keeps the class object of whichever protocol var was current then, and stops satisfying the protocol once this namespace is reloaded.

Ports through which the portable hot-reload core reaches host-specific work.

The core — schema, cascade, strategy — is portable .cljc and names only these
protocols. One adapter namespace per host implements them; a per-host wiring
namespace constructs the adapters and injects them into the reload context.

Adapters are constructed by FUNCTIONS, never held in a `def` or `defonce`: an
instance built at load time keeps the class object of whichever protocol var
was current then, and stops satisfying the protocol once this namespace is
reloaded.
raw docstring

hive-addon.hot.schema

Malli value objects for the IAddon hot-reload bridge.

Shapes are uncompiled malli DATA (house idiom: PascalCase defs) seeded into a LOCAL composite registry that COMPOSES hive-addon.mount.schema's registry — AddonId/MountResult/MountSpec are reused, never redefined. The registry is NEVER installed as the malli global default; reach it via schema/validate/explain/validate* or by passing {:registry registry}.

Hot shapes are registered under :hot/* keys (:hot/registration, :hot/report, :hot/remount-report, :hot/source, :hot/strategy-id).

Two invariants are carried as DATA rather than prose:

  • RemountReport's :teardown/data-preserved? is [:= true] — the no-nuke invariant inherited from TeardownReport.
  • :hot/strategy-id is an open :keyword, never an enum. The strategy set is extensible by any module (OCP); closing it here would be the defect.
Malli value objects for the IAddon hot-reload bridge.

Shapes are uncompiled malli DATA (house idiom: PascalCase defs) seeded into a
LOCAL composite registry that COMPOSES hive-addon.mount.schema's registry —
AddonId/MountResult/MountSpec are reused, never redefined. The registry is
NEVER installed as the malli global default; reach it via
`schema`/`validate`/`explain`/`validate*` or by passing {:registry registry}.

Hot shapes are registered under :hot/* keys (:hot/registration, :hot/report,
:hot/remount-report, :hot/source, :hot/strategy-id).

Two invariants are carried as DATA rather than prose:
- RemountReport's :teardown/data-preserved? is [:= true] — the no-nuke
  invariant inherited from TeardownReport.
- :hot/strategy-id is an open :keyword, never an enum. The strategy set is
  extensible by any module (OCP); closing it here would be the defect.
raw docstring

hive-addon.hot.source

Where an addon's constructor namespace physically lives — the Collect stratum of the hot-reload bridge.

An addon wired as a :local/root dep (typically through an untracked local.deps.edn override) puts its src on the classpath as a real DIRECTORY: the bytes on disk can change, so clj-reload can watch and reload it. The same addon consumed as an :mvn/version coordinate arrives inside a JAR, whose bytes cannot change without a restart — hot-reload of it is not merely unsupported, it is meaningless.

That distinction is the input to strategy selection, so it is resolved here as DATA (an AddonSource) rather than guessed at the reload site.

Pure apart from classpath lookup; never throws — an unresolvable namespace becomes {:hot/source-kind :absent}.

Where an addon's constructor namespace physically lives — the Collect stratum
of the hot-reload bridge.

An addon wired as a `:local/root` dep (typically through an untracked
local.deps.edn override) puts its `src` on the classpath as a real DIRECTORY:
the bytes on disk can change, so clj-reload can watch and reload it. The same
addon consumed as an `:mvn/version` coordinate arrives inside a JAR, whose
bytes cannot change without a restart — hot-reload of it is not merely
unsupported, it is meaningless.

That distinction is the input to strategy selection, so it is resolved here as
DATA (an AddonSource) rather than guessed at the reload site.

Pure apart from classpath lookup; never throws — an unresolvable namespace
becomes {:hot/source-kind :absent}.
raw docstring

hive-addon.hot.strategy

How an addon's new code is brought into a live system — the OCP seam of the hot-reload bridge.

The set of reload strategies is OPEN. Most addons want the default (:remount: shut down, reconstruct from the manifest, re-initialize, cascade to dependents), but an addon with particular needs — an open socket it must drain, a native handle it cannot re-acquire, a stateless dispatcher that only needs its vars refreshed — supplies its own. So strategies are a PROTOCOL with a rule chain, never a case over a closed keyword set: closing this set would be the defect, and adding a strategy must never mean editing this namespace.

Selection, in order:

  1. A spec that DECLARES :addon/reload-strategy <id> gets exactly that strategy, looked up by id. A declared-but-unregistered id is an error, not a silent fallback to the default — an addon that asked for special handling and quietly got generic handling is worse than one that refused.
  2. Otherwise the first chain member whose -applies? answers true. The built-in chain ends in a catch-all, so selection always succeeds.

Registration is a var-held chain plus an install seam, mirroring the licence gate in hive-addon.mount.entitlement. Strategies are resolved from the var at CALL time, never captured at wiring time.

Portable: this namespace names no host API. The two effectful collaborators — the namespace reloader and the mount driver — arrive through the reload context (:hot/reload-ns!, :hot/mount-driver), so hive-hot stays a soft dependency and the classpath-bound mount boundary stays behind hive-addon.hot.port/IMountDriver.

How an addon's new code is brought into a live system — the OCP seam of the
hot-reload bridge.

The set of reload strategies is OPEN. Most addons want the default
(`:remount`: shut down, reconstruct from the manifest, re-initialize, cascade
to dependents), but an addon with particular needs — an open socket it must
drain, a native handle it cannot re-acquire, a stateless dispatcher that only
needs its vars refreshed — supplies its own. So strategies are a PROTOCOL with
a rule chain, never a `case` over a closed keyword set: closing this set would
be the defect, and adding a strategy must never mean editing this namespace.

Selection, in order:
1. A spec that DECLARES `:addon/reload-strategy <id>` gets exactly that
   strategy, looked up by id. A declared-but-unregistered id is an error, not
   a silent fallback to the default — an addon that asked for special handling
   and quietly got generic handling is worse than one that refused.
2. Otherwise the first chain member whose `-applies?` answers true.
The built-in chain ends in a catch-all, so selection always succeeds.

Registration is a var-held chain plus an install seam, mirroring the licence
gate in hive-addon.mount.entitlement. Strategies are resolved from the var at
CALL time, never captured at wiring time.

Portable: this namespace names no host API. The two effectful collaborators —
the namespace reloader and the mount driver — arrive through the reload
context (`:hot/reload-ns!`, `:hot/mount-driver`), so hive-hot stays a soft
dependency and the classpath-bound mount boundary stays behind
hive-addon.hot.port/IMountDriver.
raw docstring

hive-addon.mount

Facade for the addon mounter — the public surface a host consumes.

Re-exports the pure solver (solve), the effectful boundary (mount!, dry-run, teardown!, discover-specs, parse-spec), the DIP config resolver + host constructor (resolve-config-default, atom-mount-host), and the schema validators/keys. mount-classpath! is the one-call composition root: discover-specs -> solve -> mount!. The IMountHost protocol is NOT re-exported (a protocol cannot be plain-def aliased) — implement it from its canonical home hive-addon.mount.port.

Rationale lives in hive memory (KG-linked), not here.

Facade for the addon mounter — the public surface a host consumes.

Re-exports the pure solver (solve), the effectful boundary (mount!, dry-run,
teardown!, discover-specs, parse-spec), the DIP config resolver + host
constructor (resolve-config-default, atom-mount-host), and the schema
validators/keys. mount-classpath! is the one-call composition root:
discover-specs -> solve -> mount!. The IMountHost protocol is NOT re-exported
(a protocol cannot be plain-def aliased) — implement it from its canonical
home hive-addon.mount.port.

Rationale lives in hive memory (KG-linked), not here.
raw docstring

hive-addon.mount.boundary

Effectful boundary of the addon mounter — all IO and var resolution, injected through the IMountHost port and an optional config resolver.

Collect: discover-specs scans the classpath for META-INF/hive-addons/*.edn. Promote: parse-spec turns an EDN string into a validated MountSpec Result. Pipeline is hive-addon.mount.solve (pure, elsewhere). Boundary: mount!/dry-run/teardown! resolve constructors, inject already-mounted sibling instances into each dependent's config (DIP), and drive the host.

mount! GRACEFULLY DEGRADES: a spec that fails at any step is recorded in the MountReport and the loop CONTINUES; already-mounted addons are NEVER torn down on a mid-DAG failure. teardown! shuts down in reverse mount order and always reports :teardown/data-preserved? true (shutdown! never deletes data).

Effectful boundary of the addon mounter — all IO and var resolution, injected
through the IMountHost port and an optional config resolver.

Collect: discover-specs scans the classpath for META-INF/hive-addons/*.edn.
Promote: parse-spec turns an EDN string into a validated MountSpec Result.
Pipeline is hive-addon.mount.solve (pure, elsewhere).
Boundary: mount!/dry-run/teardown! resolve constructors, inject already-mounted
sibling instances into each dependent's config (DIP), and drive the host.

mount! GRACEFULLY DEGRADES: a spec that fails at any step is recorded in the
MountReport and the loop CONTINUES; already-mounted addons are NEVER torn down
on a mid-DAG failure. teardown! shuts down in reverse mount order and always
reports :teardown/data-preserved? true (shutdown! never deletes data).
raw docstring

hive-addon.mount.compose

Composition root: select discovered MountSpecs through declarative plug layers (build.edn / iaddon.edn), then order + mount them.

Joins the two pure engines by :addon/id — hive-addon.plug (SELECT, keyed by lib-sym) and hive-addon.mount (ORDER + EXECUTE, keyed by :addon/id). Per-addon plug :config overrides merge on top of the injected base config resolver. With no plug layers (or none carrying :iaddon/plugs) every discovered spec is kept — behaviourally identical to mount/mount-classpath!.

Composition root: select discovered MountSpecs through declarative plug layers
(build.edn / iaddon.edn), then order + mount them.

Joins the two pure engines by :addon/id — hive-addon.plug (SELECT, keyed by
lib-sym) and hive-addon.mount (ORDER + EXECUTE, keyed by :addon/id). Per-addon
plug :config overrides merge on top of the injected base config resolver.
With no plug layers (or none carrying :iaddon/plugs) every discovered spec is
kept — behaviourally identical to mount/mount-classpath!.
raw docstring

hive-addon.mount.entitlement

Whether a MountSpec is permitted to mount at all. Pure.

The mounter depends on this abstraction, never on a licence implementation: a gate is any ILicenseGate, or any (fn [spec] -> nil | reason-keyword). Returning nil permits; returning a keyword refuses and names the reason.

A spec whose :addon/trust-class is :proprietary is refused unless a gate is installed, so the default posture of an unconfigured host is closed.

Whether a MountSpec is permitted to mount at all. Pure.

The mounter depends on this abstraction, never on a licence implementation:
a gate is any ILicenseGate, or any (fn [spec] -> nil | reason-keyword).
Returning nil permits; returning a keyword refuses and names the reason.

A spec whose :addon/trust-class is :proprietary is refused unless a gate is
installed, so the default posture of an unconfigured host is closed.
raw docstring

hive-addon.mount.port

DIP seam for the addon mounter — the host registry abstraction.

IMountHost is the port through which the effectful boundary registers, initializes, shuts down, and looks up addon instances. hive-addon ships one in-memory implementation (atom-mount-host) for tests, dry-run, and non-MCP hosts; a real host (an MCP server) supplies its own. resolve-config-default is the identity-ish config resolver — a host may inject a richer one (e.g. hive-di-backed) at the boundary.

register!/shutdown! are no-nuke: a duplicate register! MUST NOT throw and shutdown! MUST NOT delete data.

DIP seam for the addon mounter — the host registry abstraction.

IMountHost is the port through which the effectful boundary registers,
initializes, shuts down, and looks up addon instances. hive-addon ships one
in-memory implementation (atom-mount-host) for tests, dry-run, and non-MCP
hosts; a real host (an MCP server) supplies its own. resolve-config-default
is the identity-ish config resolver — a host may inject a richer one (e.g.
hive-di-backed) at the boundary.

register!/shutdown! are no-nuke: a duplicate register! MUST NOT throw and
shutdown! MUST NOT delete data.
raw docstring

hive-addon.mount.schema

Malli value objects for the addon mounter.

Declarative mount manifests (MountSpec), the pure solver output (MountPlan), and the effectful outcome reports (MountResult, MountReport, TeardownReport). Shapes are uncompiled malli DATA (house idiom: PascalCase defs) seeded into a LOCAL composite registry that COMPOSES hive-addon.schema's registry — the AddonId/AddonType/CapabilitySet value objects are reused, never redefined. The registry is NEVER installed as the malli global default; reach it via schema/validate/explain/validate* or by passing {:registry registry} yourself.

Mount shapes are registered under :mount/* keys (:mount/spec, :mount/plan, :mount/result, :mount/report, :mount/teardown-report).

TeardownReport carries the no-nuke invariant as data: :teardown/data-preserved? is [:= true], so a report can only validate when teardown preserved data.

Malli value objects for the addon mounter.

Declarative mount manifests (MountSpec), the pure solver output (MountPlan),
and the effectful outcome reports (MountResult, MountReport, TeardownReport).
Shapes are uncompiled malli DATA (house idiom: PascalCase defs) seeded into a
LOCAL composite registry that COMPOSES hive-addon.schema's registry — the
AddonId/AddonType/CapabilitySet value objects are reused, never redefined.
The registry is NEVER installed as the malli global default; reach it via
`schema`/`validate`/`explain`/`validate*` or by passing {:registry registry}
yourself.

Mount shapes are registered under :mount/* keys (:mount/spec, :mount/plan,
:mount/result, :mount/report, :mount/teardown-report).

TeardownReport carries the no-nuke invariant as data: :teardown/data-preserved?
is [:= true], so a report can only validate when teardown preserved data.
raw docstring

hive-addon.mount.solve

Pure DAG solver for the addon mounter — spec set -> MountPlan. No IO, no var resolution.

Ordering constraints are a rule-chain: IDependencyRule turns the spec set into directed [from-id to-id] edges (from mounts before to); edges folds the chain into one edge set. Two built-in rules cover hard id deps (:addon/dependencies) and looser capability deps (:addon/requires-capabilities satisfied by any peer whose :addon/capabilities contains the cap).

solve is a pure deterministic fn of the spec SET: it Kahn topo-sorts with a lowest-:addon/id lexicographic tie-break, so shuffled input yields an identical :ordered. Cycles/missing-deps/unmet-capabilities are diagnosed as data. Graceful by default (acyclic subset ordered, cycles reported); opt-in :fail-closed-cycles true returns (r/err :mount/unsolvable ...).

PORTABLE STRATUM. This namespace is in the require closure of the hot-reload core and must load and BEHAVE IDENTICALLY on the JVM, cljw and cljrs. Three admission rules follow, each mechanically checkable and each derived from a measured divergence rather than from caution:

  • Zero reader conditionals and zero host interop. :clj does not select the JVM: cljw presents :clj and cljrs presents :rust.
  • No for. On cljrs a second binding and :let are unbound-symbol errors, and :when is SILENTLY IGNORED — in topo-sort that would emit a wrong mount order while reporting success. mapcat/keep/reduce say the same thing and are cleared on all three.
  • No :or destructuring default that is not SELF-EVALUATING. cljrs does not evaluate the default, so a symbol default binds the symbol itself; rules became the symbol default-rules, edges folded over a symbol into an EMPTY edge set, and solve returned a lexicographic order reporting no cycles. Literal defaults (false, 0, :kw) are unaffected, which is exactly what hides the bug. Use (or x default) in the body.

The three rules are enforced by hive-addon.mount.portable-test, which reads this stratum's source and fails on a violation, and the behaviour is pinned by the tri-runtime differential oracle in test/portable/oracle.cljc.

Pure DAG solver for the addon mounter — spec set -> MountPlan. No IO, no var
resolution.

Ordering constraints are a rule-chain: IDependencyRule turns the spec set
into directed [from-id to-id] edges (from mounts before to); `edges` folds the
chain into one edge set. Two built-in rules cover hard id deps
(:addon/dependencies) and looser capability deps (:addon/requires-capabilities
satisfied by any peer whose :addon/capabilities contains the cap).

`solve` is a pure deterministic fn of the spec SET: it Kahn topo-sorts with a
lowest-:addon/id lexicographic tie-break, so shuffled input yields an
identical :ordered. Cycles/missing-deps/unmet-capabilities are diagnosed as
data. Graceful by default (acyclic subset ordered, cycles reported); opt-in
:fail-closed-cycles true returns (r/err :mount/unsolvable ...).

PORTABLE STRATUM. This namespace is in the require closure of the hot-reload
core and must load and BEHAVE IDENTICALLY on the JVM, cljw and cljrs. Three
admission rules follow, each mechanically checkable and each derived from a
measured divergence rather than from caution:

- Zero reader conditionals and zero host interop. `:clj` does not select the
  JVM: cljw presents `:clj` and cljrs presents `:rust`.
- No `for`. On cljrs a second binding and `:let` are unbound-symbol errors,
  and `:when` is SILENTLY IGNORED — in `topo-sort` that would emit a wrong
  mount order while reporting success. `mapcat`/`keep`/`reduce` say the same
  thing and are cleared on all three.
- No `:or` destructuring default that is not SELF-EVALUATING. cljrs does not
  evaluate the default, so a symbol default binds the symbol itself; `rules`
  became the symbol `default-rules`, `edges` folded over a symbol into an
  EMPTY edge set, and solve returned a lexicographic order reporting no
  cycles. Literal defaults (`false`, `0`, `:kw`) are unaffected, which is
  exactly what hides the bug. Use `(or x default)` in the body.

The three rules are enforced by hive-addon.mount.portable-test, which reads
this stratum's source and fails on a violation, and the behaviour is pinned
by the tri-runtime differential oracle in test/portable/oracle.cljc.
raw docstring

hive-addon.opaque.schema

Malli value objects for the OPAQUE addon wire — the contract between a host proxying an IAddon and the out-of-process kernel that really implements it.

Shapes are uncompiled malli DATA (house idiom: PascalCase defs) seeded into a LOCAL composite registry that COMPOSES hive-addon.schema's registry — the AddonId/AddonType/CapabilitySet value objects are reused, never redefined. The registry is NEVER installed as the malli global default; reach it via schema/validate/explain/validate* or by passing {:registry registry}.

Opaque shapes are registered under :opaque/* keys (:opaque/wire, :opaque/request, :opaque/response, :opaque/describe, :opaque/spec).

The kernel side never loads this namespace: hive-addon.opaque.codec and hive-addon.opaque.serve are malli-free so a cljw-built binary carries no schema runtime. These schemas are the host-side single source the m/=> contracts (hive-addon.opaque.contracts) and the generated tests read.

Malli value objects for the OPAQUE addon wire — the contract between a host
proxying an IAddon and the out-of-process kernel that really implements it.

Shapes are uncompiled malli DATA (house idiom: PascalCase defs) seeded into a
LOCAL composite registry that COMPOSES hive-addon.schema's registry — the
AddonId/AddonType/CapabilitySet value objects are reused, never redefined.
The registry is NEVER installed as the malli global default; reach it via
`schema`/`validate`/`explain`/`validate*` or by passing {:registry registry}.

Opaque shapes are registered under :opaque/* keys (:opaque/wire,
:opaque/request, :opaque/response, :opaque/describe, :opaque/spec).

The kernel side never loads this namespace: hive-addon.opaque.codec and
hive-addon.opaque.serve are malli-free so a cljw-built binary carries no
schema runtime. These schemas are the host-side single source the m/=>
contracts (hive-addon.opaque.contracts) and the generated tests read.
raw docstring

hive-addon.plug

Resolve ordered iaddon.edn layers (lowest precedence first) into the selected plug set: merge → validate → lint → profile → drop-disabled → capability-select.

Resolve ordered iaddon.edn layers (lowest precedence first) into the selected
plug set: merge → validate → lint → profile → drop-disabled → capability-select.
raw docstring

hive-addon.plug.lint

Fail-closed lint of a merged iaddon config — an extensible registry of pure rules.

Fail-closed lint of a merged iaddon config — an extensible registry of pure rules.
raw docstring

hive-addon.plug.merge

Fold iaddon config layers into one, with the trust security inversion applied to :iaddon/trust so higher-precedence layers can only tighten, never loosen.

Fold iaddon config layers into one, with the trust security inversion applied to
:iaddon/trust so higher-precedence layers can only tighten, never loosen.
raw docstring

hive-addon.plug.schema

Malli schemas for iaddon.edn (:iaddon/* keys), in a local composite registry. Credential values never appear here — only reference chains.

Malli schemas for iaddon.edn (:iaddon/* keys), in a local composite registry.
Credential values never appear here — only reference chains.
raw docstring

hive-addon.plug.source

Plug sources as trust-aware value objects: a tools.deps coord classified into a family with its mutability/locality, behind the ISource protocol.

Plug sources as trust-aware value objects: a tools.deps coord classified into a
family with its mutability/locality, behind the ISource protocol.
raw docstring

hive-addon.protocol

IAddon protocol — THE single source of truth for addon contracts.

A host loads addons that implement this protocol to gain capabilities (DIP): the host depends on this abstraction, never the concrete addons, and no addon compile-depends on the host. Any project — an MCP server or something unrelated — can host addons by consuming this leaf lib.

All addons implement this protocol. A host's registry operates on instances via this abstraction barrier.

Addon types:

  • :native — Built-in Clojure addons (same JVM)
  • :mcp-bridge — External MCP servers proxied via stdio/sse/http
  • :external — Non-MCP integrations (REST APIs, CLIs, etc.)

Lifecycle: (initialize! addon config) → Start services, open connections (shutdown! addon) → Release resources, close connections (health addon) → Query current health status

IAddon protocol — THE single source of truth for addon contracts.

A host loads addons that implement this protocol to gain capabilities
(DIP): the host depends on this abstraction, never the concrete addons,
and no addon compile-depends on the host. Any project — an MCP server or
something unrelated — can host addons by consuming this leaf lib.

All addons implement this protocol. A host's registry operates on
instances via this abstraction barrier.

Addon types:
- :native      — Built-in Clojure addons (same JVM)
- :mcp-bridge  — External MCP servers proxied via stdio/sse/http
- :external    — Non-MCP integrations (REST APIs, CLIs, etc.)

Lifecycle:
  (initialize! addon config) → Start services, open connections
  (shutdown! addon)          → Release resources, close connections
  (health addon)             → Query current health status
raw docstring

hive-addon.registry.commands

Command contributions to a composite tool.

An addon contributes named subcommands to a host tool, so analysis command="lint ..." reaches the contributing addon's handler. Each contribution records the contributing addon so shutdown can retract everything that addon added.

Shape: {tool-name {command-name {:handler fn :params {} :description str :addon addon-id}}}

Command contributions to a composite tool.

An addon contributes named subcommands to a host tool, so
`analysis command="lint ..."` reaches the contributing addon's handler.
Each contribution records the contributing addon so shutdown can retract
everything that addon added.

Shape: {tool-name {command-name {:handler fn :params {} :description str
                                 :addon addon-id}}}
raw docstring

hive-addon.registry.extension

Opaque capability registry.

An addon registers an implementation under a keyword key at startup; a consumer looks it up without knowing which addon provides it:

(register! :gs/struct-cmp my-cmp-fn)

(if-let [f (get-extension :gs/struct-cmp)] (f a b) fallback)

Registration is atomic and idempotent — re-registering a key replaces it.

Opaque capability registry.

An addon registers an implementation under a keyword key at startup; a
consumer looks it up without knowing which addon provides it:

  (register! :gs/struct-cmp my-cmp-fn)

  (if-let [f (get-extension :gs/struct-cmp)]
    (f a b)
    fallback)

Registration is atomic and idempotent — re-registering a key replaces it.
raw docstring

hive-addon.registry.schema

Schema-property extensions contributed to a host tool.

An addon widens an existing tool's input schema by registering extra properties under that tool's name. Contributions merge, so several addons may extend the same tool.

Schema-property extensions contributed to a host tool.

An addon widens an existing tool's input schema by registering extra
properties under that tool's name. Contributions merge, so several addons
may extend the same tool.
raw docstring

hive-addon.registry.tools

Dynamically registered tool definitions.

A tool definition is a map carrying at least a :name string and a :handler fn. Registration is keyed by :name, last write wins.

Dynamically registered tool definitions.

A tool definition is a map carrying at least a :name string and a
:handler fn. Registration is keyed by :name, last write wins.
raw docstring

hive-addon.schema

Malli schema layer for the IAddon contract.

Every data shape that crosses the IAddon protocol boundary has a schema here, so a host can validate an addon's contract outputs and an addon can validate the config it receives. Stratified: the schemas sit as pure data BELOW the protocol — they read the protocol's own constants (single source, no drift) and the protocol never depends on the schemas.

Self-contained by design (DDD): the addon bounded context owns its schemas. Deps are malli + hive-dsl only — NO hive-spi. Schemas are registered in a LOCAL composite registry under :addon/* keys; reach them via schema, validate, explain (which thread {:registry registry}) or by passing {:registry registry} yourself. The registry is NEVER installed as the malli global default (shared-JVM safety).

Errors bridge to hive-dsl Result: the validate*/validate-addon helpers return (r/ok x) on success and (r/err :addon/... {:explanation ...}) on failure, with a qualified-keyword error category.

Malli schema layer for the IAddon contract.

Every data shape that crosses the IAddon protocol boundary has a schema
here, so a host can validate an addon's contract outputs and an addon can
validate the config it receives. Stratified: the schemas sit as pure data
BELOW the protocol — they read the protocol's own constants (single source,
no drift) and the protocol never depends on the schemas.

Self-contained by design (DDD): the addon bounded context owns its schemas.
Deps are malli + hive-dsl only — NO hive-spi. Schemas are registered in a
LOCAL composite registry under :addon/* keys; reach them via `schema`,
`validate`, `explain` (which thread {:registry registry}) or by passing
{:registry registry} yourself. The registry is NEVER installed as the malli
global default (shared-JVM safety).

Errors bridge to hive-dsl Result: the `validate*`/`validate-addon` helpers
return (r/ok x) on success and (r/err :addon/... {:explanation ...}) on
failure, with a qualified-keyword error category.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close