Skip to content

Transcript envelope and lifecycle

Part of the termverify.transcript/v1 protocol.

Common envelope

Every line is an object with these required members:

Member Type Meaning
protocol string Exactly termverify.transcript/v1.
run_id string Caller-supplied stable identifier for this run.
seq non-negative integer Zero-based, contiguous line sequence.
id string Stable unique record identifier within run_id.
kind string Record type from the v1 set below.
payload object Data defined by kind; no omitted required members.

run_id and id use lowercase ASCII letters, digits, ., _, and -; they must be non-empty and generated by the caller or deterministic harness, never from adapter ambient randomness. The recommended identifiers are the stable run-<fixture-or-request-name> and record-<zero-padded-seq> forms. A reader rejects a repeated id, a changed run_id, or a non-contiguous seq.

An extension member starts with x- and has no meaning to a generic v1 reader. All other members are reserved. A producer must not use an extension to change the meaning of a required v1 member.

Record ordering and lifecycle

A valid transcript contains exactly this lifecycle shape:

  1. run.started at seq: 0;
  2. capability.result records in configuration-table order until all requested constraints are applied, the first is unsupported, or an adapter failure terminates negotiation;
  3. if initialization completes, exactly one initial readiness observation;
  4. zero or more single-flight input epochs;
  5. exactly one terminal record: run.finished, run.failed, or run.unsupported.

An adapter failure may terminate negotiation before any capability result or after any applied prefix. The transcript then contains only run.started, that prefix, and run.failed. A subject that exits or an adapter that fails after negotiation but before readiness may likewise terminate without an initial observation. run.unsupported remains negotiation-only and has no body records.

Execution epochs and causality

The initial readiness observation is the first semantic body record after all seven capability results. Zero or more startup diagnostics may precede it, but no input may. Its position, rather than an additional payload member, declares that initialization completed and the subject is ready for input.

After readiness, v1 is single-flight. Each epoch contains:

  1. exactly one input record;
  2. zero or more diagnostics caused while handling that input;
  3. exactly one observation that closes the epoch at deterministic quiescence, unless a terminal record closes it first.

The closing observation is the complete normalized evidence available at quiescence, not an output chunk. Transcript order supplies causality, so another input before that observation is invalid. Observations and diagnostics while idle are invalid: diagnostics do not create hidden epochs. input.stop is the final accepted input. Its drain epoch may contain diagnostics and an optional final observation, followed by run.finished or run.failed; no later input is valid.

A terminal record may also occur while idle when the subject exits naturally or the adapter fails. This ordered model intentionally excludes unsolicited body events in v1. General asynchronous work requires a future explicit polling, draining, or correlation contract rather than wall-clock quiet-window polling.

No record follows a terminal record. Input records are ordered by dispatch; observations are ordered by the point at which the adapter captured them; event arrays inside an observation are ordered by application emission. Records never claim wall-clock ordering: all timing uses the configured manual clock in integer milliseconds. A capture failure is represented by run.failed, not by silently dropping a record.

run.started has a payload.config object containing the requested deterministic constraints. capability.result reports each constraint's actual outcome before the adapter accepts the first input. input.* records describe the requested user action; observation records contain the resulting structured evidence; diagnostic carries non-oracle information such as a normalized process warning.

run.started.payload is closed except for x- extensions and requires exactly config plus subject. subject is a versioned replay selector with this v1 shape:

{
  "format": "termverify.replay-subject/v1",
  "application": {"id": "example.app", "version": "1", "build": "build-1"},
  "fixture": {"id": "basic", "version": "1"},
  "adapter": {"id": "example.direct", "version": "1"},
  "normalizer": {"id": "example.identity", "version": "1"},
  "state_schema": {"id": "example.state", "version": "1"}
}

Every selector value uses the same lowercase ASCII identifier grammar as run_id. Application identity separates its application version from the exact build selector. Fixture identity selects the registered invocation without embedding a raw command line or environment. Adapter, normalizer, and state schema identities bind the interpretation needed for replay. An optional platform object has exactly normalized os and architecture selectors. Volatile hostname, account, absolute path, raw argv, and environment details are not replay selectors and are forbidden as generic subject members. Every subject and nested selector is closed except for uninterpreted x- extensions.

Terminal record payloads are:

Kind Required payload members
run.finished exit: {"kind": "code", "value": integer} or {"kind": "signal", "value": non-empty string}
run.failed error: {"code": non-empty string, "message": string, "details"?: JSON value}
run.unsupported constraint, code, message, and optional details; constraint and code are non-empty strings

Every defined record payload and nested generic protocol object is closed to its listed members plus uninterpreted x- extensions. In particular, capability.result permits effective, tier, and delivery only with applied and reason only with unsupported; delivery additionally requires the delivered tier. Application-defined JSON values such as observation state, event data, diagnostic details, and error details remain open semantic values rather than generic protocol objects. The delivery.env object (present only with the spawn-env channel; see enforcement tiers) is a value map of delivered environment-variable names, not a generic protocol object: its member names are exact variable names with no x- extension semantics.

message is diagnostic only. Consumers use stable code values for behavior; v1 reserves adapter-start-failed, adapter-runtime-failed, constraint-unsupported, and constraint-not-enforced.