# mneva progress log
# Format: [DATE] SESSION | FEATURE_ID | ACTION | RESULT
# Append only. Never edit historical entries.
# Entries before 2026-07-31 are backfilled from git history and docs/, not contemporaneous.

[2026-05-01] backfill | repo-init | Repo created (mneva-ai/mneva) | v0.0.1 placeholder
[2026-05-10] backfill | plan-1-foundation | M0-M4: CLI + local API, no LLM | v0.1.0 alpha released
[2026-05-17] backfill | v0.1.1 | gap fixes + dep hygiene + CI matrix | released
[2026-05-17] backfill | v0.1.2 | Obsidian vault read-write integration | released
[2026-05-18] backfill | v0.1.3 | `mneva distill` command | released
[2026-05-22] backfill | v0.2.0 | MCP server (6 tools) + uvx-first install + opt-in diagnose --share | released
[2026-05-24] backfill | v0.2.1 | `mneva upgrade` + fixed --version drift (version now sourced only from src/mneva/__init__.py) | released
[2026-05-24] backfill | v0.2.2 | Fixed README MCP config: `uvx mneva-mcp` -> `uvx --from mneva mneva-mcp` | released; LAST COMMIT before dormancy
[2026-05-25] backfill | user-research | Ran HN/Reddit research (00:50), merged package (13:36), non-technical integrated package (14:03) | 3 docs written to docs/, left UNCOMMITTED for 67 days
[2026-05-25] backfill | strategy-decision | DECIDED: non-technical users are the beachhead; engineer line demoted to future engine/power-user tier. Git/token/benchmark/CLI excluded from v0.3 | recorded in nontechnical-user-research-integrated-2026-05-25.json
[2026-05-25] backfill | outreach-batch-1 | Sent 5 cold Reddit DMs (ersinkiymaz / Any_Enthusiasm8346 / FounderArcs / Such_Grace / yanguoyu) | 0 REPLIES. Own success bar was >=2 replies + 3 pain quotes + 1 $5+ WTP. All missed. Copy sent was never recorded.
[2026-05-25..2026-07-31] backfill | (none) | 67 days dormant, zero commits | -

