Liking cljdoc? Tell your friends :D

vaelii.impl.llm.inventory

The KB's own vocabulary, put in front of the model — and the flag for when the model invents vocabulary anyway.

Measured failure mode. Asked to write type-level common sense about a term, every local model that writes usable s-expressions at all encodes structure into the predicate name rather than into arguments:

(implies (penguin ?x) (lives_in_antarctica ?x))
(implies (penguin ?x) (capable_of_swimming ?x))
(implies (penguin ?x) (thermoregulates_via_blubber_and_feathers ?x))

Each is admissible, novel, and useless: a one-off unary predicate joins no rule and matches no other sentence. (livesIn ?x Antarctica) is the same claim in vocabulary the KB can reason with. Swapping models does not fix it — the strongest model produces the most of it, because it produces the most output.

The check chain cannot catch it. A unary snake_case functor is a well-formed type name by the naming invariants, so (has_black_and_white_feathers ?x) passes naming, groundness, well-formedness, argIsa, disjointness and functionality alike — and it should, since coining a type is a legitimate thing to do. So there are exactly two guards, and they are both here:

  1. Preventioninventory / render put the relevant available vocabulary in the prompt with each predicate's arity and argument types, and the prompt tells the model to reuse it. A model that can see livesIn with args 1:animal 2:place writes (livesIn ?x Antarctica).
  2. Detectioncoined reports every functor in a proposal the KB has never seen, with its arity and naming role, plus a reuse-versus-coin count over the proposal's literals. It reports, never rejects: a reviewer decides, because a genuinely new type is how an ontology grows.

Selection is by relevance, bounded by tokens. Nothing enumerates the KB: the inventory is seeded from the page's term, walks its genl neighbourhood (supertypes nearest first, subtypes, siblings), and takes the predicates that neighbourhood licenses — the argIsa-declared ones whose argument types the term satisfies, plus the ones already used in facts with those terms. Every read is pinned by a term (a cached closure lookup, an argIsa query on a fixed argument, a bounded argument-root walk), so the cost tracks the neighbourhood rather than the knowledge base.

The KB's own vocabulary, put in front of the model — and the flag for when the model
invents vocabulary anyway.

**Measured failure mode.**  Asked to write type-level common sense about a term, every
local model that writes usable s-expressions at all encodes structure into the
*predicate name* rather than into arguments:

    (implies (penguin ?x) (lives_in_antarctica ?x))
    (implies (penguin ?x) (capable_of_swimming ?x))
    (implies (penguin ?x) (thermoregulates_via_blubber_and_feathers ?x))

Each is admissible, novel, and useless: a one-off unary predicate joins no rule and
matches no other sentence.  `(livesIn ?x Antarctica)` is the same claim in vocabulary
the KB can reason with.  Swapping models does not fix it — the strongest model
produces the *most* of it, because it produces the most output.

**The check chain cannot catch it.**  A unary snake_case functor is a well-formed type
name by the naming invariants, so `(has_black_and_white_feathers ?x)` passes naming,
groundness, well-formedness, argIsa, disjointness and functionality alike — and it
should, since coining a type is a legitimate thing to do.  So there are exactly two
guards, and they are both here:

1. **Prevention** — `inventory` / `render` put the *relevant available* vocabulary in
   the prompt with each predicate's arity and argument types, and the prompt tells the
   model to reuse it.  A model that can see `livesIn` with `args 1:animal 2:place`
   writes `(livesIn ?x Antarctica)`.
2. **Detection** — `coined` reports every functor in a proposal the KB has never seen,
   with its arity and naming role, plus a reuse-versus-coin count over the proposal's
   literals.  It **reports, never rejects**: a reviewer decides, because a genuinely
   new type is how an ontology grows.

