69 lines
3.0 KiB
Markdown
69 lines
3.0 KiB
Markdown
# 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
|
|
|
|
```js
|
|
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
|
|
|
|
```js
|
|
fs.copyFileSync(sourceOsm, path.join(revisionDirectory, 'source.osm'));
|
|
```
|
|
|
|
This duplicates equal OSM files and provides no integrity check.
|
|
|
|
### Correct
|
|
|
|
```js
|
|
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.
|