diff --git a/README.md b/README.md index ac19200..72ef125 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ surface (mostly `usb_kbd.rs`) is in [`MEMORY_AUDIT.md`](MEMORY_AUDIT.md). | UI layer | Custom thin widget layer | Ratatui's API _shape_ without its char-grid terminal model ([ADR-002](docs/adr.md#adr-002-ui-strategy--custom-widgets-on-embedded-graphics-not-ratatui)). | | Editor core | Custom, in-tree (`src/editor.rs`) | Modal (Normal / Insert / Visual / VisualLine / View / Command), motions, operators + text objects. Plain-ASCII buffer until the v0.2 UTF-8 work. | | USB host | `esp-idf` TinyUSB bindings | Boot-protocol HID; verified on hardware (Spike 4). | -| Git | **libgit2 via `git2`**, built as an esp-idf component with mbedTLS (`firmware/components/libgit2/`) | `gix` was the original pick but can't push over HTTPS — the [ADR-004](docs/adr.md#adr-004-git-implementation--gitoxide-gix) kill-switch fired ([postmortem](docs/postmortems/2026-07-05-spike7-gix-https-push.md)). On-device add → commit → push verified; ~16 s cold-`:sync` [latency breakdown](docs/notes/sync-latency.md). | +| Git | **libgit2 via `git2`**, built as an esp-idf component with mbedTLS (`firmware/components/libgit2/`) | `gix` was the original pick but can't push over HTTPS — the [ADR-004](docs/adr.md#adr-004-git-implementation--gitoxide-gix) kill-switch fired ([postmortem](docs/postmortems/2026-07-05-spike7-gix-https-push.md)). On-device add → commit → push verified; ~16 s cold-`:gp` [latency breakdown](docs/notes/sync-latency.md). | | TLS | `mbedtls` via `esp-idf` | GitHub HTTPS with the chain checked against embedded roots; ≈35 KB heap measured during handshake (Spike 6). | | Auth | HTTPS + GitHub PAT | v0.1 bakes credentials in at build time via `TW_*` env vars; provisioning + at-rest protection is [ADR-011](docs/adr.md#adr-011-credential-provisioning--how-the-pat-reaches-the-device-and-is-protected-at-rest) (open), on-device settings land in v0.9. | | Filesystem | FAT on SD (`esp_vfs_fat`) | Working copy lives here. Internal LittleFS holds config. | diff --git a/display/src/lib.rs b/display/src/lib.rs index a185384..8de5f28 100644 --- a/display/src/lib.rs +++ b/display/src/lib.rs @@ -47,7 +47,7 @@ impl Frame { /// Reset to all-white paper, reusing the existing allocation when the /// buffer is already full-size. This is what lets firmware repaint without - /// allocating: a background `:sync` push can take the heap to the floor, + /// allocating: a background `:gp` push can take the heap to the floor, /// and a failed framebuffer alloc aborts the whole app (2026-07-13). pub fn clear_white(&mut self) { self.buf.clear(); diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 4d44fb1..5409b3d 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -86,7 +86,7 @@ pub enum Mode { /// are locked out. View, /// `:` command line — keys accumulate a command shown in the status strip; - /// Enter runs it, Esc cancels. Handles `:fmt` (in-core) plus `:w`/`:sync` + /// Enter runs it, Esc cancels. Handles `:fmt` (in-core) plus `:w`/`:gp` /// (which ask the host to persist/publish via an [`Effect`]). Command, /// File palette (`Cmd-P`) — a modal transient panel over the writing column. @@ -98,8 +98,8 @@ pub enum Mode { /// Which of the two file scopes ([`CONTEXT.md`]) a buffer belongs to. Fixed at /// creation — there is no move-between-scopes operation. **Tracked** files live -/// under [`REPO_DIR`] and can be Published (`:sync`); **Local** files live under -/// [`LOCAL_DIR`] and never leave the device, so `:sync` is refused in-core. +/// under [`REPO_DIR`] and can be Published (`:gp`); **Local** files live under +/// [`LOCAL_DIR`] and never leave the device, so `:gp` is refused in-core. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Scope { Tracked, @@ -128,13 +128,13 @@ pub enum Effect { /// [`Editor::install_loaded`]. Queued when switching to a file that is not /// resident in memory (`:e`, palette pick). Load { path: String, scope: Scope }, - /// `:sync` — publish the Tracked working copy (git push). Preceded by a + /// `:gp` — publish the Tracked working copy (git push). Preceded by a /// [`Save`](Effect::Save) of the current buffer in the same batch. Never /// queued from a Local buffer (blocked in-core). Publish, /// `:gl` — pull from the remote: fetch, then **fast-forward only**. The host /// refuses (and surfaces) a divergence rather than merging, and never - /// touches local commits. Complements `:sync` (push) as the download half. + /// touches local commits. Complements `:gp` (push) as the download half. Pull, /// `:delete` — unlink `path` from the card. For a **Tracked** file the removal /// lands in the git working copy, so the next [`Publish`](Effect::Publish)'s @@ -157,7 +157,7 @@ pub const REPO_DIR: &str = "/sd/repo"; pub const LOCAL_DIR: &str = "/sd/local"; /// The git-tracked preferences file. Read at boot and rewritten when a palette /// `>` command changes a pref, so the setting survives a reboot and rides the -/// next `:sync` to every device that clones the repo. Deliberately **distinct** +/// next `:gp` to every device that clones the repo. Deliberately **distinct** /// from the gitignored `/sd/typoena.conf` device secrets (Wi-Fi / PAT / remote / /// author, never committed — see v0.1): behaviour is shared, secrets are not. pub const PREFS_PATH: &str = "/sd/repo/.typoena.toml"; @@ -171,12 +171,12 @@ pub const PREFS_PATH: &str = "/sd/repo/.typoena.toml"; pub struct Prefs { /// Auto-save the active buffer on the idle typing-pause, so `:w` becomes /// optional. The idle save is **unformatted** — a safety net against power - /// loss, not a formatting pass; `:fmt` only runs on an explicit `:w`/`:sync` + /// loss, not a formatting pass; `:fmt` only runs on an explicit `:w`/`:gp` /// (see [`format_on_save`](Prefs::format_on_save)) so text is never reflowed /// mid-session. Honoured by the host loop, not the core. pub save_on_idle: bool, /// Run `:fmt` (table alignment, blank-line collapse, trailing-whitespace - /// strip) on the buffer before an explicit `:w`/`:sync` persist. + /// strip) on the buffer before an explicit `:w`/`:gp` persist. pub format_on_save: bool, /// Show the absolute line-number gutter (built always-on in v0.2). Off /// reclaims the gutter's columns for text — applied live by [`gutter_cols`]. @@ -701,7 +701,7 @@ pub struct Editor { /// command mode can toggle them live; the host reads the file at boot and /// applies it via [`set_prefs`](Self::set_prefs), and reads it back for the /// keys it honours (`save_on_idle`). `format_on_save` and `line_numbers` are - /// consulted in-core (`:w`/`:sync` and the gutter). + /// consulted in-core (`:w`/`:gp` and the gutter). prefs: Prefs, /// The unnamed register: the last yanked or deleted text, replayed by /// `p`/`P`. `y`, `d`, `c`, and `x` all fill it (vim's unnamed register), so @@ -735,7 +735,7 @@ pub struct Editor { /// `/sd/repo/notes.md`). Empty for an unnamed scratch buffer (the boot-message /// layout use); `:w` on an empty path posts "no file name" rather than saving. path: String, - /// The active buffer's scope. Gates Publish — `:sync` is refused in Local. + /// The active buffer's scope. Gates Publish — `:gp` is refused in Local. scope: Scope, /// Whether the active buffer has unsaved edits. Set at each change-group /// ([`checkpoint`](Self::checkpoint)) and cleared when the host confirms a @@ -903,7 +903,7 @@ impl Editor { /// Seed a fresh editor from a named file's saved text — the boot-load and /// file-open path. Same boot posture as [`with_text`](Self::with_text) /// (Normal mode, caret on the last character) but records the file's `path` - /// and `scope` so `:w` knows where to persist and `:sync` knows whether + /// and `scope` so `:w` knows where to persist and `:gp` knows whether /// Publish is offered. pub fn with_file(path: String, scope: Scope, text: String) -> Self { let mut ed = Editor { text, path, scope, ..Editor::new() }; @@ -918,7 +918,7 @@ impl Editor { self.mode } - /// The full buffer contents, for the host to persist on `:w`/`:sync`. + /// The full buffer contents, for the host to persist on `:w`/`:gp`. pub fn text(&self) -> &str { &self.text } @@ -1554,7 +1554,7 @@ impl Editor { self.request_save_active(); } // fmt → save → push, shared with the `>` publish command. - "sync" => self.run_publish(), + "gp" => self.run_publish(), "gl" => self.requests.push(Effect::Pull), _ => {} } @@ -2237,7 +2237,7 @@ impl Editor { self.palette_sel = 0; } - /// The publish path shared by `:sync` and the `>` `publish` command: format on + /// The publish path shared by `:gp` and the `>` `publish` command: format on /// save (if enabled), queue the buffer save, then the git push — the host /// services them in order. Tracked-only: a Local buffer never reaches the /// remote, so it is a no-op with a notice. @@ -2260,7 +2260,7 @@ impl Editor { /// string ([`Theme`](PaletteCmd::Theme), [`AutoSync`](PaletteCmd::AutoSync)) /// rotates to its next option and wraps — so from the palette every setting /// is "press Enter to change". The queued `SavePrefs` is what makes the - /// change durable and lets it ride the next `:sync` to other devices. + /// change durable and lets it ride the next `:gp` to other devices. fn cycle_pref(&mut self, cmd: PaletteCmd) { match cmd { PaletteCmd::SaveOnIdle => self.prefs.save_on_idle = !self.prefs.save_on_idle, @@ -3345,7 +3345,7 @@ impl Editor { /// [`draw`](Self::draw) into a caller-owned frame, reusing its allocation. /// Firmware keeps two boot-time frames and round-trips them through here so /// a repaint never allocates: the editor must stay drawable even when a - /// background `:sync` push has taken the heap to the floor — a failed + /// background `:gp` push has taken the heap to the floor — a failed /// framebuffer alloc aborts the whole app (the 2026-07-13 OOM). pub fn draw_into(&mut self, out: &mut Frame, cursor_on: bool) { let lay = self.layout(); @@ -4347,9 +4347,9 @@ mod tests { } #[test] - fn sync_command_saves_then_publishes() { - // `:sync` queues a save of the current buffer, then the git publish. - assert_eq!(kinds(&command("sync").1), vec![Kind::Save, Kind::Publish]); + fn gp_command_saves_then_publishes() { + // `:gp` queues a save of the current buffer, then the git publish. + assert_eq!(kinds(&command("gp").1), vec![Kind::Save, Kind::Publish]); } #[test] @@ -4358,15 +4358,15 @@ mod tests { } #[test] - fn sync_formats_the_buffer_before_publishing() { - // fmt → save → commit → push: `:sync` runs :fmt in-core first (default on). + fn gp_formats_the_buffer_before_publishing() { + // fmt → save → commit → push: `:gp` runs :fmt in-core first (default on). let mut e = Editor::with_file( "/sd/repo/notes.md".into(), Scope::Tracked, "hello \nworld".to_string(), // trailing spaces ); e.handle(Key::Char(':')); - for c in "sync".chars() { + for c in "gp".chars() { e.handle(Key::Char(c)); } e.handle(Key::Enter); @@ -4375,15 +4375,15 @@ mod tests { } #[test] - fn sync_is_refused_in_a_local_buffer() { - // Publish is Tracked-only; `:sync` in Local queues nothing and warns. + fn gp_is_refused_in_a_local_buffer() { + // Publish is Tracked-only; `:gp` in Local queues nothing and warns. let mut e = Editor::with_file( "/sd/local/journal.md".into(), Scope::Local, "dear diary".to_string(), ); e.handle(Key::Char(':')); - for c in "sync".chars() { + for c in "gp".chars() { e.handle(Key::Char(c)); } e.handle(Key::Enter); diff --git a/firmware/README.md b/firmware/README.md index 6684e10..c79a656 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -190,13 +190,13 @@ builds are incremental. ### Build modes — git (default) vs light -Publishing (`:sync` → git push) is expensive to build: it drags in libgit2 + +Publishing (`:gp` → git push) is expensive to build: it drags in libgit2 + mbedTLS (compiled as an esp-idf component) and the `git2` crate. It sits behind a switch. The nominal build turns it on (it's the product); a **light** build leaves it off — ideal for iterating on the editor, EPD, USB, or SD without paying for libgit2: -| Build | Command | libgit2 component | `git2` crate | `:sync` | +| Build | Command | libgit2 component | `git2` crate | `:gp` | | ----- | ------- | ----------------- | ------------ | ------- | | **Full / git** (default) | `just build` / `just flash` | compiled | linked | save → push | | **Light** | `just build-light` / `just flash-light` | not compiled (empty no-op) | not linked | saves locally, skips push | diff --git a/firmware/src/git_sync.rs b/firmware/src/git_sync.rs index ed79a8d..f80041b 100644 --- a/firmware/src/git_sync.rs +++ b/firmware/src/git_sync.rs @@ -1,4 +1,4 @@ -//! On-device git publish — the transport behind the editor's `:sync`. +//! On-device git publish — the transport behind the editor's `:gp`. //! //! Graduated from the `src/bin/git_sync.rs` spike (milestone #2A, hardware- //! verified 2026-07-07). The spike proved `open` + fast-forward `push` over @@ -17,7 +17,7 @@ //! notes. A `/sd/repo` that isn't a valid repo is a provisioning error //! (`just init`), surfaced as such, not papered over. //! 3. **No synthetic content.** The spike appended a marker line; here the -//! editor has already saved the user's buffers before `:sync` signals us, +//! editor has already saved the user's buffers before `:gp` signals us, //! so we just commit + push what's on disk. //! 4. **The commit is an O(depth) TreeBuilder splice, not an index pass.** //! The request carries the repo-relative paths saved/deleted since the last @@ -92,7 +92,7 @@ const ODB_CACHE_MAX_BYTES: isize = 1024 * 1024; /// What the UI task asks the git thread to do. pub enum GitRequest { - /// `:sync` — commit the dirty paths and push (the upload half). + /// `:gp` — commit the dirty paths and push (the upload half). Publish(PublishRequest), /// `:gl` — fetch + fast-forward only (the download half). The UI only /// sends this when the dirty journal is empty, so the checkout can't @@ -140,7 +140,7 @@ pub enum PullOutcome { /// Origin's tip is our HEAD — nothing to pull. UpToDate, /// We are strictly ahead of origin (e.g. a stranded commit whose push - /// failed) — nothing to pull; the next `:sync` publishes it. + /// failed) — nothing to pull; the next `:gp` publishes it. LocalAhead, /// Local and remote histories diverged. Refused — no merge on the device. Diverged, @@ -214,7 +214,7 @@ pub fn run_git_service( ) { Ok(o) => o, Err(e) => { - log::error!("❌ :sync failed: {e:?}"); + log::error!("❌ :gp failed: {e:?}"); PublishOutcome::Failed(short_reason("sync", &e)) } }, @@ -260,16 +260,16 @@ fn publish_cycle( } // Nothing recorded dirty and origin's tracking ref already has HEAD: this - // `:sync` has nothing to do — say so without touching the radio (~150 ms + // `:gp` has nothing to do — say so without touching the radio (~150 ms // instead of a Wi-Fi + TLS round). A stranded local commit (committed but // never pushed, e.g. a push that failed mid-air) makes the check false and // takes the full path below, where publish_once pushes it. if paths.is_empty() && remote_current().unwrap_or(false) { - log::info!(":sync — no dirty paths and origin has HEAD; up to date, radio untouched"); + log::info!(":gp — no dirty paths and origin has HEAD; up to date, radio untouched"); return Ok(PublishOutcome::UpToDate); } - // Phases are timed so a cold :sync reports where the seconds go. Wi-Fi, clock + // Phases are timed so a cold :gp reports where the seconds go. Wi-Fi, clock // and TLS run only on the first sync of a session; a warm sync skips them, so // they read 0 ms and the total collapses to just publish(fetch+commit+push). let t_total = Instant::now(); @@ -278,7 +278,7 @@ fn publish_cycle( let t_publish = Instant::now(); let outcome = publish_once(paths)?; log::info!( - ":sync timing — publish(commit+push) {}ms, total {}ms", + ":gp timing — publish(commit+push) {}ms, total {}ms", t_publish.elapsed().as_millis(), t_total.elapsed().as_millis(), ); @@ -763,7 +763,7 @@ fn update_tracking(repo: &Repository, branch: &str, tip: Oid) -> Result<()> { /// Open `/sd/repo`, fetch origin, and **fast-forward only** — never a merge. /// The four non-failure shapes map to [`PullOutcome`]: already current, we're -/// strictly ahead (a stranded commit — `:sync`'s job), a clean fast-forward, +/// strictly ahead (a stranded commit — `:gp`'s job), a clean fast-forward, /// or a divergence (refused; a machine with a real git resolves it). /// /// The fast-forward is checkout-then-ref-move, with a **SAFE** checkout: it @@ -827,7 +827,7 @@ fn pull_once() -> Result { let _ = remote.disconnect(); update_tracking(&repo, &branch, theirs)?; log::info!( - "pull: HEAD {} is ahead of origin {} — nothing to pull, :sync publishes it (ls-refs {ls_ms}ms, no fetch)", + "pull: HEAD {} is ahead of origin {} — nothing to pull, :gp publishes it (ls-refs {ls_ms}ms, no fetch)", short(head), short(theirs) ); diff --git a/firmware/src/lib.rs b/firmware/src/lib.rs index f77daa5..59298dd 100644 --- a/firmware/src/lib.rs +++ b/firmware/src/lib.rs @@ -15,7 +15,7 @@ pub mod epd; pub mod net; pub mod persistence; -// On-device git publish (the editor's `:sync` transport). Behind the `git` +// On-device git publish (the editor's `:gp` transport). Behind the `git` // feature so a light build never pulls libgit2/git2 — see main.rs `publish` and // the feature note in Cargo.toml. #[cfg(feature = "git")] diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 176b79d..1d1b2f8 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -93,8 +93,8 @@ fn main() -> anyhow::Result<()> { // Bring up the USB keyboard in the background; keys arrive via next_key(). usb_kbd::start()?; - // Spawn the dedicated git thread — the `:sync` publish transport. It owns - // the Wi-Fi stack (brought up lazily on the first `:sync`, so the radio + // Spawn the dedicated git thread — the `:gp` publish transport. It owns + // the Wi-Fi stack (brought up lazily on the first `:gp`, so the radio // stays off until you publish) and parks on `git_tx` until signalled; the // push runs off the UI loop, and its outcome returns on `git_rx` for the // snackbar. Behind the `git` feature so a light build carries no libgit2. @@ -114,7 +114,7 @@ fn main() -> anyhow::Result<()> { .stack_size(GIT_STACK) .spawn(move || run_git_service(modem, sys_loop, nvs, req_rx, res_tx))?; log::info!( - "git thread up ({} KB stack); Wi-Fi comes up on the first :sync", + "git thread up ({} KB stack); Wi-Fi comes up on the first :gp", GIT_STACK / 1024 ); (req_tx, res_rx) @@ -191,7 +191,7 @@ fn main() -> anyhow::Result<()> { // The only two framebuffers the loop ever uses, both allocated here at // boot: every repaint below renders into `back` (`draw_into` reuses its // allocation) and swaps it with `shown` on success. A repaint must never - // allocate — a background `:sync` push can take the heap to the floor, and + // allocate — a background `:gp` push can take the heap to the floor, and // a failed `Vec` alloc aborts the whole app (the 2026-07-13 OOM: 66 s into // the push, one HalfPageUp repaint died on a 27 KB framebuffer). let mut back = Frame::new_white(); @@ -218,7 +218,7 @@ fn main() -> anyhow::Result<()> { // Service the host-side effects the batch queued, in order. A file open // queues a Save of the outgoing dirty buffer *then* a Load of the target; - // `:sync` queues a Save of the current buffer *then* Publish. Save/Load + // `:gp` queues a Save of the current buffer *then* Publish. Save/Load // are inline (fast SD IO); Publish hands off to the git thread — behind // the `git` feature, so a light build carries no libgit2/git2. // @@ -262,12 +262,12 @@ fn main() -> anyhow::Result<()> { } } #[cfg(not(feature = "git"))] - log::info!(":sync — saved; light build (no `git` feature) — push skipped"); + log::info!(":gp — saved; light build (no `git` feature) — push skipped"); } Effect::Pull => { // `:gl` — fetch + fast-forward, on the git thread like a // publish. Gated on an empty dirty journal: unpublished - // saves would fight the checkout, and `:sync` first is + // saves would fight the checkout, and `:gp` first is // the appliance's natural order anyway. (A RAM-dirty // buffer that was never saved doesn't gate — its edits // simply win over the pulled state, see the outcome @@ -279,8 +279,8 @@ fn main() -> anyhow::Result<()> { // Log it too — on the 2026-07-14 run this gate // firing looked like a silent no-op in the // serial log. - log::info!(":gl refused — dirty journal non-empty; :sync first"); - ed.set_notice("pull: unsynced changes - :sync first"); + log::info!(":gl refused — dirty journal non-empty; :gp first"); + ed.set_notice("pull: unsynced changes - :gp first"); } else { match git_tx.send(GitRequest::Pull) { Ok(()) => ed.set_notice("pulling..."), @@ -314,7 +314,7 @@ fn main() -> anyhow::Result<()> { GitOutcome::Publish(outcome) => { // Settle the dirty snapshot this publish took: confirmed // published (or up to date) → forget it; failed → back to - // pending so the next :sync retries the same paths. + // pending so the next :gp retries the same paths. match &outcome { PublishOutcome::Pushed(_) | PublishOutcome::UpToDate => { storage.publish_succeeded() @@ -357,7 +357,7 @@ fn main() -> anyhow::Result<()> { format!("pulled {oid}") } PullOutcome::UpToDate => "up to date".to_string(), - PullOutcome::LocalAhead => "ahead - :sync to publish".to_string(), + PullOutcome::LocalAhead => "ahead - :gp to publish".to_string(), PullOutcome::Diverged => "diverged - resolve on a computer".to_string(), PullOutcome::Failed(reason) => reason, }, @@ -410,7 +410,7 @@ fn main() -> anyhow::Result<()> { // buffer so a power pull can't cost more than the last couple seconds. // Silent — no snackbar and no forced e-ink flash (a safety net, not an // action; `:w` is the loud save). Unformatted: fmt only runs on an - // explicit `:w`/`:sync`, never reflowing text mid-session. Fires once + // explicit `:w`/`:gp`, never reflowing text mid-session. Fires once // per idle window (`idle_saved`), so a failing save can't busy-loop. if !idle_saved && ed.prefs().save_on_idle @@ -506,7 +506,7 @@ fn main() -> anyhow::Result<()> { // exactly like a failed `save_buffer`. Drop this frame, leave `shown` // untouched so the next paint repaints the same diff, and force a // clean full refresh then. Typical cause: internal DMA-capable RAM - // briefly starved by Wi-Fi/TLS during a background `:sync`; it frees + // briefly starved by Wi-Fi/TLS during a background `:gp`; it frees // the moment the push finishes. log::warn!("{refresh} refresh #{updates} FAILED ({e}); frame dropped, full refresh next"); force_full = true; @@ -595,7 +595,7 @@ fn save_buffer(storage: &Storage, ed: &mut Editor, path: &str, contents: &str) { /// Persist the preferences file after a palette `>` command changed a pref /// (`Effect::SavePrefs`). The editor already applied the change live and /// serialized it; this is a plain atomic write to the fixed `.typoena.toml` -/// path. Under `/sd/repo`, so it rides the next `:sync` to other devices. +/// path. Under `/sd/repo`, so it rides the next `:gp` to other devices. fn save_prefs(storage: &Storage, ed: &mut Editor, contents: &str) { match storage.save_path(PREFS_PATH, contents) { Ok(()) => log::info!("prefs saved to {PREFS_PATH}"), @@ -626,21 +626,21 @@ fn open_buffer(storage: &Storage, ed: &mut Editor, path: String, scope: Scope) { /// Unlink a file from the card (`:delete`). The editor has already dropped it /// from its model and switched away, so this is pure IO plus the snackbar. For a -/// Tracked file the removal is left in the git working copy — the next `:sync`'s +/// Tracked file the removal is left in the git working copy — the next `:gp`'s /// `add --all` stages the deletion — so nothing git-specific happens here. A /// failure keeps the file on disk and says so; the buffer has still switched, so /// the file is recoverable by re-opening it. fn delete_buffer(storage: &Storage, ed: &mut Editor, path: String, scope: Scope) { // Scope-qualified label (`repo/notes.md`), so the snackbar names exactly which // file left the card — and, for a Tracked file, that the removal is only local - // until the next `:sync` publishes it (deleting from the card alone never + // until the next `:gp` publishes it (deleting from the card alone never // touches the remote — that mirrors how a Save is local until Publish). let label = path.strip_prefix("/sd/").unwrap_or(&path); match storage.delete_path(&path) { Ok(()) => { log::info!("deleted {path} ({scope:?})"); ed.set_notice(match scope { - Scope::Tracked => format!("deleted {label} - :sync to publish"), + Scope::Tracked => format!("deleted {label} - :gp to publish"), Scope::Local => format!("deleted {label}"), }); } diff --git a/firmware/src/persistence.rs b/firmware/src/persistence.rs index a01661a..c72c80a 100644 --- a/firmware/src/persistence.rs +++ b/firmware/src/persistence.rs @@ -272,7 +272,7 @@ impl Storage { if carried > 0 { log::info!( "dirty journal: {carried} unpublished path(s) carried over from a previous \ - session — the next :sync will commit them" + session — the next :gp will commit them" ); } Ok(storage) @@ -431,7 +431,7 @@ impl Storage { /// Whether any saved-but-unpublished paths are recorded (pending or riding /// an in-flight publish). `:gl` refuses to pull while this is true: a - /// fast-forward checkout would fight those files, and `:sync` first is the + /// fast-forward checkout would fight those files, and `:gp` first is the /// single-writer appliance's natural order anyway. pub fn has_dirty(&self) -> bool { let d = self.dirty.borrow(); @@ -441,7 +441,7 @@ impl Storage { /// Snapshot the dirty paths for a publish (repo-relative). The snapshot /// moves to `in_flight` — the journal keeps carrying it — until the UI /// task reports the outcome: [`Storage::publish_succeeded`] forgets it, - /// [`Storage::publish_failed`] returns it to pending for the next `:sync`. + /// [`Storage::publish_failed`] returns it to pending for the next `:gp`. pub fn take_dirty(&self) -> BTreeSet { let mut d = self.dirty.borrow_mut(); let taken = std::mem::take(&mut d.pending); @@ -457,7 +457,7 @@ impl Storage { self.persist_dirty(); } - /// The publish failed: return its snapshot to pending so the next `:sync` + /// The publish failed: return its snapshot to pending so the next `:gp` /// retries it (the splice is idempotent, so a retry of an already-clean /// path is free). The journal already carries these paths — no rewrite. pub fn publish_failed(&self) { @@ -487,7 +487,7 @@ impl Storage { /// Seed the dirty set from the journal at mount — the paths a previous /// session saved but never got confirmed as published (power pull, failed - /// sync, or simply no `:sync` before shutdown). Returns how many. + /// sync, or simply no `:gp` before shutdown). Returns how many. fn load_dirty_journal(&self) -> usize { let Ok(text) = fs::read_to_string(DIRTY_JOURNAL) else { return 0; // no journal yet — nothing carried over