Map: interlingua lowering model page, scoring, convergence and cut sequence

Prose map for campaign weksa-lowering, written by Imagination for the
Eureka-area Self (session self-2026-10-29-eureka).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Eureka 2026-10-29 10:10:21 +01:00
commit 2583fb91c3

View file

@ -0,0 +1,121 @@
# Map: interlingua lowering (campaign `weksa-lowering`)
Prose map for the campaign whose target is
[interlingua-lowering.md](interlingua-lowering.md). Rulings, questions,
follow-ups and cut specs live in the Eureka mind under `weksa-lowering:`; this
page is the model page and the cut sequence, and cites them by label. Written
by Imagination for the Eureka-area Self, 2026-10-29.
## The design in one paragraph
The interlingua spec has two layers. The **schema** is Rust types: packet
families, node ids, role slots, value kinds. It changes rarely and by commit.
The **inventory** is data in CultCache: the concepts, frames, roles, relation
types, speech acts and nuance features (hedge, certainty, warmth, irony and
whatever the loop finds missing) that the schema's slots name by id. Every
open slot in a packet is an inventory id, never free text, so a packet carries
no target-language wording (`no-surface-leak`). The golf loop improves the spec
mostly by growing and refining the inventory, which is fast; schema changes are
the slow path. Concepts are open-class and grow forever; the other families are
closed-class, and convergence is measured on them
(`weksa-lowering:ruling:convergence-test`). This split is
`weksa-lowering:ruling:spec-is-schema-plus-inventory`.
## Model page
Every persistent kind, what names it, its life, and who decides. "Derived"
means no store holds it and nothing writes it.
| Kind | What names it | Lifecycle | Who decides |
|---|---|---|---|
| Packet | Key `packet:<item-id>:r<inventory-rev>:<encoder-digest>`; CultCache type `weksa.interlingua_packet`, carrying `schema_version` and `inventory_rev` | Minted by an encoder for one corpus item under one spec version; immutable; a new spec version means a new packet, never an edit. Admitted only through the validator | The encoder writes; the validator refuses malformed packets at the door (`typed-packet`); nothing else writes packets |
| Schema version | `weksa.interlingua_packet.v<N>` constant in `crates/weksa`; published JSON Schema at `schemas/cultnet/` | Bumped by a commit that changes the Rust types; the published schema is regenerated in the same commit and a test fails on drift | A cut (Hands, under a spec); never the loop |
| Inventory entry | Key `<family>:<n>` (families: concept, frame, role, relation, speech_act, feature); type `weksa.inventory_entry`, with gloss, value kind, `introduced_rev`, optional `retired_rev` | Added or retired by an inventory revision; never edited in place (a changed meaning is a retire plus an add); a retired id still resolves for packets of earlier revisions | The loop's accepted rounds; Self on merge to main |
| Inventory revision | Key `rev:<n>`, type `weksa.inventory_revision`, with parent, change list and `frozen` | Appended per accepted round; exactly one revision is ever marked frozen, by the convergence test | The loop's accept rule appends; the convergence test freezes; Self records the freeze as a ruling |
| Target profile | Locale or language-project id (`en-US`, `ja-JP`, a conlang's slug) plus a revision | Authored, revised by commit or by an accepted round that changed the lowering; each lowering names the revision it used | Profile authors and accepted rounds (`profile-owns-register`); never the lowerer's defaults |
| Corpus item | Key `item:<source-slug>:<n>`; type `weksa.corpus_item` with text, language, speaker, split group, prior turns, source | Admitted once; the text is immutable (a correction is a new item that `supersedes` the old); withdrawn, never deleted, so old scores stay explainable | Importers and `weksa corpus add`, under `corpus-cast-plus-public-domain`; the source enum has no variant for Discord or any training-forbidden source (`corpus-clean`) |
| Split assignment | Derived: `split(scheme, item.group)` | Fixed for a scheme; a new scheme is a new constant, never a reassignment. Model-written items never land in test | The hash function. Nobody assigns by hand, so there is no writer to forbid |
| Loop round | Key `run:<label>:round:<n>`; type `weksa.loop_round` with spec version, profile revisions, model ids and prompt digests, sampled train items, proposed change, validation scores before and after, outcome | Opened, scored, closed accepted or rejected; immutable once closed. Lives in the run store on Yggdrasil | The loop driver's accept rule (validation composite improves past the measured noise floor and no metric regresses past tolerance) |
| Score | Key `<lowering-key>:<metric>@<metric-version>`; type `weksa.score` | Immutable; a new metric version scores again under a new key | The scorer, deterministic given the metric version and the embedder's model id |
| Lowering trace | Key `lowering:<packet-key>:<profile>@<rev>:<lowerer-digest>:<sample>`; type `weksa.lowering` holding the surface text and its trace together | Immutable. A surface text cannot exist without its trace, because they are one record (`lowering-traced`) | The lowerer emits; the lowerer sees the packet without its `trace` (the input type has no such field), never the corpus item |
| Conlang lexicon entry | Key `lex:<language>:<concept-id>`; root form, morphology class | Root placed at its concept's embedding when the concept first needs a form in that language; stable once accepted; diachrony derives later forms, it does not overwrite | The language project's owner: the operator for her settings' languages, the loop proposing roots for her review |
| Conlang grammar and phonology data | `<language>/grammar`, `<language>/phonology` documents per language project | Revised by commit; every lowering names the revision | The language project's owner |
| Training pair (later) | Key `pair:<frozen-rev>:<item-id>:<profile>` | Created only by re-encoding the whole corpus under the frozen inventory revision; never before the freeze (`staged-distill-every-language-equal`); split inherited from the item | The re-encode job, which can only be constructed from a frozen revision |
## Authority map (the packet boundary)
- Owner: the validator in `crates/weksa` owns whether a packet exists.
- Inputs: the packet, the inventory at the packet's revision.
- Outputs: an admitted packet, or a typed refusal naming the rule.
- Derived state: split assignment (from the item's group); the lowering view
of a packet (the packet minus its trace).
- Forbidden writers: the Node daemon's `decomposeThoughtIntoInterlingua` and
`normalizeInterlinguaPacket` (parked, cut `park-node-body`); the Metame
harness's packet-like YAML (parked); any loop step that writes a packet
without the validator.
- Shared paths: the loop's encoder, the CLI, tests and the future live
lowering all admit packets through the same `validate` call.
- Deletion line: the Node body goes before the crate lands, so no second packet
shape survives on main.
## Scoring (`golf-metric-automatic`, `conlang-scoring`, `meaning-metric-embedding-cosine`)
- English against its corpus item: chrF (character n-gram F-score, beta 2; a
20-line implementation matched sacrebleu 2.4.3 exactly on four probe pairs)
and meaning cosine (sentence embeddings from qwen3-embedding:0.6b, already
served by Ollama on Yggdrasil). Probe on one reference: hedged paraphrase
0.948, flattened claim 0.643, register-shifted 0.720, negation 0.496.
BERTScore proper needs per-token transformer embeddings and a torch stack;
it is deferred, not adopted.
- Every language by the same meaning cosine: the embedder reads the output
directly when it can (natural languages, since the embedder is multilingual);
for a conlang it reads the deterministic gloss made from the language's own
lexicon (Weksa owns the lexicon, so reading it back needs no model). Plus
lexicon adherence for a conlang: the share of tokens that parse against its
lexicon and morphology.
- A style axis follows as its own cut, behind the same scorer port.
- Leak check: an inventory entry minted in a round is refused if its gloss
shares a word 3-gram with the corpus item it was minted for. The packet's
only free text is proper names and the trace, so the gloss is the channel to
guard.
## Where things live and run (`rust-single-crate`, `loop-runs-on-yggdrasil`)
- Code: one Cargo workspace at the repo root, one package `crates/weksa` (a
library and the `weksa` binary). CultCache through `cultcache-rs` pinned by
git rev, as Huginn pins it. No daemon: the loop is a batch command. A daemon
is earned later only by the live lowering service, if isolation protects a
named invariant there.
- Stores: `data/interlingua/inventory.cc` and `data/corpus/corpus.cc` in the
repo (small, reviewed with `weksa dump`); run stores (rounds, lowerings,
scores) on Yggdrasil under the loop's state root, not in git.
- The loop runs on Yggdrasil as a detached unit with a log and a status record.
Starfire runs none of it. Model calls go through a `ModelPort`; its first
adapter drives `claude -p` on the operator's subscription. Never OpenAI
(`eureka-body:ruling:no-return-to-openai-supplier`), never Discord data
(`gamecult-chat:ruling:discord-taints-training`).
## Convergence (`convergence-test`)
The spec has converged when, on a panel of en-US, ja-JP and the first conlang,
for 20 consecutive rounds no accepted change to a closed-class inventory family
or the schema raised the validation composite past the noise floor, and at that
checkpoint the test-split composite is within the noise floor of validation.
The test split is read only at checkpoints. Self then records the freeze, and
the training set is the whole corpus re-encoded under the frozen revision.
## Cut sequence
One line per cut; only the first two have specs.
1. `park-node-body`: park the Node daemon, MiMo command, RUDP shims, launchers and the Discord-sourced Metame harness at tag `parked/weksa-node-body`; README front door tells the truth. Waits on nothing. (spec)
2. `packet-store`: Rust crate with packet, inventory and corpus types, the validator, the derived split, the stores and the `weksa` CLI; the standard reconciled to the types. Waits on nothing; sibling of 1. (spec)
3. `scorer`: chrF, meaning cosine behind an `Embedder` port (Ollama on Yggdrasil), the leak check wired, `weksa score`. Waits on 2.
4. `pd-importer`: public-domain play and fiction dialogue into the corpus with speaker, scene group and edition provenance. Waits on 2 and follow-up `gutenberg-terms`.
5. `golf-loop-en`: the loop driver, `ModelPort` with the `claude -p` adapter, encoder and lowerer prompts, round records, the accept rule, the detached unit on Yggdrasil. Waits on 3, 4 and follow-up `claude-on-yggdrasil`.
6. `style-axis`: a style-embedding score behind the scorer port. Waits on 3.
7. `ja-panel`: ja-JP into the loop's panel. Waits on 5.
8. `conlang-lexicon`: the first conlang's lexicon placed at concept embeddings, its morphology data and the gloss reader. Waits on 3 and question `first-conlang`.
9. `convergence-freeze`: the convergence test, the freeze, the re-encode into training pairs. Waits on 5, 7 and 8.
10. `distil`: fine-tune a lowerer on the frozen training set. Waits on 9 and a host question asked then.
11. `live-lowering`: agents emit packets and Weksa lowers them for the reader. Waits on 9.