# Data coverage — the measured state of Phase 1

> **UPDATE 2026-08-28 — largely resolved.** The 23-place figure below was measured
> against Pleiades' *location date ranges*. Pleiades carries a second, far denser
> temporal signal — **attested periods** — which resolves through PeriodO to real
> years. Switching the ETL to that signal took the dataset to **62 places and
> recovered Babylon**. §§1–2 are kept because they explain *why* the obvious
> approach fails; §6 records what replaced it and what is still missing.

**This is the ROADMAP's named critical-path risk, now measured rather than feared.**

> *"Pleiades Bronze Age coverage may come in lower than expected — its strength is
> Greco-Roman. If under 100 usable places, supplement from AWMC and CDLI findspots
> before proceeding rather than widening the date envelope."* — ROADMAP, Phase 1

It came in lower. Substantially.

---

## 1 · The numbers

| | count |
|---|---|
| Pleiades places, all regions | 42,305 |
| …inside the v0.1 envelope (38–50°E, 28–38°N) | **1,692** |
| …with a JSON record in the current snapshot | 706 |
| …that parsed | 698 |
| …carrying **no date at all** | 56 |
| …dated but entirely outside −3300…−1200 | 619 |
| **…overlapping the Bronze Age envelope** | **23** |

**Target was ≥150. Actual is 23.** The Phase 1 gate as written is not met.

Median place start year in the envelope is **−30** — the Roman period. 537 of the
dated places begin after 750 BCE.

## 2 · The cause is not "few Bronze Age places". It is how Pleiades dates them.

This distinction decides what to do next, so it is worth being precise.

The Bronze Age cities are all **present** in Pleiades, with good coordinates. What
is missing is usable *periodisation*:

| City | Pleiades id | date range | verdict |
|---|---|---|---|
| Ur | `912985` | −4000 … −1 | ✅ usable |
| Uruk | `912986` | −4000 … −2000 | ✅ usable |
| Nippur | `912910` | −3000 … 640 | ✅ usable |
| Girsu | `912855` | −6500 … −1600 | ✅ usable |
| **Babylon** | `893951` | **none** | ✗ no dates at all |
| **Isin** | `912868` | **none** | ✗ no dates at all |
| **Kish** | `894028` | −720 … −540 | ✗ Iron Age only — wrong for a major Early Dynastic centre |
| **Sippar** | `894089` | −750 … 640 | ✗ sits on the Barrington Atlas floor |

Much of Pleiades' temporal data for this region derives from the *Barrington Atlas
of the Greek and Roman World*, whose coverage begins around 750 BCE. A Bronze Age
filter against those ranges does not select Bronze Age places — **it selects the
subset of Bronze Age places that happen to have been given deeper ranges by a
contributor.** That is an artefact of editorial history, not of antiquity.

> Filtering on Pleiades dates therefore excludes **Babylon** — the city the Old
> Babylonian band is named after. Any dataset built that way is wrong in a way
> that looks fine until someone who knows the period opens it.

## 3 · What follows

**Pleiades is authoritative for identity, coordinates, name variants and
citation. It is not authoritative for period.** The pipeline now reflects that
split, and the 23 records it emits are honest: every one has a real Pleiades id,
real coordinates, and a working source link.

What it must not do is hit the count by widening the envelope. Admitting −750
places into a Bronze Age dataset to reach 150 would make the timeline a lie, and
the ROADMAP already ruled it out.

### Options for periodisation, in preference order

1. **PeriodO** (CC0) — built for exactly this: reconciled period definitions with
   spatial and temporal extents. Already a planned Phase 1 input
   (`etl/fetch-periodo.js`). *Blocked from the build sandbox — `data.perio.do` is
   unreachable here; needs a run from a normal network.*
2. **Hand-curated band assignment for a core set** (~40–60 well-known sites), each
   with a citable reference. Slower, but this is the "30 well-sourced edges beat
   300 inferred ones" argument applied to places, and it is how Phases 4–5 already
   plan to work.
3. **AWMC / CDLI findspots** — the ROADMAP's own suggestion. CDLI findspots in
   particular are Bronze Age by construction, since they are where dated tablets
   were excavated.

**Not recommended:** inferring dates from `placeTypes` or from description prose.
It would manufacture precision from text that was never meant to carry it, and
nothing downstream could tell an inferred date from an attested one.

### Decision taken

Option 1, PeriodO. Implemented — see §6.

## 4 · Reproducing this

```sh
node etl/fetch-pleiades.js    # ~13s warm, writes etl/.cache/ (gitignored)
node etl/normalize.js         # prints the funnel counts in §1
node etl/validate.js
```

