Phase 1 of the registry consolidation rolling out across the homelab —
infra/docs/plans/2026-05-07-forgejo-registry-consolidation-plan.md.
* New .woodpecker/build.yml runs the test suite, then dual-pushes to
viktorbarzin/claude-memory-mcp on DockerHub AND
forgejo.viktorbarzin.me/viktor/claude-memory-mcp.
* GHA ci.yml renamed to .disabled — its build job would otherwise
race the Woodpecker build and clobber Forgejo with a stale image.
Re-enable only on rollback.
* DockerHub remains the canonical pull source until Phase 3 flips
infra/stacks/claude-memory/main.tf image= to Forgejo. Phase 3 also
archives this GitHub repo and CLAUDE.md is updated to point
`claude plugins install` at the Forgejo URL.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
SSE is deprecated per MCP spec. Both clients (wizard, emo) already use
type: "http" pointing to /mcp/mcp. Removes HandleSSE class,
SseServerTransport, and /mcp/sse + /mcp/messages/ routes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When content exceeds 500 chars, it's automatically split into multiple
memories on paragraph boundaries. Each chunk gets the same category,
tags (with part-N-of-M suffix), keywords, and importance. Removes the
old 800 char hard limit from the Pydantic model.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The REST API /api/memories/recall already broadens to OR-match when the
strict AND full-text search returns too few results, but the MCP tool
handler was missing this fallback. Broad queries (many terms) produced
empty results because plainto_tsquery ANDs all terms and no single
memory contains every word.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
With 375 memories and 1M context window, low limits just hide results.
Agents can still pass a smaller limit when they want fewer results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All memories are now visible to all users in recall/list/count queries.
Each memory still has an owner (user_id) who retains exclusive delete
rights. This removes the need for explicit sharing — wizard and emo
automatically see each other's memories.
Changes:
- recall/list: single query without user_id filter, added owner field
- count: counts all memories globally
- REST categories/tags: show all users' data
- Delete/update: unchanged (owner-only or write-share)
- Sync: unchanged (stays user-scoped)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous per-request transport implementation couldn't maintain
session state. Using the SDK's built-in SessionManager with stateless=True
means sessions start pre-initialized — tool calls work immediately
without the init handshake, avoiding the reconnection race condition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SSE transport has reliability issues through Cloudflare/Traefik proxies
(connections drop, causing init failures on reconnect). Streamable HTTP
is stateless — each request carries its own session, avoiding persistent
connection issues.
New endpoint: POST/GET/DELETE /mcp/mcp (streamable-http)
Existing: GET /mcp/sse + POST /mcp/messages/ (SSE, unchanged)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BaseHTTPMiddleware buffers response bodies, which prevents SSE events
from streaming to the client in real time. This caused MCP initialization
to never complete, resulting in -32602 errors on all tool calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expand/collapse button in bottom-right corner lets the graph fill the
entire viewport for better exploration. Escape key exits fullscreen.
Category filters overlay in top-left when fullscreen.
Add GET /api/tags endpoint returning distinct tags with counts,
add ?tag= filter to GET /api/memories, and a new Tags tab in the
Neural Archive UI with searchable tag cloud and expandable memory cards.
- Replace $nextTick with setTimeout(100) for D3 render (same fix as dashboard)
- Re-render graph when switching back to tab (not just on first load)
- Fallback width from parent element when container clientWidth is 0
- Use contextvars to resolve user identity from Authorization header
in SSE connections, replacing hardcoded "default" user_id
- memory_recall now includes shared memories (individual + tag-based)
with deduplication and shared_by attribution
- memory_list now includes shared memories with same approach
- All 11 MCP tool functions use _current_user contextvar
- Add "New Memory" button with collapsible form (content, category, tags, importance)
- Add share memory with user typeahead via new GET /api/users endpoint
- Fix dashboard charts not rendering (replace $nextTick with setTimeout for x-if timing)
- Redesign theme: warm walnut/amber palette, Playfair Display + JetBrains Mono fonts
- Update Chart.js and D3 graph colors to match warm theme
Alpine.js + Tailwind CSS (CDN) served by FastAPI StaticFiles — no build step,
zero Docker changes. Features: memory browser with inline edit/delete, full-text
search with debounce, D3.js force-directed graph, Chart.js stats dashboard.
New endpoints: GET /api/stats, GET /api/categories, pagination on GET /api/memories.
- Document 7 new MCP tools for memory sharing
- Add Memory Sharing section with usage examples and permission levels
- Update API Reference with 7 new sharing endpoints + PUT update
- Update migrations list with 004_add_sharing
- Add permissions.py to project structure
- 7 new tools: memory_share, memory_unshare, memory_share_tag,
memory_unshare_tag, memory_update, memory_shared_with_me, memory_my_shares
- Tools only advertised when API key is configured (HTTP_ONLY or HYBRID mode)
- SQLite-only mode stays unchanged — sharing requires the API server
- Tool handlers proxy to REST API endpoints
- New migration 004: memory_shares and tag_shares tables with indexes
- Share individual memories or entire tags with other users (read/write)
- Tag shares are live rules: future memories with shared tags auto-visible
- Recall query merges own + shared memories via UNION, returns shared_by field
- Owner-only delete enforcement (403 for non-owners, even with write access)
- PUT /api/memories/{id} update endpoint with permission checks
- 5 new MCP SSE tools: memory_share, memory_unshare, memory_share_tag,
memory_unshare_tag, memory_update
- Permission helper checks ownership, individual shares, and tag shares
Adds SSE endpoint at /mcp/sse so Claude Code can connect over HTTPS
without needing a local Python bridge script. Benefits:
- No local files or sandbox permission issues
- Works from any machine (OpenClaw, DevVM)
- No startup delay or stderr suppression hack
- Auth via Bearer token in request headers
- Decouple push and pull in _sync_once() so pull always runs even if push fails
- Add startup full resync to catch drift from other agents and schema changes
- Add periodic full resync every ~10 minutes for continuous drift correction
- Add auth failure detection (401/403) with graceful SQLite-only degradation
- Add /api/auth-check endpoint for lightweight key validation
- Add retry cap (5 attempts) on pending ops to prevent infinite queue buildup
- Add orphan reconciliation: push local-only records with content dedup
- Add memory_count MCP tool for sync diagnostics
- Add version-based SQLite schema migration (PRAGMA user_version)
- Fix API key in ~/.claude.json to match server
- Update README with sync resilience docs, test structure, project layout
- Add 30 new tests covering all new behaviors (155 total, all passing)
Add detailed Search Algorithm section covering FTS5/BM25 (SQLite) and
tsvector/ts_rank (PostgreSQL) backends, query construction, semantic
expansion at store time, and a comparison table. Also add Sensitive
Memory & Secrets section, Auto-Learn details, and Background Sync Engine
documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add type annotations to all FastAPI endpoints in api/app.py
- Fix bare list/dict generics in sync.py and app.py
- Fix no-any-return in vault_client.py and sync.py
- Remove mypy || true from GitHub Actions CI — mypy is now clean
- Add type annotations to all _sqlite_* private methods
- Type sqlite_conn as sqlite3.Connection | None with assert narrowing
- Fix _api_request return type (dict[str, Any])
- Add return type to _init_sqlite
- Fix tool_name type in handle_tools_call
- Import sqlite3 at module level
- GHA ci.yml: add build + deploy jobs (push to DockerHub, trigger Woodpecker)
- Drop test matrix to single Python 3.12, preserve mypy step
- Build/deploy gated on push to main (PRs still run tests only)
- Woodpecker: deploy.yml (manual event, kubectl set image + slack notify)
- Old pipeline preserved as build-fallback.yml (manual trigger)