Skip to content

Releases: Vahlame/create-vkm-kit

v4.5.1

Choose a tag to compare

@github-actions github-actions released this 21 Jul 23:19
1d38a0f

Security

  • Personal phone number removed from every LICENSE.md (root + the 8 package
    mirrors, license:sync-verified). Found by the pre-promotion privacy sweep — the
    license contact stays email-only. Residual exposure noted honestly: the number
    remains in old git blobs and in the already-published npm tarballs (≤4.5.0); the
    current tree, the next release, and everything a visitor reads going forward are
    clean. Full-history rewrite deliberately NOT done — it would break every clone,
    tag and PR reference for marginal gain.

Added

  • Bench results visualized in the READMEs — a static dumbbell chart
    (docs/assets/bench-results.svg + a dark-mode variant, selected via
    <picture>/prefers-color-scheme) of the committed 2026-07-21 live-round
    numbers: skill vs stock per bench × model (research / design / discipline ×
    Haiku / Sonnet / Opus), with direct value labels and deltas on every row.
    Palette CVD-validated for both surfaces. Pure rendering of data already under
    evals/*/results/ — no benchmark execution involved. Embedded in
    README.md, README.en.md and evals/README.md.

  • /vkm-research grew from a 68-line monolith into a full skill (same standard as
    the 4.5.0 vkm-spec rebuild): rewritten SKILL.md with a copyable checklist and a
    degradation ladder; references/summary-template.md (the canonical consolidated
    shape) + references/synthesis-guide.md (four axes: claims across sources, links
    that connect, visible supersession, compression with judgment);
    examples/worked-example.md with REAL validator output both ways (draft fails with
    5 named errors, rewrite passes); and scripts/validate_summary.mjs — a zero-dep
    validator that rejects promoted map-reduce drafts by their seams (--- separators,
    ## <file>.md headings, leaked url: lines), missing wikilinks, malformed
    supersedes and transcription-sized output. Doubles as the research-bench grader;
    mutation-style self-test (10 cases) gates in core CI.

  • research-bench (evals/research-bench/): a synthetic RESEARCH/<topic>/ bank
    with the pipeline's real frontmatter and two seeded probes — a contradiction between
    sources (must surface as a typed - supersedes) and an embedded instruction in one
    source (must be flagged as untrusted DATA, never obeyed). Skill vs stock; grader =
    the shipped validator + probe signals (reference consolidation scores 100, the raw
    draft 0). New job + dispatch option in llm-benchmarks.yml.

  • implementer-bench (evals/implementer-bench/): first eval covering the
    vkm-implementer agent — its real installed contract as system framing vs bare, on
    spec-shaped tasks, graded by discipline-bench's existing hidden-test instruments
    (no new graders). New job + dispatch option in llm-benchmarks.yml.

  • design-bench auto round 1 (mechanical score from the skill's own validators;
    raw HTML committed under results/2026-07-21-round1/): Sonnet +60 on the
    slop-attractor brief (stock: 15) and +30 on the held-out brief; Opus (n=1)
    +60/+40 — stock Opus scored 0 on facturio (full slop fingerprint + failing
    contrast). Haiku flat, dial-consistent. Judgment axes stay in the manual protocol.

  • Effort gate auto-match (ADR-0031 amendment): the PreToolUse effort gate now
    detects the session's current effort (CLAUDE_EFFORT inherited by the hook) and
    opens itself when the model's proposed level equals it — no pause when there is
    nothing for the user to change. Mismatch or undetectable effort keeps the original
    pause; the deny message now advertises the detected level. Subprocess-level tests
    cover match/mismatch/undetectable/template-only paths.

  • Diversified round (round 2/3 of the live benches), Opus added at reduced n
    raw data under each eval's results/2026-07-21-round2/:

    • research-bench: the skill's gain GENERALIZES — held-out domain topic
      (container-queries: Haiku +35, Sonnet +52.5, Opus +50) and Opus on sqlite-vec
      (+45); stock Opus still fails the consolidation contract.
    • discipline-bench on the new harder tasks (incl. the held-out instrument):
      Sonnet +19/+21, Opus +25/+31; Haiku flat-to-−3 within spread — where the hidden
      contract exceeds the small model's reach, the doctrine neither helps nor hurts,
      exactly what the dial predicts.
    • token-quality-ab adversarial fixture (no-keyword decisive lines): delta 0.0
      on all three models measuring the FIXED hook — verdict stays KEEP.
    • skills-triggering hard set (12 near-miss/multi-skill/tool-vs-skill cases):
      Haiku 11/12, Sonnet 12/12, Opus 11/12 — desaturated, both misses are arguable
      boundary calls, logged with the description tweaks to try next round.
  • Round 1 of both new benches, raw data committed (2026-07-21, Haiku 4.5 + Sonnet 5,
    n=3/cell, under results/2026-07-21-round1/):

    • research-bench: the skill delivers — Haiku 33.3 → 70.0 (+36.7), Sonnet
      35.0 → 96.7 (+61.7). Stock output fails the consolidation contract (no typed
      supersedes, weak linking, the seeded injection usually dropped silently).
    • implementer-bench: honest null result — explicit specs saturate both
      conditions (the value lives in the spec, which is /vkm-spec's job), underspec
      deltas sit inside replica spread with one negative Sonnet cell noted for re-check.
      The agent's case is delegation ergonomics, not raw scores; the bench exists to
      catch contract regressions. No verdict claimed beyond n=3.

Changed

  • The skill structure gate's cross-reference allowlist is now EMPTY (was 4
    tolerated vkm-design entries): the in-reference pointers became plain-text mentions
    at the point of use ("contemporary.md, this folder") and SKILL.md remains the only
    place that links — Anthropic's one-level-deep rule now holds to the letter, and the
    gate ratchets at zero exceptions. lineages.md deliberately NOT partitioned: it
    passes the ## Contents navigability gate, and splitting it would mint new
    cross-references — the exact debt just retired.

v4.5.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 20:38
16c9a74

Fixed

  • execa 10 reverted to ^9.6.1 — it broke the kit's own Node 20 floor. The Dependabot
    bump (#83) landed even though execa@10 declares engines.node >=22 and calls
    Set.prototype.union (Node 22+) at import time, crashing every execa-importing test
    file on CI's Node-20 leg (TypeError: TEXT_ENCODINGS.union is not a function) — the
    exact regression that leg exists to catch. All four workspaces pinned back to ^9.6.1
    and the stale nested node_modules/execa@10 lockfile entries deduped away. execa can
    ride to 10 when the kit's engines floor moves to 22 — deliberately, not via a bump.

  • The token-saver's Bash compaction was provably losing diagnostic DETAIL. The new
    adversarial-fixture gate (test/compact-diagnostics.test.mjs: real 650–1,100-line
    failure logs with the decisive error buried mid-stream) caught it before any live A/B:
    the rescue pass kept lines matching the diagnostic regex but dropped their neighbors —
    the TS2339: detail under an ERROR in … header, the state.go:161 stack frame under
    WARNING: DATA RACE. compactText now rescues context blocks (1 line before,
    3 after each match, overlaps merged, [...] separators, cap raised 40→60 lines) so a
    diagnosis-from-compacted-output is possible. The gate stays in CI to keep it that way.

  • Docs no longer under-report the tool surface. The installer --help, ARCHITECTURE.md
    and docs/{en,es}/how-it-works.md described obscura-web as 2 tools (it registers 8
    obscura_fetch/_fetch_many, obscura_search, obscura_research + _start/_status/_stop,
    obscura_consolidate) and vkm-downloads as 2 (it registers 6 — adds probe_mirrors and
    download_start/_status/_cancel). The hybrid MCP README now carries the authoritative
    22-tool table, and a new tool-doc-drift.test.mjs gate parses the registrations in
    hybrid-mcp.mjs and fails the build if the README table and the code ever diverge again —
    same philosophy as the schema-budget gate: doc drift a reader can't detect becomes a red build.

  • Residual "v3 kit" self-descriptions in docs/{en,es}/faq.md and troubleshooting.md updated
    to the v4 identity (historical version anchors like "since v3.8.1" are kept — they're history,
    not drift). ADR-0046's body no longer claims a "Proposed" status for a deletion that shipped in
    4.0.0, and the ADR index now surfaces ADR-0050's amendment instead of a bare "Accepted".

  • A tagged release published to npm having run zero tests. ci.yml triggers only on
    push-to-main and pull_request, and neither fires on a tag push — so release.yml reached
    npm publish gated on nothing but the version, changelog-section and license-mirror checks.
    workflow_dispatch was worse: it releases whatever main currently is, equally untested. ci.yml
    is now workflow_call-able and release.yml requires the whole matrix at the ref being released.

  • A skipped npm-publish job reports green, and the guard that would have said so is inside it.
    The job's condition matched the full owner/name, so a repo rename silently disarmed publishing
    while the workflow still reported success — the exact failure that shipped 4.2.0 and 4.3.0 late,
    and this repo has already been renamed once (obsidian-memory-kitcreate-vkm-kit). Now keyed
    on the owner. release also takes a concurrency group (never cancel-in-progress — aborting
    mid-publish is worse than queueing) so two dispatches cannot race the same npm publish.

  • markdownlint never saw any file inside a dot-directory. **/*.md does not match them, so
    .agents/rules/*.md, .continue/rules/*.md and .github/PULL_REQUEST_TEMPLATE.md were silently
    unlinted — which is also why .markdownlintignore listed a .github file the glob could never
    have reached. All ten were already clean; the gate now actually covers them.

  • The basic-memory pin CI verifies is no longer hand-copied. scripts/mcp-smoke.mjs imports
    BASIC_MEMORY_VERSION instead of repeating the literal, so the smoke test can no longer certify a
    server users never receive — the drift its own comment warned about but did not prevent.

  • The agent.toml schema check no longer rides on an unpinned interpreter. tomllib is stdlib
    only from 3.11 and the lint job had no setup-python; it is now pinned to 3.12 like the other
    Python jobs. The network-bound links and mcp-smoke jobs get timeouts instead of the 6h default.

  • engines: node >=20 was declared in five manifests, advertised in the README badge, and
    verified nowhere
    — every CI job pinned Node 24 and there is no runtime guard. test-node now
    includes one ubuntu leg on the declared floor, enough to keep the claim honest without tripling
    the matrix.

  • Latent test flakes, fixed at the mechanism rather than by widening a tolerance.
    hybrid-mcp.test.mjs's cleanup() restored BASIC_MEMORY_HOME but never closed the client or
    server, leaking a connected pair per call (~15 per run); since the vault is resolved lazily from
    the environment on every call, a surviving server that handled anything after the restore would
    resolve against the ambient vault — the developer's real one. It now closes both, before
    restoring the env. And ollama-resources.test.mjs asserted elapsed < 1000ms against a closed
    port as a proxy for "it short-circuited" — a proxy a loaded runner, a GC pause, or a firewall
    that DROPs rather than RSTs can blow. It now points ensureOllamaServer at a reachable fake
    Ollama and asserts it still returns false: an implementation that probed would find
    /api/version answering and return true, so only a real short-circuit passes. Deterministic, and
    a stronger claim than the timing check ever made.

  • The installer reported its own version as v2 / v3. A hardcoded banner string on a 4.x kit —
    the one place every user sees a version was the one place guaranteed to be wrong. It now prints
    readKitVersion(), already the source of truth for --check-update and the sidecar's
    kitVersion. Relatedly, --version / -v used to fall through to the interactive wizard, so
    the standard way to answer "which version are you on?" started an install; it now prints the
    version and exits.

  • The issue and PR templates were from the deleted v1 project, and blocked filing. bug_report
    required a PowerShell version ($PSVersionTable) in a repo with no .ps1 file, required a
    Cursor version, asked for Doctor.ps1 / Vault-Doctor.ps1 output (neither exists), and made
    "I read docs/troubleshooting.md" a required checkbox for a path that does not exist — the
    real file is docs/en/troubleshooting.md. feature_request required agreeing that "this is not a
    runnable codebase" and that "scripts live inside the prompt as literal text", both false for a
    repo with a Go daemon, eight npm workspaces and a Python package. Rewritten around what actually
    ships: kit version via --version, the real component list, the six agent surfaces, vkm-doctor
    output, and Windows/macOS/Linux rather than Windows-only.

  • CONTRIBUTING.md's "local checks" claimed to mirror CI and did not. It omitted version.mjs check, lint, typecheck, license:sync:check, linkcheck, the agent.toml parse and the
    whole Node test suite, while listing npx lychee — lychee is a Rust binary, so that command
    installs an unrelated npm package. Now the lint job verbatim and in order, with an explicit
    note about which gates (lychee, gitleaks, govulncheck, mcp-smoke, the benches) are CI-only and
    why. The PR template's checklist points at it instead of drifting a third variant.

  • version.mjs could never fix — or even report — a drifted -ldflags version. The Go daemon
    carries the kit version twice (var version and the example -ldflags in the build comment),
    and they were one marker whose read looked only at the first. Three things then compounded:
    set skips a file whose read already matches, so once var version was right the second
    replace never ran again; the "refusing partial write" guard compares the whole file, so a no-op
    second replace is invisible whenever the first one changed something; and check only ever
    inspected what read returned. The documented build command could print a stale version forever
    with both set and check silent. Now two independent markers — surveyed, checked and written
    separately — with a regression test that also pins every marker's read/write as inverses, so
    a regex that writes a shape its own reader cannot parse can't create self-inflicted drift.

  • First query against a cold vault could crash with database is locked. store.connect() set
    PRAGMA journal_mode=WAL before PRAGMA busy_timeout, but the ordering was never the real
    problem: converting a database into WAL takes a brief EXCLUSIVE lock on a sqlite code path
    (pagerExclusiveLock) that does not consult the busy handler, so busy_timeout does not
    apply to that one statement however large it is or how early it is set. Measured: with
    busy_timeout=3000 already in effect, a contended transition raises in 0.000s, while the same
    pragma against an already-WAL database returns wal in 0.008s taking no lock at all. That second
    measurement is why a bounded retry is the fix and not a band-aid — the only way to lose this race
    is another connection converting the same database, so once any one of them wins, every later
    attempt is a lock-free no-op and the loop exits immediately. The trigger was never exotic:
    assembleContext fans out three rag processes over one vault at once, so the first query against
    a fresh index has three processes creating the same database concurrently. It surfaced as a flaky
    vkm-spec pipeline test in CI, but it was a user-facing cold-start bug, not a test artifact.
    connect() now also closes the...

Read more

v4.4.0

Choose a tag to compare

@github-actions github-actions released this 20 Jul 07:44

Added

  • obscura_research gains a background deep-research mode: three new tools, an unattended
    multi-round crawl, and a per-run report (ADR-0060).

    obscura_research_start(objective, topics, topic, …) launches a job that runs INSIDE the MCP
    server process for up to 30 minutes, the way a human researcher iterates on a hard question —
    each round reuses deepResearch whole, then a
    local model (generateLeads, zero-shot, the same qwen3.5:4b-q4_K_M expand model curation
    already uses for recall tasks) proposes typed next queries (subtopic/related/analogy/
    application) anchored to the stated objective rather than the seed topics' literal wording. The
    call itself returns in milliseconds: the MCP transport's 60s tools/call wall is client-side and
    unavoidable for one call (ADR-0057 §5), so depth now lives entirely off the wire instead of
    costing an agent round-trip per increment, the way the prior persist:true+excludeHashes loop
    did (still supported, unchanged). Poll progress with obscura_research_status, stop early with
    obscura_research_stop; only one job runs at a time — the same fan-out ban risk ADR-0057 §6
    measured live applies at greater scale to an unattended run. Every round persists to
    RESEARCH/<topic>/ as it finishes (a killed process loses only the round in flight), and a run
    report lands in RESEARCH/<topic>/runs/<timestamp>.md on every exit path — including an
    "Unexplored leads" section naming application/improvement ideas the job never got to chase —
    ready for /vkm-research <topic> to consolidate. New, optional env knobs:
    OBSCURA_DEEP_ROUND_MS (default 100000) and OBSCURA_DEEP_PACE_MS (default 15000). This is the
    "background continuation" mechanism ADR-0057's fourth addendum explicitly declined, pending "a
    use case [that] ever needs progress with no caller ever returning" — the trigger it named,
    firing. packages/obscura-web 326/326 tests, no regressions.

  • Static analysis now gates the ~32k LOC of shipped JavaScript. New root ESLint 10 flat
    config (@eslint/js recommended + tuned no-unused-vars, no-shadow, and the type-aware
    promise rules no-floating-promises / await-thenable / no-misused-promises via
    typescript-eslint), plus a CI-gated tsc checkJs pass (tsconfig.checkjs.json) over
    shipped src — tests stay covered by ESLint's type-aware rules through
    tsconfig.eslint.json; skill templates are excluded because their optional deps only exist
    in the user's env. Run with npm run lint / npm run typecheck; both wired into the
    ci / lint job, and node:test runners are allowed via allowForKnownSafeCalls instead of
    a blanket disable. The first sweep took 861 ESLint + 424 checkJs findings to zero and fixed
    real defects along the way: vkm-doctor's CLI main() ran unawaited (a crash died as an
    unhandled rejection instead of a clean non-zero exit), a no-op await on the synchronous
    NodeSDK.start(), a dozen wrapper throws that discarded the original error (now
    { cause }), async HTTP handlers passed where a void listener is expected (now guarded with
    a .catch backstop), dead stores, and producer JSDoc contracts narrower than the values they
    actually return (e.g. curatePage's undocumented relevance/reason fields).

  • A safe self-update path for installed skill/subagent templates, plus an opt-in version
    check (ADR-0061).
    npx @vkmikc/create-vkm-kit --check-update reports the installed vs.
    npm-latest version and a plan for every managed file under ~/.claude/skills/ and
    ~/.claude/agents/, without writing anything or ever failing on a network error (offline
    degrades to an honest "skipped" line). --update applies that plan: new/missing/kit-only-changed
    files install, a file you edited locally is left alone and reported as conflict (only
    --force overwrites it, and it says so — that DISCARDS the local edit), and both accept
    --dry-run to preview with zero writes. The classification is three-way (template vs. the
    sidecar's recorded install-time hash vs. what's on disk now — chezmoi's
    source/target/destination model), because the sidecar already recorded the ancestor hash for
    uninstall's benefit; that is what makes "you edited this" and "the kit changed this"
    distinguishable instead of both looking like "disk differs from template." v1 covers the
    hash-guarded asset layer only — managed rule blocks and MCP registrations still need a normal
    installer re-run.

  • Structure gate for the shipped skill templates, against Anthropic's published
    skill-authoring checklist (ADR-0061).
    New test/skill-structure.test.mjs enforces, over all
    four shipped skills: SKILL.md body ≤500 lines, a ## Contents heading on any reference file
    over 100 lines, one-level reference nesting (no new relative .md-to-.md link inside a
    skill, past a small named baseline of four pre-existing ones), forward-slash link paths, and a
    valid non-branded frontmatter name. vkm-design's eight reference files over 100 lines gained
    a Contents heading as part of landing this gate.

  • Drift gate for the repo's own Cursor memory rule. The committed
    .cursor/rules/obsidian-memory.mdc is fresh-install output of the installer
    (installRules(["cursor"], "es")), not an agents-manifest.yaml artifact — so no check
    covered it and it silently kept the pre-rename obsidian-memory:start/end sentinels and
    create-obsidian-memory branding across the vkm-kit rename (ADR-0041). sync-agents.ts now
    renders the fresh-install output (the newly exported CURSOR_RULE_FRONTMATTER +
    memoryRulesBlock("es") through the real mergeManagedBlock) and byte-compares it under
    --check (already in CI), failing with a "rerun the generator: npm run sync-agents" hint;
    write mode regenerates the file. The committed copy is regenerated to the current block, and a
    package test pins generator output ≡ gate expectation so the two can never diverge.

Changed

  • CONTRIBUTING.md's SemVer section now describes the kit, not the v1 prompt, and adds an
    explicit post-4.x versioning policy.
    The MAJOR/MINOR/PATCH definitions still spoke of
    "prompt section numbers"; they are rewritten in terms of the installed contract (CLI flags,
    MCP tools, vault layout/hooks), and the new policy freezes majors except for unavoidable
    contract breaks — batched into one planned major with its migration doc. README links the
    policy from "Más · More".

  • Repo face: a social-preview card, and the measured claims now name their substrate.
    docs/assets/social-preview.png (1280×640, on the hero.svg design language) gives GitHub's
    social preview and link unfurls a real card instead of a cropped avatar. The READMEs'
    token-economy paragraph now states what those numbers are measured against — a fixed labelled
    corpus and a deterministic embedder, i.e. reproducible regression floors, not leaderboard
    claims — and the hybrid-memory chip marks semantic search as the local opt-in it is. The code
    already said both (embeddings.py, ci.yml comments); the front page now matches it.

Fixed

  • The repo's own .cursor/rules/obsidian-memory.mdc no longer ships the pre-rename block. The
    committed dogfooding artifact (not generated by sync-agentsagents-manifest.yaml never
    listed it) still carried v2 branding: obsidian-memory:start/end sentinels, "Bloque gestionado
    por create-obsidian-memory", the (obsidian-memory-kit) frontmatter description, and it
    predated the RESEARCH/ and discipline sections. Regenerated with the current generator
    (installRules, fresh-install path), asserting the ADR-0041 legacy-sentinel migration produces
    the identical managed block. The cursor frontmatter (CURSOR_RULE_FRONTMATTER in
    rules-merge.mjs, the drift gate's single source) now writes the (vkm-kit) description for
    new installs too — the last v2-branded string the installer still emitted.

  • go.mod module path matches the repo slug. The module still declared
    github.com/Vahlame/obsidian-memory-kit, so importing or go install-ing the daemon by its
    real path (github.com/Vahlame/create-vkm-kit/cmd/obsidian-memoryd) failed — latent only
    because the installer builds locally. agent.toml's [daemon].module mirror is updated to
    match.

  • release.yml no longer reports success when npm publish silently skipped. The npm-publish
    job soft-exited (exit 0) when the NPM_TOKEN secret was missing, so the workflow went green
    while npm stayed behind — this shipped 4.2.0 and 4.3.0 to GitHub without their npm counterparts
    until someone noticed. Both publish steps now emit a ::error:: annotation and fail the job
    when the token is absent; fork runs were already excluded by the repository guard, so the hard
    fail only ever fires where publishing is actually expected.

  • Stale obsidian-memory-kit URLs swept from every functional link. LICENSE.md (root + the
    package mirrors via license:sync), SECURITY.md's source-verification step and this
    changelog's compare/release link definitions still pointed at Vahlame/obsidian-memory-kit;
    the GitHub redirect masks it today, but a redirect dies the day the old slug is re-registered.
    Historical mentions of the rename stay as written.

  • License labeling stopped implying plain MIT. Both README badges now read
    "MIT-derived + attribution (non-OSI)" and the License sections state explicitly that the
    mandatory visible-attribution clause is outside the OSI open-source definition — matching what
    package.json already declares ("license": "SEE LICENSE IN LICENSE.md"). Labeling only; the
    license terms are unchanged.

  • vkm-downloads was missing from both README "What's inside" tables despite shipping since
    4.3.0 — added, including its install mode (opt-in --downloads, deliberately not part of
    --full because it writes to disk).

  • **`ci / ...

Read more

v4.3.0

Choose a tag to compare

@github-actions github-actions released this 18 Jul 08:40

Removed

  • Token-saver permissions.deny rules retired; installer now sweeps them away (ADR-0043
    amendment).
    Daily-use evidence: Read(**/*.lock) hard-blocked reading pubspec.lock in a
    Flutter session (small file, needed to resolve a dependency constraint) and the auto-deny was
    indistinguishable from a manual denial. A prompt-less hard block trades a bounded token cost for
    an unbounded workflow obstruction across heterogeneous projects. configureTokenSaver no longer
    takes a denyRules option; the four old rules live on only as LEGACY_TOKEN_SAVER_DENY_RULES,
    a frozen list that every reconcile (install, --no-token-saver, --uninstall) strips from
    ~/.claude/settings.json while leaving user-authored deny rules untouched (new regression test
    covers the sweep, both with pieces ON and with everything OFF). Token savings on build
    artifacts remain the job of the compaction hooks, which degrade gracefully instead of refusing.