**Selection is by relevance, bounded by tokens.**  Nothing enumerates the KB: the
inventory is seeded from the page's term, walks its `genl` neighbourhood (supertypes
nearest first, subtypes, siblings), and takes the predicates that neighbourhood
*licenses* — the `argIsa`-declared ones whose argument types the term satisfies, plus
the ones already used in facts with those terms.  Every read is pinned by a term
(a cached closure lookup, an `argIsa` query on a fixed argument, a bounded argument-root
walk), so the cost tracks the neighbourhood rather than the knowledge base.
raw docstring

coinedclj

(coined kb batch)

The vocabulary an edit batch introduces, as {:coined [{:predicate :arity :role :in :index} …] :vocabulary {…}}.

One entry per literal whose functor the KB has never seen, in batch order:

:predicate the functor :arity how many arguments the literal gave it — the reviewer's first question, since (capable_of_swimming ?x) and (capableOf ?x Swimming) differ in exactly that :role :type (snake_case, a one-place property) or :predicate (camelCase), by the naming invariants — a coined type and a coined relation are different judgements :in :index where in the batch it came from, so a caller can point at the entry

:vocabulary counts the whole proposal: {:literals :reused :coined :coined-types :coined-relations} — which says at a glance whether the proposal is conservative (all reuse) or inventive (mostly new names), the one number a reviewer wants before reading anything.

Nothing here rejects. :remove entries are ignored: retracting cannot coin.

The vocabulary an `edit` batch introduces, as
`{:coined [{:predicate :arity :role :in :index} …] :vocabulary {…}}`.

One entry per *literal* whose functor the KB has never seen, in batch order:

  :predicate  the functor
  :arity      how many arguments the literal gave it — the reviewer's first question,
              since `(capable_of_swimming ?x)` and `(capableOf ?x Swimming)` differ in
              exactly that
  :role       `:type` (snake_case, a one-place property) or `:predicate` (camelCase),
              by the naming invariants — a coined *type* and a coined *relation* are
              different judgements
  :in :index  where in the batch it came from, so a caller can point at the entry

`:vocabulary` counts the whole proposal:
`{:literals :reused :coined :coined-types :coined-relations}` — which says at a glance
whether the proposal is conservative (all reuse) or inventive (mostly new names), the
one number a reviewer wants before reading anything.

Nothing here rejects.  `:remove` entries are ignored: retracting cannot coin.
raw docstring

declared-aritiesclj

(declared-arities kb)

{predicate -> arity} for every predicate the KB declares — its (arity P N) sentexes and its unaryPredicate / binaryPredicate / ternaryPredicate memberships, which derive each other, so either spelling is enough.

Arity is never inferred from argIsa. argIsa constrains an argument to a type and is deliberately partial: a second argument may be unconstrained by design (you may likes anything) or not a type at all (a year, an integer). Measured on the shipped schema, the highest argIsa position disagrees with the declared arity for 8 of the 42 constrained relations — likes, birthYearOf and arity all read as unary — so an inventory built that way would print likes/1 and cause the arity errors it exists to prevent. A predicate the KB never declared gets no arity rather than a guessed one.

Cost is the size of the vocabulary: four extent reads, none of them over facts.

`{predicate -> arity}` for every predicate the KB **declares** — its `(arity P N)`
sentexes and its `unaryPredicate` / `binaryPredicate` / `ternaryPredicate` memberships,
which derive each other, so either spelling is enough.

**Arity is never inferred from `argIsa`.**  `argIsa` constrains an argument to a *type*
and is deliberately partial: a second argument may be unconstrained by design (you may
`likes` anything) or not a type at all (a year, an integer).  Measured on the shipped
schema, the highest `argIsa` position disagrees with the declared arity for 8 of the 42
constrained relations — `likes`, `birthYearOf` and `arity` all read as unary — so an
inventory built that way would print `likes/1` and *cause* the arity errors it exists to
prevent.  A predicate the KB never declared gets no arity rather than a guessed one.

Cost is the size of the **vocabulary**: four extent reads, none of them over facts.
raw docstring

head-contextclj

