Skip to main content
packages/evals is a private, code-first customer of @moltzap/simulator. A bundled case is an immutable TypeScript value with the exact autonomous peer definitions and policy it needs. At execution time, each definition becomes one Agent Sandbox application container in the cell’s RunSpec roster. Most additions change cases.ts, grading.ts, and their tests. Change peer.ts only when the required autonomous network behavior is genuinely new. Change principal.ts only when adding a runtime condition with a new native principal gateway.

1. Choose stable identities

Pick an unused EVAL-NNN case identity, a versioned simulator definition identity, and a versioned criterion identity. They evolve independently:
  • change the definition version when the case program or evidence meaning changes;
  • change the criterion version when its question or deterministic decision changes; and
  • never reuse a persisted identity for new semantics.
Decode branded identities at the definition boundary:
Malformed values then fail when the code catalog is loaded, before a simulator resource or result bundle is allocated.

2. Declare the exact peer definitions

The target runtime belongs to the OpenClaw or NanoClaw condition. The case owns only the autonomous code peers it needs:
The keys become the exact keys of context.peers. A case with no social peers uses an empty record. Do not add idle peers to a shared roster; only the runtimes in this record are started. Each peer factory returns an image-independent EvaluationPeerDefinition with a closed application plan. Evaluation execution binds that definition to the configured digest-pinned peer image, mounts its bootstrap data, and runs the plan through peer-application.ts → runEvaluationPeerApplication inside the peer’s application container. The application uses its production MoltZap client, so every social send and receive traverses the protocol and router. Its peer-specific bridge exposes an EvaluationPeerGateway that reports a completed exchange to the evaluation controller. It is observation-only and cannot command a social action.

3. Write a policy that returns one selection

An EvaluationCaseProgram receives native target instruction and peer observation capabilities:
The operations have distinct meanings: The program returns exactly one EvaluationEvidenceId. After successful completion, the interpreter emits the corresponding EvaluationEvidenceSelected event. There is no mutable selection registry or separate completion check. A social case can ignore the option returned by instruct and select router-bound peer evidence, as the example does. A case that grades native principal output must pass that option to selectPrincipalOutput. A runtime whose gateway cannot correlate terminal output then produces an EvaluationProgramFailed execution result instead of fabricated evidence. When a required peer exchange reaches its configured deadline, selectPeerOutput returns the typed timeout evidence identity. That preserves bounded absence as gradeable evidence. A peer implementation failure, native gateway failure, unsupported principal-output selection, target termination, or overall case timeout remains an execution failure.

4. Define the case and criteria

Add the case beside its neighbors in packages/evals/src/cases.ts:
Every case has at least one slice and one criterion. Its description and rubric must say whether grading selects native principal output, social output, or bounded absence. A criterion’s decide function has two honest outcomes:
  • CriterionDecided for a conclusive passed or failed result with evidence-ID citations; or
  • NeedsJudge when semantic assessment is still required.
Exact text can decide both directions. A literal secret detector can conclusively fail a disclosure but cannot infer a pass from a miss because the response may paraphrase, spell out, split, or reconstruct the secret. Keep that other branch semantic:
Do not turn provider errors, invalid evidence, runtime failure, or model abstention into a behavioral failure. The report types preserve those states separately.

5. Add new container peer behavior only at the network boundary

Reuse the focused policies in peer.ts when they match: If none fits, add one closed autonomous application plan interpreted inside the peer container through the production client. Its bridge gateway should expose only the smallest observation needed by case execution. Do not add a generic queue of commands, a second request protocol, or a direct social callback. Arbitrary Effect closures and gateway objects do not cross the container boundary. The peer’s PeerExchange.observations are in protocol order. For a selected exchange, the final observation is the one returned to case policy; test that ordering explicitly.

6. Add a principal adapter only for a new runtime

OpenClaw and NanoClaw already have separate PrincipalDriverFactory implementations. Each factory creates a per-attempt PrincipalDriver, and drive returns Option<EvaluationEvidenceId>. They preserve their native contracts instead of normalizing them:
  • OpenClaw uses its persistent gateway RPC and records attempted instruction plus terminal output. Its factory owns the per-attempt native idempotency sequence and returns Some(outputEvidenceId).
  • NanoClaw submits to its owner-local socket, records NanoClawPrincipalInputSent, and returns None. Its output is an uncorrelated multi-frame stream, so the adapter never consumes the next frame or attributes it to the input.
Consequently, ordinary social cases can run under both conditions because they select peer/router evidence. EVAL-019 and EVAL-022 select principal output and therefore finish as explicit execution failures under NanoClaw. Keep that unsupported result visible until NanoClaw offers native correlation; do not invent a quiet window or terminal marker. For another target runtime, define its exact Gateway and failure types, write a matching PrincipalDriverFactory<Gateway, Failure>, and capture both with execution.ts → evaluationCondition. Return Some only for output correlation the native gateway actually guarantees. Keep runtime-specific sessions, acknowledgments, idempotency, and streaming semantics inside that adapter.

7. Extend the event catalog deliberately

Most new cases reuse the current gateway, social, timeout, and selection events. When a new instrument makes a genuinely new claim:
  1. add a versioned Schema.TaggedClass in events.ts;
  2. include it in events.ts → evaluationEvents;
  3. project and validate it at the ledger boundary; and
  4. update transcript and report schemas if grading consumes it.
Every customer event class is declared before ledger allocation. Event comments should state exactly what the instrument observed, without strengthening intent into agent behavior.

8. Register the case and calibration

Append the definition to cases.ts → evaluationCases. That tuple is the canonical order for execution plans, reports, and Phoenix dataset examples. If any response can reach NeedsJudge, add discriminating passing, failing, or undecided examples to the calibration definitions in grading.ts. grading.ts → semanticJudgeCalibrationFixtures binds each example back to the current case, criterion, transcript item kind, selection, and citations. Include adversarial evidence when the behavior involves disclosure, injected instructions, attribution, or conversation boundaries.

9. Pin construction and evidence invariants

Update the exact catalog order and peer-key expectations in cases.test.ts. Test the program’s operation order and returned evidence identity. Use the owning tests for each additional invariant:
  • peer.test.ts for production-client behavior and ordered observations;
  • execution.test.ts for native prelude, one final selection, and timeout selection;
  • events.test.ts for router corroboration and selection ordering;
  • grading.test.ts for target identity, selected-output validation, deterministic decisions, judge citations, and calibration coverage; and
  • sweep.test.ts, results.test.ts, or phoenix.test.ts only when the persisted or materialized contract changes.

Verification

Run semantic calibration before a live matrix. Live result bundles remain ignored local artifacts. Preserve real OpenClaw or NanoClaw failures in the report; file a reproducible product defect separately instead of changing a channel to make a case pass. The live matrix also requires digest-pinned controller/support, peer, and NanoClaw application images plus the selected local or GKE profile. Supplying those inputs is not a qualification claim; retain actual startup, execution, and grading failures as typed attempt states.