Files
MultiPhysicsVault/CHANGELOG.md
T
김경종 72dad72703
Tests / Hermetic test suite (push) Has been cancelled
Tests / Skill frontmatter validation (push) Has been cancelled
add claude-obsidian
2026-05-28 10:57:16 +09:00

407 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to claude-obsidian. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
## [1.9.2] - 2026-05-27 (prompt-cache hardening + path-handling robustness)
Ports Anthropic prompt-caching best practices into the **one** place the plugin calls the Anthropic API directly: tier-1 contextual-prefix generation in `scripts/contextual-prefix.py`. Verified by full-repo sweep that `cache_control` and the Anthropic API surface exist nowhere else (incl. `claude-canvas/`). No change to retrieval output — API payload shape + observability only.
### Changed
- **Cache only above the Haiku floor** (`scripts/contextual-prefix.py`). The page-body `cache_control` marker is now attached only when the body clears the Haiku 4.5 minimum cacheable size (`HAIKU_CACHE_MIN_CHARS = 16384`, ~4096 tokens × 4 chars/token). Below the floor the Anthropic API silently ignores the marker, so the prior unconditional marker was a no-op that misled the reader. Extracted as the pure, unit-tested `cache_control_for()`.
- `.claude-plugin/plugin.json` + `marketplace.json` version 1.9.1 → 1.9.2.
### Added
- **Cache telemetry** (`scripts/contextual-prefix.py`). The tier-1 path now logs `cache: wrote=<N> read=<N> tok` from the response `usage` fields — integers only, never page content, preserving the v1.7.1 data-egress posture. Implements the docs' "monitor cache hit rates" guidance and reveals whether the body cache is actually firing given the floor.
- **Sequential-invariant note** at the chunk loop (`process_page`), documenting that cache reads depend on chunk 0's response landing before chunk 1 is sent (Anthropic prompt-caching concurrency rule). Guards against a future parallelization silently zeroing every cache read.
- `tests/test_contextual_prefix.py` — hermetic coverage of the `cache_control_for()` floor decision (below / at / above floor, empty body, floor constant matches the documented Haiku minimum). Wired into `make test` (now 9 suites) + `make test-contextual`.
### Documented
- Tier-1 "prompt-cached" claim now states the ~16 KB Haiku floor in the `contextual-prefix.py` module docstring and the ingest diagrams in `docs/compound-vault-guide.md` and `skills/wiki-retrieve/SKILL.md`, so docs match runtime behavior.
### Fixed
- **Explicit missing or out-of-vault page paths now fail cleanly** (`scripts/contextual-prefix.py`). A single explicit path that does not exist exits 3 (was: silent exit 0, swallowed by the `is_file()` filter in `main()`); a path resolving outside the vault exits 2 with a message (was: a raw `ValueError` traceback from `relative_to()`). `--all` runs are unaffected.
- Removed the dead `EXIT_NO_ADDRESS` (exit 5) constant and its docstring entry — it was defined and documented but never raised. Renamed the shadowed `prefix` progress-label variable in `process_page` to `progress`.
### Verification
- `make test`: 9 hermetic suites green; the contextual suite now mocks the API payload to assert `cache_control` attaches only above the floor and the model reply is truncated to one line. No regressions.
- Explicit-path exit codes confirmed by run: missing in-vault path → 3, out-of-vault path → 2, valid page → 0, `--all` unaffected.
- `python3 scripts/contextual-prefix.py wiki/getting-started.md --peek`: tier selection unchanged (synthetic without `--allow-egress`).
- Live tier-1 telemetry leg (sends page bodies off-machine) requires explicit `--allow-egress` and is left to operator verification per the repo's consent design.
## [1.9.1] - 2026-05-18 (v1.9.0 audit hardening)
Patch release closing **6 of 6 remaining HIGH/MEDIUM** findings from the v1.9.0 pre-public-promotion audit ([`docs/audits/v1.9.0-pre-public-promotion-audit-2026-05-18.md`](docs/audits/v1.9.0-pre-public-promotion-audit-2026-05-18.md)) plus 3 LOW hardening items. Composite score moves from 91.6 to ~94 raw average. Public-promotion ship verdict remains GREEN.
### Fixed (defensive hardening)
- **H4 — stale-lock reaper wiring** (`hooks/hooks.json`). New SessionStart command runs `bash scripts/wiki-lock.sh clear-stale --max-age 3600` at every session resume/startup. Locks orphaned by a crashed batch ingest get reaped automatically on the next session, not just on operator demand.
- **S2 — opt-out gate for PostToolUse auto-commit** (`hooks/hooks.json`). Hook now exits early if `.vault-meta/auto-commit.disabled` exists. Default behavior unchanged for existing users; per-vault opt-out is one `touch` away. Useful for shared repos, CI runs, or any scenario where the operator wants to commit manually.
- **Data M3 — symlink canonicalization** (`scripts/wiki-lock.sh:110-142`). `validate_path()` previously rejected literal `..` segments but did not canonicalize symlinks. A symlink inside `wiki/` resolving outside `VAULT_ROOT` could escape. Now resolves via `python3 os.path.realpath` and rejects any path whose canonical form is outside `commonpath(VAULT_ROOT, target)`. Cross-platform (GNU coreutils + macOS BSD); no realpath flag dependency.
- **Data M4 — `.vault-meta/locks/.gitkeep`** + gitignore pattern change. `.vault-meta/locks/*` (not the dir itself) is now ignored; `.gitkeep` is whitelisted so the directory ships on fresh clone. Runtime behavior unchanged (`ensure_dirs()` always created it lazily); this just removes the first-acquire side-effect on directory presence.
- **Data M1 — rerank.py warning routes to hook.log** (`scripts/rerank.py:158-176`). When the embed-cache lock is unavailable after 3 tries, the WARN line now also appends to `.vault-meta/hook.log` with a timestamp, so users see the event via `wiki lint` or by tailing the log. stderr-only was invisible to most callers.
- **S4 — ollama-localhost assert** (`bin/setup-retrieve.sh:97-117`). If `OLLAMA_URL` env var points off-localhost, refuse to probe unless `--allow-remote-ollama` is passed. Mirrors the existing `scripts/tiling-check.py:351` gate. Closes a defense-in-depth gap where a malicious env var could redirect probes to an attacker-controlled endpoint.
### Documented
- **H2 + S3 — multi-tenant threat model** (`SECURITY.md`). New "Threat model: single-tenant vault" section documents three intentional design choices (cross-process lock release, auto-commit hook scope, filesystem-permission trust boundary) and the mitigations for shared-host deployments. Closes the documentation gap the audit flagged: the design choices are correct for single-tenant, but operators in shared-host scenarios deserve to know what changes for them.
### Changed
- `.claude-plugin/plugin.json` + `marketplace.json` version 1.9.0 → 1.9.1.
### Deferred to v1.9.2 (not in this release)
- **Data M2 — embed-cache + chunk-orphan GC**: new `--gc` subcommand for `bm25-index.py` + `contextual-prefix.py` that prunes entries whose backing pages no longer exist. Requires hermetic tests covering edge cases; scoped for a dedicated release.
- **W1 — wiki/meta/ release-session note relocation**: 12 files with incoming wikilinks need a coordinated graph update. Bundled with References M1+M2 (17 dead wikilink targets) for one-pass wiki cleanup.
- **Security S1 — Excalidraw checksum pin**: requires upstream release hash verification + maintenance burden for future releases. Tracked separately.
- **GROW note from v1.9.0 audit — 7th always-check cut in `agents/verifier.md`**: cross-file string consistency check (catches the class of bug where a single-file fix should have been a multi-file cascade, like the 548d294 + F1-F5 sequence).
### Verification
- `make test`: 8 hermetic suites green (~1234 assertions). No regressions.
- Live symlink-escape test on `wiki-lock.sh validate_path`: `ln -s /tmp/foo ./escape && bash scripts/wiki-lock.sh acquire escape/x.md` correctly rejects with "path resolves outside vault via symlink".
- Both plugin manifests parse as JSON.
- Pre-push verifier agent dispatch on staged diff: SHIP, 0 BLOCKER, 0 HIGH.
## [1.9.0] - 2026-05-18 (10-principle thinking framework)
Minor release adding the **10-principle thinking framework** as a first-class layer of the plugin. The framework (OBSERVE-OBSERVE-LISTEN-THINK-CONNECT-CONNECT-FEEL-ACCEPT-CREATE-GROW) is integrated at three levels: as the new `/think` skill, as an appendix on every existing SKILL.md, and as the methodology spine for the v1.8.0 pre-push audit (which used it as its phase structure). Skill count: 14 → **15**.
### Added
- **`skills/think/SKILL.md`** — new skill #15. Canonical source for the 10-principle thinking loop. Walks Claude through external observation, metacognition (the often-skipped one), active listening, first-principles analysis (six-cut kernel lives here), lateral connection, system orchestration, intuition, intellectual humility (anti-sycophancy enforcement), generative output, and iterative growth. Triggers on "think this through", "10-principle review", "/think", "OBSERVE LISTEN THINK", "deep think", "systematic thinking", "structured reasoning". Allowed tools: `Read, Grep, Glob, Bash` (read-only; loads structure and discipline, not mutation). ~250 lines covering the 10 principles with one-paragraph definitions, when-to-invoke guidance, stage-by-stage prompts (5 questions per stage), anti-patterns (the common skips), and composition notes with `/best-practices`, `/save`, `/wiki-lint`, `agents/verifier.md`, and `/autoresearch`.
### Changed
- **All 14 existing SKILL.md files** — each receives a unique "## How to think (10-principle mapping)" appendix at the end. Per-skill 10-row table mapping each principle to that skill's specific work. Examples: wiki-mode's OBSERVE-internal is "audit the assumption that mode=generic is the default"; autoresearch's OBSERVE-internal is "am I steering the search toward what I already expect to find? confirmation bias kills research"; wiki-lint's FEEL is "a lint report should empower, not shame". Each appendix is non-trivial and skill-specific (not a template stub), satisfying the plan's verification rule #12.
- **`.claude-plugin/plugin.json` + `marketplace.json`** — version 1.8.2 → 1.9.0. Description unchanged (the framework is additive to the v1.8.0 + v1.8.2 surface).
### Repo hygiene (first-public-release prep)
- **`CONTRIBUTING.md`** (NEW) — workflow, six-cut self-review checklist, commit conventions, test requirements.
- **`CODE_OF_CONDUCT.md`** (NEW) — adopts Contributor Covenant v2.1 by reference.
- **`SECURITY.md`** (NEW) — private disclosure policy, response SLA, scope, credit policy.
- **`.github/ISSUE_TEMPLATE/bug_report.md`** (NEW) — reproduction + environment + skill-affected fields.
- **`.github/ISSUE_TEMPLATE/feature_request.md`** (NEW) — scope + compatibility + testing fields.
- **`.github/pull_request_template.md`** (NEW) — six-cut self-review + test results + verifier verdict + CHANGELOG reminder.
- **`.github/workflows/test.yml`** (NEW) — CI runs `make test` on push/PR + validates SKILL.md frontmatter + agents `tools:` declaration + plugin manifest JSON validity.
- **Manifest + doc URL updates** — `plugin.json`, `marketplace.json`, and 18 other files updated to reference the canonical repository URL.
### Why this release
Per the v1.8.0 pre-push audit's GROW notes (`docs/audits/v1.8.0-pre-push-audit-2026-05-18.md` §10), the 10-principle framework proved its value as the AUDIT's mental spine — OBSERVE-internal forced explicit bias documentation; GROW forced a feedback-loop section. Shipping it as a first-class skill (and as appendices on all existing skills) makes the discipline available to every future invocation, not just to one-off audits. This is the GROW step of the audit itself, embodied in code.
### Compass axis status after v1.9.0
| Axis | v1.8.0 | v1.9.0 |
|---|---|---|
| Compounding wiki primitive | #1 | #1 |
| Multi-writer safety | #1 | #1 |
| Retrieval architecture (free tier) | #1 | #1 |
| License / openness | #1 | #1 |
| Methodology support | #1 | #1 (deepened by framework integration) |
| Derivative outputs | NO | NO (v2.0 scope) |
| GUI / install ergonomics | NO | NO (v2.5+ scope) |
5 of 7 axes #1 (unchanged count, but methodology axis deepens — claude-obsidian is now the only Claude+Obsidian plugin shipping methodology modes AND a first-class thinking-loop framework AND per-skill thinking guidance).
### Migration notes
- v1.8.x vaults: no action needed. All existing skills work identically; the "How to think" appendix is additive guidance, not behavioral change.
- `/think` is invocable from any project that has this plugin installed; it does not require a vault.
- The framework can be invoked explicitly (`/think <problem>`) or applied implicitly when an existing skill's appendix references it.
### Composition
- `/think` + `/save` = the canonical compounding loop. Apply the 10 principles to a problem; when done, save the insights worth not re-deriving.
- `/think` + `/best-practices` = engineering discipline at the THINK stage of the framework. The six-cut kernel IS the inside of stage 4.
- `/think` + `agents/verifier.md` = an OBSERVE-internal substitute for solo work — fresh-context reviewer that catches biases the chair missed.
## [1.8.2] - 2026-05-18 (pre-push audit closure)
Patch release closing **all 4 HIGH findings + 1 leaked BLOCKER-class hardening** from the v1.8.0 pre-push audit. Audit: [`docs/audits/v1.8.0-pre-push-audit-2026-05-18.md`](docs/audits/v1.8.0-pre-push-audit-2026-05-18.md). Per the audit's strict push gate (any BLOCKER halts), v1.8.2 takes the release from YELLOW (0 BLOCKER / 4 HIGH) to GREEN (0 BLOCKER / 0 HIGH).
### Fixed
- **H1 — `scripts/detect-transport.sh`: implement `manual_override`.** The documented escape hatch for MCP-only users (`"manual_override": true` in `transport.json`) was documented at `wiki/references/transport-fallback.md` but never honored by the script. v1.8.2 parses the existing snapshot BEFORE auto-detection; when `manual_override: true`, the user's `preferred` and `fallback_chain` are preserved across normal cycles AND `--force` refreshes. Auto-detection still runs to refresh the `available.cli.*` informational fields. Field round-trips through the JSON schema (new `"manual_override": <bool>` in every snapshot). Verified by smoke test: a pinned `"preferred": "mcp-obsidian"` survived a `--force` refresh.
- **H2 — `agents/wiki-ingest.md`: add `Bash` to tools + add `## Mode awareness (v1.8+)` section.** The parallel batch-ingest sub-agent was missing `Bash` in its `tools:` frontmatter despite mandating `bash scripts/wiki-lock.sh acquire/release` in its body — this defeated the v1.7 multi-writer safety guarantee. Sub-agent was also missing v1.8 mode-routing awareness, so batch-ingest in LYT/PARA/Zettelkasten vaults filed to v1.7 generic paths. Both gaps closed.
- **H3 — `skills/autoresearch/SKILL.md`: new `## Web egress hygiene (v1.8.2+)` section.** Four sub-policies: (1) URL validation (reject `file://`, `javascript:`, RFC1918), (2) content sanitization before write (strip `<script>`/`<iframe>`, escape `[[/]]` injections, reject embedded frontmatter delimiters, 50KB truncation), (3) per-loop cost expectation (~45 WebFetch calls per run), (4) failure mode (log to `wiki/log.md`, never silently swallow). The router's `safe_name()` is the FILENAME guard; this is the BODY-content guard.
- **H4 — `skills/save/SKILL.md`: prepend `Step 0: Decide the destination root`.** The skill body assumed project-local `wiki/` filing while the global `~/.claude/CLAUDE.md` `/save` rule mandates personal-vault filing for cross-project saves. Step 0 makes the routing decision explicit with three rules in order: user-explicit override → project/global CLAUDE.md `/save` override → project-local `wiki/` default. Path sanitization still applies regardless of destination root. Workflow step 5 now requires a collision check (ASK before overwrite).
- **wiki/references/transport-fallback.md:** §Manual override now describes the actual v1.8.2 behavior (preserves `preferred`+`fallback_chain` while still refreshing `available.cli.*`; field round-trips through snapshot).
### Changed
- **`.claude-plugin/plugin.json` + `marketplace.json`:** version 1.8.0 → 1.8.2.
- **`docs/audits/v1.8.0-pre-push-audit-2026-05-18.md`** (NEW): full pre-push audit report. 14 per-skill scores (average 84.6/100), per-tier finding ledger, v1.8.2 fix replay, hook safety, manifest accuracy, push-gate decision tree. Audit methodology used the 10-principle thinking spine (OBSERVE-OBSERVE-LISTEN-THINK-CONNECT-CONNECT-FEEL-ACCEPT-CREATE-GROW) as audit phases.
### Verification
- All 8 hermetic test suites green (~1234 assertions): allocate-address, tiling-check, boundary-score, bm25-index, retrieve, wiki-lock, concurrent-write, wiki-mode.
- v1.8.2 manual_override smoke test: pinned `preferred="mcp-obsidian"` survived `--force` refresh.
- All path-traversal attack vectors against `route_path()` confirmed sanitized inside vault root (6 dedicated assertions).
- mkstemp atomic write yields 0600 perms (verified via `stat`).
- `--mode` preview is non-mutating (verified via mtime).
### Known issues at v1.8.2 (deferred to v1.9 or later)
The pre-push audit also surfaced 14 MEDIUM and 45 LOW findings. None block release. Triage in audit §12. Highlights:
- `wiki-cli` mcp-obsidian + mcpvault tiers documented as fallback positions 2/3 but unreachable from auto-detection (deferred — manual_override is the workaround until MCP auto-detect ships in v1.7.x patch).
- No `tests/test_detect_transport.sh` yet for v1.7+ transport script (planned v1.8.3).
- 7 skills declare incomplete `allowed-tools` lists (missing `Bash` despite shelling out) — works in practice (harness default-allows), but convention violation (planned v1.8.3).
- `tests/__init__.py` missing → `python3 -m unittest tests.X` form fails (direct invocation works) (planned v1.8.3).
- Various doc/reality drift around the v1.8 ID format change (14-digit → 20-digit), v1.8 `--mode` flag not yet referenced in consumer docs (deferred LOW polish PR).
## [1.8.0] - 2026-05-17 (methodology modes — closes compass priority gap 5)
Minor release closing the **5th and final priority gap** from the May 2026 compass artifact: methodology support. Adds the **`wiki-mode`** skill with first-class support for four organizational styles (LYT / PARA / Zettelkasten / Generic). After this release, claude-obsidian is **#1 on 5 of 7 axes per compass framework** — up from 4/7 in v1.7. Full guide: [`docs/methodology-modes-guide.md`](docs/methodology-modes-guide.md).
### Added
- **`skills/wiki-mode/SKILL.md`** — new skill (skill #14). Reads `.vault-meta/mode.json`; defaults to `generic` (v1.7 behavior) when absent. Triggers on "set vault mode", "switch to PARA", "use LYT", "zettelkasten setup", etc.
- **`scripts/wiki-mode.py`** — pure-stdlib router + config helper. Subcommands: `get` (current mode), `config` (full JSON), `route <type> <name>` (suggested path), `set <mode>` (write mode.json), `id` (mint Zettel timestamp ID), `templates` (list per-mode templates).
- **`bin/setup-mode.sh`** — interactive setup with `--mode <name>` non-interactive flag. Idempotent; safe to re-run to switch modes. Optionally seeds template folders for the chosen mode.
- **6 per-mode templates** under `skills/wiki-mode/templates/`:
- `lyt/moc-template.md`, `lyt/atomic-template.md`
- `para/project-template.md`, `para/area-template.md`, `para/resource-template.md`
- `zettel/atomic-template.md`
- **`tests/test_wiki_mode.py`** — hermetic test suite (15 assertions covering load/save round-trip, all 4 modes' routing, slugify Unicode handling, Zettel ID format, corrupted-config fallback, CLI subprocess paths). `make test` is now **8 suites**.
- **`docs/methodology-modes-guide.md`** — narrative guide; when-to-use-which decision tree per mode; full schema documentation; migration guidance.
### Changed
- **`skills/wiki-ingest/SKILL.md`** — new "## Mode awareness (v1.8+)" section. Skill now consults `wiki-mode.py route` before filing new source/entity/concept pages. mode=generic preserves v1.7 behavior byte-for-byte.
- **`skills/save/SKILL.md`** — new "## Mode awareness (v1.8+)" section. Session notes now route per active mode (with explicit note that the global `~/Documents/Obsidian Vault/sessions/` rule still applies to cross-project saves).
- **`skills/autoresearch/SKILL.md`** — new "## Mode awareness (v1.8+)" section. Research synthesis output routes per active mode; every new entity/concept page in the research loop also routes via the router.
- **`Makefile`** — new `test-mode` and `setup-mode` targets; `test` aggregate now runs 8 suites; `clean-test-state` removes `.vault-meta/mode.json` + `.vault-meta/hook.log`.
- **`.gitignore`** — `.vault-meta/mode.json` + `.vault-meta/mode.*.tmp` added. Host-specific runtime config by default; `git add -f` to commit if the user wants the mode choice to follow the repo.
- **`CLAUDE.md`** — new "## Methodology Modes (v1.8+)" section + skill table row + plugin-name header reference.
- **`.claude-plugin/plugin.json` + `marketplace.json`** — version 1.7.2 → 1.8.0; descriptions refreshed to mention methodology modes and "5 of 5 priority gaps closed."
### Compass axis status after v1.8.0
| Axis (compass §1 + audit §9) | v1.7.2 | v1.8.0 |
|---|---|---|
| Compounding wiki primitive | #1 | #1 |
| Multi-writer safety | #1 | #1 |
| Retrieval architecture (free tier) | #1 | #1 |
| License / openness | #1 | #1 |
| **Methodology support** | TIE | **#1 ← v1.8.0** |
| Derivative outputs | NO | NO (v2.0 scope) |
| GUI / install ergonomics | NO | NO (v2.5+ scope) |
**5 of 7 axes #1.** Remaining 2 axes require multi-release effort.
### Honest scope acknowledgment
- "Best ever obsidian skill per the priority research" = the 5 priority gaps closed. **v1.8.0 reaches this milestone.**
- "Best ever in the market" requires **v2.0** (derive — audio/quiz/study) + **v2.5+** (Community Plugin GUI fork). These are weeks of work per release; v1.8.0 is the foundation, not the substitute.
- The v1.7.0 audit's 24 findings (1 BLOCKER + 6 HIGH + 10 MEDIUM + 7 LOW) are fully CLOSED-or-DEFERRED as of v1.7.2; v1.8.0 inherits a clean audit ledger.
### Migration notes
- v1.7.x vaults: no action needed. `wiki-mode` defaults to `generic` when `.vault-meta/mode.json` is absent, which is identical to v1.7 behavior. The mode-awareness sections in wiki-ingest/save/autoresearch are no-ops in generic mode.
- To adopt a non-default mode: `bash bin/setup-mode.sh` (or `bash bin/setup-mode.sh --mode <name>`). Interactive prompt picks one of LYT/PARA/Zettelkasten/Generic; optionally seeds template folders.
- Switching modes does NOT auto-migrate existing files. Manual migration via file manager or `git mv`.
## [1.7.2] - 2026-05-17 (SSS+ convergence — closes every audit finding)
Patch release closing **every remaining MEDIUM (M1-M10) and LOW (L1-L7) finding** from the v1.7.0 audit. Combined with v1.7.1 (which closed the 1 BLOCKER + 6 HIGH), this means the v1.7.0 audit's full 24-finding ledger is now CLOSED or formally DEFERRED-with-rationale. Plan: [`docs/audits/v1.7.2-sss-plus-plan.md`](docs/audits/v1.7.2-sss-plus-plan.md).
### Added
- **`agents/verifier.md` two new "always check" cuts** (`83d18ea`): #5 Git hygiene (any new written path NOT in .gitignore → HIGH; would have caught the v1.7.1 `.vault-meta/hook.log` self-pollution bug mechanically); #6 Additive-without-pruning (if `git diff --shortstat main..HEAD` shows >+500 LOC with <50 deletions, flag MEDIUM). Verifier learns from what its prior dispatches missed.
- **Unicode multilingual test** (`8c219fb`): `tests/test_bm25_index.py::test_tokenize_unicode_multilingual` covers Cyrillic, CJK, accented Latin, pure-emoji skip, mixed ASCII+non-ASCII.
- **`--explain` + `--no-rerank` test coverage** (`a80ae61`): `tests/test_retrieve.py` now exercises both flags hermetically (test_explain_flag_adds_diagnostics_block + test_no_rerank_flag_strategy_bm25_only). +6 assertions.
- **Newline + carriage-return path rejection tests** (`d0db354`): `tests/test_wiki_lock.sh` +2 assertions for the new validate_path rejections.
- **`[i/total]` progress prefix on contextual-prefix.py per-page logs** (`59cd7c8`): Stage 1 over 47 pages with tier-2 (claude-cli) takes 5+ min; user now sees position, not just opaque per-page lines.
### Changed (defensive-input + correctness fixes)
- **M2 — Unicode-aware BM25 tokenizer** (`8c219fb`): `[A-Za-z][A-Za-z0-9'\-]*``\w[\w'\-]*` with `re.UNICODE`. CJK / Cyrillic / Devanagari / accented Latin vaults previously had content silently dropped at index time. Same regex mirrored in `scripts/baseline-v16.py` so v1.6/v1.7 retrieval comparisons stay apples-to-apples for non-ASCII corpora.
- **M3 — `scripts/rerank.py` off-localhost error rewritten** (`d0db354`): from "pass --allow-remote-ollama" to a 3-option breakdown citing the actual host, concrete recovery steps for each option, and the canonical paths.
- **M4 — `scripts/wiki-lock.sh validate_path()` rejects newlines + carriage returns** (`d0db354`): would have silently corrupted the meta-lock line format.
- **M5 — `scripts/retrieve.py import_sibling()` wraps in try/except** (`d0db354`): ImportError / SyntaxError / AttributeError now produce friendly diagnostics with recovery hints (`bash bin/setup-retrieve.sh --check`) instead of bare Python tracebacks. Pre-checks target file existence.
- **M6 — `scripts/contextual-prefix.py` empty-body WARN** (`d0db354`): frontmatter-only pages now produce explicit `WARN: ... has no chunkable body content` instead of silent `chunks=0`.
- **M7 — `scripts/rerank.py save_cache()` non-blocking lock + 3-attempt retry** (`d0db354`): replaces blocking `fcntl.LOCK_EX` (no timeout) with `LOCK_NB` + 100ms retry; falls back to unlocked write with WARN if all 3 attempts fail. Prevents indefinite hang on NFS / FUSE mounts without lock support. The temp+rename pattern provides write atomicity even unlocked.
- **L2 — Stage 1 progress indicator** (`59cd7c8`): per-page log lines now carry `[i/total]` prefix.
- **L6 — `scripts/wiki-lock.sh` header doc** (`59cd7c8`): explicit distinction between `STALE_AFTER_SEC` (per-acquire, default 60s) and `clear-stale --max-age` (admin reaper, default 3600s). Both time-since-acquire but distinct scopes.
- **L7 — `scripts/bm25-index.py` BM25 divide guard** (`59cd7c8`): `avg_dl_safe = avg_dl or 1.0`. Currently unreachable but invariant-by-construction now rather than emergent-from-reachability.
### Removed (kernel: "delete more than you add")
- **L3 — dead `bm25_score()` function** (`eafd449`): 28 lines, never called, self-documented as "placeholder; real score computed in query()". Removed.
- **L4 — `--rebuild` flag stub** on `scripts/bm25-index.py build` (`eafd449`): declared but never read; was reserved for incremental mode not in v1.7. Per kernel: no abstraction without 3 real callers.
- **L5 — `--no-bm25` flag stub** on `scripts/retrieve.py` (`eafd449`): returned `EXIT_USAGE` with "reserved for v1.7.x vector-only mode." Same kernel principle.
### Honest accounting
- **Net LOC delta `main..HEAD` is `+6009 / -30`**, NOT meeting the plan §1 acceptance criterion (`≤+5000 OR ≥-200`). Per plan §4 failure clause: "Do not invent prunes to game the metric." Honest decomposition: ~5500 LOC across new files alone (4 new scripts + 4 new tests + 2 new skills + 1 new agent + 1 new bin + ~2200 LOC docs). The v1.7 line was net-new feature substrate, not a refactor; v1.6 had no equivalent of a retrieval pipeline, lock primitive, transport detector, or contextual prefix generator to delete. **Kernel-application axis ceilings at ~92-95** for the v1.7 line; the deduction is structural to building substrate, not negligence. Documented in `docs/audits/v1.7.0-audit-2026-05-17.md` §10.2.
- **M9 (bounded-slices: 4 skills touched by both §3.2 and §3.4)** is documented as a process note in audit §10.3; not a code-level fix.
- **M11 (synonym category benchmark tied 60%/60%)** persists post-tokenizer-change; v1.7 hybrid neither helps nor hurts on this category. Filed for v1.7.x rerank threshold tuning.
- **M12 (negative-query precision)** was tied at 40%/40% in v1.7.0; post-Unicode-tokenizer it's 40%/20% (+20pp). Empirically closed by the tokenizer change.
- **L1 (§3.1 substrate +17/-5 no deletion)** documented as defensible process note in audit §10.3.
### Benchmark refresh (full 50-query corpus, v1.7.2 measurement)
Run via `python3 scripts/benchmark-runner.py`:
| Category | N | v17 top-1 | v17 top-5 | v16 top-1 | v16 top-5 | Δ top-1 |
|---|---|---|---|---|---|---|
| cross-page | 10 | 20.0% | 80.0% | 30.0% | 40.0% | -10.0pp |
| derived | 25 | 68.0% | 88.0% | 12.0% | 24.0% | +56.0pp |
| negative | 5 | 40.0% | 80.0% | 20.0% | 80.0% | +20.0pp |
| partial-recall | 5 | 60.0% | 100.0% | 20.0% | 60.0% | +40.0pp |
| synonym | 5 | 60.0% | 100.0% | 60.0% | 100.0% | +0.0pp |
| **TOTAL** | **50** | **54.0%** | **88.0%** | **22.0%** | **44.0%** | **+32.0pp** |
**Error reduction: +41.0%** vs the ≥30% ship gate — PASS.
The +32pp/41% slightly beats the v1.7.0 audit's reported +30pp/+39.5%; the Unicode tokenizer change made baseline-v16.py marginally weaker (2pp on its top-1) which counts as a delta improvement for v1.7. cross-page top-1 regression vs audit (was +0pp, now 10pp) is within benchmark noise on N=10; cross-page top-5 stayed at 80% which is the synthesis-relevant metric.
### Migration notes
- v1.7.1 vaults: no action needed. All v1.7.2 changes are backward-compatible behavior improvements.
- Re-running `bash bin/setup-retrieve.sh` is recommended (rebuilds BM25 index with the new Unicode tokenizer; multilingual content that was silently dropped will now be indexed).
- The v1.7.0 audit's full 24-finding ledger is now CLOSED-or-formally-DEFERRED. v1.7.2 marks the end of the v1.7 line's audit-debt remediation cycle.
## [1.7.1] - 2026-05-17 (audit-driven patch)
Patch release closing the 1 BLOCKER + 6 HIGH findings from the v1.7.0 audit ([`docs/audits/v1.7.0-audit-2026-05-17.md`](docs/audits/v1.7.0-audit-2026-05-17.md)). All v1.7.0 features remain available; the changes are guard-rails and one new agent.
### Fixed
- **BLOCKER B1 — Data egress without consent** (`ca68bb6`). `scripts/contextual-prefix.py` now requires an explicit `--allow-egress` flag (default off) before selecting tier-1 (Anthropic API) or tier-2 (claude CLI subprocess). Without the flag, `pick_prefix_tier()` returns `"synthetic"` regardless of `ANTHROPIC_API_KEY` or `claude` binary presence. `bin/setup-retrieve.sh` adds a y/N consent prompt before any non-synthetic Stage 1 run. `skills/wiki-retrieve/SKILL.md` adds a Data Privacy callout (also closes H6). Mirror of the existing `scripts/tiling-check.py:351` `--allow-remote-ollama` precedent.
- **H1 — Stage 1 failure had no rollback path** (`4837d4f`). `bin/setup-retrieve.sh` now captures Stage 1's exit code, exits 5 on non-zero, and prints a 3-option recovery hint (incremental resume, full wipe, single-page re-process). Stage 2 only runs after Stage 1 success.
- **H2 — `make clean-test-state` didn't remove v1.7 artifacts** (`7e1f187`). Extended the target to remove `.vault-meta/chunks/`, `.vault-meta/bm25/`, `.vault-meta/locks/`, `.vault-meta/transport.json`, `.vault-meta/.wiki-lock.meta`, and the related `.tmp` files. The Makefile target now matches the v1.7 `.gitignore` set.
- **H3 — PostToolUse hook swallowed lock-check errors** (`7120970`). Restructured `hooks/hooks.json` to capture the wiki-lock script's exit code directly (not via a pipeline), defer the auto-commit on any non-zero rc, and only run `git add` after both the rc check and the non-empty-list check pass.
- **H4 — No verifier-agent pass at workstream gates** (`3ea443f`). Added `agents/verifier.md` — a read-only (`Read`/`Grep`/`Glob`/`Bash` only; no `Write`/`Edit`) pre-commit specialist that reads the staged diff, applies the /best-practices six-cut + agent kernel, and returns findings in four tiers (BLOCKER/HIGH/MEDIUM/LOW). CLAUDE.md "Pre-commit verifier (v1.7.1+)" section references it as the recommended pre-commit step.
- **H5 — `detect-transport.sh` JSON escaping was shell-only** (`722ac97`). Added a `json_escape()` helper that pipes through `python3 -c json.dumps`, applied to `CLI_VERSION` (both `obsidian-cli` and `obsidian`-binary paths). The heredoc now emits `${CLI_VERSION}` without surrounding quotes since the helper produces a pre-quoted JSON string. Defense in depth against pathological upstream version output (backslashes, tabs, newlines, control chars).
- **H6 — `skills/wiki-retrieve/SKILL.md` had no Data Privacy section** (bundled with B1 in `ca68bb6`). New section at the top of the skill body documents the two-layer egress guard (`--allow-egress` flag + setup-retrieve prompt) and points back to the `tiling-check.py` precedent.
### Added
- `agents/verifier.md` — pre-commit specialist; see H4 above.
- `scripts/baseline-v16.py` + `scripts/benchmark-runner.py` — audit instrumentation that ran the 50-query retrieval benchmark documented in `wiki/meta/retrieval-benchmark-v1.7.md`. Result: v1.7 top-1 54.0% vs v1.6 baseline 24.0% (+30pp); error reduction +39.5% vs the ≥30% gate. Future audits can re-run with `python3 scripts/benchmark-runner.py`.
- `docs/audits/v1.7.0-audit-2026-05-17.md` (481 lines) — the full audit report.
- `docs/audits/v1.7.1-fixes-plan.md` — the sequenced 6-commit roadmap this release executes.
### Changed
- Versions bumped to 1.7.1 in `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json` (description fields refreshed to mention the verifier agent and the egress-consent guard).
- `CLAUDE.md` "Pre-commit verifier (v1.7.1+)" section added; "Concurrency (v1.7+)" section retained verbatim.
### Polish (post-fix self-audit refinements)
After the 7 v1.7.1 commits landed, a re-pass with `agents/verifier.md` against the slice surfaced 2 MEDIUM + 3 LOW polish items. All closed in one follow-up commit:
- `scripts/detect-transport.sh` — split `CLI_VERSION_RAW` (human log line) from `CLI_VERSION` (pre-quoted JSON for the heredoc). The JSON-escape fix from H5 made the log line `CLI: obsidian-cli ("1.12.0")` carry visible quotes; the split keeps both paths clean. **Also**: `CLI_VERSION_RAW=""` initialized in the top-of-script init block alongside `CLI_PRESENT`, `CLI_BINARY`, `CLI_VERSION` (defense in depth under `set -euo pipefail`; closes a latent unbound-variable risk that worked today only by bash short-circuit semantics).
- `agents/verifier.md``tools:` field kept as CSV (`Read, Grep, Glob, Bash`) to match in-repo precedent (`wiki-ingest.md`, `wiki-lint.md`). Both CSV and YAML list are accepted forms across `~/.claude/agents/` (audit-* use CSV, blog-* and challenge-auditor-* use YAML list), but the three local repo agents are CSV, so consistency-with-siblings is the deciding factor. The polish commit initially converted to YAML list per a prior verifier recommendation that didn't cross-check sibling files; reverted in the follow-up after deeper chair probe.
- `bin/setup-retrieve.sh` — refreshed the header docstring (lines 13-26) to mention `--allow-egress` and the consent gate. Inline comments at line 121 were already correct; the file-top doc was the stale one.
- `scripts/contextual-prefix.py` — docstring on `generate_prefix()` explaining the deliberate asymmetric fallback (api→cli→synthetic, but cli→synthetic only — climbing from cli to api would silently spend money the user did not authorize).
- `hooks/hooks.json` — breadcrumb log to `.vault-meta/hook.log` on the rare non-zero `LOCK_RC` defer path. Verified with concurrency (10 parallel hook fires → 10 atomic lines, no interleaving; line length < `PIPE_BUF`), no format-string injection (printf uses literal format with %s placeholders), filesystem-failure edge cases preserve `exit 0` (defer behavior intact). **`.vault-meta/hook.log`** added to `.gitignore` so the breadcrumb file is never auto-staged by the same PostToolUse hook (closes a self-pollution loop that would have surfaced on the first non-zero `LOCK_RC` event).
### Migration notes
- v1.6 vaults: no action needed. The new components are opt-in or read-only.
- v1.7.0 adopters who provisioned `bin/setup-retrieve.sh` and had `ANTHROPIC_API_KEY` set: the next `bin/setup-retrieve.sh` run will prompt for consent before proceeding with the non-synthetic tier. Decline to keep all data on-machine (tier-3 synthetic), accept to preserve the prior behavior. Existing chunks/ data is unaffected either way.
- Test suite: `make test` continues to run 7 hermetic suites (~1162 assertions). Zero ollama, zero network dependency.
## [1.7.0] - 2026-05-17 (Compound Vault refoundation)
The v1.7 line, codenamed **Compound Vault**, refoundations the plugin around four pillars from the May 2026 gap analysis: substrate alignment with `kepano/obsidian-skills`, Obsidian-CLI-native transport, contextual + hybrid retrieval, and safe multi-writer ingest. v1.6 vaults that never install the new opt-in components see no behavior change. Full design rationale in `docs/compound-vault-guide.md`.
### Added
- **§3.2 Default transport** — `skills/wiki-cli/SKILL.md` (recipe reference for Obsidian CLI), `scripts/detect-transport.sh` (writes `.vault-meta/transport.json` snapshot; auto-stale at 7d), `wiki/references/transport-fallback.md` (canonical decision tree). 5 transport-aware skills (wiki-ingest, wiki-query, save, autoresearch, wiki-lint) gained "## Transport (v1.7+)" sections.
- **§3.3 Hybrid retrieval pipeline (wiki-retrieve, opt-in)** — implements Anthropic's Sept 2024 Contextual Retrieval pattern as agent-skill plumbing. 4 new scripts: `scripts/contextual-prefix.py` (3-tier auto: Anthropic API → claude CLI subprocess → synthetic), `scripts/bm25-index.py` (Okapi BM25, k1=1.5 b=0.75, pure stdlib, flock-guarded), `scripts/rerank.py` (cosine on nomic-embed-text via ollama, embed-cache), `scripts/retrieve.py` (orchestrator: BM25 top-20 → rerank top-5 → page dedupe). `bin/setup-retrieve.sh` opt-in bootstrap. `skills/wiki-retrieve/SKILL.md` documents the architecture and cost ceiling (~$12/1000 docs per Anthropic). Wired into `skills/wiki-query/SKILL.md` via "## Retrieval (v1.7+)" section with graceful exit-10 fallback to the v1.6 hot→index→drill read order.
- **§3.4 Multi-writer safety (wiki-lock, core)** — `scripts/wiki-lock.sh` per-file advisory locking. Age-based staleness (default `STALE_AFTER_SEC=60`), cross-process release allowed by design. 4 skills (wiki-ingest, wiki-fold, save, autoresearch) gained "## Concurrency (v1.7+)" sections with concrete acquire/release recipes. The latent corruption bug from v1.6 — documented but unenforced in `skills/wiki-ingest/SKILL.md:259-264` — is now closed.
- **New skills (2)**: `wiki-cli` (§3.2) and `wiki-retrieve` (§3.3). Total skill count is now 13.
- **New scripts (6)**: `detect-transport.sh`, `contextual-prefix.py`, `bm25-index.py`, `rerank.py`, `retrieve.py`, `wiki-lock.sh`.
- **New tests (4)**: `tests/test_bm25_index.py` (~30 hermetic assertions including BM25 monotonicity and IDF positivity), `tests/test_retrieve.py` (22 hermetic assertions including end-to-end subprocess test), `tests/test_wiki_lock.sh` (14 hermetic assertions including age-based stale reap), `tests/test_concurrent_write.sh` (6 hermetic assertions; the critical multi-writer correctness gate — 10 parallel workers, no losses, no garbled lines). `make test` now runs 7 suites with zero network and zero ollama dependency.
- **New docs**: `docs/compound-vault-guide.md` (omnibus v1.7 guide), `wiki/references/transport-fallback.md` (transport decision tree).
- **Makefile targets**: `test-bm25`, `test-retrieve`, `test-lock`, `test-concurrent`, `setup-retrieve`.
### Changed
- **§3.1 Substrate hard-prefer on `kepano/obsidian-skills`** — `skills/obsidian-markdown/SKILL.md`, `skills/obsidian-bases/SKILL.md`, and `skills/canvas/SKILL.md` upgraded from soft-defer ("if kepano installed, prefer it") to hard-prefer ("this is a fallback; prefer kepano"). Architectural behavior unchanged; signal sharpened. `skills/defuddle/SKILL.md` documented as canonical (kepano does not ship a defuddle skill). `.claude-plugin/marketplace.json` declares `recommendedCompanions: [kepano/obsidian-skills]` with install hint.
- **`hooks/hooks.json` PostToolUse** — added a lock-in-flight check before `git add`. When `bash scripts/wiki-lock.sh list` returns non-zero count, the auto-commit defers. Prevents torn commits during multi-agent ingest. Falls through gracefully if `wiki-lock.sh` is absent.
- **`agents/wiki-ingest.md`** — rewrote the "Sub-agents MUST NOT" section. The prohibition on calling `allocate-address.sh` from sub-agents is preserved (counter monotonicity). A NEW rule is added: sub-agents MAY now write pages, but MUST acquire locks first.
- **Versions** synced to 1.7.0 across `plugin.json` and `marketplace.json`.
### Migration notes
- v1.6 vaults need no action. The new components are opt-in (`bash bin/setup-retrieve.sh` for hybrid retrieval) or universally beneficial (wiki-lock is core; no setup needed). The plugin remains MIT-licensed; no paid tier introduced.
- To install the recommended companion: `claude plugin marketplace add kepano/obsidian-skills`. Existing local fallbacks remain functional without it.
- Estimated upgrade time: 5 minutes (substrate auto-detected; transport auto-detected on first session; retrieval requires explicit `bash bin/setup-retrieve.sh`).
### Out of scope for v1.7 (deferred to v1.8+)
- Methodology modes (LYT / PARA / Zettelkasten / Generic via `wiki-mode` skill) — planned for v1.8.
- NotebookLM-class derivative outputs (audio, quiz, flashcards, study guide via `wiki-derive`) — planned for v1.9 or v2.0.
- Multimodal ingest adapters (YouTube, PDF, EPUB, image OCR via `wiki-ingest-multimodal`) — planned for v1.9.
- Periodic review artifacts (`wiki-review`) — planned for v1.8.
## [1.6.0] - 2026-04-24
### Added (DragonScale Mechanism 4, opt-in)
- **Boundary-first autoresearch**: `scripts/boundary-score.py` computes `(out_degree - in_degree) * recency_weight` across the wikilink graph and emits top-K frontier pages. `/autoresearch` invoked without a topic now offers the top-5 frontier pages as research candidates when the vault has adopted DragonScale.
- `tests/test_boundary_score.py` — 35 unit tests covering frontmatter parsing, recency weight, wikilink extraction (with code-block guard), graph construction, scoring, CLI interface.
- `make test-boundary` target + integration into `make test`.
### Changed
- `skills/autoresearch/SKILL.md` — new Topic Selection section with three paths: explicit (A), boundary-first (B, opt-in), user-ask (C, default without DragonScale).
- `commands/autoresearch.md` — no-topic usage documented for both modes.
- `wiki/concepts/DragonScale Memory.md` — Mechanism 4 flipped from NOT IMPLEMENTED to shipped; exact scoring formula and "what is NOT included" callout added. Version bumped to v0.4.
- Version synced to 1.6.0 across plugin.json and marketplace.json.
## [1.5.1] - 2026-04-24 (Phase 3.6 hardening)
### Fixed
- `scripts/tiling-check.py`: `--report PATH` now resolved against VAULT_ROOT and rejected if it escapes (security: prevents hostile or accidental writes outside the vault).
- `.vault-meta/legacy-pages.txt`: rollout baseline corrected from 2026-04-24 to 2026-04-23 (matches earliest addressed page in the seed vault).
- `AGENTS.md`: wiki-fold listed in the skills table; stale claim that "all skills use only name/description" narrowed to newer skills (older skills still carry allowed-tools for Claude Code compatibility).
- `skills/wiki-ingest/SKILL.md`: resolves the internal contradiction between "immutable .raw/" and "maintain .raw/.manifest.json" — user-dropped source documents remain immutable; only the manifest is wiki-ingest-maintained.
- `docs/install-guide.md`: version 1.2.0 -> 1.5.0 with a DragonScale optional-install callout.
## [1.5.0] - 2026-04-24
### Added (DragonScale Memory extension, opt-in)
- **Mechanism 1 — Fold operator** (`skills/wiki-fold/`): extractive, structurally-idempotent rollups of `wiki/log.md` entries into per-batch meta-pages under `wiki/folds/`. Dry-run via stdout by default (does not trigger PostToolUse auto-commit hook); commit mode explicit.
- **Mechanism 2 — Deterministic page addresses** (opt-in): `scripts/allocate-address.sh` flock-guarded atomic allocator; new `address: c-NNNNNN` frontmatter convention; re-ingest idempotency via `.raw/.manifest.json address_map`. `wiki-ingest` and `wiki-lint` skills feature-detect DragonScale setup.
- **Mechanism 3 — Semantic tiling lint** (opt-in): `scripts/tiling-check.py` uses local `nomic-embed-text` via ollama to flag candidate duplicate pages by cosine similarity. Banded thresholds (error/review/pass) documented as conservative seeds with manual calibration procedure.
- `wiki/concepts/DragonScale Memory.md` — full design spec (v0.3) with four mechanisms, scope boundary, and primary-source citations.
- `bin/setup-dragonscale.sh` — idempotent installer that provisions `.vault-meta/` counter, thresholds, and legacy-pages manifest.
- `tests/` — shell + python test suite for the allocator and tiling-check. Run via `make test`.
- `Makefile` — developer targets (`test`, `setup-dragonscale`, `clean-test-state`).
### Changed
- `hooks/hooks.json` PostToolUse now stages `.vault-meta/` in addition to `wiki/` and `.raw/` so DragonScale runtime state is captured by the auto-commit hook.
- `skills/wiki-ingest/SKILL.md` and `skills/wiki-lint/SKILL.md` gained opt-in DragonScale sections behind feature-detection guards; original behavior unchanged for vaults that have not run `setup-dragonscale.sh`.
- `agents/wiki-ingest.md` explicitly forbids parallel sub-agents from calling the allocator (single-writer rule for address assignment).
- `agents/wiki-lint.md` extended to describe Address Validation and Semantic Tiling checks.
- Stale `allowed-tools` frontmatter removed from `wiki-ingest` and `wiki-lint` SKILL.md (kepano convention: only `name` and `description`).
- Version strings synced across `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, and documentation.
### Security
- `scripts/tiling-check.py` locks `OLLAMA_URL` to localhost by default. Remote endpoints require `--allow-remote-ollama`. Symlinks and vault-root escapes are rejected before any read.
### Not in this release
- **Mechanism 4 — Boundary-first autoresearch**: documented in the spec as a future proposal; no code shipped. `skills/autoresearch/SKILL.md` unchanged.
## [1.4.3] - prior
Previous state. See git log for details.