The spindle head — the context the engine's own vocabulary is documented in. A term the head documents is structural: it is part of how you say a thing, not part of what there is to say.

The spindle head — the context the engine's own vocabulary is documented in.  A term the
head documents is **structural**: it is part of how you say a thing, not part of what
there is to say.
raw docstring

inventoryclj

(inventory kb term)
(inventory kb
           term
           {:keys [max-relations max-types max-scan]
            :or {max-relations 120 max-types 80 max-scan 200}
            :as opts})

The vocabulary a page about term should be written in, as data:

{:term       the page's term
 :seed       what `seed-types` found — the genl neighbourhood
 :types      [{:type :parent :doc} …]                    the type names to reuse
 :relations  [{:predicate :arity :args :props :inverse :doc :tier} …]  domain vocabulary
 :structural [{:predicate :arity :args :doc} …]           how to state a type-level claim
 :dropped    {:relations n :types n}}

Two blocks, because they are two different things. The domain relations are what the reader's knowledge is made of; the structural handful is how a claim about a kind is stated (genl, disjoint, comment, argIsa). A term the vocabulary head documents is structural and is left out of the domain block unless the page is about that band.

The source is the KB's declarations, not its facts. Types come from vaelii.core/types and relations from the unaryPredicate / binaryPredicate / ternaryPredicate memberships, because a schema-only KB has no facts: enumerating functors that actually appear in fact position on the shipped schema yields 20 names, every one of them an engine meta-predicate and not one of them a domain relation. argIsa then supplies argument types for the 42 relations that declare them.

Ordering is by relevance tier — predicates already used in facts with the page's term, then those an argIsa licenses for the term itself, then for each supertype in turn (nearest first), then subtypes and siblings, then everything else declared, alphabetically. On a vocabulary the size of the shipped schema everything fits and the order is cosmetic; ordering is what makes the cut meaningful when it does not.

opts: :max-relations (120), :max-types (80), :max-scan (200 facts per argument position), plus seed-types' bounds. The reads are all vocabulary-sized — four extent reads for the arities, the taxonomy's own node set, one pinned comment read — plus a per-rendered-predicate argIsa query, so the cost tracks the vocabulary and never the number of facts.

The vocabulary a page about `term` should be written in, as data:

    {:term       the page's term
     :seed       what `seed-types` found — the genl neighbourhood
     :types      [{:type :parent :doc} …]                    the type names to reuse
     :relations  [{:predicate :arity :args :props :inverse :doc :tier} …]  domain vocabulary
     :structural [{:predicate :arity :args :doc} …]           how to state a type-level claim
     :dropped    {:relations n :types n}}

**Two blocks, because they are two different things.**  The domain relations are what the
reader's knowledge is *made of*; the structural handful is how a claim about a kind is
*stated* (`genl`, `disjoint`, `comment`, `argIsa`).  A term the vocabulary head documents
is structural and is left out of the domain block unless the page is about that band.

**The source is the KB's declarations, not its facts.**  Types come from `vaelii.core/types`
and relations from the `unaryPredicate` / `binaryPredicate` / `ternaryPredicate`
memberships, because a schema-only KB has no facts: enumerating functors that actually
appear in fact position on the shipped schema yields 20 names, every one of them an engine
meta-predicate and not one of them a domain relation.  `argIsa` then supplies argument
*types* for the 42 relations that declare them.

Ordering is by **relevance tier** — predicates already used in facts with the page's term,
then those an `argIsa` licenses for the term itself, then for each supertype in turn
(nearest first), then subtypes and siblings, then everything else declared, alphabetically.
On a vocabulary the size of the shipped schema everything fits and the order is cosmetic;
ordering is what makes the *cut* meaningful when it does not.

`opts`: `:max-relations` (120), `:max-types` (80), `:max-scan` (200 facts per argument
position), plus `seed-types`' bounds.  The reads are all vocabulary-sized — four extent
reads for the arities, the taxonomy's own node set, one pinned `comment` read — plus a
per-rendered-predicate `argIsa` query, so the cost tracks the vocabulary and never the
number of facts.
raw docstring