The counts in §1 are printed by `normalize.js` on every run, so this table can be
re-derived rather than trusted. If it drifts, the printed numbers are right and
this document is stale.

## 5 · A correction to `docs/data-contracts.md`

The fixture in that document gives Ur as `pleiades:912849`. **That id is the
Euphrates river.** Ur is `pleiades:912985`. The fixture's coordinates were
approximately right, which is what made it survive review — but the `sources[]`
URL it produces points at the wrong record.

This is invariant 2's failure mode exactly: a source link that resolves, looks
authoritative, and cites the wrong thing. Corrected in that file; recorded here
because "the fixture was wrong" is worth knowing when re-reading old branches.


---

## 6 · Resolution: attested periods via PeriodO

Pleiades attaches **period attestations** to a place's locations and names,
independently of any date range:

```json
{ "timePeriod": "old-babylonian-assyrian-mesopotamia",
  "timePeriodURI": "https://pleiades.stoa.org/vocabularies/time-periods/old-babylonian-assyrian-mesopotamia",
  "confidence": "confident" }
```

**692 of 698 places carry at least one.** They are unusable on their own because
they name a period rather than a range — and PeriodO is precisely the authority
that turns one into the other. It publishes Pleiades' own vocabulary, and each
definition carries `sameAs` holding the exact Pleiades vocabulary URI.

**The join is authoritative, not string matching.** `etl/fetch-periodo.js` joins
on `sameAs` ↔ `timePeriodURI`, normalising only the URI scheme (PeriodO stores
`http:`, Pleiades emits `https:`). No label slugging, no fuzzy matching.

### Result

| | date ranges | **attested periods** |
|---|---|---|
| Bronze Age places | 23 | **62** |
| Babylon | ✗ absent | ✅ `old-bab`, `kassite` |
| Kish | ✗ Iron Age only | ✅ all seven bands |
| Isin | ✗ no dates | ✅ six bands |
| `railWeight` spread | 0.71–1.25 (nearly flat) | 0.70–1.48 |

The `railWeight` change matters beyond the count: with 23 records the bands were
near-identical in width, so the era rail would have been density-proportional in
name only.

### Band membership is computed per period, never from the union span

A place attested only in Ubaid–ED II (−5500…−2600) and Later 2nd Millennium
(−1600…−1000) spans −5500…−1000, but is attested in **neither Akkadian nor
Ur III**. Deriving bands from that union invents two eras no source claims.

Mutation-tested: making the ETL use the union span fabricates Akkadian and Ur III
membership on **12 records**, and `scripts/verify-period-join.mjs` fails naming
each one. The record's `tStart`/`tEnd` is the envelope of its attested periods;
`bandIds` is the union of bands each period touches separately.

### Confidence is carried, not flattened

Pleiades grades attestations: `confident` (4,167 in this corpus),
`less-confident` (266), `confident-inferred` (84), `less-confident-inferred` (10).
Each period keeps its grade on the record, and each place carries a
`periodConfidence` summarising the strongest grade behind its Bronze Age
membership — so a place attested only by a hedged claim can be shown as such
rather than presented as settled.

## 7 · What is still missing

**Still short of the ≥150 target.** 62 is a 2.7× improvement and the gate is still
not met. The remaining supplements in §3 (CDLI findspots, hand-curated core sites)
are unchanged, and the 986 envelope places absent from the JSON snapshot are still
untested.

**Sippar is genuinely absent, and that is not a bug.** A major Old Babylonian city
with enormous tablet archives, its five Pleiades attestations are `archaic`,
`classical`, `hellenistic-republican`, `late-antique` and `modern` — no Bronze Age
attestation exists to find. Excluding it is correct behaviour; inventing a date
for it is not. It is a good candidate for the hand-curated supplement.

**The PeriodO source is a stale archive.** `data.perio.do` is unreachable from the
build sandbox, so `etl/fetch-periodo.js` falls back to the GitHub dump, which
upstream describes as "only of historical interest". The fallback is recorded in
`data/places.geojson` under `_periodisation` and printed loudly on every run.

28 period terms do not resolve against that archive. All but one are Iron Age or
later and therefore irrelevant here. The exception is **`uruk-mesopotamia`**,
which is squarely in the envelope. Measured impact: **no places excluded**, and
**one band assignment lost** — Girsu is missing its `uruk` band. Re-running
`etl/fetch-periodo.js` from a network that can reach `data.perio.do` should
resolve it. The unresolved terms are printed by name on every ETL run, so this
can never become silent.
