Files
road-compiler/.trellis/spec/backend/road-revision-storage.md

3.0 KiB

Road Revision Storage

1. Scope / Trigger

src/compile/road-revisions.js owns the persistent v2 workbench layout. It is called after a successful import and when an existing workbench area is read, so legacy import-* directories migrate lazily without an administrative command.

2. Signatures

ensureRevisionStore(workspace) // -> { paths, active, baseline }
createCheckpoint(workspace, label) // -> restored revision
readRevision(workspace, revisionId) // -> { manifest, osm, nativeRoadOverrides, directEdits, trafficSignals }

3. Contracts

  • Legacy inputs remain at source.osm, native-road-overrides.json, and native-traffic-signals.json; migration must never rewrite them.
  • OSM bytes are stored once under osm/<sha256>.osm.
  • active/native-road-edits.json starts as a validated v2 document at version 0; active/state.json points to the active revision.
  • A revision is immutable at revisions/rev-NNNN/. Its manifest.json uses road-workbench-revision/v1, references the content-addressed OSM, lists frozen JSON documents, and records SHA-256 digests.
  • Area-config snapshots are intentionally owned by the subsequent snapshot step. Do not silently fall back to the external config when that step is introduced.

4. Validation And Error Matrix

Condition Error / behavior
Required legacy input missing Revision source is missing: <path>
Content-addressed OSM differs from its filename digest Content-addressed OSM is corrupt
Revision manifest schema or ID mismatch Invalid revision manifest
Revision ID is not rev-NNNN Reject with Invalid revision id
Manifest path leaves its OSM or revision directory Reject the read; never resolve an external path
Referenced frozen OSM or JSON digest mismatch Reject the read with a digest mismatch error
Empty checkpoint label Reject with label must be a non-empty string

5. Good / Base / Bad Cases

  • Good: first open of a legacy directory adds osm/, active/, and revisions/rev-0001/, leaving old files byte-identical.
  • Base: opening an already migrated directory is idempotent; an interrupted migration with a baseline but no active state repairs only the missing state file.
  • Bad: mutating a frozen revision document or its referenced OSM must make readRevision() fail rather than returning altered input.

6. Tests Required

test/road-revisions.js must cover:

  • legacy-byte preservation and baseline creation;
  • equal OSM bytes yielding one content-addressed copy;
  • named checkpoint creation, parent linkage, and complete restore;
  • manifest digest equality with the actual frozen files;
  • invalid checkpoint labels.

7. Wrong Vs Correct

Wrong

fs.copyFileSync(sourceOsm, path.join(revisionDirectory, 'source.osm'));

This duplicates equal OSM files and provides no integrity check.

Correct

const { file, digest } = storeOsm(paths);
manifest.source = { osmFile: path.relative(paths.workspace, file), osmSha256: digest };

The content address is both the deduplication key and the integrity contract.