From ffcd49588a70d61e31183632cf3c5597b597e16a Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Sun, 17 May 2026 01:30:36 +0200 Subject: [PATCH] style: apply oxfmt to docs (table padding, italic style) --- CONTEXT.md | 4 +- docs/adr.md | 56 +++++++++---------- docs/qfd.md | 112 ++++++++++++++++++------------------- docs/quality-house.md | 32 +++++------ docs/v0.1-mvp-technical.md | 18 +++--- 5 files changed, 111 insertions(+), 111 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 46565a0..8e1a9fe 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -50,7 +50,7 @@ again. Unavailable in **Local**. _Avoid_: push, commit, sync, upload, git-push (these leak transport details into user-facing language). -> **Commit** is deliberately *not* a user-facing term. The device authors all +> **Commit** is deliberately _not_ a user-facing term. The device authors all > commit messages itself (ISO-8601 timestamp); the user never sees a commit > prompt. A **Publish** is the only user-observable unit of "shipping work"; > internal commits are an implementation detail of that. @@ -88,7 +88,7 @@ into user-facing language). **Publish** picks it up; until then, the device is silent about it. - **No state the user didn't ask for.** No banners about pending work, no prompts about divergence, no "did you mean to publish" warnings. The status - line reflects the *current* action's outcome, nothing else. + line reflects the _current_ action's outcome, nothing else. ## Flagged ambiguities diff --git a/docs/adr.md b/docs/adr.md index 4878627..0c0c1e0 100644 --- a/docs/adr.md +++ b/docs/adr.md @@ -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)** | ~100–300 ms partial / ~700–1000 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 (~$5–15); 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)** | ~100–300 ms partial / ~700–1000 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 (~$5–15); 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 8–32 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 8–32 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. diff --git a/docs/qfd.md b/docs/qfd.md index 0a2407c..4d8187d 100644 --- a/docs/qfd.md +++ b/docs/qfd.md @@ -27,22 +27,22 @@ the downstream sections (§5–§8). What a user (= me) values about the device, with importance weights on a 1–10 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 2–7 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, +5–10 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 | ~200–300 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, +5–10 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 | ~200–300 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 "`, and the user's +"wip"`, the v0.1 product doc said `"wip "`, 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 diff --git a/docs/quality-house.md b/docs/quality-house.md index c138905..858db6f 100644 --- a/docs/quality-house.md +++ b/docs/quality-house.md @@ -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 40–100 % (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 ~10–15 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 40–100 % (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 ~10–15 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 diff --git a/docs/v0.1-mvp-technical.md b/docs/v0.1-mvp-technical.md index bd51d6a..50884a4 100644 --- a/docs/v0.1-mvp-technical.md +++ b/docs/v0.1-mvp-technical.md @@ -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 `""` (no `wip` - prefix; the timestamp *is* the message) + prefix; the timestamp _is_ the message) - push HEAD to `origin/` - 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