Skip to content

Latest commit

 

History

387 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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.md — up for grabs
Any new language Pipeline is locale-generic; smaller languages especially welcome TRANSLATION.md — up 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 .tmp → rename 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 Jews → Fornsögur Gyðinga, Proleptic Gregorian → fyrirframreiknað 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