Skip to content

Records and formats

Input modes

--input Interpretation
auto Ordinary stdin is text; recognizes the versioned Ribbit record header
text Text input
lines One string value per line
jsonl One JSON value per line
records Requires the Ribbit wire header and valid records

--file PATH and nonempty stdin are mutually exclusive. Filesystem discovery, read, and compare own explicit paths and reject piped input. Malformed JSON, invalid UTF-8, duplicate record IDs, and exceeded input limits fail explicitly.

Record wire format

A stream begins with this exact JSON header followed by newline-delimited records:

{"$ribbit":{"version":1,"kind":"records"}}
{"id":"1","value":{"ticket":"R1","body":"Checkout fails."},"annotations":{}}
Field Contract
id Nonempty string, unique within the admitted stream
value Finite JSON value
source Optional object with path, positive lineStart, and positive lineEnd fields when known
annotations Object mapping annotation names to finite JSON values

Commands preserve, transform, or aggregate records according to their own contracts. An ID is an in-run identity, not a durable global identifier. A source path records where the input was read; copying the stream does not copy the source file.

Field addressing and preservation

Plain body still means record.value.body. On record input, $.id, $.source.path, $.annotations.category.label, $.value and $ address the envelope, source, annotations, entire value, and entire envelope respectively. Quote dollar paths in the shell: ribbit where '$.annotations.category.label' --args-json '{"equals":"actionable"}'. Flow $ref bindings are unrelated to field addressing. Non-record render templates only support plain value fields. Missing fields error by default; explicit JSON null is a value, not a missing field.

select 'body,label=$.annotations.category.label' projects into value fields. Each envelope source requires an explicit ordinary destination, including snapshot=$. Whitespace around commas and aliases is ignored. Empty paths, extra =, unsafe prototype names, duplicate destinations, overlapping paths and conflicting array/object containers are rejected before input consumption; formerly order-dependent ambiguous projections are no longer supported. Arrays retain bounded indices and null-filled gaps. Projection copies are byte-bounded and never silently duplicate the envelope. --missing null substitutes null for missing lookups.

where FIELD --args-json '{"equals":1}' matches exact scalar equality without coercion: 1 differs from "1", null only matches explicit null, and a missing field errors. It streams complete retained records in order and can emit an empty stream without model calls. In flows pass native typed field and equals arguments.

Operation Preservation boundary
filter, where, take, unique, rank, sort Complete retained originals, though selection/order can change
select Replaces value with projection, retains ID/source/annotations
classify Retains evidence; replaces chosen annotation with {label}
default map Replaces value; updates map.originId, not a copy of old value
map --annotate NAME Retains evidence; replaces only the chosen annotation with the validated result
group Complete originals nested in value.members
reduce, display, bare-value exports Evidence/metadata boundary; retain upstream artifacts separately

Named classify --annotation-key NAME defaults to classify. Custom names are single safe path identifiers; classify, map, group, and tree are built-in namespaces (only classify may use its own default). A chosen custom name replaces the whole existing annotation value; different names coexist. Schema validation checks structure, not the factual truth of a model answer.

Output boundaries

Record-producing commands default to Ribbit records. --output jsonl exports bare values and drops IDs, sources, and annotations. render produces a final display/export representation. Keep records until the consumer no longer needs their metadata.

A JSON result from extract is not auto-detected as a record stream by the next shell process. If importing it into record-oriented rendering, use render --input jsonl explicitly.

stdout contains result data. stderr contains diagnostics and optional statistics. A later failure may follow an already-emitted valid prefix; inspect the exit status before accepting the whole result.

Related: composition explanation, runtime reference.