Schema versioning

What this layer does

Phase 13 lets every schema in the substrate evolve without breaking older clients. The manifest, the eval-aggregate row, the brief frontmatter, and the bootstrap version all carry an explicit version field. Older clients refuse on unknown-future-version pull rather than silently corrupt local state.

Files involved

row.

frontmatter version: 1.

on every bootstrap-affecting change.

version is compatible.

aggregate rows.

Manifest versioning

type SyncManifest = { version: 1; ... };

understands → refuse pull, print upgrade message naming the local bootstrap version and the suggested npx snappy-os@latest.

overwrites a newer manifest with a downgraded version field.

Eval-aggregate versioning

_v: 1 per the locked schema:

type AggRow = {
  _v: 1;
  ts: string;
  skill: string;
  score: number;
  run_id: string;
  tenant: string;
  cost_usd_cents?: number;
  ok: boolean;
};

The _v field comes first so a streaming reader can short-circuit on unknown-version rows without parsing the rest. The lint rejects unknown keys in known-version rows; bumping _v is the only path to adding a field.

Brief format versioning

Every brief in state/log/regen-queue/ has a frontmatter version: 1 block. The auto-regen worker reads version first; mismatches surface as eval: skipped (brief-version-mismatch) rather than running with unknown semantics.

Bootstrap version compatibility

~/projects/snappy-os/.bootstrap-version holds an integer. On every init, the bootstrap compares to the remote bootstrap-version at canonical:

recommend re-running npx snappy-os@latest"

Never auto-upgrade silently. The user's runtime hooks may depend on specific bootstrap behavior; surprise upgrades break trust.

Operational gotchas

release: lint update, aggregator update, Worker update, bootstrap bump. A mismatch between Worker and CLI surfaces as confusing "manifest invalid" errors on Joe machines.

readers parse just enough to get _v and skip on unknown.

patch-vs-minor confusion on a one-author project; the integer forces explicit "this is a new bootstrap behavior" decisions.

Auto-upgrade-on-pull would mean Joe's runtime configuration changes without his knowledge.

How to verify it's working

current canonical) refuses with the documented upgrade message.

state/lint/aggregate-schema.ts exit 0 in steady state.