Mova0.5 pre-release⌕
☰
home / docs / train / Add a seed

Add a seed

Gist

A seed is a short markdown note holding one piece of knowledge, with its source. It contains fenced blocks that a machine can read. Think of a seed as the thing a rule grows from. There are two kinds:

When to use

Domain-specific depth, such as legal terms or chemistry at expert level, belongs in a domain module, not in the global level. See Adapting Mova to your own domain.

Global seed file layout

Write prose for humans first, then the blocks. The build reads only the blocks. Files are found recursively and sorted by path. The sections already in the repo are self/, shortcuts/, values/ and wikidata/.

# Short title

**Gist.** One to three sentences: the knowledge.
**Conditions and exceptions.** Where it does not hold.
**Examples.** Two to four short English examples.
**Sources.** Book and page, paper id, dataset.

```relation
...

## Block syntax (exactly what `global/build.rs` parses)

Each non-empty line inside a block is one entry. Leading and trailing spaces are trimmed.

| block | line format | compiled into | notes |
| --- | --- | --- | --- |
| ```` ```concept ```` | `name: word word word` | `CONCEPTS` | the words trigger the concept in text (`global::concepts_in`) |
| ```` ```category ```` | `name: word word word` | `CONCEPTS` with `category: true` | explains steps (time units, measures, countable things) but never starts a consequence chain |
| ```` ```link ```` | `from -> to : why` | `LINKS` | both ends must be declared concepts or verb classes; `why` is the explanation that gets printed |
| ```` ```verbs ```` | `class effect lemma lemma ...` | `fn verb_class(lemma) -> Option<(&str, char)>` | `effect` is one of `=` `+` `-` `±` (what the verb does to the holder's quantity); the class name also becomes a concept |
| ```` ```relation ```` | `name: key=value key=value` | `RELATIONS`, `global::relation(name, key)` | values contain no spaces |
| ```` ```principle ```` | `id:`, `name:`, `gist:`, `against:`, `towards:`, optional `rule:` (one per line) | `PRINCIPLES` | `against`/`towards` are space-separated words, `_` stands for a space; `rule` names a predicate over the story world (`world::values`) |

These relation keys are currently read by code (`world/src/babi.rs`, `math/src/qread.rs`):

| key | meaning | example |
| --- | --- | --- |
| `inverse` | the reverse relation | `north_of: inverse=south_of` |
| `same` | an alias | `fit_inside: same=smaller_than` |
| `vec` | a direction on the plane, `x,y` | `east_of: vec=1,0` |
| `step` | the step letter in path answers | `north_of: step=n` |
| `transitive` | chains compose | `bigger_than: transitive=yes` |
| `order` | position in time | `morning: order=1` |
| `go` | where an agent in this state goes | `tired: go=bedroom` |

Any other key compiles without error, but no code reads it until you write that code.

## Steps: global seed

1. Find the gap. Run the reader on a tiny story (see [story-worlds.md](story-worlds.md)):

   ```sh
   cargo run --release --manifest-path world/Cargo.toml -- \
     babi-probe "Sumit is sleepy." "Where will sumit go?"
   # ? Where will sumit go? → ("?", "gap: where they go when sleepy")

The gap: marker shows the missing knowledge. 2. Check whether the knowledge already exists: grep -rn "sleepy" global/seeds. 3. Add it. Use an existing file when the topic matches, or create a new .md file. Here, the motivations seed (State → where they go) already holds hungry: go=kitchen and similar lines:

sleepy: go=bedroom
  1. Rebuild and run the tests. build.rs reruns whenever anything under seeds/ changes:

    cargo test --release --manifest-path global/Cargo.toml
    
  2. Rebuild the consumer and probe again. world and math depend on global by path:

    cargo run --release --manifest-path world/Cargo.toml -- \
      babi-probe "Sumit is sleepy." "Where will sumit go?"
    # ? Where will sumit go? → ("bedroom", "sleepy → bedroom (first level)")
    
  3. Measure the whole benchmark before and after, twice each (see "Measure" below).

# Heating

**Gist.** Heating makes a thing hot.
**Sources.** Any school physics textbook.

```verbs
heat + heat warm boil fry
hot: hot boiling
heat -> hot : heating makes a thing hot

A unit test pins the behaviour, including a negative control:

```rust
#[test]
fn heat_class() {
    assert_eq!(global::verb_class("boil"), Some(("heat", '+')));
    let p = global::path(global::concept("heat").unwrap(), global::concept("hot").unwrap()).unwrap();
    assert_eq!(p[0].why, "heating makes a thing hot");
    assert_eq!(global::verb_class("freeze"), None); // must stay unknown
}

Build gates (what makes the build fail)

cargo build panics, naming the seed file, when:

The gates are tested to really fail. Adding hot -> burn_risk : ... without declaring burn_risk stops the build with "concept «burn_risk» not declared".

Steps: grammar seed (en/seeds)

  1. Pick a section: morph, nominal, verbal, lexicon or errors. Grep first, because more than 400 rules already exist (grep -rn "much" en/seeds).

  2. Write the prose (Gist, Conditions, Examples, How it looks in UD, Sources) and a rule block:

    rule: en.verbal.double-modal
    what: two modal auxiliaries on one verb (might could go) - standard English allows one
    match: v[upos=VERB|AUX]; m1[xpos=MD, rel=aux, head=v]; m2[xpos=MD, rel=aux, head=v, after=m1]
    require: not m2[]
    severity: warn
    source: CGEL ch. 3 (modals have no non-finite forms)
    

    Fields: rule (unique id), match (nodes name[cond, cond] separated by ;), require (clauses separated by ;, alternatives within a clause separated by or; each item is v[...], not v[...], exists c[...] or none c[...]), optional unless, what, severity (default warn), source. Conditions: upos=, xpos=, lemma=, form=, rel=, rel~ (base relation), feats.X=, feats.X, !feats.X, head=v|0, before=, after=, next=, prev=, suffix=, prefix=, lemma.suffix=, lemma.prefix=, feats.X=@node, lemma=@form. Use != and !~ to negate. Separate alternative values with |.

  3. Check that it fires where it should and stays silent elsewhere. Use a tiny CoNLL-U file with one bad sentence and one good sentence:

    EN_EXPERT_LIST=1 cargo run --release --manifest-path en/Cargo.toml -- \
      expert-check en/seeds/verbal tests/double-modal.conllu
    # en.verbal.double-modal | warn | 1 | 1 | ... | dm1: go / might / could
    

    On a parser's output with a gold file, expert-check <dir> --gold gold.conllu pred.conllu adds a precision column: how many violations sit on tokens where the prediction is really wrong.

  4. Run the gate test. It requires that every rule in en/seeds parses and that rule ids are unique:

    cargo test --release --manifest-path en/Cargo.toml --lib seeds_all_parse
    
  5. Run the rule over a gold treebank such as UD EWT. A rule that fires often on gold data is either wrong or is catching errors in the gold data. Read the hits before you trust the rule.

Measure

Pitfalls

This page in Markdown → https://md.mova.elamur.ai/free/en/docs/train/add-a-seed/