Rewrite reference docs as current state; record the style rule
Reference, guidelines and plan documents describe what is and what will be. Retirement stories, resolved-risk narration and against-the-alternative argument moved out or were cut; reports keep that material. What survives the cut is the mechanism that can bite again (driving side is a country-level property) and the condition that would change a decision (a permanently installed, car-powered panel), both stated generally. Also removes a duplicated Japan section from a bad edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kj58Jugkic783b8ZrtTVZ
This commit is contained in:
+32
-33
@@ -91,8 +91,8 @@ rather than duplicated per client.
|
|||||||
|
|
||||||
### Swappable providers
|
### Swappable providers
|
||||||
|
|
||||||
The same shape has now been chosen independently three times, so it is the
|
A component with an expensive or not-yet-existing dependency puts it behind an
|
||||||
project's idiom rather than three coincidences:
|
interface, with a trivial implementation alongside the real one:
|
||||||
|
|
||||||
| Component | Interface | Real | Trivial |
|
| Component | Interface | Real | Trivial |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -194,27 +194,22 @@ owns the boundary. For transports without IP — BT SPP, plain USB serial —
|
|||||||
Because every display is a bus subscriber, adding, removing or swapping one
|
Because every display is a bus subscriber, adding, removing or swapping one
|
||||||
touches no acquisition code.
|
touches no acquisition code.
|
||||||
|
|
||||||
### Video out was dropped
|
### Directly-driven screens
|
||||||
|
|
||||||
An earlier revision had a second class: a screen driven directly by the Orin over
|
A screen driven over DP or HDMI is viable when it is **permanently installed and
|
||||||
SPI/DP/HDMI, rendered by a local process. It is gone, and with it `app-hud`.
|
powered by the car**. A dash-integrated panel qualifies: plug in DP and it is a
|
||||||
|
screen, and it adds a renderer without touching the contract, since the bus does
|
||||||
|
not care what draws it.
|
||||||
|
|
||||||
The reasoning, in order:
|
It is not viable for a removable display unit, which would need a second cable:
|
||||||
|
|
||||||
- **No connector-powered display exists.** DisplayPort's `DP_PWR` supplies 3.3 V
|
| Supply | Available | A panel needs |
|
||||||
at 500 mA — and VESA-compliant cables deliberately leave it unconnected at the
|
|---|---|---|
|
||||||
sink. HDMI's +5 V pin gives 55 mA. A panel needs 5–15 W. So a directly-driven
|
| DisplayPort `DP_PWR` | 3.3 V / 500 mA, and VESA cables leave it unconnected at the sink | 5–15 W |
|
||||||
screen always needs its own power feed, which is the cable the removable display
|
| HDMI +5 V | 55 mA | |
|
||||||
unit was meant to avoid.
|
|
||||||
- **DSI is not available.** The Orin Nano module carries DSI on SODIMM pins, but
|
|
||||||
the developer kit's carrier board does not break them out; DisplayPort is its
|
|
||||||
only display output. DSI would require a custom carrier board.
|
|
||||||
- **It bought nothing the browser-shaped path does not.** A data-only client on
|
|
||||||
one USB cable is simpler, and the display unit stays swappable.
|
|
||||||
|
|
||||||
Video out is what generated the enclosure, power and cabling complexity this
|
DSI is unavailable on this carrier board — the module carries it on SODIMM pins,
|
||||||
project started out trying to solve. Removing it removed the problem rather than
|
but the developer kit does not break them out, so DSI needs a custom carrier.
|
||||||
solving it.
|
|
||||||
|
|
||||||
## Navigation
|
## Navigation
|
||||||
|
|
||||||
@@ -252,8 +247,7 @@ Guidance is state, so it belongs on the bus: `yukart/nav/guidance` carries the
|
|||||||
next manoeuvre, retained, and a display connecting mid-drive renders immediately.
|
next manoeuvre, retained, and a display connecting mid-drive renders immediately.
|
||||||
|
|
||||||
Route *requests* are request/response and fit MQTT badly. Displays call the
|
Route *requests* are request/response and fit MQTT badly. Displays call the
|
||||||
Valhalla HTTP API through `telemetry-gw` instead of inventing a request/reply
|
Valhalla HTTP API through `svc-gw`. Recorded as decision 9.
|
||||||
topic pair. Recorded as decision 9.
|
|
||||||
|
|
||||||
### Map data is not source
|
### Map data is not source
|
||||||
|
|
||||||
@@ -287,18 +281,23 @@ checksum at both ends.
|
|||||||
| **CJK labels** | MapLibre cannot ship Japanese glyph sets as SDF ranges — far too large. Set `localIdeographFontFamily` so the browser renders CJK from on-device fonts. Protomaps tiles also carry `name:en`, so romanised labels are an option. |
|
| **CJK labels** | MapLibre cannot ship Japanese glyph sets as SDF ranges — far too large. Set `localIdeographFontFamily` so the browser renders CJK from on-device fonts. Protomaps tiles also carry `name:en`, so romanised labels are an option. |
|
||||||
| **Addressing** | Japanese addresses are block-based, not street-based. Valhalla does not geocode at all, so destination search is a separate problem — do not assume a western geocoder transfers. |
|
| **Addressing** | Japanese addresses are block-based, not street-based. Valhalla does not geocode at all, so destination search is a separate problem — do not assume a western geocoder transfers. |
|
||||||
|
|
||||||
### arm64 — resolved
|
### Verified on the Jetson
|
||||||
|
|
||||||
No longer a risk. Verified on `mikkeli-orin-nano-2` (L4T R36.4.7, JetPack 6) on
|
`ghcr.io/valhalla/valhalla` publishes `linux/arm64` and runs natively on
|
||||||
2026-09-04: `ghcr.io/valhalla/valhalla` publishes a `linux/arm64` manifest, the
|
L4T R36.4.7. Measured:
|
||||||
image runs natively, and the full pipeline works end to end — config, timezones,
|
|
||||||
admins, tiles, and a served route.
|
|
||||||
|
|
||||||
Measured: Shikoku, 49 MB PBF → 149 MB of routing tiles; Takamatsu → Matsuyama
|
| | |
|
||||||
routed in 157 km / 112 min with Japanese street names intact in the narrative.
|
|---|---|
|
||||||
|
| Shikoku PBF → routing tiles | 49 MB → 149 MB |
|
||||||
|
| Whole-Japan PBF | 2.4 GB |
|
||||||
|
| Admin DB, whole Japan | 17 MB |
|
||||||
|
| Timezone DB | 116 MB |
|
||||||
|
| Takamatsu → Matsuyama | 157 km, 112 min |
|
||||||
|
|
||||||
The server logs a benign `tiles.tar No such file or directory` warning and falls
|
Japanese street names survive intact in the route narrative.
|
||||||
back to the tile directory, which is the form decision 10 selected anyway.
|
|
||||||
|
The server logs `tiles.tar No such file or directory` at startup and falls back to
|
||||||
|
the tile directory, which is the form decision 10 selects. Not an error.
|
||||||
|
|
||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
@@ -318,12 +317,12 @@ rewriting the document.
|
|||||||
| 9 | Nav guidance published on the bus; route requests over HTTP via `telemetry-gw` | **Proposed** | 2026-09-03 |
|
| 9 | Nav guidance published on the bus; route requests over HTTP via `telemetry-gw` | **Proposed** | 2026-09-03 |
|
||||||
| 10 | Map artifacts delivered by `rsync` over LAN only; Valhalla tiles kept as a directory, not a tar | Accepted | 2026-09-03 |
|
| 10 | Map artifacts delivered by `rsync` over LAN only; Valhalla tiles kept as a directory, not a tar | Accepted | 2026-09-03 |
|
||||||
| 11 | Boot ordering is capture-first: acquisition reaches ready before serving or voice | Accepted | 2026-09-03 |
|
| 11 | Boot ordering is capture-first: acquisition reaches ready before serving or voice | Accepted | 2026-09-03 |
|
||||||
| 12 | The **tablet** owns the car's aux input; TTS crosses as chunked utterances, streaming-capable | Accepted | 2026-09-04 |
|
| 12 | The tablet owns the car's aux input; TTS crosses as chunked utterances, streaming-capable | Accepted | 2026-09-04 |
|
||||||
| 13 | Reasoning stays in the cloud — a local LLM does not fit beside the voice stack | Accepted | 2026-09-03 |
|
| 13 | Reasoning stays in the cloud — a local LLM does not fit beside the voice stack | Accepted | 2026-09-03 |
|
||||||
| 14 | Recordings are bounded with rotation | Accepted | 2026-09-04 |
|
| 14 | Recordings are bounded with rotation | Accepted | 2026-09-04 |
|
||||||
| 15 | Boundary is two simplex channels — lossy stream down, acknowledged request/response up | Accepted | 2026-09-03 |
|
| 15 | Boundary is two simplex channels — lossy stream down, acknowledged request/response up | Accepted | 2026-09-03 |
|
||||||
| 16 | Cloud links are outbound-initiated only; nothing calls in | Accepted | 2026-09-03 |
|
| 16 | Cloud links are outbound-initiated only; nothing calls in | Accepted | 2026-09-03 |
|
||||||
| 17 | `svc-gw` combines local hub and app gateway; no client may block the hub | Accepted | 2026-09-03 |
|
| 17 | `svc-gw` combines local hub and app gateway; no client may block the hub | Accepted | 2026-09-03 |
|
||||||
| 18 | Stubs publish the real topic and schema, never a fixture-specific one | Accepted | 2026-09-03 |
|
| 18 | Stubs publish the real topic and schema, never a fixture-specific one | Accepted | 2026-09-03 |
|
||||||
| 19 | Services mock themselves behind their own input interface; no separate stub implementations exist | Accepted | 2026-09-04 |
|
| 19 | A service mocks itself behind its own input interface; there is one implementation of each contract | Accepted | 2026-09-04 |
|
||||||
| 20 | Video out (Class A) dropped; every display is data-only. `app-hud` cancelled | Accepted | 2026-09-04 |
|
| 20 | Every display is data-only. Directly-driven screens are for permanently installed, car-powered panels only | Accepted | 2026-09-04 |
|
||||||
|
|||||||
+11
-14
@@ -44,9 +44,9 @@ have taken their share.
|
|||||||
|
|
||||||
Four rules follow.
|
Four rules follow.
|
||||||
|
|
||||||
**The cloud LLM is load-bearing.** [OVERVIEW.md](../OVERVIEW.md) puts reasoning in
|
**Reasoning runs in the cloud.** The local voice stack fits only because the LLM
|
||||||
the cloud. That decision is what makes a local voice stack fit at all — moving the
|
is not resident. An on-device LLM and the voice stack do not coexist in this
|
||||||
LLM on-device collapses this budget entirely. It is not a free choice to revisit.
|
budget.
|
||||||
|
|
||||||
**Size Whisper by vocabulary, not by benchmark.** In-car commands are a narrow,
|
**Size Whisper by vocabulary, not by benchmark.** In-car commands are a narrow,
|
||||||
predictable set. `large` buys accuracy this application cannot spend, at a cost
|
predictable set. `large` buys accuracy this application cannot spend, at a cost
|
||||||
@@ -156,10 +156,10 @@ Same protocol, same receiver. The receiver starts playback on the first chunk,
|
|||||||
appends as more arrive, and finishes on the terminator — which is what Android's
|
appends as more arrive, and finishes on the terminator — which is what Android's
|
||||||
`AudioTrack` streaming mode does natively.
|
`AudioTrack` streaming mode does natively.
|
||||||
|
|
||||||
**What must not be built:** an utterance as a single response body with a
|
⚠ **Never ship an utterance as a single response body with a "download, then
|
||||||
"download the file, then play the file" player. That works today and forecloses
|
play" player.** It works, and it forecloses streaming permanently — the receiver
|
||||||
streaming permanently. Whether the TTS we land on streams is still open, so the
|
has no way to begin before the last byte. The chunk boundary is what keeps both
|
||||||
transport must not assume either way.
|
TTS modes available.
|
||||||
|
|
||||||
### The transport is testable with no real TTS
|
### The transport is testable with no real TTS
|
||||||
|
|
||||||
@@ -171,10 +171,8 @@ completely. Voice quality is irrelevant to every one of those.
|
|||||||
### Ducking is the tablet's job
|
### Ducking is the tablet's job
|
||||||
|
|
||||||
Android's `AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK` ducks other applications' audio —
|
Android's `AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK` ducks other applications' audio —
|
||||||
Spotify included — for the duration of a request, then restores it. This is the
|
Spotify included — for the duration of a request, then restores it. Cross-
|
||||||
mechanism every navigation app already uses, and it is less work than the PipeWire
|
application ducking is native on the tablet.
|
||||||
policy it replaces. Cross-application ducking is a solved problem on the tablet
|
|
||||||
and would have been a bespoke one on the Orin.
|
|
||||||
|
|
||||||
### The microphone stays on the Orin
|
### The microphone stays on the Orin
|
||||||
|
|
||||||
@@ -188,6 +186,5 @@ With no tablet connected the Orin can still hear — the mic is local — but ca
|
|||||||
speak. The voice stack expires or drops pending utterances rather than queueing
|
speak. The voice stack expires or drops pending utterances rather than queueing
|
||||||
them unboundedly or blocking on a sink that may never arrive.
|
them unboundedly or blocking on a sink that may never arrive.
|
||||||
|
|
||||||
The consequence to accept: **unplug the tablet and the car has no voice.** That is
|
**The tablet is required for any audio at all** — it is both the media source and
|
||||||
tolerable because unplugging it also removes the music, so the tablet was already
|
the speech sink.
|
||||||
load-bearing for audio. Speech does not make it more so.
|
|
||||||
|
|||||||
+32
-12
@@ -115,10 +115,8 @@ it records the investigation and the dead ends, not just the final commands.
|
|||||||
|
|
||||||
### Scaffolding conventions
|
### Scaffolding conventions
|
||||||
|
|
||||||
No template repository. Template instantiation is fork-at-create, so it could not
|
Component repos are scaffolded by hand. A template repo is for carrying a real
|
||||||
reach repos that already exist, and the scaffolding is a handful of markdown files
|
toolchain, which is the ESP32 case above.
|
||||||
— lighter than the machinery to manage it. A template earns its place where there
|
|
||||||
is a real toolchain to carry, which is the ESP32 case above.
|
|
||||||
|
|
||||||
Every component repo carries:
|
Every component repo carries:
|
||||||
|
|
||||||
@@ -131,6 +129,33 @@ docs/impl/reports/
|
|||||||
docs/BOM.md only where the repo owns hardware
|
docs/BOM.md only where the repo owns hardware
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Writing style
|
||||||
|
|
||||||
|
Documents describe **what is and what will be**. They are not changelogs — git
|
||||||
|
holds the sequence.
|
||||||
|
|
||||||
|
| Instead of | Write |
|
||||||
|
|---|---|
|
||||||
|
| "X was dropped because Y" | what the design **is** |
|
||||||
|
| "this was a risk, now resolved" | what runs, and the measurements |
|
||||||
|
| "the earlier decision assumed Z" | the current constraint |
|
||||||
|
| "X was rejected in favour of Y" | Y, stated as the rule |
|
||||||
|
|
||||||
|
⚠ **A sentence justifying a current state by a current reason becomes false when
|
||||||
|
the reason changes, and nothing announces it** — it stays, confident and
|
||||||
|
well-written, and reads as fact.
|
||||||
|
|
||||||
|
Two things survive the cut, because they stay true when their subject is gone:
|
||||||
|
|
||||||
|
- **A mechanism that can bite again.** Not "we picked the wrong extract once", but
|
||||||
|
"driving side is a country-level property, so a clipped extract routes mirrored".
|
||||||
|
- **A condition that would change the decision**, stated generally — "a
|
||||||
|
permanently installed, car-powered panel" rather than a story about a panel that
|
||||||
|
was considered.
|
||||||
|
|
||||||
|
Reports under `impl/reports/` are the exception and the place for all of it: the
|
||||||
|
investigation, the dead ends, what was measured and found wrong.
|
||||||
|
|
||||||
### Front matter
|
### Front matter
|
||||||
|
|
||||||
Every markdown document carries front matter, with **no exceptions for the hub or
|
Every markdown document carries front matter, with **no exceptions for the hub or
|
||||||
@@ -145,14 +170,9 @@ for any particular repo**. The `type` discriminates:
|
|||||||
|
|
||||||
`status` is one of `active`, `superseded` or `done`.
|
`status` is one of `active`, `superseded` or `done`.
|
||||||
|
|
||||||
The extra fields exist for one reason. This document already observes that **plans
|
`status` and `updated` make a plan's staleness visible — plans go stale and
|
||||||
go stale and reports do not** — but nothing made staleness visible, so a plan
|
reports do not, and without them a plan from months ago reads identically to one
|
||||||
written months ago looked identical to one written this morning. `status` and
|
written this morning. `date` on a report records the moment it describes.
|
||||||
`updated` fix that; `date` on a report records the moment it describes, which is
|
|
||||||
the whole value of a report.
|
|
||||||
|
|
||||||
A `type` with one possible value would be decoration. This one has four and they
|
|
||||||
are worth grepping for.
|
|
||||||
|
|
||||||
## Naming
|
## Naming
|
||||||
|
|
||||||
|
|||||||
@@ -59,9 +59,6 @@ Order chosen by feedback speed, not ambition.
|
|||||||
quickest to iterate on, and is the only display in current scope
|
quickest to iterate on, and is the only display in current scope
|
||||||
- [ ] `app-esp32` — RLCD/e-ink glanceable subset. Deferred, no hardware in scope
|
- [ ] `app-esp32` — RLCD/e-ink glanceable subset. Deferred, no hardware in scope
|
||||||
|
|
||||||
Video out was dropped along with `app-hud`; see the display contract in
|
|
||||||
[ARCHITECTURE.md](ARCHITECTURE.md#display-contract).
|
|
||||||
|
|
||||||
## Phase 5 — Camera and inference · *separate task*
|
## Phase 5 — Camera and inference · *separate task*
|
||||||
|
|
||||||
Deliberately deferred. It is the reason the compute is a Jetson rather than a
|
Deliberately deferred. It is the reason the compute is a Jetson rather than a
|
||||||
|
|||||||
Reference in New Issue
Block a user