Liking cljdoc? Tell your friends :D

vaelii.koinii.channel

Koinii's core coordination library: the async assert / reply loop an agent runs over a shared channel, with the KB as the medium. An agent JOINS its own context, SUBSCRIBES to the channel over the change feed, and REPLIES to what it sees — all decoupled in time, nothing requiring two agents online at once.

The mechanism is already in the engine (the change feed, docs/feed.md); this layer is ergonomics and correctness over it, not a new transport. It commits three pieces of koinii.md's deployment shape to code:

  • D8, the per-agent context. An agent writes only its OWN context (CxAtlas), lifted under the channel ((genlCx CxDeploy CxAtlas)) so the channel sees the union of every agent's assertions. Identity fixes the write destination (identity).
  • D1, reply-as-meta-sentex. A reply is a META-SENTEX on its target (speech_acts' answers / disputes / endorses / justifies), so it lives ON the target rather than merely naming it: retract the target and its replies are torn down with it (target_following_predicate), no dangling edges.
  • D7, the single-writer total order. See the docstring on writer-order below.

Two deployment shapes, one surface. The Medium protocol has two implementations, and which one an agent joins is the whole single-process / cross-process decision (koinii.md, When to stop):

  • wire — a daemon connection (vaelii.client). The inter-agent case: agents are separate processes funnelling every write through the one daemon (the single writer), and subscribe runs the feed's poll loop OFF the agent's own thread. This is the mandatory shape the moment agents are separate processes, and the reason is the writer's thread: an in-process feed callback runs ON the single writer's thread (docs/feed.md), so one slow agent would stall the writer for everyone. Polling off the agent's thread is what removes that coupling.
  • local — an in-process KB. Simpler, and correct when every agent lives in one process: subscribe is a plain core/watch listener, no cursor apparatus. The callback runs on the writing thread, so a slow one still slows the writer — fine single-process, wrong across agents, which is why wire exists.

assert / answer / endorse / justify / dispute / vote / reply-many and the recovery reads are shape-agnostic — they run the same over either medium; only subscribe differs, because only the feed does.

Additive, like the sibling koinii modules: the public core API, vaelii.client, koinii identity, and Trove for the two subscription defaults. Every write goes through the provenance-recording assert path — never bulk-assert-facts!.

Koinii's core coordination library: the async assert / reply loop an
agent runs over a shared channel, with the KB as the medium.  An agent JOINS its own
context, SUBSCRIBES to the channel over the change feed, and REPLIES to what it sees —
all decoupled in time, nothing requiring two agents online at once.

The mechanism is already in the engine (the change feed, docs/feed.md); this layer is
ergonomics and correctness over it, not a new transport.  It commits three pieces of
`koinii.md`'s *deployment shape* to code:

- **D8, the per-agent context.**  An agent writes only its OWN context (`CxAtlas`),
  lifted under the channel (`(genlCx CxDeploy CxAtlas)`) so the channel sees the union
  of every agent's assertions.  Identity fixes the write destination (`identity`).
- **D1, reply-as-meta-sentex.**  A reply is a META-SENTEX on its target (`speech_acts`'
  `answers` / `disputes` / `endorses` / `justifies`), so it lives ON the target rather
  than merely naming it: retract the target and its replies are torn down with it
  (`target_following_predicate`), no dangling edges.
- **D7, the single-writer total order.**  See the docstring on `writer-order` below.

**Two deployment shapes, one surface.**  The `Medium` protocol has two
implementations, and which one an agent joins is the whole single-process /
cross-process decision (`koinii.md`, *When to stop*):

- **`wire`** — a daemon connection (`vaelii.client`).  The inter-agent case: agents are
  separate processes funnelling every write through the one daemon (the single writer),
  and `subscribe` runs the feed's `poll` loop OFF the agent's own thread.  This is the
  mandatory shape the moment agents are separate processes, and the reason is the
  writer's thread: an in-process feed callback runs ON the single writer's thread
  (docs/feed.md), so one slow agent would stall the writer for everyone.  Polling off
  the agent's thread is what removes that coupling.
- **`local`** — an in-process KB.  Simpler, and correct when every agent lives in one
  process: `subscribe` is a plain `core/watch` listener, no cursor apparatus.  The
  callback runs on the writing thread, so a slow one still slows the writer — fine
  single-process, wrong across agents, which is why `wire` exists.

`assert` / `answer` / `endorse` / `justify` / `dispute` / `vote` / `reply-many` and the
recovery reads are shape-agnostic — they run the same over either medium; only
`subscribe` differs, because only the feed does.

Additive, like the sibling koinii modules: the public core API, `vaelii.client`, koinii
`identity`, and Trove for the two subscription defaults.  Every write goes through the
provenance-recording `assert` path — never `bulk-assert-facts!`.
raw docstring

answerclj

(answer handle content target-handle)

answers: reply to the query at target-handle with content. A ternary meta-sentex (answers agent content (sentexHandle T)) in the agent's own context, creator recorded. The answerer's identity is read off THIS sentex, never off the query. Returns the answer's handle; a re-answer with the same content returns that same handle.

`answers`: reply to the query at `target-handle` with `content`.  A ternary meta-sentex
`(answers agent content (sentexHandle T))` in the agent's own context, creator recorded.
The answerer's identity is read off THIS sentex, never off the query.  Returns the
answer's handle; a re-answer with the same content returns that same handle.
sourceraw docstring

answer-contentclj

(answer-content sentex)

The ?content of an answers move sentex — its third argument.

The `?content` of an `answers` move `sentex` — its third argument.
sourceraw docstring

answers-toclj

(answers-to handle query-handle)

Every believed answer to the query query-handle — (answers ?agent ?content (sentexHandle query-handle)), matched anywhere. 'What answered the query, and who said it' is a plain read: pair each with speaker-of for who, and answer-content for what.

Every believed answer to the query `query-handle` — `(answers ?agent ?content
(sentexHandle query-handle))`, matched anywhere.  'What answered the query, and who said
it' is a plain read: pair each with `speaker-of` for who, and `answer-content` for what.
sourceraw docstring

assertclj

(assert handle sentence)
(assert handle sentence opts)

Originate a claim: assert sentence into the agent's OWN context, recording :creator as the agent, through the medium (for a wire handle, through the daemon). The destination is fixed by identity, and a speech act it asserts names that agent as its speaker (:koinii/speaker-mismatch otherwise). Returns the handle. A conflicting :creator is refused (:koinii/creator-mismatch); the agent's own id is redundant and allowed. Every other opts key passes through. The refusals are stated once in check-write-entry-points and held identically by reply-many.

Originate a claim: assert `sentence` into the agent's OWN context, recording `:creator` as
the agent, through the medium (for a `wire` handle, through the daemon).  The destination
is fixed by identity, and a speech act it asserts names that agent as its speaker
(`:koinii/speaker-mismatch` otherwise).  Returns the handle.  **A conflicting `:creator` is
refused** (`:koinii/creator-mismatch`); the agent's own id is redundant and allowed.
Every other `opts` key passes through.  The refusals are stated once in
`check-write-entry-points` and held identically by `reply-many`.
sourceraw docstring

disputeclj

(dispute handle target-handle)

disputes: challenge the claim at target-handle. Two writes in the agent's own context, creator recorded: the REBUTTING claim (the negation of the target's sentence, so the pair surfaces in (contradictions kb)) and a disputes meta-sentex naming the target. Represents the challenge only; adjudication is a separate policy layer. Returns the dispute edge's handle. A handle that names no record is refused (:koinii/no-such-handle), as deref's marker refuses one, since the rebuttal would assert (not nil).

