Tests
Smoke
Routes
ADRs
Agents
PWA design
Python
Platform
Field test
License
Explainer
β Project Explainer β architecture, cue distribution, Sonoma corner doctrine, team.
A real-time, on-device coaching system for track-day drivers. Runs on a
Pixel 10 via Termux: ingests CAN telemetry over USB from the car's OBD-II
port, makes coaching decisions with on-device Gemma 4 (via litert-lm),
and pairs with a Vue PWA (pitwall-web, sibling repo, fully designed in
[docs/vue/](docs/vue/README.md), implementation pending) for
presentation. Built for the May 23, 2026 Sonoma Raceway field test β
no cloud dependency for the core coaching loop.
Three LLM tiers, all on-device:
| Tier | Model | Runtime | When |
|---|---|---|---|
| Hot path | β | sonic_model + canonical phrases |
In-drive, < 50 ms |
| Warm path | Gemma 4 E2B | litert_lm.Engine (in-process) |
In-drive brief cues, < 100 ms |
| Paddock | Gemma 4 E4B | lit serve + 18 ADK agents |
Pre-brief, debrief, Q&A, 2β15 s |
Table of contents
- Status
- What this is / what this isn't
- Repo layout
- Quick start
- Architecture
- ADK multi-agent coaching
- Performance budget
- Hardware
- Data privacy & residency
- Key documentation
- Test status
- Roadmap
- Frozen artefacts
- Team 2 (Intermediate, BMW M3)
- Acknowledgements
- Contributing
- Code style
- Changelog
- License
The architecture is documented across 21 ADRs. The four most recent focus on the ADK paddock backend:
ADR-018
(field-readiness + pedagogy tuning from the 2026-04-29 Team 2 review),
ADR-019 (ADK multi-agent plan),
ADR-020 (7 structural fixes: PitwallOrchestrator, SequentialAgent/ParallelAgent pipelines, SQL safety, output_key pattern), and
ADR-021 (second audit: Runner API, KV cache via persistent sessions, PitwallTracingPlugin + DuckDB agent telemetry).
What this is: an in-cabin AI race coach for solo track-day drivers,
running offline on a Pixel + USB-CAN. Hot-path coaching cues at < 50 ms
(sonic_model + canonical phrase library, no LLM), warm-path verbal
coaching from on-device Gemma 4 E2B (paddock pre-brief + post-session
debrief, via litert-lm), and a full 18-agent ADK paddock coaching system
backed by Gemma 4 E4B. Coach avatar emotion (12-state taxonomy) is
Gemma-controlled β the LLM emits an [EMOTION: ...] tag with each
reply, surfaced to the PWA so the sprite plays the matching animation.
Telemetry sinks into local DuckDB (including agent traces); analytics
run client-side; no cloud is required for the core coaching loop.
What this isn't: a Garmin Catalyst (post-session debrief, fixed heuristics), an AiM Solo (pure logger, no coaching), or a cloud-only LLM tool. The differentiator is real-time coaching that works without cell coverage, grounded in published pedagogy (Ross Bentley) and adapted to driver skill level.
- Bridge: shipped. 56 HTTP endpoints, 358 tests passing, 0 skipped, smoke-tested end-to-end against a real 8273-frame Sonoma VBO with 51 assertions green.
- CAN pipeline: shipped.
python-canreader + simulator + DBC; 6 round-trip tests oninterface='virtual'. USB-CAN device enumeration via pyserial β adapter VID:PID lookup table covers CANable, Macchina M2, Korlan USB2CAN, PEAK, Kvaser, FTDI ELM327, CH340. - Termux foreground service: shipped. Drop-in install package in
[deploy/termux/](deploy/termux/INSTALL.md). - Three-tier coach architecture (ADR-017): shipped. In-drive cues are canonical phrases (no LLM); paddock brief + debrief are Gemma-via-litert-lm; cloud Gemini removed entirely.
- Field-readiness blockers + pedagogy tuning (ADR-018): shipped 2026-04-30. LLM friction sink, audio ducker, 1D Kalman dead-reckoning, intermediate-driver pedagogy refit.
- ADK multi-agent paddock backend (ADR-019β021): shipped 2026-05-01.
18 agents, 15 tools,
PitwallOrchestrator(BaseAgent),DebriefPipelinewithParallelAgent,BriefPipeline,Runner+ persistent sessions for KV cache reuse,PitwallTracingPlugin+agent_tracesDuckDB table,AgentMetaAgent, multi-turn Q&A (/coach/ask). All phases shipped ahead of original post-Sonoma schedule. **pitwall-webVue PWA**: design-only. 54 markdown files in[docs/vue/](docs/vue/README.md)β 38 screens specced, foundation- systems + sprite spec + character bible + Gemma-controlled emotion taxonomy + god navigation map. Code lives in a future sibling repo; implementation hasn't started.
- Sonoma field test: May 23, 2026.
pitwall/
βββ src/pitwall/ # Pitwall Backend (Flask API)
β βββ __main__.py # Application entrypoint
β βββ features/ # Feature-Sliced Design domains
β βββ telemetry/ # can_reader, signals API
β βββ session/ # vbo_parser, analytics, profiles, debrief API
β βββ coaching/ # ADK agents, coach engine, TTS, LiteRT
β βββ track/ # sonoma lore, track loaders, track API
β βββ realtime/ # Live cue streaming via SSE
βββ src/simulator/ # Standalone HTTP Clients
β βββ simulator.py # VBO-driven simulation runner
β βββ pitwall_app.py # Standalone replay TUI
β βββ vbo_client.py # Standalone VBO parser for clients
βββ scripts/ # Utility scripts
β βββ best_sonoma_lap.py # S/F line projection utility
β βββ import_sonoma_real_gps.py # OSM Overpass import
β βββ generate_sample_vbo.py # Synthetic VBO generation
βββ data/
β βββ dbc/pitwall.dbc # synthetic DBC (29 signals)
β βββ registry/obd2_pids.json # signal registry seed (54 entries)
β βββ tracks/sonoma.json + sonoma_real_gps.json
β βββ reference/ # gold-standard lap traces
β βββ markers/sonoma/ # marker thumbnails
βββ deploy/
β βββ termux/ # foreground-service install package
β βββ INSTALL.md
β βββ service/pitwall-bridge/{run, log/run}
β βββ boot/start-pitwall
βββ docs/
β βββ api.md # 56-endpoint reference
β βββ adk-agent-architecture.md # ADK 18-agent topology, tools, KV cache, tracing
β βββ adk-implementation-plan.md # as-built status + remaining backlog
β βββ internal_architecture.md # post-2026-04-28 backend topology
β βββ vue/ # CANONICAL pitwall-web design β 54 files
β β βββ README.md # index of the journey
β β βββ screens/ # 38 screen docs
β β βββ β¦
β βββ adr/ # 21 ADRs (019β021 cover ADK)
β βββ β¦ # pedagogy, telemetry-pipeline, etc.
βββ tests/ # 424 tests passing, modularised in tests/features/
βββ android/ + android-app/ # FROZEN v1 native; deletes post-PWA
βββ .claude/commands/adk.md # /adk slash command β ADK reference + audit tool
βββ mkdocs.yml
βββ CHANGELOG.md
βββ README.md
pip install flask duckdb requests python-can cantools pyserialOn-device LLM coach β Gemma 4 E2B (warm path, in-drive):
pip install litert-lm
# Download the model (~2.4 GB, public HuggingFace repo, no token needed):
litert-lm import \
--from-huggingface-repo litert-community/gemma-4-E2B-it-litert-lm \
gemma-4-E2B-it.litertlm gemma-4-e2b
# Lands at ~/.litert-lm/models/gemma-4-e2b/model.litertlmADK multi-agent paddock backend β Gemma 4 E4B (pre-brief, debrief, Q&A):
pip install google-adk
# Download E4B (~4 GB) and start the server:
lit pull gemma-4-e4b
lit serve --port 8001 # run in a separate terminal before starting the bridgeBoth backends fall back silently when unavailable β the hot-path canonical phrase coach always works.
Note: earlier docs referenced
mediapipe+.taskfiles. We uselitert-lm(cross-platform macOS / Linux / Termux) with.litertlmformat. Per ADR-017.
# HTTP-only (no live CAN, no E4B server needed for basic operation)
python3 -m src.pitwall --track src/pitwall/features/track/sonoma.json
# β β sonic_model loaded
# β Track: Sonoma Raceway (12 corners)
# β ADK coach_orchestrator loaded β 18 agents (LiteRT-LM E4B) β if lit serve running
# π Pitwall Bridge v2 on http://127.0.0.1:8765
curl -s http://127.0.0.1:8765/health | python3 -m json.tool# Dev/test (virtual bus, no hardware needed)
python3 -m src.pitwall --can-channel pitwall_dev
# Replay a VBO file as CAN frames
python3 src/simulator/can_simulator.py \
--vbo "/path/Sonoma Intermediate - 1_47.5.vbo" \
--channel pitwall_dev --speed 2.0
# Production β USB-CAN on Pixel via Termux
python3 -m src.pitwall \
--can-interface slcan --can-channel /dev/ttyACM0 \
--can-dbc data/dbc/pitwall.dbcpython3 -m pytest tests/ -q
# β 424 passed, 0 skipped
python3 tests/test_endpoints_smoke.py --keep-db
# Ingests 8273-frame Sonoma VBO β exercises 56 endpoints β 51 assertions# Start a session, run a debrief, ask a question:
curl -X POST http://127.0.0.1:8765/session/start \
-H 'Content-Type: application/json' \
-d '{"driver": "taha", "track": "sonoma"}'
curl -X POST http://127.0.0.1:8765/coach/debrief \
-H 'Content-Type: application/json' \
-d '{"session_id": "<sid>", "driver_id": "taha"}'
curl -X POST http://127.0.0.1:8765/coach/ask \
-H 'Content-Type: application/json' \
-d '{"driver_id": "taha", "session_id": "<sid>", "question": "Why was lap 4 faster than lap 2?"}'
# Discover available agents:
curl http://127.0.0.1:8765/coach/agents | python3 -m json.toolβββββββββββββββββββββββββββββ Pixel 10 (in cabin) ββββββββββββββββββββββββββ
β β
β USB-CAN adapter (CANable Pro / Macchina M2) β
β β slcan @ 500 kbps β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Termux foreground service (deploy/termux/) β β
β β β β
β β ββββββββββββββββ β β
β β β can_reader β python-can + cantools DBC decode β β
β β ββββββββ¬ββββββββ β β
β β βΌ β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β DuckDB (pitwall_sessions.duckdb) β β β
β β β telemetry Β· laps Β· coaching_notes Β· driver_events β β β
β β β llm_friction Β· conversations Β· agent_traces β β β
β β ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β β
β β βΌ β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β HOT PATH <50ms β β β
β β β sonic_model + RuleCoach + canonical phrase library β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β β
β β β WARM PATH <100ms β β β
β β β LitertCoach (Gemma 4 E2B, litert_lm.Engine in-process) β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β β
β β β PADDOCK 2β15s β β β
β β β PitwallOrchestrator β 18 ADK agents β Gemma 4 E4B β β β
β β β (lit serve --port 8001, Tensor G5 NPU) β β β
β β β brief Β· debrief Β· Q&A Β· voice scripts Β· agent telemetry β β β
β β ββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ β β
β β βΌ β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Flask HTTP server 127.0.0.1:8765 (56 endpoints) β β β
β β β GET /cues/stream β live coaching cues (SSE) β β β
β β β GET /notifications β async inbox (SSE) β β β
β β β POST /coach/brief β pre-session brief β β β
β β β POST /coach/debrief β post-session debrief β β β
β β β POST /coach/ask β multi-turn Q&A β β β
β β β GET /coach/agents β agent registry (PWA) β β β
β β β GET /session/<sid>/export.parquet β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β pitwall-web (Vue 3 PWA) β IN DESIGN β β β
β β 38 screens specced in docs/vue/ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The paddock tier uses Google ADK with 18 specialist agents routed by a deterministic keyword classifier β no LLM routing.
PitwallOrchestrator(BaseAgent) β keyword classifier, no LLM routing
βββ DebriefPipeline (SequentialAgent)
β βββ DebriefDataPhase (ParallelAgent) β 3 agents run concurrently
β β βββ HighlightFinderAgent output_key=highlights_data
β β βββ TelemetryAgent output_key=telemetry_data
β β βββ PedagogyAgent output_key=pedagogy_data
β βββ NarrativeAgentDebrief reads {highlights_data}
β {telemetry_data}
β {pedagogy_data}
βββ BriefPipeline (SequentialAgent)
β βββ PedagogyAgent output_key=pedagogy_data
β βββ NarrativeAgentBrief
βββ 15 QA specialist agents β GoldLapAgent Β· WeatherAdaptationAgent
SessionPlannerAgent Β· IncidentReviewAgent
RacePaceAgent Β· GoalSettingAgent
MentalMapAgent Β· VoiceScriptAgent
LapComparisonAgent Β· CornerCoachAgent
ProgressTrackerAgent Β· SetupAdvisorAgent
MindsetCoachAgent Β· AgentMetaAgent
TelemetryAgent (default)
Key properties:
- All 15 tools query DuckDB
read_only=TruewithLIMIT 500enforced PitwallTracingPluginlogs every agent run + tool call toagent_tracesDuckDB table- Persistent ADK sessions per driver β
lit serveclones KV cache across calls (~30β50% prefill reduction on warm requests) POST /session/starttriggersreset_driver_session()for a clean session baseline
See [docs/adk-agent-architecture.md](docs/adk-agent-architecture.md) for the full topology, tool specs, KV cache design, and agent telemetry schema.
| Path | Budget | Notes |
|---|---|---|
| Hot path β sonic_model cue | < 50 ms | ADR-002 |
| In-drive canonical phrase (RuleCoach) | < 100 ms | ADR-017 |
| Pre-brief LLM (Gemma 4 E2B, cold) | 6β8 s | measured on Apple Silicon CPU |
| Post-session debrief LLM (E2B) | 8β12 s | measured |
| ADK paddock β cold call (new session) | 2β15 s | E4B via lit serve, Tensor G5 NPU |
| ADK paddock β warm call (persistent session) | ~30β50% less | KV cache clone; session instructions pre-filled |
| CAN frame β DuckDB row | < 5 ms | src/pitwall/features/telemetry/can_reader.py |
| Agent trace DuckDB write | < 1 ms | batch drain after run_adk() |
| End-to-end smoke (8273 frames + every endpoint) | ~12 s | tests/test_endpoints_smoke.py |
| Parquet export (90-min session) | < 500 ms | GET /session/<sid>/export.parquet |
For the May 23 Sonoma field test:
| Item | Role | Notes |
|---|---|---|
| Pixel 10 | Compute + display + audio gateway | Tensor G5 NPU for E4B + E2B; Termux + bridge + PWA in Chrome |
| USB-CAN adapter | Reads OBD-II powertrain bus | CANable Pro ( |
| Powered USB-C OTG hub | Charge passthrough during 4-hour session | needs USB-PD passthrough |
| OBD-II cable | Connects USB-CAN adapter to car port | standard OBD-II to DB9 |
| Pixel Earbuds | Audio coaching output (TTS) | ANC + tight fit handle cabin noise |
Test target: 2003 BMW M3 (E46) at Sonoma Raceway.
All coaching is on-device. Every LLM call goes through litert-lm
or lit serve locally β pre-brief, debrief, Q&A, /score. There is no
cloud LLM dependency.
All telemetry, lap data, coaching notes, driver profiles, and agent
traces live in a local DuckDB on the device running the bridge. The
bridge's HTTP server binds to 127.0.0.1 only and is not externally
reachable without an explicit tunnel or POST /spectator/token.
The PWA's analytics flow uses GET /session/<sid>/export.parquet to
hydrate a client-side DuckDB-Wasm instance β Parquet lives in browser
OPFS only, never sent off-device.
[docs/api.md](docs/api.md)β every endpoint with request/response shapes[docs/adk-agent-architecture.md](docs/adk-agent-architecture.md)β ADK topology: 18 agents, 15 tools,PitwallOrchestrator,Runner, KV cache,agent_tracesschema[docs/adk-implementation-plan.md](docs/adk-implementation-plan.md)β as-built status + remaining backlog (SSE streaming, state scopes, LoopAgent)[docs/internal_architecture.md](docs/internal_architecture.md)β backend topology with mermaid diagrams[docs/adr/index.md](docs/adr/index.md)β all 21 architecture decisions
- ADR-019 β ADK multi-agent plan (conversations table, agent roster, Q&A endpoints)
- ADR-020 β 7 structural fixes:
PitwallOrchestrator,SequentialAgent/ParallelAgent,output_key, SQL safety,save_voice_scripts - ADR-021 β second audit:
RunnerAPI, race condition fix, persistent sessions + KV cache,PitwallTracingPlugin+agent_traces - ADR-018 β field-readiness: LLM friction sink, audio ducker, Kalman dead-reckoning, pedagogy refit
- ADR-017 β three-tier coach; cloud Gemini removed
[docs/vue/README.md](docs/vue/README.md)β canonical PWA design: 38 screens, emotion taxonomy, navigation map[docs/vue/10-coach-emotions.md](docs/vue/10-coach-emotions.md)β 12-emotion taxonomy, Gemma prompt contract[docs/vue/11-navigation-map.md](docs/vue/11-navigation-map.md)β god mermaid + per-screen reference[docs/ux.md](docs/ux.md)β UX principles: audio-first, silence-is-coaching, fail-open
[deploy/termux/INSTALL.md](deploy/termux/INSTALL.md)β Pixel + Termux foreground service install[docs/android_parallel_llm_install.md](docs/android_parallel_llm_install.md)β sideload pitwall-parallel + android-llm-service on one device (embedded PWA + on-device LLM)[docs/hardware.md](docs/hardware.md)β sensor + adapter + cable spec[docs/sonoma_track_intelligence.md](docs/sonoma_track_intelligence.md)β corner-by-corner reference + danger zones + weather phases
[docs/pedagogy.md](docs/pedagogy.md)β Ross Bentley curriculum in the coaching engine[docs/coaching-engine.md](docs/coaching-engine.md)β rule + LiteRT-LM coach composition
pip install mkdocs-material
mkdocs serve -a 127.0.0.1:8889| Suite | Count | Notes |
|---|---|---|
tests/ (pytest) |
424 passed, 0 skipped | Unit + integration; vendored fixtures, no network |
tests/features/telemetry/test_can_pipeline.py |
9 of 424 | Round-trip on interface='virtual' + ADR-018 dead-reckoner wiring |
tests/features/telemetry/test_dead_reckoning.py |
13 of 424 | ADR-018 1D Kalman filter unit suite |
tests/features/coaching/test_coach_engine_litert.py |
8 of 424 | Live-Gemma tests; auto-skip when model file is absent |
tests/test_endpoints_smoke.py |
51 assertions, 0 failed | End-to-end against a real 8273-frame Sonoma VBO |
python3 -m pytest tests/features/test_api_core.py -q
python3 -m pytest tests/features/telemetry/test_can_pipeline.py -q
python3 -m pytest tests/features/telemetry/test_dead_reckoning.py -q
python3 -m pytest tests/features/coaching/test_coach_engine.py -q
python3 -m pytest tests/features/coaching/test_coach_engine_litert.py -q # auto-skip without model
python3 -m pytest tests/features/session/test_api_session.py -q
python3 tests/test_endpoints_smoke.pyThe May 23, 2026 Sonoma Raceway field test is the load-bearing milestone.
Before Sonoma (3 weeks):
- Scaffold
pitwall-webβ Vue 3 + Vite + Tailwind PWA per[docs/vue/](docs/vue/README.md). MVP: boot path β session loop β 1 analytics screen β tutorial overlay. Bridge contract is complete. - Pre-rendered TTS phrase library β bake canonical phrases per coach into MP3 clips, packaged into
pitwall-web/public/audio/coaches/<id>/ - Per-car DBC packs β Subaru GR86 next; hardware enumeration table ready to extend
Post-Sonoma:
- ADK SSE streaming β
POST /coach/ask/streamwithRunConfig(streaming_mode=SSE)so the Vue PWA shows tokens as they arrive (critical for 2β15s E4B latency) - ADK state scopes β
user:prefix for driver preferences persisting across sessions;app:for shared track conditions - Vue PWA paddock Q&A screen β connects to
/coach/ask+/coach/agents - Multi-track support β Laguna Seca and Thunderhill slot ready in
data/tracks/ - Sprite generation β nano-banana prompts ready in
[docs/vue/assets/reference-sheet-source.md](docs/vue/assets/reference-sheet-source.md); ~56 frames for 4 non-T-Rod coaches - Public release + license β TBD post-field-test
- Framework refactor β per ADR-018: abstract
CoachContext, parameterise LSTM input layer, decouple spatial awareness engine
android/ and android-app/ contain the v1 Flutter + native Kotlin
implementation. No longer the active frontend β see
ADR-016. Both will
be removed once pitwall-web reaches feature parity.
| Role | Person |
|---|---|
| Tech Lead | Hemanth HM |
| Edge / Telemetry | Simon Margolis |
| AGY Pipeline | Taha Bouhsine |
| Data Reasoning | Vijay Vivekanand |
| UX / Frontend | Aileen Villanueva |
- Ross Bentley, Performance Driving Illustrated β pedagogical foundation for every coaching rule. Distilled into 9 concepts in
coach_engine.match_bentley_concept. - The T-Rod Sonoma transcript β track-specific coaching anchors (the bridge, the K-wall bend, Calamity Corner) that make verbal coaching feel native to Sonoma.
- Google ADK β multi-agent orchestration framework.
PitwallOrchestrator,SequentialAgent,ParallelAgent,BasePluginandRunnerpower the 18-agent paddock backend. - Google AI Edge /
litert-lmβ on-device Gemma 4 inference, cross-platform. Replaced the original MediaPipe Genai approach. - Racelogic VBO format β canonical telemetry file format consumed by simulator and post-session import.
- python-can + cantools β CAN ingest stack.
- DuckDB β single-process SQL persistence with first-class Parquet support. Now stores coaching, conversations, and agent traces in one file.
- Team 2 architecture review (2026-04-29, "CONDITIONAL PASS") β punch list that became ADR-018: LLM friction logging, audio ducker, Kalman dead-reckoning, intermediate-driver pedagogy refit.
Pitwall is an internal sprint repository for the May 23, 2026 Sonoma field test. External contributions are not currently accepted.
For team members:
- Branch from
master; merge via PR. Tests must pass before merge. - Run
python3 -m pytest tests/ -qandpython3 tests/test_endpoints_smoke.pybefore opening a PR that touches ingest, lap detection, or the sink. - New endpoints get an entry in
[docs/api.md](docs/api.md)plus a test intests/test_bridge.py. - New ADK agents get an entry in
AGENT_REGISTRYinadk_agents.pyand a row in the agent catalogue in[docs/adk-agent-architecture.md](docs/adk-agent-architecture.md). - Architectural changes get a new ADR in
docs/adr/(ADR-NNN-slug.md). Updatedocs/adr/index.mdin the same PR.
- Python 3.10+. Type hints encouraged on new code.
- Standard PEP 8; 4-space indent; double quotes; f-strings.
- Module-level docstrings on every public file; short docstrings on exported functions.
- Imports: stdlib β third-party β local, separated by blank lines; no star-imports.
- Side effects in
if __name__ == "__main__":only. - Tests: real-shape fixtures over mocks; round-trip tests on virtual buses preferred.
- New ADK tools:
@_adk_tooldecorator, SQL safety (LIMIT 500, non-SELECT rejected), file locks for any writes (fcntl.flock).
Notable changes by date in CHANGELOG.md. Architecture decisions in [docs/adr/](docs/adr/index.md).
TBD β to be added before public release.