AGPL-3.0-or-later · forever.

the router

You describe the job. It finds the file.

nika new takes plain words and hands back a real workflow. There is no model behind it, no network call, and no index to download: the answer is ranked against the 49 workflows compiled into the binary you installed, by a function whose constants are published below. When it is not sure, it says so.

  • 49workflows rankedcompiled in
  • 26real jobsthe answers
  • 10skeletonsthe drafts
  • 42stopwordsours included
  1. 01

    Your words become tokens

    The utterance is lowercased and split into words. Then 42 stopwords are dropped, and the list includes Nika’s own vocabulary: nika, workflow, tasks, template, slot. Saying "a nika workflow that…" must not out-score the job you actually described.

    crates/nika-onboard/src/intent.rs · the stopword table is closed and hand-audited

  2. 02

    A contract is read from the sentence, with no model

    Before any score is compared, a deterministic contract is extracted: what the job READS, what it WRITES, whether a human gates it. A closed lexicon of 21 sources and 16 transforms, zero inference. A candidate that cannot honour every required capability is out before it is ranked.

    the capabilities of a candidate are DERIVED from its body, never hand-tabled

  3. 03

    BM25 ranks what survives

    Okapi BM25 over 49 documents: 26 real jobs, 13 teaching steps, 10 skeletons, all compiled into the binary at build time. There is no index to download and no network call. A document is its name plus its body with comments stripped, so the description line you write in a workflow is what the router reads.

    k1 = 1.2 · b = 0.75 · the Lucene idf form, floored at zero

  4. 04

    A floor and a margin decide whether it may answer

    Two conditions, both required. The winner must clear the floor for its corpus, and it must beat the runner-up in its own facet by a margin. Neighbours from a different facet do not trigger the margin: a job and a skeleton are different kinds of answer, not rivals.

    floor 5.5 over the whole catalog · 3.0 over the skeleton set · margin 1.3

  5. 05

    When it is not sure, it says so

    Three ways to fail, and all of them ask instead of guessing: nothing left after stopwords, the contract emptied the field, or the score sat below the floor. You get up to three candidates with their own descriptions and a command that works. A confident route writes the file; an unsure one writes nothing.

    a routed skeleton is announced as a DRAFT · a routed example says « yours now »

When it is sure

The file lands beside you and the next command is named. A skeleton arrives as a draft with its slots to fill; a real job arrives whole, with its fixtures.

a confident route
nika new "summarize a csv every monday" x.nika.yamlx.nika.yaml ← routed intent → template `chain`  a DRAFT · fill the `# SLOT:` lines, then `nika check x.nika.yaml`

real output · nika 0.107.0 · nika new "summarize a csv every monday" x.nika.yaml · re-captured at every release

When it is not

Nothing is written. You get the closest candidates with their own descriptions, and the shortest way to get unstuck: name one, or say more.

an honest refusal
nika new "make it good" out.nika.yaml`make it good` doesn't route confidently · closest matches:    etl-quarantine   incremental data job with a state file    chain            gather facts → one model step → persist  name one, or describe the job in more words

real output · nika 0.107.0 · nika new "make it good" out.nika.yaml · re-captured at every release

The limit, said out loud

Plain-word routing is an English-first door today. The same job described in French does not route:

does not routeRelance les factures impayées par email après validation humaine

routeschase unpaid invoices by email after human approval

The same job, described in French, does not route. The lexicon splitter inside the router is ASCII-only, so an accented word breaks into pieces that match nothing: « impayées » becomes « impay » and « es ». The BM25 tokenizer beside it is Unicode-aware and keeps the word whole, which is why the two disagree. Until the splitter learns Unicode, plain-word routing is an English-first door, and the honest workaround is to name the skeleton instead of describing the job.