docs: sweep sync follow-ups into their owning version docs

Kaizen/postmortem next-steps kept accumulating with no owner. Each item
now lives in one version doc (v0.7 sync perf, v0.9 robustness, v1.0
boot) with the source docs linking there; the editor-freeze safety net
is marked hardware-verified and the v0.5 walk re-measure closed at 4.3s.
This commit is contained in:
Julien Calixte
2026-07-13 22:07:34 +02:00
parent 5d623151a8
commit 4f0d745e84
7 changed files with 76 additions and 24 deletions

View File

@@ -324,22 +324,29 @@ three runs earlier), mmap live 1868 KB, odb cache 70 KB.
~2.8 s commit on a microcontroller, four theories refuted on the way) stands
on its own.
**Next steps**
**Next steps** (each now owned by a version doc or done — this list is the
historical record, the owner is authoritative)
- Close the gap to the ≤ ~10 s target: publish is 19.8 s; the splice's 5.0 s
and the ~7 s of repo-open/negotiation overhead are the next curves.
→ [v0.7](../v0.7-search-and-git.md).
- The keep-alive race is **avoided, not fixed** (3.5 s ≪ ~30 s) — a future
multi-pack or cold-cache state could still lose it. Durable fix:
reconnect-on-stale-connection in the http/stream layer.
- Internal RAM min-ever hit **2099 bytes** during the TLS send (the diagnostic
heartbeat's 8 KB stack is part of it) — shrink or retire the heartbeat now
that it has served its purpose.
- Fold `git repack -ad` into the `_load-repo` recipe so single-pack card prep
is enforced, not remembered.
→ [v0.9](../v0.9-robustness.md).
- ~~Internal RAM min-ever hit **2099 bytes** during the TLS send (the
diagnostic heartbeat's 8 KB stack is part of it) — shrink or retire the
heartbeat now that it has served its purpose.~~ **Done 2026-07-13**
heartbeat removed; the time-gated `pack_progress` + transfer callbacks stay.
- ~~Fold `git repack -ad` into the `_load-repo` recipe so single-pack card
prep is enforced, not remembered.~~ **Done 2026-07-13**`_load-repo`
repacks the source clone (best-effort, like the pull) whenever it has more
than one pack or a multi-pack-index.
- File the libgit2 upstream bug report (tlsf double-free in the mbedTLS
stream error path).
stream error path). *(Unscheduled chore — not version-scoped.)*
- Instrument the residual ~360 ms/loose-write (suspect: FAT directory-op cost
in the freshen/refresh path) — one `sd_bench` + `p_mmap`-miss logging pass.
→ [v0.7](../v0.7-search-and-git.md).
- The images-off-card lever
([`../notes/git-sync-images-and-repo-size.md`](../notes/git-sync-images-and-repo-size.md))
composes with the splice.
composes with the splice. → [v0.7](../v0.7-search-and-git.md).

View File

@@ -202,7 +202,8 @@ it is explicitly **not recommended** and not applied.
(ADR-012, 2026-07-11); spike rewired to SPI3 (SCK 14 / MOSI 15) and
**re-verified on hardware 2026-07-11** — same mount + round-trip result.
- [ ] **Still open:** implement the FatFS atomic-save (unlink-then-rename +
`*.tmp` boot-recovery) in the real `persistence` module.
`*.tmp` boot-recovery) in the real `persistence` module — → tracked as
the crash-safe-writes item in [v0.9 robustness](../v0.9-robustness.md).
## Artifacts (this session)

View File

@@ -1,11 +1,16 @@
# Editor freeze — SPI-DMA out-of-memory during a background `:sync`
> Date: 2026-07-11 · Build at time of failure: `07-11 18:02Z @229c259-dirty`
> Status: **Safety net shipped** (paints are non-fatal, the appliance no longer
> bricks) — pending hardware re-test. **Root cause not yet eradicated**: a paint
> that lands during a sync still fails and drops the frame; the permanent fix (a
> persistent internal DMA scratch buffer) is specced below and tracked in the
> follow-ups.
> Status: **Safety net shipped and hardware-verified** (2026-07-13, during the
> [real-repo-sync kaizen](../kaizen/real-repo-sync.md): live scrolling through
> a real-repo push — the run-4 crash was this same paint-during-push family,
> and after the fixes the UI survived every subsequent push, success and
> failure alike). The paint path also **no longer allocates per repaint**
> (persistent two-frame `draw_into`/`swap` in `main.rs`, 2026-07-13), removing
> the frame-buffer half of the paint-time allocation. **Root cause not fully
> eradicated**: the EPD driver's per-refresh internal DMA scratch remains; the
> permanent fix (a persistent scratch buffer in `Epd`) is specced below and
> now tracked in [v0.9 robustness](../v0.9-robustness.md).
>
> Context: editor loop [`../../firmware/src/main.rs`](../../firmware/src/main.rs),
> EPD driver [`../../firmware/src/epd.rs`](../../firmware/src/epd.rs), git
@@ -156,9 +161,14 @@ triggered by the sync.
- [x] Make all editor-loop paints non-fatal + `force_full` recovery (`main.rs`);
release build green.
- [ ] Reflash and hardware-verify the safety net against the repro (edit →
- [x] Reflash and hardware-verify the safety net against the repro (edit →
`:sync` → scroll through the push): panel must recover, not freeze.
**Verified 2026-07-13** (real-repo-sync kaizen runs 59: concurrent
typing/scrolling through full pushes, no freeze; bonus fix — repaints no
longer allocate at all, closing the run-4 `Frame::new_white` OOM-abort
variant of the same failure).
- [ ] Implement the persistent internal DMA scratch buffer in `Epd` (eradication
above) if the stale-during-sync window proves annoying in real use.
above) if the stale-during-sync window proves annoying in real use
→ tracked in [v0.9 robustness](../v0.9-robustness.md).
- [ ] After eradication, confirm refreshes succeed *during* a push and drop the
stale window entirely.

