Commit Graph

336 Commits

Author SHA1 Message Date
Julien Calixte
091688d7c3 feat(editor): let a palette-query space match any separator
Filenames use dashes where the typist thinks "space", so `la conv`
now finds la-convergence.md (space matches / _ - . or space).
2026-07-14 10:39:23 +02:00
Julien Calixte
057d6a6e2e docs(prefs): document open_last_on_boot 2026-07-14 10:35:09 +02:00
Julien Calixte
d4c59c2929 feat(firmware): boot into the last-active file
Record the active buffer in a device-local marker (/sd/.typoena-last,
beside the dirty journal) on every switch; when open_last_on_boot is set,
boot resumes it. Prefs are now read before the boot note is chosen.
A missing/garbled/stale marker falls back to notes.md — only the default
note stays boot-fatal.
2026-07-14 10:35:00 +02:00
Julien Calixte
72b9615105 feat(editor): add open_last_on_boot pref with palette toggle
Default true. Only the choice lives in the tracked toml; the last-active
path itself is device state the host keeps out of the repo.
2026-07-14 10:34:49 +02:00
Julien Calixte
2a12f95165 feat(editor): open the Cmd-P palette from every mode
Insert/Visual/View/Command now accept Cmd-P by first bailing out as Esc
would (end insert session, drop selection, abandon the : line), then
opening the palette; closing always lands in Normal. Opening mid-change
aborts the `.` recording so a palette round-trip is never replayed.
2026-07-14 10:33:16 +02:00
Julien Calixte
6fe8c86708 docs(sync): revise wifi-auto-sync curve with v0.7 field numbers
The ~8s-burst placeholder is replaced by the measured warm :gp
(19.1s = 10.3s splice + 5.9s push leg): K roughly doubles to
~0.6 mAh/sync and its mix shifts from radio to SD/CPU, but the splice
reruns per sync so the 1/T shape, the 5-10 min knee, and the 10-min
default all hold. TLS session resumption (2.4s vs 4.0s connect) and
the run-8 keep-alive death strengthen teardown-between-syncs; clean
ticks are radio-free via the dirty journal; :gl noted as outbound-only.
ASCII curve replaced with a mermaid xychart.
2026-07-14 10:07:06 +02:00
Julien Calixte
866b7b722a docs(v0.8): re-home the auto_sync runtime to v0.8
v0.7 closed with manual :gl/:gp only, leaving the auto_sync timer
unscheduled. It lands in v0.8 alongside the sleep transitions it must
respect and a new explicit item: per-sync Wi-Fi teardown (the git_sync
modem-ownership refactor), prerequisite for both sleep modes. Forward
pointers in typoena-toml.md and the v0.5 spec updated to match; the
v0.5 macroplan learning is left as a historical record.
2026-07-14 10:06:56 +02:00
Julien Calixte
60c9aef9dc docs(sync): record the images-off-card decision — skip-media-in-apply
Repo stays untouched (remanso reads images as git blobs); the device
half landed as media-invisible apply_tree_diff. Documents the accepted
trade: stale card media, phantom git-status noise, never hand-commit
from the card.
2026-07-14 10:05:41 +02:00
Julien Calixte
026eaea69a feat(sync): skip media paths in the pull apply
A pulled image blob is materialized whole in RAM before landing on the
card — history holds 16MB PNGs and a 38MB mp3 against 8MB PSRAM, the
last OOM path in :gl. The device never renders media, and the splice's
explicit-path staging means a missing image can't be committed as a
deletion, so media become invisible to apply_tree_diff: never written,
deleted, or belt-hashed. Blobs still arrive in .git via fetch; the
card's working copy stays text-only and goes deliberately image-stale.
2026-07-14 10:05:41 +02:00
Julien Calixte
3da31de0f2 docs(v0.7): finish version 2026-07-14 09:58:22 +02:00
Julien Calixte
6d5179fa11 docs(v0.7): record the warm :gp measurement — 19.1s, mix inverted
Push leg fell to 5.9s (session-resumed TLS + repacked pack); the splice
grew to 10.3s because a depth-4 path means ~5 loose tree writes, each
paying the FAT dir-scan cost, plus the session's first 1.7MB .idx map.
The publish-gap residual is now splice depth x loose-write cost.
2026-07-14 09:55:25 +02:00
Julien Calixte
fe62b47eee docs(sync): convict the loose-write residual — FAT linear dir scans
sd_bench dir-entry scaling: stat-miss is ~0.1 ms/entry and the
loose-object composite hits p50 402.9 ms at 256 siblings, reproducing
the field ~360-400 ms/loose-write from primitives. The objects/
fan-out is ~256 dirs, so every path resolution under it pays the scan.
Bounded and accepted; levers (fewer resolutions, pack-not-loose) go to
a future perf pass.
2026-07-14 09:44:47 +02:00
Julien Calixte
7378d30450 fix(sd_bench): remove the scratch tree without trusting dirent types
std's remove_dir_all reads directories as char devices through the
prebuilt libc's generic-unix DT table, unlinks them, and FatFS refuses
with FR_DENIED — the 6b fan-out subdirs were the first to trigger it.
Recurse with the same both-tables decoding as the palette walk.
2026-07-14 09:44:36 +02:00
Julien Calixte
cdfeabc152 feat(editor): rename :sync to :gp
Pairs with :gl as the push half of the git pair. Same behavior
(fmt → save → commit → push); notices, logs, and current READMEs
follow, historical docs keep :sync as a record of their time.
2026-07-14 09:42:30 +02:00
Julien Calixte
e161b1eae7 docs(v0.7): close the version — run-3 fast-forward pull verified
Pulled shape passed on device: apply_tree_diff 1271ms for 1 file, no
crash, interning confirmed (0 KB internal on re-walk). Records run-3
timings, the reconcile 30s→5s session-resumption win, and the
1-byte-load/0-byte-save trailing-newline watch item.
2026-07-14 09:35:35 +02:00
Julien Calixte
761fd3a750 feat(editor): smartcase + accent-folded search
Supersedes the plain case-insensitive matcher from earlier today (user
decision): an all-lowercase pattern searches case-insensitively, one
capital makes it exact (vim smartcase), and diacritics always fold both
ways — /ete finds été, /été finds ete. fold() strips the Latin-1
accent set (ligatures œ/æ excluded: they fold to two chars); n/N
recompute the smartcase verdict from the remembered pattern so repeats
behave like the original search.
2026-07-14 09:29:31 +02:00
Julien Calixte
11545efb0a docs(v0.7): record run 2, the checkout rebuild, and the closing gate 2026-07-14 09:23:04 +02:00
Julien Calixte
44c65e9ea5 perf(sync): reuse the TLS session and skip the pull's needless fetch
Two halves of the connection-cost lever (first on-device pull: 9.7s
fetch to learn 'up to date'; a rejected-push :sync runs three full
handshakes):

