when-am-i / files / .claude / skills / new-calendar / SKILL.md
SKILL.md
4a2e1dbe 2026-06-21 Pavel Safronov | 1 | --- |
| 2 | name: new-calendar | |
| 3 | 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. | |
| 4 | --- | |
| 5 | ||
| 6 | # Authoring a new calendar dataset | |
| 7 | ||
| 8 | This skill walks you through creating one `src/data/calendars/<slug>.json` region | |
| 9 | file for the "When am I?" app, the way a careful contributor would. The app's | |
| 10 | framing is plural: *"When am I?"* is asked of **a person**, so a region's eras | |
| 11 | collect **every valid answer** a local could give — overlapping calendars at the | |
| 12 | same place and time are expected, not an error. | |
| 13 | ||
| 14 | **Read these first** — they are the source of truth, defer to them over this skill: | |
| 15 | - `src/data/calendars/SCHEMA.md` — the human-readable field docs. | |
| 16 | - `scripts/validate-data.mts` — the authoritative machine-checkable schema. | |
| 17 | - `src/utils/calendars.ts` — which calendar strings are **convertible** (have a converter). | |
| 18 | - An existing file as a template, e.g. `src/data/calendars/iran.json` (two eras, | |
| 19 | two converters) or `src/data/calendars/massachusett.json` (descriptive-only + | |
| 20 | an `anchorEvents` "winters since" reckoning). | |
| 21 | ||
| 22 | ## The one rule that overrides everything: never invent | |
| 23 | ||
| 24 | This dataset is only as trustworthy as its sources. **Do not invent** events, | |
| 25 | moon names, era boundaries, dates, or polygons. Every era and every anchor event | |
| 26 | must trace to a real, citable source (Wikipedia is acceptable as the project's | |
| 27 | *acceptance* gate). If you cannot source a claim, leave it out and say so. When a | |
| 28 | fact is uncertain or contested, prefer the conservative version and note it in | |
| 29 | the `description`. If you find yourself guessing a date or a boundary, stop. | |
| 30 | ||
| 31 | ## Workflow | |
| 32 | ||
| 33 | ### 1. Gather inputs | |
| 34 | Ask the user (or confirm) the **place** and the **people/polity** — e.g. "the | |
| 35 | Mughal Empire", "Coast Salish around Puget Sound", "Joseon Korea". The answer is | |
| 36 | about *whose* reckoning it is, so be specific: "Algonquian" is a language family, | |
| 37 | not one calendar; pick the actual nation. | |
| 38 | ||
| 39 | ### 2. Research the calendar system(s) — with sources | |
| 40 | For that place + people, determine: | |
| 41 | - Which calendar system(s) were used, and over **which date ranges** (era | |
| 42 | boundaries). Multiple eras are normal (e.g. a switch from a lunar to a solar | |
| 43 | civil calendar, or a Julian→Gregorian cutover). | |
| 44 | - **Whose** answer each is (`usedBy`), e.g. "court and civil administration", | |
| 45 | "Orthodox church", a specific people. | |
| 46 | - Real, dateable sources for each. Collect the URLs as you go. | |
| 47 | ||
| 48 | Use WebSearch/WebFetch to research; cite what you actually read. | |
| 49 | ||
| 50 | ### 3. Choose the calendar string (convertible vs descriptive-only) | |
| 51 | This is **two independent gates** (see plan.md, Q3): | |
| 52 | - **Acceptance** — a sourced wiki page is enough to include the calendar at all. | |
| 53 | - **Convertibility** — whether a deterministic epoch+rule yields a numeric local | |
| 54 | year. This is decided by `src/utils/calendars.ts`: | |
| 55 | - **Convertible** strings hit a converter there: `Gregorian`, `Julian`, and the | |
| 56 | Temporal-backed `Islamic`, `Hebrew`, `Persian`, `Indian`/`Saka`, `Buddhist`, | |
c571572f 2026-06-21 Pavel Safronov | 57 | `Japanese`, `Ethiopic`, plus the hand-rolled `Chinese sexagenary cycle`, |
12bc9942 2026-06-23 Pavel Safronov | 58 | `Maya Long Count`, `French Republican calendar` (year only, via a |
c571572f 2026-06-21 Pavel Safronov | 59 | sourced lookup table — its year began at the Paris equinox, an |
12bc9942 2026-06-23 Pavel Safronov | 60 | observation, not a fixed rule), and the simple year-offset counts |
| 61 | `Dangi`/`Juche`, `ab urbe condita` (AUC), and `Seleucid era`. | |
4a2e1dbe 2026-06-21 Pavel Safronov | 62 | Match an existing string **exactly** (the lookup is case/space-insensitive). |
| 63 | - **Descriptive-only** — a calendar with no continuous numbered year (e.g. an | |
| 64 | observational lunar calendar). Use a clear `calendar` name; the engine shows | |
| 65 | the era's name/description/sources + the Gregorian reference year and a | |
| 66 | "no numbered year" note. Never mislabel a descriptive calendar as convertible. | |
| 67 | - If a genuinely new convertible system is needed, that's converter work in | |
| 68 | `src/utils/calendars.ts` (a Temporal `calendarId` if Intl supports it, else | |
| 69 | hand-rolled). **Caveat:** the Temporal `chinese` calendar is broken in | |
| 70 | `@js-temporal/polyfill@0.5.1` (throws on every date) — that's why China uses a | |
| 71 | hand-rolled `Chinese sexagenary cycle` converter instead. Verify any new | |
| 72 | Temporal calendar actually converts before relying on it. | |
| 73 | ||
| 74 | ### 4. Draft the region polygon | |
| 75 | GeoJSON `Polygon` (or `MultiPolygon`), coordinates as **`[lng, lat]`** (longitude | |
| 76 | first — easy to get backwards), outer ring **closed** (first position repeated as | |
| 77 | last), ≥4 positions. Keep it a reasonable approximation of the territory; the | |
| 78 | validator checks ranges (lng −180..180, lat −90..90) and closure, not accuracy — | |
| 79 | that's the reviewer's job against the screenshot. geojson.io is handy for drafting. | |
| 80 | ||
| 81 | ### 5. Add `anchorEvents` for descriptive-only calendars (optional) | |
| 82 | For a calendar with no numbered year, an `anchorEvents` entry lets a local place | |
| 83 | you in time as "about N winters since <event>". Each event needs a `name`, a | |
| 84 | proleptic-Gregorian `date`, and **at least one source** — these must be real, | |
| 85 | documented events (e.g. the 1616–1619 New England epidemic), never invented. | |
| 86 | ||
| 87 | ### 6. Write the file | |
| 88 | Match the shape of an existing file exactly. Dates (`start`/`end`, event `date`) | |
| 89 | are **proleptic Gregorian** `YYYY-MM-DD`; `null` start/end means open-ended; | |
| 90 | intervals are half-open `[start, end)` so a cutover date is the `end` of the old | |
| 91 | era and the `start` of the new one. Fill in `usedBy`, `description`, `sources`. | |
| 92 | ||
| 93 | ### 7. Validate, test, screenshot — make it PR-ready | |
| 94 | Run, and fix anything that fails: | |
| 95 | ```bash | |
| 96 | npm run validate:data # schema + geometry + dates | |
| 97 | npm run test # converter / engine tests still green | |
| 98 | npm run gen:badges # refresh the "calendar data" count badge | |
| 99 | npm run screenshot:calendar -- src/data/calendars/<slug>.json | |
| 100 | ``` | |
| 101 | The screenshot step pins a point inside the region at a representative date per | |
| 102 | era and captures the answer panel + shaded map into | |
| 103 | `screenshots/calendars/<slug>/`, so a reviewer can *see* a wrong polygon, an | |
| 104 | off-by-a-century era boundary, a miscategorized convertible/descriptive call, or | |
| 105 | a broken conversion. Attach these to the PR. | |
| 106 | ||
| 107 | ### 8. If you added or changed a converter | |
| 108 | Add unit tests in `src/utils/calendars.test.ts` with a known date↔year pair, and | |
| 109 | update the convertible-calendars list in `README.md` and this skill's step 3. | |
| 110 | ||
| 111 | ## Definition of done | |
| 112 | - `npm run validate:data` and `npm run test` pass. | |
| 113 | - Every era and anchor event is sourced; nothing is invented. | |
| 114 | - Convertible-vs-descriptive is correct and the conversion looks right in the | |
| 115 | screenshot. | |
| 116 | - README's convertible-calendar list and the badge count are current. | |
| 117 | - **Do not commit.** Per repo policy, hand the file + screenshots to the human to | |
| 118 | review against the sources, and let them commit. |