Liking cljdoc? Tell your friends :D

hive.events.multi

Multimethod-based event dispatch + pure orchestration for hive-events.

Two complementary subsystems in one namespace:

1. Handler Dispatch (multimethod-based)

Registration:

  • (register-handler! event-id handler-fn) — register handler (no interceptors)
  • (register-handler! event-id interceptors handler-fn) — with interceptors
  • (remove-handler! event-id) — remove a registered handler

Dispatch:

  • (dispatch-sync event) — synchronous dispatch + fx execution
  • (event-id event) — extract event-id (dispatch fn)

Query:

  • (handler-registered? event-id) — check if handler exists

Handler signature: (fn [coeffects event] effects-map)

2. Pure Orchestration (multi-op compilation)

Compile a batch of cross-tool operations into a wave-based execution plan. Pure functions — no side effects, no IO, no requiring-resolve.

Pipeline: normalize → validate → expand-batch → assign-waves → compile-spec

  • (normalize-op op) — normalize string keys, auto-generate ID
  • (validate-ops ops) — validate IDs, deps, cycles (Kahn's algorithm)
  • (expand-batch-macro ops) — expand 'batch' command into fan-out sub-ops
  • (assign-waves ops) — topological sort into dependency-ordered wave groups
  • (compile-multi-spec ops) — end-to-end: validate → expand → waves → FSM spec

Architecture

hive-events (this ns)  — pure compilation (validate, sort, waves)
  ↓
hive-mcp tools/multi   — bridge (resolve handlers, execute ops, format)
  ↓
hive-mcp consolidated  — MCP facade (JSON schema, tool def)

SOLID: OCP — Open for extension via defmethod handlers SOLID: SRP — Pure orchestration logic, no side effects CLARITY: L — Layered: pure compilation separate from execution

Multimethod-based event dispatch + pure orchestration for hive-events.

Two complementary subsystems in one namespace:

## 1. Handler Dispatch (multimethod-based)

Registration:
- `(register-handler! event-id handler-fn)` — register handler (no interceptors)
- `(register-handler! event-id interceptors handler-fn)` — with interceptors
- `(remove-handler! event-id)` — remove a registered handler

Dispatch:
- `(dispatch-sync event)` — synchronous dispatch + fx execution
- `(event-id event)` — extract event-id (dispatch fn)

Query:
- `(handler-registered? event-id)` — check if handler exists

Handler signature: `(fn [coeffects event] effects-map)`

## 2. Pure Orchestration (multi-op compilation)

Compile a batch of cross-tool operations into a wave-based execution plan.
Pure functions — no side effects, no IO, no requiring-resolve.

Pipeline: normalize → validate → expand-batch → assign-waves → compile-spec

- `(normalize-op op)` — normalize string keys, auto-generate ID
- `(validate-ops ops)` — validate IDs, deps, cycles (Kahn's algorithm)
- `(expand-batch-macro ops)` — expand 'batch' command into fan-out sub-ops
- `(assign-waves ops)` — topological sort into dependency-ordered wave groups
- `(compile-multi-spec ops)` — end-to-end: validate → expand → waves → FSM spec

## Architecture

```
hive-events (this ns)  — pure compilation (validate, sort, waves)
  ↓
hive-mcp tools/multi   — bridge (resolve handlers, execute ops, format)
  ↓
hive-mcp consolidated  — MCP facade (JSON schema, tool def)
```

SOLID: OCP — Open for extension via defmethod handlers
SOLID: SRP — Pure orchestration logic, no side effects
CLARITY: L — Layered: pure compilation separate from execution
raw docstring

assign-wavesclj/s

(assign-waves ops)

Assign operations to execution waves based on dependencies.

Independent ops run in the same wave (parallel). Dependent ops are assigned to later waves. Uses Kahn's topological sort.

Returns ops with :wave key added, sorted by wave number.

Pure function — no side effects.

Example: ;; Independent ops → same wave (assign-waves [{:id "a" :depends_on []} {:id "b" :depends_on []}]) ;; => [{:id "a" :wave 1 ...} {:id "b" :wave 1 ...}]

;; Chain → sequential waves (assign-waves [{:id "a" :depends_on []} {:id "b" :depends_on ["a"]}]) ;; => [{:id "a" :wave 1 ...} {:id "b" :wave 2 ...}]

Assign operations to execution waves based on dependencies.

Independent ops run in the same wave (parallel). Dependent ops
are assigned to later waves. Uses Kahn's topological sort.

Returns ops with :wave key added, sorted by wave number.

Pure function — no side effects.

Example:
  ;; Independent ops → same wave
  (assign-waves [{:id "a" :depends_on []} {:id "b" :depends_on []}])
  ;; => [{:id "a" :wave 1 ...} {:id "b" :wave 1 ...}]

  ;; Chain → sequential waves
  (assign-waves [{:id "a" :depends_on []}
                  {:id "b" :depends_on ["a"]}])
  ;; => [{:id "a" :wave 1 ...} {:id "b" :wave 2 ...}]
sourceraw docstring

compile-multi-specclj/s

(compile-multi-spec ops)

Compile a multi-op specification from raw operations.

End-to-end pipeline: normalize → validate → expand-batch → assign-waves.

Returns:

  • On success: {:valid true :ops [...] :waves {1 [...] 2 [...]} :wave-count N}
  • On failure: {:valid false :errors [...]}

The :ops vector contains fully normalized, wave-assigned operations. The :waves map groups ops by wave number for execution.

Pure function — no side effects, no IO.

Example: (compile-multi-spec [{:tool "memory" :command "add" :content "hello" :type "note"} {:tool "kg" :command "edge" :from "a" :to "b" :depends_on []}]) ;; => {:valid true ;; :ops [{:id "op-..." :tool "memory" ... :wave 1} ;; {:id "op-..." :tool "kg" ... :wave 1}] ;; :waves {1 [{...} {...}]} ;; :wave-count 1}

Compile a multi-op specification from raw operations.

End-to-end pipeline: normalize → validate → expand-batch → assign-waves.

Returns:
- On success: {:valid true :ops [...] :waves {1 [...] 2 [...]} :wave-count N}
- On failure: {:valid false :errors [...]}

The :ops vector contains fully normalized, wave-assigned operations.
The :waves map groups ops by wave number for execution.

Pure function — no side effects, no IO.

Example:
  (compile-multi-spec [{:tool "memory" :command "add" :content "hello" :type "note"}
                        {:tool "kg" :command "edge" :from "a" :to "b"
                         :depends_on []}])
  ;; => {:valid true
  ;;     :ops [{:id "op-..." :tool "memory" ... :wave 1}
  ;;            {:id "op-..." :tool "kg" ... :wave 1}]
  ;;     :waves {1 [{...} {...}]}
  ;;     :wave-count 1}
sourceraw docstring

dispatch-syncclj/s

(dispatch-sync event)

Dispatch an event synchronously through its handler chain.

  1. Looks up handler via multimethod
  2. Builds interceptor chain (registered interceptors + handler interceptor)
  3. Executes chain via hive.events.interceptor/execute
  4. Executes effects via hive.events.fx/do-fx

Returns the final context with :coeffects and :effects. Throws if no handler is registered for the event.

Dispatch an event synchronously through its handler chain.

1. Looks up handler via multimethod
2. Builds interceptor chain (registered interceptors + handler interceptor)
3. Executes chain via hive.events.interceptor/execute
4. Executes effects via hive.events.fx/do-fx

Returns the final context with :coeffects and :effects.
Throws if no handler is registered for the event.
sourceraw docstring

event-idclj/s

(event-id event)

Extract the event-id (first element) from an event vector. This is the dispatch function for the handle multimethod.

Extract the event-id (first element) from an event vector.
This is the dispatch function for the handle multimethod.
sourceraw docstring

expand-batch-macroclj/s

(expand-batch-macro ops)

Expand batch command operations into fan-out sub-operations.

A batch operation has :command ["batch" "actual-cmd"]. Array parameters (excluding known-array-params) are fanned out into individual sub-operations. Scalar parameters are broadcast to all sub-ops.

Pure function — no side effects.

Rules:

  • Non-batch ops pass through unchanged
  • Only ONE array param can be fanned out per batch op (ambiguity error otherwise)
  • Empty fan-out array is an error
  • A join operation is appended that depends on all sub-ops

Example: (expand-batch-macro [{:id "op-1" :tool "memory" :command ["batch" "add"] :content ["note1" "note2"] :type "note"}]) ;; => [{:id "op-1.0" :tool "memory" :command "add" :content "note1" :type "note"} ;; {:id "op-1.1" :tool "memory" :command "add" :content "note2" :type "note"} ;; {:id "op-1" :tool :noop :command :join :depends_on ["op-1.0" "op-1.1"]}]

Expand batch command operations into fan-out sub-operations.

A batch operation has :command ["batch" "actual-cmd"].
Array parameters (excluding known-array-params) are fanned out
into individual sub-operations. Scalar parameters are broadcast
to all sub-ops.

Pure function — no side effects.

Rules:
- Non-batch ops pass through unchanged
- Only ONE array param can be fanned out per batch op (ambiguity error otherwise)
- Empty fan-out array is an error
- A join operation is appended that depends on all sub-ops

Example:
  (expand-batch-macro [{:id "op-1" :tool "memory" :command ["batch" "add"]
                         :content ["note1" "note2"] :type "note"}])
  ;; => [{:id "op-1.0" :tool "memory" :command "add" :content "note1" :type "note"}
  ;;      {:id "op-1.1" :tool "memory" :command "add" :content "note2" :type "note"}
  ;;      {:id "op-1" :tool :noop :command :join :depends_on ["op-1.0" "op-1.1"]}]
sourceraw docstring

handleclj/smultimethod

Multimethod for event handling. Dispatches on (first event).

Methods are installed via register-handler!, not directly via defmethod. This allows attaching interceptors alongside the handler function.

Each method returns: {:handler fn, :interceptors [...]} where handler is (fn [coeffects event] effects-map).

Multimethod for event handling. Dispatches on (first event).

Methods are installed via `register-handler!`, not directly via `defmethod`.
This allows attaching interceptors alongside the handler function.

Each method returns: {:handler fn, :interceptors [...]}
where handler is (fn [coeffects event] effects-map).
sourceraw docstring

handler-registered?clj/s

(handler-registered? event-id)

Check if a handler is registered for the given event-id.

Check if a handler is registered for the given event-id.
sourceraw docstring

normalize-opclj/s

(normalize-op op)

Normalize a single operation map for multi-op compilation.

  • Converts string keys to keywords
  • Auto-generates :id if missing
  • Normalizes :depends_on to vector of strings

Pure function — no side effects.

Example: (normalize-op {"tool" "memory" "command" "add" "id" "op-1"}) ;; => {:tool "memory" :command "add" :id "op-1" :depends_on []}

(normalize-op {:tool "kg" :command "edge"}) ;; => {:tool "kg" :command "edge" :id "op-<uuid>" :depends_on []}

Normalize a single operation map for multi-op compilation.

- Converts string keys to keywords
- Auto-generates :id if missing
- Normalizes :depends_on to vector of strings

Pure function — no side effects.

Example:
  (normalize-op {"tool" "memory" "command" "add" "id" "op-1"})
  ;; => {:tool "memory" :command "add" :id "op-1" :depends_on []}

  (normalize-op {:tool "kg" :command "edge"})
  ;; => {:tool "kg" :command "edge" :id "op-<uuid>" :depends_on []}
sourceraw docstring

register-handler!clj/s

(register-handler! event-id handler-fn)
(register-handler! event-id interceptors handler-fn)

Register an event handler with optional interceptors.

Two arities:

  • (register-handler! event-id handler-fn)
  • (register-handler! event-id interceptors handler-fn)

Handler signature: (fn [coeffects event] effects-map) Interceptors: vector of interceptor maps (from ->interceptor)

Register an event handler with optional interceptors.

Two arities:
- (register-handler! event-id handler-fn)
- (register-handler! event-id interceptors handler-fn)

Handler signature: (fn [coeffects event] effects-map)
Interceptors: vector of interceptor maps (from ->interceptor)
sourceraw docstring

remove-handler!clj/s

(remove-handler! event-id)

Remove a registered event handler.

Remove a registered event handler.
sourceraw docstring

validate-opsclj/s

(validate-ops ops)

Validate an operations vector for multi-op execution.

Returns {:valid true} or {:valid false :errors [...]}.

Checks:

  • All ops have :id and :tool
  • IDs are unique
  • :depends_on references exist (no dangling refs)
  • No self-dependencies
  • No circular dependencies (Kahn's algorithm)

Pure function — no side effects, no IO.

Example: (validate-ops [{:id "op-1" :tool "memory" :command "add"} {:id "op-2" :tool "kg" :command "edge" :depends_on ["op-1"]}]) ;; => {:valid true}

(validate-ops [{:id "op-1" :tool "memory" :depends_on ["op-2"]} {:id "op-2" :tool "kg" :depends_on ["op-1"]}]) ;; => {:valid false :errors ["Circular dependency detected among: op-1, op-2"]}

Validate an operations vector for multi-op execution.

Returns {:valid true} or {:valid false :errors [...]}.

Checks:
- All ops have :id and :tool
- IDs are unique
- :depends_on references exist (no dangling refs)
- No self-dependencies
- No circular dependencies (Kahn's algorithm)

Pure function — no side effects, no IO.

Example:
  (validate-ops [{:id "op-1" :tool "memory" :command "add"}
                  {:id "op-2" :tool "kg" :command "edge" :depends_on ["op-1"]}])
  ;; => {:valid true}

  (validate-ops [{:id "op-1" :tool "memory" :depends_on ["op-2"]}
                  {:id "op-2" :tool "kg" :depends_on ["op-1"]}])
  ;; => {:valid false :errors ["Circular dependency detected among: op-1, op-2"]}
sourceraw 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