All notable changes to Palinode. Format follows Keep a Changelog.
pyproject.tomlbumped to v0.8.6 and[tool.setuptools] packageslist corrected:palinode.diagnostics,palinode.diagnostics.checks,palinode.import_, andpalinode.lintwere missing and would have been silently omitted from the wheel. All declared packages now match on-disk layout.palinode mcp-config --diagnosenow covers Roo Cline (rooveterinaryinc.roo-cline) in addition to the original Cline extension. Roo Cline uses a different extension ID and settings filename (mcp_settings.jsoninstead ofcline_mcp_settings.json).docs/MCP-INSTALL-RECIPES.md— new JetBrains AI Assistant section (section 6): stdio and HTTP snippets, settings UI path, Settings Sync note, troubleshooting table. Covers IntelliJ IDEA, PyCharm, WebStorm, GoLand, Rider, CLion, DataGrip, RubyMine.docs/MCP-INSTALL-RECIPES.md— transport quick reference expanded into a "which transport?" decision block with use-when guidance.docs/MCP-CONFIG-HOMES.md— JetBrains section added (UI-first, version-specific path caveat) and Roo Cline paths added. Closes public #24.tests/integration/test_mcp_e2e.py— E2E test suite for the MCP client flow: exercises every major MCP tool (search, save, session_end, status, read, history, doctor, list) via in-process FastAPI dispatch with no Ollama required (#122).tests/integration/test_security.py— Security test suite covering OWASP top-10: path traversal, null bytes, symlink escape, SQL injection, SSRF, CORS enforcement, rate limiting, request size limit, no stack traces, YAML injection, CRLF header injection, and XSS/script injection (#123).palinode_cluster_neighbors/palinode cluster-neighbors/POST /cluster-neighbors— given a memory file path, returns the top-K semantically related files that are NOT already wikilinked to or from it; surfaces implicit relationships for the LLM to propose new cross-links (#235).palinode_topic_coverage/palinode topic-coverage/POST /topic-coverage— given a topic phrase, returns{covered, best_match, similarity}indicating whether an existing wiki page already covers the topic; use before ingesting new content to avoid redundancy (#235).- Both new tools are exposed across all four surfaces (MCP, REST API, CLI, parity registry) and covered by
tests/test_embedding_tools.py(#235). - IETF KU frontmatter alignment (issue #106):
parse_ku_fields()inpalinode/core/parser.pyrecognizesku_version,confidence, andlifecyclefields;config.ku_compatflag (defaultenabled: false) controls auto-population on save;confidenceis surfaced as a top-level key in search results when set. Seedocs/HOW-MEMORY-WORKS.mdfor field semantics. palinode import --from-vault <path>— import existing Obsidian vault .md files into the palinode memory store. Infers category from PARA directory structure (Projects→projects/, Areas→decisions/, Resources→research/, Archive→archive/), daily-note filename patterns, and frontmattertype:field. Rewrites wikilinks to point at new slugged paths; orphaned links are left as-is with a warning. Supports--apply(default is dry-run),--overwrite, and--into-categoryoverride. Implemented inpalinode/import_/vault.pyandpalinode/cli/import_vault.py(#236).flake.nix,nix/services/palinode-service.nix,nix/services/mcp-service.nix— Nix flake and NixOS service modules for palinode API, watcher, and MCP server; community contribution welcome for refinement on real NixOS boxes (#38).external_refsfrontmatter field for SDLC object linking — attach GitLab MR/issue/pipeline, GitHub PR, Linear, Jira, or any free-form key/value ref to a memory at save time. Supported across API (POST /save), CLI (palinode save --external-ref KEY=VALUE, repeatable), MCP (palinode_save), and plugin. Recognised keys render with pretty labels in search results; unrecognised keys pass through unchanged (#115).palinode_depends— milestone dependency modeling tool (#97). Readsdepends_on,blocks, andparallel_withfrontmatter from ProjectSnapshot files and returns the dependency neighbourhood for a slug;--unblocked(CLI) /unblocked=true(MCP/REST) returns all items whose every dependency is done. Exposed on all four surfaces: MCP (palinode_depends), REST (GET /depends/{slug},GET /depends/_unblocked), CLI (palinode depends <slug>), and plugin (palinode_depends).palinode lint --deep-contradictions— opt-in LLM-confirmed semantic contradiction check across alltype: Decisionmemories. Uses embedding similarity to identify candidate pairs (configurable--similarity-threshold, default 0.75), then calls the configured LLM endpoint to classify each as CONTRADICTION / AGREEMENT / UNRELATED. Hard cap via--max-llm-calls(default 50). Defaultpalinode lintis unchanged and makes no LLM calls (#98).tests/test_mcp_tool_count.py— assertion test that keeps thedocs/MCP-SETUP.mdavailable-tools table in sync withpalinode/mcp.pyregistered tools (#238).docs/LAUNCH-CHECKLIST.md— pre-launch readiness checklist for v1.0 (#125).docs/MCP-SETUP.md— Codex CLI section (#52).docs/INSTALL-CLAUDE-CODE.md— Cursor and Codex CLI sections with skill paths and MCP config locations (#52).README.md— "Supported Platforms" table listing all supported clients (#52).- GitHub Actions CI workflow (
.github/workflows/ci.yml) with unit-tests (Python 3.11/3.12 matrix), integration-tests, and security-scan jobs triggered on every push and PR (#121). - GitHub Actions post-merge sweep (
.github/workflows/main-ci.yml) triggered on push tomain; auto-opens a GitHub issue on regression (#198). - Integration test suite expanded to 24 tests in
tests/integration/test_api_roundtrip.py, covering git-commit behaviour, CORS origin enforcement, additional path-traversal and null-byte cases, missing-field validation, and an explicit save-index-search-read roundtrip. (issue #120) - Retrieval-event instrumentation (#256): every
palinode_search,palinode_read,palinode_history, andpalinode_blamecall now appends a structuredRetrievalEventto.audit/retrievals.jsonl, distinguishingexplicit(tool-call) frompassive(auto-inject) modes. No ranker behavior change. palinode retrieval-statsCLI command reads the JSONL log and reports event totals, explicit/passive breakdown, top-20 retrieved files, retrieval-frequency distribution, and mean/median time-since-last-retrieval.instrumentation.capture_retrievalsconfig key (defaulttrue) andPALINODE_INSTRUMENTATION_DISABLED=1env var to suppress retrieval logging for privacy.
PalinodeAPI.__init__now accepts an optionalclient: httpx.Clientargument for test injection (#197).palinode_historynow acceptsdetail="full"for commit-level diffs (#32);palinode_timelineadded as a deprecated alias.- Plugin TS schemas close the remaining plugin parity drift (#176) — 11 missing params added to
palinode_searchandpalinode_save; allknown_driftentries removed. docs/MCP-SETUP.md— removed prose tool count; the available-tools table is now the source of truth; addedpalinode_doctorandpalinode_doctor_deeprows (#238).docs/MCP-SETUP.md,docs/MCP-INSTALL-RECIPES.md,deploy/systemd/README.md,README.md— clarified thatpalinode-mcp-sseserves streamable-HTTP at/mcp/(name is historical); use"type": "http"and always include the trailing slash (#258).- Nightly consolidation (
nightly.allowed_ops) now includesMERGE(#202). The executor enforces a same-day guard: only facts sharing the same[YYYY-MM-DD]date prefix may be merged in a nightly run. Cross-date or undated MERGE proposals are rejected with a log warning and counted asmerge_rejectedin the stats dict.
palinode initHOOK_SCRIPT (the scaffolded SessionEnd hook) now usesjq -sslurp extraction for both MSG_COUNT and FIRST_PROMPT, eliminating the SIGPIPE class entirely. #257 fixed the same bug pattern inexamples/hooks/palinode-session-end.shbut the scaffolded version inpalinode/cli/init.pywas missed; #267 closes that gap sopalinode initproduces a non-buggy hook on fresh installs.- Default
audit.log_pathis now resolved to an absolute path undermemory_dirat config load time, eliminating the spuriousaudit_log_writabledoctor warning on every fresh install (#254). mcp_config_homesdoctor check no longer reports a misleading "run `palinode init`" message when palinode is running over SSH stdio — detectsSSH_CONNECTIONand returns an informational result explaining the remote context (#255).0.0.0.0binding warning is now suppressed whenPALINODE_API_BIND_INTENT=publicis set, allowing intentional network-exposed deployments (e.g., Tailscale) to start quietly. The systemd API service template sets this by default (#253).POST /savenow writeslast_updatedequal tocreated_aton initial file creation so freshly saved memories are not immediately flagged as stale by the freshness checker (#177).check_freshness()now computes per-section hashes (matching the indexer) instead of a whole-body hash; multi-section files no longer always report stale (#203).
- README.md and docs/QUICKSTART.md updated to promote Obsidian integration and
palinode doctoras headline features for the v0.8.x rollout. /saveis now the canonical mid-session checkpoint slash command;/psremains as a back-compat alias andpalinode initscaffolds both.
palinode doctoracross CLI, API, and MCP, including a constrained--fixmode for safe setup repairs.docs/DOCTOR.md, a full diagnostic guide covering the check catalog,--fix, and common failure cases.palinode init --obsidian,palinode obsidian-sync, and a broader Obsidian workflow with scaffolding, wiki footer support, and migration guidance.palinode_dedup_suggestandpalinode_orphan_repairacross CLI, API, and MCP.- Expanded MCP config diagnostics for Claude Code, Claude Desktop, Cline, Zed, and project-local
.mcp.json. - Version-controlled systemd unit templates and an installer under
deploy/systemd/. - Search improvements including score-gap dedup, daily-note penalty tuning, canonical-question anchoring, and raw cosine exposure.
- Session-end semantic dedup for recently indexed saves.
- Additional docs around MCP setup and Obsidian workflows.
/listnow sorts newest first instead of filesystem glob order.POST /savenow embeds inline before returning, and the watcher verifies vector-index presence before skipping unchanged content.- Default
db_pathnow followsPALINODE_DIRoverrides more reliably. palinode doctor --jsonnow emits only JSON on stdout./healthnow reports live chunk and entity counts.- Fresh-empty-database creation is refused when
memory_diralready contains memories. - Startup validation is stricter around
db_path,memory_dir, and reindex concurrency. - Several timestamp surfaces now use consistent UTC handling.
.gitignoreno longer hides legitimate code paths underpalinode/andexamples/.- Worktree test resolution is more reliable for editable installs.
- Root-level placeholder systemd unit files superseded by the version-controlled templates and installer under
deploy/systemd/.
Bug-fix release with small UX additions. Brings the public repo up to date with two production-impacting fixes (MCP array coercion, SessionEnd hook reasoning) plus search filters and structured session metadata. v0.8.0 remains reserved for a later major feature release.
/searchfilters and recency-only mode (#141) —types: [...]andsince_days: Nare now honored. Empty query routes to a recency-only path that orders bycreated_at descwith no semantic ranking. Backed by a newstore.list_recent()that pushes type filtering into SQL viajson_extract. Downstream consumers that wanted "recent Insights" or "recent Decisions from the last 14 days" can now do it in a single call.- Structured
session_endmetadata (#145) —palinode_session_endacceptsharness,cwd,model,trigger,session_id,duration_seconds. Fields land in the daily-note text and the indexed file's frontmatter. Ifprojectis omitted butcwdis provided, the project slug auto-derives from the cwd's basename via the same slug rulespalinode inituses. palinode_saveps=trueshortcut (#136) — MCP parity with the CLI--psflag.palinode_saveaccepts either an explicittypeorps=true(shorthand for ProjectSnapshot). Conflict betweenps=trueand a non-ProjectSnapshottypeerrors clearly rather than silently ignoring one.- Scope frontmatter parsing (#107) —
scope,visibility,accessfields recognized in memory frontmatter. Parser-only in this release; search-time use lands in a follow-up. - CLI banner —
palinode --versionandpalinode bannerprint the ASCII density-gradient mark. docs/VALIDATION-STRATEGY.md(#137) — formalizes a four-layer model (Tool fired / Data on disk / Retrievable / Behavioral) for validating any cross-session feature, with PR-checklist template. Cross-linked fromdocs/INSTALL-CLAUDE-CODE.md.
- MCP tolerates JSON-encoded array arguments (#147) — some MCP transports double-encode array args as JSON strings. Previous behavior rejected with Pydantic 422 ("expected array, received string") on
palinode_session_endandpalinode_save, and silently corrupted thepathsfilter onpalinode_diff(",".join(string)was joining char-by-char). New_coerce_str_arrayhelper decodes JSON-array strings while passing native lists through unchanged. - SessionEnd hook reason filter (#149) — the hook's README claimed a
clearmatcher that never existed insettings.json. Replaced with explicit script-side filtering via a newPALINODE_HOOK_REASONSenv var. Default allowlist:clear logout prompt_input_exit other. Override to narrow (e.g."clear") or extend (+resume,+bypass_permissions_disabled). - Empty-transcript captures (#151) — SessionEnd hook was capturing 0-message sessions despite
PALINODE_HOOK_MIN_MESSAGES=3. Root cause:grep -c '.'on empty input emitted"0"and exited 1; the|| echo "0"fallback then appended another"0", producing the literal three-byte string"0\n0"which made the integer test error and fall through. Fix: drop the|| echo "0"(grep always emits an integer regardless of exit), use|| trueto absorb the non-zero, default to0if the pipeline produces nothing. - Test isolation — full-suite test runs no longer hit 5 failures in
test_write_timedue to scope-chain reload state pollution.
- SSH keepalive options for remote-stdio MCP (
docs/MCP-SETUP.md,docs/INSTALL-CLAUDE-CODE.md) —ServerAliveInterval=30,ServerAliveCountMax=3, andTCPKeepAlive=yesadded to the example configs. This helps long-lived SSH-backed MCP sessions survive idle network drops and reconnect more cleanly after laptop sleep or WiFi changes.
- 325 tests passing at release time.
First tagged release. Persistent memory for AI agents with git-versioned markdown as source of truth, hybrid SQLite-vec + FTS5 search, and LLM-driven consolidation with reviewable git-backed updates.
Core storage and search
- SQLite-vec vector store with BGE-M3 embeddings (1024d) via any OpenAI-compatible endpoint (Ollama, vLLM, etc.)
- Hybrid search: vector similarity + BM25 (FTS5) fused via reciprocal rank fusion
- Hash-on-read freshness validation — detects out-of-band file edits without a full reindex
- File watcher daemon with debounced reindex and fault isolation
Consolidation and compaction
- Reviewable consolidation engine applying structured memory updates proposed by an LLM
- Weekly full-corpus consolidation with configurable LLM backend
- Nightly lightweight consolidation pass (
--nightlyflag) bounded toUPDATE/SUPERSEDEfor safer incremental updates - Model fallback chains — primary → fallback → fallback on timeout or HTTP error
- Prompt versioning system — extraction/compaction prompts stored as memory files with
active: truefrontmatter
Interfaces (all four expose the same capabilities)
- MCP server — Streamable HTTP transport (also supports stdio) with 18 tools. Stateless HTTP client, point it at any Palinode API server
- REST API — FastAPI on port 6340, 20+ endpoints covering search, save, diff, triggers, history, blame, rollback, consolidation, session-end, lint, migrate
- CLI — 26 commands wrapping the REST API via Click. TTY-aware (human output interactive, JSON when piped). Remote access via
PALINODE_APIenv var - Plugin — OpenClaw lifecycle hooks for agent frameworks with inject/extract patterns
New MCP tools in this release
palinode_lint— scan memory for orphaned files, stale active files, missing frontmatter, potential contradictionspalinode_blame/palinode_history/palinode_rollback— git-backed provenance toolspalinode_trigger— register prospective triggers that inject memory files when matching context is detectedpalinode_prompt— list, read, and activate versioned LLM prompt files
Capture and session management
/session-endendpoint — appends session summary to daily notes + one-liner to project status files- Session-end hook for Claude Code — auto-captures sessions on exit, idempotent, non-blocking
- Entity extraction from daily notes with keyword fallback for untagged content
Migration
palinode migrate— import existing markdown memory systems (OpenClaw format) with--reviewmode for dry-run inspection
Security and hardening
- Path validation on all file operations (rejects
.., symlinks outside memory directory) - Secret scrubbing on save path via configurable regex patterns
- Exclude-paths list prevents search results from surfacing files in
.secrets,credentials, etc.
Documentation
- Design and setup docs covering consolidation, provenance, and deployment
- Remote MCP setup guides for Claude Code, Claude Desktop, Cursor, Zed
- Example memory files (
examples/people/,examples/projects/,examples/decisions/,examples/insights/) - Compaction walkthrough (
examples/compaction-demo/) — a memory file across 3 passes with blame + diff output
Tests
- 92 tests covering parser, store, executor, API, CLI, migration, and hybrid search
- All inference is local by default. Cloud API keys (Gemini, OpenAI) are opt-in via environment variables
- REST API binds to
127.0.0.1by default; setPALINODE_API_HOST=0.0.0.0to expose on LAN - FastAPI uses lifespan protocol (deprecated
on_eventremoved) - Git commits stage only
*.mdfiles, never the SQLite journal/WAL/SHM
- Watcher no longer crashes the API server if the memory directory is temporarily unavailable
- CLI display keys match API response keys across all commands
- Migration tool correctly handles frontmatter with embedded colons
- Deprecated SSE MCP transport (replaced by Streamable HTTP per canonical MCP SDK pattern)
Initial public release. Minimum viable memory system.
- SQLite-vec vector store with BGE-M3 embeddings via Ollama
- File watcher daemon auto-indexing markdown on create/modify/delete
- FastAPI server with
/search,/save,/status,/reindexendpoints - Markdown parser with YAML frontmatter extraction and heading-level section chunking
- CLI with
searchandstatscommands - Plugin with core memory injection, topic-specific retrieval, and three tools (
palinode_search,palinode_save,palinode_status) - Session capture to daily notes on agent end
- Systemd user services for
palinode-api(port 6340) andpalinode-watcher
| Decision | Choice | Rationale |
|---|---|---|
| Source of truth | Markdown files, git-versioned | Human-readable, survives everything |
| Vector store | SQLite-vec (embedded) | No server, matches file-based philosophy |
| Embeddings | BGE-M3 via Ollama | Local, private, strong on structured text |
| Prompts | Read from specs/prompts/*.md |
Version-controlled, editable, diffable |
- No WAL mode on SQLite — concurrent writes can lock
- Session capture wrote raw text to daily notes (no LLM extraction)
- No consolidation scheduler
- No entity linking
- No MCP server (plugin-only integration)
All of these are addressed in 0.5.0.