literalsclj

(literals sentence)

Every literal in a sentence as [{:functor f :arity n} …], at any nesting depth and in reading order, with the structural frame dropped.

A rule contributes its antecedents and its consequent; a nested compound argument contributes itself, since a functor in argument position is vocabulary too. Duplicates are kept — the count of literals is what a reuse-versus-coin ratio is taken over.

Every literal in a sentence as `[{:functor f :arity n} …]`, at any nesting depth and
in reading order, with the structural frame dropped.

A rule contributes its antecedents and its consequent; a nested compound argument
contributes itself, since a functor in argument position is vocabulary too.  Duplicates
are kept — the count of literals is what a reuse-versus-coin ratio is taken over.
raw docstring

neighbourhood-typesclj

(neighbourhood-types seed)

The seed's types as one relevance-ordered vector — the term first, then supertypes, its own subtypes, its siblings. This is the order predicates inherit their relevance from, so a predicate licensed by the term itself outranks one licensed by thing.

The seed's types as one relevance-ordered vector — the term first, then supertypes,
its own subtypes, its siblings.  This is the order predicates inherit their relevance
from, so a predicate licensed by the term itself outranks one licensed by `thing`.
raw docstring

predicate-lineclj

(predicate-line {:keys [predicate props inverse doc] :as shape} max-doc-chars)

One predicate as a card line — its signature, its algebraic metadata, its inverse, and its own documentation:

- `eats`/2 : `animal` × `food` — (eats ?animal ?food) means that ?animal takes …
- `locatedIn`/2 : `physical_object` × `physical_object` [transitive] — …
One predicate as a card line — its signature, its algebraic metadata, its inverse, and
its own documentation:

    - `eats`/2 : `animal` × `food` — (eats ?animal ?food) means that ?animal takes …
    - `locatedIn`/2 : `physical_object` × `physical_object` [transitive] — …
raw docstring

predicate-shapeclj

(predicate-shape kb p arity)

One predicate's inventory entry: {:predicate :arity :args [[position type] …] :props […] :inverse :doc}.

:args is every argIsa constraint on it, sorted by position, and :arity comes from the KB's declarations — never from the constraints. Together they are the part that stops the model folding an argument into the predicate's name: seeing locatedIn/2 : physical_object × physical_object makes reuse the obvious move, where a bare locatedIn leaves the model guessing whether there is anywhere to put Antarctica.

One predicate's inventory entry:
`{:predicate :arity :args [[position type] …] :props […] :inverse :doc}`.

`:args` is every `argIsa` constraint on it, sorted by position, and `:arity` comes from
the KB's declarations — never from the constraints.  Together they are the part that stops
the model folding an argument into the predicate's name: seeing
`locatedIn/2 : physical_object × physical_object` makes reuse the obvious move, where a
bare `locatedIn` leaves the model guessing whether there is anywhere to put `Antarctica`.
raw docstring

predicate-type-aritiesclj

The predicate meta-ontology's arity declarations: the type a predicate is a member of, and the arity that membership states.

The predicate meta-ontology's arity declarations: the type a predicate is a member of,
and the arity that membership states.
raw docstring

renderclj

(render inv)
(render {:keys [relations types structural dropped]}
        {:keys [max-tokens max-doc-chars] :or {max-doc-chars 140}})

The inventory as the prompt's vocabulary section, in markdown — three blocks: the domain relations to reuse, the type names to reuse, and the small structural set a claim about a kind is stated with.

The relation block is headed by the instruction that makes it load-bearing, since a card the model reads as background buys nothing. opts: :max-tokens (nil = no cap), :max-doc-chars (140). Where a cap or a count bound cut a block, the number left out is stated rather than hidden.

