fuzzyworld

when-am-i / commit

feat: add new-calendar dataset-authoring skill

4a2e1dbe4ac69cae45e6d654b1e66e2804293dd7 · Pavel Safronov · 2026-06-20 18:31:37 -0700 · parents bdbeab60240b

Claude Code usage: this commit ≈0 out / ≈0 cached · cost unknown (no per-model trailer) · session 3 tally 29.0K in / 269.0K out / 22.9M cached · usage by commit and session

feat: add new-calendar dataset-authoring skill

A Claude Code skill (.claude/skills/new-calendar) that walks an author through
creating a calendar region file end-to-end: researching a place/people's
calendar(s) with sourced citations, drafting the GeoJSON polygon and eras,
choosing the right converter (or descriptive-only), then validating and
screenshotting for a PR-ready contribution. The project's "never invent, always
source" rule is front and centre.

Usage: 29.0K in / 269.0K out / 22.9M cached (session tally)
rtk:   110.8K saved, 80.9% over 243 cmds (global/lifetime)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
 .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 &lt;event&gt;". 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.