Files
notescraft/scripts/waves/wave4/engine-session.md
T
Marcos Paulo 57613be7f7 Wave 4: engine session prompt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 19:38:15 -03:00

4.1 KiB

Crate: crates/engine (notescraft-engine, layer L4). Slice 2: the Session and the first real commands. Slice 1 (the generic command registry Registry<C>, CommandSpec<C>, ParamSpec, Error) already exists: read src/lib.rs and src/registry.rs, src/spec.rs, src/params.rs first and follow their style; do not change registry behaviour. SPEC sections 3.1, 4.1-4.3, 6 items 1-3, 10. Allowed new dependencies (all { workspace = true }): notescraft-vault, notescraft-markdown, notescraft-index, serde, serde_json, thiserror. Read crates/vault/src/lib.rs, crates/index/src/lib.rs and crates/markdown/src/lib.rs for the exact APIs. Build in SMALL steps: one new file per tool call (<=250 lines), build and run clippy after each file, declare every module in lib.rs.

Implement:

  1. session.rs: Session::open(root: &Path) -> Result<Session> opens the vault (Vault::open), scans list() and feeds every .md file into notescraft_index::Index (update_note) and registers other files with add_file; Session::open_read_only. Fields are private; accessors vault(), index(). A file that fails to read or index is skipped and recorded in session.diagnostics() (never aborts the open).
  2. buffers.rs: open note buffers: Buffer { path, text, saved_text, undo: Vec<Edit>, redo: Vec<Edit> } with edit(range, text) using notescraft_markdown::Document::replace_range semantics (invalid ranges are errors), undo(), redo(), is_dirty(), cap 1000 undo steps and 50 MiB per buffer; Session::open_buffer(path), close_buffer(path) (error if dirty unless force), save_buffer(path) (writes through Vault::write, then re-indexes), buffers().
  3. notes.rs operations on the session (pure Rust methods the commands call): read_note, write_note(path, content) (vault write + update_note), create_note(path, content) (fails if exists; path must end in .md), delete_note (to trash + remove_note), rename_note(old, new, update_links: bool): moves the file, re-keys the index, and when update_links is true rewrites every inbound link in other notes (use Index::backlinks for source and byte ranges and notescraft_markdown::parse to find the link item; keep the alias and the #heading/#^block suffix, keep the basename form when the link used a bare name, use the new path form when it used a path); all link rewrites are collected first and applied only if every note can be rewritten (otherwise nothing is written and an error lists the failures). Returns a RenameReport { rewritten: Vec<String> }.
  4. external.rs: Session::apply_event(VaultEvent) re-reads a changed file and updates the index; ignores events for files with a dirty buffer (records a conflict in session.conflicts() instead).
  5. commands.rs: pub fn register_core(reg: &mut Registry<Session>) registering with exact ids: vault.info (note count, tag count, unresolved count), note.list (optional folder), note.read, note.write, note.create, note.delete, note.rename (from, to, optional update_links default true), links.outgoing, links.backlinks, links.unresolved, tags.list (optional prefix), buffer.open, buffer.edit, buffer.save, buffer.undo, buffer.redo. Each has full ParamSpecs, a doc, enabled rules (write commands disabled on a read-only session) and returns JSON. Commands never panic on any params (the registry validates types; you still validate values: paths, ranges).

Tests (integration in tests/, build temp vaults under std::env::temp_dir with unique names): open a vault with 20 notes and check index counts; read-only session rejects writes; create/write/delete; rename with link rewriting (bare, aliased, heading, path-form, embed, markdown link) and the all-or-nothing behaviour; buffer edit/undo/redo/dirty/close rules; external change applied and conflict recorded when dirty; every core command callable through Registry::run with valid params and rejected with bad params; the registry panic_hunt adversarial params against all core commands (no panic escapes). Reserved paths .obsidian/ and .notescraft/ are rejected by the vault already: assert the commands surface that error.