Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

387 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Chronograph — Human History According to AI

5,226 years. One JSON per year. 99.85% of events name a source; every year declares which regions its sources do not reach. A year-by-year editorial folio of human civilisation, researched by AI. Translations are up for grabs — the Icelandic backfill (19/5,226 years done) and any new language. The pipeline is built; what's needed is native-speaker review. See TRANSLATION.md.

🌐 timeline.sumarhus.com · GitHub · MIT License · 🇮🇸 Þýðingarverkefni

status status status status license


Project status (2026-08-01) — parked, lanes open

Active sprinting is paused. The corpus validates 100%, the site is live and stable, and the open lanes below are documented well enough for anyone (human or agent) to pick up. The dated change record lives at timeline.sumarhus.com/updates (source: frontend/src/data/updates.ts).

Lane State Entry point
Icelandic backfill 19/5,226 years translated; pipeline ready, ~$35 at batch pricing TRANSLATION.mdup for grabs
Any new language Pipeline is locale-generic; smaller languages especially welcome TRANSLATION.mdup for grabs
Phase 4 era research 3/21 eras have evidence dossiers; 18 open scripts/eras/registry-expansion-2026-07.json
Publishing migrated era dossiers 8 dossiers schema-valid but unpublished; needs a phase2-migrated UI treatment + editorial review evidence-layer/…/migrated-v1/MIGRATION-NOTES.md
Re-research of eras 05–08, 12 Pre-schema files lack per-paper metrics; needs fresh Scite runs same MIGRATION-NOTES

Three layers, one folio

Layer What it is Shipped
Layer 1 — Corpus 5,226 ICCRA-schema JSON files, one per year, 2025 CE → 3,200 BCE ✅ 2026-04-13
Layer 2 — Evidence Scholarly deep-dives per era via the Scite MCP; 7 eras validated, 8 migrated to schema v1 (unpublished), 5 pending re-research, 21 newly registered and pending research ✅ 2026-04-17, expanded 2026-07-29, migration pass 2026-07-31
Frontend — Chronograph Notebook editorial folio (reading), Stratum instrument view (per-year dashboard), Atlas orthographic globe (spatial), era scrubber + era selector — Next.js 16 + React 19 ✅ 2026-04-18, UX pass 2026-07-29
Translation layer (EN → IS) CI-integrated pipeline localizing the corpus into Icelandic. Locked verbatim Icelandic system prompt, Gemini Flash 3 Preview pinned, six-guard correctness chain, idempotent SHA256 manifest, GitHub Action auto-translates on push ✅ 2026-04-18 (pipeline); backfill paused at 19/5,226 years (0.36%)

Layer 1 — The Corpus (Phase 1 complete)

The daemon ran for 57.7 hours across 2026-04-10 – 2026-04-13 and finished the entire corpus without a single failed year.

Final metric Value
Years researched 5,226 / 5,226 (100%)
Events documented 17,991
Graph edges 13,130 cross-year connections
Failed years 0
Total runtime 57.7 hours
Total API cost ~$15.68 (Sonnet 4.6 via batch API, ~$0.003/year)
Model Claude Sonnet 4.6, exclusively
Source attribution 99.85% of events name a source (17,964 of 17,991)
Certainty distribution Confirmed 80.6%, probable 15.7%, approximate 3.5%, other 0.2%

Every year is a structured JSON with events, primary sources, certainty levels, geographic-gap declarations, and cause-effect graph edges. See the Methodology page on the site for the full optimisation journey.


Layer 2 — Scholarly Evidence (in progress)

After the year-level corpus shipped, a second complementary layer was built under evidence-layer/: scholarly-evidence deep-dives per scholarly era, produced via the Scite MCP with the scite-preflight / scite-research skill system.

Phase Eras Status
Phase 3 (schema v1.0.0) 7 (Archaic Greece, Persian Achaemenid Empire, Classical Athens, Warring States China, Hellenistic World, Mauryan India, Pax Romana) ✅ validated
Phase 2 (pre-schema, drifted) 13 (pre-agricultural → Iron Age Aegean) ⚙ 8 migrated to schema v1 on 2026-07-31 (scripts/migrate_phase2_eras.py, content verbatim, not yet published to the site) · ⛔ 5 (eras 05–08, 12) lack per-paper scite metrics and need re-research
Education pilots 3 (Classical Athens · paideia, Islamic Golden Age · madrasah, AI Inflection · cognitive threshold) ✅ VALOR-sourced
Unresearched 2 eras 📋 backlog
Phase 4 (expansion, registered 2026-07-29) 21 — 6 thematic, 8 crisis, 7 regional 🔬 research in progress

