Weksa/docs/interlingua-standard.md

327 lines
13 KiB
Markdown

# Interlingua Standard
Version: `0.2-draft`
The Weksa interlingua is the language-neutral meaning contract between clients,
setting surfaces, authoring tools, language projects, ontology projection, and
renderers.
It is not English. It is not a grammar tree. It is not a target-language
ontology. It is the smallest structured packet that says what is meant, who is
involved, what the context is, and which facts are stable enough for a language
project to reinterpret.
## Objective
Give every Weksa client one shared way to submit meaning without forcing all
settings through one worldview.
The interlingua should support:
- game telemetry and commentary intents
- naming briefs for places, species, institutions, artifacts, and ecological
structures
- dialogue and narration authoring
- corpus examples
- model-assisted semantic decomposition
- deterministic runtime rendering
- traceable ontology projection
## Prior Art
Weksa should learn from existing semantic annotation work without becoming a
research format clone.
- AMR gives a useful model for abstract graph meaning that is not a surface parse.
- UMR extends that direction with cross-linguistic and document-level concerns
such as coreference, temporal relations, modal dependencies, aspect, and
multilingual diversity.
- FrameNet keeps scenes and roles central.
- Universal Dependencies shows a useful governance pattern: common categories
with language-specific extensions when needed.
Weksa's difference is purpose. It is a rendering contract for constructed
languages and alien ontologies, not a general-purpose annotation benchmark.
## Ownership
The interlingua owns:
- referents
- events, states, and relations
- roles between referents and predicates
- time, aspect, modality, evidence, and polarity when known
- discourse function and speaker intent
- context needed for rendering
- provenance and confidence
- extension slots with explicit namespaces
The interlingua does not own:
- target-language concept splits or collapses
- grammar obligations
- morphology
- phonology
- diachronic history
- final wording
- culture-specific metaphors unless the source meaning explicitly contains them
Those belong downstream.
## Design Principles
- Meaning before words.
- Graph before sentence.
- Explicit uncertainty beats fake precision.
- Use common fields for common meaning.
- Use namespaced extensions for client or setting pressure.
- Extensions are evidence, not authority, until a language project consumes them.
- Runtime packets must be deterministic and serializable.
- The renderer must be able to trace every target-language decision back to a
packet field, ontology rule, or language-project rule.
## Packet Shape
The Rust types in [`crates/weksa/src/packet.rs`](../crates/weksa/src/packet.rs)
are the definition; the JSON Schemas in [`schemas/cultnet/`](../schemas/cultnet/)
are generated from them and a test fails when they drift. A packet is the
CultCache document `weksa.interlingua_packet`. This section describes the shape;
where it and the types disagree, the types win.
Two layers carry the standard. The **schema** is the Rust types: the packet
families, node ids, slots and value kinds. It changes by commit and rarely. The
**inventory** is data: the concepts, frames, roles, relation types, speech acts
and features that the schema's slots name. It is stored as
`weksa.inventory_entry` documents under appended `weksa.inventory_revision`
records, never edited in place (a changed meaning is a retire plus an add), and
a retired id still resolves for packets of earlier revisions.
Every id is an opaque form type, parsed when it is built and when a store
reads it: an inventory id is `<family>:<n>` with `n` a canonical decimal of at
least 1 (`role:1`; `role:01` is not an id), a node id is `n<k>` with `k` in
1..=9999, a packet id is `packet:<16 hex>` and an encoder id is
`encoder:<16 hex>`, an entity id is `entity:<slug>` (`[a-z0-9]` and inner
`-`, 1 to 48 bytes) and a corpus item id is `item:<slug>:<n>`. The only free
text a packet holds is the `trace` (`notes` and `rejected_readings`), which the
validator neither bounds nor reads and which is stripped before lowering: a
referent names its entity by id, and the entity store (`weksa.entity`) holds
the entity's forms per language. The packet store refuses a referent whose
entity the store lacks, a packet whose `corpus_item` the corpus lacks, and one
whose entity is neither named on that item nor its speaker or a prior-turn
speaker (a packet without a corpus item has no door in this store):
an id-set check, not a text rule. Leakage of scored text is owned outside the
validator, by the loop's context builder (what each model call sees) and the
scorer; the validator owns form and capacity. The trace is stripped before
lowering. Concept ids are opaque and carry a gloss in the inventory; concepts
are open-class and grow, while the other families are closed-class.
```text
Packet
id, schema_version, inventory_rev, kind
provenance { source, corpus_item, encoder, confidence }
context { setting, medium, participants, audience }
discourse { speech_act, addressee, features }
referents [ { id, kind, concept, entity, features } ]
predications [ { id, concept, frame, roles: [ { role, filler } ], features } ]
relations [ { relation, from, to, features } ]
constraints { preserve, deterministic, max_latency_ms }
extensions [ { key: InvId (family extension), value: Grade | Option | Flag } ]
trace { notes, rejected_readings }
stance { features }
```
`schema_version` and `inventory_rev` name the exact schema and inventory
revision a packet was written against. A grade is an integer permille,
`0..=1000`, and the published schema says so. There are no floats.
### Validation
A packet exists only if the validator's rules pass and the packet store admits
it. `validate` judges and returns `Ok(())` or the refusals; the packet store's
`admit` is the one door and the only source of the lowering view. A refusal
names the rule.
- V1: `schema_version` is the version these types define.
- V2: at least one of `referents`, `predications`, `relations` is present.
- V3: a node id appears once across referents and predications.
- V4: every role filler, relation end, addressee, participant, audience member
and preserved node names a node.
- V5: every inventory id resolves at the packet's `inventory_rev`: introduced
at or before it, and not retired at or before it.
- V6: an inventory id is of the family its slot takes.
- V7: a feature or extension value fits its entry's value kind (a grade is a
grade, an option is one the entry declares, a flag is a flag).
- V8: a feature sits only in a slot its entry declares.
- V10: `inventory_rev` has a revision record.
- V11: a feature id appears at most once in one slot's feature list, an
extension id at most once per packet, a node id at most once in each of
`participants`, `audience` and `preserve`, and a role id at most once in one
predication's roles.
- V12: a packet has at most 64 referents, predications and relations together
(a calibration constant, capacity and not meaning).
V9 is retired and not renumbered: the form of a key or a grade is its type, and
an extension key's family is V6. `PacketStore::admit` returns the lowering view of a
packet it admits; the view carries the meaning and the stance, no packet id, no
provenance and no trace, and is built nowhere else. The store also refuses a
packet that names a withdrawn corpus item (packets admitted before the
withdrawal stay stored). A packet is admitted against the inventory the packet store
reads at the packet's revision, never one the caller supplies.
### `kind`
`Utterance`, `NameRequest`, `CommentaryIntent`, `Narration`, `DialogueLine`,
`CorpusExample`, `DebugProbe`.
### `provenance`
`source` is `Authored`, `ModelDecomposition`, `GameTelemetry` or
`ImportedCorpus`. `corpus_item` is the required `item:<slug>:<n>` key of the
corpus item the packet was minted for, `encoder` names what wrote it, and `confidence` is a grade.
### `context`
`setting` and `medium` are concept ids. `participants` and `audience` are node
ids.
### `discourse`
Why the packet is said: `speech_act` is a speech-act id, `addressee` a node id,
and `features` carry source-side nuance on content (hedge, certainty, irony and
whatever the inventory grows). This is source-side communicative pressure, not
target-language register.
### `stance`
The speaker's felt affect and how much of it the speaker holds back. `features`
is a feature list in slot `Stance`, checked like every other (V5 to V8, V11).
The seed carries felt affect as grades of how strongly it is felt (anger, fear, sadness,
joy, disgust, surprise, contempt, affection, shame) and `restraint`, a grade for
how much of the felt affect is suppressed (0 none, 1000 fully). The stance is
what is felt, never what is displayed: how a feeling shows, and how much of a
restrained one leaks, are cultural, and each language's ontology maps felt
affect and restraint to its own register. A held-back feeling is not a claim
about the world, so it is never a predication. An empty stance is valid. The
stance reaches the lowerer in the lowering view.
### `referents`
Entities, groups, places, abstractions and the rest. `kind` is a referent-kind
id (the inventory seeds `person`, `group`, `organism`, `place`, `artifact`,
`institution`, `ecology`, `route`, `memory`, `event`, `abstract`), `concept` is
a concept id, and `entity` is the id of the named entity when the referent has one.
### `predications`
Events, states, processes and scenes. `concept` and `frame` are ids; `roles`
pair a role id with a filler node (the inventory seeds `agent`, `patient`,
`experiencer`, `instrument`, `source`, `goal`, `location`, `beneficiary`). Tense,
aspect, polarity, modality and evidentiality are features with declared option
sets, not fields.
### `relations`
Edges not owned by one predication. `relation` is a relation id (the inventory
seeds `coreference`, `part_of`, `member_of`, `located_at`, `near`, `before`,
`after`, `causes`, `enables`, `prevents`, `owns`, `owes`, `knows`, `names`,
`represents`, `contrasts_with`), and `from` and `to` are node ids.
### `constraints`
`preserve` lists nodes the lowering must keep, `deterministic` and
`max_latency_ms` bound the renderer. Requirements about which outputs a
renderer must or must not produce belong to the target profile, not the packet.
### `extensions`
Setting or client data. An extension key is an inventory entry of family
`extension` (`extension:<n>`) whose entry declares the value kind, so a key is
an id and not a string. Extensions must not replace common fields when common
fields suffice, a language project may ignore them, and one that becomes
broadly required is promoted into the inventory or the schema.
### `trace`
Authoring and debug notes. The trace is stripped before lowering: the lowering
input type has no trace field.
## Minimal Examples
These examples illustrate meaning in the 0.1 shape, with free-text labels and
predicates. Those fields no longer exist; the examples are not valid packets.
### Name Request
```yaml
interlingua_version: 0.1-draft
packet_id: zyphos-name-001
kind: name_request
provenance:
source_type: authored
context:
setting: Eusocial Interbeing
referents:
- id: target
kind: ecology
label: disconnected tree network
- id: namer
kind: group
label: Airawa imperial surveyor
relations:
- type: names
from: namer
to: target
extensions:
zyphos.memory_relation: severed_from_saturated_substrate
zyphos.boundary_permeability: hidden_refusal
```
### Commentary Intent
```yaml
interlingua_version: 0.1-draft
packet_id: rtd-commentary-001
kind: commentary_intent
context:
setting: Rust to Dust
medium: broadcast
discourse:
speech_act: commentary
audience_function: spectacle
referents:
- id: actor
kind: person
- id: target
kind: person
predications:
- id: event
predicate: eliminate
frame: combat
roles:
agent: actor
patient: target
extensions:
rtd.aesthetic_reading:
form: raw
spectacle_value: high
```
## Promotion Rule
The interlingua should stay boring. When a new requirement appears:
1. Put it in a namespaced extension.
2. Use it in at least two real packets.
3. Ask which invariant it protects.
4. Promote it only if multiple clients or language projects need it.
No generic bucket earns permanence by sounding useful. That is how a schema
becomes a junk drawer with a hat.
## References
- [Abstract Meaning Representation 1.0 Specification](https://www.isi.edu/results/publications/14628/abstract-meaning-representation-amr-1-0-specification/)
- [Uniform Meaning Representation release paper](https://aclanthology.org/2024.lrec-main.229.pdf)
- [Global FrameNet](https://www.globalframenet.org/)
- [Universal Dependencies Guidelines](https://universaldependencies.org/guidelines)