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):
: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).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.
Where a newly-opened dispute is pushed: (fn [dispute-entry] …), called once per
dispute as it transitions open -> notified. This is the seam the channel's feed
subscribers and a configured human sink wire into; nil ships no push (the
lifecycle mark is still recorded, so nothing is lost — a later notify-disputes with a
sink still finds it un-pushed only if the mark was cleared). Idempotency does not live
here — it lives in the stored mark — so a sink need not dedupe.
Where a newly-opened dispute is pushed: `(fn [dispute-entry] …)`, called once per dispute as it transitions `open -> notified`. This is the seam the channel's feed subscribers and a configured human sink wire into; nil ships no push (the lifecycle mark is still recorded, so nothing is lost — a later `notify-disputes` with a sink still finds it un-pushed only if the mark was cleared). Idempotency does not live here — it lives in the stored mark — so a sink need not dedupe.
The operator sink for sweep-stale: (fn [dispute-entry] …), called once per dispute
swept to :stale. An aged-out dispute is re-surfaced to a human, never silently
dropped. nil ships no push.
The operator sink for `sweep-stale`: `(fn [dispute-entry] …)`, called once per dispute swept to `:stale`. An aged-out dispute is re-surfaced to a human, never silently dropped. nil ships no push.
How long a dispute may stay live with no ruling before sweep-stale flags it (and
re-surfaces it to a human). A judgement call — a curated ground truth wants it long
enough that a real disagreement is not swept before anyone looks. Default 24h.
How long a dispute may stay live with no ruling before `sweep-stale` flags it (and re-surfaces it to a human). A judgement call — a curated ground truth wants it long enough that a real disagreement is not swept before anyone looks. Default 24h.
(contested-premises kb S ctx)The disputed premises the conclusion S rests on in ctx: the handles in S's support
closure that are a side of an open dispute ctx observes. Empty if S rests on nothing
contested.
This is the paraconsistent default made HONEST. An open dispute does NOT block dependent
reasoning — the KB keeps deriving and both sides stay believed at :default — but a
conclusion resting on a contested premise should be visible as such so a reader is never
silently misled. A pure read: it changes no belief, unlike quarantine. Returns the
contested premise handles.
The disputed premises the conclusion `S` rests on in `ctx`: the handles in S's support closure that are a side of an open dispute `ctx` observes. Empty if S rests on nothing contested. This is the paraconsistent default made HONEST. An open dispute does NOT block dependent reasoning — the KB keeps deriving and both sides stay believed at `:default` — but a conclusion resting on a contested premise should be *visible as such* so a reader is never silently misled. A pure read: it changes no belief, unlike `quarantine`. Returns the contested premise handles.
The principal recorded as ruler of a majority-vote resolution — not a real agent but
the stand-in 'the house majority', so who-ruled tells a counted vote apart from a named
arbiter's decree.
The principal recorded as ruler of a majority-vote resolution — not a real agent but the stand-in 'the house majority', so `who-ruled` tells a counted vote apart from a named arbiter's decree.
(notify-disputes kb channel)The default policy's core move: announce every not-yet-announced dispute channel
observes and record it notified. For each :open dispute (from the dispute module's
pending-disputes), stamp a notified mark (open -> notified) and push the entry to
*notify-sink*. Changes NO belief — both sides stay in? at :default and argue
still reports :contradiction; only the lifecycle record moves.
Fires once per dispute. The stored mark is the idempotency key: a notified dispute
is no longer :open, so a redelivery or a catch-up re-run does not re-push
it. Returns the disputes newly notified.
The default policy's core move: announce every not-yet-announced dispute `channel` observes and record it `notified`. For each `:open` dispute (from the dispute module's `pending-disputes`), stamp a `notified` mark (`open -> notified`) and push the entry to `*notify-sink*`. Changes NO belief — both sides stay `in?` at `:default` and `argue` still reports `:contradiction`; only the lifecycle record moves. **Fires once per dispute.** The stored mark is the idempotency key: a notified dispute is no longer `:open`, so a redelivery or a catch-up re-run does not re-push it. Returns the disputes newly notified.
(poll kb channel)One driver tick over channel: notify the fresh disputes, then sweep the aged ones.
Returns {:notified [...] :stale [...]}. A subscribe loop or a timer calls
this; the two halves are also usable apart.
One driver tick over `channel`: notify the fresh disputes, then sweep the aged ones.
Returns `{:notified [...] :stale [...]}`. A subscribe loop or a timer calls
this; the two halves are also usable apart.(quarantine kb channel target-handle)OPTIONAL, off by default: hide the contested sentex target-handle from channel's
reads and derivations, reversibly, via an index-layer except asserted in channel. For
a high-stakes channel that must never let a conclusion rest silently on a contested
premise. Scoped to channel: the claim stays believed in its OWN context (its author
still holds it); only channel, and what channel feeds, stops seeing it.
The trade-off, and why it is off by default. Quarantine never lets a conclusion rest
silently on a contested premise — but it OVER-SUPPRESSES: with the claim masked, channel
can no longer see the dispute at all (argue there reads the lone surviving side, not
:contradiction), and except is an index-layer mask that interacts with the TMS and
contexts. Prefer contested-premises / rests-on-contested? (pure reads that surface
the risk without hiding anything) unless a channel genuinely must exclude contested claims
from derivation. Returns the mask's handle; unquarantine! retracts it and restores the
claim (cascading its derivations back).
OPTIONAL, off by default: hide the contested sentex `target-handle` from `channel`'s reads and derivations, reversibly, via an index-layer `except` asserted in `channel`. For a high-stakes channel that must never let a conclusion rest silently on a contested premise. Scoped to `channel`: the claim stays believed in its OWN context (its author still holds it); only `channel`, and what `channel` feeds, stops seeing it. **The trade-off, and why it is off by default.** Quarantine never lets a conclusion rest silently on a contested premise — but it OVER-SUPPRESSES: with the claim masked, `channel` can no longer see the dispute at all (`argue` there reads the lone surviving side, not `:contradiction`), and `except` is an index-layer mask that interacts with the TMS and contexts. Prefer `contested-premises` / `rests-on-contested?` (pure reads that surface the risk without hiding anything) unless a channel genuinely must exclude contested claims from derivation. Returns the mask's handle; `unquarantine!` retracts it and restores the claim (cascading its derivations back).
(quarantined kb channel)The sentex handles channel is currently quarantining — the targets of its
(except (sentexHandle ?h)) masks. 'What has this channel screened out' as a plain read.
The sentex handles `channel` is currently quarantining — the targets of its `(except (sentexHandle ?h))` masks. 'What has this channel screened out' as a plain read.
(resolve-by-majority kb id claim-handle channel)Resolve dispute id over the claim at claim-handle by MAJORITY VOTE, within
channel. Count the ballots (tally); if one side has strictly more, uphold it as a
reversible ruling (rule, arbiter majority-arbiter) — the claim for a :for majority,
its negation for an :against majority — so the clash clears and retracting the ruling
reopens it. A tie upholds nothing: an evenly-split house (or one nobody has voted in)
stays honestly disputed, the leave-open default holding rather than a winner picked by
fiat — which is the whole reason to count instead of decree.
Returns {:for n :against n :outcome :for/:against/:tie :ruling handle-or-nil}. Idempotent
in spirit: re-running after a resolution re-counts and re-rules (a fresh monotonic assert
of the same side, a no-op on belief), so a driver may poll it.
Resolve dispute `id` over the claim at `claim-handle` by MAJORITY VOTE, within
`channel`. Count the ballots (`tally`); if one side has strictly more, uphold it as a
reversible ruling (`rule`, arbiter `majority-arbiter`) — the claim for a `:for` majority,
its negation for an `:against` majority — so the clash clears and retracting the ruling
reopens it. **A tie upholds nothing**: an evenly-split house (or one nobody has voted in)
stays honestly disputed, the leave-open default holding rather than a winner picked by
fiat — which is the whole reason to count instead of decree.
Returns `{:for n :against n :outcome :for/:against/:tie :ruling handle-or-nil}`. Idempotent
in spirit: re-running after a resolution re-counts and re-rules (a fresh monotonic assert
of the same side, a no-op on belief), so a driver may poll it.(rests-on-contested? kb S ctx)Does the conclusion S in ctx rest on any premise that is currently disputed there?
The boolean over contested-premises — the flag a high-stakes reader checks before
trusting a derived answer.
Does the conclusion `S` in `ctx` rest on any premise that is currently disputed there? The boolean over `contested-premises` — the flag a high-stakes reader checks before trusting a derived answer.
(rule kb arbiter id upheld channel)Arbiter escalation: arbiter (an agent id, e.g. AgentArbiter) rules dispute id
within channel in favour of upheld — which MUST be one of the dispute's two clashing
sentences.
The ruling is an ordinary assertion: upheld at :monotonic strength in the arbiter's
OWN context (id/context-for), lifted under channel so the channel sees it, stamped
creator arbiter and tagged :adjudication id in provenance. Monotonic strength
defeats the losing :default side, so the coexisting clash clears — the dispute reads
:resolved and argue collapses to :true/:false. why on upheld shows the
adjudication and who ruled.
Reversible. Retract the returned handle and the losing side is no longer defeated —
the dispute reopens, cascading through the JTMS. A ruling koinii could not undo would be
a worse store than one that stays honestly disputed. The dispute's open/notified/stale
marks are cleared as the episode ends, so a reopen starts fresh at :open (and
re-notifies). Returns the ruling handle.
Resolves a :default coexisting dilemma only. A :monotonic :conflict (two things
asserted known-true) cannot be settled by a monotonic ruling — it needs a human to
retract a premise — so it is not this path's job.
Arbiter escalation: `arbiter` (an agent id, e.g. `AgentArbiter`) rules dispute `id` within `channel` in favour of `upheld` — which MUST be one of the dispute's two clashing sentences. The ruling is an ordinary assertion: `upheld` at `:monotonic` strength in the arbiter's OWN context (`id/context-for`), lifted under `channel` so the channel sees it, stamped creator `arbiter` and tagged `:adjudication id` in provenance. Monotonic strength defeats the losing `:default` side, so the coexisting clash clears — the dispute reads `:resolved` and `argue` collapses to `:true`/`:false`. `why` on `upheld` shows the adjudication and who ruled. **Reversible.** Retract the returned handle and the losing side is no longer defeated — the dispute reopens, cascading through the JTMS. A ruling koinii could not undo would be a worse store than one that stays honestly disputed. The dispute's open/notified/stale marks are cleared as the episode ends, so a reopen starts fresh at `:open` (and re-notifies). Returns the ruling handle. Resolves a `:default` coexisting dilemma only. A `:monotonic` `:conflict` (two things asserted known-true) cannot be settled by a monotonic ruling — it needs a human to retract a premise — so it is not this path's job.
(sweep-stale kb channel)Sweep every dispute channel observes that has stayed live past *timeout-ms* with no
ruling to :stale, pushing each to *stale-sink*. A stale dispute is STILL live — both
sides coexist, argue still reports :contradiction — :stale only flags that it aged
out unaddressed, so open disputes do not accumulate unbounded and none is silently
dropped. Idempotent: an already-:stale dispute is skipped. Returns the disputes swept.
Sweep every dispute `channel` observes that has stayed live past `*timeout-ms*` with no ruling to `:stale`, pushing each to `*stale-sink*`. A stale dispute is STILL live — both sides coexist, `argue` still reports `:contradiction` — `:stale` only flags that it aged out unaddressed, so open disputes do not accumulate unbounded and none is silently dropped. Idempotent: an already-`:stale` dispute is skipped. Returns the disputes swept.
(tally kb claim-handle)Count the ballots cast on the claim at claim-handle: {:for n :against n}, each a
count of DISTINCT voters. Matched anywhere (?ctx) because a ballot names the
globally-unique claim handle — the same reason the dispute and channel recovery reads do; a channel
read would miss ballots sitting in the voters' own contexts (sentexes-matching scopes
to a context's own sentexes, not the genlCx cone).
A voter who cast BOTH stances (without retracting the first, against vote's contract)
has SPOILED their ballot — counted on neither side — so one self-contradicting voter can
neither manufacture a tie nor swing a majority; their confusion abstains rather than
double-voting.
Count the ballots cast on the claim at `claim-handle`: `{:for n :against n}`, each a
count of DISTINCT voters. Matched anywhere (`?ctx`) because a ballot names the
globally-unique claim handle — the same reason the dispute and channel recovery reads do; a channel
read would miss ballots sitting in the voters' own contexts (`sentexes-matching` scopes
to a context's own sentexes, not the genlCx cone).
A voter who cast BOTH stances (without retracting the first, against `vote`'s contract)
has SPOILED their ballot — counted on neither side — so one self-contradicting voter can
neither manufacture a tie nor swing a majority; their confusion abstains rather than
double-voting.(unquarantine! kb handle)Undo a quarantine: retract the except mask at handle, so the claim and anything it
fed count again in the channel. Returns retract!'s counts.
Undo a `quarantine`: retract the `except` mask at `handle`, so the claim and anything it fed count again in the channel. Returns `retract!`'s counts.
(who-ruled kb ruling-handle)Read a ruling off its handle: {:arbiter :dispute-id :at} from the provenance rule
stamped, or nil if ruling-handle is not an adjudication assertion. 'Who ruled this
dispute, and when' as a plain provenance read.
Read a ruling off its handle: `{:arbiter :dispute-id :at}` from the provenance `rule`
stamped, or nil if `ruling-handle` is not an adjudication assertion. 'Who ruled this
dispute, and when' as a plain provenance read.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |