Mova0.6 pre-release⌕
☰
home / docs / tools / mova-whale-world — document dossiers and one global world for Whale epochs

mova-whale-world — document dossiers and one global world for Whale epochs

🐋 Whales write long descriptions of documents epoch after epoch (see tools/whale). Each epoch adds texts about a document: the original, Mova's long draft, a consultant's description, earlier descriptions and pages found on the web. This crate keeps those texts as an append-only dossier per document and reads all of them with Mova into one global world — entities, statements, a word → sentence → document index and a document graph. A writer works from a document's neighbourhood in that world: its own texts of earlier epochs and the texts of its nearest documents, with all their content. Everything is internal by default.

It reuses mova-topic-corpus: the library reader and neighbourhood graph (library, graph), licence classes (licence), Mova's annotator over the Whales' reader (corpus::annotate), the acronym test and statement kinds (world), explanation scoring (corpus::explain_score) and the per-paper topic corpora (their cached parses, web pages and consultant drafts). No parsing is reimplemented here. A separate crate keeps the Whale epoch driver's dependency small: a library and one binary over topic-corpus.

cargo build --release --manifest-path tools/whale-world/Cargo.toml --offline
WORLD_EN_MODEL=…/en.bin cargo test --manifest-path tools/whale-world/Cargo.toml --offline

Every command takes one configuration (mova-whale-world-v1, unknown fields refused, absolute paths); a deployment keeps its configuration outside this crate. config.example.json shows every field with placeholder paths: dossier_root, world_root, optional library (the topic-corpus library block), corpora_root, site_old {roots, files, licence}, mova_state_roots, mova_rounds (last | all), public_originals (default false) and limits {cache_shards, cache_buckets, neighbours, context_bytes}.

Commands

mova-whale-world dossier import CONFIG DOC...       # all importers, idempotent
mova-whale-world dossier add CONFIG DOC --epoch E --kind K --file PATH --source S
                 [--licence L] [--input TEXT_ID]... [--created UNIX]
mova-whale-world dossier list CONFIG DOC [--epoch E] [--kind K]
mova-whale-world dossier verify CONFIG DOC          # exit 1 when a file changed
mova-whale-world dossier text CONFIG TEXT_ID
mova-whale-world world build CONFIG [--epoch E] [DOC...]
mova-whale-world world stats CONFIG [--epoch E]
mova-whale-world world entity CONFIG TERM [--epoch E]
mova-whale-world world explain CONFIG TERM [K] [--offset N] [--epoch E]
mova-whale-world world related CONFIG DOC [K] [--offset N] [--epoch E]
mova-whale-world world neighbourhood CONFIG DOC [--radius R] [--max-docs N] [--epoch E] [--content]
mova-whale-world world sentences CONFIG WORD [K] [--offset N] [--epoch E]
mova-whale-world world context CONFIG DOC [--radius R] [--max-docs N] [--max-bytes N] [--epoch E]
mova-whale-world export-texts EXPORT_CONFIG OUT_REPO [DOC...]

Every command prints one JSON value on stdout (neighbourhood --content prints JSON lines). A refusal (unknown document, changed file, existing text id, interrupted build) is an error with a non-zero exit. Writes are incremental, not a transaction over the whole store: a build that fails after it started writing can leave directories, shards, indexes and pending.json, and the next build refuses that root; inspect the failure and rebuild into a new root. Defaults: K 5 (explain) or 10, --offset 0, --radius 1, --max-docs 20, --epoch the store's built epoch, world build --epoch the highest dossier epoch plus one (everything).

Dossier store (mova-whale-dossier-v1)

<dossier_root>/<doc-id>/
    texts.jsonl                      append-only index, one row per text
    texts/<epoch>-<kind>-<n>.md      a text (originals .md.zst)

doc-id is the library id of the document (a lowercase slug). A text id is <doc-id>/<epoch>-<kind>-<n>; n counts from 1 within (epoch, kind). Row fields:

fieldmeaning
idthe text id
docthe document id
epochunbounded integer (u64); 0 for material that existed before the epochs, 1 for imported drafts
kindoriginal, mova, sol, site-old, web
sourcewhere it came from: a path, a URL or a terminal call
licence, licence_classthe licence as stated and its class (topic_corpus::licence::Class); ours (internal) when none is given
public_okfalse by default; true only for an original library text under CC BY, CC0 or public domain that is not local-only, and only when the configuration's public_originals is true. web, sol, mova and site-old are never public, whatever licence they state
sha256, wordsof the text (decompressed)
created_unixwhen the text was made (the source file's mtime for imports)
inputstext ids the text was written from
citations{count, unresolved}: numeric ([3], [2, 5-7]) and author–year ((Smith et al., 2016), Smith (2016)) markers, and those without a reference entry (a line [n], or a line after a References/Bibliography heading with the surname and year)
filethe text's file, relative to the dossier directory

Append-only: a row is never rewritten and a file never replaced (files are created with create_new; appends hold lock and are fsynced). Adding an existing id is refused; a file whose hash differs from its row is refused by every reader (verify, text, the world build) until the new content is added as a new text. Rows are selected by epoch and kind from the index alone.

Importers (dossier import, idempotent: a text of the same kind, hash and source is skipped):

An epoch driver adds its own texts with dossier add (or Dossier::add).

Global world (mova-whale-world-v1)

<world_root>/
    world.json                       schema, reader, built epoch, counts
    docs.jsonl                       one row per document (rewritten on add)
    texts.jsonl                      append-only: t → text id, doc, epoch, kind, sha, counts
    edges.jsonl                      library graph edges (rewritten on add)
    shards/<t>.jsonl.zst             every parsed sentence of text t
    index/w-XX.json.zst              256 word buckets: word → [(t, s)]
    index/e-XX.json.zst              64 entity buckets: entity → kinds, forms, postings, expansions
    doc-entities/<doc>.json.zst      entity → text → (kind, mentions) of one document

Queries

All answers are deterministic (ties by document, text, sentence) and page with K/--offset. Citations are {doc, text, s}: a dossier text id or lib:<id>, and the sentence number in that text's shard.

Library API (for an epoch driver)

use whale_world::{config::Config, dossier::{self, Dossier, Kind, NewText}, world::World, query};

let cfg = Config::load(path)?;
let d = Dossier::open(&cfg, "doc-id")?;                       // creates the directory
let rows = d.select(Some(2), Some(Kind::Mova))?;              // no text read
let text = d.read(&rows[0])?;                                 // hash-checked
let row = d.add(NewText { epoch: 2, kind: Kind::Mova, text: &body, source: "…",
                          licence: None, local_only: true, inputs: vec![…], created_unix: None })?;
dossier::import::all(&cfg, lib.as_ref(), "doc-id")?;          // lib: Option<topic_corpus::library::Library>
let mut w = World::open(&cfg)?;
w.add(&cfg, 3, &[])?;                                         // world(3): texts of epochs < 3
query::neighbourhood(&mut w, "doc-id", query::RADIUS, query::MAX_DOCS, 3)?;
query::neighbourhood_content(&mut w, "doc-id", 1, 20, 3, &mut |line| Ok(()))?;
query::context(&mut w, "doc-id", 1, 20, 3, Some(60_000))?;
query::explain(&mut w, "GEC", 5, 0, 3)?;  query::entity(&mut w, "GEC", 3)?;
query::related(&mut w, "doc-id", 10, 0, 3)?;  query::sentences(&mut w, "edit", 10, 0, 3)?;

Answers are serde_json::Value in the shapes above.

Texts export (mova-whale-texts-v1)

export-texts writes the finished descriptions into an editorial texts repository (main article and shadows, as in a repository whose subjects have a main article and preparatory «shadows»). Its configuration: world (a mova-whale-world-v1 configuration: dossiers and library), batches (the batch log), learners (learner name → state root), from_batch, dir (the directory in the repository), describer, visibility and licence (stated in every file). Without DOC it exports every paper assigned in a batch >= from_batch whose learner state says passed; the main text is the last round whose gates.json passed.

<repo>/<dir>/index.md                                   list of exported papers
<repo>/<dir>/export.json                                manifest: papers, file → sha256 written
<repo>/<dir>/<doc>/index.md                             main article
<repo>/<dir>/<doc>/seo.json                             title, meta description
<repo>/<dir>/<doc>/shadow/<epoch>-<kind>-<n>/index.md   dossier texts: mova, sol, site-old

The answer is {papers: [{paper, words, batch, whale, round, shadows, notes, citations}], exported, not_passed, report: {written, unchanged, edited, foreign}}.

Licences

The world is internal. public_ok travels with every dossier row and world text; nothing here writes a public output. The texts export writes internal texts only and states their visibility and licence in every file. Pages fetched by Sea Spider, consultant drafts, Mova drafts and earlier descriptions are never public_ok, whatever licence they state (dossier::public_ok).

Limits

This page in Markdown → https://md.mova.elamur.ai/free/en/docs/tools/whale-world/