v3.2.2
3.2.2 - 2026-05-28
Internal hardening, tooling, and documentation. No behavioral or API
changes — a safe patch upgrade from 3.2.1.
Added
- Pyright as a second type-check gate in CI. A dedicated
typecheck-pyrightjob runspyright(standard mode, scoped to the
package) alongside the existing strict mypy gate, so the two checkers
cover each other's blind spots. Configuration moved into
[tool.pyright]inpyproject.toml(replacing a stray
pyrightconfig.jsonthat pinned the MCP-server runtime venv, which CI
doesn't build); the optional lazy-imports (sentence-transformers,
fastembed, numpy, anthropic, openai) carry targeted
# pyright: ignore[reportMissingImports]mirroring the existing mypy
ignore_missing_importsoverride.reportUnreachableis left off
deliberately: the handlers' defense-in-depthisinstance/ None
guards at the MCP-JSON boundary are "unreachable" to the static
checker but validate untyped runtime input — the same reason the mypy
config does not enablewarn_unreachable.
Changed
- One definition of the durable-private-write discipline (Q29).
store._atomic_write_postandepisodes._write_pathwere two
near-identical hand-rolled copies of the tmp + fchmod-before-rename +
fsync + rename + dir-fsync ceremony. Both now delegate to
_fsutil.atomic_write_bytes, which gained amode_before_rename
option —os.fchmodon the tmp file descriptor before the rename, so
a 0o600 file is never world-readable at its visible path even for an
instant (mutually exclusive with the existing chmod-after-rename
mode). Behaviour-preserving: identical privacy guarantee, fsync
ordering, dir-fsync ceremony, and orphan-tmp cleanup. The two
remaining bespoke writers stay bespoke for good reason —
events._compress_rotatingstreams gzip in 64 KB chunks and
semantic.flush_persistent_cachewrites a numpy container under a
flock, so neither is a plain-bytes-in-memory caller. A future fix to
the write discipline now lands in one place instead of three.
Fixed
- Documentation accuracy. Corrected a
Store.restoreexcept
comment that attributed the malformed-createdValueErrorto a
non-existent_load_tombstone_pathsymbol — it's raised inline in
Store.restore(Y9-fu3). Refreshed thedocs/ROADMAP.md"where we
are" version label (v3.1.0 → v3.2.2), whose body already described the
3.2.x feature set. Added the missingunique_silent_miss_memories
field to thecuration_pendingrollup list in the plugin skill doc,
aligning it withdocs/api.md.
Full diff: v3.2.1...v3.2.2