Notable changes to vaelii, newest first. Versions follow
semantic versioning; pre-1.0, a Breaking
entry raises the minor. What each class means, and why a Refusal is patch-eligible,
is CONTRIBUTING.md §3.
Releases before 0.17.0 are summarized rather than reproduced. Each one keeps its
title, its class census and every *Breaks:* token, so an upgrade across several
releases is still a grep for the name you call. The full entry prose for a released
version is in this file's git history, at the tag of the release that shipped it —
git show v0.16.0:CHANGELOG.md.
Every bounded entry point refuses a value outside its domain by name, and the anytime
budget roster splits into what ask-within reads and what prove-within reads. A bound
holding a value it cannot mean — a string :max-ms, a non-integer :max-depth, a non-fn
:on-progress, a string :believed? — reaches arithmetic or a call and throws a bare
ClassCastException, which the daemon answers :internal-error (a 500) where every sibling
refusal is a typed 400. prove / ask / query / why / find-terms already refuse
their caps; prove-within, ask-within, search-tree, compare-tacticians,
forward-chain, assert, assert-rule, abduce, kb-quality, export!, import!,
clear-caches and the extent readers now do too, reading the one shared domain table
(vaelii.impl.opts/bound-domains). A bound of 0 stays a real question wherever it was
one — no time, no rule expansion, realize nothing and resume, report at every opportunity.
Beside the value gaps: add-evaluatable refuses a key off #{:result :arity :cost :completeness}; set-cache-limit warns and records a pin for an id no cache has registered
yet rather than refusing it, since the register fills lazily; and assert-rule runs the
unrecovered-KB gate before its range check, so an unrecovered KB answers :unrecovered-kb
and not :not-range-restricted. The anytime roster splits the way ask and prove split
theirs: ask-within reads :max-ms / :max-results / :max-cost and refuses a rule
depth, prove-within reads the two clocks plus :max-depth / :max-term-growth and
refuses :max-cost, and resume holds the union so a continuation of either passes.
Class: Refusal (the two accepted-and-wrong boolean values answered at a setting nobody
chose, and the bad numbers crashed the daemon). Migration: nothing for a caller whose
bounds are numbers of the right kind; (clear-caches kb {:counters? "yes"}) and an extent
read with a string :believed? were both accepted as true and are now :unknown-option,
so pass an actual boolean. docs/api.md, docs/anytime.md
Breaks: :counters?, :believed?, :max-cost, :max-depth, :max-term-growth, add-evaluatable
assert-inert refuses an open sentence and stores an (ist Ctx S) form as S in
Ctx; describe refuses a :limit, and why-not a :nearest, that is not a count.
An inert (dog ?x) was a stored record a CxEverything read answered as a fact, and a
stored sentence is closed (checks/check-ground, the refusal assert makes:
:not-ground). An (ist CxA S) stored as written was a record with the ist functor,
which handle-of, contexts-of and every read resolve past — reachable by its handle
and by nothing else; it now stores S in CxA, as assert does, and a malformed one is
:shape. (describe kb t c {:limit 0}) answered every window empty under a :total
that read as an answer, and {:limit "5"} reached take and threw a bare
ClassCastException, which the daemon reports as :internal-error; both are
:unknown-option now, as at find-terms. (why-not kb s c {:nearest "3"}) read the
value as no request and answered the plain :not-stored; a :nearest that is not a
non-negative integer is :unknown-option, and 0 still asks for nothing.
Class: Refusal (the stored records matched no query, and the accepted values
answered nothing a caller asked for). Migration: nothing for a caller whose inert
sentences are ground and whose caps are positive integers; a caller passing
{:limit 0} to describe for the totals alone passes 1 and reads :total.
docs/api.md, docs/solving.md
Breaks: assert-inert, describe, why-not
The upper ontology gains a space/time division, a metatype-order ladder and an
expression subtree, renames spatial_thing to spatial and temporal_thing to
temporal, and adds a CxUniverse collector context for the cross-member disjoints.
CxCore roots the space/time divisions — spatial and aspatial, temporal and
atemporal — and the abstract collection under thing, declares the complement
disjoints (disjoint spatial aspatial) and (disjoint temporal atemporal) beside the
cross-division (disjoint intangible spatial), and re-roots abstract under
intangible; the rename removes temporal's only prior upward edge, so
(genl temporal thing) restores it and (disjoint physical_object intangible) lands in
CxAbstract. A metatype-order ladder — metatype, meta_metatype, at_least_metatype,
fixed_order_type, variable_order_type, type_type_by_order — states the three-order
partition and its subtype edges, and the inert predicates genlInverse, typeGenl and
partitionedByType classify without an inference path. CxAbstract gains an expression
subtree under abstract holding context, formula, relation, relation_application
and unrepresented_term, and a substance division whose states are solid, liquid,
gas and the added plasma, with liquid moved out of stuff_type_by_substance
because a liquid is a state and not a material. The two typeGenl claims move from
CxCore, which sees no member, to CxAbstract, which sees substance and the stuff
metatypes, so a type-relating claim is written only in a context that sees the terms it
names. A new top-level CxUniverse.txt collector context holds (disjoint organization animal), a disjointness a single context sees both members of, and the starter loader
now discovers and loads top-level kb/Cx<Name>.txt collector files after the upper and
middle theories so the cross-member axiom is a live sentex. Three tests pin the shipped
set: one refuses a type-relating claim whose context cannot see a term it names, one
holds the six head-context terms kept there on purpose, and ontology_test pins the
quality clash census at {:negation 4, :disjoint 8} (the remaining eight come from the
integer-and-person checker, a stated limitation rather than real conflicts).
Class: Additive (shipped ontology content, which takes no Breaking label however
far it moves an answer). Migration: a KB that wrote spatial_thing or temporal_thing
renames to spatial / temporal; the old spelling stores clean under open-world
semantics but attaches to nothing in the taxonomy, so a spatial or temporal argument
check does not admit it. docs/contexts.md,
docs/glossary.md
Breaks: spatial_thing, temporal_thing
A process-wide cache profile scales every derived cache's count bound by one
multiplier, tunable through VAELII_CACHE_SCALE and three new vaelii.core
functions. caches/limit-of is the one resolver every count-bounded cache reads, on
both its store path and its caches reporting row, so the enforced bound and the
reported bound are one number. It returns the shipped default at scale 1.0 with no
override, and otherwise default × scale floored at 16 entries. VAELII_CACHE_SCALE
(default 1.0, a number at or above zero, read at load) sets the scale a process starts
with and refuses a non-number or a negative value at engine load. (set-cache-scale x)
multiplies every counted bound on a running process, (set-cache-limit id n) pins one
cache's bound or clears the pin with nil, and (cache-profile) returns the profile in
force as {:scale m :overrides {id limit}}; set-cache-scale and set-cache-limit each
refuse a value they cannot mean with :type :unknown-option. The browser /caches page
gains a scale form posting an origin-checked write beside the existing clear. At scale
1.0 with no override the resolver returns the shipped default unchanged, so the cost
budgets and the perf gate read the same bounds as before. Class: Additive (three
new public functions and one new configuration name; test/golden/api-surface.edn and
test/golden/config-surface.edn move with them). docs/caches.md,
docs/operations.md
A memory-pressure guard shrinks the caches when the old generation fills after a
collection and grows them back as it drains. The guard adds a second multiplier,
:pressure, to the cache profile, so a counted cache's bound becomes
default × scale × pressure floored at 16 entries and a pinned cache's bound becomes
override × pressure. A post-collection notification listener reads the old-generation
used-over-max fraction after each garbage collection: at or above 0.85 it halves
pressure and trims the counted caches to the lower bound, at or below 0.60 it raises
pressure by half again while pressure is below 1.0, and otherwise it holds. Pressure
is clamped to [0.125, 1.0], so a heap under sustained pressure keeps an eighth of each
cache rather than emptying it. The trim keeps the earliest entries of a plain map and
defers to a shape-aware cache's own recency order rather than clearing. The daemon and
the browser install the guard at startup — the browser feeds it the live KBs through the
catalog — and nothing attaches it at engine load, so a library embedding arms no
listener. A JVM whose collectors emit no such notification, or that names no
old-generation pool, holds pressure at 1.0. limit-of's docstring is corrected in the
same batch to say a pin is scaled by pressure alone, not by the profile scale.
Class: Additive (install-memory-guard! and the profile's :pressure field are
new; the guard's functions stay in vaelii.impl.caches and the servers install them, so
no public-API golden moves). docs/caches.md
A reified NAT or context constant is named by the SHA-256 of its expression, not a
per-process counter, so the same expression reifies to the same constant across
processes and rebuilds. constant-for names a minted constant nat/a<17 base62 digits>, or cx/a… for a context NAT, from the first 96 bits of the SHA-256 of the
printed expression, where the prior scheme minted nat/g<N> from a process-local
gensym counter. The base62 payload holds [A-Za-z0-9] only, because a reified constant
sits in argument position and the naming check forbids the - and _ a base64url
payload would carry. The constant symbol is documented as opaque and never read back — a
page and a remote reader are held to never show it, and belief never keys on it — so a
caller resolving the constant through term-expression or expand-expression still
reads the expression (FruitFn AppleTree) and observes no change. The content name
extends the engine's order-independence property to the constant symbol itself: two
processes that mint the same NAT now spell it identically, where the counter spelled it
by allocation order and left the cross-process merge to the collision-repair path. A dump
written by an earlier release keeps its nat/g… names, because dedup by the stored
termOfUnit expression runs before any mint and re-reifying resolves to the existing
constant. The skolem head-existential witnesses inherit the scheme through the same mint
path, and a hash collision degrades only into the ordinary two-expressions-one-constant
case the merge already reconciles by content. Class: Fix (the documented
order-independence invariant and the opaque-constant contract both held; the constant
symbol moves to match them). docs/nat.md, docs/skolem.md
kb-quality stops reporting a clash that no ground term can reach, when an antecedent
arg declaration demands a type disjoint from the term the paired rule types.
kb-quality's contradictions-in-waiting reading reports two rules whose consequents
place one unified term in two disjoint types, a clash that could form if both rules fire
for that term. A fourth rule-out reads each antecedent predicate's arg declarations up
the predicate hierarchy — through the reader assert uses — and drops a pair when a
declared argument type is disjoint from another type the two rules place the unified term
in, because no ground term then satisfies both antecedents. The rule-out reads the two
consequents beside the antecedents, since the demanding literal states nothing about its
own argument's type and the demand lives on the paired rule's arg declaration. At least
one side of a disjoint pair must be a declared argument type, so two stated unary-literal
types that clash stay the clash the pair reports. The rule-out reads arg and not
genlArg or the covering forms, because genlArg demands a subtype one stratum up and
no shipped rule concludes a unary type from a genlArg-typed antecedent. The docstring
and docs/quality.md already described the reading as one that reports a clash that could
form, so the code moves to stop reporting the unreachable pairs; the shipped clash census
drops the relation-classification false positives the upper-ontology additions
introduced, leaving the eight integer-and-person residual pairs ontology_test pins.
Class: Fix. docs/quality.md
handle-of and the sentence arity of why-not resolve a ground reifiable NAT to
its stored constant, as sentexes-matching and ist do. After
(ist kb C '(likes Rex (FruitFn Apple))) stored (likes Rex nat/…),
(handle-of kb '(likes Rex (FruitFn Apple)) C) answered nil and why-not answered
:not-stored, so (retract! kb (handle-of kb s c)) — the composition the docstrings
name — retracted nothing and reported {:removed-sentexes 0}. Both now run the
read-mode reify (dedup, never mint) before the lookup. Class: Fix.
docs/nat.md, docs/api.md
provable? and ask? refuse a non-map opts by name (:unknown-option), as
prove and ask do. (provable? kb g c :oops) threw a bare
IllegalArgumentException out of the seq that picks the bounded arm; the roster
check now runs first. The daemon pads its arguments to the option arity and answered
:bad-args already, so only an in-process caller saw the difference. Class: Fix.
docs/api.md
docs/api.md matches the code for reasoners, subsumption-status, contexts-of and
has-prop?. The API reference is corrected in four places to state what the functions
return. reasoners lists :brave-cautious, the ASP dilemma reader, beside the algebras.
subsumption-status returns :inconsistent when two of the genl relationships hold at
once, which the documented keyword set had omitted. contexts-of lists the contexts a
sentence is stored and believed in, so a defeated sentex's context is not among them,
where the line had said asserted. has-prop? recognizes six :declares-* prop kinds,
adding :declares-arg-and-rest-isa and :declares-arg-and-rest-genl. Class: Fix
(documentation only; the code already answered this way). docs/api.md
docs/solving.md states that a constraint background literal and an assumptionRule
antecedent ground over the registry leaf. A constraint background literal and an
assumptionRule antecedent reach provers/solve-goal, the registry leaf, so a stored
fact, a backward rule, or a registered prover or evaluatable answers one, not only a
believed fact, and a computed relation is a legal antecedent. The documentation had
understated the reach. A new solve_context_test case pins that a constraint body reaches
a prover. Class: Fix. docs/solving.md
The vocabulary load and the minting-assert path drop a redundant settle and two
redundant declaration reads. The CxCore and starter loaders wrap their bodies in
with-deferred-settle, so the JTMS settles once at the end rather than once per asserted
sentence, measured at roughly 200 ms off the starter load on the memory backend.
constraining-predicates stops seeding an undeclared predicate — a mint's own type
functor — into its result, dropping the per-assert match walk that returned nothing,
measured at the CxCore load falling from 327 ms to 272 ms with assertive argument types
on. The first-level materializer skips the definitional constraint re-check when the
assert-path entailment check already ran it, threaded through a pre-checked? flag, while
the deeper cascade levels and the retroactive sweeps still run the re-check;
assert_cost_test re-pins the read counts down. cascade-clash builds the disjointness
separation frame once and applies its closure per candidate, where it had rebuilt the
frame inside a filter, and perf's disjoint-clique-membership and
disjoint-metatype-membership claims guard the result at linear in member count.
Class: neither label — the prior path and the new one compute the same belief,
justification set and stored-sentex fingerprint, so only the load and assert latency move.
A script renders the upper-ontology genl hierarchy to one SVG.
scripts/ontology-graph.py reads the resources/kb/*.txt authoring files and emits a
layered is-a diagram — one node per type in the genl hierarchy, one edge per
(genl sub super) — with node fill set by the context a term is defined in and edge color
by the context the edge is written in, and it needs no KB boot. It draws through Graphviz
dot when dot is on the path and falls back to a dependency-free layout otherwise.
--engine selects among dot, gridfold, icicle and dendrogram, --ratio sets the
sheet aspect, and --text-scale sets the label point size independently of --ratio.
Class: Additive.
15 entries — 2 Breaking, 1 Refusal, 9 Additive, 3 Fix. Assertive argument types become
the default reading: an arg / genlArg / interArg declaration mints the type it
constrains rather than only testing for it. A bare implies rule defaults to :backward
and materializes nothing, and set/forwardRule adds forward chaining to the backward use
rather than replacing it, so a rule forward-chains only where its author asks. The arity
vocabulary gains a runtime floor — a variable-arity application below its arityMin is
refused — and admitsArgnum answers a position query from the declared arity. New
declaration vocabulary types a whole variable-arity tail (args, argsGenl, argAndRest,
argAndRestGenl) and names an intersection kind that derives its taxonomy edges, and new
readers report the brave and cautious status of a labeling dilemma, a cardinality bound over
ASP choice heads, and the subsumption status of every type pair. A state-of-affairs and
causality cluster joins the upper ontology in CxAbstract.
Breaks: VAELII_ASSERTIVE_ARG_TYPES, (implies asserted bare, set/forwardRule,
arityMin, (lessThan, (greaterThan, (termsRelated, (functionCorrespondingPredicate
14 entries — 1 Breaking, 4 Refusal, 4 Additive, 5 Fix. A declaration that restates
what the taxonomy already concludes turns that conclusion into a precondition, so the
arrival order of two assertions decides which facts a KB holds. Four entries retire such a
declaration — on genl, on fifteen unary marks, on six arity marks, and in the predAll
pair's third argument — and the arity vocabulary underneath is rebuilt so relation is
the common parent of predicate and function and every relation lands in exactly one
arity policy. predAllSpecified and predSpecifiedAll go binary and derive the filler
type from the predicate's own slot contract. Three composite function marks — injection,
surjection and bijection — arrive as one declaration each, a genlCx edge's merge
sweep stops growing with the KB, and a late symmetric declaration folds a mirrored pair
no earlier version could fold.
Breaks: (predAllSpecified, (predSpecifiedAll, specified-violations,
all-specified-violations, (binary_predicate P) beside (variable_arity P),
:arg-type, *assertive-arg-types?*, VAELII_ASSERTIVE_ARG_TYPES,
(genlArg genl 1 thing), (arg symmetric 1 predicate), (arg functional 1 predicate)
18 entries — 3 Breaking, 1 Refusal, 7 Additive, 7 Fix. The predAll quantifier
family lands in all eight cells. Three refusals stop answering with the wrong keyword:
an unpinned indeterminate term is not provably different from anything, a missing
adapter is not an unknown backend, and a wrong operand count is not an unknown option.
Declarations arriving after the facts now reach them — a (symmetric P) mark folds
records already stored, a computed genlCx edge runs the reconcilers a stated one runs,
and quotedArg is answered along the genl closure. Every refusal declares what its
ex-data carries, and a throw that drops a key fails the build.
Breaks: (different, indeterminate_term, :unknown-backend, :sqlite, :pg,
:unknown-option, :not-stratified
7 entries — 2 Breaking, 5 Additive. Definitional membership is answered at query
time rather than only by a forward rule. Two renames: the sentex polarity slot is
:polarity, and the AtomicSentex record is LiteralSentex. A unary predicate is
snake_case and assert enforces the spelling in both directions, which retired the
camelCase marks. CxCore names the expression kinds and gains a curation vocabulary.
Breaks: unaryPredicate, reifiableFunction, abduciblePredicate,
closedExtentPredicate, disjointMetatype, siblingDisjoint, warmBlooded, :truth
10 entries — 1 Refusal, 1 Additive, 5 Fix. The mapped index image becomes a backend
of its own, :disk-snapshot, rather than a property of the disk store, and stops
carrying the argument roots into heap. The disk store's live-handle sets become
compressed bitmaps. The writer refreshes a drifted image mid-life and can be told not
to. A functionalInArg declaration arriving after the facts it convicts is reported
rather than silently late. Neither adapter shipped at this version; both stayed at
0.13.0.
Breaks: vaelii.index.snapshot, :argument-family-ceiling
93 entries — 5 Breaking, 12 Refusal, 32 Additive, 22 Fix. The largest release:
calendar time, joined queries and a sweep through the entry points that refuse.
CxChange ships an event calculus, calendar constructors give a date its own endpoints
so it orders itself, and a metric constraint narrows an interval relation. or is
accepted in a rule antecedent, stored as one rule per alternative, and refused as a
goal. Every search entry point takes a bound and the daemon holds them to its ceiling.
Twelve refusals close inputs whose acceptance stored junk, and the :disk and
:pg-disk pairings are renamed to say that both halves are out of core.
Breaks: :disk, :pg-disk, VAELII_TEST_BACKEND=disk, edit!,
edit-with-consequences!, apply-proposal!, contexts, count-in-context,
contextDenotingFunction, lein cli load, prove, provable?, query, argue,
forward-chain, ask, ask?, query-plan, abduce, sentexes-matching,
handle-of, assert, load-text!, lein cli assert, unaryPredicate,
binaryPredicate, ternaryPredicate, /kbs, lein serve --listen <flag>,
vaelii.client/client, :timeout-ms, :token, dereference, resolve-by-locator,
set-trust!, trust-of, display-name-of
99 entries — 3 Breaking, 3 Refusal, 7 Additive, 5 Fix. Query contexts, bulk loading,
and a literal's type. resultIsa and resultGenl become result and genlResult; the
four function marks classify what they mark, and the reifiability criterion is written
down. A records read stays lazy, and a proof's witness is one of its bindings. Three
reads that could not answer the question stop answering empty. First release of the two
adapters, com.vaelii/postgres and com.vaelii/sqlite, each at this version.
Breaks: resultIsa, resultGenl, reifiableFunction, unreifiableFunction,
quotingFunction, contextDenotingFunction, ist, :proof?, ?ctx,
qualitative-network, possible-relations, :arg-type, :quoted-arg-type, result,
genlResult, :arg-genl, character_string, :pg-disk, :dir,
:stale-index-records, register-modal-predicate!
67 entries — 2 Breaking, 4 Additive. Contradiction solving, arrival order, and the
durable log. antiTransitive convicts the chain it forbids rather than being declared
and deferred. Definitional collection relations tie membership to a defining condition,
and sibling disjointness lets a collection's specializations separate themselves, with
an escape hatch for a pair that must overlap. A computed predicate or function is
registered in one line.
9 entries — 4 Additive. Koinii: several agents coordinate over one shared knowledge
base, with belief projection for what each agent holds true. A context can be a reified
function application whose genlCx edges compute themselves. Mention-opacity arrives —
a quoting function reads its argument by spelling — and quotedArg types an argument
against a syntactic type. The argIsa, argGenl and interArgIsa spellings become
arg, genlArg and interArg.
15 entries — 5 Breaking, 8 Additive. The dense truth-maintenance network becomes the
default and gives a concurrent reader a consistent view. Four relation properties are
enforced rather than documented. A subsumption rests on its strongest route rather than
its shortest. An algebraic property becomes one predicate instead of a mark and a twin,
which retired the ...Predicate spellings.
Breaks: defeat-class
50 entries — 1 Breaking, 1 Additive. Predicates inherit down the hierarchy. A KB
whose declared hazards are unresolved refuses writes rather than accepting them
unchecked, and a derived record's teardown is refused where belief was never built.
check and check-edit answer for the entry point they mirror. Five refusals close
recovery paths that believed records the store did not hold.
Breaks: :unrecovered-kb, write-hazards, note-hazards!, contradictions,
violations, :constraint-exposure
2 entries. Contexts get one spelling. A context name is Cx-prefixed rather than
Context-suffixed, and the context-transitivity predicate is genlCx.
22 entries — 2 Breaking, 4 Refusal, 2 Additive. Stored rules become first-class: a
rule can conclude a rule, and a rule carries a handle, TMS support and retraction with
no rule-specific machinery. A capability claim about a kind is capabilityType and
about a member is hasCapability. A NAF guard written as a conjunction now guards, and
the strictest policy stops being the leakiest.
15 entries. Faster writes, more to watch. A settle pays for the region it moved rather than for what the KB holds. The arbitrating half of a bounded pass says when its budget stopped it. Four places where arrival order decided an answer are closed.
23 entries. Operating the engine as a service. The daemon authenticates and refuses
to bind an address without a token. One space number names a KB's stores, :space,
replacing the separate record and index spellings. context-size becomes
count-in-context, different descends into compound arguments, and a name can carry a
sense and a lexeme.
Breaks: :record-space, :index-space, docs/storage.md
33 entries. Correctness fixes against the four invariants. A conjunctive query could
answer nothing while each of its conjuncts answered, and no longer does. assert
refuses a sentence that is not an s-expression, an exceptWhen query's literals are
held to the naming invariants, and an edit! batch key nothing reads is refused.
29 entries. A type on every refusal: every ex-info the engine throws carries a
:type, and the daemon's refusal keywords become plain. Both servers hold one
request-body ceiling, and the browser serializes its writes. An ist form must have
exactly three elements.
17 entries. The public API boundary is drawn — six public namespaces, everything
else vaelii.impl.* and free to change. Every handle-taking function refuses a
non-handle. close! releases a durable KB's directory, an argument-constraint refusal
names its convicting declaration in content order, and the five sweeps start running in
CI.
The first public release.
Twelve dated entries before versioning began, one per day of the initial build: the whole stack on day one (2026-07-19), then order independence made an invariant, equality and a sudoku solved, sound negation as failure, performance fixes and an operational surface, denser storage measured first, OpenCyc in the engine's own format, reads scoped to the asking context, aggregation over query results, the gate (lint, suite and scaling), one entry point for backward chaining, and declarations that re-check what they change (2026-07-30).
Can you improve this documentation?Edit on GitHub
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 |