an.semantic#

The semantic layer: one versioned vocabulary registry, methods, aspects and the matcher.

ADR 0003 (the structured ↔ semantic spectrum) and ADR 0002 (capability-based applicability). Defined once, in the core, for every genre (design principle 3):

  • the vocabulary — every name a document may spell, as an Entry (id, version, kind, title, description, params as JSON Schema with defaults, examples, requires, accepted levels, expand): action and entity kinds, easings, camera moves and IR fields from the core (an.semantic.seeds); motion and expression presets, methods and aspects from the genres (an.genres.Genre’s vocabulary and aspects);

  • methods and aspects — a Method is an entry of kind method realising an Aspect, whose default chain ends in a method that requires nothing or in the recorded NOOP;

  • the queries — applicable(), why_not() and resolve() (with a Policy), all calls to the one matcher an.capabilities.missing();

  • the generated surfaces — the an iterate prompt (an.semantic.prompt), the skill’s vocabulary section (an.semantic.docs), the MCP surface (an.mcp), and the shot’s vocabulary digest for the compile key (an.semantic.digest).

Nothing here imports an.ir at module level, and nothing below the IR ever calls an LLM.

>>> from an.semantic import entry, vocabulary
>>> entry("action.tween").kind
'action'
>>> any(e["id"] == "easing.ease_in_out" for e in vocabulary())
True

Functions

applicable(aspect_name, subjects)

The methods of aspect_name that apply to subjects, the default chain's order first.

aspect(name)

The registered aspect name; UnknownEntryError otherwise.

aspect_names(*[, owner])

aspects()

check_registry(*[, owner, capabilities])

The problems with the registered aspects and methods (empty: sound).

entries(*[, kind, owner])

Every entry (registered and viewed), filtered by kind/owner, in a stable order.

entry(entry_id)

The entry with this id; UnknownEntryError naming the known ids otherwise.

lookup(kind, name, *[, aspect])

The entry of kind a document spells name (for methods, within aspect).

methods_of(aspect_name)

Every registered method of an aspect: its chain first, in order, then the rest.

owner_of(entry_id)

register_aspect(a, *[, owner, replace])

Register an aspect and its default chain.

register_entry(e, *[, owner, replace])

Register a vocabulary entry under owner: ONE entry per id, and per spelling.

resolve(aspect_name, subjects[, requested, ...])

Choose the method that realises aspect_name on subjects.

vocabulary(*[, kind, owner])

Every entry as data (what the MCP surface returns), filtered by kind/owner.

why_not(method, subjects)

What method is missing on subjects, each with its remedy (empty: it applies).

Classes

Aspect(name, chain[, applies_to, ...])

Something every asset of a kind gets (locomotion, speech, blink, …).

Choice(method[, args, version])

One method choice in a policy or a request: previz's FormulaCall plus a pin.

Entry(id, kind[, version, name, title, ...])

One vocabulary entry: a named, versioned, described, parametrised recipe.

Method(id[, kind, version, name, title, ...])

A way of realising an aspect (ADR 0002): an entry of kind method.

Missing(term, remedy)

One unmet requirement term and what would meet it.

Policy([order])

Per-aspect method orders set by a style (study_the_masters §4).

Resolution(aspect, method[, args, source, ...])

What resolve() chose: the method, its args, where the choice came from.

Exceptions

UnknownEntryError

A name or id is not in the vocabulary; the message lists what is.

VocabularyError

A vocabulary entry, aspect or policy is malformed or collides with a registered one.

class an.semantic.Aspect(name, chain, applies_to=frozenset({}), description='', declared_by='', records_fallback=False)[source]#

Bases: object

Something every asset of a kind gets (locomotion, speech, blink, …).

chain is the default order of method ids, first applicable wins; its last link requires nothing, or is NOOP. applies_to is the entity kinds it makes sense for (empty: all); for any other kind the aspect resolves to the recorded no-op.

declared_by: str = ''#

The asset-document field an asset declares its request in (a character’s gait, speech): a declared method choice, reported as such by describe_asset and honoured by the compiler.

records_fallback: bool = False#

Record falling down the chain even when nothing was requested (reason missing, so --strict-assets sees it). For an aspect whose fallback is a behaviour the asset never had before — speech’s pulse on a baked face — rather than today’s long-standing default (a legless walk).

