From 908d0af99e033aa15a21b9769b8604a579bf75e7 Mon Sep 17 00:00:00 2001 From: jojoprison Date: Thu, 23 Jul 2026 19:27:02 +0700 Subject: [PATCH 1/2] =?UTF-8?q?feat(review):=20--full=20=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D1=82=20=D0=B0=D0=B2=D1=82=D0=BE-=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=8F=D1=82=D1=8C=20=D1=81=D0=B2=D1=8F=D0=B7?= =?UTF-8?q?=D0=B8=20connect=20(opt-in,=20v1.2.10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Новый флаг review.full.autoConnect (default false): когда включён, шаг connect в цепочке --full пишет предложенные связи без per-suggestion подтверждения (connect Step 5.5) и репортит каждую запись — закрытие сессии больше не спотыкается о «apply these? (y/N)». Согласие = юзер набрал --full И выставил флаг (зеркалит review.lint.autoStampReviewed) — второе, default-off исключение из non-destructive-принципа. Standalone /mn:connect НИКОГДА не авто-применяет, дефолтная установка байт-в-байт прежняя (connect остаётся suggest-only). Дуальность по построению: поведение в общей прозе SKILL.md, которую читают оба рантайма; Claude-only frontmatter model: не тронут. test-skill-write-contracts.py пинит инварианты (флаг default false, standalone не авто-применяет, verify-проход по-прежнему не линкует). Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude-plugin/marketplace.json | 2 +- CHANGELOG.md | 9 ++++++++- docs/design-decisions.md | 2 +- plugins/mnemo/.claude-plugin/plugin.json | 2 +- plugins/mnemo/.codex-plugin/plugin.json | 2 +- plugins/mnemo/references/config-schema.md | 4 +++- plugins/mnemo/skills/connect/SKILL.md | 10 ++++++++-- plugins/mnemo/skills/review/SKILL.md | 5 +++-- scripts/test-skill-write-contracts.py | 17 +++++++++++++++++ 9 files changed, 43 insertions(+), 10 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b0ffea5..2989fe9 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.9", + "version": "1.2.10", "source": "./plugins/mnemo", "category": "productivity", "keywords": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 569b4e4..a46b4e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +## [1.2.10] - 2026-07-23 + +### Added + +- **`review --full` can auto-apply connect's links (opt-in, `review.full.autoConnect`, default false).** When the flag is on, the `--full` chain's `connect` step writes its suggested links without a per-suggestion prompt (connect Step 5.5) and reports every write — so closing a session no longer stops on "apply these? (y/N)". The user typing `--full` **and** setting the flag is the consent, mirroring `review.lint.autoStampReviewed`: it's the second, default-off exception to the non-destructive principle (`docs/design-decisions.md`). A standalone `/mn:connect` / `$mnemo:connect` never auto-applies regardless of the flag, and the default install is byte-for-byte unchanged (connect stays suggest-only). Dual-runtime by construction — the behavior lives in the shared `SKILL.md` prose that both Claude Code and Codex read; the Claude-only `model:` frontmatter is untouched. `test-skill-write-contracts.py` pins the new invariants (flag default false, standalone never auto-applies, verify pass still never links). + ## [1.2.9] - 2026-07-21 ### Changed @@ -933,7 +939,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.9...HEAD +[Unreleased]: https://github.com/jojoprison/mnemo/compare/v1.2.10...HEAD +[1.2.10]: https://github.com/jojoprison/mnemo/compare/v1.2.9...v1.2.10 [1.2.9]: https://github.com/jojoprison/mnemo/compare/v1.2.8...v1.2.9 [1.2.8]: https://github.com/jojoprison/mnemo/compare/v1.2.7...v1.2.8 [1.2.7]: https://github.com/jojoprison/mnemo/compare/v1.2.6...v1.2.7 diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 52ae687..c9ceabe 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -9,7 +9,7 @@ Why mnemo is shaped the way it is, and which tempting features it deliberately d Three consequences: - **Human-authored** — the atomic claims are written by you (or by the agent, in-conversation, on your behalf). mnemo does not generate vault content from a document corpus. The agent "was there" when the insight happened and pins the discrete claim. Contrast: an LLM that ingests a folder of PDFs and auto-explodes them into pages. -- **Non-destructive** — skills report and suggest; they never auto-delete, overwrite, or author content. The single, opt-in exception is the `reviewed:` snooze stamp (`review.lint.autoStampReviewed`), and only on notes the lint judged still-valid. +- **Non-destructive** — skills report and suggest; they never auto-delete, overwrite, or author content. There are two **opt-in, default-off** exceptions, each a config flag the user must flip: the `reviewed:` snooze stamp (`review.lint.autoStampReviewed`), written only on notes the lint judged still-valid; and `review --full` auto-linking (`review.full.autoConnect`), where the chain's `connect` step writes its suggested links without a per-suggestion prompt. Both stay suggest-only until the user opts in — a standalone `/mn:connect` never auto-applies, and the default install writes neither. - **In-agent** — mnemo runs inside Claude Code / Codex. The harness already injects the `memory/MEMORY.md` index and the live conversation as hot context, so mnemo never has to re-create the agent's working memory. ## Cross-runtime recall is federation, not synchronization (v1.2.3) diff --git a/plugins/mnemo/.claude-plugin/plugin.json b/plugins/mnemo/.claude-plugin/plugin.json index 9e7e745..1c2df33 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.9", + "version": "1.2.10", "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 c060710..e692f1c 100644 --- a/plugins/mnemo/.codex-plugin/plugin.json +++ b/plugins/mnemo/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mnemo", - "version": "1.2.9", + "version": "1.2.10", "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 78a541d..55dc59e 100644 --- a/plugins/mnemo/references/config-schema.md +++ b/plugins/mnemo/references/config-schema.md @@ -69,6 +69,7 @@ Path: `~/.mnemo/config.json`. Created by `setup` skill on first install. All oth }, "review": { + "full": { "autoConnect": false }, "staleDays": { "default": 30, "atom": 60, @@ -98,7 +99,7 @@ Path: `~/.mnemo/config.json`. Created by `setup` skill on first install. All oth } ``` -The whole `review` section is **optional** — if absent, `health` falls back to a uniform 30-day staleness threshold (the legacy behavior) and the content-lint pass stays off. Add it only when you want type-aware cadence or the LLM lint. +The whole `review` section is **optional** — if absent, `health` falls back to a uniform 30-day staleness threshold (the legacy behavior), the content-lint pass stays off, and `review.full.autoConnect` defaults to **false** so `review --full`'s connect step stays suggest-only. Add it only when you want type-aware cadence, the LLM lint, or one-command auto-linking. The `recall` section is optional and ships off. `recall.codeGraph` (default `null`) is a seam for `/mn:ask` Step 4c: set it to a code-knowledge-graph backend you have installed — `"graphify"` (reads its `graph.json` / `GRAPH_REPORT.md`) or an MCP server (`"sourcegraph"` / `"ast-grep"` / `"tree-sitter-analyzer"`) — and recall gains structural "what's where" context. With no backend it's a no-op. (The project-repo **git-log** grounding in Step 4c runs regardless whenever `/mn:ask` is invoked inside a git project — it needs no config.) @@ -133,6 +134,7 @@ The `recall` section is optional and ships off. `recall.codeGraph` (default `nul | `memory.indexWarnKB` | Early loaded-content byte warning for Claude `MEMORY.md`. Current loader limits are 200 lines or 25,000 bytes after stripping leading YAML frontmatter and block-level HTML comments; health always reports either hard-limit breach independently of this threshold. Default **22** | health | | `handoff.maxKB` | Size ceiling (KB) before `vault-write.py archive-handoff` rotates CLOSED old blocks into ` Archive` (cold). The handoff is a live index, not a store; un-rotated it becomes a token bomb read every session. Default **40** | session | | `handoff.keepDays` | Blocks newer than this stay hot regardless of status; older **and** closed (no open `- [ ]`) move to the archive. Default **14** | session | +| `review.full.autoConnect` | In `review --full`, the internal `connect` step **applies** its link suggestions without a per-suggestion prompt instead of only showing them (connect Step 5.5, review Step 9A). The user typing `--full` **and** setting this flag is the consent — mirroring `review.lint.autoStampReviewed`, the opt-in that lets a skill write. A standalone `/mn:connect` is unaffected and never auto-applies. Default **false** (non-destructive default; connect stays suggest-only for everyone who doesn't opt in) | review, connect | | `review.staleDays.default` | Days before a note becomes a review candidate when its type has no specific entry. Default **30**. (`review.staleDays` may also be a bare integer — a single uniform threshold for every type.) | health | | `review.staleDays.{type}` | Per-type staleness cadence (key = a taxonomy `type` you actually use: `atom`/`molecule`/`source`/`session`/`moc`). A fast-moving fact ages quicker than an architectural decision | health | | `review.lint.enabled` | Run the content-lint deep pass (LLM re-reads candidates, emits still-valid/update-needed/contradicts verdicts). Default **false** — it reads note bodies and costs tokens | health | diff --git a/plugins/mnemo/skills/connect/SKILL.md b/plugins/mnemo/skills/connect/SKILL.md index 04f041f..b8bb92a 100644 --- a/plugins/mnemo/skills/connect/SKILL.md +++ b/plugins/mnemo/skills/connect/SKILL.md @@ -96,9 +96,15 @@ Apply these? (y/N, or pick numbers: 1,3) Links are applied **with their one-line context** (the «Why»), not as bare `[[wikilinks]]` (Luhmann: state why you linked). +### Step 5.5: Auto-apply mode (the `review --full` chain only) + +**Fires only when the invoking payload carries an explicit auto-apply directive** — the `review --full` chain sends one after reading `review.full.autoConnect: true` from `~/.mnemo/config.json` (see `/references/config-schema.md`). A standalone `/mn:connect` / `$mnemo:connect` — anything a user typed, or any invocation without that directive — **never** auto-applies; it renders Step 5 and waits. This is the same shape as the other `--full` writes: the user opting into `--full` *and* flipping the config flag is the consent, exactly as `review.lint.autoStampReviewed` gates the one write `health` makes. + +When the directive is present, skip the Step 5 confirmation prompt and go straight to **Step 6** for the ranked, deduplicated, backlink-excluded suggestions this run produced — then report every link written (tension links included and clearly marked) so nothing is applied invisibly. The suggestions are still the same high-quality set (max 5-7, meaningful-not-generic, orphans excluded); auto-apply removes the keystroke, not the quality bar. If the config flag is off, or the directive is absent, this step does not run — fall through to Step 5. + ### Step 6: Apply on Confirmation -If user confirms: +If the user confirms (Step 5) — or auto-apply mode is active (Step 5.5): 1. Read the target again, choose one unique stable anchor in `{links_section}`, then add new links **with a one-line context** (Luhmann — state why connected): `- [[Name]] — {why, ≤10 words}`, not a bare `[[Name]]`. Tension links get the marker: `- [[Name]] #contradiction — {what disagrees}`. 2. Apply the confirmed block through the bundled writer: @@ -133,4 +139,4 @@ Common failures in `/references/gotchas.md`. Tool-routing rationale - **Don't suggest links to orphan notes** — they need their own fixing first (run `/mn:health` if interested). - **Ghost notes are normal** — `[[Technology]]` pointing to a non-existent note enables entity discovery. Not "unresolved" in the bad sense. - **Avoid generic connections** — "both mention Claude" is noise. A connection is meaningful if it shares a concept, approach, or unresolved question. -- **Never auto-apply** — always ask the user before writing new wikilinks. +- **Never auto-apply on the standalone path** — a user-typed `/mn:connect` / `$mnemo:connect`, or any invocation without the explicit `review --full` auto-apply directive, always asks before writing new wikilinks (Step 5). The **only** exception is Step 5.5: the `review --full` chain with `review.full.autoConnect: true` — there the user opted into `--full` and flipped the config flag, so the links apply without a per-suggestion `y` and every write is reported. Default config keeps the flag off, so the default connect behavior is unchanged. diff --git a/plugins/mnemo/skills/review/SKILL.md b/plugins/mnemo/skills/review/SKILL.md index b5bed09..049d3ce 100644 --- a/plugins/mnemo/skills/review/SKILL.md +++ b/plugins/mnemo/skills/review/SKILL.md @@ -252,7 +252,7 @@ Codex: read `/skills/save/SKILL.md` completely, then follow it with 1. **save** — persist the Step 7 candidates (decisions, findings, and any `principle` / `pain` / `stance` material routed by `depth-contract.md` into typed atoms, never the narrative). Actionable rules still route to `.claude/rules/` (save Step 3.5). 2. **session** — write the single narrative note + handoff (never a duplicate of today's). 3. **[focus / extras]** — apply any focus text left in the invocation after `--full` (Step 0 item 6) here, between session and connect. -4. **connect** — discover genuine links on the notes just parked, including non-obvious ones; suggest-only, never auto-applied. +4. **connect** — discover genuine links on the notes just parked, including non-obvious ones. Read `review.full.autoConnect` from `~/.mnemo/config.json` (default **false**): when **true**, delegate to `connect` with an explicit **auto-apply directive** so it writes the links without a per-suggestion prompt (connect Step 5.5) and reports every write; when **false/absent**, delegate **suggest-only** (connect renders its Step 5 offer and stops — the v1.2.8 default, unchanged). The user typing `--full` *and* setting the flag is the consent; a standalone `/mn:connect` never auto-applies regardless of the flag. Discover genuinely — including non-obvious links — never manufacture links to hit a count. `health` is **excluded** — heavy and manual; surface it as a recommendation in the report, don't run it. Recommendations flow **down** from this audit into the chain; `connect` never reaches back up to re-drive the list. @@ -284,5 +284,6 @@ All-green → "🏛 palace in order". Otherwise emit the residual-gap list (advi - **`--full` = consent, not autorun** — the explicit flag chains save → session → connect without per-skill `y`; plain `/mn:review` still never auto-runs (v0.16.0 intact). Only the user typing `--full` triggers the chain - **`health` stays manual** — never in the `--full` chain (heavy); recommend it, don't run it - **Verify grounds externally, never self-grades** — every Step 9 check cites git / orphans / `session-scan.py` / the Step-0 snapshot, never the agent's own assertion -- **Verify never links** — an orphan is delegated to `connect`; Step 9 adds no link itself, and link *count* is never a green signal +- **Verify never links** — an orphan is delegated to `connect`; the Step 9 **verify** pass adds no link itself, and link *count* is never a green signal +- **`connect` auto-applies only under the opt-in flag** — the Step 9A **chain** step (distinct from the verify pass above) writes links without a prompt only when `review.full.autoConnect: true` (default false); otherwise it stays suggest-only, and a standalone `/mn:connect` never auto-applies regardless of the flag - **memory-not-CI** — `--full` REPORTS a missing prod/e2e verification as a gap; it never runs tests, hits prod, or fires a trigger diff --git a/scripts/test-skill-write-contracts.py b/scripts/test-skill-write-contracts.py index a7d2668..ecd61d0 100644 --- a/scripts/test-skill-write-contracts.py +++ b/scripts/test-skill-write-contracts.py @@ -123,6 +123,23 @@ def test_verify_is_grounded_binary_and_never_links(self) -> None: self.assertIn("never add a link here", review) self.assertIn("memory-not-CI", review) # prod/e2e reported, never run + def test_auto_connect_is_opt_in_and_standalone_never_auto_applies(self) -> None: + review = skill("review") + connect = skill("connect") + schema = (REPO_ROOT / "plugins/mnemo/references/config-schema.md").read_text() + # v1.2.10 flag: documented in review, connect, and the schema, and default-off. + self.assertIn("review.full.autoConnect", review) + self.assertIn("review.full.autoConnect", connect) + self.assertIn("review.full.autoConnect", schema) + self.assertIn('"full": { "autoConnect": false }', schema) # default off in the sample schema + # connect gained an auto-apply mode gated on the --full directive... + self.assertIn("Auto-apply mode", connect) + self.assertIn("auto-apply directive", connect) + # ...but the standalone path still never auto-applies. + self.assertIn("Never auto-apply on the standalone path", connect) + # review's chain step reads the flag; the verify pass still never links. + self.assertIn("auto-applies only under the opt-in flag", review) + def test_depth_contract_routes_mental_model_to_save_not_narrative(self) -> None: contract = (REPO_ROOT / "plugins/mnemo/references/depth-contract.md").read_text() self.assertIn("one kind → one home", contract) From 5547e9c39e4b3643f7b174881dbbfdccfdfbda19 Mon Sep 17 00:00:00 2001 From: jojoprison Date: Thu, 23 Jul 2026 19:32:39 +0700 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20=D0=B4=D0=BE=D0=BA=D0=B2=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B8=20stale=20=C2=ABconnect=20never=20auto-appli?= =?UTF-8?q?es=C2=BB=20=D0=BF=D0=BE=D0=B4=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9?= =?UTF-8?q?=20opt-in=20(=D0=B0=D0=B4=D0=B2=D0=B5=D1=80=D1=81-=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Адверсариальный веер (4 линзы) подтвердил: dual-runtime и flag-plumbing чисты, но фикс в connect/SKILL.md не был догрёблен по siblings — depth-contract.md (её review --full ИНЖЕКТИТ как контракт цепочки), docs/connect.md и README всё ещё утверждали безусловное «never auto-applies». Дописан carve-out под review.full.autoConnect в каждом. README:91 («never auto-delete or overwrite») не тронут — авто-apply связи это append. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 2 +- docs/connect.md | 6 +++--- plugins/mnemo/references/depth-contract.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7549f8b..24129c8 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ You work → mnemo remembers → Your vault grows → You find things later | **session** | `/mn:session` | Session summary note + cross-session handoff for the next session | | **review** | `/mn:review` | End-of-session orchestrator — audits the session, recommends save + session + the rest, asks before running anything | | **ask** | `/mn:ask` | Search the vault + active runtime memory (+ optional verified counterpart memory), synthesize a cited answer, date sources, and ground current-state answers in git history | -| **connect** | `/mn:connect` | Discover hidden connections between notes — suggests, never auto-applies | +| **connect** | `/mn:connect` | Discover hidden connections between notes — suggests; standalone never auto-applies (`review --full` can, opt-in `review.full.autoConnect`) | | **health** | `/mn:health` | Vault audit: orphans, broken links, type-aware review candidates (+ optional LLM lint), growth stats | | **setup** | `/mn:setup` | Interactive onboarding — vault, taxonomy, language, optional cross-runtime recall | diff --git a/docs/connect.md b/docs/connect.md index 73a8137..e00a2e4 100644 --- a/docs/connect.md +++ b/docs/connect.md @@ -2,7 +2,7 @@ ## Overview -Analyzes a note and discovers connections to other notes you'd never think of. Shows suggestions with explanations — you confirm before applying. +Analyzes a note and discovers connections to other notes you'd never think of. Shows suggestions with explanations — you confirm before applying. (One exception: `/mn:review --full` with `review.full.autoConnect` turned on applies them for you — the standalone command never does.) ## Usage @@ -18,7 +18,7 @@ Analyzes a note and discovers connections to other notes you'd never think of. S 3. Searches vault for each concept 4. Compares found notes vs already linked notes 5. Suggests new connections with reasoning -6. Applies only what you approve +6. Applies only what you approve (the `review --full` chain can auto-apply when `review.full.autoConnect` is on) ## Example Output @@ -41,7 +41,7 @@ Apply these? (y/N, or pick numbers: 1,2) ## Important Notes - **Max 5-7 suggestions** — won't overwhelm you -- **Never auto-applies** — you confirm each connection +- **Standalone never auto-applies** — you confirm each connection; only `review --full` with `review.full.autoConnect` (default off) applies them for you - **Ignores generic matches** — "both mention Claude" is not a meaningful connection - **Won't suggest orphans** — they need their own fixing first diff --git a/plugins/mnemo/references/depth-contract.md b/plugins/mnemo/references/depth-contract.md index fdae4ac..ed74624 100644 --- a/plugins/mnemo/references/depth-contract.md +++ b/plugins/mnemo/references/depth-contract.md @@ -27,7 +27,7 @@ Step 0b, applied across the whole close-out. | A cross-note synthesis (≥2 notes) | `save` → insight role | non-trivial insight, `cites:` the sources | | An actionable never-X / always-Y rule tied to code | `save` Step 3.5 → `.claude/rules/` | path-scoped auto-inject, not recall | | The session narrative (what happened, the arc, decisions-in-context) | `session` | one note + handoff, never a duplicate of today's | -| Connections between the new notes (incl. non-obvious) | `connect` | suggest-only links, no orphans | +| Connections between the new notes (incl. non-obvious) | `connect` | suggest-only links (auto-applied only under `review.full.autoConnect`), no orphans | | A future-useful thread not finished | `session` handoff `- [ ]` pending | thin live index | The business-logic / pains / mental-model layer is the crown material — it routes to `save`'s typed