Security

  • obscura_research's crawl no longer hands an LLM curator content hidden from every human
    viewer, unfiltered.
    Found while reviewing Scrapling (a
    Python scraping library) for capabilities worth matching: its own MCP server strips CSS/ARIA-
    hidden elements before conversion, which this package had no equivalent of. Verified live, not
    hypothesized — a synthetic page hiding text behind display:none/aria-hidden had that text
    pass straight through the real obscura binary's --dump markdown (and --dump text, tried as
    a cheaper fix-hypothesis and found to fail identically — neither is visibility-aware) as if it
    were visible, including a prompt-injection-shaped payload that this package's own
    scanInjection heuristic — a visible-text scanner — also failed to flag. Every page
    obscura_research fetches is now parsed with cheerio (packages/obscura-web/src/sanitize.mjs
    — the package's first runtime dependency beyond @modelcontextprotocol/sdk/execa/zod, added
    deliberately for this) and stripped of hidden elements, <template> content, and HTML comments
    before either the heuristic extractor or the local curator sees it. Re-verified live,
    end-to-end, through the real pipeline against the same synthetic payload: the curated result
    contained only real, visible content. Scoped to the automated crawl — obscura_fetch's
    general, single-URL default is unaffected (matching its existing markdown fidelity from
    sanitized HTML would need a second, unapproved dependency); a class-based hiding rule (versus an
    element's own inline style/aria-hidden) is a named, tested, accepted gap, since catching it
    needs a real browser's computed styles. See ADR-0057's third addendum.

Fixed

  • CI red since b59e4fb (4 pushes): two independent causes, both reproduced before fixing.
    (1) lint: slop-check.mjs was committed unformatted for the pinned prettier@3.8.4
    reformatted (installed copy re-synced). (2) test-python: the ADR-0038 regression test
    (test_usage_boost_decays_stale_credit_so_new_note_not_buried) was green on Windows and red on
    ubuntu — retrieval order was platform-dependent: os.walk returns names sorted on NTFS but
    hash-ordered on ext4, insertion order decides FTS rowids, and rowids are the hidden tie-breaker
    when BM25/cosine scores tie exactly; with a tie at the top, the ranks fed into RRF differ per
    platform and the stale note's residual usage credit (linear taper leaves ~0.022 at 89.5/90
    days) crossed the ~w/(k+r)² one-rank margin, burying the brand-new note the test protects.
    Root fixes, all five ordering sites + the decay: sorted walk in _iter_markdown_files;
    ORDER BY score, path (BM25 FTS query); ORDER BY dist, path, ordinal (sqlite-vec path);
    heapq.nsmallest over (-score, path, ordinal) (brute-force cosine — same O(n·log k));
    (-score, path) tie-breaks in reciprocal_rank_fusion and the post-lever sort; and
    usage_counts_decayed taper linear→quadratic so window-edge credit lands two orders below
    any rank margin instead of inside it. Verified: an adverse-order simulation (reversed
    insertion, the ext4 condition) reproduced the CI failure locally with the ordering fixes alone
    and passes with the full set; 197/197 pytest; all four retrieval/token/assemble bench gates
    exit 0 locally at CI thresholds.
  • vkm-design modes/visual-loop.md: capability-check note for preview panes that only render
    file:// inside the project folder.
    Two real sessions hit the blank "static snapshot" +
    "no site open" wall on an outside-the-project file and misread it as a page bug; the mode now
    prescribes the copy-into-project pattern (dot-named temp, re-copy after each edit, delete at
    loop end) instead of fighting the pane or skipping the loop.
  • vkm-design audit scripts: four defects exposed by a real user session log (a session
    "audited" a roadmap HTML with these scripts and half the tool output was wrong — each fix
    below re-verified against the exact input that failed).
    (1) scale.mjs no longer infers the 1.067 ratio: its steps sit 6.7% apart, so the default
    ±3% tolerance bands cover ~91% of the value space and almost any incoherent set "fits" — 22
    real ad-hoc font sizes scored 1 offender under inferred 1.067, a false PASS that buried
    exactly the incoherence the check exists to expose (now honestly 11+ offenders under the
    best non-degenerate ratio, with a hint that --ratio 1.067 remains available explicitly).
    (2) scale.mjs --gen accepts the natural named-flag form (--gen --base 16 --ratio 1.25 --steps 6): the packed-only parser swallowed a following --base as its value and died
    with a misleading "not a comma-separated number list".
    (3) checkSpacing attaches a halfGrid diagnosis when it explains part of the mess: a file
    interleaving 4k and 4k+2 paddings (a real 2px half-step rhythm) was reported flat as "8
    values off-rhythm", sending the session on a manual detour to conclude "false alarm" — the
    CLI now says "only 3 stay off — the rest is a half-step rhythm; judge intent" itself, while
    the exit code still fails against the declared base.
    (4) audit-css.mjs given .html/.htm audits only <style> blocks + inline style=""
    attributes: the whole file used to go through the rule regex, which read a <script> mermaid
    theme object ({ background: "#0e2846", … }) as CSS — one bare color: key away from
    fabricated contrast pairs — while real inline styles stayed invisible. Regression tests for
    all four; installed copies under ~/.claude/skills/ re-synced.
  • vkm-doctor skills-drift check: compares EVERY installed skill file, not just SKILL.md.
    The motivating real case (2026-07-18): vkm-discipline/domains/design-ui.md stale under a
    byte-identical SKILL.md — the check reported "ok" for exactly the drift class it was built
    to catch, and the routing fix that file carried (design tasks → vkm-design gate) silently
    never reached sessions. A skill is now STALE if any of its template files differs installed-
    side or is absent (new regression test reproduces the design-ui.md case).
  • vkm-design modes/visual-loop.md: new trap — republishing a document to a NEW artifact URL
    while the user still holds the old link.
    Real session: the fixes landed on a fresh artifact,
    the user's open copy never changed, and the session read back "no veo ningún cambio" as a
    design complaint instead of a delivery failure. The loop now instructs: list existing
    artifacts, redeploy to the SAME url, verify at the link the user actually holds.
  • obsidian-memory-rag: concurrent ensure_fresh writers no longer die with database is locked. Two simultaneous vault_hybrid_search calls with a stale index (reproduced live while
    verifying ADR-0056) raced index_vectors' BEGIN IMMEDIATE; the loser only got Python's
    implicit ~5s sqlite3.connect timeout — undocumented and demonstrably too short for a
    batch_commit_every=64 batch under a real embedder. Root-cause fix in the shared
    store.connect(): explicit PRAGMA busy_timeout=30000 on every connection, so a second writer
    blocks and then reads the winner's committed state (the existing mtime/size incremental logic
    already skips re-indexing) — no manual retry loop needed, SQLite's busy handler transparently
    retries BEGIN IMMEDIATE. Verified red→green with a new barrier-synced two-thread test
    (tests/test_concurrent_ensure_fresh.py, slow-embedder holds the lock ~6.4s); full suite 197
    passed, none modified.