The inventory as the prompt's vocabulary section, in markdown — **three blocks**: the
domain relations to reuse, the type names to reuse, and the small structural set a claim
about a kind is stated with.

The relation block is headed by the instruction that makes it load-bearing, since a card
the model reads as background buys nothing.  `opts`: `:max-tokens` (nil = no cap),
`:max-doc-chars` (140).  Where a cap or a count bound cut a block, the number left out is
stated rather than hidden.
raw docstring

seed-typesclj

(seed-types kb term)
(seed-types kb
            term
            {:keys [max-genls max-specs max-siblings]
             :or {max-genls 8 max-specs 12 max-siblings 12}})

The types the inventory is drawn around, most specific first, for a page about term — read off the term's own role:

a type the type itself, its supertypes, its subtypes, and its siblings (the other subtypes of its nearest supertype) an individual the types it is asserted to have, and their supertypes a predicate the types its argIsa constraints name, so what it can be said of is on the card

opts: :max-genls (8), :max-specs (12), :max-siblings (12). Each closure is a cached taxonomy read, so the whole walk is a handful of set lookups.

The types the inventory is drawn around, most specific first, for a page about
`term` — read off the term's own role:

  a type        the type itself, its supertypes, its subtypes, and its siblings
                (the other subtypes of its nearest supertype)
  an individual the types it is asserted to have, and their supertypes
  a predicate   the types its `argIsa` constraints name, so what it can be said *of*
                is on the card

`opts`: `:max-genls` (8), `:max-specs` (12), `:max-siblings` (12).  Each closure is a
cached taxonomy read, so the whole walk is a handful of set lookups.
raw docstring

signatureclj

(signature {:keys [arity args]})

A predicate's shape as a signature — 2 :animal×food``, with ? for a position the KB constrains to no type, the bare arity when it constrains none of them, and nothing at all when it never declared the arity. Reading locatedIn/2 : physical_object × physical_object is what makes reuse the obvious move.

A predicate's shape as a signature — `2 : `animal` × `food``, with `?` for a position the
KB constrains to no type, the bare arity when it constrains none of them, and nothing at
all when it never declared the arity.  Reading
`locatedIn/2 : physical_object × physical_object` is what makes reuse the obvious move.
raw docstring

specificityclj

(specificity kb t)

How specific a type is, as the size of its own genl up-closure: penguin (5) sits under bird (4) under animal (3). Sorting descending puts the nearest, most specific type first — the order relevance runs in — and the name breaks ties, so the order is a function of the taxonomy and never of arrival.

Public because vaelii.impl.llm.text builds a card from a document's resolved terms rather than from one page term, and a second copy of this ordering would drift.

How specific a type is, as the size of its own `genl` up-closure: `penguin` (5) sits
under `bird` (4) under `animal` (3).  Sorting **descending** puts the nearest, most
specific type first — the order relevance runs in — and the name breaks ties, so the
order is a function of the taxonomy and never of arrival.

Public because `vaelii.impl.llm.text` builds a card from a document's resolved terms
rather than from one page term, and a second copy of this ordering would drift.
raw docstring

structural-functor?clj

(structural-functor? f)

Is f frame rather than vocabulary?

Is `f` frame rather than vocabulary?
raw docstring

structural-functorsclj

The functors that are frame, not vocabulary: the connectives that canonicalize into the sentex record and the meta-forms the engine interprets. A proposal using implies is not coining a predicate, so these are never reported — and they are named here rather than read from the KB so the flag works on a KB with no vocabulary loaded at all. A namespaced functor (set/defaultRule, do/labeling) is excluded the same way, by its namespace.

The functors that are **frame, not vocabulary**: the connectives that canonicalize
into the sentex record and the meta-forms the engine interprets.  A proposal using
`implies` is not coining a predicate, so these are never reported — and they are named
here rather than read from the KB so the flag works on a KB with no vocabulary loaded
at all.  A namespaced functor (`set/defaultRule`, `do/labeling`) is excluded the same
way, by its namespace.
raw docstring

