RFC-016 — Spaceflight Fleet · architecture, schema, and dataset boundaries
Orrery · Open RFC · v0.2 · May 2026
Status: Draft Author: product Closes into: ADR-052 (fleet schema + bidirectional cross-reference contract), ADR-053 (mission badge + crew portrait sourcing), ADR-054 (fleet i18n strategy) Slice gate: v0.6 Why this is an RFC: PRD-012 promises a curated ~110-entry inventory of the machines used in spaceflight — launchers, crewed spacecraft, cargo spacecraft, stations, rovers, landers, orbiters, observatories — each cross-linked to the existing
missions.json/iss-*/tiangong-*/earth-objects/moon-sites/mars-sitesdata bidirectionally (so Apollo 11's mission panel links to Saturn V on /fleet, and the Gale Crater marker on /mars links to Curiosity on /fleet, and back). The technical questions cluster five ways: schema (per-generation granularity inflates entries; what's the source-of-truth contract between fleet and the existing datasets?); cross-route bidirectional linking (every primary route gains afleet_refsfield; build-time symmetric validation must catch drift); imagery + crew badges (mission patches and crew portraits per notable flight, plus full hand-authored anatomy SVGs); epoch UI (timeline strip + named-epoch chip filter); i18n + scope discipline (110 entries × 14 locales = 1,540 overlay files at full parity).v0.2 additions: per-generation granularity (Q3 lock); failure entries included (Q5 + "not all about success"); cross-route bidirectional linking (mission ↔ fleet, surface markers ↔ fleet, earth-objects ↔ fleet); epoch timeline + named-epoch chip; full hand-authored SVG anatomy per fleet entry as the headline original contribution. OQ-2 fact error from v0.1 corrected (missions don't have a structured
launch_vehiclefield today;fleet_refsmigration is locked in instead).
Context
Orrery has six hardware datasets today:
| Dataset | Path | Records | Purpose |
|---|---|---|---|
rockets.json | static/data/rockets.json | 13 | Launcher specs (used by /missions and /plan) |
iss-modules + iss-visitors | static/data/iss-*.json + i18n | 18 + 7 | Pressurised modules + visiting fleet |
tiangong-modules + tiangong-visitors | static/data/tiangong-*.json + i18n | 3 + 2 | Mirror of the ISS shape |
moon-sites.json | static/data/moon-sites.json | ~20 | Lunar landing sites (rover + lander as payload strings, not first-class) |
mars-sites.json | static/data/mars-sites.json | ~12 | Mars landing sites (similar shape to moon-sites) |
earth-objects.json | static/data/earth-objects.json | ~30 | LEO satellites (Hubble, JWST currently live here as orbital objects) |
Today, "what hardware exists" is fragmented across these six files plus the 36 missions/* records. A user looking for Saturn V finds the launcher in rockets.json and the missions it launched in missions/. A user looking for Curiosity finds the rover encoded as a payload: "Curiosity" string on a Mars site, with no first-class entry. A user looking for Hubble finds it as an earth-objects orbital object. Nothing today gives the unified hardware-inventory view PRD-012 asks for.
The simplest aggregator is a new fleet.json (with locale overlays) that becomes the canonical hardware-family entry and references the existing data via cross-link IDs. The existing datasets remain authoritative for their scoped purpose — rockets.json keeps its launcher specs that /plan reads at runtime; iss-modules keeps its station-specific module attributes that /iss reads. Fleet entries copy what they need into a flatter schema for the fleet-card grid + detail panel.
PRD-012's V1 lock (post-v0.2 update): ~110 curated entries (per-generation granularity), crew + badges in V1, failure entries included alongside successes, route at /fleet between TIANGONG and SCIENCE in the nav, bidirectional cross-route linking with /missions / /moon / /mars / /earth, epoch timeline strip + named-epoch chip filter, hand-authored SVG anatomy per entry (tiered V1/V1.5/V2 rollout) as the original-content contribution.
Open Questions
OQ-1 — One unified fleet.json vs per-category files?
PRD-012 lists 8 categories. Two structural options:
| Option | Layout | Pros | Cons |
|---|---|---|---|
A. Single fleet.json with all 50 entries | One ~2,000-line file mirroring missions/index.json | Easiest to validate, easiest to filter (single load), matches /missions precedent | Heterogeneous schema fields per category creep into one document |
B. Per-category files under static/data/fleet/{category}/{id}.json + fleet/index.json | Mirrors the existing missions/{dest}/{id}.json pattern | Lets per-category schema variation breathe; smaller files | More fetches, more validation surface |
C. Single index + per-entry detail like missions/index.json + missions/mars/curiosity.json | Index has summary fields; detail file has full record | Lazy-loadable, scales to V2 (~150 entries) | More files than A, more plumbing than A |
Recommendation: C — mirror the proven /missions pattern. static/data/fleet/index.json carries the 50-entry summary (id, name, category, agency, era, first-flight year, status, hero-image path, tagline). static/data/fleet/{category}/{id}.json carries the per-entry detail (specs, linked_missions, links, gallery manifest, crew). Locale overlays live at static/data/i18n/{locale}/fleet/{category}/{id}.json — same shape as missions. The category-keyed directory makes new entries discoverable by humans editing the repo.
This locks into ADR-052 alongside the cross-reference contract.
OQ-2 — Cross-reference contract: how do fleet entries link to missions? [CORRECTED v0.2]
v0.1 fact error: claimed missions already had a structured
launch_vehicleID. They don't — missions only carry a free-textvehicle: "Saturn V (AS-506)"display field. Corrected below.
A Saturn V fleet entry needs to surface the 13 Apollo + Skylab missions it launched. The MISSIONS tab in the fleet detail panel reads these from the fleet record and renders them as clickable mini-cards. Conversely, the Apollo 11 mission panel must render "Hardware: [Saturn V →] [Apollo CSM →] [Apollo LM →]" chips linking back to fleet entries.
Locked decision: full bidirectional cross-reference via new structured field.
Schema change
Add a new fleet_refs field to the mission base schema (and to moon-sites / mars-sites / earth-objects — see OQ-9 for the cross-route extension):
type FleetRef = { id: string; role: 'launcher' | 'spacecraft' | 'payload' | 'station' };
type Mission = { ...existing fields, fleet_refs?: FleetRef[] };Apollo 11 example:
{
"id": "apollo11",
"fleet_refs": [
{ "id": "saturn-v", "role": "launcher" },
{ "id": "apollo-csm-block-ii", "role": "spacecraft" },
{ "id": "apollo-lm", "role": "spacecraft" }
]
}Migration: ~36 mission base files get a hand-curated fleet_refs array (Phase E of Issue #59).
Fleet-side reverse pointer
Fleet entries keep linked_missions: [...] as the curated reverse list — used to render the MISSIONS tab in fleet panels.
Build-time symmetric check
scripts/validate-data.ts:
- Every
mission.fleet_refs[*].idmust exist infleet/index.json. (Build fails if not.) - Every
fleet_entry.linked_missions[*]must exist inmissions/index.json. (Build fails if not.) - Bidirectional integrity: if a mission has
fleet_refscontaining{id: "saturn-v", role: "launcher"}, then Saturn V'slinked_missionsMUST include that mission. (Build fails on drift.)
Why bidirectional in V1 (changed from v0.1)
The "see also: Saturn V" affordance from the mission panel is the discovery path users will use to enter /fleet. Deferring to V1.5 leaves /fleet as a one-way cul-de-sac — users find it via nav, not via missions. Bringing it forward to V1 makes /fleet feel native to the rest of the app. The schema cost is small (one new array field on missions); the validation cost is one symmetric loop in validate-data.
This locks into ADR-052.
OQ-3 — Mission badges + crew portraits: where do they come from?
PRD-012 V1 includes crew badges + crew portraits for crewed-spacecraft entries. Concrete sourcing:
| Asset | Source | Example | License |
|---|---|---|---|
| Mission patches | Wikimedia Commons (NASA-released originals are PD-USGov; some Roscosmos / CNSA require careful vetting) | Apollo 11 patch, STS-1 patch, Demo-2 patch | PD-NASA / Roscosmos-released / CNSA-released |
| Crew portraits | Agency archives (NASA Image Library, ESA Photo Library, JAXA Library, Roscosmos), mirrored via Wikimedia Commons where available | Yuri Gagarin (Roscosmos archive), Neil Armstrong (NASA), Liu Yang (CMSA) | PD-USGov for NASA / fair-use risk for non-Western agencies |
The license-vetting pattern is exactly the existing pipeline (ADR-046 / ADR-047): every image gets a row in static/data/image-provenance.json, every license short-name appears in scripts/license-allowlist.ts or in static/data/license-waivers.json, the build fails closed if anything is unattributed.
Sourcing approach:
| Option | How | Trade-off |
|---|---|---|
| A. Wikimedia-only | Fetch all imagery via the existing Wikimedia Commons fetch script | Easiest license vetting; some images may not be on Commons |
| B. Agency-first (per ADR-046) with Wikimedia fallback | Crew portraits prefer agency archives; patches prefer Wikimedia (since patches are typically uploaded there in canonical form) | Matches imagery-sourcing ADR-046 exactly; one extra agency-API integration per agency |
C. Hand-curated + manual upload to static/images/fleet/ | Author selects each image, downloads, attributes, commits | Most editorial control; most authoring effort |
Recommendation: B — extend the existing scripts/fetch-assets.ts with a fleet-imagery branch that mirrors the agency-first priority from ADR-046. Crew portraits route through agency archives first (NASA Image Library is well-documented with stable URLs; ESA / JAXA have similar APIs). Patches route through Wikimedia Commons (where official patches are uploaded in canonical form, which is the convention agencies themselves rely on). Failure to find an image at agency/Commons → entry can ship without that image (graceful no-op in the CREW tab, not a build-failure).
This locks into ADR-053.
OQ-4 — Detail panel reuse vs new component?
PRD-012 §what-it-must-do specifies tabs OVERVIEW / GALLERY / ANATOMY / CREW / MISSIONS / LEARN. The existing StationModulePanel.svelte carries OVERVIEW / GALLERY / ANATOMY / LEARN already. Two new tabs (CREW, MISSIONS) need to be added.
| Option | Implementation |
|---|---|
A. Extend StationModulePanel.svelte with conditional CREW + MISSIONS tabs | Single component, props gate which tabs render. Reuse all existing styling + tab nav |
B. New FleetEntryPanel.svelte copy-pasted from StationModulePanel | Cleaner separation; no risk of breaking /iss or /tiangong |
C. Generic EntityDetailPanel.svelte that both stations and fleet use | Refactor towards a unified component |
Recommendation: B for V1, C as a refactor in v0.7. Copy StationModulePanel.svelte to FleetEntryPanel.svelte and add the two new tabs. The two existing station panels carry station-specific concerns (microgravity-axes integration, station context links) that don't apply to a Saturn V fleet entry; cleaner separation now, generalise later when the third use case proves the pattern. A small style-token extraction can land alongside (drawer chrome, tab nav) without committing to a full unified component.
ANATOMY tab reuses the same SVG schematic infrastructure shipped in May 2026 (the 9 spacecraft diagrams). Fleet entries point to static/diagrams/fleet/{id}.svg if available; tab no-ops if not authored. CREW tab is new — list of mission patches + crew portraits per flight, organised chronologically.
This locks into the V1 implementation strategy (no ADR needed; component-level decision).
OQ-5 — i18n: 50 entries × 14 locales = 700 overlay files. Same Tiangong rollout strategy?
PRD-011 / Tiangong Explorer rolled out 5 entries × 14 locales = 70 overlay files via auto-translate (DeepL / Claude with human review) per ADR-017 / ADR-033. Fleet is 10× the volume.
| Option | Scope | Cost |
|---|---|---|
| A. en-US only at launch, rely on en-US fallback (per ADR-017 fallback) | 50 files | Smallest scope; non-English users see English fleet content |
| B. en-US + es (mirrors existing ADR-017 baseline locales) | 100 files | 2× scope; covers the existing two-locale baseline |
| C. All 14 locales (matches Tiangong / ISS rollout) | 700 files | 14× scope; full i18n parity |
Recommendation: B for V1 launch, C as a follow-up slice. Ship en-US + es locked-in; the Spanish translation pipeline is well-tested. The remaining 12 locales (de / fr / ja / it / ko / nl / pl / pt / ru / sv / tr / zh) follow in a v0.6.x slice after V1 ships, using the same auto-translate + spot-check pattern as Tiangong (ADR-033). This keeps the V1 surface tight while acknowledging the eventual full-locale parity goal.
This locks into ADR-054.
OQ-6 — Filter chip taxonomy: which axes?
PRD-012 §what-it-must-do specifies CATEGORY / AGENCY / ERA / STATUS chips. Concrete sets:
| Chip | Values |
|---|---|
| CATEGORY | launcher / crewed-spacecraft / cargo-spacecraft / station / rover / lander / orbiter / observatory |
| AGENCY | NASA / Roscosmos / ESA / JAXA / CNSA / ISRO / SpaceX / Northrop Grumman / Boeing / multi (e.g., ISS) |
| ERA | 1957–1969 (Sputnik–Apollo 11) / 1969–1981 (post-Apollo–Shuttle debut) / 1981–2011 (Shuttle era) / 2011–now (post-Shuttle modern era) / planned |
| STATUS | FLOWN / ACTIVE / RETIRED / PLANNED |
Era boundaries chosen by inflection points (first crewed orbital → first lunar landing → Shuttle debut → Shuttle retirement → modern crewed-commercial era). Not "decades", because the decades-by-tens cuts cross meaningful technology shifts (Apollo straddles the 60s/70s boundary).
Recommendation: ship as specified. No new ADR — these are app-level UX decisions encoded in /fleet/+page.svelte.
OQ-7 — Where does Fleet sit in the nav?
PRD-012 proposes ... → MARS → FLEET → SCIENCE. Final order:
EXPLORE → MISSIONS → PLAN → FLY → EARTH → MOON → MARS → ISS → TIANGONG → FLEET → SCIENCE
(Note: per recent commit 5833415, ISS / TIANGONG moved to the last menu slots before SCIENCE; FLEET inserts between TIANGONG and SCIENCE so the inventory-style screens cluster: ISS → Tiangong → Fleet → Science.)
Recommendation: insert between TIANGONG and SCIENCE. Both stations and Fleet are inventory-shaped pages (cards / detail panels); placing them adjacent groups the catalogue cluster cleanly, with SCIENCE remaining the educational tail.
This locks into ADR-052 alongside the schema decision.
OQ-8 — Hero-image strategy for the card grid?
110 cards each need a hero photo. The existing /missions grid uses agency-first imagery via ADR-046; same pattern ports.
Recommendation: reuse the existing pipeline. static/images/fleet/{id}.jpg resolved at build time via scripts/fetch-assets.ts. Per-image provenance row in image-provenance.json (ADR-047). No new ADR needed.
OQ-9 — Embed vs reference: how do fleet entries relate to canonical hardware data already in rockets.json / earth-objects.json / moon-sites.json / mars-sites.json?
Existing hardware data overlap (PRD-012 §why):
- Saturn V's specs already in
rockets.json(used by /plan). - Hubble + JWST already in
earth-objects.json(used by /earth). - Curiosity / Perseverance encoded as free-text
payloadstrings onmars-sites.jsonentries. - Lunokhod / Apollo LRV likewise encoded on
moon-sites.jsonentries.
| Option | Layout | Trade-off |
|---|---|---|
| A. Reference at runtime | Fleet entries store IDs only; specs/photos pulled from canonical source at fetch-time. Saturn V's mass comes from getRocket('saturn-v') | Zero drift; more plumbing in data.ts (fleet entry composes from multiple sources) |
| B. Embed (copy fields) | Fleet entries are flat self-contained records. Build-time validator asserts copied fields match source-of-truth (catches drift) | Lighter at fetch-time; simpler component code; explicit drift check covers correctness |
| C. Migrate (fleet replaces overlapping data) | rockets.json becomes thin /plan-only file (or deleted). Curiosity becomes a fleet entry, removed from mars-sites payload string | Cleanest long-term; biggest refactor surface |
Locked: B — embed with build-time drift validation. Fleet records are flat and self-contained. scripts/validate-data.ts extended: for each fleet entry that overlaps a canonical source (e.g., a launcher with a matching rockets.json ID), asserts copied fields match within tolerance. Drift fails the build.
This locks into ADR-052.
OQ-10 — Variant granularity: family or generation?
| Option | Example |
|---|---|
| A. One entry per family | "Soyuz" = single entry covering 7K-OK → T → TM → TMA → MS (60 years) |
| B. One per generation/variant | "Soyuz 7K-OK", "Soyuz T", "Soyuz TM", "Soyuz TMA", "Soyuz MS" as separate entries |
| C. Family entry + variant timeline tab | Single Soyuz card with TIMELINE tab showing variants chronologically |
Locked: B — one per generation/variant. Inflates the entry count from ~50 to ~110 but honours the historical record (Soyuz T flew very different missions than Soyuz MS; Apollo CSM Block I and Block II had different fates). Entry-count budget grew accordingly per Q5.
Apollo CSM splits into Block II + Skylab CSM + ASTP CSM. Long March splits into 2C / 2F / 3B / 5/5B / 7. Cygnus splits into Standard + Enhanced. Crew Dragon stays single (one variant currently flying). Cargo Dragon splits v1 / v2.
This locks into ADR-052 alongside the schema decision.
OQ-11 — Crew data scope per crewed-spacecraft entry
PRD-012 V1 includes crew portraits + mission patches for each crewed-spacecraft entry. How many flights?
| Option | Scope |
|---|---|
| A. Notable-flights curated | 3–6 hand-picked flights per spacecraft (first flight, last flight, 1–4 historic ones). Soyuz: e.g., Soyuz 1 (Komarov), Soyuz 11 (Salyut 1), TM-32 (Tito), MS-25 (recent). |
| B. Every flight, paginated | Soyuz family covers ~150+ flights. Heavy authoring + image-fetch load. |
| C. Crew-of-first-flight only | Single block per spacecraft: inaugural mission's crew. Smallest scope. |
Locked: A — notable-flights curated. 3–6 per spacecraft, ~15 crewed entries × ~4 avg = ~60 flight-blocks total. Each block surfaces the mission patch + crew portrait grid + cross-link to that flight's /missions entry. Manageable scope; reads as "highlights" rather than encyclopedia.
This locks into ADR-053.
OQ-12 — Stations with explorer routes (ISS, Tiangong): full panel or redirect?
| Option | Behaviour |
|---|---|
| A. Full panel + "Open Explorer →" CTA | ISS card opens fleet detail panel (specs / gallery / crew / missions / learn). Top of OVERVIEW tab has prominent "Open ISS Explorer →" deep-link to /iss. |
| B. Direct redirect (no panel) | Click ISS card → navigates to /iss immediately, no fleet panel. |
| C. Skip from /fleet entirely | ISS / Tiangong don't appear on /fleet. Stations category becomes ~4 entries (Salyut 1, Skylab, Mir, Tiangong-1). |
Locked: A — full panel + CTA. Consistent UX across all 110 cards; the dedicated explorer route is one click away. ISS overview specs (assembly start, mass, orbit, crew complement) live in fleet; deep station-module exploration lives at /iss. No content duplication — fleet's OVERVIEW tab and /iss serve different reading depths.
This locks into the V1 implementation strategy.
OQ-13 — ANATOMY SVG authoring strategy: reuse path + tiered rollout
PRD-012 commits to hand-authored anatomy SVGs for every fleet entry in the canonical /science chapter style (white-on-dark, monospace labels, viewBox 400×320, teal accents) — this is the headline original-content contribution that distinguishes /fleet from a Wikipedia-clone catalogue.
Path: existing 9 station-visitor SVGs at /diagrams/spacecraft/{id}.svg are reused as-is (mapping fleet IDs to file names). New entries go into the same folder, making /diagrams/spacecraft/ the canonical "spacecraft anatomy" location across the app. No /diagrams/fleet/ duplication.
Tiered rollout: ~110 SVGs is too much for a single V1. Split into authoring tiers:
| Tier | When | Scope | Approx. count |
|---|---|---|---|
| F.1 — Reuse | V1 (already shipped) | The 9 station-visitor diagrams | 9 |
| F.2 — Marquee tier | V1 launch | Highest-iconic entries: Saturn V, Shuttle stack, Apollo CSM, Apollo LM, Hubble, JWST, Curiosity, Perseverance, Voyager (1+2 share or split), Cassini, Mir, Salyut 1, ISS, Tiangong, R-7/Vostok, Falcon 9, Crew Dragon (already), Soyuz 7K-OK + Soyuz MS (already), Long March 5, Falcon Heavy, SLS, Lunokhod-1, LRV (Apollo) | ~25–30 |
| F.3 — Per-category coverage | V1.5 | Fill in ~5–10 more per category that aren't yet drawn | ~30 |
| F.4 — Long tail | V2 | Remaining ~50 entries (lower-priority orbiters, observatories, smaller landers) | ~40 |
V1 ships with F.1 + F.2 = ~35 SVGs. ANATOMY tab no-ops gracefully on entries without an SVG yet (per RFC-016 OQ-4 lock).
Validation: existing validate-diagrams.ts (chained into validate-data) extends to cover fleet diagrams.
This locks into the V1 implementation strategy.
OQ-14 — Default sort order on the card grid?
| Option | Behaviour |
|---|---|
| A. Chronological ascending (oldest first) | R-7 (1957) at top, SLS (2022) toward bottom. Reads as a story from Sputnik onward. |
| B. Chronological descending (newest first) | Falcon 9 / Crew Dragon / Perseverance / JWST at top, historical further down. Page feels current. |
| C. Category-grouped | Cards group under category headers, chronologically within each. |
Locked: B — chronological descending. Page feels current and relevant on first load. Matches /missions and most card-grid conventions in the app. User can flip to ascending via sort controls if they want the "story from Sputnik" reading.
This locks into UX-level decision (no ADR).
OQ-15 — Failure entries: separate STATUS or overlay on FLOWN?
PRD-012 commits to "every machine, every outcome" — N1, Buran, Schiaparelli, Beresheet, Hakuto-R, Vikram CY-2, Mars 2, Hitomi all included. UX question: where does FAILED sit in the STATUS chip set?
| Option | STATUS values | Filter behaviour |
|---|---|---|
| A. FAILED as separate value | FLOWN / ACTIVE / RETIRED / FAILED / PLANNED | User can filter to FAILED to tour the redemption-arc layer of spaceflight history. Most explicit. |
| B. FAILED as overlay on FLOWN | FLOWN+failed-flag / ACTIVE / RETIRED / PLANNED | Card shows red "FAILED" badge atop FLOWN; filter chips don't change. Cleaner taxonomy but no "show me only failures" affordance. |
| C. PARTIAL-SUCCESS sub-distinction | Add PARTIAL alongside FAILED for missions like IM-1 (sideways landing, partial success) | Most granular. |
Locked: A — FAILED as separate STATUS value. The "tour the failures" reading is exactly what the framing asks for. Card chip shows FAILED in red distinct from FLOWN. PARTIAL-SUCCESS handled inside the editorial copy of those entries (e.g., IM-1's tagline notes "first US lunar landing since Apollo, sideways orientation").
This locks into UX-level decision (no ADR).
OQ-16 — Cross-route bidirectional linking: how far does it reach?
PRD-012 commits to fleet becoming the canonical hardware reference. Bidirectional links from:
- Missions → fleet (via new
fleet_refsfield, locked in OQ-2) - Moon-sites → fleet (which hardware landed at this site)
- Mars-sites → fleet (which rover/lander operates here)
- Earth-objects → fleet (Hubble's earth-objects entry → fleet hubble entry)
- Fleet entries → all of the above (via reverse
linked_sites: [...]array)
| Option | Scope |
|---|---|
| A. Full bidirectional across all 4 contexts (locked) | Each route's panel renders fleet chip(s); fleet panel renders contextual back-links to /missions / /moon / /mars / /earth |
| B. One-way only (mission → fleet) | Fleet stays a destination, no back-links. |
| C. Defer to V1.5 | V1 ships with /fleet alone; cross-links land later. |
Locked: A — full bidirectional across all 4 contexts. Same fleet_refs: [{id, role}] field on missions / moon-sites / mars-sites / earth-objects entries. Same linked_sites: [...] reverse array on fleet entries. Build-time validator enforces symmetric integrity (drift fails the build).
This is Phase K in the Issue #59 implementation tracker.
This locks into ADR-052.
OQ-18 — airandspacehistory.com as a first-party photo source
Marko owns + operates airandspacehistory.com, a curated archive of original museum photography from Russia, USA, UK, France, Germany, Netherlands museums (Smithsonian / Memorial Museum of Cosmonautics / Science Museum London / Musée de l'Air et de l'Espace / Speyer / Aviodrome / etc). Many fleet entries have hardware that's been photographed in those collections. Including those photos in fleet galleries:
- Adds unique value to /fleet — first-party museum-grade composition that's not available on Wikimedia or in agency press kits. Differentiates Orrery's fleet panels from a Wikipedia-clone catalogue.
- Drives back-traffic to airandspacehistory.com — each photo links to its source page on the site. Natural cross-promotion of two products in the same author's portfolio.
- Provenance is straightforward — Marko is the photographer + site operator; license is "© airandspacehistory.com — used with permission" (self-grant). Goes into
static/data/license-waivers.jsononce with a single explicit waiver row.
Sourcing priority (extends ADR-046):
airandspacehistory.com— checked first, used when available- Agency archives (NASA / ESA / JAXA / Roscosmos / CMSA per ADR-046)
- Wikimedia Commons fallback
Implementation:
- Extend
scripts/fetch-assets.tswith anairandspacehistory.combranch that scrapes structured photo metadata (title, hardware-id mapping, photo URL, page URL) — needs a small index file or sitemap on the airandspacehistory.com side. - Each fetched photo lands in
static/images/fleet-galleries/{id}/N.jpgwithcredit: "© airandspacehistory.com"andsource_url: "https://airandspacehistory.com/..."in the per-image provenance row. - Gallery rendering shows credit chip + clickable link to source page.
Open detail: format of the airandspacehistory.com sitemap / photo index for the fetcher to consume. Three options:
- A. Custom JSON manifest at
https://airandspacehistory.com/orrery-photos.json— cleanest, requires manual curation per photo - B. RSS / sitemap.xml mining — works automatically but pulls everything
- C. Hand-curate a list in this repo (
static/data/airandspacehistory-curation.json) mapping{hardware_id}→[photo URLs]— most explicit; avoids airandspacehistory.com side complexity
Recommendation: C for V1 (zero cross-site dependency), promote to A in V1.5 if the curation list grows beyond ~50 photos.
This locks into ADR-053.
OQ-17 — Epoch UI: timeline strip + named-epoch chip
PRD-012 commits to 8 named epochs replacing the original 5-bucket date-range ERA chip:
First Steps · Space Race · Lunar Era · First Stations · Shuttle & Mir · ISS Assembly · Commercial Era · Lunar Return
| Option | UI |
|---|---|
| A. Just rename ERA chip values | Chip labels become "Lunar Era", "First Stations", etc. instead of "1969–1981". Behaviour unchanged. Simplest. |
| B. Named chip + timeline strip (locked) | Above the card grid, a horizontal scrub-bar timeline shows the 8 epochs as labelled bands with entry-count tooltips. Click an epoch to filter. Today indicator on the strip. Mobile: collapses to a horizontal swipe carousel. |
| C. Pure timeline (no chips) | Epoch chip removed; timeline strip is the only filter affordance. Cleaner but reduces filter discoverability. |
Locked: B — named chip + timeline strip. The chip is the persistent filter affordance; the timeline strip is the rich-context overview that shows where the user is on the historical axis. Cards can fade-in/out as the user drags the timeline scrub. Strip is read-only on mobile (tap to filter, no drag).
This locks into UX-level decision (no ADR).
Decision criteria
We accept the resolution when:
- Schema validates.
validate-data.tsenforcesfleet/index.json+ per-entry shape; everylinked_missionsID resolves to an existing mission; every fleetidreferenced frommissions[*].launch_vehicleexists in fleet. - 50 cards render. Card grid loads, filter chips work, deep-link
?id=saturn-vopens the detail panel pre-selected. - Cross-references both ways. Saturn V's MISSIONS tab lists the 13 Apollo + Skylab missions; clicking Apollo 11 deep-links to
/missions?id=apollo11cleanly. - Crew tab renders for crewed-spacecraft entries with at least one mission patch + crew portrait per flight (≥10 entries, varying counts of flights each). Provenance complete in
image-provenance.json. - Anatomy tab no-ops gracefully for entries without an authored SVG (most launchers initially) — UI shows a single placeholder line, not a broken tab.
- i18n parity en-US + es at launch. Remaining 12 locales tracked in a follow-up issue, ships in a v0.6.x slice.
- Mobile-first. 375 px width renders without horizontal scroll; bottom-sheet detail panel works as expected.
?view=listfallback — same ADR-042 pattern. Card grid is plain DOM already, but the contract is consistent.- Provenance complete. Every image, every external link recorded in
image-provenance.json/ link-provenance pipeline (ADR-047 / ADR-051). Fail-closed at build.
Closure path
- ADR-052 — Fleet schema + bidirectional cross-reference contract: index + per-entry files under
fleet/{category}/{id}.json; newfleet_refsfield on missions / moon-sites / mars-sites / earth-objects with symmetric build-time validation;linked_missions+linked_sitesreverse arrays on fleet entries; embed-with-drift-check vs the four canonical hardware datasets (rockets, earth-objects, moon-sites, mars-sites); nav slot between TIANGONG and SCIENCE. - ADR-053 — Imagery sourcing extended: airandspacehistory.com first-party photos with self-grant license waiver, agency-first per ADR-046 (NASA / ESA / JAXA / Roscosmos / CMSA archives), Wikimedia Commons for canonical patches + fallback. Crew portrait scope: 3–6 notable flights per crewed-spacecraft entry.
- ADR-054 — Fleet i18n: en-US + es at V1 launch (~110 entries × 2 = 220 overlay files); remaining 12 locales via the existing ADR-033 auto-translate pipeline in a v0.6.x slice.
v0.2 locked decisions (added since v0.1):
- OQ-2 corrected: bidirectional
fleet_refsmigration on ~36 mission base files, validated symmetrically at build (was: deferred to V1.5). - OQ-9: embed-with-drift-check against rockets.json / earth-objects.json / moon-sites.json / mars-sites.json (was: not addressed).
- OQ-10: per-generation granularity (was: family-level).
- OQ-11: notable-flights-curated crew scope (was: not addressed).
- OQ-12: full panel + "Open Explorer →" CTA for ISS / Tiangong (was: not addressed).
- OQ-13: hand-authored anatomy SVG per fleet entry, tiered F.1 / F.2 / F.3 / F.4 rollout (was: ~6–8 new SVGs).
- OQ-14: chronological descending default sort (was: chronological ascending).
- OQ-15: FAILED as separate STATUS chip value (was: not addressed; PRD-012 now includes failure entries).
- OQ-16: full bidirectional cross-route linking on /missions / /moon / /mars / /earth (was: deferred to V1.5).
- OQ-17: epoch timeline strip + 8 named-epoch chip (was: 5 date-range buckets only).
- OQ-18: airandspacehistory.com as first-party photo source (was: not addressed).
Deferred to v0.7+:
- Unified
EntityDetailPanel.svelterefactor across stations + fleet (OQ-4 option C). - Full 14-locale i18n parity (OQ-5 option C).
- F.4 long-tail anatomy SVG completion (Phase F long tail).
Orrery · RFC-016 · v0.2 · Open. Update on closure with the ADR numbers landed.