style: apply oxfmt to docs (table padding, italic style)

This commit is contained in:
Julien Calixte
2026-05-17 01:30:36 +02:00
parent 1e1e59e716
commit ffcd49588a
5 changed files with 111 additions and 111 deletions

View File

@@ -34,15 +34,15 @@ up across nine downstream releases.
### Options considered
| Option | Pros | Cons |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| Option | Pros | Cons |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **C on ESP-IDF (no Arduino)** | Reference platform on the bare native SDK; every peripheral has a driver; smallest binary of the C-family options; no C++ runtime / exceptions / RTTI to reason about. | All memory safety on you; no RAII for resource cleanup; no generics so widget / state code gets repetitive; refactoring at scale is painful. |
| **C++ on ESP-IDF** | Same peripheral coverage as C; RAII, templates, and `std::` containers ease widget / state code; mature in the ESP-IDF examples. | Exception / RTTI story on embedded is messy; ABI / linker surprises; memory safety still on you; binary larger than plain C. |
| **Rust on `esp-idf-rs` (std)** | First-class Espressif-sponsored Rust support; `std` gives heap / threads / VFS / mbedtls; can use the broader Rust ecosystem (`gitoxide`, `ropey`, `embedded-graphics`). | Larger binary than `no_std`; longer build times; some `unsafe` at FFI seams. |
| **Rust on `esp-hal` (no_std)** | Smallest binary, most "pure" embedded experience. | No `std` = no off-the-shelf git, no easy TLS, would re-implement a lot of plumbing. |
| **Gleam + Shore on AtomVM** | Beautiful language, the user's stated preference. | BEAM on ESP32 is memory-hungry; no bindings for USB host, e-ink, SD, TLS, git in that ecosystem. Two research projects stacked. |
| **MicroPython / CircuitPython** | Fastest to prototype. | Too slow for responsive editing at the latencies e-ink already imposes; GC pauses would surface as dropped keys. |
| **TinyGo** | Modern, ergonomic. | ESP32-S3 support is thinner than Rust's; smaller ecosystem of embedded crates equivalents. |
| **C++ on ESP-IDF** | Same peripheral coverage as C; RAII, templates, and `std::` containers ease widget / state code; mature in the ESP-IDF examples. | Exception / RTTI story on embedded is messy; ABI / linker surprises; memory safety still on you; binary larger than plain C. |
| **Rust on `esp-idf-rs` (std)** | First-class Espressif-sponsored Rust support; `std` gives heap / threads / VFS / mbedtls; can use the broader Rust ecosystem (`gitoxide`, `ropey`, `embedded-graphics`). | Larger binary than `no_std`; longer build times; some `unsafe` at FFI seams. |
| **Rust on `esp-hal` (no_std)** | Smallest binary, most "pure" embedded experience. | No `std` = no off-the-shelf git, no easy TLS, would re-implement a lot of plumbing. |
| **Gleam + Shore on AtomVM** | Beautiful language, the user's stated preference. | BEAM on ESP32 is memory-hungry; no bindings for USB host, e-ink, SD, TLS, git in that ecosystem. Two research projects stacked. |
| **MicroPython / CircuitPython** | Fastest to prototype. | Too slow for responsive editing at the latencies e-ink already imposes; GC pauses would surface as dropped keys. |
| **TinyGo** | Modern, ergonomic. | ESP32-S3 support is thinner than Rust's; smaller ecosystem of embedded crates equivalents. |
### Decision
@@ -113,12 +113,12 @@ Owns the two top-ranked functions (H1 latency, H2 region area) in
### Context
The display has the largest downstream blast radius of any hardware choice.
The *medium* (e-ink vs. LCD vs. memory LCD vs. OLED) — not the specific
The _medium_ (e-ink vs. LCD vs. memory LCD vs. OLED) — not the specific
panel — is the real architectural decision: it sets the render strategy
([ADR-002]), the per-keystroke latency floor, the idle-power profile (and
so the v0.8 battery story — [ADR-008]), the UX posture, and the BOM shape.
The specific panel (GDEY0579T93 + DESPI-c579 breakout) is already on hand
and documented here as the *instantiation*, not as a freshly weighed option.
and documented here as the _instantiation_, not as a freshly weighed option.
This ADR records the medium choice with eyes open. E-ink has well-known
costs at the typing latencies a writing appliance wants — Astrohaus shipped
@@ -128,12 +128,12 @@ costs the category leader retreated from.
### Options considered
| Option | Refresh / persistence | Pros | Cons |
| --------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **E-ink (reflective, image-persistent)** | ~100300 ms partial / ~7001000 ms full / persists at 0 W | Paper aesthetic; persists at zero idle power; no backlight (no glare, no eye strain); category convention (Freewrite, reMarkable, Kindle Scribe, Boox); medium enforces writing posture | Slow per-keystroke feedback; ghosting accumulates → periodic full-refresh flash; scroll is the worst-case refresh op (full edit-area redraw); requires waveform / refresh-cadence tuning; Astrohaus retreated from e-ink in Freewrite Alpha (2023) on typing-latency grounds |
| **FSTN graphical LCD (monochrome)** | <16 ms, no refresh quirks | Cheap (~$515); trivial render code; snappy scroll | Backlit (always-on power), unreadable indoors without it; no image persistence; calculator / feature-phone aesthetic; writing-grade resolution (≥600 px wide at ≥6") effectively unavailable as a hobbyist part |
| **Sharp Memory LCD (monochrome, reflective)** | ~20 ms, persists at near-0 W | Persists *and* refreshes fast (best technical combo); sun-readable; ghost-free | Caps around 4.4" before getting rare and expensive; reflective-only feels like a screen, not paper; niche sourcing; lower DPI than e-ink at writing size |
| **TFT / OLED (color, self-lit or backlit)** | <16 ms, persists only at full power | Bright, fast, plentiful | Backlit / self-lit → screen-feel, not paper; OLED burns in static text (status line, header); defeats the writing-tool posture; not seriously a contender |
| Option | Refresh / persistence | Pros | Cons |
| --------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **E-ink (reflective, image-persistent)** | ~100300 ms partial / ~7001000 ms full / persists at 0 W | Paper aesthetic; persists at zero idle power; no backlight (no glare, no eye strain); category convention (Freewrite, reMarkable, Kindle Scribe, Boox); medium enforces writing posture | Slow per-keystroke feedback; ghosting accumulates → periodic full-refresh flash; scroll is the worst-case refresh op (full edit-area redraw); requires waveform / refresh-cadence tuning; Astrohaus retreated from e-ink in Freewrite Alpha (2023) on typing-latency grounds |
| **FSTN graphical LCD (monochrome)** | <16 ms, no refresh quirks | Cheap (~$515); trivial render code; snappy scroll | Backlit (always-on power), unreadable indoors without it; no image persistence; calculator / feature-phone aesthetic; writing-grade resolution (≥600 px wide at ≥6") effectively unavailable as a hobbyist part |
| **Sharp Memory LCD (monochrome, reflective)** | ~20 ms, persists at near-0 W | Persists _and_ refreshes fast (best technical combo); sun-readable; ghost-free | Caps around 4.4" before getting rare and expensive; reflective-only feels like a screen, not paper; niche sourcing; lower DPI than e-ink at writing size |
| **TFT / OLED (color, self-lit or backlit)** | <16 ms, persists only at full power | Bright, fast, plentiful | Backlit / self-lit → screen-feel, not paper; OLED burns in static text (status line, header); defeats the writing-tool posture; not seriously a contender |
### Decision
@@ -182,7 +182,7 @@ as the price of those properties.
sizing exercise straightforward — see [ADR-008] and
[roadmap → v0.8](roadmap.md#v08--power-battery--sleep--).
- 10.3" e-ink upgrade path is preserved by keeping the renderer
resolution-agnostic. A *non*-e-ink swap (e.g. Sharp Memory LCD) would
resolution-agnostic. A _non_-e-ink swap (e.g. Sharp Memory LCD) would
invalidate [ADR-002]'s dirty-rect strategy and force a fresh medium ADR.
---
@@ -283,12 +283,12 @@ the scale where async wins. The number of "tasks" is bounded and small (≤ 8).
### Options considered
| Option | Pros | Cons |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **`std::thread` + channels** | Boring, debuggable, stack traces work, no executor to tune; ESP-IDF FreeRTOS underneath is well-understood. | Each thread costs 832 KB stack depending on workload; not zero-cost like async. |
| Option | Pros | Cons |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **`std::thread` + channels** | Boring, debuggable, stack traces work, no executor to tune; ESP-IDF FreeRTOS underneath is well-understood. | Each thread costs 832 KB stack depending on workload; not zero-cost like async. |
| **`embassy` async** | Trendy, ergonomic, low memory per task. | `esp-idf-rs` and `embassy` don't mix cleanly; adopting embassy means dropping `std` and rewriting against `esp-hal` ([ADR-001] reversed). |
| **`tokio` on `esp-idf-rs`** | Familiar async. | Heavy executor, oversized for ≤ 8 tasks, mbedtls/`gitoxide` integration would need a lot of glue. |
| **Single-threaded event loop** | Smallest memory. | Long-running ops (git push, full refresh) block input. |
| **`tokio` on `esp-idf-rs`** | Familiar async. | Heavy executor, oversized for ≤ 8 tasks, mbedtls/`gitoxide` integration would need a lot of glue. |
| **Single-threaded event loop** | Smallest memory. | Long-running ops (git push, full refresh) block input. |
### Decision
@@ -431,11 +431,11 @@ with a `git pull --no-edit` fallback when the push fails non-fast-forward.
### Options considered
| Option | Pros | Cons |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Three separate gestures** (save / commit / push) | Maximally git-native; user has fine control. | Three keys to remember, three failure modes to surface, three concepts in the user's head. Wrong shape for an appliance whose job is to remove ceremony. |
| **One gesture, prompt for message** (`Ctrl-G` → modal asking for message → commit → push) | Conventional "publish" pattern; each commit is named. | A modal prompt on e-ink is hostile (latency, full refresh); the user's actual workflow (`gct`) explicitly avoids authoring messages; messages would be noise (`"updated notes"` × 1000). |
| **One gesture, auto-timestamp message** (`Ctrl-G` mirrors `gct`) | Matches the user's real workflow; one key, one outcome; no prompts, no modes, no decisions in the writing path. | Commit history is timestamp-noise (useless for code archaeology); a future reader will wonder where the commit messages went; locks in a UX assumption that's hard to undo without breaking muscle memory. |
| Option | Pros | Cons |
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Three separate gestures** (save / commit / push) | Maximally git-native; user has fine control. | Three keys to remember, three failure modes to surface, three concepts in the user's head. Wrong shape for an appliance whose job is to remove ceremony. |
| **One gesture, prompt for message** (`Ctrl-G` → modal asking for message → commit → push) | Conventional "publish" pattern; each commit is named. | A modal prompt on e-ink is hostile (latency, full refresh); the user's actual workflow (`gct`) explicitly avoids authoring messages; messages would be noise (`"updated notes"` × 1000). |
| **One gesture, auto-timestamp message** (`Ctrl-G` mirrors `gct`) | Matches the user's real workflow; one key, one outcome; no prompts, no modes, no decisions in the writing path. | Commit history is timestamp-noise (useless for code archaeology); a future reader will wonder where the commit messages went; locks in a UX assumption that's hard to undo without breaking muscle memory. |
### Decision
@@ -447,7 +447,7 @@ retry). Failure surfaces as a single retry-able outcome in the status line.
### Consequences
- The user's vocabulary collapses to **Save** and **Publish**;
[`CONTEXT.md`](../CONTEXT.md#user-facing-actions) pins this — *commit* is
[`CONTEXT.md`](../CONTEXT.md#user-facing-actions) pins this — _commit_ is
not a user-facing term.
- Commit history is a stream of timestamps. The device is a writing tool, not
a code repository — the history is here for recoverability, not narrative.

View File

@@ -27,22 +27,22 @@ the downstream sections (§5§8).
What a user (= me) values about the device, with importance weights on a
110 scale. Source columns point at the doc the requirement comes from.
| ID | Requirement | Weight | Source |
| --- | ------------------------------------------------------- | :----: | ------------------------------------------------------------------------------------------------------------------ |
| W1 | Sub-second visible response to typing | 10 | [product → Write](v0.1-mvp-product.md#user-stories), [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W2 | **Publishing** is one deliberate action away | 9 | [product → Publish](v0.1-mvp-product.md#user-stories), [CONTEXT → Publish](../CONTEXT.md#user-facing-actions) |
| W3 | Pulling power never corrupts the file | 10 | [product → Recover](v0.1-mvp-product.md#user-stories), [acceptance](v0.1-mvp-product.md#acceptance-criteria) |
| W4 | Provisioning never interrupts a writing session | 7 | [product → Provisioning](v0.1-mvp-product.md#provisioning-build-time-dev-only), [roadmap → v0.9](roadmap.md#v09--robustness--) |
| W5 | Quick boot to a writing cursor | 6 | [product → acceptance](v0.1-mvp-product.md#acceptance-criteria) (≤ 5 s) |
| W6 | Long sessions without crash / lag / drift | 9 | [product → acceptance](v0.1-mvp-product.md#acceptance-criteria) (1 h soak) |
| W7 | Nothing on the device competes with prose | 8 | [README → vision](../README.md#vision) |
| W8 | The UI never moves except when I move it | 7 | [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W9 | Codebase absorbs the planned roadmap without rewrite | 8 | [roadmap](roadmap.md) |
| W10 | I can repair or fork it with hobbyist tools | 5 | [README → vision](../README.md#vision) |
| W11 | Multi-day battery life (v0.8 onward) | 4 | [roadmap → v0.8](roadmap.md#v08--power-battery--sleep--) |
| W12 | Local-only file scope coexists with git scope (v0.5+) | 5 | [README → scopes](../README.md#vision), [roadmap → v0.5](roadmap.md#v05--file-palette--multi-file--) |
| W13 | Typography sets a writing-tool tone — typewriter or developer editor, never gadget | 7 | [roadmap → v1.0](roadmap.md), [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W14 | I can carry the device and write away from a desk | 8 | [roadmap → v0.8](roadmap.md#v08--power-battery--sleep--), [README → hardware](../README.md#hardware) |
| ID | Requirement | Weight | Source |
| --- | ---------------------------------------------------------------------------------- | :----: | ------------------------------------------------------------------------------------------------------------------------------ |
| W1 | Sub-second visible response to typing | 10 | [product → Write](v0.1-mvp-product.md#user-stories), [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W2 | **Publishing** is one deliberate action away | 9 | [product → Publish](v0.1-mvp-product.md#user-stories), [CONTEXT → Publish](../CONTEXT.md#user-facing-actions) |
| W3 | Pulling power never corrupts the file | 10 | [product → Recover](v0.1-mvp-product.md#user-stories), [acceptance](v0.1-mvp-product.md#acceptance-criteria) |
| W4 | Provisioning never interrupts a writing session | 7 | [product → Provisioning](v0.1-mvp-product.md#provisioning-build-time-dev-only), [roadmap → v0.9](roadmap.md#v09--robustness--) |
| W5 | Quick boot to a writing cursor | 6 | [product → acceptance](v0.1-mvp-product.md#acceptance-criteria) (≤ 5 s) |
| W6 | Long sessions without crash / lag / drift | 9 | [product → acceptance](v0.1-mvp-product.md#acceptance-criteria) (1 h soak) |
| W7 | Nothing on the device competes with prose | 8 | [README → vision](../README.md#vision) |
| W8 | The UI never moves except when I move it | 7 | [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W9 | Codebase absorbs the planned roadmap without rewrite | 8 | [roadmap](roadmap.md) |
| W10 | I can repair or fork it with hobbyist tools | 5 | [README → vision](../README.md#vision) |
| W11 | Multi-day battery life (v0.8 onward) | 4 | [roadmap → v0.8](roadmap.md#v08--power-battery--sleep--) |
| W12 | Local-only file scope coexists with git scope (v0.5+) | 5 | [README → scopes](../README.md#vision), [roadmap → v0.5](roadmap.md#v05--file-palette--multi-file--) |
| W13 | Typography sets a writing-tool tone — typewriter or developer editor, never gadget | 7 | [roadmap → v1.0](roadmap.md), [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W14 | I can carry the device and write away from a desk | 8 | [roadmap → v0.8](roadmap.md#v08--power-battery--sleep--), [README → hardware](../README.md#hardware) |
---
@@ -189,24 +189,24 @@ constrains the choice.
Components (with anchoring ADR):
| ID | Component | ADR |
| --- | ------------------------------------ | ---------------------- |
| C1 | ESP32-S3-N16R8 SoC | [ADR-001], [ADR-008] |
| C2 | `esp-idf-rs` (std) + ESP-IDF | [ADR-001] |
| C3 | `std::thread` + `crossbeam-channel` | [ADR-006] |
| C4 | PSRAM allocator wrapper | [ADR-001] |
| C5 | GDEY0579T93 + DESPI-c579 panel | [ADR-003] |
| C6 | `embedded-graphics` + e-paper driver | [ADR-002], [ADR-003] |
| C7 | Custom widget / dirty-rect layer | [ADR-002] |
| C8 | `ropey` rope buffer | [ADR-001] (ecosystem) |
| C9 | TinyUSB host (`esp-idf` bindings) | [ADR-009] |
| C10 | FAT on microSD | [ADR-007] |
| C11 | LittleFS on internal flash | [ADR-007] |
| C12 | `gitoxide` (`gix-*`) | [ADR-004] |
| C13 | mbedtls TLS (via ESP-IDF) | [ADR-005] |
| C14 | HTTPS + GitHub PAT auth | [ADR-005] |
| C15 | eFuse-derived encryption key | [ADR-005], [ADR-007] |
| C16 | USB-C wall PSU | [ADR-008] |
| ID | Component | ADR |
| --- | ------------------------------------ | --------------------- |
| C1 | ESP32-S3-N16R8 SoC | [ADR-001], [ADR-008] |
| C2 | `esp-idf-rs` (std) + ESP-IDF | [ADR-001] |
| C3 | `std::thread` + `crossbeam-channel` | [ADR-006] |
| C4 | PSRAM allocator wrapper | [ADR-001] |
| C5 | GDEY0579T93 + DESPI-c579 panel | [ADR-003] |
| C6 | `embedded-graphics` + e-paper driver | [ADR-002], [ADR-003] |
| C7 | Custom widget / dirty-rect layer | [ADR-002] |
| C8 | `ropey` rope buffer | [ADR-001] (ecosystem) |
| C9 | TinyUSB host (`esp-idf` bindings) | [ADR-009] |
| C10 | FAT on microSD | [ADR-007] |
| C11 | LittleFS on internal flash | [ADR-007] |
| C12 | `gitoxide` (`gix-*`) | [ADR-004] |
| C13 | mbedtls TLS (via ESP-IDF) | [ADR-005] |
| C14 | HTTPS + GitHub PAT auth | [ADR-005] |
| C15 | eFuse-derived encryption key | [ADR-005], [ADR-007] |
| C16 | USB-C wall PSU | [ADR-008] |
Function-to-component matrix (9 strong / 3 medium / 1 weak):
@@ -237,7 +237,7 @@ Function-to-component matrix (9 strong / 3 medium / 1 weak):
touch it. That's why [ADR-004] includes a kill-switch (fall back to
`libgit2-sys` if spike 7 fails). It's also why H9 sits in the top three
priorities — `gitoxide`'s memory profile is the unknown.
[ADR-010] pins the *shape* of the publish sequence (the `gct` flow); C12
[ADR-010] pins the _shape_ of the publish sequence (the `gct` flow); C12
is just the library that implements it. Changing [ADR-010] doesn't change
C12's column, but changing C12 (the kill-switch) does not change
[ADR-010]'s user contract.
@@ -261,16 +261,16 @@ also lifted H8 durability over its narrow voter base; W14 has widened
that base, so H8's #3 spot is now arithmetic — see §3.) These are the
numbers spikes 27 must validate before integration starts.
| Rank | Function | Target | Watched on | If we miss it |
| ---- | -------------- | ------------------------------------- | ----------------- | ----------------------------------------------------------------------- |
| Rank | Function | Target | Watched on | If we miss it |
| ---- | -------------- | ------------------------------------- | ----------------- | ------------------------------------------------------------------------- |
| 1 | H2 region area | ≤ 1 line per keypress | spike 2 + spike 5 | Increase font size to shrink per-glyph dirty rect ([ADR-003] consequence) |
| 2 | H9 PSRAM heap | ≥ 1 MB free at push peak | spike 7 | [ADR-004] kill-switch → `libgit2-sys`; cap rope at 128 KB |
| 3 | H8 durability | 100 % survive power yank after status | bench HIL | Re-evaluate [ADR-007] (move config to internal NVS only) |
| 4 | H1 latency | ≤ 200 ms keypress→glyph | spike 5 | Larger partial-refresh region; render multi-char bursts |
| 5 | H6 push % | ≥ 95 % on healthy Wi-Fi | spike 6 + spike 7 | TLS cipher trim; reconnect backoff tuning |
| 6 | H3 cadence | full every ~20 partials | spike 2 | Adjust per panel temperature; defer flash to idle ≥ 1 s |
| 7 | H4 boot | ≤ 5 s to cursor | integration smoke | Trim startup logging; lazy-mount SD after splash |
| 8 | H5 soak | 1 h no leak / no drop | 1 h bench soak | Glyph-cache eviction; PSRAM heap-fragmentation review |
| 4 | H1 latency | ≤ 200 ms keypress→glyph | spike 5 | Larger partial-refresh region; render multi-char bursts |
| 5 | H6 push % | ≥ 95 % on healthy Wi-Fi | spike 6 + spike 7 | TLS cipher trim; reconnect backoff tuning |
| 6 | H3 cadence | full every ~20 partials | spike 2 | Adjust per panel temperature; defer flash to idle ≥ 1 s |
| 7 | H4 boot | ≤ 5 s to cursor | integration smoke | Trim startup logging; lazy-mount SD after splash |
| 8 | H5 soak | 1 h no leak / no drop | 1 h bench soak | Glyph-cache eviction; PSRAM heap-fragmentation review |
The two not-in-MVP rows but already-shaped-by-design:
@@ -283,18 +283,18 @@ The two not-in-MVP rows but already-shaped-by-design:
Plain-language summary of what we accepted in exchange for what.
| Tradeoff | Got | Paid | ADR |
| ------------------------------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------- |
| std (esp-idf-rs) over no_std (esp-hal) | Heap, threads, VFS, mbedtls, gitoxide-compatible | +1 MB binary, +510 min builds | [ADR-001] |
| Custom widget layer over Ratatui | Dirty-rects aligned to e-ink regions; 200 KB binary back | 500 LoC we own and maintain | [ADR-002] |
| e-ink medium over FSTN / memory LCD / OLED | Paper aesthetic; 0 W idle persistence; medium enforces writing posture | ~200300 ms typing latency; periodic full-refresh flash (scroll worst-case) | [ADR-003] |
| `gitoxide` over `libgit2-sys` | Pure Rust, modular, no FFI cross-compile pain | Smart-HTTP path is newer; PSRAM profile unproven (spike 7) | [ADR-004] |
| HTTPS + PAT over OAuth device-flow or SSH | Simplest auth that `gitoxide` smart-HTTP already supports | Long-lived secret on device; in v0.1 the PAT is compiled into the binary (dev-only target user makes this acceptable); v0.9 moves it to encrypted NVS | [ADR-005] |
| `std::thread` over `embassy` or `tokio` | Boring, debuggable, real stack traces; no exec to tune | ~76 KB total stack across 5 tasks | [ADR-006] |
| FAT-on-SD + LittleFS-on-flash split | Desktop can read SD; config survives SD reformat | Two filesystems to manage; FAT's power-loss weakness mitigated by atomic-rename | [ADR-007] |
| Wall power for v0.1, battery deferred | Measure real draw before sizing the cell | Tethered MVP; not the final aesthetic | [ADR-008] |
| USB host (TinyUSB) over BLE-HID | No radio contention with Wi-Fi during push; keyboard powered from the device | One more USB connector on enclosure | [ADR-009] |
| Atomic `Ctrl-G` + auto-timestamp commit message | One key, one outcome; matches the user's existing `gct` workflow; no modal prompt to slow H1 latency | Commit history is timestamp noise; the device may author merge commits the user never sees; reversal would break muscle memory | [ADR-010] |
| Tradeoff | Got | Paid | ADR |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| std (esp-idf-rs) over no_std (esp-hal) | Heap, threads, VFS, mbedtls, gitoxide-compatible | +1 MB binary, +510 min builds | [ADR-001] |
| Custom widget layer over Ratatui | Dirty-rects aligned to e-ink regions; 200 KB binary back | 500 LoC we own and maintain | [ADR-002] |
| e-ink medium over FSTN / memory LCD / OLED | Paper aesthetic; 0 W idle persistence; medium enforces writing posture | ~200300 ms typing latency; periodic full-refresh flash (scroll worst-case) | [ADR-003] |
| `gitoxide` over `libgit2-sys` | Pure Rust, modular, no FFI cross-compile pain | Smart-HTTP path is newer; PSRAM profile unproven (spike 7) | [ADR-004] |
| HTTPS + PAT over OAuth device-flow or SSH | Simplest auth that `gitoxide` smart-HTTP already supports | Long-lived secret on device; in v0.1 the PAT is compiled into the binary (dev-only target user makes this acceptable); v0.9 moves it to encrypted NVS | [ADR-005] |
| `std::thread` over `embassy` or `tokio` | Boring, debuggable, real stack traces; no exec to tune | ~76 KB total stack across 5 tasks | [ADR-006] |
| FAT-on-SD + LittleFS-on-flash split | Desktop can read SD; config survives SD reformat | Two filesystems to manage; FAT's power-loss weakness mitigated by atomic-rename | [ADR-007] |
| Wall power for v0.1, battery deferred | Measure real draw before sizing the cell | Tethered MVP; not the final aesthetic | [ADR-008] |
| USB host (TinyUSB) over BLE-HID | No radio contention with Wi-Fi during push; keyboard powered from the device | One more USB connector on enclosure | [ADR-009] |
| Atomic `Ctrl-G` + auto-timestamp commit message | One key, one outcome; matches the user's existing `gct` workflow; no modal prompt to slow H1 latency | Commit history is timestamp noise; the device may author merge commits the user never sees; reversal would break muscle memory | [ADR-010] |
### Conflicts left explicitly _unresolved_ by v0.1
@@ -336,7 +336,7 @@ These are the live tensions we are watching, not deciding harder:
comfortably in the ESP32-S3's 512 KB internal SRAM, so no design
change — just documentation accuracy.
- **Commit-message format triple-mismatch.** README said `git commit -m
"wip"`, the v0.1 product doc said `"wip <timestamp>"`, and the user's
"wip"`, the v0.1 product doc said `"wip <timestamp>"`, and the user's
actual shell alias (`gct` / `git-commit-timestamp`) uses a pure ISO-8601
timestamp with no `wip` prefix. Resolved by aligning all docs on `gct`
and recording the decision as
@@ -363,7 +363,7 @@ These are the live tensions we are watching, not deciding harder:
artifact, not a signal that durability matters less to the design.
- **W13 reframed, W14 removed.** Earlier W13/W14 rows named solutions
("beautiful monospace", "beautiful serif") inside the requirements
column, conflating *what the user values* with *which asset delivers it*.
column, conflating _what the user values_ with _which asset delivers it_.
Replaced with one outcome WHAT — typography sets a writing-tool tone —
and moved the mono+serif option to §7 as a v1.0 unresolved tension.
Σ shifted (H9 205→193, H2 198→177, H1 155→148) because the prior

View File

@@ -590,22 +590,22 @@ number), W9 both 1→2 (Rust rewrite explicitly unblocked features that
JS could not carry; still closed so neither reaches reMarkable's
hackable-Linux 3).
| ID | WHAT (truncated) | Typoena | reM. | Frw.T | Frw.S | Pom. | Rationale (shortest defensible) |
| --- | ----------------------------------------------- | :--: | :--: | :---: | :---: | :--: | ---------------------------------------------------------------------------------------------------------------------------- |
| W1 | Sub-second response to typing | 4 | 2 | 4 | 3 | 5 | Typoena targets ≤200 ms; reMarkable e-ink visibly laggy — tested less responsive than Smart Typewriter; both Freewrites post-Sailfish trimmed latency 40100 % (Frw.T plausibly inside 200 ms; Frw.S still trails by one notch on larger panel); Pomera LCD ~zero. |
| W2 | Publishing is one deliberate action away | 5 | 4 | 4 | 4 | 2 | Ctrl-G atomic; reMarkable + Freewrite cloud-sync is one-tap but not git; Pomera = USB/SD copy or QR transfer. |
| W3 | Pulling power never corrupts the file | 4 | 4 | 2 | 2 | 2 | Typoena: atomic-rename + fsync. reMarkable journals. Freewrite + Pomera: forum reports of corruption on yank. |
| W4 | Provisioning never interrupts writing | 5 | 2 | 2 | 2 | 5 | Typoena v0.1: build-time config (dev-only). reM/Frw need Wi-Fi + account. Pomera: literally none. |
| W5 | Quick boot to a writing cursor | 4 | 3 | 4 | 4 | 5 | Typoena target ≤5 s. reMarkable cold-boots ~20 s (great from sleep). Both Freewrites accelerated post-Sailfish (no published number; were ~1015 s e-ink wake). Pomera ~3 s. |
| W6 | Long sessions without crash / lag / drift | 3 | 3 | 4 | 4 | 5 | Typoena unproven (1 h target). Freewrite famously stable (both variants). Pomera firmware is decades-mature. |
| W7 | Nothing on the device competes with prose | 5 | 2 | 5 | 5 | 5 | reMarkable has apps, menus, drawing, PDFs. Freewrite + Pomera are single-purpose; Typoena by design. |
| W8 | The UI never moves except when I move it | 4 | 3 | 4 | 4 | 5 | reMarkable animates more; Typoena uses dirty-rects; Freewrites minimal motion; Pomera near-static LCD. |
| W9 | Codebase absorbs the planned roadmap | 4 | 3 | 2 | 2 | 1 | Modular Rust Typoena; reMarkable is hackable Linux; both Freewrites carry Sailfish (Rust rewrite explicitly unblocked features JS could not carry) but closed; Pomera closed firmware. |
| W10 | I can repair or fork it with hobbyist tools | 5 | 4 | 2 | 2 | 1 | Typoena: open BOM + ESP32. reMarkable: rooted Linux + community ROMs. Freewrite + Pomera: closed. |
| W11 | Multi-day battery life (v0.8 onward) | 1 | 5 | 5 | 5 | 4 | Typoena v0.1 = wall-powered (battery deferred). reMarkable + both Freewrites legendary (~4 weeks; Sailfish trimmed 30 % typing / 50 % idle). Pomera ~24 h. |
| W12 | Local-only files coexist with git scope | 3 | 1 | 2 | 2 | 3 | Typoena v0.5+ design. reMarkable cloud-only. Freewrites have local + Postbox but no VCS. Pomera = pure local. |
| W13 | Typography sets a writing-tool tone | 3 | 5 | 2 | 2 | 2 | Typoena v0.1: single mono (serif option in v1.0). reMarkable: rich type rendering. Freewrite + Pomera: utilitarian. |
| W14 | I can carry the device and write away from a desk | 2 | 4 | 5 | 1 | 5 | Typoena v0.1 wall-powered (ADR-008), no enclosure spec yet — desk-bound by design. reMarkable + Type Folio bag-friendly with bulk. Freewrite Traveler is the form-factor reference (~1.6 lb, folds). Smart Typewriter ~5 lb, desk-bound. Pomera DM250 pocketable foldable. |
| ID | WHAT (truncated) | Typoena | reM. | Frw.T | Frw.S | Pom. | Rationale (shortest defensible) |
| --- | ------------------------------------------------- | :-----: | :--: | :---: | :---: | :--: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| W1 | Sub-second response to typing | 4 | 2 | 4 | 3 | 5 | Typoena targets ≤200 ms; reMarkable e-ink visibly laggy — tested less responsive than Smart Typewriter; both Freewrites post-Sailfish trimmed latency 40100 % (Frw.T plausibly inside 200 ms; Frw.S still trails by one notch on larger panel); Pomera LCD ~zero. |
| W2 | Publishing is one deliberate action away | 5 | 4 | 4 | 4 | 2 | Ctrl-G atomic; reMarkable + Freewrite cloud-sync is one-tap but not git; Pomera = USB/SD copy or QR transfer. |
| W3 | Pulling power never corrupts the file | 4 | 4 | 2 | 2 | 2 | Typoena: atomic-rename + fsync. reMarkable journals. Freewrite + Pomera: forum reports of corruption on yank. |
| W4 | Provisioning never interrupts writing | 5 | 2 | 2 | 2 | 5 | Typoena v0.1: build-time config (dev-only). reM/Frw need Wi-Fi + account. Pomera: literally none. |
| W5 | Quick boot to a writing cursor | 4 | 3 | 4 | 4 | 5 | Typoena target ≤5 s. reMarkable cold-boots ~20 s (great from sleep). Both Freewrites accelerated post-Sailfish (no published number; were ~1015 s e-ink wake). Pomera ~3 s. |
| W6 | Long sessions without crash / lag / drift | 3 | 3 | 4 | 4 | 5 | Typoena unproven (1 h target). Freewrite famously stable (both variants). Pomera firmware is decades-mature. |
| W7 | Nothing on the device competes with prose | 5 | 2 | 5 | 5 | 5 | reMarkable has apps, menus, drawing, PDFs. Freewrite + Pomera are single-purpose; Typoena by design. |
| W8 | The UI never moves except when I move it | 4 | 3 | 4 | 4 | 5 | reMarkable animates more; Typoena uses dirty-rects; Freewrites minimal motion; Pomera near-static LCD. |
| W9 | Codebase absorbs the planned roadmap | 4 | 3 | 2 | 2 | 1 | Modular Rust Typoena; reMarkable is hackable Linux; both Freewrites carry Sailfish (Rust rewrite explicitly unblocked features JS could not carry) but closed; Pomera closed firmware. |
| W10 | I can repair or fork it with hobbyist tools | 5 | 4 | 2 | 2 | 1 | Typoena: open BOM + ESP32. reMarkable: rooted Linux + community ROMs. Freewrite + Pomera: closed. |
| W11 | Multi-day battery life (v0.8 onward) | 1 | 5 | 5 | 5 | 4 | Typoena v0.1 = wall-powered (battery deferred). reMarkable + both Freewrites legendary (~4 weeks; Sailfish trimmed 30 % typing / 50 % idle). Pomera ~24 h. |
| W12 | Local-only files coexist with git scope | 3 | 1 | 2 | 2 | 3 | Typoena v0.5+ design. reMarkable cloud-only. Freewrites have local + Postbox but no VCS. Pomera = pure local. |
| W13 | Typography sets a writing-tool tone | 3 | 5 | 2 | 2 | 2 | Typoena v0.1: single mono (serif option in v1.0). reMarkable: rich type rendering. Freewrite + Pomera: utilitarian. |
| W14 | I can carry the device and write away from a desk | 2 | 4 | 5 | 1 | 5 | Typoena v0.1 wall-powered (ADR-008), no enclosure spec yet — desk-bound by design. reMarkable + Type Folio bag-friendly with bulk. Freewrite Traveler is the form-factor reference (~1.6 lb, folds). Smart Typewriter ~5 lb, desk-bound. Pomera DM250 pocketable foldable. |
**Totals** (sum across 14 WHATs, no weighting): Typoena 52, Pomera 50,
Freewrite Traveler 47, reMarkable 45, Freewrite Smart Typewriter 42

View File

@@ -116,14 +116,14 @@ stack decision (e.g. fall back to libgit2; switch to a separate SD SPI bus).
### `keymap` — v0.1 keymap table
| Key | Action |
| --------------- | --------------------------------- |
| printable | insert char |
| `Backspace` | delete char before cursor |
| `Enter` | insert `\n` |
| `←` `→` `↑` `↓` | move cursor (visual lines for ↑↓) |
| `Home` `End` | line start / end |
| `Ctrl-S` | save |
| Key | Action |
| --------------- | -------------------------------------------------------------------------------- |
| printable | insert char |
| `Backspace` | delete char before cursor |
| `Enter` | insert `\n` |
| `←` `→` `↑` `↓` | move cursor (visual lines for ↑↓) |
| `Home` `End` | line start / end |
| `Ctrl-S` | save |
| `Ctrl-G` | publish (save → stage → commit → push, with pull-and-retry on remote divergence) |
Anything else is ignored in v0.1.
@@ -182,7 +182,7 @@ PSRAM heap during push is a top-3 watched metric — see
- stage everything under the working copy (`git add .` equivalent)
- short-circuit return if nothing is staged — status: "nothing to publish"
- commit with author from config, message `"<ISO-8601 timestamp>"` (no `wip`
prefix; the timestamp *is* the message)
prefix; the timestamp _is_ the message)
- push HEAD to `origin/<current branch>`
- on push failure: `git pull --no-edit` (merge), then retry the push once.
Only surface failure to the status line if the pull conflicts or the