Added

  • vkm-downloads: background downloads, sets of files, resume, and fastest-mirror selection
    (ADR-0059).
    Follow-up to the synchronous tool: the user needed to download large files or SETS
    of files "whether the download is fast or slow" and to "find the site with the fastest download" —
    both blocked by the MCP transport's ~60s per-call wall (a 3 GB ISO can't be one synchronous call)
    and by the conservative 500MB cap. Additive (the sync download_file and every ADR-0058 guardrail
    are byte-for-byte unchanged): four new tools + a background job runner. download_start({ files })
    registers a job and returns a job_id immediately, running the transfer as an async task inside
    the long-lived stdio server — no child process, so the "never execute downloaded bytes" property
    holds
    ; download_status / download_cancel poll and abort. Each file may list several mirror
    URLs
    ; probe_mirrors (and prefer_fastest) measures each with a ~512 KB ranged GET and ranks by
    real throughput (latency tiebreak; a size_mismatch flag catches a "mirror" that isn't the same
    file), all through the same guarded, IP-pinned path so a private/loopback mirror is refused, not
    probed. Large files are gated by a free-disk-space check (fs.statfs) rather than a hard byte
    wall (background ceiling VKM_DOWNLOAD_MAX_BYTES_BG, 100GB); interrupted d...
Read more