- esp_mbedtls_stream.c caches the mbedTLS session at stream close and
  offers it on the next connect to the same host — the server then
  skips the certificate exchange and most of the key exchange. Second
  vendor delta, single git thread, best-effort (any refusal falls back
  to a full handshake).

- pull_once now ls-refs first: the ref advertisement alone answers
  'anything new?', so up-to-date and local-ahead return without ever
  entering pack negotiation, and a needed download rides the SAME open
  connection instead of a second handshake. Tracking-ref updates share
  one update_tracking helper with the reconcile fetch.
2026-07-14 09:21:58 +02:00
Julien Calixte
75166f74cf perf(palette): intern the file list into one PSRAM blob
1099 paths as individual Strings measured 182 KB of internal DRAM
resident (each alloc is under the 16 KB SPIRAM-malloc threshold), a
main contributor to the DMA-pool exhaustion behind the :gl crash. The
walk now appends into one newline-joined String seeded at 64 KB so it
and its growth land in PSRAM; the editor stores the blob plus sorted
byte spans (set_file_list_joined) and the palette reads slices. :enew
appends to the blob; :delete drops only the span. Also log the :gl
dirty-journal gate, which looked like a silent no-op on the serial log.
2026-07-14 09:17:36 +02:00
Julien Calixte
329995ab58 fix(sync): apply the pull fast-forward as a tree diff, not checkout_tree
First on-device ff attempt (2026-07-14) crashed: libgit2's SAFE
checkout iterates the whole working directory (readdir over SPI on all
~1100 files) to find dirty files — the same O(tree) wall the splice
commit avoids — and with fetch memory still resident it exhausted
internal DRAM; esp-idf's spi_master then NULL-derefs on its own
failed-DMA-alloc path instead of erroring.