`disputes`: challenge the claim at `target-handle`.  Two writes in the agent's own context,
creator recorded: the REBUTTING claim (the negation of the target's sentence, so the pair
surfaces in `(contradictions kb)`) and a `disputes` meta-sentex naming the target.
Represents the challenge only; adjudication is a separate policy layer.  Returns the
dispute edge's handle.  **A handle that names no record is refused**
(`:koinii/no-such-handle`), as `deref`'s `marker` refuses one, since the rebuttal would
assert `(not nil)`.
sourceraw docstring

endorseclj

(endorse handle target-handle)

endorses: stand behind the claim at target-handle. A binary meta-sentex (endorses agent (sentexHandle T)). Two endorsers of one claim are two distinct sentexes with two creators — the case a bare re-assert would collapse to one. Returns the endorsement's handle.

`endorses`: stand behind the claim at `target-handle`.  A binary meta-sentex
`(endorses agent (sentexHandle T))`.  Two endorsers of one claim are two distinct
sentexes with two creators — the case a bare re-assert would collapse to one.  Returns
the endorsement's handle.
sourceraw docstring

endorsements-ofclj

(endorsements-of handle target-handle)

Every believed endorsement naming target-handle — (endorses ?agent (sentexHandle target-handle)), matched anywhere. Distinct endorsers are distinct sentexes.