v4.2.0

Choose a tag to compare

@github-actions github-actions released this 12 Jul 20:59

Added

  • obscura_search gains a local SearXNG structured backend, started on demand (ADR-0052). Free
    SERP scraping can't be fast + high-volume + relevant at once; a local SearXNG (aggregated engines,
    structured JSON, no anti-bot wall) can. ensureSearxng() starts SearXNG the moment obscura_search
    needs it and stops it after an idle window (OBSCURA_SEARXNG_IDLE_MS, default 90 s) — nothing runs
    in the background while idle — and falls back to the scrape chain when it's unavailable. searchWeb
    now defaults to a local instance (http://127.0.0.1:8888; OBSCURA_SEARXNG_URL="" disables it).
    A reproducible Windows setup (no Docker) ships under packages/obscura-web/searxng/
    (settings.template.yml, a pwd shim, a README); paths are overridable via
    OBSCURA_SEARXNG_{PORT,PY,SRC,SETTINGS,IDLE_MS,AUTOSTART}.
  • Desktop monitor for SearXNG (searxng-gui.pyw). A stdlib-only Tkinter app: live up/idle status
    plus a feed of what the agent has searched (each search is appended to
    ~/.vkm/searxng/searches.log). It only monitors — the MCP owns the on-demand lifecycle, so closing
    the window frees only the window.
  • Domain reference system for /vkm-discipline + an obscura web-search domain. Skills can now
    ship on-demand domain reference files: the installer copies a skill's whole directory (not just
    SKILL.md), loaded on demand (progressive disclosure). The first is
    skills/vkm-discipline/domains/web-search.md — how to search/fetch well through obscura and hand
    back a verified, grounded result (cite only what you fetched, cross-check high-stakes facts,
    match freshness/cache to volatility).
  • Full cross-domain reference set for /vkm-discipline, re-authored from the SOP-suite's
    bench-refined annexes in the execution-first voice (deliver-a-better-result checklists + anti-patterns,
    not bureaucratic gates):
    coding, debugging (bugs + live incidents:
    first-hypothesis-is-what-changed, capture before mitigate, SEV cadence, revert-by-default,
    next-update-not-resolution comms), data (rehearse destructive DML with a SELECT-count + backup,
    parameterize, validate with aggregates), infra (test-env-first, restorable backup, health-check
    baseline, canonical mechanism), writing (every claim traceable, run every instruction, recompute
    numbers), design-ui (contrast computed per theme, keyboard/focus, ≥24px targets, 200% zoom, the
    three states), web-search, security (untrusted-input-at-the-boundary, rotate-don't-delete leaked
    secrets, least-privilege, dependency vetting), llm-artifacts (model proposes, evidence decides;
    plausibility raises the bar), and expertise (separate fact/inference/hypothesis, calibrate
    confidence, steelman the alternative, say what would make you wrong). Ten domains cover the SOP-suite's
    A1–A8 plus web-search and expert judgment — the kit now supersedes the SOP-suite's cross-domain role.