structural-predicatesclj

The structural predicates a type-level claim actually needs, and the only ones offered — in the order they matter, so a token cap cuts the least useful first.

The head documents far more than this — the evaluables, the quantity apparatus (conversionFactor, quantityGreaterThanOrEqual, termOfUnit), the context plumbing. Handing a model asked about penguins the whole vocabulary head is irrelevant at best and an invitation to misuse something it half-recognizes at worst, so the structural block is an allowlist rather than a dump. The connectives (implies, and, not, exceptWhen) are not here because they are frame: the prompt states the sentence shapes directly.

The structural predicates a type-level claim actually needs, and the only ones offered —
**in the order they matter**, so a token cap cuts the least useful first.

The head documents far more than this — the evaluables, the quantity apparatus
(`conversionFactor`, `quantityGreaterThanOrEqual`, `termOfUnit`), the context plumbing.
Handing a model asked about penguins the whole vocabulary head is irrelevant at best and an
invitation to misuse something it half-recognizes at worst, so the structural block is an
allowlist rather than a dump.  The connectives (`implies`, `and`, `not`, `exceptWhen`) are
not here because they are *frame*: the prompt states the sentence shapes directly.
raw docstring

structural-termsclj

(structural-terms kb)

Every term the vocabulary head documents, from its comment sentexes in that context — one pinned read, and the exact answer, since a term's documentation is stored where the term is defined.

Every term the vocabulary head documents, from its `comment` sentexes in that context —
one pinned read, and the exact answer, since a term's documentation is stored where the
term is defined.
raw docstring

term-kindclj

(term-kind kb term)

How a page about term should treat it — :type, :predicate, :individual, :context, or nil.

The naming invariants leave one genuine ambiguity, and say so: a single lowercase word (dog, flies) satisfies both the predicate and the type spelling, and role is decided by position and arity rather than by the symbol. So the KB decides — a term with genl edges is a type, one with argIsa constraints is a relation, and a bare unary is a type. Which way it goes decides what the inventory is drawn from, so guessing off the spelling alone would give a page about dog the vocabulary of a relation.

How a page about `term` should treat it — `:type`, `:predicate`, `:individual`,
`:context`, or nil.

The naming invariants leave one genuine ambiguity, and say so: a single lowercase word
(`dog`, `flies`) satisfies both the predicate and the type spelling, and role is decided
by position and arity rather than by the symbol.  So the **KB** decides — a term with
`genl` edges is a type, one with `argIsa` constraints is a relation, and a bare unary is
a type.  Which way it goes decides what the inventory is drawn from, so guessing off the
spelling alone would give a page about `dog` the vocabulary of a relation.
raw docstring

type-lineclj

(type-line {:keys [type parent doc]} max-doc-chars)

One type as a card line: its nearest supertype, and what it means. Every type in the block carries its parent, so the block is the hierarchy — no separate subtype list is needed to read it.

One type as a card line: its nearest supertype, and what it means.  Every type in the
block carries its parent, so the block *is* the hierarchy — no separate subtype list is
needed to read it.
raw docstring

unknown-termsclj

(unknown-terms kb candidates)

The candidates the KB has never seen — no fact, no rule, no declaration, at any nesting depth.

The O(1) roots answer first; only what they cannot resolve falls through to the term roster (vaelii.core/terms), which is the exact answer and is read once for the whole candidate set. So a proposal that reuses vocabulary throughout costs a handful of set-size reads, and the roster pass is paid only when something really is new — which is the case worth paying for.

The `candidates` the KB has **never seen** — no fact, no rule, no declaration, at any
nesting depth.

The O(1) roots answer first; only what they cannot resolve falls through to the term
roster (`vaelii.core/terms`), which is the exact answer and is read **once** for the
whole candidate set.  So a proposal that reuses vocabulary throughout costs a handful
of set-size reads, and the roster pass is paid only when something really is new —
which is the case worth paying for.
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