Skip to content

File formats

Two artifacts cross machine and time boundaries: the trace a run writes and the bundle an export produces. Both formats are specified here completely — and both examples on this page are produced by executing it, so the row shown is a row that round-tripped and the manifest shown is a manifest that was written.

The trace

A trace is JSON Lines: one JSON object per step, in step order, in a file conventionally named traces.jsonl. It is the only thing the evaluation layer reads — no environment, no model — so a conforming file from any producer scores identically.

import json
from pathlib import Path

from skyfall_crl.eval.ingest import load_trace
from skyfall_crl.rewards.trace import EpisodeStep, TraceWriter

row = EpisodeStep(step=17, regime_id="windy", reward=0.62,
                  reward_components={"upright": 0.62})
with TraceWriter(Path("traces.jsonl"),
                 meta={"run_id": "example", "seed": 3, "algorithm_id": "steady"}) as writer:
    writer.write(row)

print(Path("traces.jsonl").read_text().strip())
back = load_trace("traces.jsonl")
print(f"read back: {len(back)} run(s), {len(back[0].steps)} row(s), "
      f"run_id={back[0].run_id!r}, seed={back[0].seed}")
{"run_id": "example", "seed": 3, "algorithm_id": "steady", "step": 17, "regime_id": "windy", "regime_origin": null, "observed_at": null, "action": null, "reward": 0.62, "reward_components": {"upright": 0.62}, "reward_detail": {}, "invalid_action": false, "effective_rank": null, "ticket_snapshot": {}, "verifier_snapshot": {}}
read back: 1 run(s), 1 row(s), run_id='example', seed=3

Row fields

Field Type Meaning
step int The step index within the run. Required.
reward float The step's scalar reward. Required.
regime_id str or null The regime label — what segments the run into configuration intervals. Null rows form one unlabelled segment.
regime_origin str or null What the label repeats, for a revisit scheduled under a new name; forgetting pairs encounters through it.
reward_components mapping Each component's weighted contribution to reward, by name — the per-term attribution every composed reward carries.
reward_detail mapping of mappings Each component's internal sub-terms, when it reports them.
invalid_action bool Whether the environment refused the action; the invalid-action rate is the mean of this column.
observed_at timestamp or null When the step was observed — recorded so a reward term that reads a clock can be replayed exactly; null for environments with no such term.
action mapping or null The action taken, as data.
effective_rank float or null The policy's representation rank at this step, when capture is on; null means not measured, never zero.
ticket_snapshot, verifier_snapshot mappings Operational records from environments that have them (the MORPHEUS adapter); empty elsewhere.

Run metadata, and how rows group into runs

The writer merges run identity into every row — top-level keys beside the step fields — so a trace file is self-describing and a directory of them aggregates with no side-channel manifest. Grouping uses the named identity fields, in order: run_id, seed, policy_id, task, algorithm_id. Rows carrying none of them form one anonymous run. Any other merged key is carried as metadata and ignored by grouping — which is deliberate: producers add per-step extras, and grouping on everything would put each row in a run of its own.

Tolerance is part of the format. A reader ignores fields it does not know and defaults fields that are absent, so the minimal conforming row is {"step": 0, "reward": 0.0} — everything else sharpens what the metrics can say. To check a file you produced elsewhere: skyfall-crl eval --traces yours.jsonl.

The bundle

An exported policy as one movable directory: a manifest.yaml describing everything, beside the checkpoint. Written by skyfall-crl run --export DIR (or run_experiment(..., export_dir=...)), consumed by skyfall-crl serve.

bundle/
  manifest.yaml
  checkpoint/          the policy's weights, in the policy's own format

The block below trains a deliberately tiny run and exports it; the manifest that follows is the one it wrote, in full.

from pathlib import Path

from skyfall_crl.train import ExperimentConfig, run_experiment

config = ExperimentConfig.from_dict({
    "env": {"id": "CartPole-v1"},
    "algorithm": {
        "name": "discrete_hill_climbing",
        "params": {"seed": 1},
        "policy": {"id": "skyfall_crl.train.backends.hill_climbing:linear_menu_policy",
                   "kwargs": {"n_actions": 2, "seed": 1}},
    },
    "rollout": {"window_steps": 50, "total_steps": 100, "auto_reset": True},
    "run": {"name": "format-example", "seed": 1},
})
run_experiment(config, export_dir="bundle")
print(Path("bundle/manifest.yaml").read_text())
version: 1
policy:
  id: skyfall_crl.train.backends.hill_climbing:linear_menu_policy
  kwargs:
    n_actions: 2
    seed: 1
  format: weights
  checkpoint: checkpoint
  external: false
observation_space:
  type: Box
  low:
  - -4.800000190734863
  - -inf
  - -0.41887903213500977
  - -inf
  high:
  - 4.800000190734863
  - inf
  - 0.41887903213500977
  - inf
  shape:
  - 4
  dtype: float32
action_space:
  type: Discrete
  n: 2
  start: 0
reward_spec: null
env:
  id: CartPole-v1
  kwargs: {}
regime:
  provider: constant
  params: {}
run:
  run_id: format-example
  seed: 1
  algorithm_id: discrete_hill_climbing
provenance: {}

Manifest fields

Field Meaning
version The manifest format version; currently 1. Readers tolerate fields they do not know, so the format can grow without breaking older bundles.
policy.id, policy.kwargs How to rebuild the policy object — the same dotted-factory form the configuration uses. Null id means the run's backend built its own default policy, and the bundle cannot be served.
policy.format How the checkpoint restores: checkpoint (the policy loads a directory, the language-model path) or weights (the rebuilt policy's load_weights(path) is handed a file, the array path). Absent means the backend saved nothing — the bundle records the run but cannot be served.
policy.checkpoint Where the weights are, relative to the bundle by default — which is what makes it movable.
policy.external True when --export-external recorded an absolute path instead of copying; the bundle then only works where that path resolves, and says so if it does not.
observation_space, action_space The Gymnasium spaces, encoded losslessly — including the details a naive encoding drops: infinite bounds, dtypes, Discrete.start, Text alphabets and zero minimum lengths. Null when the environment declared none.
reward_spec The reward specification the run was scored under, by value — a name would not resolve wherever the bundle lands. Null for a Tier-1 run: the environment scored itself, and recording that nothing was reported is honest where inventing a description is not.
env The environment's id and kwargs, so serve can rebuild it with nothing else — overridable, because serving against a different world should be something you asked for.
regime The configuration-shift provider the run trained under; serve replays it by default so served traces segment like training traces.
run The training run's identity, merged into every served trace's rows.
provenance Whatever the environment's describe() reported, stored opaquely. Empty when it reported nothing.