Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Experiment manifest

A manifest is generated JSON describing an experiment’s inputs and presentation. The runner uses it for validation, credential discovery and result descriptions.

Read a packaged manifest with:

nix run .#inspect-hello -- --describe

Top-level fields

FieldMeaning
schema_versionCurrently 1; results are ordered, named declarations.
nameUnique experiment name and app name.
summaryShort description of the experiment.
readmeOptional Markdown source from the experiment directory’s README.md.
paramsObject mapping parameter names to declarations. Every key must be bound for a run.
schemaPayload union identity and export: { "version": 0, "models": "module:attribute", "path": "/nix/store/.../schema.json" }. Separate from the manifest file’s schema_version.
resultsOrdered list of possible result declarations (see below), each with a unique name. The runner warns on repeated or undeclared result events; readers derive the last emitted value for each declared name.
envOptional experiment metadata. It does not cause the runner to inject arbitrary variables or provision services.
originPackaging repository reference, separate from a run’s pinned fetch reference.
linksExternal references, each with label and url.

The source identity and executable are wrapper settings, not manifest fields. reps.mkExperiment takes name, summary, params, program and src, with optional results, schema, schemaPython, sharedSrcs, env and links. Schema defaults to version 0 and reps_events:Payload; typed experiments supply their own union pointer and schemaPython = "${env}/bin/python" from the built program environment. Packaging imports that pointer and calls export_schema, failing the build if it cannot import. schema.json and the shared export sit beside the manifest, along with a python link to the schema interpreter used by reps-runner verify; schema.path is generated, not author-supplied.

The catalog packages the experiment directory’s images under assets/<experiment-name>/, preserving subdirectories such as figures/. Relative image paths in readme refer to these assets, copied to index/catalog/assets/<name>/ in the published site. Images are separate catalog assets, not embedded in the manifest JSON.

Parameter declarations

Each entry of params contains type plus optional fields:

FieldMeaning
initialInitial form value and CLI suggestion. It is never an implicit run-time parameter default.
descriptionExplanation shown alongside the input.
nullablePermit explicit JSON null; default false.
orderPresentation order, lower first; default 100, with name breaking ties.
groupForm section label.
suggestionsSuggested strings or { "value": STRING, "description": STRING } entries. Suggestions do not restrict legal values.
minLen, maxLenList-length bounds checked before execution on realized values.
fieldsNamed parameter declarations for a structured object editor. The runner still validates an object as free-form JSON.
depends_on, variantsForm metadata selecting a field set using another parameter’s value. No in-tree manifest currently produces this variant form.

LLM-typed parameters receive shared model suggestions during manifest generation. This also applies to LLM fields inside lists of structures. Experiment-specific suggestions are prepended to the shared suggestions.

Result declarations

results is an ordered list of declarations. Each declaration contains a unique name, a type descriptor and optional presentation fields:

FieldMeaning
nameRequired string matching the name on a result event. Duplicate names fail Nix evaluation.
typeRequired type descriptor, using the types listed below. It does not enforce the emitted result’s type.
labelOptional string used as the readable result name. The metric key remains its identifier.
descriptionOptional short string explaining the result in plain language.
detailsOptional longer string explaining the calculation, aggregation or interpretation caveats.
unitOptional string displayed alongside the value; it does not convert or rescale the value.

In Nix, use results = [ { name = "count"; type = reps.types.int; label = "Recorded count"; description = "Count supplied to the program."; } ];. The manifest preserves the list and its presentation fields in that order. Map declarations and bare-type shorthand are not supported.

Results are possible outputs, not required outputs. A declared metric that was never emitted is absent from the summary; absence does not mean zero. Undeclared results remain in the stream but warn and stay out of the summary. Boolean results do not inherently indicate success or failure; their meaning comes from the declaration.

The runner snapshots the ordered declaration list in run.json under definitions.results and in run.start as result_definitions. These saved definitions preserve the run’s interpretation when the installed experiment changes. Result events have no unit field; units come from the declaration. Events do not override result labels, descriptions or details.

Type descriptors

Nix constructorJSON descriptorAccepted value
reps.types.str{ "kind": "str" }String
reps.types.llm{ "kind": "llm" }Model-ID string; also participates in credential discovery
reps.types.int{ "kind": "int" }Integer, excluding booleans
reps.types.float{ "kind": "float" }JSON number, excluding booleans
reps.types.bool{ "kind": "bool" }Boolean
reps.types.enum values{ "kind": "enum", "values": [...] }One declared value
reps.types.listOf type{ "kind": "list", "of": TYPE }Array whose elements match TYPE
reps.types.struct fields{ "kind": "struct", "fields": {...} }Object with exactly the declared keys and matching field types
reps.types.object{ "kind": "object" }JSON object with arbitrary keys and values

Use listOf (struct {...}) for a list of structured values. A structure field can be a bare type descriptor or a wrapper containing type, description and suggestions. param type attrs attaches parameter metadata in Nix.

For example:

with reps.types; {
  agents = param (listOf (struct {
    name = str;
    model = param llm { description = "Model for this agent."; };
  })) {
    minLen = 1;
    description = "One row per agent.";
  };
}

What does validation establish?

The runner rejects unknown and missing parameter keys, then checks value types before hashing the condition. It checks realized values again before executing, including list-length bounds. Specified and realized parameters are currently identical; no distribution or sweep syntax is implemented.

Validation does not verify model existence, credentials, endpoint access or semantic constraints that only the experiment understands. Result type descriptors do not validate emitted metrics against the result declaration; standard result payloads have their own scalar-value rule.