Liking cljdoc? Tell your friends :D

vaelii.impl.koinii.adjudication

Koinii adjudication — the DEFAULT policy: leave-open-and-notify, plus the lifecycle that keeps disputes from piling up, plus arbiter escalation. A thin CLIENT wrapper over the dispute reads (vaelii.impl.koinii.dispute) — it touches no engine internals and changes belief only through ordinary asserts / retracts.

koinii's honest first answer to a disagreement is NOT to pick a winner. When two agents assert P and ¬P at :default, the KB stays paraconsistent — both coexist, argue reports :contradiction (Priest's LP) — and this layer just records the dispute open, pushes it to whoever is watching, and manages its life. Automatic resolution by source trust is a harder, engine-side policy; do not reach for it here.

Three policies, one default (koinii.md, Adjudication: split by policy):

  • Leave-open-and-notify (the default, here) — record open, notify, change no belief. Correct for a ground truth people curate.
  • Arbiter escalation (also here, client) — a designated arbiter's ruling is an ordinary :monotonic assertion of the upheld side; its strength defeats the losing :default side, so the clash clears, why explains who ruled, and retracting the ruling reopens the dispute (cascading).
  • Trust-resolve — out of scope; that is engine work, not this layer's.

The dispute module owns the reads, the state vocabulary, and the dispute id; THIS module owns the policy the dispute module deliberately left out — the clock, the timeout, and the notify sinks. The clock is the engine clock (v/*clock*), so a lifecycle stamp and its assertion's :created provenance agree.

Additive, like the sibling koinii modules: only the public core API plus koinii dispute and identity — nothing in core loads it.

Koinii adjudication — the DEFAULT policy: leave-open-and-notify, plus the
lifecycle that keeps disputes from piling up, plus arbiter escalation.  A thin CLIENT
wrapper over the dispute reads (`vaelii.impl.koinii.dispute`) — it touches no
engine internals and changes belief only through ordinary asserts / retracts.

koinii's honest first answer to a disagreement is NOT to pick a winner.  When two agents
assert P and ¬P at `:default`, the KB stays **paraconsistent** — both coexist, `argue`
reports `:contradiction` (Priest's LP) — and this layer just records the dispute open,
pushes it to whoever is watching, and manages its life.  Automatic resolution by source
trust is a harder, engine-side policy; do not reach for it here.

Three policies, one default (koinii.md, *Adjudication: split by policy*):

- **Leave-open-and-notify** *(the default, here)* — record open, notify, change no
  belief.  Correct for a ground truth people curate.
- **Arbiter escalation** *(also here, client)* — a designated arbiter's ruling is an
  ordinary `:monotonic` assertion of the upheld side; its strength defeats the losing
  `:default` side, so the clash clears, `why` explains who ruled, and retracting the
  ruling reopens the dispute (cascading).
- **Trust-resolve** — out of scope; that is engine work, not this layer's.

The dispute module owns the reads, the state vocabulary, and the dispute id; THIS module owns the policy
the dispute module deliberately left out — the **clock**, the **timeout**, and the **notify sinks**.  The
clock is the engine clock (`v/*clock*`), so a lifecycle stamp and its assertion's
`:created` provenance agree.

Additive, like the sibling koinii modules: only the public core API plus koinii
`dispute` and `identity` — nothing in core loads it.
raw docstring

vaelii.impl.koinii.belief

Koinii belief projection and own-statement disregard — reasoning about what agents hold, built on modal belief projection (vaelii.impl.modal, docs/belief.md) and except visibility masking.

Two capabilities, and a boundary between them that matters:

  • Projection — read what an agent holds. (believes agent P) proves P in the agent's OWN context (CxAgent<agent>), never the asker's, so agents may hold contradictory beliefs without the KB contradicting itself, and asking what one agent believes never pulls in another's. believe-own links an agent's belief context to its koinii write context, so it believes what it asserted and endorsed. This is the whole cross-agent story: you ask what an agent holds from that agent's own context — you never merge one agent's beliefs into another (a cross-agent genlCx would, and would drag one agent's contradictions into the other).

  • Disregard — an agent reversibly withdraws its OWN statement. disregard puts an (except (sentexHandle H)) in the agent's own context, hiding H for reads and derivations, reversibly (restore!), without deleting it. It is restricted to the agent's own statements by construction, and that restriction is the point: except is an index-layer mask — it removes a sentex from view — so using it across agents (agent B hiding agent A's claim) would make a common-descendant context unable to argue: argumentation needs both a claim and its rebuttal visible so the TMS can weigh them, and an index-layer removal takes the claim out of view entirely. Cross-agent disagreement is therefore dispute / argue (speech-acts + adjudication), which keeps both sides visible; except is only ever an agent editing the visibility of what it itself said.

Additive, like the other koinii modules: only the public core API plus modal, sentex, and koinii identity — nothing in core loads it.

Koinii belief projection and own-statement disregard — reasoning about what agents
hold, built on modal belief projection (`vaelii.impl.modal`, `docs/belief.md`) and
`except` visibility masking.

Two capabilities, and a boundary between them that matters:

- **Projection — read what an agent holds.**  `(believes agent P)` proves `P` in the
  agent's OWN context (`CxAgent<agent>`), never the asker's, so agents may
  hold contradictory beliefs without the KB contradicting itself, and asking what one
  agent believes never pulls in another's.  `believe-own` links an agent's belief
  context to its koinii write context, so it believes what it asserted and endorsed.
  This is the whole cross-agent story: you ask what an agent holds *from that agent's
  own context* — you never merge one agent's beliefs into another (a cross-agent
  `genlCx` would, and would drag one agent's contradictions into the other).

- **Disregard — an agent reversibly withdraws its OWN statement.**  `disregard` puts
  an `(except (sentexHandle H))` in the agent's own context, hiding `H` for reads and
  derivations, reversibly (`restore!`), without deleting it.  It is restricted to the
  agent's own statements **by construction**, and that restriction is the point:
  `except` is an **index-layer** mask — it removes a sentex from *view* — so using it
  across agents (agent B hiding agent A's claim) would make a common-descendant context
  unable to *argue*: argumentation needs both a claim and its rebuttal visible so the
  TMS can weigh them, and an index-layer removal takes the claim out of view entirely.
  Cross-agent disagreement is therefore `dispute` / argue (speech-acts + adjudication), which
  keeps both sides visible; `except` is only ever an agent editing the visibility of
  what it *itself* said.

Additive, like the other koinii modules: only the public core API plus `modal`,
`sentex`, and koinii `identity` — nothing in core loads it.
raw docstring

vaelii.impl.koinii.catchup

Koinii catch-up: make 'an agent that was offline catches up on what it missed' CORRECT, including the case the naive version gets wrong — the feed's ring is bounded (256 events), so an agent gone long enough is lagged PAST recovery and its stored cursor can no longer replay the gap.

This is exactly the CDC snapshot+tail pattern (Debezium / Kafka): a consumer that joined late — or fell too far behind — re-reads current state (the snapshot), then resumes streaming from the newest offset (the tail). Koinii's context re-read IS the snapshot half. The subscribe loop (the channel) handles the happy path; this handles the gap. Commits koinii.md's D6 (snapshot+tail) and D7 (order).

Why the snapshot is authoritative, not a fallback nicety. The change feed is add-oriented: it reports a datum ENTERING or a derived conclusion LEAVING belief, but a premise RETRACTED is dropped — its record is gone and 'a datum the dependency-directed sweep deleted is dropped rather than guessed at' (docs/feed.md). So the incremental stream cannot, by itself, be a complete replica: only a full re-read reflects retractions. The tail is an optimization for the common case (koinii accretes — claims, replies, votes); the snapshot is the source of truth, and every catch-up path ends reconciled against it or against a live tail.

Snapshot reads through the CONE. A channel sees its agents' own-context sentexes up the genlCx cone; sentexes-matching does NOT walk the cone (it scopes to a context's own sentexes) but query does — so the snapshot is channel/query, whose solution set is the same view the standing-query feed delivers.

Wire-only: the ring, the cursor, and lag exist on the wire feed (vaelii.impl.subscribe). An in-process medium has no ring to fall off, so -feed-open/-feed-poll throw there and a single-process agent needs none of this.

Additive: requires only channel and clojure.walk. Nothing in core loads it.

Koinii catch-up: make 'an agent that was offline catches up on what it
missed' CORRECT, including the case the naive version gets wrong — the feed's ring is
bounded (256 events), so an agent gone long enough is lagged PAST recovery and its stored
cursor can no longer replay the gap.

This is exactly the **CDC snapshot+tail** pattern (Debezium / Kafka): a consumer that
joined late — or fell too far behind — re-reads current state (the **snapshot**), then
resumes streaming from the newest offset (the **tail**).  Koinii's context re-read IS the
snapshot half.  The subscribe loop (the `channel`) handles the happy path; this handles
the gap.  Commits `koinii.md`'s D6 (snapshot+tail) and D7 (order).

**Why the snapshot is authoritative, not a fallback nicety.**  The change feed is
add-oriented: it reports a datum ENTERING or a derived conclusion LEAVING belief, but a
premise RETRACTED is dropped — its record is gone and 'a datum the dependency-directed
sweep deleted is dropped rather than guessed at' (docs/feed.md).  So the incremental
stream cannot, by itself, be a complete replica: only a full re-read reflects retractions.
The tail is an optimization for the common case (koinii accretes — claims, replies,
votes); the snapshot is the source of truth, and every catch-up path ends reconciled
against it or against a live tail.

**Snapshot reads through the CONE.**  A channel sees its agents' own-context sentexes up
the `genlCx` cone; `sentexes-matching` does NOT walk the cone (it scopes to a context's
own sentexes) but `query` does — so the snapshot is `channel/query`, whose solution set is
the same view the standing-query feed delivers.

Wire-only: the ring, the cursor, and lag exist on the wire feed (`vaelii.impl.subscribe`).
An in-process medium has no ring to fall off, so `-feed-open`/`-feed-poll` throw there and
a single-process agent needs none of this.

Additive: requires only `channel` and `clojure.walk`.  Nothing in core loads it.
raw docstring

vaelii.impl.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 (targetFollowingPredicate), 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.

reply / assert / 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, sentex, and koinii identity. Every write goes through the provenance-stamping 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
  (`targetFollowingPredicate`), 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.

`reply` / `assert` / `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`,
`sentex`, and koinii `identity`.  Every write goes through the provenance-stamping
`assert` path — never `bulk-assert-facts!`.
raw docstring

vaelii.impl.koinii.deref

Koinii cross-seat dereference: the DISTRIBUTED topology, where independent seats — separate processes, each holding its own copy of the KB — stay in sync by content-addressed commits rather than by sharing one live daemon. A seat asserts a sentence and commits; another seat pulls the same commit and resolves the same sentence from its own KB. The locator travels over a transport; the proof comes from the KB, and neither seat trusts the marker.

A different deployment shape from koinii's default (N agents funnelling writes through one single-writer daemon — docs/koinii.md, The deployment shape). There the daemon IS the shared KB and dereference is a plain read; here each seat is its own reader with its own store, and the shared reference point is a commit, not a socket. Complements, not rivals: the daemon for live co-writing, this for disconnected or independently-replicated seats.

The spike's vocabulary, mapped to real primitives:

  • seat — an independent KB-holding process. In this layer a seat is just a kb; every function takes one, exactly as the other koinii modules do.
  • locator / marker — a content-addressed reference to a canonical assertion (the locator), plus the untrusted transport payload that carries it (the marker).
  • transport — any byte-faithful channel that carries a marker. A marker is a plain Clojure map, so any transport that ships EDN ships one.
  • the KB — the sole authority for meaning. A marker is never trusted, only resolved: dereference finds the sentence in the seat's own store, reads its provenance, and hands off to why for the proof.

Three ideas, each grounded on a primitive that ships:

  • The locator is content-addressed, not handle-addressed. A handle is a number one store minted and does not travel; a locator is a self-describing digest — the literal "sha256:" followed by 64 lowercase hex chars — over a sentex's canonical identity (its context, truth polarity, and canonicalized sentence, docs/canonicalization.md). Three best-practice commitments live in that one string:
    • SHA-256, not SHA-1. A locator is a tamper-detection boundary — dereference rehashes the resolved form and compares — and SHA-1 has practical chosen-prefix collisions, so it is the wrong primitive for this job.
    • Self-describing. The "sha256:" multihash-style tag names the algorithm in the value itself, so a later migration to another primitive is unambiguous rather than a silent reinterpretation of 64 anonymous hex chars.
    • A spec'd canonical encoding, not pr-str. The digest input is the explicit, type-tagged, self-delimiting byte encoding of canonical-bytes — independent of ambient *print-* vars and injective across the value space a sentence holds, so a symbol never digests as the like-spelled string and (a b) never as (a (b)). Two seats holding the same assertion compute the same locator, because v/import! re-canonicalizes every record through the reading build's own constructor.
  • The commit is a Merkle function of state. commit-id is the RFC-6962-style Merkle root over the seat's sorted per-sentex leaf digests — leaf hashing domain-separated with a 0x00 byte and internal-node hashing with 0x01, so a leaf can never be forged as an internal node — prefixed "sha256:". Order- and handle-independent by construction, so two seats that reached the same set of assertions by different routes compute the same commit id (belief and storage are order-independent — docs/nmtms.md), and a KB exported, pulled and recovered on another seat carries the id across. The Merkle shape (over the old flat re-hash) buys pure auditability: inclusion-proof yields an audit path and verify-inclusion recomputes the root from just a (locator, proof) pair — no KB — which a flat digest cannot. commit-id fingerprints knowledge (what two seats compare to agree they hold the same thing); state-root is a second root whose leaves fold each record's provenance (:creator + :created + identity), a full snapshot identity like a git commit (covers who/when), so it moves when provenance moves even when content does not. publish! / pull! move the bytes (via v/export! / v/import!); the roots are what say two seats hold the same knowledge (and the same snapshot).
  • The marker is untrusted. dereference finds the sentence in the seat's own KB and rehashes what it found; a stale or tampered marker fails that check and is rejected, and a marker the seat cannot resolve means the commit was not received — never that the marker's payload should be believed. Attribution is trustworthy only as far as the identity model makes it: a distributed KB inherits the same cooperative-vs-proof-tier question.

Additive, like the other koinii modules: only the public core API plus protocols (to walk the record store) and resolution (to canonicalize a sentence the store's own way) — nothing in core loads it.

Koinii cross-seat dereference: the DISTRIBUTED topology, where
independent *seats* — separate processes, each holding its own copy of the KB — stay
in sync by **content-addressed commits** rather than by sharing one live daemon.  A
seat asserts a sentence and commits; another seat pulls the same commit and resolves
the same sentence **from its own KB**.  The locator travels over a transport; the
proof comes from the KB, and neither seat trusts the marker.

A different deployment shape from koinii's default (N agents funnelling writes through
one single-writer daemon — `docs/koinii.md`, *The deployment shape*).  There the
daemon IS the shared KB and dereference is a plain read; here each seat is its own
reader with its own store, and the shared reference point is a commit, not a socket.
Complements, not rivals: the daemon for live co-writing, this for disconnected or
independently-replicated seats.

The spike's vocabulary, mapped to real primitives:

- **seat** — an independent KB-holding process.  In this layer a seat is just a `kb`;
  every function takes one, exactly as the other koinii modules do.
- **locator / marker** — a content-addressed reference to a canonical assertion (the
  `locator`), plus the untrusted transport payload that carries it (the `marker`).
- **transport** — any byte-faithful channel that carries a marker.  A marker is a
  plain Clojure map, so any transport that ships EDN ships one.
- **the KB** — the sole authority for *meaning*.  A marker is never trusted, only
  resolved: `dereference` finds the sentence in the seat's own store, reads its
  provenance, and hands off to `why` for the proof.

Three ideas, each grounded on a primitive that ships:

- **The locator is content-addressed, not handle-addressed.**  A handle is a number
  one store minted and does not travel; a locator is a **self-describing** digest — the
  literal `"sha256:"` followed by 64 lowercase hex chars — over a sentex's **canonical
  identity** (its context, truth polarity, and canonicalized sentence,
  `docs/canonicalization.md`).  Three best-practice commitments live in that one string:
    * **SHA-256, not SHA-1.**  A locator is a tamper-detection boundary — `dereference`
      rehashes the *resolved* form and compares — and SHA-1 has practical chosen-prefix
      collisions, so it is the wrong primitive for this job.
    * **Self-describing.**  The `"sha256:"` multihash-style tag names the algorithm in
      the value itself, so a later migration to another primitive is unambiguous rather
      than a silent reinterpretation of 64 anonymous hex chars.
    * **A spec'd canonical encoding, not `pr-str`.**  The digest input is the explicit,
      type-tagged, self-delimiting byte encoding of `canonical-bytes` — independent of
      ambient `*print-*` vars and injective across the value space a sentence holds, so
      a symbol never digests as the like-spelled string and `(a b)` never as `(a (b))`.
  Two seats holding the same assertion compute the **same** locator, because `v/import!`
  re-canonicalizes every record through the reading build's own constructor.
- **The commit is a Merkle function of state.**  `commit-id` is the RFC-6962-style
  **Merkle root** over the seat's *sorted* per-sentex leaf digests — leaf hashing
  domain-separated with a `0x00` byte and internal-node hashing with `0x01`, so a leaf
  can never be forged as an internal node — prefixed `"sha256:"`.  Order- and
  handle-independent by construction, so two seats that reached the same set of
  assertions by different routes compute the same commit id (belief and storage are
  order-independent — `docs/nmtms.md`), and a KB exported, pulled and recovered on
  another seat carries the id across.  The Merkle shape (over the old flat re-hash) buys
  pure auditability: `inclusion-proof` yields an audit path and `verify-inclusion`
  recomputes the root from just a `(locator, proof)` pair — no KB — which a flat digest
  cannot.  `commit-id` fingerprints **knowledge** (what two seats compare to agree they
  hold the same thing); `state-root` is a second root whose leaves fold each record's
  provenance (`:creator` + `:created` + identity), a full **snapshot** identity like a
  git commit (covers who/when), so it moves when provenance moves even when content does
  not.  `publish!` / `pull!` move the bytes (via `v/export!` / `v/import!`); the roots
  are what say two seats hold the same knowledge (and the same snapshot).
- **The marker is untrusted.**  `dereference` finds the sentence in the seat's own KB
  and rehashes what it found; a stale or tampered marker fails that check and is
  rejected, and a marker the seat cannot resolve means the commit was not received —
  never that the marker's payload should be believed.  Attribution is trustworthy only
  as far as the identity model makes it: a distributed KB inherits the same
  cooperative-vs-proof-tier question.

Additive, like the other koinii modules: only the public core API plus `protocols`
(to walk the record store) and `resolution` (to canonicalize a sentence the store's
own way) — nothing in core loads it.
raw docstring

vaelii.impl.koinii.dispute

Koinii dispute reads: two context-scoped views over the engine's whole-KB contradiction surface, plus the small dispute-STATE surface the adjudication driver drives.

The engine represents contradiction but answers it only whole-KB: contradictions and conflicts each scan the whole store and hand back entries whose sides carry their own :context. A subscriber wants the per-channel question — 'is there an open dispute here?' — so that scoping lives in one wrapper rather than being re-derived at every call site.

'Disputed' is a precise word. It is NOT 'false' and NOT 'defeated-by-strength'. A dispute is a coexisting clash — S and ¬S both believed with no strength winner, so argue returns :contradiction and the engine deliberately leaves both standing (paraconsistent tolerance — Priest's LP, the four-valued argue). A clean strength-defeat (a :monotonic premise beating a :default one) is the opposite: the loser is :defeated, argue returns :false, and that is resolved, not disputed.

Two error classes, kept distinct, never merged:

  • :contradiction — a coexisting :default dilemma (a rebuttal, or a definitional clash left at equal strength). Both sides believed; argue -> :contradiction.
  • :conflict — an irreducible clash among :monotonic content: two things asserted known-true that cannot both hold, which the engine has no grounds to prefer. Harder than a rebuttal, and a caller usually wants to see it alongside.

Detection matches the engine, not the intuition. A coexisting dilemma keeps BOTH sides in?, so why-not reports :believed? true on either — it never reads :defeated there (that reason is reserved for the strength-defeat, i.e. the resolved case). So the named-sentence read is argue, whose :contradiction verdict is exactly 'both provable, neither strength-wins' and whose context scoping is exactly 'the asker sees both sides'. argue is per-sentence and never computes whole-KB contradictions, which is the perf property the hot path needs.

This module owns the reads, the state vocabulary, and the dispute id. The clock, the timeout value, and the notify sink are the driver's. So the recording functions here take the timestamp (and stale reason) from the caller rather than reading a clock: this module supplies the mechanism, the driver supplies the policy.

Additive, like the sibling koinii modules: only the public core API (argue, canonical-sentex, sentex-handle) — nothing under vaelii.impl, and nothing in core loads it.

Koinii dispute reads: two context-scoped views over the engine's
whole-KB contradiction surface, plus the small dispute-STATE surface the
adjudication driver drives.

The engine *represents* contradiction but answers it only whole-KB: `contradictions`
and `conflicts` each scan the whole store and hand back entries whose sides carry
their own `:context`.  A subscriber wants the per-channel question — 'is there an open
dispute *here*?' — so that scoping lives in one wrapper rather than being re-derived at
every call site.

**'Disputed' is a precise word.**  It is NOT 'false' and NOT 'defeated-by-strength'.
A dispute is a *coexisting* clash — S and ¬S both believed with no strength winner, so
`argue` returns `:contradiction` and the engine deliberately leaves both standing
(paraconsistent tolerance — Priest's LP, the four-valued `argue`).  A clean
strength-defeat (a `:monotonic` premise beating a `:default` one) is the opposite: the
loser is `:defeated`, `argue` returns `:false`, and that is *resolved*, not disputed.

Two error classes, kept distinct, never merged:

- **`:contradiction`** — a coexisting `:default` dilemma (a rebuttal, or a definitional
  clash left at equal strength).  Both sides believed; `argue` -> `:contradiction`.
- **`:conflict`** — an irreducible clash among `:monotonic` content: two things asserted
  known-true that cannot both hold, which the engine has no grounds to prefer.  Harder
  than a rebuttal, and a caller usually wants to see it alongside.

**Detection matches the engine, not the intuition.**  A coexisting dilemma keeps BOTH
sides `in?`, so `why-not` reports `:believed? true` on either — it never reads
`:defeated` there (that reason is reserved for the strength-defeat, i.e. the resolved
case).  So the named-sentence read is `argue`, whose `:contradiction` verdict is exactly
'both provable, neither strength-wins' and whose context scoping is exactly 'the asker
sees both sides'.  `argue` is per-sentence and never computes whole-KB contradictions,
which is the perf property the hot path needs.

This module owns the *reads*, the state *vocabulary*, and the dispute *id*.  The clock,
the timeout value, and the notify sink are the driver's.  So the
recording functions here take the timestamp (and stale reason) from the caller rather
than reading a clock: this module supplies the mechanism, the driver supplies the policy.

Additive, like the sibling koinii modules: only the public core API (`argue`,
`canonical-sentex`, `sentex-handle`) — nothing under `vaelii.impl`, and nothing in core
loads it.
raw docstring

vaelii.impl.koinii.identity

Koinii actor identity: per-agent contexts as the identity substrate AND the write boundary, an admin-only agent registry, and the one auth seam whose strength is conditional on the adjudication policy.

The engine deliberately pushes per-caller identity OUT — *creator* is an unauthenticated annotation and the daemon's only auth is one shared bearer token — so koinii's answer is the context lattice, not a new auth subsystem:

  • An agent IS its context. Atlas writes into CxAtlas, lifted under the channel by (genlCx CxDeploy CxAtlas). A reader of CxDeploy sees the union of every agent's assertions; CxAtlas alone is 'everything Atlas said' — a plain context read. Because context is part of sentex identity, Atlas's P and Boreas's P are two DISTINCT sentexes, each with its own creator, so first-writer-wins loses no co-source (co-attribution-survives?).
  • The write boundary is 'your own context, and nothing else.' That is the one enforcement point identity needs, and it is why the registry context is the one context agents may NOT write — the governed may not write the authority that governs them.

The auth seam is conditional on policy (koinii design D4):

  • Cooperative (the default) — *creator* bound by convention, the write routed to the agent's own context, trusted because the agents are. Correct for a notify-only deployment. It defends fat-fingers, NOT attackers: authenticate trusts the claimed id with no proof, so a client may claim any identity. State plainly that identity is unauthenticated here.
  • Proof-tier — REQUIRED the moment trust-resolve is enabled, because trust-weighting a spoofable identity is worse than no trust. authenticate verifies a credential (the verify-fn seam — sign-at-ingest, an authenticating proxy, or A2A AgentCards / DIDs) and REFUSES an unverified request; the write-boundary is enforced at that same seam.

Every write goes through the provenance-stamping assert path — NEVER bulk-assert-facts!, which binds *bulk-load?* and writes no provenance at all.

Koinii actor identity: per-agent contexts as the identity substrate AND the
write boundary, an admin-only agent registry, and the one auth seam whose strength
is conditional on the adjudication policy.

The engine deliberately pushes per-caller identity OUT — `*creator*` is an
unauthenticated annotation and the daemon's only auth is one shared bearer token —
so koinii's answer is the context lattice, not a new auth subsystem:

- **An agent IS its context.**  Atlas writes into `CxAtlas`, lifted under the
  channel by `(genlCx CxDeploy CxAtlas)`.  A reader of `CxDeploy` sees the union of
  every agent's assertions; `CxAtlas` alone is 'everything Atlas said' — a plain
  context read.  Because context is part of sentex identity, Atlas's `P` and
  Boreas's `P` are two DISTINCT sentexes, each with its own creator, so
  first-writer-wins loses no co-source (`co-attribution-survives?`).
- **The write boundary is 'your own context, and nothing else.'**  That is the one
  enforcement point identity needs, and it is why the registry context is the one
  context agents may NOT write — the governed may not write the authority that
  governs them.

The auth seam is conditional on policy (koinii design D4):

- **Cooperative** (the default) — `*creator*` bound by convention, the write routed
  to the agent's own context, trusted because the agents are.  Correct for a
  notify-only deployment.  It defends fat-fingers, NOT attackers: `authenticate`
  trusts the claimed id with no proof, so a client may claim any identity.  State
  plainly that identity is unauthenticated here.
- **Proof-tier** — REQUIRED the moment trust-resolve is enabled, because
  trust-weighting a spoofable identity is worse than no trust.  `authenticate`
  verifies a credential (the `verify-fn` seam — sign-at-ingest, an authenticating
  proxy, or A2A AgentCards / DIDs) and REFUSES an unverified request; the
  write-boundary is enforced at that same seam.

Every write goes through the provenance-stamping `assert` path — NEVER
`bulk-assert-facts!`, which binds `*bulk-load?*` and writes no provenance at all.
raw docstring

vaelii.impl.koinii.speech-acts

Koinii speech-acts: the small vocabulary of moves agents make, as sentexes in the KB. A move is not an out-of-band message but knowledge — queryable, retractable, auditable like any other fact — and the SHAPE of the move carries the layer's headline property (koinii design D1 / D5).

Two kinds of move, one split (koinii.md, Reply is an assertion):

  • Origination (asserts, queries) — a plain assertion in the acting agent's own context. The claim, or the query node, plus its provenance IS the act: nothing wraps it, because provenance already records who spoke (first-writer-wins). So an assertion in koinii is just an assertion — assert-claim mints no asserts edge. A query is minted as a node (pose-query), because a question must be told apart from a claim.
  • Response (answers, disputes, endorses, justifies) — a META-SENTEX on the target sentex, naming it by handle (sentexHandle H), asserted in the RESPONDER's own context and stamped with the responder as creator. Each response predicate is declared targetFollowingPredicate in CxSpeechActs, so retracting a target sweeps its replies with it (core/retract-following-metas!). Two facts force this: the cascade needs BOTH the meta-sentex AND the mark (an unmarked meta orphans harmlessly), and first-writer-wins forces each act to be its own object — two endorsers are two sentexes with two creators, never one re-assert.

retracts is the engine's retract! on a handle. The error acts (notUnderstood, refuse) name the received edge, in the refusing agent's context, and are deliberately unmarked. This layer only REPRESENTS the moves; adjudication is a separate layer.

Additive, like the sibling koinii modules: requires only the public core API and koinii identity — nothing under vaelii.impl, and nothing in core loads it. Every write goes through the provenance-stamping assert path, never bulk-assert-facts!.

Koinii speech-acts: the small vocabulary of moves agents make, as
sentexes in the KB.  A move is not an out-of-band message but knowledge — queryable,
retractable, auditable like any other fact — and the SHAPE of the move carries the
layer's headline property (koinii design D1 / D5).

Two kinds of move, one split (`koinii.md`, *Reply is an assertion*):

- **Origination** (`asserts`, `queries`) — a plain assertion in the acting agent's
  own context.  The claim, or the query node, plus its provenance IS the act: nothing
  wraps it, because provenance already records who spoke (first-writer-wins).  So an
  assertion in koinii is just an assertion — `assert-claim` mints no `asserts` edge.
  A query is minted as a node (`pose-query`), because a question must be told apart
  from a claim.
- **Response** (`answers`, `disputes`, `endorses`, `justifies`) — a META-SENTEX on
  the target sentex, naming it by handle `(sentexHandle H)`, asserted in the
  RESPONDER's own context and stamped with the responder as creator.  Each response
  predicate is declared `targetFollowingPredicate` in `CxSpeechActs`, so retracting a
  target sweeps its replies with it (`core/retract-following-metas!`).  Two facts
  force this: the cascade needs BOTH the meta-sentex AND the mark (an unmarked meta
  orphans harmlessly), and first-writer-wins forces each act to be its own object —
  two endorsers are two sentexes with two creators, never one re-assert.

`retracts` is the engine's `retract!` on a handle.  The error acts
(`notUnderstood`, `refuse`) name the received edge, in the refusing agent's context,
and are deliberately unmarked.  This layer only REPRESENTS the moves; adjudication is
a separate layer.

Additive, like the sibling koinii modules: requires only the public core API and koinii
`identity` — nothing under `vaelii.impl`, and nothing in core loads it.  Every write goes
through the provenance-stamping `assert` path, never `bulk-assert-facts!`.
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