v0.9.2 gives an agent a portable form. rotifer genome export writes which genes an agent uses and how they are composed into one .rgenome file. rotifer genome import reads that file on another machine, rebuilds the agent from the Registry, and prints, for every gene, what it installed and why.
v0.9.2 is the protocol version. It shipped in two npm releases of @rotifer/playground: 0.28.0 (export and import) and 0.29.0 (the file format fixed at format 1, stricter imports, and four changes that may affect existing use, covered below). Install 0.29.0.
What we ran
A fresh HOME, a fresh global install of 0.29.0, two empty workspaces. In the first, build a one-gene agent from a gene published on the Registry and export it:
npm install -g @rotifer/playground@0.29.0
rotifer init demo-a && cd demo-a
rotifer install json-validator
rotifer agent create validator -g json-validator
rotifer genome export validator Genome Export
Agent: validator
Composition: Single
Mode: capability + gene IDs
json-validator: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1 Native
✓ Credential scan passed (key names, value patterns, entropy)
✓ Wrote …/demo-a/validator.rgenomeIn a second workspace, which has never seen this agent, copy the file over and import it:
cd ..
rotifer init demo-b && cd demo-b
cp ../demo-a/validator.rgenome .
rotifer genome import validator.rgenome --dry-run # print the plan, install nothing
rotifer genome import validator.rgenome Genome Import
Composition: Single
json-validator: fallback (no-admissible-candidate) → rotifer-protocol/json-validator@0.2.2 8836abe0-…
original geneId: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1
installed geneId: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1
ℹ rejected rotifer-protocol/json-validator@0.1.0: Arena tier is 'not_evaluated', not 'verified'; …
✓ Agent 'validator' createdThen run it:
rotifer agent run validator --input '{"data":{"age":3},"schema":{"type":"object","properties":{"age":{"type":"number"}}}}'✓ [1/1] json-validator completed via WASM sandbox
ℹ Final output:
{
"errorCount": 0,
"errors": [],
"valid": true
}The rebuilt agent installed the same gene: the gene ID computed from the downloaded code matches the one in the file, byte for byte. It ran in the WASM sandbox on both machines and used the same amount of fuel, 907,943 units, on each.
The line that says fallback (no-admissible-candidate) is the honest part of this release, and the next two sections explain it.
What goes into the file
A .rgenome file is JSON with these top-level fields:
blueprintFormat:1. The format is fixed; a later change gets a new number.protocolVersion: the protocol version that wrote the file (informational).snapshot: the genes by their content-derived v2 IDs, and the composition (Single,Seq,Par,CondorTry, the formsagent runexecutes).capabilities: for each role, what the gene does: its domain, its input and output schemas, and a minimum fitness (--min-fitness, default 0.3). This is what lets an importer look for a different gene that does the same job.resolutionHints: the exact author, name and version on the Registry.metadata: a name, a creation time, and an optional description (-d).
Before anything is written, every key and string goes through a credential scan with three layers: field names that hold credentials, value patterns (known API key formats, JWTs, private keys), and entropy. A hit aborts the export, names the location in the file and the rule, and never prints the value. The import runs the same scan again on the file it is handed.
A genome's identity, genomeId, is bp1: plus a SHA-256 over the parts that affect execution. It appears in --json output.
How import chooses a gene
Each role is resolved one of two ways.
Pinned (--pinned): install exactly the author, name and version from the file, by Registry ID, and refuse unless the gene ID recomputed from the downloaded code equals the one in the file.
By capability (the default): look for a gene that can do the same job and admit it only if every condition holds:
- its Arena tier is
verifiedand its Arena entry has not been invalidated; - its fitness is above the file's minimum;
- its declared safety score V(g) is above the strictest of the author's suggestion,
--min-vg, and 0.7; - its Arena evidence is no older than
--max-eval-age(90 days); - it is Native or Hybrid and has a WASM artifact on the Registry;
- its input and output schemas match the original's exactly, apart from descriptions, titles, examples and defaults.
A gene from a different author also needs your confirmation, or --yes in a script. If no candidate is admitted, import falls back to the pinned gene and prints every rejected candidate with its reasons. Everything is downloaded and checked in a staging directory before your genes/ folder is touched.
Where this stands today: no gene on the Registry has reached the verified Arena tier yet. So today capability mode never admits a substitute, and every import installs the pinned genes, which is the fallback line above. The rule is in place and will start choosing once genes earn that tier; until then, what import gives you is a faithful rebuild of the original agent, with each gene checked against the file.
What it will not do
- Wrapped genes do not travel. An agent that uses a Wrapped gene exports, with a warning, but import refuses that role: a Wrapped gene's source is not distributed through the Registry, and there is no verified substitute to choose instead.
- Unpublished genes do not travel. A gene that only exists on your machine has no Registry entry to rebuild from.
- Files that cannot be honoured exactly are refused. Import rejects a file without
composition, with a required field missing, with schemas using a$keyword other than$comment, with duplicate keys, non-finite numbers (1e400) or integers JSON cannot hold exactly, and Cond formsagent runcannot execute (MATCHES,CONTAINSon a non-string, a field path). Export applies the same file checks, so it will not write a file that import would reject as malformed.
Four changes in 0.29.0 that may affect existing use
Two are format changes and two are stricter checks.
1. TryCatch handlers receive a structured error. A handler in a Try composition now gets:
{
"error": {
"geneId": "v2:…",
"errorCode": "FUEL_EXHAUSTED",
"message": "…",
"context": { "retryable": …, "details": … }
},
"original_input": { "…": "…" }
}geneId is the v2 ID of the gene that actually failed (or null if it has none), and a sandbox failure keeps its own code (FUEL_EXHAUSTED, TIMEOUT, RESOURCE_EXHAUSTED, SANDBOX_VIOLATION, EXECUTION_FAILURE) instead of a generic INTERNAL_ERROR. A handler that read error.code must now read error.errorCode; retryable and details moved under error.context.
2. genomeId changed format. It was 64 hex characters; it is now bp1: plus 64, and it covers only what is executed. The same Blueprint gets a different value than under 0.28.0, so do not compare IDs across the two versions.
3. A Blueprint's Cond accepts only the forms agent run executes. Import refuses MATCHES, CONTAINS on a non-string value, and a field path instead of a top-level field; export will not write them either.
4. Gene network requests cannot set client-managed headers. A rotifer.net request that sets Host, Content-Length, Transfer-Encoding, Expect or a hop-by-hop header is refused with -10 before any traffic leaves the sandbox. A gene that set one of these headers must stop setting it.
Also in this release
- Cond compares numbers by value:
EQandNEQtreat1and1.0as equal. - The sandbox's HTTP client moved to ureq 3.
installchecks what it downloaded: an installed gene's phenotype is compared with the content hash the Registry recorded; a mismatch warns without stopping the install.- Par, Cond and Try run on compiled genes, and a failed run no longer prints "complete".
- The security scanner no longer flags
evalinside strings and comments. - The Registry keeps the first publisher of a content hash, and the Arena leaderboard pages without repeating or skipping rows.
- Dependency advisories cleared in wasmtime, rustls-webpki and webpki-roots.
The full list is in the changelog.
Where this sits
An agent that improves by swapping in better genes is the goal. This release gives that idea a checkable first step: an agent's genome is now a file you can move, inspect and rebuild, with every gene identified by its content and every substitution held to written rules. The substitution itself has not happened yet, because no gene has the evidence those rules ask for.
If you export an agent and it will not rebuild somewhere else, open an issue with the import output. That is the case we most want to hear about.
Install / Upgrade
npm install -g @rotifer/playground@0.29.0If you have Try handlers, stored genomeId values, Blueprints with Cond, or genes that set request headers, read the four changes above first.
