diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 26642dd..45dec33 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ { "name": "mnemo", "description": "7 memory skills for Claude Code and Codex via Obsidian — vault audit, knowledge search, bounded cross-runtime recall, connection discovery, session handoff, memory routing, review orchestration, and setup", - "version": "1.2.6", + "version": "1.2.7", "source": "./plugins/mnemo", "category": "productivity", "keywords": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 312301b..3e7502e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +## [1.2.7] - 2026-07-21 + +### Added + +- **First-class "how you think" capture (`principle` / `pain` / `stance`)** — `save` gains a semantic sub-type for the user's business logic, pains, and decision stance, piggybacking on the `insight` role (no `taxonomy_roles` schema change, so it works on every existing vault without re-`setup`). These are human-authored/confirmed atomic claims — never an agent-generated dossier — recorded with a searchable `kind:` field so `[kind:pain]` is enumerable in `/mn:ask`. +- **Typed body slots** — each note's body now follows a template for its semantic type: `decision` → one Y-statement (context / choice / rejected / goal / trade-off / because); `gotcha` / business rule → GIVEN / WHEN / THEN + Because + Fails-when; `principle` / `pain` / `stance` → JTBD (Job / Pain / Done-well / Anti-goal); `fact` / `insight` → claim-title + BLUF first line + evidence. "Poured-in" polish achieved by structure, not verbosity. +- **Optional `aliases:` retrieval keys** — EN/RU synonyms or a short name so a cross-language or short query still reaches an atom (a polyglot-vault search key, not authored content). + +### Changed + +- **`save` splits deep material into atoms, never one blob** — a new Step 0b atomicity gate requires a claim-shaped title plus a `because` rationale per typed note (hard-gated for `decision` / actionable rule / `principle`·`pain`·`stance`; a soft nudge for a plain `fact`). Material carrying ≥2 separable claims becomes ≥2 notes plus an optional synthesis, never one exhaustive note. This keeps point-precise retrieval intact and stays inside the human-authored, non-destructive principle — no auto-ingest. The `save` description and `agents/openai.yaml` presentation are updated with the new triggers; `evals/trigger-eval.json` adds W4 positives and a near-miss. + ## [1.2.6] - 2026-07-20 ### Changed @@ -899,7 +911,8 @@ Frontmatter now includes `session_id: {CLAUDE_SESSION_ID}` — disambiguates sam - `config.example.json` - MIT License -[Unreleased]: https://github.com/jojoprison/mnemo/compare/v1.2.6...HEAD +[Unreleased]: https://github.com/jojoprison/mnemo/compare/v1.2.7...HEAD +[1.2.7]: https://github.com/jojoprison/mnemo/compare/v1.2.6...v1.2.7 [1.2.6]: https://github.com/jojoprison/mnemo/compare/v1.2.5...v1.2.6 [1.2.5]: https://github.com/jojoprison/mnemo/compare/v1.2.4...v1.2.5 [1.2.4]: https://github.com/jojoprison/mnemo/compare/v1.2.3...v1.2.4 diff --git a/docs/save.md b/docs/save.md index 6bfd54c..310586d 100644 --- a/docs/save.md +++ b/docs/save.md @@ -10,6 +10,8 @@ The brain of mnemo. When you say "remember this" or "save to memory", this skill /mn:save "we decided to use SCOPE over TextGrad for self-correction" /mn:save "gotcha: execSync with shell=true is banned in antomate" /mn:save "HisPO algorithm stabilizes RL training for MoE models" +/mn:save "принцип: сначала ambitious-вариант с честными trade-offs" +/mn:save "это моя боль: вставляю стену промпта каждую сессию" ``` Or just say naturally: @@ -68,10 +70,21 @@ In `~/.mnemo/config.json`: Don't have claude-mem? Set `"enabled": false` — everything else works. +## Depth via structure, not volume + +`save` never writes one exhaustive blob — depth is distributed across many atomic, claim-titled notes so a future agent can grab the exact slice. Each note's body follows a **typed slot** for its semantic type (Step 0b): + +- **decision** → a single Y-statement (context / choice / rejected / goal / trade-off / because) +- **gotcha / business rule** → GIVEN / WHEN / THEN + Because + Fails-when +- **principle / pain / stance** → JTBD (Job / Pain / Done-well / Anti-goal) — captures your business logic, pains, and how you think/decide as human-authored/confirmed atoms (recorded with a searchable `kind:` field), never an agent-invented dossier +- **fact / insight** → claim-title + BLUF first line + evidence + +Material with ≥2 separable claims becomes ≥2 notes (plus an optional synthesis), never a single note. Every typed note carries a claim-shaped title + a `because` rationale (hard-gated for decision / rule / pain). + ## Important Notes - **Fails visibly, never invents a shadow store** — unavailable durable backends are reported so the user can retry -- **Classifies automatically** — you don't choose a physical type; the exact five-key `taxonomy_roles` map resolves it +- **Classifies automatically** — you don't choose a physical type; the exact five-key `taxonomy_roles` map resolves it, and a narrower `kind:` sub-type shapes the body - **Duplicate check** — always searches Obsidian before creating - **Bundled adapters in both runtimes** — dynamic reads go through `safe-read.py`; every vault Markdown write goes through the JSON-stdin optimistic `vault-write.py` diff --git a/evals/trigger-eval.json b/evals/trigger-eval.json index 0bdb49c..af5235b 100644 --- a/evals/trigger-eval.json +++ b/evals/trigger-eval.json @@ -1,5 +1,5 @@ { - "about": "Trigger-eval set for mnemo skill descriptions. 6 proactive-positive prompts (a specific skill SHOULD auto-invoke) + 6 near-miss negatives (share keywords but must NOT trigger). Re-run after editing any canonical description to catch trigger regressions. Baseline 2026-07-05 (v1.1.x): 12/12 at the simulated-routing level. Format is compatible with skill-creator's run_loop.py (query + should_trigger); expect_skill/why are extra context.", + "about": "Trigger-eval set for mnemo skill descriptions. 8 proactive-positive prompts (a specific skill SHOULD auto-invoke) + 7 near-miss negatives (share keywords but must NOT trigger). Re-run after editing any canonical description to catch trigger regressions. Baseline 2026-07-05 (v1.1.x): 12/12 at the simulated-routing level. 2026-07-21 (v1.2.7): +P7/P8 for the save W4 principle·pain·stance layer, +N7 near-miss (a code-comment 'принцип', not a personal-memory save). Format is compatible with skill-creator's run_loop.py (query + should_trigger); expect_skill/why are extra context.", "evals": [ { "id": "P1", "query": "Сейчас буду чинить баг в auth — токен опять отваливается на рефреше, вроде уже сталкивались с похожим", "should_trigger": true, "expect_skill": "ask", "why": "recurring/previously-seen bug → recall before re-fixing" }, { "id": "P2", "query": "Разобрался почему CI падал: race condition в setup-фикстуре, добавил await перед teardown. Наконец-то", "should_trigger": true, "expect_skill": "save", "why": "solved a tricky bug → proactive save" }, @@ -7,11 +7,14 @@ { "id": "P4", "query": "Окей, решили брать Redis вместо in-memory очереди — нужна персистентность между рестартами", "should_trigger": true, "expect_skill": "save", "why": "non-obvious decision → save (post-decision, not council)" }, { "id": "P5", "query": "что мы решили про стратегию миграций схемы?", "should_trigger": true, "expect_skill": "ask", "why": "explicit recall query" }, { "id": "P6", "query": "найди связи для только что созданной заметки про OAuth", "should_trigger": true, "expect_skill": "connect", "why": "explicit connect + new note" }, + { "id": "P7", "query": "запомни как я обычно принимаю решения — сначала ambitious-вариант с честными trade-offs, потом safe", "should_trigger": true, "expect_skill": "save", "why": "the user's decision stance / mental model → W4 principle·stance save" }, + { "id": "P8", "query": "вот моя боль: каждую сессию вставляю огромную портянку промпта чтобы получить нормальный save", "should_trigger": true, "expect_skill": "save", "why": "a user pain / business-logic → W4 pain save" }, { "id": "N1", "query": "найди функцию parse_order в кодовой базе и покажи где она вызывается", "should_trigger": false, "why": "grep of CODE, not vault recall — near-miss ask" }, { "id": "N2", "query": "поищи в интернете как работает OAuth PKCE flow, дай ссылки", "should_trigger": false, "why": "web search, not vault — 'поищи' overlaps, meaning differs" }, { "id": "N3", "query": "сохрани этот файл и закоммить", "should_trigger": false, "why": "'сохрани' is about a FILE + VCS, not memory — near-miss save" }, { "id": "N4", "query": "свяжись с бэкенд-командой насчёт API-контракта", "should_trigger": false, "why": "'свяжись' = communicate, not 'link notes' — near-miss connect" }, { "id": "N5", "query": "проверь здоровье сервиса — дёрни /health эндпоинт прода", "should_trigger": false, "why": "service health, not vault health — near-miss health" }, - { "id": "N6", "query": "почини опечатку в переменной userNmae → userName", "should_trigger": false, "why": "trivial fix, not a recurring bug — ask must NOT fire" } + { "id": "N6", "query": "почини опечатку в переменной userNmae → userName", "should_trigger": false, "why": "trivial fix, not a recurring bug — ask must NOT fire" }, + { "id": "N7", "query": "запиши в комментарии к функции принцип работы алгоритма сортировки", "should_trigger": false, "why": "'принцип'/'запиши' about a CODE comment, not the user's personal mental model — near-miss save (W4)" } ] } diff --git a/plugins/mnemo/.claude-plugin/plugin.json b/plugins/mnemo/.claude-plugin/plugin.json index 814c0fc..f5e13cd 100644 --- a/plugins/mnemo/.claude-plugin/plugin.json +++ b/plugins/mnemo/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "mn", "displayName": "mnemo", - "version": "1.2.6", + "version": "1.2.7", "description": "Persistent memory layer for Claude Code and Codex via Obsidian — 7 skills for vault health, knowledge search, verified cross-runtime project recall, connection discovery, session continuity, memory routing, skill-aware session review, and interactive setup. Bundled JSON-stdin atomic vault writer, argv-safe indexed reads, dual-runtime helpers, hybrid model routing, parallel CLI calls, SessionStart prewarm, incremental JSONL parsing, shared references, and semantic taxonomy roles.", "author": { "name": "jojoprison", diff --git a/plugins/mnemo/.codex-plugin/plugin.json b/plugins/mnemo/.codex-plugin/plugin.json index d9b9a07..7618fd7 100644 --- a/plugins/mnemo/.codex-plugin/plugin.json +++ b/plugins/mnemo/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mnemo", - "version": "1.2.6", + "version": "1.2.7", "description": "Persistent memory layer for Codex and Claude Code via Obsidian: vault health, knowledge search, verified cross-runtime project recall, link discovery, session notes, memory routing, review orchestration, and setup.", "author": { "name": "jojoprison", diff --git a/plugins/mnemo/references/config-schema.md b/plugins/mnemo/references/config-schema.md index 77c0135..78a541d 100644 --- a/plugins/mnemo/references/config-schema.md +++ b/plugins/mnemo/references/config-schema.md @@ -151,6 +151,15 @@ Two optional fields override the config cadence on a single note. Neither is wri This is deliberately **not** an absolute `review-by:` date. Computed-from-type plus an optional relative `ttl` does not rot the way a hand-typed future date does, and avoids the "guilt-debt" failure mode where stale review dates pile up as unactioned nags. +## Optional per-note frontmatter (retrieval) + +Two optional fields sharpen retrieval; both are written by `save` (see `Step 0b` in `../skills/save/SKILL.md`), never required: + +| Field | Meaning | Example | +|-------|---------|---------| +| `kind` | Semantic sub-type when it is **narrower** than the taxonomy role: `decision`/`gotcha` (on the `fact` role), `principle`/`pain`/`stance` (on the `insight` role). Makes `[kind:pain]` property-searchable in `/mn:ask`. Omit for a plain `fact`/`insight`/`source` — there it would only echo the role | `kind: pain` | +| `aliases` | Retrieval keys — EN/RU synonyms or a short name so a cross-language or short query still finds the note. A search key, **not** authored content; add only when a future search would plausibly use a different term than the title | `aliases: [save redesign, редизайн save]` | + ## Defaults when fields are missing If the whole `cascade` section is absent, defaults are: obsidian=true, claude_mem=false, memory_dir=true, project_rules=true, claude_md=false. @@ -202,6 +211,18 @@ The PARA role map above is the setup suggestion: durable knowledge defaults to ` **Atom vs Molecule:** if one source note supports the claim → Atom. If the insight only emerges by combining ≥2 → Molecule. "Linked two notes" alone is not a Molecule. +## Semantic sub-types (the `kind` field) + +The five `taxonomy_roles` are the physical routing targets; a note may also carry a narrower **semantic sub-type** in `kind:` that shapes its body (typed slots) without changing where it is stored. These piggyback on a role — no schema change, works on every vault: + +| `kind` | Piggybacks on role | Body slot (see `save` Step 0b) | +|--------|--------------------|-------------------------------| +| `decision` | `fact` | Y-statement: context / choice / rejected / goal / trade-off / because | +| `gotcha` | `fact` | GIVEN / WHEN / THEN + Because + Fails-when | +| `principle` / `pain` / `stance` | `insight` | JTBD: Job / Pain (func·emo·social) / Done-well / Anti-goal | + +`principle`/`pain`/`stance` capture the **user's** business logic, pains, and decision stance as human-authored/confirmed atomic claims — never an agent-generated dossier (that would be the rejected auto-ingest fork; see `../../../docs/design-decisions.md`). A plain `fact`/`insight`/`source` note omits `kind:`. + ## Internationalization `links_section` is the natural i18n point. Russian vaults use `## Связи`, English `## Links`, Chinese `## 链接`, etc. All skills honor whatever string is set. diff --git a/plugins/mnemo/skills/save/SKILL.md b/plugins/mnemo/skills/save/SKILL.md index 54bc1a4..e14a76b 100644 --- a/plugins/mnemo/skills/save/SKILL.md +++ b/plugins/mnemo/skills/save/SKILL.md @@ -1,6 +1,6 @@ --- name: save -description: "Use right after something worth keeping appears — you solved a tricky bug, made a non-obvious decision, or hit a gotcha — proactively, without being asked, so a future session doesn't relearn it. Also when the user says 'remember this', 'save this', 'запомни', 'запоминай', 'сохрани', 'сохрани в память', 'сохрани в обсидиан', 'сохрани в базу знаний', 'помни', 'отложи в память', 'в мнемо', or similar. Routes recall to the configured Obsidian taxonomy, optional claude-mem, and Claude auto memory when appropriate; Codex memories remain generated read-only state. Actionable never-X / always-Y rules keep the existing project-instruction routing." +description: "Use right after something worth keeping appears — you solved a tricky bug, made a non-obvious decision, hit a gotcha, or the user voiced a business rule, a pain, or how they think/decide — proactively, without being asked, so a future session doesn't relearn it. Also when the user says 'remember this', 'save this', 'запомни', 'запоминай', 'сохрани', 'сохрани в память', 'сохрани в обсидиан', 'сохрани в базу знаний', 'помни', 'отложи в память', 'в мнемо', 'сохрани принцип', 'это моя боль', 'запомни как я думаю', 'запиши мой подход', 'моё правило', or similar. Splits deep material into atomic, claim-titled notes with typed slots (decision / gotcha / principle·pain·stance) — never one blob — and routes recall to the configured Obsidian taxonomy, optional claude-mem, and Claude auto memory when appropriate; Codex memories remain generated read-only state. Actionable never-X / always-Y rules keep the existing project-instruction routing." model: inherit --- @@ -36,6 +36,7 @@ Then determine what type of information is being saved: | **insight** | Obsidian `taxonomy_roles.insight` + optional claude-mem | "CLI-first is 70,000x cheaper because of token savings" | | **decision** | Obsidian `taxonomy_roles.fact` + optional claude-mem + Claude auto memory when error-preventing | "We chose SCOPE over TextGrad for self-correction" | | **gotcha** | Obsidian `taxonomy_roles.fact` + Claude auto memory + possibly CLAUDE.md | "execSync with shell=true is banned in antomate" | +| **principle / pain / stance** (how the user thinks / what pains them / their quality bar — the business-logic & mental-model layer) | Obsidian `taxonomy_roles.insight` (narrower semantic type in `kind:`) + optional claude-mem | "the user prefers ambitious-first with honest trade-offs" · "pain: pasting a wall of prompt every session" | | **source** | Obsidian `taxonomy_roles.source` + optional claude-mem | External article, tool, research finding | | **actionable rule** | `.claude/rules/.md` (auto-inject, path-scoped) — Step 3.5 only | "After touching `sign_epl.py`, always gate the Kontur call on the flag" | @@ -43,11 +44,34 @@ Then determine what type of information is being saved: > > **Routing consequence — apply this to every step below.** A *recall* item flows through **Steps 1-4** normally. An *actionable rule* goes to **Step 3.5 only**: skip Steps 1-3 (Obsidian / claude-mem / memory/ are superseded by the auto-injecting rule file — don't double-write), and Step 4 (CLAUDE.md) fires only as the fallback if Step 3.5 declined (e.g. `project_rules` disabled). One kind → one home. +### Step 0b: Shape the note — typed slots, atomicity, one rationale + +Depth comes from **structure, not volume.** The unit you retrieve must equal the unit you address — a well-titled atom, never a 5000-word blob. A blob is unretrievable point-precisely (you can't grab the right slice from it) and drifts straight into the auto-ingest fork mnemo's founding principle rejects (`docs/design-decisions.md`). + +**Split, don't dump.** If the material carries ≥2 separable claims, create ≥2 atomic notes — plus, *only* if a genuine cross-note insight emerges, one `insight`-role synthesis linking them. Never truncate; never fuse unrelated claims into one note. "Maximally detailed" is a property of the *corpus* (many atoms + one narrative `session` note + `connect` links), never of a single note. + +**Two non-negotiables per typed note:** +- **Claim-shaped title** (title-as-API): a statement, not a topic — `{fact_prefix}Redis fail-open keeps reads alive when cache is down`, not `{fact_prefix}Redis`. If no single claim fits the whole note, it isn't atomic → split. +- **`because` / rationale** in the body: the premise the claim rests on, so a future agent sees *why* and never silently revokes a decision whose condition it can't see. +- **Gate strength:** **hard** for `decision` / actionable rule / `principle`·`pain`·`stance` — reject the write until both exist. **Soft nudge** for a plain `fact` (a bare fact may legitimately have no deeper because). + +**Route the body into the slot template for its semantic type** (these are body *shapes* written into the same Markdown string — not new backends, scripts, or frontmatter machinery): + +- **decision** → one Y-statement: *In the context of ``, facing ``, we chose `