---
title: "roundtrip-wiki-rounds — Mova code model"
description: "Mova code model of tools/roundtrip/src/wiki.rs: components with their labels, obernants and the model text."
url: "https://groupants.elamur.ai/free/en/mova/tools/roundtrip/src/wiki.rs.groupant/"
---
Elamur: [Mova](https://md.mova.elamur.ai/free/en/) · [Dictionary](https://md.dictionary.elamur.ai/free/en/) · [Research](https://md.research.elamur.ai/free/en/) · **Groupants**

This page as HTML: <https://groupants.elamur.ai/free/en/mova/tools/roundtrip/src/wiki.rs.groupant/>

# roundtrip-wiki-rounds

The groupant beside tools/roundtrip/src/wiki.rs in the Mova repository: what the code computes, what it never does, how to go back from its outputs, its invariants, cost and checks.

## Facts

- **kind:** groupant
- **status:** partial source bound model
- **source:** [`tools/roundtrip/src/wiki.rs`](https://github.com/ElamurAI/mova-free/blob/main/tools/roundtrip/src/wiki.rs)
- **repository folder:** [`tools/roundtrip/src/wiki.rs.groupant`](https://github.com/ElamurAI/mova-free/tree/main/tools/roundtrip/src/wiki.rs.groupant)

> **Implementation material.** A Mova code model describes this code at one source revision: partial models, finite checks and their stated scope. It is not a reviewed general exposition, and its labels are shown as written, never raised.

## Components

| component | label | where |
|---|---|---|
| **Approximant** | partial source derived | [Forward](#forward) |
| **Ast** | partial | `syntax.json` |
| **Composition** | partial source derived | [Composition](#composition) |
| **Cost** | partial source derived | [Invariant and cost](#invariant-and-cost) |
| **Descriptant** | partial source derived | [Descriptant](#descriptant) |
| **Invariant** | partial source derived | [Invariant and cost](#invariant-and-cost) |
| **Tests** | finite evidence | [Tests](#tests) |

## Obernants

The typed relations around the forward model: the behaviour complement (what the code never does), the reverse reconstruction (every input compatible with an output) and specialised views.

| relation | type | label | where |
|---|---|---|---|
| Behavior complement | behavior complement | partial source derived finite checks | [Behavior complement](#behavior-complement) |
| Reverse reconstruction | reverse reconstruction | partial source derived finite checks | [Reverse reconstruction](#reverse-reconstruction) |

## Model text


### Descriptant

`roundtrip wiki-round`: one autoencoder learning round on Wikipedia text. It draws a deterministic
sample from the tier-1 records of Mova's Wikipedia corpus (a fixed dev sample, a fixed test sample
and the k-th fresh train window), measures the exact round trip's residual bits on dev and test with
the tables in use, adapts `en`'s order generator on the installed domain plus the window, measures
again and writes `round.json`. It decides nothing: the Eel gates and installs. Its outputs hold
Wikipedia text (CC BY-SA 4.0) and stay outside git and releases.

### Forward

Pool P = articles of shards 0..S in shard order. h(t) = fnv("ae-dev\0" + t): dev if h mod 20 = 0,
test if h mod 20 = 1, train otherwise. Dev and test: their articles ordered by their own keys, the
first `per_article` sentences each, until `dev` sentences (the last article cut). Train: articles
ordered by fnv("ae-train\0" + t); window k starts at article (k·⌈train/per_article⌉) mod |train|,
epoch = the quotient, and takes articles cyclically until `train` sentences. Volumes: one per
article, one sentence a line. Measure: `pipeline::run` exact, unit block, merged in volume order;
bits = sum of every residual cell. Candidate: domain = last `domain_max` lines of (installed domain ++
window), `Model::adapt(domain, 1)` (linearizer retrained on the model's own base trees plus the
domain's own parses; tagger and parser unchanged).

### Invariant and cost

The split depends on the title alone: no round's window contains a dev or test article, and dev and
test are the same in every round (checked again by the caller). A short pool is an error, never a
short sample. Same inputs give byte-identical tables. Cost: reading ≤ train + 2·dev sentences from
zstd frames; annotating dev and test twice; one adaptation that parses ≤ domain_max sentences and
retrains the linearizer on base + domain; memory one model (≈1.1 GB peak RSS measured).

### Composition

`main` → `pool` → `sample` (`read_article`) → `tsv` files → `Annotator::load` (current or base) →
`measure` (dev, test) → `next_domain` → `Model::adapt` → `Annotator::save` → `measure` → `round.json`.
Called by the Eel's autoencoder round (`tools/eel/src/autoencoder.rs`), which pins this executable's
hash and checks the sample files independently.

### Behavior complement

Domain: rounds and pools. Relation: the module never writes outside `--out`, never installs, never
reads the parser model, never trains the tagger or parser; with `--lin-shuffle` or `--lin-reverse` it
learns from damaged windows (controls), never both. Counterexample: articles shorter than
`per_article` make consecutive windows overlap slightly at their ends; a wrapped pool repeats
windows (epoch > 0). Evidence: unit tests below, real hand runs.

### Reverse reconstruction

Domain: `round.json` and the sample files. Relation: from (corpus, shards, sizes, k) the sample is
recomputed exactly; from the candidate tables the window is not recoverable (the domain is capped and
the linearizer is a many-to-one function of its training trees).

### Tests

`dev_is_fixed_and_never_in_any_train_window` (red when the split admits held-out articles),
`rounds_take_fresh_windows_until_the_pool_wraps`, `a_small_pool_is_an_error_not_a_short_sample`,
`domain_keeps_the_newest_lines`, `sentences_and_volumes_keep_the_text`.

## History


#### 2026-11-06 · invariant-and-cost

Measured 2026-11-06 on shards 0..10 and 0..100, train 2,000, dev 500 and 2,000, 4 threads: round
5–17 s, child peak RSS 1.07–1.18 GB; the same round run twice gave byte-identical `en-lin.bin`.
Dev 2,000 over 8 rounds: 382,333 → 380,329 bits (tree layer only; frames and world unchanged), test
378,751 → 377,049. `--lin-reverse` raised dev bits (refused); `--lin-shuffle` lowered them less than
the window as written (99,338 → 99,288 against → 99,204).

## Provenance

- **source SHA-256:** `bdd42a5c813014de…`
- **repository commit:** `c24d02b4c83b`