class an.semantic.Choice(method, args=<factory>, version=None)[source]#

Bases: object

One method choice in a policy or a request: previz’s FormulaCall plus a pin.

>>> Choice.of({"method": "speech.mouth_flap", "args": {"shapes": 3}}).args
{'shapes': 3}
>>> Choice.of("loco.rock").method
'loco.rock'
class an.semantic.Entry(id, kind, version='1', name='', title='', description='', usage='', params=<factory>, examples=(), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), expand=None)[source]#

Bases: object

One vocabulary entry: a named, versioned, described, parametrised recipe.

  • id is the persisted identifier (dotted: motion.walk, loco.legged_cycle); name is how a document spells it (walk; defaults to id), looked up per kind.

  • version changes whenever the meaning changes for the same params; the versions of the entries a shot uses fold into its compile key.

  • params is a JSON Schema object; properties.*.default are the defaults ({} when the entry takes none).

  • aspects names the aspects that using this entry resolves (a walk resolves locomotion), so the digest of a shot can include the methods it may resolve to.

  • usage is the longer note the generated surfaces print under the one-sentence description (how a document spells it, what bites).

defaults()[source]#

The params’ defaults (properties.*.default).

Return type:

dict[str, Any]

classmethod from_json(data, *, expand=None)[source]#

An entry from its JSON form (what to_json() writes, or another package exports — previz’s formulas): the explicit loader, a method kind giving a Method.

Return type:

Entry

>>> e = Entry("camera.demo", "camera_move", name="demo", description="d")
>>> Entry.from_json(e.to_json()) == e
True
property term: str#

How a document spells this entry.

to_json()[source]#

The entry as data (no expand): what the MCP surface and the docs list.

Return type:

dict[str, Any]

class an.semantic.Method(id, kind='method', version='1', name='', title='', description='', usage='', params=<factory>, examples=(), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), expand=None, aspect='', remedies=<factory>)[source]#

Bases: Entry

A way of realising an aspect (ADR 0002): an entry of kind method.

remedies overrides a capability’s own remedy per requirement term (a method can say “split the legs into two slots with hip pivots” where the capability only says “add legs”).

>>> m = Method("loco.rock", aspect="locomotion", name="rock")
>>> m.kind, m.aspect, m.requirement_free
('method', 'locomotion', True)
property requirement_free: bool#

Whether the method applies to anything (a legal last link of a chain).

to_json()[source]#

The entry as data (no expand): what the MCP surface and the docs list.

Return type:

dict[str, Any]

class an.semantic.Missing(term, remedy)[source]#

Bases: object

One unmet requirement term and what would meet it.

class an.semantic.Policy(order=<factory>)[source]#

Bases: object

Per-aspect method orders set by a style (study_the_masters §4).

The parsed form of a style document’s policy: block; precedence when resolving is the author’s request, then the shot, then the style, then the aspect’s chain (layered()).

>>> p = Policy.of({"locomotion": ["loco.bob", {"method": "loco.glide", "args": {"bob": 0}}]})
>>> [c.method for c in p.choices("locomotion")], p.choices("speech")
(['loco.bob', 'loco.glide'], ())
static layered(*policies)[source]#

Policies in precedence order (shot before style): the first that names an aspect wins it.

Return type:

Policy

class an.semantic.Resolution(aspect, method, args=<factory>, source='chain', substitution=None, considered=(), skipped=(), choice=None)[source]#

Bases: object

What resolve() chose: the method, its args, where the choice came from.

source is request, policy, chain or noop; substitution is the record when the choice departs from what was asked (None when it did not); considered is the trail of methods tried before it, each with what it was missing; skipped the policy’s entries among them (a policy is an order: an entry that does not apply is passed over, information for whoever records the choice, never a substitution); choice the request or policy entry that was chosen, as written (its own args, without the method’s defaults), None for a chain link.

exception an.semantic.UnknownEntryError[source]#

Bases: VocabularyError, KeyError

A name or id is not in the vocabulary; the message lists what is.

exception an.semantic.VocabularyError[source]#

Bases: ValueError

A vocabulary entry, aspect or policy is malformed or collides with a registered one.

an.semantic.applicable(aspect_name, subjects)[source]#

The methods of aspect_name that apply to subjects, the default chain’s order first.

Return type:

tuple[Method, ...]

an.semantic.aspect(name)[source]#

The registered aspect name; UnknownEntryError otherwise.

Return type:

Aspect

an.semantic.check_registry(*, owner=None, capabilities=True)[source]#

The problems with the registered aspects and methods (empty: sound).

ADR 0002 decision 5 as a check: every aspect’s chain names methods of that aspect, and its last link requires nothing or is the recorded NOOP; one spelling is one entry; and (capabilities) every requirement names a registered capability. owner limits it to one genre’s aspects and entries. an.genres.register_genre() runs the chain checks per genre; an.genres.load() runs the capability check once every genre is in, so a genre needing another’s capability does not depend on load order.

Return type:

list[str]

an.semantic.entries(*, kind=None, owner=None)[source]#

Every entry (registered and viewed), filtered by kind/owner, in a stable order.

Return type:

tuple[Entry, ...]

an.semantic.entry(entry_id)[source]#

The entry with this id; UnknownEntryError naming the known ids otherwise.

Return type:

Entry

an.semantic.lookup(kind, name, *, aspect=None)[source]#

The entry of kind a document spells name (for methods, within aspect).

Return type:

Entry | None

an.semantic.methods_of(aspect_name)[source]#

Every registered method of an aspect: its chain first, in order, then the rest.

Return type:

tuple[Method, ...]

an.semantic.register_aspect(a, *, owner='an', replace=False)[source]#

Register an aspect and its default chain. Checked as a whole by an.semantic.check_registry() (a chain may name methods registered after it, within the same genre).

Return type:

Aspect

an.semantic.register_entry(e, *, owner='an', replace=False)[source]#

Register a vocabulary entry under owner: ONE entry per id, and per spelling.

A name means one thing everywhere (push_in is one entry, an#257): a second entry under a taken id — registered or provided by a view — or under a taken (kind, name) (within an aspect, for methods) raises, unless replace=True is passed explicitly, and then the replacement is recorded (replacements()). Re-registering the same entry is a no-op.

Return type:

Entry

>>> from an.semantic.entries import Entry
>>> register_entry(Entry("demo.push_in", "camera_move", name="push_in"), owner="demo")
Traceback (most recent call last):
...
an.semantic.entries.VocabularyError: camera_move 'push_in' is already defined by 'camera.push_in' (owner 'an'); ...
an.semantic.resolve(aspect_name, subjects, requested=None, *, declared=None, policy=None, entity='', entity_kind=None)[source]#

Choose the method that realises aspect_name on subjects.

requested is the author’s explicit choice (a method id, its spelling in the aspect — "hem" for locomotion —, or {method, args, version}); declared the asset’s own declaration for the aspect (the field Aspect.declared_by names), placed by DECLARED_OUTRANKS_POLICY (the request when nothing was requested, or the policy’s last entry); policy the layered shot/style policy; entity_kind the asset’s kind, checked against the aspect’s applies_to. Never raises for a method that does not apply: it falls back and records why.

Return type:

Resolution

an.semantic.vocabulary(*, kind=None, owner=None)[source]#

Every entry as data (what the MCP surface returns), filtered by kind/owner.

Return type:

list[dict[str, Any]]

an.semantic.why_not(method, subjects)[source]#

What method is missing on subjects, each with its remedy (empty: it applies).

Return type:

tuple[Missing, ...]

Modules

describe

Describe an asset: what it affords, and per aspect what applies and what is missing.

digest

Which vocabulary entries a shot names, at which versions: the shot's vocabulary digest.

docs

The vocabulary section of the downstream an skill, generated from the registry.

export

The vocabulary as a published, code-free contract file: an/data/timing/vocabulary.json.

matcher

The three queries and the policy (ADR 0002 decision 4): applicable, why_not, resolve.

prompt

The an iterate system prompt, generated from the vocabulary registry.

registry

The one vocabulary registry (ADR 0003 decision 6): entries and aspects, by owner.

seeds

The core's vocabulary: views over the kind and easing registries, camera moves, IR fields.

views

View spaces: what a camera move moves through, defined once for every engine (an#257).