[2026-07-31] review | project-audit | Deep review via gstack /health + repo/external state check | Engineering 10.0/10 (mypy clean, ruff clean, 172 passed, CI all green, 0 TODO markers). Traction ~0: 1 star, 0 forks, 0 watchers, 0 issues ever. PyPI downloads UNKNOWN (pypistats 429 x3). Core finding: strategy decided 5/25 had zero implementation follow-through, while the shipped surface (14 CLI commands + MCP server) is 100% the audience that decision demoted.
[2026-07-31] review | strategy-recheck | User reconfirmed non-technical beachhead is still operative; confirmed outreach WAS sent and got 0 replies | Dissent recorded: 0/5 has no statistical power (cold-DM base rate 5-20%, expected replies <1). State is ZERO evidence, not negative evidence. Next step is to fix the validation instrument, not to build product.
[2026-07-31] review | p0b-repo-hygiene | Migrated google-generativeai (EOL) -> google-genai; rewrote provider to Client API and updated its tests; added a None-response test | 173 passed (+1), ruff clean, mypy clean, EOL FutureWarning gone
[2026-07-31] review | p0b-repo-hygiene | Created CLAUDE.md + .claude/feature_list.json + .claude/claude-progress.txt; committed the 3 research docs | Harness established; 118KB of strategy assets now under version control
[2026-07-31] review | traction-data | PyPI download stats retrieved on the 4th attempt (pypistats 429'd x3) | last_month=54, last_week=4, last_day=1. Corrects the earlier "traction ~0" reading to "very low but non-zero". Still pulling ~4/week 67 days after the last release. Cannot separate CI/mirror/bot traffic from real users, and 0 issues / 1 star / 0 forks argue against a reachable user base -- so this does NOT justify switching P0 to interviewing existing users. Re-evaluate if monthly downloads jump an order of magnitude.

[2026-07-31] review | competitive-analysis | Deep read of MemTensor/memmy-agent (GitHub API + memmy.bot/docs via gstack /browse) | DIRECT COMPETITOR, same positioning, far ahead. Created 2026-07-16 (15 days), 355 stars / 37 forks, pushed daily, TypeScript/MIT, backed by MemTensor (the MemOS memory research team). Has: desktop app + CLI/TUI + OpenAI-compatible API + desktop pet; automatic history import from Cursor/Claude Code/Codex/OpenCode/OpenClaw/Hermes with a "First Meeting Report"; a four-layer memory engine (L1 Trace / L2 Policy / L3 World Model / Skill) with episode merging, reward backpropagation, reflection scoring, MMR dedup, Dream consolidation; messaging channels (TG/Discord/WeChat/Feishu/DingTalk); managed Chromium. Signed Mac+Win builds, CN and international variants, Product Hunt, Discord, free signup credits. mneva's feature surface is a STRICT SUBSET except Obsidian. Caveat: this is a read of their repo and docs, NOT a benchmark -- engine quality unverified, star-growth composition unknown.
[2026-07-31] review | strategy-reversal | REVERSED the 2026-05-25 non-technical beachhead decision. New direction: Markdown/privacy/Obsidian engineer line | REASONING (do not re-litigate without reading this): memmy already shipped a better version of the 5/25 plan's P1 (make it installable by non-technical users) -- desktop app, free credits, one-click install, auto history scan. Meanwhile mneva's only five remaining differentiators (Markdown is source of truth; zero account/cloud; Obsidian two-way sync; scoped to the memory layer only; usable as a Python library) matter ONLY to engineers, Obsidian users, and privacy-sensitive developers -- exactly the group 5/25 demoted. New positioning: "Your AI memory is your own Markdown files. Follows the repo, goes into git, opens in Obsidian, reviewed in a PR. No account, no cloud." memmy structurally cannot copy this: its four-layer engine requires a structured DB, and retreating to plain Markdown would disarm it. IMPORTANT: this reversal is NOT "non-technical demand was falsified" -- the 0/5 outreach had no statistical power and that question still has zero evidence. It is a competitive-position call. v0.3 main line reverts to the engineer roadmap in docs/user-research-hn-reddit-2026-05-25.md sections 7-8.
[2026-07-31] review | index-blocker | Code review found a hard blocker for every v0.3 feature | Indexer._init_schema() (indexer.py:51) uses CREATE TABLE IF NOT EXISTS, so adding a column is a SILENT NO-OP on existing databases. Codebase-wide grep found no rebuild / reindex / ALTER TABLE / migration entry point. Also: search() filters in SQL before BM25 ranking (indexer.py:89-100), so new filterable fields must be real columns -- post-filtering would break top_k. Consequence: schema versioning + rebuild() + `mneva reindex` is a PREREQUISITE, not a follow-up. It also closes a real gap, since CLAUDE.md architecture rule 1 ("SQLite is rebuildable from Markdown") is currently not true in code.
[2026-07-31] review | p0-reposition | Rewrote CLAUDE.md ## Current Strategy, updated ## Common Mistakes (added the two Indexer traps), rewrote .claude/feature_list.json around the engineer roadmap, cancelled the non-technical entries | Harness now matches the 7/31 direction
[2026-07-31] review | p1-0-index-migration-path | Added PRAGMA user_version schema versioning (_SCHEMA_VERSION=1) + Indexer.rebuild() from store.iter_records() + `mneva reindex` command | 177 passed (+4), ruff clean, mypy clean. Regression guard EMPIRICALLY VERIFIED: stashed the new indexer.py and reran, old code asserted 1 == 2 (legacy DB silently kept its shape). Side effect worth knowing: opening a stale-version DB now DROPs and rebuilds, so index rows with no backing Markdown file are discarded. That is correct per architecture rule 1, and no real install can produce such a row (cli.py capture writes Markdown first, indexes second) -- but it broke tests/integration/test_concurrent_capture.py::test_wal_upgrade_from_legacy_journal_mode, whose fixture fabricated an index-only row. Fixed the fixture to also write the Markdown (what a real v0.1.x install would have) and strengthened the assertion to check the record is findable, not just counted. The IRON regression intent is preserved, not weakened.
[2026-07-31] review | p1-0-hardening | Advisor flagged a rebuild concurrency race; investigated and found the theory only partly held | Added BEGIN IMMEDIATE + rollback + double-checked user_version to rebuild(). EMPIRICAL FINDING: the theorized interleave (B's DROP landing mid-way through A's inserts) does NOT reproduce -- a 2-thread test passed 10/10 even with the lock removed, because SQLite's single-writer lock + busy_timeout already serializes both full rebuilds. What the transaction DOES buy is atomicity of the version stamp w.r.t. the data: verified discriminating (fails `assert 1 == 5` without it). Docstrings and the test docstring corrected to claim only what is proven. SEPARATE REAL BUG found by that same test: PRAGMA journal_mode=WAL ran BEFORE busy_timeout was set, and SQLite returns SQLITE_BUSY for journal-mode conversion WITHOUT invoking the busy handler -- so two mneva-mcp processes opening a fresh/pre-WAL DB simultaneously could get "database is locked" at startup. Fixed: busy_timeout first, WAL switch best-effort. The pre-existing concurrency test hid this by pre-opening the DB before racing. Also added `reindex` to the help-listing test and both READMEs. 179 passed, ruff clean, mypy clean.
[2026-07-31] review | p1-05-schema-decision | User decided the git provenance field set | KEEP repo_path / branch / commit_sha. DROP dirty and changed_files: volatile (stale the instant they are written) and changed_files can bloat frontmatter, which undercuts the Obsidian-readability selling point the new positioning is built on. commit_sha already answers "which code state did this memory correspond to". ALSO NOTED: p1-05's original "batch all three field groups into one migration" rationale is now weak -- p1-0 made migrations cheap (bump _SCHEMA_VERSION, rebuild does the rest) and all bumps land inside unreleased v0.3, so users see one rescan regardless. Prefer adding each field group alongside the feature that uses it instead of landing unused columns early.
[2026-07-31] review | ci-first-run | Pushed branch, opened PR #16, CI ran the real matrix for the first time since 2026-05-25 | RESULT: all three Python 3.11 cells PASS, all three 3.12 cells FAIL. Failure was NOT in mneva code: mypy aborted on numpy's own stub -- "numpy/__init__.pyi:737: Type statement is only supported in Python 3.12 and greater". Cause: mypy.ini pins python_version = 3.11, numpy arrives transitively via rank-bm25 and ships py.typed stubs that mypy follows, and recent numpy releases write PEP 695 `type X = ...` in those stubs. Only the 3.12 cells resolve a stub new enough to trigger it, because latest numpy dropped Python 3.11 -- hence the clean version split. PRE-EXISTING drift, not caused by this branch (rank_bm25 import exists on main at indexer.py:8; mypy.ini untouched by these commits). Fixed with [mypy-numpy] / [mypy-numpy.*] follow_imports = skip + follow_imports_for_stubs = True (the latter is REQUIRED -- follow_imports alone does not apply to .pyi files, which is where the syntax lives). Verified with a real negative control: injected a PEP 695 type statement into the local numpy stub, reproduced CI's error verbatim without the config sections, confirmed Success with them, restored the stub.
[2026-07-31] review | ci-green | CI re-run after the mypy/numpy fix | ALL 6 CELLS PASS (ubuntu/macos/windows x Python 3.11/3.12). This is the first green matrix run covering the google-genai dependency swap and the sqlite schema-versioning/transaction work; both had previously only been verified locally on a single Python 3.14 interpreter. PR #16 ready for review. Note the matrix does NOT cover 3.13/3.14 even though pyproject classifiers advertise them -- worth closing that gap at some point, but it is pre-existing and out of scope for this branch.
[2026-07-31] review | p1-1-git-aware-metadata | Implemented git provenance: new src/mneva/gitctx.py + repo/repo_path/branch/commit_sha on Record, wired through CLI, MCP, HTTP, replay, distill, vault | 206 passed (+27), ruff + mypy clean. KEY DESIGN CALLS: (1) `repo` = normalized remote URL, not the absolute path -- survives cloning to another machine; falls back to local:<dirname> with no remote. (2) Filter is `repo = ? OR repo IS NULL`, never bare equality -- a strict filter would make every pre-v0.3 record vanish from default search on upgrade. (3) Do NOT filter on branch; most memories are project-level and branch-strict filtering would hide nearly everything the moment you branch. (4) MCP tools take repo_path because the server process inherits the AI client's cwd, not the user's project.
[2026-07-31] review | p1-1-vault-dataloss | Planning assumed vault.py needed no changes; reading it proved that WRONG and it was a data-loss path | write_to_vault and sync_from_vault each had their own hardcoded frontmatter field list, and sync_from_vault calls write_record(overwrite=True) -- so capture-with-provenance -> sync-vault would have ERASED the git fields from the canonical record, straight through the Obsidian path the new positioning leads with. Fixed by giving store.py a single record_frontmatter/record_from_frontmatter pair that both modules use. Lesson: three copies of a field list is not a style issue, it is a silent-data-loss generator.
[2026-07-31] review | p1-1-guard-verification | All three critical guards verified to FAIL against the bug they guard | (a) reverting to `repo = ?` fails the pre-v0.3 visibility test; (b) dropping repo from rebuild()'s INSERT fails the reindex-isolation test (counts alone would have stayed green); (c) restoring vault's own field list fails the round-trip test. Restored and all 9 pass.
[2026-08-01] review | ci-hang-diagnosis | PR #17 first CI run: ubuntu+macos green, BOTH Windows cells hung 3.5h and had to be cancelled | ROOT CAUSE (mine, from p1-1): mcp_server._git_for fell back to Path.cwd() when the client passed no repo_path, so every MCP tool call shelled out to git -- inside a process that speaks MCP over STDIO. subprocess.run(capture_output=True) redirects stdout/stderr but LEAVES STDIN INHERITED, so the git child got the live protocol pipe. Localised by counting the 60 dots in the cancelled log against cumulative per-file collection counts: test #61 is the first test of test_mcp_protocol.py, the only test that spawns the stdio server. Two fixes: (1) gitctx._run now passes stdin=subprocess.DEVNULL unconditionally -- also prevents git blocking on credential/editor prompts; (2) _git_for returns None unless repo_path is explicit -- the cwd fallback was not just dangerous but meaningless, since the server's cwd is the AI client's directory, not the user's project. Tripwire test added and verified discriminating.
[2026-08-01] review | ci-command-gap | Discovered I had NEVER run the command CI runs | CI runs `pytest --cov=mneva --cov-report=term-missing --cov-fail-under=80`; I had been running bare `pytest` all session. Under --cov, test_reindex_rebuilds_from_markdown_after_index_loss FAILED on Windows with PermissionError WinError 32. Root cause: Indexer never closed its sqlite connection, and my earlier gc.collect() workaround depended on refcounting -- coverage's tracer keeps frames (and their connections) alive. That test shipped in PR #16 and passed there only by luck of GC timing, i.e. it was already flaky on main. Real fix: Indexer.close() + __enter__/__exit__, and cli.py now uses `with Indexer(...)` at all 6 sites. LESSON: run the CI command locally, not an approximation of it.
[2026-08-01] review | ci-hardening | Added ci.yml timeout-minutes: 15 and pytest.ini faulthandler_timeout = 120 | A hung job previously burned toward the 6h default AND never published logs, so the diagnostic was destroyed by the failure itself. Now it fails in 15 min and dumps thread stacks at 120s.
[2026-10-15] review | strategy-amendment | Market re-research (platform memory, standalone tools, demand) + REA teardown of memmy 1.2.2; founder named the segment: heavy AI users who live in Claude Code/Codex and want one background carried into other tools incl. AI video | AMENDED the 7/31 line, not reversed. Findings: Claude (3/2), Gemini (3/26), Doubao Work (10/14) import competitor memory as one-shot snapshots; Mem0 OpenMemory ext archived 3/23, Supermemory Nova shut 9/9, Limitless to Meta; memmy 355->2101 stars but 3,284 GitHub desktop downloads over 22 releases; memmy stores memory in a ~40-table SQLite the user cannot read, free credits are a Bailian model gateway. Consumer cross-tool pain is near-absent among non-devs, present among power users. Video tools reach Claude Code via their own MCPs (Runway, Higgsfield, HeyGen, Kling official), so mneva integrates none. Baselines to beat: a symlinked CLAUDE.md/AGENTS.md file, and memmy. Files: docs/market-research-2026-10-15/.
[2026-10-16] review | validation-method | Founder rejected external-user validation: the founder IS the target user | Step 0 is now p0-dogfood-profile: 14-day re-explain log, one end-to-end video task with zero restated background, kill condition = symlinked-file baseline is just as good. Accepted cost: n=1. Harness updated: CLAUDE.md strategy amendment, feature_list reordered (p1-3 to front, new history-import / profile-kind / brief / emit entries, p1-2 to P2, p1-4 folded into brief).
[2026-10-16] review | all-memory-any-tool | Founder widened goal to 'all memory, switchable to any mainstream AI tool' and listed daily tools: Claude web+app, ChatGPT web (text+image), Doubao web, Gemini app (text+image), Claude Code, Runway, ChatCut, Seedance | Three delivery tiers: local MCP (Claude Code, Claude desktop; video via Runway MCP inside Claude Code), remote MCP connector needing public HTTPS (Claude web, ChatGPT), paste-only brief (Doubao, Gemini, ChatCut). ASSUMED without explicit answer: user-run tunnel + token, no hosted relay (copy changes to 'no mneva cloud, no account'); two-way via official export import, ChatGPT first. Added p1-remote-connector; extended p1-history-import and p1-brief. Plan phases A-D in docs/market-research-2026-10-15/next-step-plan.md.
[2026-10-16] review | phase-a | Built 'mneva brief' (src/mneva/brief.py, +12 tests) and fixed capture stdin on Windows (+3 tests); registered mneva MCP in Claude Code user config; drafted profile v0.1 from Claude Code history (scratchpad, pending review) | 222 passed, ruff + mypy (win/linux/darwin) clean, cov 89.8%. TWO LESSONS: (1) clip.exe guesses encodings: UTF-16 with BOM keeps U+FEFF as a char, without BOM a lone CJK char is misread as ANSI; my first unit test asserted the BOM and so encoded the bug. Only a real clipboard round trip caught it; now Win32 CF_UNICODETEXT via ctypes. (2) capture '-' decoded stdin in the console code page and crashed on Chinese; CliRunner wraps stdin as UTF-8 so it can never reproduce this; guard is a real subprocess pipe test, verified failing before the fix. Deviation: --target dropped for --max-chars (vendor limits unverified). Gap: brief order is by random id.
[2026-10-16] review | pr-18-ci | PR #18 first CI run failed on all 6 cells: mypy 'object not callable' at cli.py synthesize prompt | Not caused by the PR: CI installed click 8.5.0, which deprecates get_text_stream and exports it through a wrapper mypy types as object; local click 8.3.3 passed. Same class as the 7/31 numpy drift. Reproduced the exact error in a fresh venv, replaced with sys.stdin.readline(), all 6 cells green on 3d7f6c4. Added CLAUDE.md common mistake #9 (fresh-venv check before push; gh pr checks --watch exits 0 before checks register). Founder profile stored as 6 profile records (scope profile, --no-git); brief at 1500 chars keeps 5 of 6 sections. Re-explain log started at ~/.mneva/reexplain-log.md, 10/16 to 10/29.
[2026-10-16] build | p1-setup | Founder chose path A ("make it easy for everyone" scoped to agent-CLI users). Built 'mneva setup' (src/mneva/setup.py, +10 tests): registers the MCP server with Claude Code (user scope) and Codex via their own 'mcp add', MNEVA_MCP_CLIENT set per client, idempotent by 'mcp get' exit code, writes ~/.mneva/profile-template.md, prints next steps. Bumped to 0.3.0 | Real run on this box: Claude Code already registered (left alone), Codex added (codex mcp get mneva confirms), second run changed nothing. 232 passed locally. Packaging: hatchling now emits metadata 2.5; twine 6.2.0 rejects it, twine 7.0.0 passes check, so upload with twine 7. PyPI upload left to the founder (no publish workflow, no stored credentials). Item 3 of path A (Claude Code drafts the profile from history) deferred to p1-history-import. Follow-ups: FastMCP instructions= so agents read the profile unasked; trusted-publishing release.yml; README v1+ roadmap line still promises a GUI installer and hosted SaaS, which contradicts the 10/15 strategy.
[2026-10-16] ship | v0.3.0 | Uploaded mneva 0.3.0 to PyPI with a founder-supplied project-scoped token (twine 7.0.0, env vars only, not stored); verified by a fresh uvx install (version 0.3.0, setup and brief present). Published GitHub release v0.3.0. Added .github/workflows/release.yml: tag vX.Y.Z -> version check, build, twine>=7 check, PyPI trusted publishing (environment pypi); workflow_dispatch builds only | Founder chose to keep the token. Trusted publisher on PyPI needs the founder to log in first; Shipping section of CLAUDE.md updated.
[2026-10-16] ship | release-automation | PyPI trusted publisher added in the founder's logged-in browser (mneva-ai/mneva, release.yml, environment pypi; PyPI confirmed "Added release.yml"); GitHub environment pypi created. First real tag-triggered publish not yet exercised.
[2026-10-16] build | mcp-instructions | Founder's next two weeks: Claude Code coding + ChatGPT/Doubao/Gemini web chat. Added FastMCP instructions telling agents to call replay_context(scope="profile") once per conversation; setup next steps updated; +1 protocol test (233 passed) | Live check inconclusive: headless 'claude -p' starts before user MCP servers connect (mneva 'pending' at init, no tools), and with --strict-mcp-config mneva connected but the API key in env returned 'Credit balance is too low'. Needs an interactive-session check by the founder. Web tools: advised pasting brief once into each tool's persistent about-me setting (ChatGPT custom instructions 1500 chars) instead of per chat; remote connector waits on the re-explain log.
[2026-10-16] verify | mcp-instructions | Live A/B in headless Claude Code (claude.ai login, ANTHROPIC_API_KEY unset; message sent after 8-20 s so MCP servers are connected), 2 runs per cell | Background questions: 0.3.0 without instructions used the profile 0/4 (one run found it via search_memory by chance, no profile fact in the answer); with instructions 4/4 (mneva only) and 4/4 (founder's full MCP config), each calling replay_context(scope="profile") unprompted and citing the FDA priority that exists only in the profile. Pure coding prompt: no call in any setup, accepted as desirable. Codex untested: 'codex exec' returned 401 (CLI not logged in). Small sample. Released 0.3.1 as the first tag-triggered trusted-publishing run. Harness: scratchpad/profile_trial.py.
[2026-10-16] build | p1-brief | Founder worried that pasting the brief into web tools is too manual. Researched automation: Claude web+app and ChatGPT (developer mode, possibly read-only on Plus, beta) can be automated by the remote connector; Gemini and Doubao have no custom connector, so only a browser extension or paste. Chosen first step: stale reminders. 'mneva brief --for <tool>' records fingerprint+budget in ~/.mneva/brief-copies.json and reuses the budget; stale tools named in MCP capture_memory(scope=profile) summary, 'mneva status', and every brief run; staleness compares re-rendered text so a record that never fit a tool's budget does not count | +6 tests, 239 passed. Real-profile run on a copy of the store: chatgpt@1500 and doubao@4000 recorded, capture flagged both, re-copy reused 1500, clipboard round trip 1286 chars with the new record, no BOM. Noted: at 1500 chars the '我在做的事' record (a84bf36064288bcc) never reaches ChatGPT; still the ordering gap in p1-profile-kind.
[2026-10-16] build | p1-brief | Founder rejected 0.3.2's '--for <tool>' as too much work ("当mneva安装使用时需要自动运行，不需要让用户多加一个工具名"). 0.3.3: every 'brief --copy' is tracked by itself, keyed by scope+budget; reminder prints the exact copy commands; --for removed | 241 passed; real-profile copy run: default + 1500 copies tracked, MCP profile capture flagged both, recopy cleared. Saved feedback memory zero-config-ux. Founder approved the direction: browser extension as the main line for web tools (replaces paste), remote connector later (only for the Claude phone app). Next: strategy amendment + extension spec.
