Evidence-based User Journeys for public administration: a JSON model where every claim points to evidence, lints for the known journey-mapping pitfalls, an interactive single-file viewer, and a portable Claude skill that synthesises journeys from raw material.
Most journey maps are posters: built in a meeting room, biased towards the happy path, never updated, never translated into work. JourneyKit treats a journey as a data set, not a picture. The primary artefact is a JSON file (journey.json) that holds persona, scenario, phases, steps, pain points, recovery paths, KPIs and opportunities — and every statement in it references evidence atoms (quotes, metrics, observations) with a source and an evidence class: observed, reported or assumed. HTML, story map, action list, audit report and version diff are generated from that model.
The vocabulary is translated for public administration, where nobody "converts" or "churns" — people fall back to the phone, the counter, a complaint, or doing nothing. Content is in German (Swiss spelling); schema field names are English.
- Journey model as JSON Schema (draft 2020-12) with evidence atoms, sources, PII status, owner and review cycle
- Methodological lints for the five pitfalls — Inside-Out Bias, Happy Path Bias, Static Map Trap, Empathy Vacuum / Overcomplexity, Micro-Level Disconnect — plus Blueprint smell, PII, KPI translation and evidence quality (L001–L103)
- Interactive viewer: a self-contained HTML file with six layers — overview (share of experience statements backed by primary evidence, per phase as well; statements without primary evidence; evidence per statement type and phase as a bubble matrix; every pain point with its strongest evidence, keyboard-navigable), journey map with emotional curve, evidence-backed phase durations on a common scale, the costs due at each step and links from a pain point to its cause, process and failure paths, evidence heatmap and source register, impact/effort matrix with user stories, audit, and with
--comparea comparison with an earlier version (the overview's figures before → after, evidence per phase, both emotional curves overlaid, every model change with a jump into the journey) — evidence drill-down to the original quote on every cell, a "primary evidence only" filter, multi-persona overlay, light/dark, print, interface in German or French - Exports: Patton story map (Markdown), action list (CSV), opportunity matrix, audit report (diagnosis, proposed verdict, next steps, costs, findings), evidence heatmap as a self-contained SVG image for reports and slides or as CSV — also downloadable from the viewer's evidence layer
- Diff between two versions of a journey — what did new evidence change?
- Claude skill
user-journeywith three modes: Synthesis (raw material → journey), Workshop (hypothesis-first with research plan), Audit (review an existing journey) - Worked examples: kindergarten entry from the parents' perspective, built from a synthetic input mix (3 interviews, 30 inquiries, a survey, web analytics, an internal process document, workshop notes); a building permit for a heat pump (applicant and neighbour as two personas); and a music school enrolment (parents, waiting list, fee reduction, cancellation via the teacher). The last two test transferability to other domains and document where the model reaches its limits – costs, chains of causes, deadlines the persona must meet
Open examples/kindergarteneintritt/journey.html in a browser for the full interactive version.
- Python 3.10+
jsonschema(installed automatically)- No JavaScript toolchain: the viewer is plain HTML, CSS and JS in one file
git clone https://github.com/malkreide/journeykit.git
cd journeykit
pip install -e ".[dev]"If journeykit is not found afterwards (typical for Windows user installations, where pip puts scripts into %APPDATA%\Python\Python3xx\Scripts), use python -m journeykit … instead — every command below works that way too.
# Validate against the schema
journeykit validate examples/kindergarteneintritt/journey.json
# Methodological lints (ERROR / WARN / INFO)
journeykit lint examples/kindergarteneintritt/journey.json
# Render the interactive viewer (several files = persona comparison;
# --compare adds an earlier version of the same journey, matched by meta.id)
journeykit render examples/kindergarteneintritt/journey.json \
examples/kindergarteneintritt/journey-berufstaetig.json \
--compare examples/kindergarteneintritt/workshop-hypothese.json -o journey.html
# Exports
journeykit export examples/kindergarteneintritt/journey.json --format storymap
journeykit export examples/kindergarteneintritt/journey.json --format actions -o massnahmen.csv
journeykit export examples/kindergarteneintritt/journey.json --format audit
journeykit export examples/kindergarteneintritt/journey.json --format heatmap-svg -o heatmap.svg
# What did user evidence change compared to the workshop hypothesis?
journeykit diff examples/kindergarteneintritt/workshop-hypothese.json \
examples/kindergarteneintritt/journey.json
# Start a new journey (status: hypothesis)
journeykit new kita-anmeldung --title "Kita-Anmeldung" --persona "Eltern" --role "Elternteil" --goal "Platz sicher haben" -o kita.json| Command | Description |
|---|---|
journeykit validate FILE... [--lang de|fr] |
JSON Schema validation (exit 2 on errors) |
journeykit lint FILE... [--strict] [--quiet] [--format json] [--today DATE] [--lang de|fr] |
Pitfall lints; --strict makes warnings fail. Messages in German or French (default: meta.language of the file) |
journeykit render FILE... [--compare OLD]... [-o OUT] [--title T] [--lang de|fr] |
Self-contained interactive HTML; multiple files overlay personas, --compare adds an earlier version (matched by meta.id) and the tab «Vergleich» / «Comparaison». Interface and lint messages in German or French (default: meta.language of the first file); journey content stays as it is |
journeykit export FILE --format storymap|actions|opportunities|audit|heatmap-svg|heatmap-csv [-o OUT] [--lang de|fr] |
Markdown / CSV / SVG exports; Markdown and SVG in German or French (default: meta.language), the CSV files have technical column names |
journeykit diff OLD NEW [-o OUT] [--lang de|fr] |
Model changes between versions (exit 1 when there are changes); report in German or French (default: meta.language of the new version) |
journeykit new ID --title --persona --role --goal [--lang de|fr] |
Scaffold a valid journey in status hypothesis; --lang sets meta.language and the language of the placeholders |
journeykit schema |
Print the JSON Schema |
Language of all output: --lang per command, otherwise meta.language of the journey, otherwise the environment variable JOURNEYKIT_LANG (de or fr), otherwise German. Texts generated by argparse itself («usage:», «options:») and JSON Schema error details stay English, as before.
| Range | Pitfall | Examples |
|---|---|---|
| L001–L004 | Integrity | dangling evidence references, duplicate IDs, contradictory phase duration (min > typical or typical > max), contradictory cost range (min > amount or amount > max), unknown cause of a pain point or a cause that comes after its consequence |
| L010–L012 | Inside-Out Bias | experience claims without primary evidence, or none at all; persona on fewer than 3 primary sources |
| L020–L023 | Happy Path Bias | no breakdowns; phase without friction or edge case; breakdown without recovery path; change of channel or counterpart not marked as transition |
| L030–L035 | Static Map Trap | no owner, no review cycle, severe pain point without owner; no date |
| L040–L043 | Empathy Vacuum | steps without feelings or thoughts; emotions without evidence |
| L050–L054 | Overcomplexity | too many phases/steps; no scope exclusions; no end state; no trigger |
| L060–L065 | Micro-Level Disconnect | no opportunities; severe pain point without linked opportunity; no stories; opportunity linked to neither a pain point nor a phase; opportunity that addresses only consequences, not their cause |
| L070–L073 | Blueprint smell | no diagnosis; more back-stage notes than steps; most steps carried out by intermediaries instead of the persona |
| L080–L081 | Privacy | source contains PII; e-mail/phone patterns in evidence text |
| L090–L092 | KPIs | none; commercial naming; no leading indicator |
| L100–L103 | Evidence quality | unused atoms; contradictions; primary evidence without locator; costs backed only by assumptions such as a fee schedule |
The skill lives in skill/user-journey/. Point Claude at it (Claude Code: the folder is picked up through CLAUDE.md; claude.ai: upload the folder as a skill) and describe your material. The skill diagnoses first — is this a user journey or a service blueprint? — chooses a mode, registers sources with PII status, extracts evidence atoms with locators, synthesises the model and runs the lints. It refuses to invent emotions, insists on breakdowns and recovery paths, and says plainly when the material only supports a hypothesis map.
None. The viewer has no external dependencies and makes no network requests; rendered HTML files can be shared inside closed networks.
journeykit/
├── src/journeykit/
│ ├── schema/journey.schema.json # the journey model (single source of truth)
│ ├── model.py # index and claim iterator over a journey
│ ├── validate.py # JSON Schema validation
│ ├── lint.py # pitfall rules L001–L103
│ ├── lint_messages.py # messages and hints, German and French
│ ├── render.py # injects data into the viewer
│ ├── export.py # story map, actions CSV, opportunities, audit, heatmap
│ ├── export_texts.py # export and diff texts, German and French
│ ├── diff.py # version diff
│ ├── cli.py # journeykit command
│ ├── cli_texts.py # CLI messages and help, German and French
│ └── viewer/viewer.html # self-contained interactive viewer
├── skill/user-journey/ # Claude skill: SKILL.md + references/ + evals/ (extraction test set)
├── examples/kindergarteneintritt/ # worked, synthetic example incl. raw inputs
├── examples/baubewilligung/ # second domain: transferability check and findings
├── examples/musikschule/ # third domain: costs, avoidable contacts, deadlines
├── docs/ # concept, architecture decisions, demo image
├── scripts/ # repo checks, scorer for the extraction test set
├── tests/ # pytest
└── CLAUDE.md # entry point for working on this repo with Claude Code
See CHANGELOG.md
Contributions are welcome — see CONTRIBUTING.md.
Please report vulnerabilities as described in SECURITY.md. Journey files may contain quotes from real people: keep them anonymised (pii_status) before sharing.
MIT License — see LICENSE
Hayal Özkan · malkreide