Every believed endorsement naming `target-handle` — `(endorses ?agent (sentexHandle
target-handle))`, matched anywhere.  Distinct endorsers are distinct sentexes.
sourceraw docstring

joinclj

(join medium channel agent-id)
(join medium channel agent-id opts)

Bind agent-id to medium and the coordination channel (koinii design D8). The agent's own context (id/context-for, AgentAtlas -> CxAtlas) is lifted under the channel ((genlCx channel CxAtlas)), rooted so it speaks the reply vocabulary ((genlCx CxAtlas CxSpeechActs), override with :speaks), and marked as the agent's own (id/agent-context-mark). All three are monotonic and idempotent. Returns the agent handle {:medium :agent :context :channel}, threaded first into every call below.

The channel must be a channel (:koinii/not-a-channel): grafting onto the admin registry, the agent's own context, or a context placed as another agent's own is refused (check-channel-parent). Requires the channel and CxSpeechActs already loaded, so the target_following_predicate marks that make a reply cascade are in force.

Bind `agent-id` to `medium` and the coordination `channel` (koinii design D8).  The agent's
own context (`id/context-for`, `AgentAtlas` -> `CxAtlas`) is lifted under the channel
(`(genlCx channel CxAtlas)`), rooted so it speaks the reply vocabulary
(`(genlCx CxAtlas CxSpeechActs)`, override with `:speaks`), and marked as the agent's own
(`id/agent-context-mark`).  All three are monotonic and idempotent.  Returns the agent
handle `{:medium :agent :context :channel}`, threaded first into every call below.

**The channel must be a channel** (`:koinii/not-a-channel`): grafting onto the admin
registry, the agent's own context, or a context placed as another agent's own is refused
(`check-channel-parent`).  Requires the `channel` and `CxSpeechActs` already loaded, so the
`target_following_predicate` marks that make a reply cascade are in force.
sourceraw docstring

justifyclj

(justify handle ground target-handle)

justifies: offer ground as a reason for the claim at target-handle. A ternary meta-sentex (justifies agent ground (sentexHandle T)). Returns its handle.

`justifies`: offer `ground` as a reason for the claim at `target-handle`.  A ternary
meta-sentex `(justifies agent ground (sentexHandle T))`.  Returns its handle.
sourceraw docstring

localclj

(local kb)

A channel medium over an in-process KB — the single-process shape. subscribe is a plain core/watch listener; simpler, and correct when every agent is in one process.

A channel medium over an in-process KB — the single-process shape.  `subscribe` is a
plain `core/watch` listener; simpler, and correct when every agent is in one process.
sourceraw docstring

open-queriesclj

(open-queries handle)

Every query node on the channel — (queries ?agent ?question), matched anywhere. The catch-up read: an agent connecting LATER reads the open questions from the durable KB, decoupled in time from whoever asked and whether they are still online.

Every query node on the channel — `(queries ?agent ?question)`, matched anywhere.  The
catch-up read: an agent connecting LATER reads the open questions from the durable KB,
decoupled in time from whoever asked and whether they are still online.
sourceraw docstring

pose-queryclj

(pose-query handle question)

Originate a query NODE (queries agent question) in the agent's own context — the thing a responder later answers by handle. Minted (unlike a plain assert) because a question must be told apart from a claim. Returns its handle.

Originate a query NODE `(queries agent question)` in the agent's own context — the
thing a responder later `answer`s by handle.  Minted (unlike a plain
`assert`) because a question must be told apart from a claim.  Returns its handle.
sourceraw docstring

queryclj

(query handle goal context)

ANCESTOR-SET-AWARE solutions for goal in context, as binding maps — the read that sees a channel's agents' own-context sentexes up the genlCx ancestor set (unlike the direct sentexes-matching the reads above use). It answers the channel's whole current view of goal, which is what catch-up (catchup) snapshots the state from.

ANCESTOR-SET-AWARE solutions for `goal` in `context`, as binding maps — the read that sees a
channel's agents' own-context sentexes up the genlCx ancestor set (unlike the direct
`sentexes-matching` the reads above use).  It answers the channel's whole current view of
`goal`, which is what catch-up (`catchup`) snapshots the state from.
sourceraw docstring

reply-manyclj

(reply-many handle sentences)

Assert MORE THAN ONE linked claim as one reply: VALIDATE the whole batch with check-edit first, then commit it with edit! in one settle, since edit! is not atomic on a throw (docs/feed.md) and a mid-batch failure would leave a HALF-reply. Each of sentences is asserted into the agent's own context, creator recorded. Every entry point assert holds, this holds for the whole batch before the first write: the registry is not writable (:koinii/registry-forbidden) and no sentence may name another agent as its speaker (:koinii/speaker-mismatch, check-write-entry-points). Throws :koinii/reply-inadmissible (carrying the check-edit :problems) if the batch is not admissible. Returns edit!'s result.

Assert MORE THAN ONE linked claim as one reply: VALIDATE the whole batch with `check-edit`
first, then commit it with `edit!` in one settle, since `edit!` is not atomic on a throw
(docs/feed.md) and a mid-batch failure would leave a HALF-reply.  Each of `sentences` is
asserted into the agent's own context, creator recorded.  **Every entry point `assert`
holds, this holds for the whole batch before the first write**: the registry is not
writable (`:koinii/registry-forbidden`) and no sentence may name another agent as its
speaker (`:koinii/speaker-mismatch`, `check-write-entry-points`).  Throws
`:koinii/reply-inadmissible` (carrying the `check-edit` `:problems`) if the batch is not
admissible.  Returns `edit!`'s result.
sourceraw docstring

speaker-ofclj

(speaker-of sentex)

Who made the response move sentex — its ?agent, the FIRST argument of the meta-sentex. Read off the reply's own sentence (so it crosses the wire without a provenance op) and equal to the :creator provenance the daemon recorded: the conversation is in the graph, not in the client.

Who made the response move `sentex` — its `?agent`, the FIRST argument of the
meta-sentex.  Read off the reply's own sentence (so it crosses the wire without a
provenance op) and equal to the `:creator` provenance the daemon recorded: the
conversation is in the graph, not in the client.
sourceraw docstring

subscribeclj

(subscribe handle goal context callback)
(subscribe handle goal context callback opts)

Call callback when a claim matching goal appears or changes in context: the async reply trigger. goal nil watches every change; a goal refused by core/watch is refused identically here. Watch the CHANNEL context to see every agent's moves (the feed is scoped up the genlCx ancestor set). For a wire handle the poll loop runs OFF the agent's own thread, since an in-process callback runs on the single writer's thread and one slow agent would stall every writer. opts: :wait-ms (long-poll, wire only), :on-lagged (fn of the dropped count: resync from the KB), :on-error (fn of a throwable); unset, the wire shape LOGS each.

Returns a subscription {:token … :stop (fn []) …}; :stop drops it (wire: it also wakes the parked poll, and :running reads false from then on). A subscription is FORWARD-ONLY: history that predates it is recovered by READING the KB (answers-to / sentexes-matching), the durable half of the channel; the feed is the live half.

Call `callback` when a claim matching `goal` appears or changes in `context`: the async
reply trigger.  `goal` nil watches every change; a `goal` refused by `core/watch` is
refused identically here.  Watch the CHANNEL context to see every agent's moves (the feed
is scoped up the `genlCx` ancestor set).  For a `wire` handle the poll loop runs OFF the
agent's own thread, since an in-process callback runs on the single writer's thread and
one slow agent would stall every writer.  `opts`: `:wait-ms` (long-poll, wire only),
`:on-lagged` (fn of the dropped count: resync from the KB), `:on-error` (fn of a
throwable); unset, the wire shape LOGS each.

Returns a subscription `{:token … :stop (fn []) …}`; `:stop` drops it (wire: it also wakes
the parked poll, and `:running` reads false from then on).  A subscription is
FORWARD-ONLY: history that predates it is recovered by READING the KB (`answers-to` /
`sentexes-matching`), the durable half of the channel; the feed is the live half.
sourceraw docstring

unsubscribeclj

(unsubscribe subscription)

Drop a subscription (from subscribe). Idempotent.

Drop a `subscription` (from `subscribe`).  Idempotent.
sourceraw docstring

voteclj

(vote handle stance target-handle)

Cast a ballot on the claim at target-handle: stance :for -> (votesFor agent (sentexHandle T)), :against -> (votesAgainst agent (sentexHandle T)). A meta-sentex in the agent's own context, creator recorded, which a resolution policy COUNTS (adjudication/tally). This entry point authenticates nobody, so its ballot carries no attestation, and adjudication/resolve-by-majority refuses a count that includes one unless a daemon holding a token table recorded the ballot's :principal; adjudication/cast-ballot casts the attested ballot in process. A ballot is a response act (target_following_predicate in CxSpeechActs), so retracting the disputed claim withdraws its votes. Idempotent by sentence identity: one ballot per agent per stance; to change a vote, retract the old ballot first. Returns the ballot's handle. A stance that is neither is refused by name (:koinii/no-such-stance, carrying the two that exist).

Cast a ballot on the claim at `target-handle`: `stance` `:for` -> `(votesFor agent
(sentexHandle T))`, `:against` -> `(votesAgainst agent (sentexHandle T))`.  A meta-sentex
in the agent's own context, creator recorded, which a resolution policy COUNTS
(`adjudication/tally`).  This entry point authenticates nobody, so its ballot carries no
attestation, and `adjudication/resolve-by-majority` refuses a count that includes one
unless a daemon holding a token table recorded the ballot's `:principal`;
`adjudication/cast-ballot` casts the attested ballot in process.  A ballot is a response
act (`target_following_predicate` in `CxSpeechActs`), so retracting the disputed claim
withdraws its votes.  Idempotent by sentence identity: one ballot per agent per stance; to
change a vote, retract the old ballot first.  Returns the ballot's handle.  **A stance
that is neither is refused by name** (`:koinii/no-such-stance`, carrying the two that
exist).
sourceraw docstring

wireclj

(wire conn)

A channel medium over a daemon connection conn (from vaelii.client/client) — the cross-process shape. Every write funnels through the daemon (the single writer) and subscribe polls the wire feed off the agent's thread.

A channel medium over a daemon connection `conn` (from `vaelii.client/client`) — the
cross-process shape.  Every write funnels through the daemon (the single writer) and
`subscribe` polls the wire feed off the agent's thread.
sourceraw docstring

writer-orderclj

Documentation of the consistency model, not a runtime value (koinii design D7).

The daemon serializes every write through one monitor (docs/operations.md), so the change-feed cursor delivers a TOTAL ORDER: every agent reads the same events in the same sequence, and an agent can only reply to a claim it has already seen. The happy path is causally sound BY CONSTRUCTION. Logical clocks (Lamport / vector) are redundant here: they reconstruct a causal order across CONCURRENT writers, and with one writer the store order is the causal order. Causal metadata becomes necessary only if koinii goes multi-writer (multiple daemons over sharded KBs).

**Documentation of the consistency model, not a runtime value** (koinii design D7).

The daemon serializes every write through one monitor (docs/operations.md), so the
change-feed cursor delivers a TOTAL ORDER: every agent reads the same events in the same
sequence, and an agent can only reply to a claim it has already seen.  The happy path is
causally sound BY CONSTRUCTION.  **Logical clocks (Lamport / vector) are redundant
here**: they reconstruct a causal order across CONCURRENT writers, and with one writer the
store order is the causal order.  Causal metadata becomes necessary only if koinii goes
multi-writer (multiple daemons over sharded KBs).
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