Highlights:

  • 161 bibliography entries harvested from Scite and VALOR.
  • 9 validation missions including the Mediterranean-diet / CVD validation run that caught the PREDIMED retraction.
  • 143 citations from the VALOR education corpus catalogued for cross-reference.

The era model — 43 eras, overlapping by design

Until July 2026 an era was a slice of the linear chronological sweep, and an event belonged to at most one. That cannot express the questions the corpus should answer: the Haitian Revolution belongs to the Age of Revolutions and the Age of Abolition and the modern sweep at the same time.

The registry (scripts/eras/registry-expansion-2026-07.json, merged by scripts/eras/build-era-index.mjs) now carries four kinds of era in reserved id bands, so the chronological space stays clean:

Kind Ids Count What it is
chronological 1–50 22 the original linear sweep
thematic 100–119 6 Big-History lenses that cross geography — Scientific Revolution, Age of Revolutions, Decolonisation
crisis 120–139 8 rupture, suffering, environmental shock — the Transatlantic Slave Trade, the Black Death, the World Wars
regional 140–159 7 spheres outside the Western default — West African Golden Age, Tang & Song, Classic Maya

Each entry also carries tone (golden / sombre / neutral), which drives palette from data rather than from a hardcoded list of era ids, and careLevel. Seven eras are careLevel: high: they cover mass atrocity, enslavement or genocide. That is not a publish gate — it is a research-depth and provenance requirement. Contested figures are shown as ranges with named provenance, victim counts never rest on a single source, and contested naming conventions are attributed to whoever uses them.

scripts/eras/validate-era-index.mjs enforces the id bands, unique ids and start < end. It deliberately does not reject overlapping date ranges — overlap is the point.

Research provenance — angles and evidence are separated

No single model both proposes a claim and supplies its own support.

  1. Angles (scripts/research/grok_angle_mission.py) — Grok 4.5 generates falsifiable research claims per era plus a source-bias audit naming what the standard account distorts and where an English-web-trained model reliably fails. It is explicitly barred from producing citations.
  2. Evidence — a separate Scite MCP pass retrieves the actual literature and returns a verdict per claim.

The separation earns its keep: on the first run the evidence pass overruled the angle pass three times — African demographic decline 1700–1850 downgraded from supported to contested (the offsetting term has never been estimated, so no net figure exists), Black Death mortality corrected from a 40–60% band to the literature's 30–60%, and Classic Maya equinoctial alignments downgraded to contested ("deeply rooted but unfounded"). Raw dossiers live under evidence-layer/ (gitignored — see the note in .gitignore); the aggregated derivatives ship in frontend/public/data/eras/.

  • Scite MCP whitepaper + case study available under /methodology/scite-mcp on the live site.

See evidence-layer/README.md for the full layer 2 inventory.


Frontend — Chronograph v0.2 (Notebook)

The original prototype frontend ("Eternal Codex" — dark-gold grimoire aesthetic) was removed and rebuilt on 2026-04-18 using the Claude Design handoff bundle.

