clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

main
atlas / docs / decisions / 0009-the-enrich-compose-adapter-seam.md
0009-the-enrich-compose-adapter-seam.md 2.6K rendered
1# 9. `generate ⊕ enrich` is joined by an adapter in the CLI23- **Date:** 2026-08-024- **Status:** accepted5- **Where it is written down:** [enrich.md](../enrich.md) §1; issue #5 §3.2,6 §5.378## Context910[ADR 3](0003-merge-moves-to-enrich.md) makes the composed multi-source result11`generate ⊕ enrich`, and issue #5 §3.2 forbids the two lanes to import each12other. So the ⊕ has to be performed somewhere, by something, in a vocabulary13that belongs to neither lane. Three shapes were possible.1415- **Enrich composes.** Enrich would import `generate/compose`. Rejected — and16 the forbidding is not bureaucratic: it is what keeps either lane replaceable17 without the other.18- **Enrich emits doc-level additions the CLI feeds back.** Workable, but it19 would make every enricher learn the interchange document's schema.20- **A shared model in `format/`.** Rejected: a volume under enrichment is not a21 format concept. The format describes a written bundle; this model describes a22 build in progress, with a grid to measure distances in and a ledger that is23 not final yet.2425## Decision2627The ⊕ is performed by the one binary that holds both lanes. `cmd/atlas` owns a28mechanical adapter in each direction:2930```31atlas enrich32 ├─ generate: archive ──▶ doc.Document, one per source reading33 ├─ cmd/atlas: doc.Document ──▶ enrich.Volume (adapt.go, a copy)34 ├─ enrich: the curated queue runs, contributions are applied35 ├─ cmd/atlas: enrich.Volume ──▶ doc.Document + ledger (adapt.go, a copy)36 └─ generate: compose.Compose(document, ledger, revision) ──▶ a .atlas file37```3839Enrich keeps its own model and its own vocabulary; an enricher never learns the40document schema. The **ledger does not travel through the document** — a41document is what one source has to say, a ledger is provenance about a42composition — so the accounts reach the payload through43`compose.Options.Ledger`, already serialized and spliced in verbatim.4445## Consequences4647- `TestAdaptationRoundTrips` holds the adapter to identity: a document adapted48 and adapted back is the same document, byte for byte. That test is also the49 mechanical form of the lane's fifth law — an empty queue cannot produce a50 different build, because it cannot produce a different document.51- Composition never learns what a matched pair or a held feature is, which is52 what lets the ledger vocabulary belong wholly to the enrich lane.53- The adapter is duplication, deliberately: two models that carry the same54 information because both are shaped by what the format needs. The cost is a55 copy; the purchase is two lanes that can be rewritten independently.