v4.4.0
Added
-
obscura_researchgains 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 reusesdeepResearchwhole, then a
local model (generateLeads, zero-shot, the sameqwen3.5:4b-q4_K_Mexpand 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 60stools/callwall 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 priorpersist:true+excludeHashesloop
did (still supported, unchanged). Poll progress withobscura_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 inRESEARCH/<topic>/runs/<timestamp>.mdon 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) andOBSCURA_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-web326/326 tests, no regressions. -
Static analysis now gates the ~32k LOC of shipped JavaScript. New root ESLint 10 flat
config (@eslint/jsrecommended + tunedno-unused-vars,no-shadow, and the type-aware
promise rulesno-floating-promises/await-thenable/no-misused-promisesvia
typescript-eslint), plus a CI-gatedtsccheckJspass (tsconfig.checkjs.json) over
shippedsrc— 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 withnpm run lint/npm run typecheck; both wired into the
ci / lintjob, andnode:testrunners are allowed viaallowForKnownSafeCallsinstead of
a blanket disable. The first sweep took 861 ESLint + 424checkJsfindings to zero and fixed
real defects along the way: vkm-doctor's CLImain()ran unawaited (a crash died as an
unhandled rejection instead of a clean non-zero exit), a no-opawaiton the synchronous
NodeSDK.start(), a dozen wrapperthrows that discarded the original error (now
{ cause }), async HTTP handlers passed where a void listener is expected (now guarded with
a.catchbackstop), dead stores, and producer JSDoc contracts narrower than the values they
actually return (e.g.curatePage's undocumentedrelevance/reasonfields). -
A safe self-update path for installed skill/subagent templates, plus an opt-in version
check (ADR-0061).npx @vkmikc/create-vkm-kit --check-updatereports 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).--updateapplies that plan: new/missing/kit-only-changed
files install, a file you edited locally is left alone and reported asconflict(only
--forceoverwrites it, and it says so — that DISCARDS the local edit), and both accept
--dry-runto 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). Newtest/skill-structure.test.mjsenforces, over all
four shipped skills:SKILL.mdbody ≤500 lines, a## Contentsheading on any reference file
over 100 lines, one-level reference nesting (no new relative.md-to-.mdlink inside a
skill, past a small named baseline of four pre-existing ones), forward-slash link paths, and a
valid non-branded frontmattername.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.mdcis fresh-install output of the installer
(installRules(["cursor"], "es")), not anagents-manifest.yamlartifact — so no check
covered it and it silently kept the pre-renameobsidian-memory:start/endsentinels and
create-obsidian-memorybranding across the vkm-kit rename (ADR-0041).sync-agents.tsnow
renders the fresh-install output (the newly exportedCURSOR_RULE_FRONTMATTER+
memoryRulesBlock("es")through the realmergeManagedBlock) 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 thehero.svgdesign 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.ymlcomments); the front page now matches it.
Fixed
-
The repo's own
.cursor/rules/obsidian-memory.mdcno longer ships the pre-rename block. The
committed dogfooding artifact (not generated bysync-agents—agents-manifest.yamlnever
listed it) still carried v2 branding:obsidian-memory:start/endsentinels, "Bloque gestionado
porcreate-obsidian-memory", the(obsidian-memory-kit)frontmatter description, and it
predated theRESEARCH/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_FRONTMATTERin
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.modmodule path matches the repo slug. The module still declared
github.com/Vahlame/obsidian-memory-kit, so importing orgo 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].modulemirror is updated to
match. -
release.ymlno longer reports success when npm publish silently skipped. The npm-publish
job soft-exited (exit 0) when theNPM_TOKENsecret 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-kitURLs swept from every functional link.LICENSE.md(root + the
package mirrors vialicense:sync),SECURITY.md's source-verification step and this
changelog's compare/release link definitions still pointed atVahlame/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.jsonalready declares ("license": "SEE LICENSE IN LICENSE.md"). Labeling only; the
license terms are unchanged. -
vkm-downloadswas 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
--fullbecause it writes to disk). -
ci / linksno longer flakes red on github.com HTTP/2 resets. lychee fired up to 128
concurrent requests; github.com answers such bursts with HTTP/2 GOAWAY/protocol errors — one
run failed 10 links that all returned 200 when probed individually, half of them in files the
PR never touched. The token/API fallback can't help (it's a transport error, not a 429), so the
job now caps--max-concurrency 32and retries with backoff (--max-retries 4,
--retry-wait-time 2). De-flaking then unmasked two deterministic 404s in this changelog's own
link definitions —v3.6.0was never tagged — fixed by dropping the dead[3.6.0]definition,
retargeting[3.7.0]tov3.5.0...v3.7.0, and moving[Unreleased]off its stalev3.12.0
base ontov4.3.0. -
ci / secrets-scanno longer fails on a third-party licence server.gitleaks-actionv3
validates a licence against gitleaks.io on every run; while that endpoint answered "No server is
currently available" the job failed with "missing gitleaks license" on a leak-free tree — that is
what turned PRs #67 and #68 red. The gitleaks binary itself is MIT and needs no licence, so CI
installs v8.30.1 directly (sha256-verified, retry on download) and runs the same full-history
scan (236 commits, no leaks, exit 0).