Three coordinated surfaces, one navigation:

  • 📖 Notebook (/) — the default reading experience. Cream parchment with ruled lines, oxblood editorial stamps, Newsreader serif display + Inter Tight chrome + IBM Plex Mono. Virtualised 5,226-row timeline with a bookmark lane for era/decade rules. Every year is a folio entry.
  • 🔬 Stratum (/stratum) — dataset-as-instrument. Near-black dashboard, click-anywhere-to-jump year strip, 3-panel stats (event count by category, certainty distribution, source-mix bar), VB-style event grid with category rail, contempo/later source indicators, and full per-year dissent block.
  • 🌍 Atlas (/atlas) — orthographic globe with 17,515 plotted events. Imperative RAF-driven canvas draw reads rotation from a ref (no React re-render per frame over 17k items); d3-geo convention for drag so right-drag reveals east. Time-brush histogram across all 5,226 years, category filter, Off-Earth / orbital toggle (Apollo coords don't plot in the Pacific), LOD that switches heat → cluster → pin by zoom.

Shell:

  • Primary nav (brand · Methodology · GitHub) always visible.
  • Secondary nav (Notebook · Stratum · Atlas) only on the three interactive routes.
  • Year folio (/year/[id]), Era dossier (/era/[id]), and Methodology pages render without the view switcher — pure reading.

Era navigation (2026-07-29):

  • Era scrubber — one strip above the timeline carrying an event-density histogram across all 5,225 years (sqrt-normalised, because under linear scaling the modern buckets flatten everything before 1500 into a line), all 43 eras packed into lanes so their deliberate overlap is legible, and drag-to-select a year span.
  • Era selector — a menu replacing what had become a 43-item horizontal scroll strip. Grouped by kind, two columns, type-to-filter, and each row shows its span plus whether a dossier is filed.
  • Eras registered before their research lands render a registry stub — label, span, kind, focus and the editorial care note — rather than the "Era not found" state a missing dossier used to produce.
  • Proper Open Graph card rendering a cream folio preview (not the Vercel ▲).

Tech: Next.js 16.2, React 19.2, Tailwind v4, TanStack Virtual + Query, motion/react, d3-geo + topojson-client. All visual tokens from a single 3-variant system (--fg / --stamp / --rule / --accent) with WCAG AA contrast verified by scripts/qa-contrast.mjs.

Frontend — hardening pass (2026-07)

A dedicated pass took the interface from "database inspector" to editorial product, all guarded by tests:

  • Classical Music & Opera timeline (/music) — 233 works across 29 eras, Layer-1 draft + Layer-2 scholarship (161 peer-reviewed sources verified against Crossref/DataCite), also overlaid onto the main timeline as a filterable "musical" category.
  • Follow-the-thread — music entries link to the earlier/later work in the chronology, landing on the exact entry (scroll + focus), turning the overlay into a guided narrative.
  • Proportional era ribbon — the home era filter is a single band whose segment widths follow each era's real time-span across the 5,226 years; a map of deep time that doubles as the filter.
  • Reading-first folio — narrative always visible, citations behind an explicit control; global "expand all sources"; a vertical era minimap.
  • Durable quality — a committed Playwright E2E suite + GitHub Actions CI (frontend/e2e/) asserting, on every route: zero console errors, no horizontal overflow (320→1440), the signature features work, and zero serious/critical axe-core a11y violations. 30 tests.
  • Accessibility — skip link, keyboard-navigable controls (roving-tabindex era ribbon, arrow-key nav), labelled globe controls + canvas, meaningful link/button names, WCAG-AA contrast across all three themes.
  • Discoverability — dynamic sitemap.xml (5,255 URLs: every year + era), robots.txt, and a working Open Graph card (1200×630 cream folio).
  • Sovereign hosting — served from an owned Hetzner edge (Caddy + Docker, auto-TLS) at timeline.sumarhus.com; the old Vercel host 308-redirects. Ideal-state criteria tracked in docs/ideal-state-criteria.md.

Translation layer — EN → IS — open source 🇮🇸 (2026-04-18)

Looking for Icelandic-speaking contributors. See TRANSLATION.md for the full methodology, fixed-terminology glossary, and how to review or extend a locale. Live coverage: outputs/translations/is/STATUS.json.

The corpus is being localized into Icelandic via a CI-integrated pipeline at scripts/translate/. The architecture is generic and supports additional locales; PRs adding new languages welcome.

Model: gemini-3-flash-preview (pinned — -latest tags are forbidden in production runs so dialect/format doesn't shift mid-backfill).

Strategy: The model is prompted in Icelandic — the full system prompt is written in Icelandic to lock the model into the Icelandic latent space. Prompt is verbatim at scripts/translate/prompts/system.is.md and content-hash-pinned in the manifest so any edit invalidates the entire locale cache.

Six-guard correctness chain (every translated file must pass all six or it's not written):

  1. Locked verbatim Icelandic system prompt (rule #1: translate values only, keep keys in English).
  2. Glossary preamble in the user message listing every JSON key + every ICCRA enum value that must be preserved.
  3. API-level responseMimeType: "application/json" forcing strict JSON output.
  4. Structural diff rejecting any file where keys, enums, IDs, numerics, or array lengths differ from source. cross_references and coordinates_approx allow English parenthetical annotations to translate but require the ID/numeric prefix to match.
  5. Deterministic key-rename auto-repair for the mechanical extra_key + missing_key with same ICCRA-enum value class of model errors (e.g. model produces "certain": "confirmed" instead of "certainty": "confirmed" — schema is authoritative, rename is logged, content is untouched).
  6. Ajv schema validator rejecting any file that breaks ICCRA.

Atomic .tmprename ensures no partial writes ever land on disk. Idempotent via SHA256(source) + SHA256(prompt) per file per locale in .translation-manifest.json.

Fixed Icelandic terms (from scripts/translate/glossary.json): BCE → f.Kr., CE → e.Kr., Antiquities of the JewsFornsögur Gyðinga, Proleptic Gregorianfyrirframreiknað gregorískt tímatal. Canonical Icelandic forms for historical names (Ágústus, Heródes mikli, Parþaveldið, Silkivegurinn, Jósefus) handled by the locked prompt.

Automation: .github/workflows/translate-is.yml — on push to main touching outputs/json/** or scripts/translate/**, runs the IS pipeline (manifest-skipped for cached files), re-aggregates frontend chunks, opens a PR via peter-evans/create-pull-request. workflow_dispatch supports a reset_manifest flag for forced full re-translation. Concurrency group prevents overlapping runs.

Frontend consumption: frontend/scripts/aggregate-data.mjs emits per-locale chunk trees at frontend/public/data/<locale>/{chunks,manifest.json}. Missing-locale years fall back to English with a _locale_fallback: "en" marker so the UI never sees a hole in the timeline. frontend/src/i18n/ provides a hasLocale() typeguard, dictionary loader, and native-speaker-authored EN / IS dictionaries for chrome, category labels, certainty labels, and era names. The app/[lang]/ route restructure is scheduled as a follow-up session.

Run locally:

cd scripts/translate
npm install
echo "GOOGLE_AI_API_KEY=..." >> ../../.env
npm run dry-run:is            # 3-file smoke
npm run run:is                # full 5,226-year backfill (resumable)

What each year contains

Every year follows the ICCRA schema (see RESEARCH_PROMPT.md):

{
  "year": 1066,
  "year_label": "1066 CE",
  "era_context": "High medieval period...",
  "documentation_level": "rich",
  "geographic_coverage_gaps": ["Sub-Saharan Africa", "Southeast Asia"],
  "events": [
    {
      "id": "1066-001",
      "title": "Battle of Hastings",
      "region": "England",
      "category": "military",
      "description": "...",
      "key_figures": ["William, Duke of Normandy"],
      "sources": [{"name": "Bayeux Tapestry", "type": "primary_text", "contemporary": true}],
      "certainty": "confirmed",
      "certainty_note": "Extensively documented..."
    }
  ],
  "disconfirming_evidence": "...",
  "historiographic_note": "...",
  "graph_edges": [{"from": "1066-001", "to": "1086-001", "relation": "led_to", "note": "..."}],
  "_meta": {"model": "claude-sonnet-4-6", "processed_at": "2026-04-11T..."}
}

Design principles: no fabrication · anti-sycophancy (disconfirming evidence mandatory) · no anachronism · global coverage (gaps declared, not hidden) · named source typing (primary text, archaeological, epigraphic, numismatic, chronicle, oral tradition).


The optimisation journey (Phase 1)

Started as a subscription-based CLI daemon; evolved through five phases to complete the corpus 29× faster at 98.6% lower cost.

Metric Phase 1 (CLI) Phase 5 (API) Improvement
Cost per year $0.22 $0.003 99% cheaper
Years per cycle 5 25 5× throughput
Cycle interval 20 min 60 sec 20× faster
Schedule Off-hours only 24/7 Unrestricted
Total time ~70 days (est.) 57.7 hours (actual) 29× faster
Total cost ~$1,150 (est.) ~$15.68 (actual) 98.6% cheaper

Full breakdown of each phase, including the failed Haiku experiment and the Kimi-analysed plan-mode migration, lives on /methodology.


Run it yourself

Self-contained prompt, no dependencies — drop into any capable AI:

You are a historical research agent. Produce structured JSON for a single year.

YEAR TO RESEARCH: [INSERT YEAR, e.g., 1453 or -776]

RULES:
1. Negative years = BCE. Positive = CE.
2. Research significant events across ALL regions.
3. Modern years: 15-25 events. Ancient: 0-5. Zero is acceptable.
4. Every event names its source. "General knowledge" is not acceptable.
5. Every event has a certainty level: confirmed/probable/approximate/traditional/legendary.
6. Include "disconfirming_evidence" — what's disputed about this year?
7. Include "geographic_coverage_gaps" — what regions are you missing?
8. Do NOT fabricate. Empty events with honest context > hallucinated entries.
9. Respond with VALID JSON ONLY.

SCHEMA: {"year": int, "year_label": str, "era_context": str,
"documentation_level": "rich|moderate|sparse|minimal|negligible",
"geographic_coverage_gaps": [str], "events": [{id, title, region,
category, description, key_figures, sources, certainty, certainty_note}],
"disconfirming_evidence": str, "historiographic_note": str,
"graph_edges": [{from, to, relation, note}]}

Quality assurance

# Full ICCRA schema validation
python3 scripts/validate_corpus.py

# Auto-fix compound categories ("political | military" -> "political")
python3 scripts/fix_categories.py

# Backfill _meta on files missing model/cost tracking
python3 scripts/backfill_meta.py

Final validation pass: 100% valid — 5,226 files, 17,991 events, 0 errors.


Architecture

chronograph/
├── LICENSE                        MIT
├── README.md                      this file
├── RESEARCH_PROMPT.md             ICCRA prompt template (locked)
├── LEDGER.md                      append-only progress log
├── CONTRIBUTING.md                how to do adversarial review
│
├── scripts/                       Python 3.11 async daemon (Phase 1) + TS translate
│   ├── api_client.py              direct Anthropic API, tiered models
│   ├── orchestrator_optimized.py  main daemon loop
│   ├── batch_processor.py         5 years per API call
│   ├── validate_corpus.py         ICCRA schema validator
│   ├── fix_categories.py          auto-fix compound categories
│   ├── health_check.sh            quick status
│   └── translate/                 EN → IS translation pipeline (2026-04-18)
│       ├── gemini.ts              @google/genai client, pinned flash-3-preview
│       ├── schema.ts              Ajv ICCRA validator (mirrors Python)
│       ├── structural-diff.ts     post-translation keys/enums/IDs guard
│       ├── key-repair.ts          deterministic schema-key auto-repair
│       ├── translate.ts           per-file orchestration
│       ├── manifest.ts            SHA256 idempotency
│       ├── run.ts                 CLI entrypoint
│       ├── prompts/system.is.md   locked verbatim Icelandic prompt
│       └── glossary.json          DO_NOT_TRANSLATE + fixed IS terms
│
├── docker/                        reboot-persistent daemon container
│
├── outputs/
│   ├── json/                      5,226 ICCRA JSON files (Layer 1)
│   ├── haiku_experiment/          archived quality comparison
│   └── gemini_experiment/         archived quality comparison
│
├── evidence-layer/                Layer 2 — Scite/VALOR deep-dives
│   ├── methodology/               scite-skill-system + validation missions
│   ├── eras/                      phase2-eras-01-13, phase3-eras-14-20
│   └── education-layer/           VALOR-sourced education pilots
│
├── frontend/                      Chronograph v0.2 (Notebook)
│   ├── src/app/                   Next.js 16 App Router
│   │   ├── page.tsx               / (Notebook timeline)
│   │   ├── atlas/page.tsx         /atlas (orthographic globe)
│   │   ├── stratum/page.tsx       /stratum (instrument dashboard)
│   │   ├── year/[id]/             per-year folio
│   │   ├── era/[id]/              per-era scholarly brief
│   │   ├── methodology/           editorial methodology page
│   │   └── opengraph-image.tsx    OG card (cream folio, 1200×630)
│   ├── src/components/
│   │   ├── shell/                 two-layer nav (primary + secondary)
│   │   ├── notebook/              folio timeline + year layout
│   │   ├── stratum/               instrument dashboard
│   │   └── atlas/                 globe + imperative draw loop
│   ├── scripts/
│   │   ├── aggregate-data.mjs     Layer 1: chunk JSON (+ per-locale trees)
│   │   ├── aggregate-evidence.mjs Layer 2: flatten evidence to per-era JSON
│   │   ├── qa-tour.mjs            Playwright screenshot sweep
│   │   ├── qa-font-audit.mjs      DOM sweep for <14px text
│   │   └── qa-contrast.mjs        WCAG AA computed-contrast checker
│   ├── src/i18n/                  locale loader + en/is dictionaries
│   └── public/data/               aggregated chunks + era bundles + locale trees
│
└── state/
    ├── progress.json              daemon progress (completed/failed/in_progress)
    └── cache/                     SHA256-keyed response cache

License

MIT License for all code (see LICENSE). Corpus data (JSON under outputs/json/, aggregated chunks, evidence layer) released under CC BY 4.0 — attribution required.


Contributing

Layer 1 is complete. Layer 2 and the frontend are living work.

  1. Adversarial review — prove the AI wrong. Find fabrications, bad sources, anachronisms
  2. Regional deep dives — African, East Asian, Indigenous American, Pacific
  3. Graph edges — cause/effect chains, parallel developments, trade routes
  4. Evidence layer — re-research the 13 Phase-2 eras under schema v1.0.0
  5. Frontend — file issues for broken UX or missing affordances

See CONTRIBUTING.md.


Built by Magnús Smári Smárason — one daemon, 5,226 years, zero fabrication. Frontend design by Claude Design (claude.ai/design), implementation by Claude Code.

About

Chronograph — 5,226 years of human history as a sourced JSON corpus, with Notebook/Stratum/Atlas frontend and EN→IS translation layer

Topics

Resources

Contributing

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages