From 8f65fb695afa8ddefdd598ef09f345d440110218 Mon Sep 17 00:00:00 2001 From: jojoprison Date: Tue, 21 Jul 2026 01:54:58 +0700 Subject: [PATCH] =?UTF-8?q?feat(review):=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC?= =?UTF-8?q?=20--full=20=E2=80=94=20=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=20=D0=BE?= =?UTF-8?q?=D0=B4=D0=BD=D0=BE=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4?= =?UTF-8?q?=D0=BE=D0=B9=20(v1.2.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /mn:review --full сворачивает весь ритуал закрытия сессии в одну явную команду без пер-скилл подтверждений: флаг = согласие (не имплицитный авторан, убитый в v0.16.0 — юзер сам его печатает). Фазы: origin-anchor (реконструкция начала сессии + дрейф) → audit → чейн save→session→ [фокус]→connect → read-only verify. health исключён (тяжёлый, вручную). Сворачивает четыре ручных промпта (подробно/зафиксировали-всё/что-осталось/ скрытые-связи) в нативные фазы. Обычный /mn:review не тронут. - references/depth-contract.md: routing-контракт (глубина через структуру, не объём) — бизнес-логика/боли/склад-мышления → в типизированные атомы save, не в нарратив session. - verify guardrails: grounded (git/orphans/session-scan/snapshot, не self-grade), non-orphan бинарно (сироты → connect, verify не линкует), prod/e2e репортится как gap (memory-not-CI), идемпотентно. - session: depth-routing дефолт в Step 1 + own-note self-check в Step 7. - test-skill-write-contracts.py: 6 guard-тестов на инварианты --full. - evals: +P9/P10/N8. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude-plugin/marketplace.json | 2 +- CHANGELOG.md | 15 ++++++- docs/design-decisions.md | 15 +++++++ docs/review.md | 17 +++++++- docs/session.md | 3 ++ evals/trigger-eval.json | 7 +++- plugins/mnemo/.claude-plugin/plugin.json | 2 +- plugins/mnemo/.codex-plugin/plugin.json | 2 +- plugins/mnemo/references/depth-contract.md | 48 +++++++++++++++++++++ plugins/mnemo/skills/review/SKILL.md | 49 +++++++++++++++++++++- plugins/mnemo/skills/session/SKILL.md | 9 +++- scripts/test-skill-write-contracts.py | 46 ++++++++++++++++++++ 12 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 plugins/mnemo/references/depth-contract.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 45dec33..19bbfcc 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.7", + "version": "1.2.8", "source": "./plugins/mnemo", "category": "productivity", "keywords": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e7502e..58c45ff 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.8] - 2026-07-21 + +### Added + +- **`/mn:review --full` — one-command session close-out.** An explicit `--full` flag turns `review` into the whole end-of-session ritual with no per-skill prompt: the flag itself is consent (**not** the implicit autorun removed in v0.16.0 — the user types it). It anchors on the session's **origin** (reconstructs the first request, measures drift: discussed vs wanted vs did), audits the arc, chains **save → session → [focus text] → connect**, then runs a read-only **verify** pass. `health` is excluded (heavy — manual). Folds the four manual "be thorough / did we capture everything / what's left / find hidden links" prompts into built-in phases. Plain `/mn:review` is byte-for-byte unchanged (audit + one interactive offer, never auto-runs). +- **`references/depth-contract.md` — thoroughness by routing, not volume.** A standing contract `--full` injects into its `save`/`session` steps (and `session` reads as its default bar): business-logic / pains / how-the-user-thinks route into `save`'s typed `principle`/`pain`/`stance` atoms, links to `connect`, unfinished work to the handoff — never a "capture everything" blob. Encodes depth = structure (the v1.2.7 rule) across the whole close-out. +- **Grounded, idempotent verify guardrails.** The verify pass cites only external facts (git diff, orphans, `session-scan`, a Step-0 snapshot), never self-grades; its non-orphan check is binary and never rewards link count (orphans delegate to `connect`; verify never links); it REPORTS a missing prod/e2e verification as an unchecked gap (memory-not-CI — it never runs QA); and a re-run on an unchanged session prints "already in order" and stops. + +### Changed + +- **`session` is thorough-by-routing and self-checks its own note.** Step 1 carries a standing depth-routing default (pointing at the depth-contract) so even a standalone `/mn:session` routes atom-worthy material to `save` instead of swelling the narrative; Step 7 adds an own-note self-check (dup / MOC / orphan / delegation). The cross-skill palace audit stays in `review --full`. `review`'s description gains the `--full` triggers; `evals/trigger-eval.json` adds P9/P10 (positives) + N8 (near-miss); `test-skill-write-contracts.py` pins the new `--full` invariants (flag = consent not autorun, chain order, grounded-binary-never-links verify, depth-contract routing). + ## [1.2.7] - 2026-07-21 ### Added @@ -911,7 +923,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.7...HEAD +[Unreleased]: https://github.com/jojoprison/mnemo/compare/v1.2.8...HEAD +[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 [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 diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 8349bba..8d3755c 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -44,6 +44,21 @@ mnemo saves two different kinds of thing, and they must land in different places - **Auto-route on explicit save, confirmed in the orchestrator.** A direct `/mn:save` of a rule routes automatically (the user explicitly asked to save). `/mn:review` never writes unattended — since v0.16.0 *every* skill it wants to run goes through one Step 8 confirmation, and an actionable rule additionally gets its own explicit line item there, because routing it creates/edits committed project files. - **Codex caveat acknowledged, not solved here.** Codex reads only `AGENTS.md` (32 KiB, silent truncate), not `.claude/rules/`. mnemo routes to `.claude/rules/` and flags the AGENTS.md build-step as the user's responsibility — it does not own that assemble step. +## One-command close-out: `review --full` (v1.2.8) + +`/mn:review --full` collapses the end-of-session ritual — audit, save, session, connect, verify — into a single explicit command, so the user never pastes a "capture everything, check everything, keep it like a clean palace" wall of prompt. It is a **flag on `review`**, not an eighth skill (the 7-skill canon holds). + +**Deliberate boundaries** (each is load-bearing — removing it reintroduces a rejected behavior): + +- **Flag = consent, not implicit autorun.** The explicit `--full` the user typed *is* the confirmation, so the chain runs without a per-skill `y`. This does not revive the autorun removed in v0.16.0: that was *plain* `/mn:review` firing skills unasked. Default `/mn:review` still audits + offers + never auto-runs — only the typed flag chains. +- **Verify grounds externally, never self-grades.** The Step 9 verify pass anchors every check to a fact — git diff, orphans, `session-scan`, the Step-0 snapshot — never the agent's own assertion. A same-agent self-audit that trusts itself rubber-stamps (eval-integrity). +- **Verify never links; over-linking is defused.** The non-orphan check is binary (connected or not) and never rewards link *count*; an orphan is delegated to `connect`, and verify adds no link itself. So "score the palace green" cannot incentivize link-spam from a "connect everything" instruction. +- **Depth-contract is routing, not volume.** `--full` injects `references/depth-contract.md` as guidance: thorough means the right material in the right home (business-logic / pains / mental-model → `save`'s typed atoms, **not** the session narrative), never a bigger note. "Capture everything, super-detailed" is the blob anti-pattern rejected in v1.2.7; it is explicitly dropped. +- **memory-not-CI.** For "did we verify on prod / run e2e / really done?", `--full` REPORTS the absence of test/deploy evidence as an unchecked gap; it never runs tests, hits prod, or fires a trigger. That execution lives in the harness (`finish-the-work` / `loop-gate`), outside a memory plugin's mandate. +- **`health` stays manual.** Heavy; recommended in the report, never auto-chained. +- **`session` stays pure narrative.** Its own Step 7 self-check verifies only *its* note (dup / MOC / orphan / delegation); the cross-skill palace audit is `review --full`'s Step 9, not session's — the same boundary that keeps rule-routing out of session (v0.15.0). +- **Idempotent.** A second `--full` on an unchanged session prints "already in order" and stops — it never re-parks or re-links. + ## Proactive nudges via hooks (v1.1.1) Descriptions get an agent to *consider* mnemo, but Opus 4.8 / Fable 5 under-trigger skills — a good description raises the odds, it doesn't guarantee the call. v1.1.1 adds a deterministic **delivery** layer via hooks, with one honest caveat baked into the design: **deterministic delivery ≠ deterministic effect** — the hook always fires, but the model still decides whether to act. A prose nudge is exactly the "marginal rule ≈ 0" pattern, so it's kept short, factual (not an order), and gated. diff --git a/docs/review.md b/docs/review.md index 7beedc9..f1b04e4 100644 --- a/docs/review.md +++ b/docs/review.md @@ -2,7 +2,7 @@ ## Overview -The only command you need at session end. Analyzes everything, then presents one prioritized offer — unsaved decisions (`/mn:save`), session notes (`/mn:session`), and the remaining skills. **Nothing runs without your confirmation.** One command replaces manually running save + session + connect + health — you approve the batch once. +The only command you need at session end. Analyzes everything, then presents one prioritized offer — unsaved decisions (`/mn:save`), session notes (`/mn:session`), and the remaining skills. **Nothing runs without your confirmation.** One command replaces manually running save + session + connect + health — you approve the batch once. Add `--full` and even that one approval goes away: the flag itself is consent (see [`--full` below](#--full--one-command-close-out-v128)). ## Usage @@ -115,6 +115,21 @@ Recommended: Run any? (1,2,3,4 / A=all / N=skip) ``` +## `--full` — one-command close-out (v1.2.8) + +``` +/mn:review --full +``` + +The explicit flag **is** the consent, so the whole close-out runs with no per-skill prompt. It: + +1. **Anchors on the session's origin** — reconstructs the first request and measures drift (discussed vs wanted vs did), not just an end-state inventory. +2. **Audits** the whole arc (Done / Missed / Hanging Threads / score) — this produces the recommendation list. +3. **Executes the chain** in order — `save → session → [any focus text you passed after --full] → connect` — injecting the depth-contract so business-logic / pains / how-you-think route into typed `save` atoms, not the session narrative. `health` is excluded (heavy — run it by hand). +4. **Verifies (read-only)** — checks the freshly-parked notes for typed-slot quality, atomicity, and connectedness (binary orphan check, delegated to `connect`, never self-linked); REPORTS any missing prod/e2e verification as a gap (mnemo never runs QA); and is idempotent — a second `--full` on an unchanged session prints "🏛 already in order" and stops. + +Plain `/mn:review` (no flag) is unchanged: audit + one interactive offer, never auto-runs (v0.16.0). `--full` does **not** revive that killed implicit autorun — the user typed the flag. + ## Important Notes - **Always thorough** — no quick mode, full analysis every time diff --git a/docs/session.md b/docs/session.md index db20b63..7f9c219 100644 --- a/docs/session.md +++ b/docs/session.md @@ -20,6 +20,7 @@ No arguments. Summarizes the current conversation automatically. 4. Verifies the note is linked in the hub reached through `taxonomy_roles.moc` 5. Updates `Meta — Session Handoff` with pending items 6. Checks for orphans after creation +7. Self-checks its own note (duplicate / MOC link / orphan / atom-delegation) before confirming ## Example Output @@ -62,6 +63,8 @@ When the next session starts, it reads `Meta — Session Handoff`: - **MOC verification** — automatically adds to MOC if missing - **Branch field optional** — research sessions don't have branches - **Ghost notes generously** — wraps projects, technologies, people in `[[wikilinks]]` +- **Thorough by routing, not volume** — the note stays a narrative; decisions, business-logic, pains, and how-you-think route to `save`'s typed atoms (see the depth-contract), links to `connect`, unfinished work to handoff — never a "capture everything" blob +- **Own-note self-check** — Step 7 verifies only this note's own artifact; the cross-skill palace audit is `review --full`'s job, not session's ## Related Skills diff --git a/evals/trigger-eval.json b/evals/trigger-eval.json index af5235b..30edc4f 100644 --- a/evals/trigger-eval.json +++ b/evals/trigger-eval.json @@ -1,5 +1,5 @@ { - "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.", + "about": "Trigger-eval set for mnemo skill descriptions. 10 proactive-positive prompts (a specific skill SHOULD auto-invoke) + 8 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). 2026-07-21 (v1.2.8): +P9/P10 for the review --full one-command close-out, +N8 near-miss ('полный прогон тестов' — 'полный' overlaps --full but is CI, not review). 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" }, @@ -9,12 +9,15 @@ { "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": "P9", "query": "закрой сессию начисто одной командой — прогони весь цикл закрытия и проверь что всё уложено", "should_trigger": true, "expect_skill": "review", "why": "one-command end-of-session close-out → review (--full)" }, + { "id": "P10", "query": "review --full", "should_trigger": true, "expect_skill": "review", "why": "explicit --full flag → review one-command pass" }, { "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": "N7", "query": "запиши в комментарии к функции принцип работы алгоритма сортировки", "should_trigger": false, "why": "'принцип'/'запиши' about a CODE comment, not the user's personal mental model — near-miss save (W4)" } + { "id": "N7", "query": "запиши в комментарии к функции принцип работы алгоритма сортировки", "should_trigger": false, "why": "'принцип'/'запиши' about a CODE comment, not the user's personal mental model — near-miss save (W4)" }, + { "id": "N8", "query": "запусти полный прогон тестов перед мержем", "should_trigger": false, "why": "'полный' overlaps review --full but this is a CI test run, not a session close-out — near-miss review" } ] } diff --git a/plugins/mnemo/.claude-plugin/plugin.json b/plugins/mnemo/.claude-plugin/plugin.json index f5e13cd..ca1889b 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.7", + "version": "1.2.8", "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 7618fd7..16e01a9 100644 --- a/plugins/mnemo/.codex-plugin/plugin.json +++ b/plugins/mnemo/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mnemo", - "version": "1.2.7", + "version": "1.2.8", "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/depth-contract.md b/plugins/mnemo/references/depth-contract.md new file mode 100644 index 0000000..fdae4ac --- /dev/null +++ b/plugins/mnemo/references/depth-contract.md @@ -0,0 +1,48 @@ +# depth-contract — thoroughness by routing, not volume + +The standing "be thorough when closing a session" contract. `review --full` injects this as the +thoroughness guidance for the `save` and `session` steps of its chain; `session` also reads it as +its default bar for a standalone run. It exists so the user never again pastes a "capture +everything, super-detailed, everything in its place" wall of prompt — that intent lives here, +natively. + +## The one rule + +**Thorough = the right material in the right home, richly linked — NOT more words in one note.** + +Depth is a property of the *corpus* (many atoms + one narrative `session` note + `connect` links), +never of a single note. "Maximally detailed / capture everything" written into one note is a blob: +unretrievable point-precisely, and it drifts straight into the auto-ingest fork mnemo's founding +principle rejects (`docs/design-decisions.md`). This is the depth-via-structure rule from `save` +Step 0b, applied across the whole close-out. + +## Routing table — material → home + +| Material surfaced this session | Home | Shape | +|--------------------------------|------|-------| +| A decision + why | `save` → fact role | Y-statement (Step 0b) | +| A gotcha / business rule | `save` → fact role | GIVEN / WHEN / THEN + Because + Fails-when | +| How the user thinks / decides / what pains them / their quality bar | `save` → insight role, `kind: principle / pain / stance` | JTBD slots — user-authored/confirmed, never an agent dossier | +| A durable fact / external source | `save` → fact / source role | claim-title + BLUF + evidence | +| 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 | +| 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 +`principle` / `pain` / `stance` atoms, **not** the session narrative. That is how "capture the way I +think" lands as connected, retrievable atoms instead of a scroll. + +## Aesthetic (the palace) + +Apply the user's memory aesthetic, captured as a durable `stance` atom: a curated palace where every +atom sits in its right place and connects — not a warehouse of "everything, in full". Curated over +exhaustive; atomic over monolithic; linked over loose; structured over voluminous. + +## What NOT to do + +- Do **not** inflate a note to be "more detailed" — split into atoms instead. +- Do **not** triplicate one finding into every store "to be safe" — route by kind (one kind → one home). +- Do **not** invent a psychological dossier of the user — the how-they-think atoms are user-authored/confirmed only. +- Do **not** fold business-logic / mental-model into the session narrative — that is `save`'s typed-atom job. diff --git a/plugins/mnemo/skills/review/SKILL.md b/plugins/mnemo/skills/review/SKILL.md index b9d1bfe..b5bed09 100644 --- a/plugins/mnemo/skills/review/SKILL.md +++ b/plugins/mnemo/skills/review/SKILL.md @@ -1,6 +1,6 @@ --- name: review -description: "End-of-session orchestrator. Audits the session, then recommends the core save and session skills plus the rest — always asks before running anything, never auto-runs. Triggers on 'что забыли', 'что осталось', 'что ещё осталось', 'что ещё тут осталось', 'что осталось добить', 'что надо добить', 'что ещё надо добить', 'session review', 'ревью сессии', 'сессия ревью', 'что добить до идеала', 'all done', end of significant work, or similar. The ONLY command users need at session end — one confirmation covers everything." +description: "End-of-session orchestrator. Audits the session, then recommends the core save and session skills plus the rest — always asks before running anything, never auto-runs. Add the explicit '--full' flag to close a session in one command: the flag itself is consent, so it audits from the session's origin, then chains save → session → connect and runs a grounded verify pass with no per-skill prompts. Triggers on 'что забыли', 'что осталось', 'что ещё осталось', 'что ещё тут осталось', 'что осталось добить', 'что надо добить', 'что ещё надо добить', 'закрой сессию начисто', 'прогони весь цикл закрытия', 'одной командой закрой сессию', 'session review', 'ревью сессии', 'сессия ревью', 'что добить до идеала', 'all done', 'review --full', end of significant work, or similar. The ONLY command users need at session end — one confirmation covers everything, or '--full' covers it with none." model: inherit --- @@ -29,11 +29,22 @@ Use the runtime's normal tools to collect fresh evidence. Do not rely on Claude- 3. Run `python3 "/scripts/session-scan.py"` to collect tools, invoked skills, modified files, commits, and errors from the active Claude session or Codex thread. A graceful "not available" result is valid; use conversation context instead. 4. Run `python3 "/scripts/skills-discover.py"` to build the allowlist of installed skills. 5. Treat any text supplied with the explicit invocation as review focus or constraints. Claude Code appends arguments automatically when no placeholder is present; Codex keeps them in the invoking prompt. +6. **Detect `--full`.** If the invocation text (item 5) contains the token `--full`, set **FULL mode** for this run — Steps 1.5, 3, 6, 8, and 9 branch on it. Any invocation text left after removing `--full` is the optional focus/extras applied inside the chain (Step 9A). Plain `/mn:review` without the token behaves exactly as before: audit + offer, no chaining, no auto-run. +7. **FULL start-snapshot (only when FULL).** Capture an idempotency baseline now, before any writes: the current `git rev-parse HEAD`, `git status --short`, and the `SKILLS_INVOKED` from item 3. Step 9 diffs against this to decide "already in order — nothing to redo". ### Step 1: Load Project Context Read the project's `AGENTS.md` and `CLAUDE.md` when present (respect symlinks and avoid reading the same content twice). Check the active runtime's loaded memory index when available: Claude Code project `memory/MEMORY.md` or Codex-generated read-only `${CODEX_HOME:-~/.codex}/memories/` state. +### Step 1.5: Session Origin Anchor (FULL only) + +**Skip unless FULL mode.** Reconstruct where the session *began*, so the audit measures drift from the original intent — not just a flat inventory of the end state: + +- The **first user request** of the session and the earliest decisions/scope choices — from conversation history (you hold the whole session). +- The **git start-state** relative to now — the diff between the session's first commit and `HEAD`. + +State the anchor in one line — "Session began with: {original ask}" — and carry it into Step 3 (the scan becomes a *delta* vs this baseline) and Step 6 (the drift line + "really done?" verdict). `session-scan.py` surfaces tools/commits, not the opening prompt, so ground the origin in conversation history, never a script guess. + ### Step 2: Determine Session Type Classify by primary activity using Step 0 evidence + conversation history: @@ -63,6 +74,8 @@ From conversation history + the fresh Step 0 evidence, identify: 7. **External systems** — Linear tasks, GitHub PRs, Obsidian notes — updated? 8. **Actionable rules learned** — any "never do X / always do Y" lesson tied to specific code/paths that a *future agent* must auto-see before repeating the mistake (vs recall "what/why"). These belong in `.claude/rules/.md` (path-scoped auto-inject), not just recall memory — flag them for Step 8 routing. +**FULL mode:** run this scan as a three-way delta against the Step 1.5 origin anchor — *discussed* vs *wanted* vs *did* — so Missed / Hanging Threads reflect drift from the original intent, not only unfinished end-state items. + ### Step 4: Skill Gap Analysis Cross-reference: @@ -171,6 +184,10 @@ From `AGENTS.md` / `CLAUDE.md`, check mandatory steps: | Skills | {used/recommended} | | ``` +**FULL mode — add two lines to the report:** +- **Origin → Now:** the drift from the Step 1.5 anchor — what was asked at the start vs what stands now. +- **Really done?** an advisory verdict aggregating memory-native signals (unsaved items, hanging threads) **plus** the prod/e2e gap from Step 9's verify. mnemo REPORTS "e2e/prod not verified this session" as an unchecked gap; it never runs QA. Never assert "done" when that evidence is absent — say "not REALLY done until prod-verified + e2e-run" instead. + ### Step 7: Prepare Core Skill Candidates (no auto-run) **Never invoke save or session yourself without confirmation** — every skill run goes through the Step 8 offer. Here, only prepare the two core candidates with specific payloads so the offer is concrete ("3 decisions: X, Y, Z", not "maybe save something"): @@ -194,6 +211,8 @@ From `AGENTS.md` / `CLAUDE.md`, check mandatory steps: ### Step 8: Offer Skills +**FULL mode branches here.** If FULL is set, do **not** render the interactive "Run any? (1,2,3 / A / N)" prompt — the explicit `--full` flag is the consent. Skip straight to **Step 9**, which executes the chain and verifies. Everything below is the **default** `/mn:review` path only, unchanged (interactive offer, one confirmation). + Present everything — core candidates from Step 7 first among equals, sorted by priority — and ask: Render mnemo entries with the current runtime's explicit syntax. The sample below uses Claude Code syntax; in Codex the corresponding entries are `$mnemo:save`, `$mnemo:session`, `$mnemo:connect`, and `$mnemo:health`. @@ -224,6 +243,29 @@ Codex: read `/skills/save/SKILL.md` completely, then follow it with 3. Dependency order: /commit before /ship 4. After all done, output updated score +### Step 9: Full Pass — Execute Chain + Verify (FULL only) + +**Reached only in FULL mode** (Step 8 sent you here without an interactive offer). The explicit `--full` flag is the consent for the whole pass — no per-skill `y`. This does **not** revive the implicit autorun removed in v0.16.0: that was plain `/mn:review` firing skills *unasked*; here the user typed the flag. + +**A. Execute the recommendation chain** in this fixed order, each via the **Portable paths** delegation contract, injecting `/references/depth-contract.md` as the thoroughness guidance for the write skills: + +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. + +`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. + +**B. Verify (read-only, advisory — never auto-rewrites).** Audit what the chain just parked, and ground **every** check in an external fact — git, orphans, `session-scan.py`, or the Step-0 snapshot — never your own say-so (a same-agent self-audit that trusts itself rubber-stamps): + +- **Parked?** `git status --short` + `git diff --stat` show the new `.claude/rules/` files and repo changes, and the new vault notes exist. Name anything from Step 6 "Missed" that is still unparked. +- **Structural quality?** Read each new note: does it fill its typed slot (save Step 0b), is it a single-claim atom (not a scroll), correct type/place? Report gaps — do **not** rewrite the user's notes. +- **Connected?** `python3 "/scripts/safe-read.py" orphans` — the non-orphan check is **binary** (connected or not); never reward link *count*. An orphan → hand it to `connect` (which just ran); **never add a link here.** Respect the 1-5s cache lag (re-check before flagging CRITICAL — see `/references/gotchas.md`). +- **Prod / e2e / really-done?** If git + `session-scan.py` show no test/deploy/trigger evidence, REPORT "e2e/prod not verified this session" as an unchecked gap and fold it into the Step 6 verdict. mnemo is memory-not-CI: it flags the absence, it never runs, triggers, or verifies QA (that lives in the harness — `finish-the-work` / `loop-gate`). +- **Idempotent?** Diff the current `git rev-parse HEAD` + `git status --short` + `SKILLS_INVOKED` against the Step-0 FULL snapshot. Nothing changed (a prior `--full` already closed this session) → print "🏛 already in order — nothing to redo" and **stop**. Never re-park or re-link what is already there. + +All-green → "🏛 palace in order". Otherwise emit the residual-gap list (advisory) and stop. + ## Rules - **Always thorough** — full analysis, no shortcuts @@ -239,3 +281,8 @@ Codex: read `/skills/save/SKILL.md` completely, then follow it with - **Don't over-report** — unchecked plan AC is noise if code + tests pass - **Multiple projects** — analyze each project dir separately - **Respect completed work** — `save` already ran? Acknowledge, don't re-recommend +- **`--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 +- **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/plugins/mnemo/skills/session/SKILL.md b/plugins/mnemo/skills/session/SKILL.md index 6dd3b54..d6a752c 100644 --- a/plugins/mnemo/skills/session/SKILL.md +++ b/plugins/mnemo/skills/session/SKILL.md @@ -37,6 +37,8 @@ Tool-routing (bundled atomic writer for writes, bundled CLI adapter for reads/se Analyze the conversation: what was done, key decisions, commits/PRs created, findings. +**Thoroughness by routing (standing default).** A good session note is thorough by *routing* material to its right home, not by swelling this one note: the narrative + arc + decisions-in-context live here; business-logic / pains / how-the-user-thinks route to `save`'s typed `principle` / `pain` / `stance` atoms; connections go to `connect`; unfinished threads become handoff `- [ ]` items. Full contract: `/references/depth-contract.md`. Depth = structure, not volume — never fold "capture everything" into the narrative (that is the blob anti-pattern). + **Ground the summary in facts — don't rely on conversation memory alone** (a note that claims "shipped X" when git shows no such commit is worse than no note). Before writing "what was done", cross-check against reality: `git log --oneline -15` + `git status --short` for real commits/changes, and — when the script is reachable — the session's actual tool/skill activity: ```bash @@ -173,13 +175,16 @@ If the newly created note appears in orphans, it means no `## Связи` links ⚠️ **`obsidian orphans` caches & lags writes 1-5s** — a freshly written note may show as orphan falsely. If it appears right after creation, wait 2-3s and re-run, or verify authoritatively via `obsidian eval` (`metadataCache.resolvedLinks`/`unresolvedLinks`). See `/references/gotchas.md`. -### Step 7: Confirm +### Step 7: Confirm — with an own-note self-check + +Before the summary, self-verify **this note's own artifact** (not a cross-skill audit — that is `review --full` Step 9): reusing Steps 2 / 4 / 6, reconfirm it is not a duplicate of today's session, its `## Связи` carries the mapped MOC link, it is non-orphan, and any atom-worthy material was delegated to `save` rather than folded in. Report residual own-note gaps. Output summary: - Note name - MOC updated (yes/no) - Handoff updated (yes/no) - Orphan status (clean / flagged) +- Own-note self-check (clean / gaps: {list}) ## Rules @@ -193,6 +198,8 @@ Output summary: - **Handoff = thin live index, not an archive** — targeted optimistic replacement, not blind append. Named ceiling: when it exceeds `handoff.maxKB` (default 40KB), `vault-write.py archive-handoff` (Step 5) rotates CLOSED blocks older than `handoff.keepDays` into `{handoff_note} Archive` (cold); open `- [ ]` + recent stay hot. Prevents multi-MB token-bomb accumulation without a second writer. - **Links section is mandatory** — at least one MOC link, else the note orphans (invisible to graph navigation) - **Ghost notes generously** — wrap projects, technologies, people in `[[wikilinks]]` +- **Thorough by routing, not volume** — the note stays a narrative; atom-worthy material (decisions, business-logic, pains, how-the-user-thinks) goes to `save`, links to `connect`, unfinished work to handoff. Full contract in `/references/depth-contract.md` +- **Own-note self-check only** — Step 7 verifies this note's own artifact (dup / MOC / orphan / delegation); the cross-skill palace audit belongs to `review --full`, not here ## Gotchas diff --git a/scripts/test-skill-write-contracts.py b/scripts/test-skill-write-contracts.py index c372c55..a7d2668 100644 --- a/scripts/test-skill-write-contracts.py +++ b/scripts/test-skill-write-contracts.py @@ -90,5 +90,51 @@ def test_default_config_has_exact_roles_and_functional_self_maps(self) -> None: self.assertEqual(roles["moc"], "moc") +class ReviewFullContractTests(unittest.TestCase): + """Pin the v1.2.8 `review --full` one-command close-out invariants.""" + + def test_full_flag_mode_and_new_phases_present(self) -> None: + review = skill("review") + self.assertIn("Detect `--full`", review) + self.assertIn("FULL mode", review) + self.assertIn("Session Origin Anchor", review) + self.assertIn("Step 9: Full Pass — Execute Chain + Verify", review) + + def test_full_is_consent_not_the_killed_implicit_autorun(self) -> None: + review = skill("review") + # v0.16.0 no-implicit-autorun stays intact on the DEFAULT path. + self.assertIn("No auto-run", review) + # --full is explicit consent, explicitly distinguished from the killed autorun. + self.assertIn("implicit autorun removed in v0.16.0", review) + self.assertIn("consent, not autorun", review) + + def test_chain_order_excludes_health_and_injects_depth_contract(self) -> None: + review = skill("review") + self.assertIn("references/depth-contract.md", review) + self.assertIn("**excluded**", review) # health never auto-chained + # Chain runs save -> session -> connect in that order. + s, se, c = (review.index(f"**{x}**") for x in ("save", "session", "connect")) + self.assertLess(s, se) + self.assertLess(se, c) + + def test_verify_is_grounded_binary_and_never_links(self) -> None: + review = skill("review") + self.assertIn("**binary**", review) # non-orphan check, not link count + self.assertIn("never add a link here", review) + self.assertIn("memory-not-CI", review) # prod/e2e reported, never run + + 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) + self.assertIn("not** the session narrative", contract) + self.assertIn("auto-ingest fork", contract) + + def test_session_carries_depth_routing_and_own_note_check(self) -> None: + session = skill("session") + self.assertIn("references/depth-contract.md", session) + self.assertIn("own-note self-check", session) + self.assertIn("Own-note self-check only", session) + + if __name__ == "__main__": unittest.main()