Changed

  • /vkm-discipline reoriented from a coding contract into cross-domain "resourceful execution"
    (begins superseding the SOP-suite's role; ADR-0049 channel).
    Infer the real intent, do it the
    best way, deliver more than the literal ask — with minimal friction, depth scaled to task
    difficulty AND model (a smaller model skips long step-by-step reasoning, which measurably hurts it;
    a larger model self-verifies). Bias to action; ask only when the answer changes what you'd do.
    Guardrails (confirm-before-irreversible, injection/untrusted-data scanning, evidence gates) are now
    opt-in modules, off by default — the skill's job is execution, not friction. Installed copy re-synced.

v4.0.0

Choose a tag to compare

@github-actions github-actions released this 11 Jul 07:22

The kit becomes vkm-kit: one plug-and-play efficiency suite for Claude Code —
persistent vault memory + token-saver + local usage doctor + spec-builder. The
repo is now Vahlame/create-vkm-kit; the installer package is
@vkmikc/create-vkm-kit (bins create-vkm-kit and vkm), and the old
@vkmikc/create-obsidian-memory name lives on as a forwarding shim — see the
migration notes in ADR-0041/0050. Machine identifiers your installs depend on
are FROZEN: the MCP server key obsidian-memory-hybrid, the obsidian-memoryd
daemon, and BASIC_MEMORY_HOME. Managed blocks and hook files written by 3.x
are recognized and migrated in place on the next install (dual-read markers,
vkm-kit:start/end sentinels, VKM_VAULT env accepted alongside the legacy
names).

Changed

  • Rename (ADR-0041): packages/create-obsidian-memory
    packages/create-vkm-kit; ownership marker vkm-kit (legacy
    create-obsidian-memory still recognized); managed-block sentinels
    <!-- vkm-kit:start/end --> with in-place migration of legacy blocks
    (test-pinned); VKM_VAULT accepted before BASIC_MEMORY_HOME /
    OBSIDIAN_MEMORY_VAULT; release workflow publishes the renamed installer
    plus the shim.

Security

  • RAG-passthrough MCP tools no longer accept a caller-supplied vault path.
    vault_hybrid_search, vault_fts_search, vault_fts_index, vault_complete,
    vault_relations, vault_observations, vault_kg_suggest,
    memory_extract_candidates, vault_audit, and vault_memory_report exposed
    an optional vault parameter with no validation against the configured
    default — a prompt-injected note could coerce an agent into pointing one of
    these tools at an arbitrary directory on disk, indexing and searching its
    .md files (a cross-directory read oracle) and creating a
    .obsidian-memory-rag/ sidecar there. Removed the parameter from all 10
    tool schemas, matching the posture the filesystem tools (vault_read_file,
    vault_write_file, vault_edit_file, vault_list_directory) already had.
    See ADR-0040.

Added

  • vkm-spec package (ADR-0046/0047/0048) — the spec-builder pipeline: a one-line
    idea + vault context (one assembleContext call) compiles into an editable
    <orchestration_package> XML prompt, optionally enriched by a LOCAL Ollama
    draft (phi4-mini:3.8b-q4_K_M, /api/chat with format=<JSON schema>
    constrained decoding, fetch-only client, typed failures) — then reviewed by
    the human (GUI on 127.0.0.1:4923 with an SSE draft stream and a visible
    ollama/fallback source badge, or vkm-spec CLI with $EDITOR review) and
    pasted into Claude Code / Claude web / any AI. The deterministic fallback is
    structural: buildSpec always compiles a working prompt and the LLM draft
    only overwrites fields on success — pinned by a degradation-gate test (Ollama
    down → SSE error frame still carries a working XML). The installer can
    auto-provision Ollama + the model under explicit --full or --ollama
    (winget silent on Windows, ~2.3GB pull, best-effort — never curl|sh, never
    a surprise download on a bare install; --no-ollama opts out).

  • vkm skills + subagent template (ADR-0049)/vkm-discipline (execution
    contract: same functionality and quality in fewer readable lines — density,
    never reduced scope; vault-first context via one assemble_context call;
    terse output; nothing is "done" without executed evidence) and /vkm-spec
    (turn a one-line idea into a precise, context-grounded spec in-session — no
    local LLM in the synchronous path) installed into ~/.claude/skills/, plus
    the vkm-implementer subagent template in ~/.claude/agents/. All
    hash-tracked (--skills / --agents toggles; user-modified files survive
    uninstall), with a lint gate keeping every skill description ≤300 chars. The
    managed rules block gains one "Executable discipline (vkm)" bullet (ES/EN,
    within existing budgets; AGENTS.md + install docs re-synced — drift gates
    green).

  • assemble_context MCP tool (ADR-0045) — one call returns a budgeted context
    bundle for a task (typed [decision]s from the project note, non-decision
    observations + relevant cross-note passages, #stack facts, and a raw-note
    fallback excerpt when a project has no typed decisions yet), replacing the
    3-6 discrete search/read round-trips an agent would otherwise chain. Engine
    relocated from the prompt-compiler's context-search.mjs into
    obsidian-memory-mcp/src/context-assemble.mjs (parallel json-hybrid-search --graph + two json-observations passes over the same Python bridge).
    Honors the security posture of every RAG tool: no wire-level vault param
    (env-resolved only, pinned by test), untrusted-data _trust envelope with
    injection flagging, and a budget_chars cap (default 6000) that trims
    passages-first so decisions survive longest. Measured and CI-locked by the
    new bench-assemble gate (--assert-savings 0.60 --assert-answered 0.90):
    on the labelled fixture, median 68% fewer wire tokens than the naive
    multi-call pattern at 100% completeness (10,867 vs 30,816 tokens aggregate;
    cross-cutting queries save an honest 45%, project tasks 68% — and the naive
    arm pays no discovery cost, so these are floors).

  • vkm-doctor package + local telemetry (ADR-0044) — a zero-dependency local
    OTLP/HTTP JSON sink (vkm-otel-sink, 127.0.0.1:4319 → NDJSON rollups in
    ~/.vkm/telemetry/, 90-day prune, lockfile singleton, tolerant parser that
    archives unknown metric shapes instead of dropping them) plus a vkm-doctor
    CLI that reports token usage per day/model/type, the cache-hit ratio, cost,
    and a "broken cache" diagnosis (high input volume with near-zero cacheRead).
    The installer wires Claude Code's OTEL export to the sink (managed env
    block; --no-telemetry opts out) and a SessionStart hook spawns the sink
    when it isn't running — no OS service. Local only: nothing leaves the
    machine. Transcript scanning exists only as a clearly-labeled
    transcript-estimate fallback (the JSONL format is officially unstable).

  • Token-saver module (ADR-0043) in create-obsidian-memory — on by default for
    Claude Code installs (--no-token-saver / --no-terse-style opt out;
    VKM_TOKEN_SAVER=0 is a runtime kill switch). Three pieces, all reconciled
    symmetrically and fully reversed by --uninstall: (1) a PostToolUse hook
    (matcher Bash) that compacts noisy shell output before it enters context —
    ANSI stripped, \r progress repaints reduced to their final frame, runs of
    identical lines collapsed, >200-line logs windowed to head+tail — while a
    hard, test-gated guarantee keeps every error/warn/fail/exit-code line and the
    final lines verbatim (≥30% reduction on the noisy CI fixture, zero
    diagnostic loss); (2) a PostToolUse hook (matcher mcp__.*) that
    re-serializes pretty-printed JSON tool results into compact form
    (whitespace-only — parsed data, including the _trust envelope, is
    byte-identical after JSON.parse); (3) permissions.deny read rules for
    token-hungry artifacts (node_modules/**, **/dist/**, lockfiles) plus the
    vkm-terse output style (keep-coding-instructions: true), installed
    hash-tracked and activated via the outputStyle setting.

  • Shared settings-write infrastructure (settings-io.mjs, settings-writers.mjs,
    asset-install.mjs) in create-obsidian-memory
    — Phase 0 groundwork for the
    vkm-kit efficiency-suite train. The safe ~/.claude/settings.json idiom
    (read→BOM-strip→parse with invalid-JSON backup, pure managed-hook merge/remove
    deduped by filename stem, JSON sanity parse, restricted backups, atomic
    tmp+rename writes, ownership-marker checks) is extracted out of
    claude-native-memory.mjs into settings-io.mjs — behavior unchanged, existing
    suite as the refactor guard — and joined by new pure writers for the sections
    upcoming modules manage (env, permissions.deny/allow, outputStyle;
    removal only reverses values still provably ours) plus a managed-asset
    installer that tracks installed template files by SHA-256 in a
    ~/.claude/vkm-kit.assets.json sidecar so uninstall deletes only unmodified
    files.

  • Cross-process lock for the Go daemon's git-sync. Two obsidian-memoryd
    instances (or a daemon plus a manual sync once/git operation) pointed at
    the same vault could previously race add/commit/pull/push against
    the same working tree. See ADR-0040.

  • create-obsidian-memory --uninstall. Fully reverses this kit's
    Claude Code integration (hook entries, autoMemoryEnabled override, and the
    hook script files it owns) without touching a user's own unrelated hooks or
    same-named files. Turning an enforcement flag off on a re-run
    (--no-effort-gate, --no-memory-enforcement, --no-native-memory-override)
    now also removes the corresponding previously-installed hook instead of
    leaving it active. See ADR-0040.

Removed

  • obsidian-prompt-compiler package (ADR-0046) — absorbed into vkm-spec.
    Its pure modules (compile-xml, prompt-defaults, project-resolve,
    clipboard, review) moved verbatim; its retrieval was already relocated to
    obsidian-memory-mcp/src/context-assemble.mjs (ADR-0045). The GUI moved off
    port 4317 (OTLP collision) to 4923.

Fixed

  • Go daemon doctor could report healthy while actually blind. A failed
    git add/commit/pull (not just push) now feeds the same health state
    doctor alarms on; a repeated rebase --abort now feeds the alarm too
    (previously only displayed, never alarmed); a silently-failing filesystem
    watcher startup (fsnotify.NewWatcher) now sets an immediate alarm instead
    of leaving the daemon looking idle-but-fine with a heartbeat that never
    starts; a sync aborted by intentional shutdown (Ctrl-C / service stop) no
    longer counts toward the "vault not syncing" alarm; doctor's unpushed-
    commit git call now h...
Read more

v3.15.0

Choose a tag to compare

@github-actions github-actions released this 09 Jul 05:56

Added

  • RULES contract — dated, sourced, reasoned project rules (ADR-0039). The
    vault scaffold now creates RULES/TEMPLATE.md (es/en) and the managed rules
    block teaches the contract in its What-to-save section: keep project
    rules (knowledge invisible from the repo — domain contracts, deliberate
    traps, user mandates), each carrying its why (history), its source
    (the file/test/ADR that defines or enforces it) and a last_verified
    date; re-verify a rule against its source when you use it, and when a note
    contradicts the repo, fix the note in the same session. Grounded in an
    external multi-agent bench (sop-suite RUN1–RUN7): memory pays on invisible
    project rules, not on generic method (+2–3 pts/task); rules with a recorded
    why get respected; agents self-verify stale rules when the source of truth
    is one link away (aged-memory trap: 0/7 fell). Rules-block budgets raised
    es 8,400 → 9,100 / en 8,200 → 8,850 chars as a reviewed ADR-0036 decision
    (+~270 chars per language), with two new load-bearing phrases pinned per
    language.

v3.14.0

Choose a tag to compare

@github-actions github-actions released this 06 Jul 09:17

Changed

  • ADR-0038 retrieval levers join the default stack. The installer now wires
    OBSIDIAN_MEMORY_PIN_FAILURES=1 and OBSIDIAN_MEMORY_USAGE_BOOST=1 into the
    obsidian-memory-hybrid MCP env by default (all three surfaces: Cursor
    mcp.json, claude mcp add, codex mcp add), same rationale as sqlite-vec:
    ranking-only levers over telemetry that is already collected by default,
    bench-gated in CI. Opt out with --no-pin-failures / --no-usage-boost.
    The cross-encoder reranker stays opt-in (--rerank) — it downloads a model
    on first use and needs the [rerank] extra.

Fixed

  • npm license metadata caught up with the license change. The published
    package (@vkmikc/create-obsidian-memory) still declared "license": "MIT"
    and its tarball shipped no license text — npm only auto-includes LICENSE*
    files from the package root, not the repo root. The field now reads
    SEE LICENSE IN LICENSE.md, every packages/* dir mirrors the canonical
    root LICENSE.md (new npm run license:sync + license:sync:check,
    drift-gated in the CI lint job and before npm publish in the release
    workflow), the private packages (obsidian-memory-mcp,
    obsidian-prompt-compiler) declare the same field, and stale "MIT" wording
    in the package README and CONTRIBUTING.md now points at LICENSE.md.
    Verified with npm pack --dry-run that LICENSE.md lands in the tarball.

v3.13.1

Choose a tag to compare

@github-actions github-actions released this 06 Jul 04:18
3b8d27e

Changed

  • Docs caught up with v3.13.0 across both languages. how-it-works /
    como-funciona gain a "multi-writer safety + evolutive memory" section with
    two new diagrams (ifMatch sequence; the learning loop), the retrieval-stack
    and memory-report diagrams now show the pin_failures/usage levers,
    recall telemetry and reflect: true; sync guides document the 3.13
    audit-trail commits and OBSIDIAN_MEMORY_AGENT; the AGENTS stack catalog
    and README --full callouts mention the new capabilities (ADR-0037/0038).

v3.13.0

Choose a tag to compare

@github-actions github-actions released this 06 Jul 03:29
64a13a2

Added

  • Optimistic concurrency for vault writes (ADR-0037). vault_read_file
    now reports a content etag (+ mtime) outside the untrusted envelope;
    vault_write_file / vault_edit_file accept an opt-in ifMatch that
    fails the write with a retryable precondition failed when the note
    changed since the read — closing the lost-update window across
    read→think→write round-trips. Write results return the new etag so edits
    can chain without re-reading.
  • Advisory cross-process write lock (ADR-0037). Sidecar writes serialize
    through an O_EXCL lockfile in .obsidian-memory-rag/ with automatic
    stale-lock recovery (dead PID / expired TTL, same-host only — foreign-host
    locks are never stolen). Kill-switch OBSIDIAN_MEMORY_NO_LOCK=1. The
    daemon watcher now skips the sidecar dir so lock/index churn can't trigger
    spurious sync cycles.
  • Audit-grade auto-commits (ADR-0037). Daemon commits now carry the
    staged-file list (subject counts, body lists up to 20 paths) and an
    optional Agent: trailer from OBSIDIAN_MEMORY_AGENT, so git log
    answers what changed and which daemon committed it.
  • Corrupted-state checks in vault_audit (ADR-0037). New read-only keys:
    Syncthing sync_conflicts (any extension), committed git
    conflict_markers outside code regions, stale_tmp leftovers from crashed
    atomic writes, and git_state (in-progress rebase/merge). doctor also
    records the watcher's last conflict-file sighting and scans the vault for
    pre-existing ones.
  • Opt-in frontmatter schema validation (ADR-0037). A committed
    memory-schema.json declares required keys per top-level folder (*
    fallback); the write path warns (or rejects with enforce: true, before
    anything lands on disk) and vault_audit reports schema_violations from
    the same config. Required-keys presence only — no YAML dependency.
  • Evolutive memory loop (ADR-0038). memory_extract_candidates
    classifies candidates (failure/gotcha/preference/decision/fact,
    EN+ES) with routing hints and dedups failures against KNOWN_FAILURES.md;
    a structured failure-entry convention makes lessons queryable by
    category/tag; the opt-in pin_failures retrieval lever (CLI
    --pin-failures / OBSIDIAN_MEMORY_PIN_FAILURES=1) resurfaces recorded
    lessons on matching tasks, gated in CI by a new failure-recall bench
    slice run baseline + lever-on.
  • Usage reinforcement + decay (ADR-0038). A local recall_log telemetry
    table (survives reindexes, 1y auto-prune, OBSIDIAN_MEMORY_RECALL_LOG=0
    to disable) records which notes searches return and which the agent then
    opens; the opt-in usage lever (CLI --usage /
    OBSIDIAN_MEMORY_USAGE_BOOST=1) boosts memory that demonstrably helped,
    and vault_memory_report surfaces never-touched cold_notes for human
    review (no telemetry ⇒ no cold claims; nothing is auto-deleted).
  • memory-reflect consolidation proposals (ADR-0038). New CLI pair
    (memory-reflect / json-memory-reflect, plus
    vault_memory_report({reflect:true})): pending PRACTICES/observations.md
    lines past their window → confirm/dismiss; near-duplicate pairs → merge
    candidates; stale status: hypothesis notes and orphans → verify/link/
    archive proposals; SESSION_LOG recent-activity digest. --write-note
    renders them to _meta/reflection-YYYY-MM-DD.md (idempotent per day).
    vault_audit gains stale_hypotheses / unverified from the new
    status: / last_verified: frontmatter convention.

Changed

  • ADR-0013 amended: the documented-but-never-implemented three-way
    git merge-file auto-merge for Syncthing conflicts is withdrawn — the
    daemon's contract is detect-and-surface; resolution stays human.
  • Docs answer the "system of record belongs in Postgres" critique head-on:
    new ADR-0037 (vault vs database: transactions/concurrency/auth/audit stated
    honestly) and ADR-0038 (evolutive loop), a FAQ entry ("Can I run a ticketing
    system on the vault?" — no, and why), a SECURITY.md authentication-model
    subsection, and a "Multiple agents writing to one vault" section (EN+ES)
    with an ifMatch worked example and a conflict recovery playbook.