Done. Per-directory session persistence is in. #47

Merged
navicore merged 1 commit from persist-repl into main 2026-07-04 19:18:01 +00:00
Owner

What it does

Restart plgr in the same directory and your program is back; :reset clears it
(and the saved state) so a fresh start sticks. Each project directory gets its
own session — never a global program.

  • Where: $HOME/.local/share/plgr/sessions/.pl (mirrors the
    existing plgr_history convention).
  • On startup: App::new() loads this dir's session, recompiles it (so the first
    query works), and logs restored N clause(s) from this directory's session.
  • On exit: main.rs calls the new app.save_session() right after
    save_history().
  • :reset: now also deletes the persisted file (otherwise the restart would
    resurrect it).
  • Empty session: saving an empty buffer deletes the file rather than writing a
    stub, so an empty exit behaves like :reset.

Design choices worth flagging

  1. Filename encoding — I used a reversible percent-encoding of the absolute
    cwd, not a hash. Two reasons: (a) collision-free — a naive /→_ would
    conflate /a/b with /a_b; (b) no new dep and stable forever —
    DefaultHasher's algorithm isn't locked across Rust versions and would
    silently orphan sessions after a toolchain bump, and there's no sha2/dirs
    in the workspace (and the project keeps deps lean). Each file gets a
    leading % cwd: /abs/path line for cat-readability, stripped on load so it
    never pollutes :list.
  2. Save timing = on exit (matching save_history), not after every clause. This
    keeps it consistent with the existing model and avoids per-keystroke disk
    I/O. The trade-off: a crash/SIGKILL loses work since the last exit. If
    you'd rather have autosave-after-recompile (lose nothing, small I/O cost
    per clause add), that's a one-line move of persist::save into recompile() —
    say the word and I'll switch it.
  3. plgr somefile.pl still works: it restores the cwd session then loads the
    file on top (existing load_source extends rather than replaces), so the
    explicit file augments the persisted session.

Files

  • crates/repl/src/persist.rs (new, ~130 lines, focused + independently tested)
  • crates/repl/src/app.rs — cwd field, restore_session/save_session, :reset
    delete, :help note
  • crates/repl/src/main.rs — module decl + save_session() exit hook

Verification

501 tests pass workspace-wide (was 495; +6 new — encoding injectivity,
tag-stripping, and a real filesystem round-trip that also pins the
:reset-deletes-file path). cargo fmt --check clean, cargo clippy -D warnings
clean workspace-wide. No runtime/compiler changes; no new dependencies.

What it does Restart plgr in the same directory and your program is back; :reset clears it (and the saved state) so a fresh start sticks. Each project directory gets its own session — never a global program. - Where: $HOME/.local/share/plgr/sessions/<encoded-cwd>.pl (mirrors the existing plgr_history convention). - On startup: App::new() loads this dir's session, recompiles it (so the first query works), and logs restored N clause(s) from this directory's session. - On exit: main.rs calls the new app.save_session() right after save_history(). - :reset: now also deletes the persisted file (otherwise the restart would resurrect it). - Empty session: saving an empty buffer deletes the file rather than writing a stub, so an empty exit behaves like :reset. Design choices worth flagging 1. Filename encoding — I used a reversible percent-encoding of the absolute cwd, not a hash. Two reasons: (a) collision-free — a naive /→_ would conflate /a/b with /a_b; (b) no new dep and stable forever — DefaultHasher's algorithm isn't locked across Rust versions and would silently orphan sessions after a toolchain bump, and there's no sha2/dirs in the workspace (and the project keeps deps lean). Each file gets a leading % cwd: /abs/path line for cat-readability, stripped on load so it never pollutes :list. 2. Save timing = on exit (matching save_history), not after every clause. This keeps it consistent with the existing model and avoids per-keystroke disk I/O. The trade-off: a crash/SIGKILL loses work since the last exit. If you'd rather have autosave-after-recompile (lose nothing, small I/O cost per clause add), that's a one-line move of persist::save into recompile() — say the word and I'll switch it. 3. plgr somefile.pl still works: it restores the cwd session then loads the file on top (existing load_source extends rather than replaces), so the explicit file augments the persisted session. Files - crates/repl/src/persist.rs (new, ~130 lines, focused + independently tested) - crates/repl/src/app.rs — cwd field, restore_session/save_session, :reset delete, :help note - crates/repl/src/main.rs — module decl + save_session() exit hook Verification 501 tests pass workspace-wide (was 495; +6 new — encoding injectivity, tag-stripping, and a real filesystem round-trip that also pins the :reset-deletes-file path). cargo fmt --check clean, cargo clippy -D warnings clean workspace-wide. No runtime/compiler changes; no new dependencies.
Done. Per-directory session persistence is in.
All checks were successful
WASM Gates / WASM gates (Tier 1 wasi + Tier 2 reactor) (pull_request) Successful in 8s
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 1m2s
84841b57fa
What it does

 Restart plgr in the same directory and your program is back; :reset clears it
 (and the saved state) so a fresh start sticks. Each project directory gets its
 own session — never a global program.

 - Where: $HOME/.local/share/plgr/sessions/<encoded-cwd>.pl (mirrors the
   existing plgr_history convention).
 - On startup: App::new() loads this dir's session, recompiles it (so the first
   query works), and logs restored N clause(s) from this directory's session.
 - On exit: main.rs calls the new app.save_session() right after
   save_history().
 - :reset: now also deletes the persisted file (otherwise the restart would
   resurrect it).
 - Empty session: saving an empty buffer deletes the file rather than writing a
   stub, so an empty exit behaves like :reset.

 Design choices worth flagging

 1. Filename encoding — I used a reversible percent-encoding of the absolute
    cwd, not a hash. Two reasons: (a) collision-free — a naive /→_ would
    conflate /a/b with /a_b; (b) no new dep and stable forever —
    DefaultHasher's algorithm isn't locked across Rust versions and would
    silently orphan sessions after a toolchain bump, and there's no sha2/dirs
    in the workspace (and the project keeps deps lean). Each file gets a
    leading % cwd: /abs/path line for cat-readability, stripped on load so it
    never pollutes :list.
 2. Save timing = on exit (matching save_history), not after every clause. This
    keeps it consistent with the existing model and avoids per-keystroke disk
    I/O. The trade-off: a crash/SIGKILL loses work since the last exit. If
    you'd rather have autosave-after-recompile (lose nothing, small I/O cost
    per clause add), that's a one-line move of persist::save into recompile() —
    say the word and I'll switch it.
 3. plgr somefile.pl still works: it restores the cwd session then loads the
    file on top (existing load_source extends rather than replaces), so the
    explicit file augments the persisted session.

 Files

 - crates/repl/src/persist.rs (new, ~130 lines, focused + independently tested)
 - crates/repl/src/app.rs — cwd field, restore_session/save_session, :reset
   delete, :help note
 - crates/repl/src/main.rs — module decl + save_session() exit hook

 Verification

 501 tests pass workspace-wide (was 495; +6 new — encoding injectivity,
 tag-stripping, and a real filesystem round-trip that also pins the
 :reset-deletes-file path). cargo fmt --check clean, cargo clippy -D warnings
 clean workspace-wide. No runtime/compiler changes; no new dependencies.
navicore deleted branch persist-repl 2026-07-04 19:18:01 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-prolog!47
No description provided.