apply_tree_diff mirrors the splice: diff_tree_to_tree skips identical
subtree OIDs in memory, then writes/deletes exactly the changed paths
(unlink+tmp+rename — FAT rename won't overwrite). Belt kept O(changed):
any to-be-clobbered file whose content no longer hashes to the old blob
aborts the pull before the first write. Half-applied + unmoved ref
self-heals on the next :gl (idempotent re-apply).

Also raise the internal DMA reserve 32→64 KB so the SPI driver never
sees a failed alloc (its error path is the crash), and log the fetch
completion so the fetch/apply phases are separable in the serial log.
2026-07-14 09:17:36 +02:00
Julien Calixte
0734c1f67e feat(editor): make / search case-insensitive
Unicode lowercase-fold comparison at char boundaries (find_ci/rfind_ci)
instead of str::find — no lowercased buffer copy, whose byte lengths
could shift the returned offsets. Prose search shouldn't care about
capitalization; deliberately not smartcase (a capital silently flipping
to exact-match is a surprise here, not a feature).
2026-07-14 09:09:01 +02:00
Julien Calixte
3009dc4d9c feat(boot): async splash refresh + background palette file walk
The splash's ~2.2s full-refresh waveform now runs while the SD mounts
and the note/prefs load (Epd::display_frame_async + wait_ready guard on
every public display call), and the 4.3s palette walk moves to a 16KB
background thread whose result lands in the idle branch — the first
editor frame no longer waits on non-mandatory work. The post-pull
re-walk rides the same channel, removing a 4.3s UI stall after :gl.

Also: rustfmt pass on sd_bench, drop the spike-9 log line in splash.
2026-07-14 01:34:28 +02:00
Julien Calixte
8c4866df1f feat(bench): attribute the loose-write residual — dir-scaling + splice I/O counters
sd_bench grows a directory-entry-scaling section (stat hit/miss and
the loose-object composite at 8/64/256 siblings): FAT resolves every
path component by linear scan, so cost climbing with N convicts
directory scans as the ~360ms residual; flat cost exonerates them.
stage_and_commit brackets the splice with the p_mmap counters and logs
'N mmaps / M KB read' per commit, splitting the residual into pack-read
I/O vs everything else on the next real :sync.
2026-07-14 01:12:35 +02:00
Julien Calixte
a941ae39b3 feat(sync): :gl pull — fetch + fast-forward only on the git thread
The git channel now carries GitRequest::{Publish,Pull} with a shared
ensure_online preamble (Wi-Fi/clock/TLS once per session). pull_once
fetches origin — refreshing the tracking ref so the radio-free
up-to-date check stays honest — and maps four shapes: up to date,
LocalAhead (stranded commit, :sync's job), clean fast-forward,
Diverged (refused; no merge on the device). The fast-forward is a SAFE
checkout then ref move: it refuses to overwrite content that differs
from HEAD, the belt under the UI gate that refuses :gl while the dirty
journal is non-empty. A RAM-dirty buffer doesn't gate — its edits win
(last-writer-wins, like the reconcile).

After a pull the UI drops clean parked buffers, re-reads the clean
active buffer in place (Editor::refresh_active), and re-walks the
palette list. Firmware 0.6.0 -> 0.7.0. On-device verification pending.
2026-07-14 01:10:00 +02:00
Julien Calixte
8d22f81804 feat(editor): / forward search with n/N repeat (v0.7)
Literal case-sensitive substring search sharing the : command line's
editing (cmd_prompt discriminates the prompt; Enter dispatches). Jumps
on Enter only — incremental caret-chasing loses to the e-ink refresh
cost, same call as the snippet no-popup decision. Wraps with a notice,
keeps the caret on a miss, bare / repeats the editor-global last
pattern. n/N live in move_by so counts and Visual extension come free;
they are not operator targets.
2026-07-13 22:15:19 +02:00
Julien Calixte
4f0d745e84 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.
2026-07-13 22:07:34 +02:00
Julien Calixte
5d623151a8 chore(justfile): repack multi-pack source clones in _load-repo
Single-pack card prep is now enforced, not remembered: each pack's
whole .idx (plus any midx) is mmap'd outside the device's window
budget, and a multi-pack marking walk lost the HTTP keep-alive (run 8:
31s vs 3.5s repacked). Skipped when the clone is already one pack.
2026-07-13 22:07:26 +02:00
Julien Calixte
8d5bd1903d refactor(sync): retire the diagnostic push heartbeat
It served its purpose (localized the mmap plateau, runs 7-9); its 8KB
internal stack was live during the TLS send, where internal min-ever
hit 2099 bytes. The time-gated pack/transfer callbacks stay.
2026-07-13 22:07:16 +02:00
Julien Calixte
028ebc8f5f docs(kaizen): close the real-repo sync loop — first :sync at 24.1s
Step 6 evaluated: infinity (never completes) to 24.1s measured
snackbar-to-snackbar, with the measured after-bar charted in step 6 (the
step-1 chart stays potential-only, per the format). Documents the second
localization loop the verification runs forced on the push half: UI death
by shared heap fate, odb cache exonerated, telemetry axis bug, mmaps named
as the eater, the impossible fixed-maps budget, the keep-alive race, and
pack layout as a device-performance variable.
2026-07-13 21:45:47 +02:00
Julien Calixte
9e99c1767a fix(sync): budget mmaps at 64KB/1.5MB, trace the push heap live
Runs 7-9 on the real repo (2026-07-13): esp_map live-bytes wired into
log_push_heap plus a 5s sibling heartbeat thread proved the push OOM was
libgit2's mmaps — ~3.4MB of whole-file .idx/multi-pack-index maps living
OUTSIDE the mwindow budget, plus windows at the (working) 4MB soft limit,
an impossible budget on the 8MB pool. Shrinking to 64KB windows / 1.5MB
mapped limit left 4.5MB min-ever free and cut marking reads 4x; with the
card repacked to a single pack, the first-ever real-repo push landed
(6713ea6e, :sync 24.1s end-to-end).

The heartbeat stays for now (the marking phase fires no libgit2 callbacks,
so it is the only heap trace through it); its 8KB internal stack is part
of the min-ever-2099B-internal tightness flagged for follow-up.
2026-07-13 21:45:47 +02:00
Julien Calixte
f79d13453a fix(sync): survive push heap exhaustion and instrument the push path
Run 4 (2026-07-13): remote.push() consumed ~6 MB of PSRAM over 66 s and
the UI thread aborted on Frame::new_white's per-draw vec![0xFF; 26928].
Run 5 confirmed the UI now survives, but the push still exhausts both
pools (a ~7 KB inflateInit fails inside the pack build) and the heap
telemetry never fired — hence the telemetry rework.

- display/editor: Editor::draw_into() renders into a caller-owned
  Frame; main.rs keeps two boot-time frames (shown/back) and mem::swaps
  on success, so steady-state repaints never allocate.
- git_sync: odb cache capped at 1 MB via raw libgit2-sys opts (git2
  0.20 doesn't wrap the total cap); log_push_heap at pre-push,
  post-push and push-failure with largest-PSRAM-block and per-pool
  min-evers; pack_progress logging is now time-gated (2 s) — the
  count gate (+256 objects) never fired because AddingObjects reports
  total=0 and a small push inserts only dozens of objects.
- editor: empty/whitespace snippets file parses as 0 snippets instead
  of a JSON error at boot.
- build.rs: refuse a git-feature build with unset TW_* publish vars —
  a bare `cargo build --features git` baked empty creds and produced a
  firmware whose :sync could never work.
2026-07-13 21:04:18 +02:00
Julien Calixte
05706478a2 feat: wrap palette selection; fix push classing, file walk, card load
- Palette Ctrl-N/P (and half-page keys) wrap around the result list
  instead of clamping at the ends; tests updated to the new semantics.
- try_push maps libgit2's negotiation-time NotFastForward error to
  PushFailure::Rejected so the reconcile path runs instead of failing
  as a transport error.
- walk_files decodes esp-idf d_type values (DT_REG=1/DT_DIR=2) that
  libc 0.2.178's generic unix table misreads as fifo/chardev — the
  palette walk was silently dropping every file on the card.
- `just load` refuses to mirror over unpublished device edits listed
  in the card's .typoena-dirty journal (backup+discard via
  TW_DISCARD_UNPUBLISHED=1), gains --force for a from-scratch copy,
  and a flash-only recipe flashes the already-built ELF.
2026-07-13 20:15:14 +02:00
Julien Calixte
5f50cf1b45 chore: remove bar 2026-07-13 11:35:08 +02:00
Julien Calixte
5c97e019ec docs(kaizen): drop the after-kaizen bar until step 6 measures it
Step 1 is baseline + target only; the target line stays, the bar would
fake a measurement that doesn't exist yet.
2026-07-13 11:07:25 +02:00
Julien Calixte
7712de6153 docs(kaizen): size the xychart so its labels stop clipping
Explicit 900x500 canvas, tighter x-axis labels, unit-only y-axis title
(the long vertical title collided with the tick numbers in Remanso).
2026-07-13 11:06:44 +02:00
Julien Calixte
1df25f75d6 docs(kaizen): chart the improvement potential as a mermaid xychart
Before/after bars with the target as a line series; the caption keeps
the nuance the chart can't draw (true before value is ∞, not 611 s).
2026-07-13 11:03:43 +02:00
Julien Calixte
acbafa3d6b docs(kaizen): retell the real-repo sync fix as a six-step kaizen
The tradeoff-curves doc stays the chronological data layer; the kaizen
is the story layer — value model, 8-factor hypothesis table, ideas
ranked by verified cause, and an evaluation left PENDING until the
end-to-end on-device :sync lands.
2026-07-13 10:56:05 +02:00
Julien Calixte
e801d2d480 docs(palette): note the two on-device boot-log measurements pending
Walk time after the d_type fix, and the file-list internal-DRAM cost
with the decision rule for interning paths into one PSRAM buffer.
2026-07-13 10:26:25 +02:00
Julien Calixte
a771c3f6de chore(diag): log internal DRAM next to PSRAM-dominated heap totals
"free heap 5.8MB" in the crash log was PSRAM and masked the actual
internal-RAM exhaustion. git_sync's publish/commit/push lines now
carry the MALLOC_CAP_INTERNAL reading, and boot brackets the palette
file-list build with it — small Strings are forced internal by the
16 KB SPIRAM malloc threshold, so the 1098-path list is a suspected
~60-70 KB DRAM consumer competing with Wi-Fi/TLS; the log decides
whether interning into one PSRAM buffer is worth it.
2026-07-13 10:25:25 +02:00
Julien Calixte
4c92b0dddc fix(sync): move mbedTLS allocations to PSRAM
esp-idf's default is internal-RAM-only, and a TLS connection needs
~33 KB of it (two ~17 KB I/O buffers + contexts) at the exact moment
:sync pushes — with Wi-Fi, USB host, the editor and libgit2 resident,
mbedtls_ssl_setup failed there on the first real-repo push (and its
failure path then hit the stream double-free fixed in the previous
commit). TLS buffers are CPU-only data, so PSRAM is safe; the
handshake is network-bound.
2026-07-13 10:25:25 +02:00
Julien Calixte
af5b41a104 fix(sync): patch the mbedtls stream double-free that reset the chip
Upstream libgit2 v1.9.4 bug: mbedtls_stream_wrap's ssl_setup error
path closes and frees the caller's socket stream, then
git_mbedtls_stream_new frees it again — and wrap's other error paths
don't free it at all, so callers can't compensate either way. When
ssl_setup failed on the device (internal-RAM exhaustion during the
first real-repo push), the double git__free tripped tlsf ("block
already marked as free") and reset the chip mid-:sync.

Fixed the way esp_map.c replaces map.c: the vendored streams/mbedtls.c
is excluded from the component and esp_mbedtls_stream.c (verbatim copy
+ one-hunk fix: out_err leaves st->io to the caller, and frees the
leaked ssl struct) takes its place. Keep the copy in lockstep on
submodule bumps; worth reporting upstream.
2026-07-13 10:25:12 +02:00
Julien Calixte
2660a3e9dd perf(palette): trust dirent d_type instead of a per-entry stat
The per-entry metadata() call makes FatFS re-walk the directory by
path every time — ~32ms/file on the card, 35s for a 1098-file tree.
esp-idf's FAT VFS always fills d_type (DT_DIR/DT_REG from the FILINFO
readdir already holds, never unknown) and Rust std maps file_type()
onto it stat-free, so the walk is now one readdir pass per directory.
2026-07-13 01:05:10 +02:00
Julien Calixte
79fad4689c docs(palette): amend v0.5 with the recursive walk and search threshold 2026-07-13 00:55:34 +02:00
Julien Calixte
98fc817b3f feat(palette): show recents only until the query reaches two chars
With the file list now a recursive walk of the whole card, an unranked
short query pages through too much. Below PALETTE_MIN_QUERY (2) the
result list is the MRU only, keeping Cmd-P + Enter quick-switch one
keystroke away; two chars reveal the full fuzzy-ranked list. Commands
(>) and snippets ($) are short curated lists and keep matching from
the first char.
2026-07-13 00:55:25 +02:00
Julien Calixte
d306caacf7 feat(palette): walk the card recursively for the file list
A nested repo (jcalixte/notes) showed only its top-level files in the
palette. Dot entries are skipped at every level so .git is never
descended; each directory is read fully before recursing so a single
FatFS dir handle is open at a time; depth capped at 8. The boot walk
logs its file count and duration to keep the FAT dir-IO cost visible.
2026-07-13 00:55:16 +02:00
Julien Calixte
2166b932b6 docs(sync): update the wiring section to how-it-landed
The merged plan section now records the shipped design: single path
set (working tree as truth) instead of {changed, deleted}, the dirty
journal, stranded-commit recovery, radio-free up-to-date, and the
soft-reset reconcile with its carried-files side win.
2026-07-13 00:53:50 +02:00
Julien Calixte
02b7ed88b6 fix(provisioning): rewrite the card origin to HTTPS at load
The device's libgit2 has no SSH transport (HTTPS+PAT over mbedTLS
only), but _load-repo copied the desktop clone's origin verbatim — an
SSH-shaped URL fails every on-device push/fetch with "unsupported URL
protocol" (first real-repo :sync, 2026-07-13). Derive the HTTPS
equivalent for the card copy (git@host:path and ssh://git@host/path);
the source clone keeps its own URL. Warns on non-github hosts, since
the embedded trust store carries GitHub roots only.
2026-07-13 00:53:50 +02:00
Julien Calixte
a5edaed810 feat(sync): commit by splicing journaled dirty paths onto HEAD
The index pipeline (add_all → index.write → write_tree) is O(N_tree)
and cannot commit the real 1179-file / 570 MB-pack clone (index.write
measured up to 611 s on FAT's racy-clean re-hash). stage_and_commit is
now an O(depth) TreeBuilder splice of exactly the paths the editor
saved or deleted — ~2-2.8 s on the real clone — with the working tree
as source of truth (existing file → insert, missing → remove).

Storage records those repo-relative paths on every save/delete and
journals them to /sd/.typoena-dirty (atomic, only on growth), so a
power pull can't strand a saved-but-unpublished note now that nothing
walks the tree. take_dirty → publish_succeeded/publish_failed settles
each publish's snapshot from the UI outcome handler.

Also required by / discovered with the splice:
- mwindow opts at git-service start (32-bit defaults would OOM PSRAM
  on the first real-pack access; bench-proven 256 KB / 4 MB).
- 16-FD mount for git builds (libgit2 holds pack+idx descriptors open;
  the editor's 4-FD budget overruns).
- reconcile is a soft reset (no index to reset anymore); side win: a
  remote-only added file is carried by the replay instead of dropped.
- stranded-commit recovery: tree-unchanged now pushes anyway when
  origin/<branch> lacks HEAD (a commit whose push failed used to be
  silently never retried).
- radio-free up-to-date: empty dirty set + origin at HEAD answers
  without bringing Wi-Fi up.
- try_push splits ref rejection (reconcilable) from transport failure
  (surfaced directly) — the first on-device run burned a doomed
  reconcile on "unsupported URL protocol" and hid the cause.

Deliberate behavior change: files changed on the card outside the
editor are never committed anymore (also retires the macOS-cruft
filter). Trail: docs/tradeoff-curves/sync-commit-staging.md.
2026-07-13 00:53:39 +02:00
Julien Calixte
e86a3b8254 docs(sync): merge the commit handoff into the staging tradeoff curve
The bench phase the handoff was written for is closed (splice benched,
fast-seek landed, cache removed, decision made), so the note's live half —
the firmware plumbing plan — moves into sync-commit-staging.md as "The fix —
wiring the O(depth) splice into the firmware", reconciled to the run-5 state.
The duplicated TL;DR/measurement summaries are dropped in favor of the
curve's own trail; inbound references (notes index, git_bench comments)
now point at the curve.
2026-07-13 00:29:07 +02:00
Julien Calixte
9309f3f239 docs(sync): record the esp_map v2 verdict and cache removal
Run 4: memory discipline verified (1833 KB flat, no OOM) but 0 hits
with the small maps demonstrably retained — window-repetition theory
refuted, sub-second bar failed (splice 2.83 s cold / ~2 s steady).
Decision: wire the splice in anyway (~9-10 s cold real-repo :sync vs
611 s/OOM for every alternative). Run 5: cache removal confirmed
free; the ~1.85 MB "resident" was mwindow's live window set, which
makes the mwindow opts in shipping git_sync.rs load-bearing.
2026-07-13 00:20:12 +02:00