when-am-i / commit
feat: add new-calendar dataset-authoring skill
.claude/skills/new-calendar/SKILL.md | 114 +++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/.claude/skills/new-calendar/SKILL.md b/.claude/skills/new-calendar/SKILL.md new file mode 100644 index 0000000..8ab7256 --- /dev/null +++ b/.claude/skills/new-calendar/SKILL.md @@ -0,0 +1,114 @@ +--- +name: new-calendar +description: Author a new calendar region dataset file for "When am I?" end-to-end — research a place/people's calendar system(s) with sourced citations, draft the GeoJSON region polygon and eras with proleptic-Gregorian dates, choose the right converter (or descriptive-only), then validate, screenshot, and prepare a PR-ready contribution. Use when adding a calendar/region, contributing calendar data, or extending coverage to a new place or people. +--- + +# Authoring a new calendar dataset + +This skill walks you through creating one `src/data/calendars/<slug>.json` region +file for the "When am I?" app, the way a careful contributor would. The app's +framing is plural: *"When am I?"* is asked of **a person**, so a region's eras +collect **every valid answer** a local could give — overlapping calendars at the +same place and time are expected, not an error. + +**Read these first** — they are the source of truth, defer to them over this skill: +- `src/data/calendars/SCHEMA.md` — the human-readable field docs. +- `scripts/validate-data.mts` — the authoritative machine-checkable schema. +- `src/utils/calendars.ts` — which calendar strings are **convertible** (have a converter). +- An existing file as a template, e.g. `src/data/calendars/iran.json` (two eras, + two converters) or `src/data/calendars/massachusett.json` (descriptive-only + + an `anchorEvents` "winters since" reckoning). + +## The one rule that overrides everything: never invent + +This dataset is only as trustworthy as its sources. **Do not invent** events, +moon names, era boundaries, dates, or polygons. Every era and every anchor event +must trace to a real, citable source (Wikipedia is acceptable as the project's +*acceptance* gate). If you cannot source a claim, leave it out and say so. When a +fact is uncertain or contested, prefer the conservative version and note it in +the `description`. If you find yourself guessing a date or a boundary, stop. + +## Workflow + +### 1. Gather inputs +Ask the user (or confirm) the **place** and the **people/polity** — e.g. "the +Mughal Empire", "Coast Salish around Puget Sound", "Joseon Korea". The answer is +about *whose* reckoning it is, so be specific: "Algonquian" is a language family, +not one calendar; pick the actual nation. + +### 2. Research the calendar system(s) — with sources +For that place + people, determine: +- Which calendar system(s) were used, and over **which date ranges** (era + boundaries). Multiple eras are normal (e.g. a switch from a lunar to a solar + civil calendar, or a Julian→Gregorian cutover). +- **Whose** answer each is (`usedBy`), e.g. "court and civil administration", + "Orthodox church", a specific people. +- Real, dateable sources for each. Collect the URLs as you go. + +Use WebSearch/WebFetch to research; cite what you actually read. + +### 3. Choose the calendar string (convertible vs descriptive-only) +This is **two independent gates** (see plan.md, Q3): +- **Acceptance** — a sourced wiki page is enough to include the calendar at all. +- **Convertibility** — whether a deterministic epoch+rule yields a numeric local + year. This is decided by `src/utils/calendars.ts`: + - **Convertible** strings hit a converter there: `Gregorian`, `Julian`, and the + Temporal-backed `Islamic`, `Hebrew`, `Persian`, `Indian`/`Saka`, `Buddhist`, + `Japanese`, `Ethiopic`, plus the hand-rolled `Chinese sexagenary cycle`. + Match an existing string **exactly** (the lookup is case/space-insensitive). + - **Descriptive-only** — a calendar with no continuous numbered year (e.g. an + observational lunar calendar). Use a clear `calendar` name; the engine shows + the era's name/description/sources + the Gregorian reference year and a + "no numbered year" note. Never mislabel a descriptive calendar as convertible. +- If a genuinely new convertible system is needed, that's converter work in + `src/utils/calendars.ts` (a Temporal `calendarId` if Intl supports it, else + hand-rolled). **Caveat:** the Temporal `chinese` calendar is broken in + `@js-temporal/polyfill@0.5.1` (throws on every date) — that's why China uses a + hand-rolled `Chinese sexagenary cycle` converter instead. Verify any new + Temporal calendar actually converts before relying on it. + +### 4. Draft the region polygon +GeoJSON `Polygon` (or `MultiPolygon`), coordinates as **`[lng, lat]`** (longitude +first — easy to get backwards), outer ring **closed** (first position repeated as +last), ≥4 positions. Keep it a reasonable approximation of the territory; the +validator checks ranges (lng −180..180, lat −90..90) and closure, not accuracy — +that's the reviewer's job against the screenshot. geojson.io is handy for drafting. + +### 5. Add `anchorEvents` for descriptive-only calendars (optional) +For a calendar with no numbered year, an `anchorEvents` entry lets a local place +you in time as "about N winters since <event>". Each event needs a `name`, a +proleptic-Gregorian `date`, and **at least one source** — these must be real, +documented events (e.g. the 1616–1619 New England epidemic), never invented. + +### 6. Write the file +Match the shape of an existing file exactly. Dates (`start`/`end`, event `date`) +are **proleptic Gregorian** `YYYY-MM-DD`; `null` start/end means open-ended; +intervals are half-open `[start, end)` so a cutover date is the `end` of the old +era and the `start` of the new one. Fill in `usedBy`, `description`, `sources`. + +### 7. Validate, test, screenshot — make it PR-ready +Run, and fix anything that fails: +```bash +npm run validate:data # schema + geometry + dates +npm run test # converter / engine tests still green +npm run gen:badges # refresh the "calendar data" count badge +npm run screenshot:calendar -- src/data/calendars/<slug>.json +``` +The screenshot step pins a point inside the region at a representative date per +era and captures the answer panel + shaded map into +`screenshots/calendars/<slug>/`, so a reviewer can *see* a wrong polygon, an +off-by-a-century era boundary, a miscategorized convertible/descriptive call, or +a broken conversion. Attach these to the PR. + +### 8. If you added or changed a converter +Add unit tests in `src/utils/calendars.test.ts` with a known date↔year pair, and +update the convertible-calendars list in `README.md` and this skill's step 3. + +## Definition of done +- `npm run validate:data` and `npm run test` pass. +- Every era and anchor event is sourced; nothing is invented. +- Convertible-vs-descriptive is correct and the conversion looks right in the + screenshot. +- README's convertible-calendar list and the badge count are current. +- **Do not commit.** Per repo policy, hand the file + screenshots to the human to + review against the sources, and let them commit.