ADR-020 — Canonical mission JSON schema
Status · Accepted Date · 2026-04-28 Closes · RFC-002 (mission JSON schema) TA anchor · §contracts/mission-record · §contracts/mission-index-entry
Context
RFC-002 raised three open questions about the mission JSON schema:
- Should the
eventsarray be optional or required-but-empty for missions without CAPCOM data? - Should
warningevents carry athresholdfield, or is anomaly detection purely in code? - Is
data_qualityan enum (good,partial,reconstructed) or free text?
Plus the canonical field names (renaming prototype-era dep, arr, tof, dv, j2000 to readable form). Original closure was scheduled for the Slice 4 gate when the schema would be exercised by three mission types in the fly screen. We are closing it at Slice 2 entry instead — before any mission JSON is written — to avoid churning 28 files if the schema changes downstream.
Decision
File layout (three records per mission)
data/missions/index.json— single file, array of lightweight manifest entriesdata/missions/[dest]/[id].json— full base record per mission, language-neutraldata/i18n/[locale]/missions/[dest]/[id].json— editorial overlay per mission per locale
Locked field names (renames from prototype)
| Prototype | Canonical | Reason |
|---|---|---|
dep | departure_date | Unambiguous |
arr | arrival_date | Unambiguous |
tof | transit_days | Self-documenting unit |
dv | delta_v | Consistent with physics notation |
j2000 | (removed) | Computable from dates |
All other prototype field names retained: id, agency, agency_full, dest, status, year, sector, color, first, description, data_quality, credit, links, name, type, vehicle, payload.
Strictness: additionalProperties: false everywhere
Unknown fields fail validation. Catches typos and stale fields at PR time. Adding a new field is intentional, not accidental.
Answers to RFC-002 open questions
eventsis required in mission overlays. Every Slice 4 mission has at least LAUNCH and arrival/landing events. Earth-orbit objects and Moon sites have separate overlay schemas whereeventsdoes not apply.- No
thresholdfield on warning events in v1. Anomaly thresholds live in code. Addingthresholdis a v2 candidate when CAPCOM gains user-configurable alerts. data_qualityis an enum:good|partial|reconstructed. Free text rejected because it makes UI badging ambiguous; the enum drives a small visual contract (no badge forgood, amber badge for the other two).
Events shape (in mission overlays)
{ "met": <number>, "label": "STRING", "note": "STRING", "type": "nominal" | "info" | "warning" }met is days from departure (not seconds — "T+22377600s" is unreadable, "T+259d" is human).
Rationale
Closing now instead of at Slice 4 gate avoids rewriting 28 mission files if the schema needs to change after the fly screen exercises it. The risk that Slice 4 surfaces new requirements is low — RFC-002's proposed approach was already validated against the prototype's embedded data shape, and Issue #2's spec enumerates the field set exhaustively.
Strict schema (additionalProperties: false) is locked because the cost is one line per schema and the win is catching typos like agency_fl instead of agency_full at PR time.
Alternatives considered
- Defer to Slice 4 gate — original RFC-002 plan. Rejected: real risk of churning 28 mission files if any field changes; schema design is mature enough to lock now.
- Open schema (
additionalProperties: true) — allows experimentation. Rejected per ADR-019 spirit (validation should fail loudly at PR time). eventsoptional — simpler for mission types without timeline. Rejected: every Slice 4 mission has launch/arrival events; missions without them shouldn't appear in the mission library at all.
Consequences
Positive: schema locked before any mission JSON is written → zero rework risk; strict validation catches typos; events shape unambiguous for CAPCOM mode. Negative: Slice 4 may surface a need for an additional field; adding requires updating the schema + 28 files. Mitigation: schema is informed by both the prototype (complete) and Issue #2's exhaustive field list.
Implementation notes
Schema files live in data/schemas/:
mission.schema.json— base mission recordmission-index.schema.json—index.jsonentrymission-overlay.schema.json— locale overlay
Validator: scripts/validate-data.ts runs ajv against all base + overlay files. CI step blocks PR on schema failure (per ADR-019).