View File

@@ -170,11 +170,13 @@ apply to them.
**TODO (on-device, next time the device is on the bench)** — two measurements
from the same boot log, both already instrumented:
- [ ] **Re-measure the walk time** after the d_type fix (`2660a3e` — dirent
- [x] **Re-measure the walk time** after the d_type fix (`2660a3e` — dirent
`file_type()` instead of a per-entry `metadata()` stat, which cost
~32 ms/file and made run 1 take 35 s for 1098 files). Read the
`file walk: N files in Xms` line. Only if it's still slow does the
async-walk idea come back on the table.
~32 ms/file and made run 1 take 35 s for 1098 files). **Measured
2026-07-13: 1098 files in 4.3 s** (~4 ms/file, readdir-over-SPI bound —
8× under the stat-per-entry cost, but boot cursor-ready is now ~8.7 s,
so the async-walk question is back open; it now lives with the
[v1.0 ≤ 3 s boot item](v1.0-polish.md)).
- [ ] **Read the file-list DRAM cost** from the new
`file list: internal heap <before> -> <after> (<N> KB consumed)` line
(the build is bracketed with `MALLOC_CAP_INTERNAL` readings in

View File

@@ -12,3 +12,17 @@ fast-forward is still to build. Search not started.
surface it (renamed from the planned `:Gpull`). Editor command +
`Effect::Pull` done 2026-07-11 (host-tested); the git-thread
fetch/fast-forward in `git_sync` remains (only push is wired today).
Sync performance — inherited from the
[real-repo-sync kaizen](kaizen/real-repo-sync.md) (closed 2026-07-13 at
24.1 s end-to-end; target ≤ ~10 s):
- [ ] Close the publish gap: 19.8 s today — the splice's 5.0 s and the ~7 s of
repo-open/negotiation overhead are the next curves
([tradeoff analysis](tradeoff-curves/sync-commit-staging.md)).
- [ ] Instrument the residual ~360 ms/loose-write (suspect: FAT directory-op
cost in the freshen/refresh path) — one `sd_bench` + `p_mmap`-miss
logging pass.
- [ ] The images-off-card lever
([notes/git-sync-images-and-repo-size.md](notes/git-sync-images-and-repo-size.md))
composes with the splice — decide whether to take it here.

View File

@@ -5,8 +5,22 @@
**Status:** not started.
- [ ] Crash-safe writes (write to `.tmp`, fsync, rename)
- [ ] Recover from interrupted push (re-attempt on next save)
- [ ] Crash-safe writes (write to `.tmp`, fsync, rename) — NB FatFS `f_rename`
refuses to overwrite, so it's unlink-then-rename + `*.tmp` boot-recovery
(found in [Spike 3](postmortems/2026-07-05-spike3-sd-cmd59.md), still
open there)
- [ ] Recover from interrupted push (re-attempt on next save) — the splice
journal + stranded-commit recovery (2026-07-13) already survive a
power-pull mid-push; this item is the *retry* half
- [ ] Reconnect-on-stale-connection in the libgit2 http/stream layer — the
push keep-alive race is **avoided, not fixed** (repack keeps the marking
phase at ~3.5 s ≪ the ~30 s idle window; a multi-pack or cold-cache
state could still lose it — see the
[real-repo-sync kaizen](kaizen/real-repo-sync.md))
- [ ] Eradicate the paint-during-sync DMA allocation failure: persistent
internal DMA scratch in `Epd` (safety net + allocation-free repaints
shipped; see the
[editor-freeze postmortem](postmortems/2026-07-11-editor-freeze-spi-dma-oom-during-sync.md))
- [ ] SD card removal / reinsert handling
- [ ] Wi-Fi reconnect with backoff
- [ ] On-device provisioning + settings screen: SSID, PAT rotation, default

View File

@@ -5,8 +5,12 @@
**Status:** not started.
- [ ] Boot time ≤ 3 s to usable cursor — currently ~4.26 s; the ~1.9 s cold-boot
full refresh is a hard e-ink floor, so ≤ 3 s is marginal (see
- [ ] Boot time ≤ 3 s to usable cursor — 4.26 s at v0.1 ship; **regressed to
~8.7 s on a real 1098-file card** once the v0.5 palette walk joined the
boot path (4.3 s of readdir-over-SPI, already d_type-optimised — see the
[v0.5 walk amendment](v0.5-palette-and-multi-file.md)), so the walk must
go async/deferred, and the ~1.9 s cold-boot full refresh + ~0.74 s PSRAM
memtest are the remaining levers (see
[`notes/boot-time-budget.md`](notes/boot-time-budget.md))
- [ ] Font selection (at least one serif + one mono) with adjustable font
size, switchable at runtime and persisted across reboots