4.3 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 }
setActiveAreaConfig(workspace, options) // -> { paths, areaConfig }
readAreaConfigSnapshot(file) // -> compiler options
createCheckpoint(workspace, label) // -> restored revision
readRevision(workspace, revisionId) // -> { manifest, osm, areaConfig, nativeRoadOverrides, directEdits, trafficSignals }
3. Contracts
- Legacy inputs remain at
source.osm,native-road-overrides.json, andnative-traffic-signals.json; migration must never rewrite them. - OSM bytes are stored once under
osm/<sha256>.osm. active/native-road-edits.jsonstarts as a validated v2 document at version0;active/state.jsonpoints to the active revision.- The active config is
{ schema: 'road-workbench-area-config/v1', options }atactive/area-config.snapshot.json. Updating it must also updatebase.areaConfigSha256through a versioned v2 document write. - A revision is immutable at
revisions/rev-NNNN/. Itsmanifest.jsonusesroad-workbench-revision/v1, references the content-addressed OSM and frozenarea-config.snapshot.json, lists frozen JSON documents, and records SHA-256 digests. compileInput()usesareaConfigSnapshotFilewhen present. Its externaloptionsare ignored in that mode; a missing or invalid snapshot is an error, never a fallback.
4. Validation And Error Matrix
| Condition | Error / behavior |
|---|---|
| Required legacy input missing | Revision source is missing: <path> |
| Snapshot file is missing | Area config snapshot is missing: <path> |
| Snapshot schema or options are invalid | Invalid area config snapshot: <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/, andrevisions/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, config snapshot, or 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, including area config;
- active config writes incrementing
documentVersionand preserving old revision snapshots; - compiler snapshot precedence and an explicit missing-snapshot error;
- invalid checkpoint labels.
test/fixtures.js compares normalized current compiler outputs with the checked-in native-road-package/v1.1 fixture baselines. Regenerate them only through npm run test:fixtures:update-baseline after an intentional compiler output change.
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.
Wrong
compileInput({ ...input, options: externalOptions });
This allows later external config changes to alter a frozen revision.
Correct
compileInput({ ...input, areaConfigSnapshotFile: revisionSnapshot });
The compiler validates and consumes the frozen snapshot exclusively.