From 0c1bd64cb9ac545edf194709aad9507e5f5652c9 Mon Sep 17 00:00:00 2001 From: PureTensor Date: Sat, 17 Oct 2026 16:54:47 +0100 Subject: [PATCH] v0.1.3: release the second-review fixes under their own version The v0.1.2 tag (ae32eeb) was published before the 14 fixes in 3f51f1b, so pinned v0.1.2 installs lack them while main reported 0.1.2 with them. - __version__ is 0.1.3. - CHANGELOG.md: the 0.1.2 section is back to its wording at the tag, and a new 0.1.3 section lists the 14 fixes, including that a changed embedder_revision now causes a full re-embed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011zd8G1cibJoQPVbZEpVLYs --- CHANGELOG.md | 51 ++++++++++++++++++++++++++++++++---- src/puremind_mac/__init__.py | 2 +- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a37ccfd..b04621b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ # Changelog +## 0.1.3 (2026-10-17) + +Fixes from a second review of 0.1.2. Upgrading from 0.1.2 embeds nothing again. The `v0.1.2` tag was made before these fixes, so an install from `main` between the two releases reports 0.1.2 but already has them. + +### Indexing + +- A local model path is compared in absolute form, so an index built with a path as typed (`~/models/x`) no longer makes every search from `puremind serve` fail with a model mismatch that `--rebuild` could not clear. +- The index records the model's revision. A changed `embedder_revision` now causes a full re-embed: the next index run empties the index and embeds every note again, and searches are refused until then. An index from an earlier release, which recorded no revision, is kept as it is. +- When a folder cannot be listed (a root-owned `lost+found`, for example), only the notes below it are kept in the index. Notes deleted elsewhere in the vault are removed; 0.1.2 removed none while any folder was unreadable. +- Two processes upgrading an index from 0.1.1 at the same time no longer fail with "duplicate column". + +### Watcher + +- `puremind watch` stops with an error when a system limit keeps it from watching the vault (the inotify watch limit, or no free file descriptors or memory). 0.1.2 took this for a missing vault and retried four times a second without end, leaking a file descriptor each time. +- Changes are no longer missed when the vault path and the paths in file events differ only in letter case or Unicode normalisation. +- `puremind watch` stops when a vault symlink is pointed at a directory the index does not belong to, instead of failing every run. + +### Models + +- Each model runs one encode or rerank call at a time. Overlapping tool calls shared a model without a lock, and its fast tokenizer could fail with "Already borrowed". +- When a cached model copy has what the models need but completing its missing files fails, the cached copy is used instead of the load failing. +- The offline error names the variable that is set (`HF_HUB_OFFLINE` or `TRANSFORMERS_OFFLINE`). + +### Chunker + +- A tiny section merged into the chunk before it is labelled with the headings the sections share, so the breadcrumb covers all of its text. Notes already indexed keep their breadcrumbs until they change or `puremind index --rebuild` runs. + +### MCP tools + +- `puremind_get` clears non-blocking mode before reading, so a note that has to be fetched first (a cloud placeholder) no longer fails with `TypeError`. +- Tool errors name a local model by its last path component, so no absolute path reaches the agent. The command line still prints the full path. + +### Command line + +- Bidirectional control characters (U+202E, for example) are removed from printed text, so a note can no longer reorder the lines `search` prints. +- `puremind connect` also carries `TRANSFORMERS_OFFLINE` and `HF_ENDPOINT`. + +### Benchmark + +- `bench/longmemeval.py` follows `[retrieval] rerank`. + ## 0.1.2 (2026-10-17) Fixes from a full review of 0.1.1. The first `puremind index` or `watch` run after upgrading cuts and embeds every note again, so it takes about as long as the first index did; searches keep working meanwhile. @@ -16,17 +57,17 @@ Fixes from a full review of 0.1.1. The first `puremind index` or `watch` run aft - Runs happen one at a time on the main thread, and a change during a run schedules exactly one more. A note edited while it was being embedded is read again before it is stored, so an older version can no longer overwrite a newer one. - Ctrl-C exits with status 130 and no traceback, without leaving the index half-written. - The watcher follows a vault directory that is replaced (a remount, or a sync tool swapping the folder), made again under the same inode number (ext4 does this), or unreadable for a while, and scans anyway after 10 quiet minutes. A vault that disappears again while it is being watched no longer ends `puremind watch`, and nothing runs while the vault is missing. -- A run that fails while watching is reported on one line, without a traceback, and watching goes on. Two failures end it instead, since no later run could succeed: a vault symlink that now names a directory the index does not belong to, and a system limit (the inotify watch limit, for example) that keeps the vault from being watched again. +- A run that fails while watching is reported on one line, without a traceback, and watching goes on. ### Indexing - The vault-ownership check runs before anything is cleared, and a new embedding model must embed a test passage before the old index is discarded. A mistyped model name, or a test run with `PUREMIND_DB` exported, can no longer wipe an index. -- Notes below a folder that cannot be listed are not removed from the index, and nothing is removed when the vault looks empty while the index is not (an unmounted drive, for example). A note that exists but cannot be read keeps its entry. +- Nothing is removed from the index when a folder cannot be listed or the vault looks empty while the index is not (an unmounted drive, for example). A note that exists but cannot be read keeps its entry. - One bad note no longer stops a run: symlink loops, pipes, names that are not valid UTF-8, and notes that fail to read or chunk are skipped (the last three are also reported). Visible symlinks to hidden or non-Markdown files are skipped, and a note replaced by such a symlink while a run is in progress is not read through it. - A failing index file (a full disk, or another process holding the lock too long) stops the run with an error and exit status 2, instead of skipping every remaining note. - Notes whose path inside the vault is longer than 1,024 characters are skipped and reported, since `puremind_get` could not open them. - Embedding is batched across notes (64 chunks per call), two to four times faster for vaults of small notes in our measurements. When the model fails on a batch, its notes are embedded one at a time, so a note the model cannot handle is skipped and named instead of stopping the run; a model that fails on every note still stops it. -- `--rebuild` embeds every note again in place, so searches keep finding the old version of each note until it is replaced. It used to empty the index first. (A new embedding model, or a new revision of it, still empties it, since vectors from two models cannot be searched together. The index records the revision, so a changed `embedder_revision` is noticed like a changed model.) +- `--rebuild` embeds every note again in place, so searches keep finding the old version of each note until it is replaced. It used to empty the index first. (A new embedding model still empties it, since vectors from two models cannot be searched together.) - Each note records the version of the chunker that cut it, so a release that changes chunking cuts every note again by itself. 0.1.2 does this once for indexes from earlier versions. - Folders nested so deep that no note below them could be opened are not entered. On Python 3.11 a vault nested about 1,000 folders deep used to stop the run. - An index whose vault path is spelled differently (APFS ignores letter case) is recognised as the same vault. @@ -65,7 +106,7 @@ Fixes from a full review of 0.1.1. The first `puremind index` or `watch` run aft ### Command line - `puremind init` writes TOML correctly for any vault path (an emoji in a path made every later command fail), keeps a configured `db` with `--force`, never copies environment overrides into the file, and creates `~/.puremind` as 0700 and config.toml as 0600. A config.toml that is a symlink keeps its target's mode. A malformed config.toml is reported with its path, and `init --force` repairs it. -- `puremind connect` prints an absolute, quoted command and carries the `PUREMIND_*` variables and the Hugging Face cache, offline and endpoint variables set in your shell. The Cursor entry has `type: stdio`. +- `puremind connect` prints an absolute, quoted command and carries the `PUREMIND_*` and Hugging Face cache variables set in your shell. The Cursor entry has `type: stdio`. - Exit status is 0 on success, 1 when a search finds nothing, 2 on errors (an `error:` line, sometimes followed by a hint or cause line, and no traceback for expected failures) and 130 on Ctrl-C. `puremind serve` now stops on Ctrl-C. - `index` and `watch` check the vault directory before opening the index, so a mistyped path no longer creates an empty index, and the error names the path. - `puremind serve` no longer writes a library's debug lines to its log. @@ -87,7 +128,7 @@ Fixes from a full review of 0.1.1. The first `puremind index` or `watch` run aft - The version has one source, `puremind_mac.__version__`. - CI runs on macOS and Linux with Python 3.11 to 3.13 (and 3.14 on Linux), plus jobs for the mcp 1.x SDK, the lowest allowed dependencies, the built wheel, ruff, pip-audit and gitleaks, and a weekly smoke test with the real models. Actions are pinned to commit SHAs with a read-only token. - The test suite is hermetic: it ignores `PUREMIND_*` variables in your shell, runs offline, and never touches your index or vault. Against an installed wheel, its server and command-line subprocesses run the wheel too. -- `bench/longmemeval.py` uses the current Hugging Face download command, adds LongMemEval's own metrics, records its settings, follows `[retrieval] rerank`, and resumes safely after a crash. +- `bench/longmemeval.py` uses the current Hugging Face download command, adds LongMemEval's own metrics, records its settings, and resumes safely after a crash. ## 0.1.1 (2026-10-16) diff --git a/src/puremind_mac/__init__.py b/src/puremind_mac/__init__.py index 5e79159..5b3150f 100644 --- a/src/puremind_mac/__init__.py +++ b/src/puremind_mac/__init__.py @@ -1,3 +1,3 @@ """pureMind for Mac: local memory for AI coding agents, served over MCP.""" -__version__ = "0.1.2" +__version__ = "0.1.3"