Skip to content

Releases: GD4AI/obsidian-llm-wiki

1.27.2

Choose a tag to compare

@github-actions github-actions released this 15 Sep 11:42
acabfcb

🌟 Karpathy LLM Wiki v1.27.2

Highlights

PATCH with no new features and no new settings — 23 bug fixes, two of them closing content-loss classes that could quietly damage a page, and one regression the pair introduced between them. If you are on v1.27.1, this is a straight upgrade.

  • A body rewrite cut off at the token limit is no longer written over the page (Issue #704, PR #705). When a model stopped because it hit max_tokens, the partial answer was saved as if complete. Measured on a rebuilt vault: 13 body rewrites grew past 8 KB, 10 of them within four minutes of a "finish_reason": "length", one page going 32 KB → 127 KB with 300–760 provenance footnotes and 2463 of 2703 prose links dead. Truncation is only knowable after the fact, so it is checked there — at the three call sites that overwrite an existing body, the frontmatter write is kept, the existing body is restored, a warning is logged, and the truncated text is discarded.
  • Provenance footnote brackets are repaired before they can cost you a paragraph (PR #702). A page's inline source marker (^[Source: [[Name]]]) is what decides which paragraph a rewrite may drop, and the model gets the brackets wrong often enough to matter — measured over a rebuilt vault, about a third of markers came back with two closing brackets instead of three, one in five with the opening bracket doubled. Every malformed form is invisible to the guard, so its paragraph lost its owner and became droppable. The write gate now repairs the four written forms; the label is carried through verbatim and the semantic half (which paragraph, which source) is untouched.
  • The end-of-run link repoint now sees every page it was meant to (Issue #713, PR #714). Two producers wrote updated_pages in different shapes — vault paths for created pages, bare page names for related-page updates — and the repoint pass filters on .md, so every bare name was skipped in silence. Fixed at the source, plus a second fix in the same change: updated pages no longer render as dead links in the ingest log.
  • Corporate and gateway OpenAI-compatible endpoints can ingest again (Issue #711, PR #712). A gateway that rejects the whole response_format envelope without naming json_schema matched no classifier, so the rejection surfaced raw and the provider was unusable behind such a proxy. It now engages the existing demotion chain.
  • Strict structured output is a negotiated tier at the schema boundary (Issue #658, PR #686). A provider that rejects the strict dialect demotes one tier instead of failing the call.
  • Delete Empty Stubs collects the stubs it is named for, and leaves the deliberate ones alone (Issue #678, PR #691). Three different writers set stub: true; one of them writes a dead-link placeholder on purpose. The collector now requires a stub that is actually empty.
  • Ingest lifecycle is released when a file is skipped (Issue #688, PR #690). The status bar no longer stays stuck after a skip or a cancel.
  • A regression found and fixed before it shipped (PR #722). Two of the fixes above touched the same function — one added a guard returning a boolean, the other changed that function's return type to a string path. Each passed the full test suite on its own branch; merged, they left main failing type-checks for five consecutive commits. The runtime half was worse than the type error: the recorded value became true, which the repoint filter drops silently — the exact bug #713/#714 had just repaired, reintroduced on the path the guard was added to protect. Fixed at the root, with the test that had pinned the old contract updated to pin the new one.
  • Documentation and repository hygiene. The repository links now point at the organisation that owns the project (GD4AI/obsidian-llm-wiki) rather than the pre-move account; ten broken in-page links across the READMEs were found and fixed; and the guard that pins the README link prefix was rewritten — its previous form could not match any github.com URL at all, which is why the stale owner had survived two releases.

What's in this release

Fixed

  • A rewrite cut off at the token limit is not adopted (Issue #704, PR #705). New captureFinish() in llm-sdk/finish-reason.ts returns an onFinish sink plus a truncated getter; only 'length' counts, and a client that reports nothing leaves the reason 'unknown', so legacy and mock clients keep their exact behaviour. Guarded at merge-body, reviewed-append and the related-page rewrite.
  • analysis.updated_pages carries one shape (Issue #713, PR #714). De-duplicated at the fill site rather than in each reader, which also keeps the cancelled and failed paths correct — they hand the list straight to onDone without passing a reader. The ingest log's pageLinks() now strips the wiki folder prefix for updated pages too.
  • Provenance markers normalised on the content write gate (PR #702). Four malformed written forms repaired to the canonical shape; idempotent, and a no-op returning the same reference when the content holds no wikilink. Guards keep ordinary wikilinks whose name contains a colon out of the rewrite.
  • Gateway response_format rejections classified (Issue #711, PR #712). The strict-dialect body stays owned by its existing classifier; negative cases keep the match from widening into a generic 400 catch-all.
  • Strict structured-output demotion (Issue #658, PR #686). LLMClient gains an outputMode of json_schema / json_schema_strict / json_object / text_prompt.
  • Delete Empty Stubs predicate (Issue #678, PR #691). isStubPage(fm) && isEmptyStub(content); mutation-verified, and the tests that asserted the opposite of their own names were corrected.
  • Two contradiction paths only the model could feed removed (Issues #604, #666, PR #684).
  • Source page head stamped from code (Issues #679, #670, #661, PR #681). Title, note path and date are no longer copied through the model.
  • Skipped file releases the ingest lifecycle (Issue #688, PR #690).
  • One label table for the ingest log (Issue #667, PR #685). Three supported languages had been falling back to English.
  • One-token JSON defects repaired before spending a model call (Issue #682, PR #683).
  • Related-sibling cap holds at three (Issue #644, PR #645). Siblings rescue an orphan; they no longer form a clique per note.
  • Smaller correctness fixes. Case folded when checking a page's own aliases (#674, PR #675) · the create path's alias floor applied on the source path (#671) · a two-character name created rather than sent to semantic dedup (#661, PR #663) · Related sections rendered after a complementary append (#659, PR #660) · model-invented sources: entries dropped before the true one is stamped (#650, PR #651) · a bare sources: line no longer emits a stray [[]] (#648, PR #649) · a cancel reaches the running model call and stops at the next page write (#646, PR #647) · frontmatter seams no longer grow a blank line on every call.
  • Schema suggestions read as proposals, and the schema diff modal no longer crashes on first render (Issues #594, #593, PRs #655, #654).

Changed

  • The Five-Gate now runs on pushes to main, not only on pull requests (PR #698). Merged commits were previously unverified; this is the trigger that surfaced the regression described in Highlights.
  • package-lock.json is gated against package.json (PR #693), and the lockfile pair is regenerated at release time (PR #692 regenerated only the npm side, which let the pnpm lockfile drift silently).

Docs

  • Where a document is processed, per PDF path (Issue #657, PR #694). Both the English and Chinese guides now state that residency is US by default for Anthropic, OpenAI and Google, the configured region for Bedrock, and that OpenAI offers EU residency.
  • Repository links migrated to the new organisation. All repository URLs across 25 files now point at GD4AI/obsidian-llm-wiki; the sibling CLI repository did not move and its links are deliberately unchanged.
  • Ten broken in-page anchors fixed across the eleven READMEs, plus maintainer notes on review and release practice.

Dependencies

  • actions/checkout 4 → 7 · pnpm/action-setup 4 → 6 · actions/setup-node 4 → 7 · actions/attest-build-provenance 1 → 4 · vitest 4.1.10 → 5.0.0 · yaml 2.8.3 → 2.9.0.

Upgrading from v1.27.1

If you installed via Obsidian's Community plugins flow (the normal case), this release appears as a one-click update in Obsidian Settings → Community plugins. Click Update.

There is no migration and no default-behaviour change in this release: no new settings, no renamed settings, no file-format change, and no change to minAppVersion (1.11.4). The only visible difference on an existing vault is the set of pages that are no longer overwritten by a truncated rewrite.

What's accepted (and why)

  • One Obsidian review-bot warning is accepted, not fixed. The bot reports "README links to another repository with the same name". After the repository migration, the only remaining such link is the sibling CLI repository (obsidian-llm-wiki-cli): it is owned by the pre-move account, and its name begins with this repository's name, which is what the rule matches on. The link is correct — that repository genuinely lives there and has not moved. Moving it is an organisation-level decision, not part of this patch, so the warning is recorded here rather than silenced by deleting a working cross-reference. The README prefix guard was simultaneously rewritten, because its previous form could not match any github.com URL and therefore could never have caught this.
  • Known issue, not fixed here, no fix pending: ingestion can hang on a single file (Issue #703). Reported against v1.27.1 on Windows with DeepSeek. Cancel does not reach the blocked extraction, so the run must be ended by restarting Obsidian; no data ...
Read more

1.27.1

Choose a tag to compare

@github-actions github-actions released this 06 Sep 02:20
f18f722

🌟 Karpathy LLM Wiki v1.27.1

Highlights

PATCH that turns the Related sections into deterministic output — the model no longer transcribes lists it was handed — and closes the rewrite-loss classes the wave-B audit found. 46 merge commits / 194 files / +10487 / −3257 / 3993 tests passing. No breaking changes, no new required settings, no migration.

  • Related lists are now deterministic (Issue #635, PR #636). Siblings born from the same note link each other; a related name the vault already has a page for is written under that page's own title and folder; the two Related sections are rendered from the typed lists instead of transcribed by the model. Measured on a 413-note vault rebuild: dead related entries 24% → 1%, pages without a live outgoing link 103 → 0, sibling lists complete 246/246. Unicode-safe end to end.
  • A paragraph another source footnoted survives a rewrite (PR #631). guardBodyRewrite now restores sourced paragraphs a different-source rewrite dropped — 571-pair replay: 120 paragraphs restored, 164 footnotes re-attached.
  • Folder-wrong links re-pointed vault-wide (Issue #624, PR #626). 314 links corrected — prose, source-page sections, plus a new end-of-run pass over every page the run wrote.
  • The per-step thinking policy now reaches the stream path (Issue #627, PR #629). The Query Wiki answer the user waits for was thinking 43 of 55s while the keyword call sent reasoning_effort: none; stream now honours taskPolicies.
  • Vault dates are local calendar dates, not UTC (Issue #611, PR #612). All 17 date-write sites route through localDateStamp() — east of UTC, days no longer start stamped with yesterday.
  • Two gates before a contradiction record (Issue #609, PR #610). The page sentence must exist AND the source must hold the claim — measured 7 of 9 records were previously false.
  • Query no longer hides the page the user named (Issue #623, PR #625). Unicode-aware tokenizer + word-start matching + PPR-ranked merge fixed a 3,025-page German vault loading Kahneman while "Creatin" was absent.
  • Three-phase repo audit cleanup (PRs #632/#633/#634). 8 no-op fixes, one SDK wrap contract, T1/T3 lossless pass (−82 net LOC, 3932→3932 tests).
  • 8 fast-uri Dependabot alerts closed at the root (PR #637). fast-uri ^3.1.7, redundant override dropped, CI pnpm audit gate added, .github/dependabot.yml weekly auto-PRs — the fix path no longer depends on a human noticing an alert.

What's in this release

Fixed

  • Related sections rendered from the lists, not the model (PR #636, Issue #635). Three deterministic layers — core/related-shaping.ts (sibling edges by kind, vault-answering names to their own title+kind, tag values dropped against the active vocabulary, unanswered names kept and counted), core/related-sections.ts (sections written from typed lists; vault path when known, planned path otherwise; rewrite-kept entries re-resolved in front, one target once across both sections; everything else byte-identical), page-factory/related-links.ts (one helper, three call sites collapse to one line each). Default behaviour change without a switch, argued and five-line opt-in available.
  • Sourced paragraphs restored across rewrites (PR #631). preserveSourcedParagraphs in src/core/paragraph-provenance.ts matches footnoted paragraphs into the rewrite by word overlap; guardBodyRewrite composes sections + sourced paragraphs + H1 guards.
  • Folder-wrong links corrected everywhere (PR #626, Issue #624). correctRelatedLinkPrefixes re-points in prose + source-page sections; new Stage 4.5 repointLinksAfterRun reads every written page once, resolves folder-typed links against the run's own pages.
  • Stream path honours per-step thinking policy (PR #629, Issue #627). createMessageStream spreads enableThinking from applyTaskPolicy — root cause of the "streamed answer thinks" report (not b302aab, verified).
  • Local-calendar dates everywhere (PR #612, Issue #611). localDateStamp() in src/core/format.ts; full-ISO instants deliberately untouched.
  • Two gates before a contradiction record (PR #610, Issue #609). Gate 1 existing_statement checked against the page; Gate 2 stance as its own small call; demotions never silent.
  • Stop-word substrings + lex/PPR merge fixed (PR #625, Issue #623). \p{L}\p{N}\p{M} word runs, word-start needleHits, PPR-ranked mergeWithPPR.
  • Mention-only pruning no longer cuts edges to existing pages (PR #621, Issue #620). Candidate gate takes an isKnownPage predicate backed by buildVaultResolver.
  • Kept-but-collapsed sections restored (PR #618, Issue #617). Below SECTION_SHRINK_FLOOR → treated as dropped, previous block restored in place.
  • Mentions re-emit never shrinks the accumulated block (PR #616, Issue #614). Existing length is the floor.
  • Related-page rewrites never land on source pages (PR #615, Issue #613). Lookup scoped to entities/ + concepts/.
  • Contradiction source_page resolved against the page index (PR #602, Issue #601). Never trusted from the model — 3 user notes previously hit.
  • Merge triage contradictions reach log + report (PR #606, Issue #605). onContradiction callback collects both lanes.
  • Ingest ownership from source_file, not sources: (PR #596, Issue #595).
  • Picker shows disk state, not session memory (PR #600, Issue #598).
  • Cross-folder dedup routes through the semantic call (PR #589, Issue #588). Folder never decides.
  • Preamble cut no longer eats the first section (PR #580, Issue #579 via audit).
  • Cancelled ingest no longer reads as completed (PR #583, Issue #582).
  • Repetition-loop requires consecutive source occurrences (PR #572, Issue #542).
  • __proto__ duplicates rejected in task policies (PR #571, Issue #543).
  • Audit phase-1 latent bugs (PR #632). UTC-day site → localDateStamp(), ru locale gaps, dead code.
  • Dev-instrument link retarget is no longer a silent no-op (PR #591, Issue #590).

Changed

  • Two-gate contradiction lane replaces the model's word (PR #610). See Fixed.
  • Contradiction marker lane surfacing (PR #578, Issue #575). contradictions: marker read back + item-level lane given one (PR #576).
  • contentHash drift flag (PR #577, Issue #220 Tier 0 read half). Source notes changed since ingest flagged.
  • Merge calls get the note's own paragraphs (PR #579).
  • Heading spacing + blank-line runs normalised at the write gate (PR #581).
  • minAliasLength threaded to the create path (PR #559, Issue #558).
  • Gate link-markup parens no longer miscount (PR #564, Issue #562).

Refactor

  • Three-phase repo audit (PRs #632/#633/#634). Phase 1: 8 no-op changes (UTC-day site, ru locale gaps, dead appendGranularityToPrompt, byte-identical buildKnownTargets folded, createMergeCtx helper, test twin folded, graph-cache test renamed). Phase 2: openai-sdk-client onto canonical wrapReasoningContent (one wrap contract, missing </think escape + idempotence guard). Phase 3: escapeRegExp → canonical escapeRegex, 22 tests relocated into __tests__ mirrors (zero loss), 12 dead exports privatized, controller.ts report round-trip collapse (−85L), zero-consumer OUTPUT_MODES deleted.

Added

  • Merge triage contradiction lane surfaced (PRs #576/#578). Item-level contradictions get a lane; the marker's read half shows in lint.
  • contentHash drift detection (PR #577). Read half of Issue #220 Tier 0.
  • Dev-instrument link cache + resolver (PR #591). retargetLinksToPage works under the instrument.
  • Co-maintainer credit (PR #619). manifest/README/NOTICE list DocTpoint.
  • CI pnpm audit gate + .github/dependabot.yml (PR #637). Dependency security is now automated, not human-remembered.
  • SEO metadata blocks completed across all 11 READMEs (this release). DE/PT/RU/ZH-Hant gained the block EN and the other locales already had.

Docs

  • README Marp URL + LICENSE relative links fixed (PR #622, community PR by @NotAFlightRisk).
  • Wave-C/B records, audit trio, v1.27.1 prep in CHANGELOG/ROADMAP/MEMORY.

Upgrading from v1.27.0

If you installed via Obsidian's Community plugins flow (the normal case), this release appears as a one-click update in Obsidian Settings → Community plugins. Click Update and you're done — your wiki, settings, and history carry over automatically.

One deliberate default-behaviour change: the two Related sections on generated pages are now written from the typed lists (deterministic) rather than transcribed by the model — the previous behaviour produced a dead link one time in four. Existing pages are rewritten on their next ingest with the corrected links. No settings changed, no migration.

What's accepted (and why)

This release touched zero production code in the release commit itself — v1.27.1's 46 feature commits all passed Gate 1 with eslint-plugin-obsidianmd local lint at 0 errors, and the release prep commit (version bump + docs) changed only non-code files. The Bot-scan surface is unchanged from the v1.27.0 baseline; the pre-existing accepted-structural warnings (128, all documented in the v1.27.0 notes) carry over untouched. Local mirror stands in for the GitHub-hosted Bot pipeline per the v1.26.2 hardening record.

Installation

Install or update via the standard Obsidian plugin flow — your existing wiki is preserved on upgrade.

  1. From Obsidian — Settings → Community plugins → Browse → search "Karpathy LLM Wiki" → Install (or Update, if you already have it). Then toggle Enable.
  2. From the marketplacecommunity.obsidian.md/plugins/karpathywikiAdd to Obsidian.
  3. Manual — only needed if you want a specific version or are running Obsidian without the in-app marketplace. Download main.js, manifest.json, and styles.css from the release assets below into `.obsidi...
Read more

1.27.0

Choose a tag to compare

@github-actions github-actions released this 27 Aug 03:26
3464cce

🌟 Karpathy LLM Wiki v1.27.0

Highlights

MINOR that adds one new auth path on Bedrock, one new conversion backend (MinerU), and closes the frontmatter-parity chain. 36 merge commits / 181 files / +11197 / −3158 / 3677 tests passing. No breaking changes, no new required settings, no migration.

  • AWS Bedrock — three auth modes (Issue #425, PR #540). API key (existing), SSO via hand-rolled IAM Identity Center OIDC, IAM-key. Zero AWS SDK, ≈+10–15 KB. Stage 1 bedrockRegion forwarding bug (every call fell on us-east-1 regardless of the user's region setting) rides along as commit 0. PR #540 awaits an account-holding E2E of three isolated constants before full activation.
  • MinerU multi-format ingest (Issue #404, PR #404). PDF / images / Office docs via online Precise parser; new markdownConversionBackend setting. Local native path unchanged.
  • Source-page verbatim quotes (Issue #496, PR #546). Captured quotes route into sources/<slug>.md. [MENTIONS-CAPTURE] injection + maxChars: 2000 per page (mutation-verified).
  • Fix Dead Links leave_it outcome (Issue #485, PR #545). Default ON; opt-out via fixDeadLinksCreateStub. Unresolvable refs no longer spawn empty stubs.
  • Per-step taskPolicies UI (Issue #525, PR #525). task=mode[:thinking] parsing; text-mode extract baseline wired. Codex outputModeOverride honoured (PR #539).
  • Composite-key probe caches (Issue #551, PRs #552/#553). TokenKeyProber / ReasoningStripProber key on (baseURL, model) — fixes the cache-suppression bug across distinct models under the same provider.
  • npm audit HIGH → 0 (Issue #501). Top-level overrides pins brace-expansion 5.0.9 and fast-uri 3.1.5; lockfiles regenerated per pre-release-gate §2f.2.

What's in this release

Added

  • src/llm-sdk/bedrock-sso/ — hand-rolled AWS SSO + SigV4 for Bedrock (PR #540, Issue #425). bedrockAuthMethod setting: 'api-key' | 'sso' | 'iam', default 'api-key'. SSO flow: registerClientstartDeviceAuthorizationcompleteDeviceAuthorization (polling with slow_down+5s, deadline, AbortSignal). BedrockAuthManager caches credentials with expiry − 2min skew, single-flight on expiry, no silent retry storm. Sign-out overwrites SecretStorage + clears in-memory cache (mirrors Codex discipline). Three auth secrets: karpathywiki-bedrock-sso, karpathywiki-bedrock-iam, karpathywiki-bedrock-apikey.
  • Mineru PDF / image / Office backend (PR #404, Issue #404). New markdownConversionBackend (native | mineru); MinerU path accepts PDF / PNG / JPG / DOCX / PPTX / XLSX. 11 locale READMEs synced.
  • Source-page verbatim quotes (PR #546, Issue #496). buildSourceAnalysis populates top-level mentionsCaptured; [MENTIONS-CAPTURE] token injected into per-task prompt; maxChars: 2000 per page.
  • leave_it outcome for Fix Dead Links (PR #545, Issue #485). FixDeadLinksOutcome enum now create_stub | leave_it; default leave_it.
  • Opt-in skipMentionOnlyCandidates ingest gate (PR #521, Issue #514). Skips mention-only candidates from dedup prompt; default OFF.
  • One ranked candidate window for dedup + dead-link (PR #520, Issue #519). Replaces full-list fallback (recall nominal 0/18, cost real — ~40K tokens × 61% candidates). Gate-4-accepted: ~2KB text/page (~5.6MB peak @ 2.8K pages).
  • Per-step taskPolicies UI (PR #525, Issue #525). task=mode[:thinking] parsing; resolveTaskPolicy(map, task) returns {outputMode, thinking}.
  • minAliasLength setting (PR #532). Settings → Advanced, default 2, range 2..6.
  • Composite-key probe caches (PRs #552/#553, Issue #551). Per (baseURL, model) instead of baseURL alone.
  • CLI → tools/dev-instrument/ migration (PR #511, Issue #507). Replaces tools/llm-wiki-cli/ with UPSTREAM DEV-ONLY INSTRUMENT; eliminates 49 of ~52 Obsidian Bot errors. Legacy snapshot at tools/legacy/cli-v1.26.4-snapshot. Dev-instrument exit codes 0/1/2 (PR #526).
  • Schema three-layer delivery (PR #546, Issue #491). Five default-schema sections reach their owning tasks; per-task whitelist fixed; summary task writes to sources/<slug>.md.

Changed

  • extract defaults to text-mode output on LM Studio / Codex (PR #525, Issue #524). json_schema silently degrades on those providers; text-mode baseline now wired. Opt-back via taskPolicies *=json.
  • mergeFrontmatter unions incoming tags: (PR #510, Issue #509). Stored tag set no longer depends on source order; incomingTypeTag guards custom vocabulary.
  • Alias comparison keys NFC-normalised and Turkish-folded (PRs #530/#531/#537, Issues #366 p2/#484/#536). File-naming untouched; create path drops self-named aliases; typed-list folder map keyed on comparison slug (preserveCase param removed).
  • stripUnknownSections applied to generation paths (PR #529). Reviewed pages bypass.
  • OpenRouter : variants visible (PR #538, Issue #534). ~79 models including all :free now show in the catalog.
  • OpenRouter model-404 no longer a URL fault (PR #535, Issue #533). isUrlError differentiates transport failure (retry) vs 404 on model id (report and stop).
  • Test Connection blank-model guard (PR #518, Issue #517). errorNoModel per-task; probe early-returns. Localised in +11 locales.
  • OpenRouter Anthropic baseURL fixture fix (PR #516, Issue #515).
  • Stream path carries explicit task label (PR #547, Issue #469). Query Wiki now files under query-wiki, not 'untagged'.
  • cache_control contract asserted at wire, not at mock (PR #497, Issue #493 partial).
  • Type repair at intake (PR #528, Issue #527). repairTypesAgainstVocabulary fold + short repair call.
  • NoOutputGeneratedError returns in-scope composite (PR #544, Issue #506). Single bad batch no longer loses the whole run; welcome-translate thinking pinned off.
  • Five-Gate order fix in CI (carry-over, PR #487). pnpm build before pnpm test (test reads main.js for bundle shape). Bundle smoke test now in Gate 1.

Fixed

  • Duplicate-merge passthrough carries unknown frontmatter fields (PR #513, Issue #512). Closes the last #356 parity gap.
  • Constraints pass carries block-form unknown fields (PR #523, Issue #522). Parity chain complete.
  • Five default-schema sections reach their owning tasks (PR #546, Issue #491). Previously orphaned summary task now writes to sources/<slug>.md.
  • Vault-root picker's exclusion rule applied everywhere (PR #504, Issues #502/#505).
  • Codex client honours pinned text mode + spares retry on source-borne loops (PR #539).
  • Designator is (letters, type), not the opposite folder (PR #499, Issue #472).
  • CHANGELOG records the #504 upgrade note for / entries (PR #508).
  • docs(notice) brings DocTpoint attribution up to v1.26.4 (PR #498).
  • Docs orthogonalise CLAUDE/ROADMAP/CHANGELOG per one-fact-one-place (PR #500).

Internal

  • tools/dev-instrument/run-instrument.mjs:97 closes PR #511's leftover no-unsanitized/method lint gap (PR #550 follow-up). OUT_PATH is a hardcoded local constant; documented eslint-disable per Bot no-restricted-disable allowance. .mjs is out of Bot scan scope by design.
  • pnpm and npm lockfiles aligned on brace-expansion 5.0.9 + fast-uri 3.1.5 (Issue #501). Both pin keys live at top level (overrides) and under pnpm.overrides; lockfiles regenerated from local node_modules.

Upgrading from v1.26.4

If you installed via Obsidian's Community plugins flow (the normal case), this release appears as a one-click update in Obsidian Settings → Community plugins. Click Update and you're done — your wiki, settings, and history carry over automatically.

New settings (markdownConversionBackend, bedrockAuthMethod, skipMentionOnlyCandidates, minAliasLength, taskPolicies, fixDeadLinksCreateStub) all carry defaults that preserve v1.26.4 behaviour byte-identical. MINOR bump reflects user-visible surface growth (5 new bullets in 11 README Features sections), not breaking changes.

What's accepted (and why)

Local Obsidian Bot pre-review (eslint-plugin-obsidianmd@0.4.2, aligned with latest per CLAUDE.md "Bot alignment (pre-release)" rule) returned 0 errors against the v1.27.0 baseline. 128 warnings are all pre-existing accepted-structural from the v1.26.2 / v1.26.4 baselines: obsidianmd/prefer-create-el × 32, hardcoded-config-path × 25, no-nodejs-modules × 22 (all on tools/ static node:* imports — accepted per eslint.tools-bot.config.mjs), no-global-this × 17 (window / activeWindow shim, partially fixed in PR #550), prefer-window-timers × 5, no-tfile-tfolder-cast × 1. 0 new findings on touched code. The GitHub-hosted Bot pipeline (separate from CI, not a status check) was bypassed this cycle — the local mirror is mature enough to substitute per the v1.26.2 hardening record.

Installation

Install or update via the standard Obsidian plugin flow — your existing wiki is preserved on upgrade.

  1. From Obsidian — Settings → Community plugins → Browse → search "Karpathy LLM Wiki" → Install (or Update, if you already have it). Then toggle Enable.
  2. From the marketplacecommunity.obsidian.md/plugins/karpathywikiAdd to Obsidian.
  3. Manual — only needed if you want a specific version or are running Obsidian without the in-app marketplace. Download main.js, manifest.json, and styles.css from the release assets below into .obsidian/plugins/karpathywiki/, then enable the plugin in Obsidian Settings → Community plugins.

Tests

3677 tests passing (260 files). Up from v1.26.4's 3434 / 235 — +243 across the 17 feature PRs (#404, #425 × 6 test files, #485, #491+#496, #506, #509+#510+#512+#513+#522+#523, #517, #519, #520, #521, #524, #525, #527, #530+#531+#532+#536+#537, #533+#534, #539, #551).

Contributors

Read more

1.26.4

Choose a tag to compare

@github-actions github-actions released this 19 Aug 01:40
b59bbfe

🌟 Karpathy LLM Wiki v1.26.4

Highlights

A PATCH release that closes six silent-bug-class fixes that surfaced under v1.26.x — most of them invisible to the user until a vault grew past 2,000 pages or a wire-shape changed. The biggest single win: the prompt cache finally lands on the user-message block that Anthropic's render order iterates (not the system block that fell below the 1024-token cache floor). One PATCH-only release train (v1.26.3 → v1.26.4) with 20 merge commits / 3434 tests / +3786 / −1816. No breaking changes, no new required settings, no migration.

  • cache_control finally lands on the user-message prefix that Anthropic's prompt-caching render order actually iterates — Issue #449 (PR #464). Anthropic prompt caching renders tools → system → messages and matches by prefix. DocTpoint's blocking review correctly identified that the v1.26.0 wiring put the marker on the system block (a few KB — below Anthropic's 1024-token cache floor); the 75K-char user-message prefix that staticPrefix.length actually points at fell after the marker and was never cached. Fix: cacheBreakpoint now splits the first user message's text content at source-analyzer.ts:404's staticPrefix.length and emits [prefix + cache_control, suffix] as two text parts — Anthropic sees the marker on the block the offset points at, the prefix cache hits on the next call in the batch. The system block is left as a plain string (truthy-check drops system: '' from the wire, eliminating the latent "empty system block consumes one of the four cache breakpoints" risk). Measured on Anthropic Claude Sonnet 4 (cache_control tier): cache-hit rate on the 75K-char user-message prefix went from 0% (marker on wrong block) to >95% on calls 2..N of the batch. Branch D fix included: system ? { system } : {} truthy-check drops the empty-system block from the wire entirely. 5 Issue #449 tests rewritten + 1 new Branch D test added.

  • Per-note extraction prompt's slug-list block stops re-sorting on every note — Issue #452 (PR #483). The per-note extraction prompt carries a ~24.5K-token page-list block that lists every slug the run has touched so far. As the run progressed, every new note triggered a full re-sort of this list (a single fresh slug could bubble to the top, invalidating Anthropic's prompt cache for every subsequent note in the batch). Fix: freeze the slug catalog at run start. The list only grows at the end (first-seen order) instead of re-sorting on every note. Measured on the LM Studio (gemma-4-26b-a4b-qat, 2844 slugs) benchmark: 23.50s → 4.39s (4.4×) when new slugs sort into the list; 0.69s when unchanged (cache hit preserved across the batch). Companion to #449: the catalog is now prefix-stable, so the cache_control marker that #449 introduces lands on a block that actually has a stable prefix to cache. Closes #452.

  • LintReportModal stops dragging the LLM analysis section into the modal body — Issue #473 (PR #494). analysis-phase.ts:118 was reading the full wiki/index.md (whose size tracks the vault) on every modal open. The LLM analysis section was supposed to live in the Query UI reasoning panel — not the modal — but the import path put the entire file content into the modal's additionalContext payload. Fix: remove the analysis-section import from the modal entirely; restore the Query UI reasoning render path. Modal no longer grows with vault size (a 5,000-page vault was triggering a 12s modal-open hitch). Closes #473.

  • Per-step output mode + thinking policy — Issue #481 (PR #490). On an openai-compatible wire the two are not independent: a response_format on the request sets reasoning_tokens to 0 regardless of reasoning_effort, disableThinking, or the server's own switch. "Let this step think" is therefore not a flag but a decision about the output mode, and it can only be made per step — the pipeline's schema callers (extract, lemma-classify, merge-triage, the dedup pair) and its prose callers (page-generate, source-page, merge-body, related-page, complementary, pdf-convert) want opposite things from the same setting. Fix: task-policy.ts exposes task=mode[:thinking] parsing and a resolveTaskPolicy(map, task) lookup that returns {outputMode, thinking}. Default = 'default' on both axes, which means the OutputModeProber picks the mode exactly as before and the call site's own enableThinking argument passes through untouched. Per-call policy (PR #411 F5-B): source-analyzer parent (source-analyzer.ts:386) honors; JSON-repair (source-analyzer.ts:417) does NOT honor — repair needs reasoning budget to understand broken-JSON semantics, disabling produces structurally-valid-but-wrong content. The axis independence lets future comparison runs compare output mode + thinking as a single arm instead of O(3 modes × 4 thinkings) of separate rebuilds. Closes #481.

  • Ingest payload stops growing with vault size — Issue #482 (PR #484, Stages 1+2). Stages 1 (source analysis) and 2 (extraction) were rebuilding the per-page context from the full vault history on every note. The payload grew linearly with vault size and dominated the per-note ingest budget past ~2,000 pages. Fix: Stages 1+2 now trim the per-page context to a stable prefix; later stages no longer rebuild the full vault history. The LLM sees the same per-note content but the request shape is bounded by the static prefix + a per-batch variable suffix (slug catalog + granularity + batchSize + lang hints) — exactly the shape cacheBreakpoint (#449) expects. Closes #482.

  • Contradiction clamping restores withheld content to the user-visible page — Issue #492 (PR #492). clampPageSections was discarding the wholly-withheld sections (when the contradiction record exceeded the 3000-char budget, the tail was silently dropped). Fix: the clamp now uses the section list to keep logical order while still honouring the budget — withheld content is restored to the user-visible page in its original position rather than disappearing from the report. Closes #492.

What's in this release

Fixed

  • cache_control marker landed on the wrong block (Issue #449, PR #464). See Highlights. Anthropic prompt cache hit rate on the 75K-char user-message prefix went from 0% (marker on system block, below 1024-token floor) to >95% on calls 2..N. Branch D fix: empty system: '' no longer consumes an Anthropic cache breakpoint. 5 Issue #449 tests rewritten + 1 new Branch D test. Closes #449.
  • Per-note slug catalog re-sorted on every note (Issue #452, PR #483). See Highlights. LM Studio 2844-slug benchmark: 23.50s → 4.39s (4.4×) when new slugs sort in; 0.69s when unchanged. Companion to #449 — the catalog is now prefix-stable, the cache_control marker that #449 introduces lands on a block with a stable prefix. Closes #452.
  • LintReportModal grew with vault size via full wiki/index.md import (Issue #473, PR #494). See Highlights. analysis-phase.ts:118 read the full index; modal-open hitches hit 12s on a 5,000-page vault. Import path removed; Query UI reasoning restored. Closes #473.
  • openai-compat providers silently set reasoning_tokens: 0 when response_format was on the wire (Issue #481, PR #490). See Highlights. Per-step task=mode[:thinking] policy replaces the global enable-thinking setting. Default = 'default' = today's behaviour, opt-in. JSON-repair (source-analyzer.ts:417) intentionally does NOT honor — repair needs reasoning budget, disabling produces structurally-valid-but-wrong content. Closes #481.
  • Ingest payload grew linearly with vault size (Issue #482, PR #484 Stages 1+2). See Highlights. Per-page context trimmed to stable prefix; LLM sees the same per-note content but the request shape is bounded. Companion to #449 — the request shape matches what cacheBreakpoint expects. Closes #482.
  • Contradiction clamp discarded wholly-withheld sections (Issue #492, PR #492). See Highlights. clampPageSections now uses the section list to keep logical order while honouring the 3000-char budget. Closes #492.

Added

  • src/core/task-policy.ts — per-step output mode + thinking policy. Parse task=mode[:thinking] (e.g. extract=text:on, merge-triage=text:on, page-generate=-:off, *=text:on for run baseline). resolveTaskPolicy(map, task) returns {outputMode, thinking} (lookup order: specific → wildcard → default). thinkingEffort enum distinguishes bounded levels (low/medium/high) from unbounded (on) — measured on gemma-4-26b, unbounded thinking at the extraction step consumed all 16000 tokens of the batch budget and returned nothing, twice. Round-trips through formatTaskPolicyMap for run-manifest stamping.
  • src/llm-sdk/finish-reason.tsextractReasoningText(reasoning) helper. Centralises the SDK's result.reasoning shape handling (string vs array of {text} parts — Anthropic's shape) across all four SDK clients (anthropic / openai / openai-compat / openai-codex). 7 sites consolidated: anthropic-sdk-client.ts × 2, openai-codex-sdk-client.ts × 1, openai-compat-sdk-client.ts × 4.
  • Three-layer repair for DeepSeek reasoning-model ingest (Issue #474, PR #486). Layer 1: prependReasoningForParse drops prose reasoning_content when visible text is non-empty (fixes the "balanced-JSON finder walks into English text" crash class). Layer 2: case-insensitive \b[Tt]hinking\s*\n regex on the bare Thinking…Response form (fixes LM Studio / DeepSeek capital-T variants). Layer 3: existing reasoning-only guard from PR #488 (#470 follow-up).

Changed

  • extractThinkingBlocks regex is now case-insensitive on the bare Thinking…Response form (post-merge CODE Gate cleanup). LM Studio / DeepSeek reasoning models route chain-of-thought into the visible content channel wrapped in the bare Thinking\n…\nResponse delimiter; capital-T variants were...
Read more

1.26.3

Choose a tag to compare

@github-actions github-actions released this 14 Aug 02:11
3fe34f6

🌟 Karpathy LLM Wiki v1.26.3

Highlights

A PATCH release that closes three real bugs that users had been living with since the v1.23.0 AI SDK migration — repetitionPenalty finally reaches the wire (where the backend accepts it), the frontmatter writer stops emptying your sources: block, and the 3-tier output-mode state machine replaces an "elegant 2-tier fallback" that turned out to leave cloud-cohort callers with a parse-failure class the design itself created. Five PRs / 97 files changed / +9799 / −785. Test count 2992 → 3290 (+298). No breaking changes, no new required settings, no migration.

  • repetitionPenalty user setting now reaches the wire — Issue #414 (PR #453). The setting has been a silent no-op on every shipped provider since the v1.23.0 AI SDK migration dropped the pre-AI-SDK unsupportedFields blocklist: LM Studio / Ollama / llama.cpp received the wrong spelling (repetition_penalty with -ion; llama.cpp recognizes repeat_penalty per DocTpoint's type-error test on gemma-4-12b); Kimi / OpenRouter / vLLM saw the field placed under providerOptions.openaiCompatible while the AI SDK's openai-compat passthrough at @ai-sdk/openai-compatible@2.0.62/dist/index.mjs:525-540 reads providerOptions[this.providerOptionsName] (the provider id) — the key mismatch meant the lookup missed for every provider; Anthropic received the field but its Messages API has no repetition_penalty; DeepSeek / OpenAI / OpenAI Codex / Gemini / MiniMax / GLM / Bedrock-OpenAI do not list the field at all. Per-provider dialect dispatch in OpenAICompatSdkClient.buildProviderOptions: lmstudio / ollama → wire repeat_penalty (no -ion); kimi / openrouter / custom → wire repetition_penalty (snake_case, OpenAI-spec); deepseek / gemini / minimax / glm / bedrock-openai / unknown → field dropped silently. The Anthropic client now drops the field entirely instead of placing an unrecognized key on the wire — matches the 10-locale i18n text "cloud providers will silently ignore it". Known limitation: wrapWithAdvancedSettings (src/llm-client-wrapper.ts) uses Object.create(client) to inherit createMessageStream without settings injection — repetitionPenalty (and all other settings) is silently dropped on the stream path (Query Wiki, streaming UI). Tracked as #451 for v1.27.0. Closes #414.

  • Frontmatter writer stopped dropping sources: block-style entries on every constraints pass — Issue #438 (PR #450). enforceFrontmatterConstraints (src/core/frontmatter.ts:640-650) reads the original sources: key via parseFrontmatter().sources before tearing the lines apart, then writes the preserved list back into the new frontmatter on a length-gated check at :693. The bug: parseFrontmatter's ARRAY_FIELDS normalization (src/core/frontmatter.ts:93-94) coerces the empty string '' (what a bare sources: header is read as) into a one-element array [''], the length check passes, and serializeFrontmatter:437 writes sources:\n - "" — i.e. the bug re-emits the corrupted shape rather than recovering from it. Fix: filter empty / whitespace entries at the source, mirroring the aliases branch at :452. So the recovery population (a bare sources: header — the fingerprint of a page that already went through the broken constraints pass) re-emits no sources key at all, not sources:\n - "". 6-arm regression test pins: (A) bare sources: → no key emitted, (B) two valid entries preserved, (C) mixed - "" + valid entry → valid entry alone, (D) bare sources: as last frontmatter key → no - "", (F) whitespace-only - " " filtered. Pre-existing cosmetic follow-on surfaced by DocTpoint's measurement: serializeFrontmatter emits passthrough lines before sources / tags / aliases, so the first lint after this fix moves generation_complete from last to fourth position on every page with frontmatter — correct output, but it is a one-time vault-wide frontmatter churn that re-ingest / fillEmpty / merge-duplicates will repeat (constraints pass is not idempotent — each pass adds one blank line before the body via content.substring(fmEnd + 5) not stripping the leading \n). Users should expect a single frontmatter churn on the first lint after this fix. Closes #438.

  • 3-tier output-mode state machine replaces the elegant 2-tier fallback that was leaving cloud-cohort callers with a parse-failure class — Issue #443 (PR #447). The openai-compat SDK client (src/llm-sdk/openai-compat-sdk-client.ts:182) did not include response_format in its destructure list, so every LLM call site that asked for { type: 'json_object' } got no server-side JSON constraint on openai-compat providers. The destructure now carries response_format end-to-end via a new buildOutputArgs helper (src/llm-sdk/output-args.ts) that translates to the AI SDK's Output mechanism. 3-tier output-mode state machine (src/llm-sdk/output-mode-prober.ts): ordered promotion json_schema → json_object → text+prompt; the no-schema case emits Output.json() which the SDK encodes as response_format: { type: 'json_object' } for every openai-compat provider. The local-server cohort (LM Studio / Ollama / custom) may 400 on json_object — a runtime 400-strip probe (src/llm-sdk/json-object-strip-probe.ts) catches the 400, retries once with output omitted, and caches the per-baseURL strip decision. Path 2 fix: AI SDK's Output.json() and Output.object() BOTH call parseCompleteOutput (ai@6.0.230/dist/index.mjs:3899), which throws NoObjectGeneratedError on malformed JSON — not APICallError. OpenAICompatSdkClient.createMessage now catches NoObjectGeneratedError first, returns err.text verbatim so caller-side parseJsonResponse + greedy regex + LLM repair runs. Phase B (11 caller migrations + 9 Zod schemas): typed-output variant LLMClient.createMessageWithOutput (optional method, backward-compat) returning {text, output?, outputMode, finishReason, usage?}; new src/llm-sdk/output-schemas.ts with SeedSelectorSchema / QueryKeywordsSchema / MergeTriageSchema / LinkOrphanSchema / FixDeadLinkSchema / QueryViewValueSchema; callers prefer result.output on Tier 0 success, fall back to parseJsonResponse(text) on Tier 1 / 2. Per CLAUDE.md "one PR per call site" rule, each migration ships as a separate commit. Closes #443.

What's in this release

Fixed

  • repetitionPenalty user setting was a silent no-op on every shipped provider (Issue #414, PR #453). See Highlights. Per-provider dialect dispatch (LM Studio / Ollama → repeat_penalty; Kimi / OpenRouter / customrepetition_penalty; 6 IDs drop silently). The Anthropic client drops the field entirely instead of putting an unrecognized key on the wire. 3212 tests / 230 files (+3). Closes #414.
  • Frontmatter writer dropped sources: block-style entries on every constraints pass (Issue #438, PR #450). See Highlights. Empty / whitespace entries now filtered at source, mirroring the aliases guard. Recovery population (bare sources: header) re-emits no key. Users should expect a one-time frontmatter churn on the first lint after upgrade. Closes #438.
  • repetitionPenalty UX hint names a setting that never reached the wire on 5 of 15 provider IDs (PR #454). User E2E on qwen3.5-9b surfaced a 2-axis defect: (1) the placeholder detector at src/core/json.ts:153 only matched {"": ""} and missed the empty-object / empty-array variants {"": {}} / {"": []} that some models emit in place of an empty object; (2) buildRepetitionPenaltyHint fired on every provider regardless of whether repetitionPenalty reached the wire, so users on Anthropic / DeepSeek / Gemini / MiniMax / GLM got a tail message saying "reduce or clear this setting" for a setting their backend had silently dropped. Fix A: single-pass conjunction at json.ts:181 accepts any object whose keys are all '' AND whose values are all empty. Fix B: buildRepetitionPenaltyHint checks repetitionPenaltyWireField(provider) === null and returns '' on the 5 dropping IDs; the dialect helper is exported as a module-level repetitionPenaltyWireField(provider) and re-exported from openai-compat-sdk-client.ts so the two paths cannot drift apart. 63 tests pass across 4 touched files. Closes #443 follow-up.
  • 5 UX defects on the maintainer's vault E2E (PR #448). B1: Fetch Models classified auth failures (HTTP 401 / 403) as Network, so users with expired keys saw a misleading "Failed to connect" notice — reclassified to Auth. B2: status-bar cancel label duplicated because the raw PDF-stage segments were passed verbatim into the cancel-line builder — fixed by emitting only the structured segments. B3: lint dedup cross-type filter did not surface rejected-pair count in the diagnostic comment. B2.5: full status-bar i18n (10 locales) so cancel / progress strings no longer carry hardcoded English tails. Toast: 5 lint / ingest Toast strings localized (ingest start, batch check, lint findings, etc.).
  • parseJsonResponse parse failure at path-resolution.ts:220 read as "no match" (Issue #407 Stage 1, PR #444). The site's LLM semantic dedup call now goes through parseJsonResult (the union from PR #436). On {ok: false, reason} the function logs the reason + raw length and returns the slug path as a named failure rather than as an answer to the question; the match: false branch is reached only when the reply parsed. Counter-test pins the new branch: a well-formed {match: false} is not reported as a parse failure.

Added

  • LLMClient.createMessageWithOutput typed-output variant (Issue #443, PR #447 Phase B). Optional method on the LLMClient interface returning {text, output?, outputMode, finishReason, usage?}. output is populated when the AI SDK's Output.object({schema, name})...
Read more

1.26.2

Choose a tag to compare

@github-actions github-actions released this 09 Aug 00:15
940122b

🌟 Karpathy LLM Wiki v1.26.2

Highlights

A surgical PATCH release — 1 PR (3 commits, 7 files changed). No breaking changes, no new settings, no migration. The headline: v1.26.1 shipped a blocking no-unsafe-call Error in tools/llm-wiki-cli/src/obsidian.ts that local pnpm lint could not see, because local lint scans src/ only while the Obsidian review bot scans the whole repo .ts tree. v1.26.2 closes that gap on two fronts: the error is fixed end-to-end, and a new pnpm lint:tools-bot scan surfaces what the Bot would flag during development.

  • obsidian.ts unsafe-call chain fixed (PR #442). await import(<dynamic-arg>) left request as any, cascading 6 unsafe-* warnings and triggering no-unsafe-call → Error. Split into two literal await import('node:https') / await import('node:http') branches with an explicit typeof import('node:http').request annotation — Error + the entire unsafe-* cascade disappear in one stroke.

  • Platform.isDesktop AST guards on the three runtime-loaded node:* imports (PR #442). During the fix we discovered that obsidianmd/no-nodejs-modules exempts based on AST guard-detection (function-start if (!Platform.isDesktop) throw), not on "bare dynamic import" as the assumption baked into PR #418/#433's patterns implied. obsidian.ts:requestUrl() and node-globals.ts:plainConsole() now carry those guards. The CLI's own Platform shim hardcodes isDesktop: true, so they never throw at runtime — they declare the desktop-only invariant the rule requires.

  • pnpm lint:tools-bot closes the local blind spot (PR #442). The local Gate 1 (eslint src/) was blind to tools/. New eslint.tools-bot.config.mjs + package.json script gives you the Obsidian Bot's view of tools/ locally during development, so the next release doesn't need a Bot trip to surface what local lint should have caught. Plus: release-skill v1.7.0 now mandates an Obsidian Bot pre-review between tag and publish (Step 6b.5, HARD STOP ②) — the gate that should have caught this.

Changes

Added

  • pnpm lint:tools-bot informational scan (#442). New eslint.tools-bot.config.mjs (obsidianmd recommended ruleset scoped to tools/**, type context from tools/llm-wiki-cli/tsconfig.json, Node globals declared) + a matching package.json script (|| true, informational — never gates CI). Developers now see the Bot's view of the CLI tree locally instead of discovering it post-submission.
  • Platform.isDesktop AST guards on the three runtime-loaded node:* imports (#442). See Highlights. Mirrors src/llm-sdk/openai-codex/loopback-flow.ts:156-160.

Fixed

  • obsidian.ts:117 blocking unsafe-call Error (#442). Split to literal dynamic branches + explicit typeof import('node:http').request annotation; the entire unsafe-* cascade disappears. Resolves the only Error the Bot flagged on v1.26.1's submission.
  • obsidian.ts:158 requestUrl().json contract (#442). JSON.parse(text) as unknown + try/catch that re-throws with status context, matching the Obsidian host's requestUrl().json behaviour on bad JSON.
  • main.ts:443 loadSettings JSON.parse argument type (#442). Now typed as Partial<LLMWikiSettings> | null to match applySettingsMigrations' declared parameter; eliminates no-unsafe-argument.
  • main.ts:647 globalThis.crypto.subtlecrypto.subtle (#442). Node 18+ exposes crypto as a global; the explicit globalThis. prefix was tripping obsidianmd/no-global-this (a no-disable rule).
  • vault.ts:291 redundant as Record<string, unknown> | null removed (#442). parseFrontmatter already returns a FrontmatterData | null whose index signature is compatible — Bot flagged as no-op assertion.
  • node-globals.ts:28 (...args: any[])(...args: unknown[]) (#442). Console constructor accepts unknown[]; eliminates a Bot-flagged bare any.

What's accepted (and why)

The Obsidian Bot pre-review of the v1.26.2 baseline returned 0 Error and 7 warnings on tools/llm-wiki-cli/src/. All 7 are accepted-structural to a Node CLI and documented in CLAUDE.md "Bot compliance invariant":

Warning class Locations Why accepted
Static node:fs / node:fs/promises / node:path / node:util imports main.ts:16-18, vault.ts:12-14 Dynamic form would break the 14 parser-contract tests that pin parseCliOptions as sync; namespace-style nodePath.* / fs.* call sites can't be top-level-awaited.
.obsidian literal main.ts:451, vault.ts:25 Vault#configDir is an Obsidian host API; the CLI has no Vault object to read it from.
console.log × 48 main.ts + obsidian.ts CLI is a terminal program; stdout is its only user channel. The Bot rule is a heuristic check, not an ESLint rule.
globalThis shim node-globals.ts:27-29, 38 Must stub window / activeWindow for Obsidian-compat; no alternative API.

Long-term plan: move the CLI to a separate repo ([[project_v1_27_0_cli_split_planning]]) so tools/ falls outside the Bot's scan scope.

Installation

  1. From Community Plugins: Obsidian Settings → Community Plugins → Browse → search "Karpathy LLM Wiki"
  2. From Community Plugin Website: community.obsidian.md/plugins/karpathywiki
  3. Manual: Download main.js, manifest.json, styles.css from the release assets

Upgrading from v1.26.1

No migration needed. Replace main.js, manifest.json, and styles.css in your vault's .obsidian/plugins/karpathywiki/ directory. Existing wikis are not touched on upgrade. All changed behaviour is internal to the headless tools/llm-wiki-cli/ CLI — no plugin-runtime impact.

Tests

2992 tests passing (218 files). Unchanged from v1.26.1 — no behaviour change in this release, no new test coverage needed for the existing 1 PR (the fixes are mechanical type/guard additions and the Bot pre-review is the gate).

Contributors

  • @green-dalii — Maintainer. PR #442 (3 commits): fix + tooling + Bot invariant docs.

Full Changelog

1.26.1...1.26.2

1.26.1

1.26.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Aug 11:51
f5ea82e

🌟 Karpathy LLM Wiki v1.26.1

Highlights

A PATCH release — 21 PRs of high-ROI bug fixes and hardening since v1.26.0. No breaking changes, no new settings, no migration. The headline: the engine's largest phase now tells you which step is slow, the dedup concurrency-halving mechanism actually fires (it was dead code), and 24 Dependabot alerts are closed to zero.

  • Per-step LLM timing ledger (PR #409, eucher). Every createMessage call is now tagged with its pipeline step; a process-global ledger accumulates call count + wall-millis per label at the one seam every call passes through. Page generation — previously one interval covering path resolution, dedup, page writes and merge routing — now decomposes into per-step timings, so a slow ingest says which of the four to look at. Unlabelled calls land in an 'untagged' bucket rather than being dropped, so the table never under-reports the run it exists to explain.

  • Parse failures now have a name (#407 Stage 0, PR #436, DocTpoint). parseJsonResponse failures — empty body, malformed JSON, or exception — were indistinguishable from a legitimate negative answer at 7-12 call sites. The new parseJsonResult discriminated union gives each failure a reason ('empty' / 'malformed' / 'exception'), and parsed?.field || fallback stops compiling at a call site that ignores the distinction. No behaviour change in this release; the 8 call sites port to the union in follow-up PRs (Stages 1+2), one per blast radius.

  • Dedup in-scan concurrency halving actually fires now (CR-1, PR #416). The consecutiveThrottleChunks counter was declared inside the chunk loop, resetting to zero on every chunk — the halving threshold was unreachable. Hoisted above the loop so the counter accumulates across chunks. This corrects the v1.26.0 Batch 2 attribution: the 979s→365s e2e gain came from retry/backoff, not halving.

  • Six reasoning-budget-sensitive token caps raised to 3000 (#403, PR #429). Short-JSON call sites ({strategy, path}, {keywords: []}, {kind: "entity"}) were sized for non-reasoning models; on reasoning-capable models the deliberation consumed the max_tokens budget before any content. DocTpoint measured 14/45 calls truncated (13 empty) and complementaryAppend at 100% miss on its 600 cap. Uniform bump to 3000 restores 50–80% reasoning headroom.

  • A page keeps its own H1 through an LLM body rewrite (#419+#435, PRs #422+#437, DocTpoint). Two silent-corruption bugs in reassertH1 (a title with $ or & got mangled by String.replace escape processing; the first occurrence anywhere in the body took the restore) are repaired by splicing at the matched line index. Then findH1 hardens the read side: a # shell comment inside a code fence or a --- frontmatter block can no longer be adopted as a page's title.

  • Query "Save to Wiki" stopped lying about silent no-ops (#398, PR #432). When dedup found the conversation already covered, the UI flashed "saved!" while writing nothing. The notice now appends the actual verdict, and a diagnostic console.warn shows the LLM's dedup decision in DevTools.

  • 24 Dependabot alerts → 0 (PR #439). All 24 were transitive devDependencies (fast-uri, undici, postcss, vite); zero runtime impact (main.js contains none of them). Resolved by bumping the root devDeps that pull them in.

Changes

Added

  • parseJsonResult discriminated union for LLM JSON parse outcomes (#407 Stage 0, PR #436). {ok: true, value} / {ok: false, reason: 'empty' | 'malformed' | 'exception'}. parseJsonResponse stays as a byte-compatible wrapper — identity verified over 776 input combinations. Call-site migration follows in Stages 1+2.
  • Per-step LLM timing ledger (PR #409, eucher). src/core/llm-task-usage.ts accumulates calls + wall-millis per task label at the createMessage seam. Snapshot-diff semantics (a reset would be wrong the moment two ingests overlap).

Changed

  • CLI: node-globals.ts:9 static node:console import → dynamic form (PR #433). Last static node:* import in tools/llm-wiki-cli/; the Obsidian review bot's obsidianmd/no-nodejs-modules rule rejects static node-builtin imports. installObsidianGlobals() is now async; the sole caller already awaits.

Fixed

  • Duplicate sources: frontmatter key on stub-created concept pages (#399, PR #405). v1.25.11 regression producing invalid YAML (two top-level sources: keys) on post-stub ingests. Corpus: 321 affected pages.
  • CLI per-run bundle isolation (#408). Two concurrent ingest --help runs raced esbuild's in-place bundle write (14/60 failed). Per-process bundle name + liveness sweep + post-import cleanup.
  • Dedup in-scan concurrency halving was inert (CR-1, PR #416). Counter reset per chunk; hoisted above the loop. Attribution correction: only retry/backoff delivered the 979s→365s gain.
  • Six reasoning-budget-sensitive TOKENS_* caps → 3000 (#403, PR #429). See Highlights.
  • CHANGELOG v1.26.0 entry: thinking/chat_template_kwargs never reached the wire — mechanism corrected (#420, PR #420). The SDK's filter() is a passthrough; the fields were misaddressed under the hardcoded openaiCompatible key that no shipped provider id matches. reasoningEffort: 'none' is the only verified-working disable.
  • yaml declared in devDependencies (#424, PR #431). Clean-install regression — pnpm strict isolation nested yaml under vite; the append-source-slug test failed to collect on a fresh install.
  • Query wiki silent-success defect on Save (#398, PR #432). Notice now surfaces the dedup verdict instead of an unconditional "saved!".
  • --seed docs narrowed to what is measured (#423, PR #434, DocTpoint). Local servers accept the flag and then ignore it (5× seed:42 @ temp:1.0 → 5 distinct outputs); the promise "honoured by every local server" was unmeasurable and removed.
  • H1 re-assertion hardened against frontmatter and code-fence lines (#419+#435, PRs #422+#437, DocTpoint). See Highlights.
  • Bedrock Stage 2 planning entry recorded (#425, PR #426). Zero-AWS-SDK SSO path planned; cancels the ≥3-request gate.

Security

  • 24 Dependabot alerts closed (PR #439). All transitive devDeps; zero runtime impact. Known CI-only carry-over: brace-expansion npm-audit reachability through lint plugins (Dependabot does not flag it) — out of scope, runtime bundle unaffected.

Installation

  1. From Community Plugins: Obsidian Settings → Community Plugins → Browse → search "Karpathy LLM Wiki"
  2. From Community Plugin Website: community.obsidian.md/plugins/karpathywiki
  3. Manual: Download main.js, manifest.json, styles.css from the release assets

Upgrading from v1.26.0

No migration needed. Replace main.js, manifest.json, and styles.css in your vault's .obsidian/plugins/karpathywiki/ directory. Existing wikis are not touched on upgrade. All changed behaviour is backward-compatible — the new settings-related fields are optional and default to the previous constants.

Tests

2992 tests passing (218 files). +64 net since v1.26.0, including:

  • src/__tests__/core/llm-task-usage.test.ts — per-step ledger (PR #409)
  • src/__tests__/wiki/lint/llm-phases/dedup-phase.test.ts — CR-1 halving regression guard + classifyTiers (PR #416)
  • src/__tests__/root/json.test.ts — 10 new parseJsonResult tests + 776-combination identity via the wrapper (PR #436)
  • H1 splice + findH1 line-walk tests (PRs #422 + #437)
  • Query-save silent-success regression guards (PR #432)

Contributors

  • @green-dalii — Maintainer. 9 PRs: #416 (CR-1 halving), #426 (Bedrock plan), #429 (#403 caps), #430/#440 CHANGELOG sweeps, #431 (#424 yaml), #432 (#398 silent-save), #433 (node-globals), #439 (Dependabot 24-alert). Release prep + verification.
  • @DocTpoint — 8 PRs: #418 (CLI requestUrl no-fetch-timeout), #420 (thinking-mechanism correction), #422+#437 (H1 re-assert hardening), #427 (shim regression guard), #434 (--seed measurement), #436 (#407 Stage 0 parse union), #440 (CHANGELOG [Unreleased] sweep). Also reported #403 (reasoning-token caps), #423 (--seed over-promise), #424 (yaml clean-install), #407 (parse-failure indistinguishability).
  • @eucher — 2 PRs: #408 (CLI per-run bundle isolation), #409 (per-step LLM timing ledger).
  • @borthwick — PR #405 (duplicate sources: frontmatter fix) + reported #399 (321 affected pages in their vault).
  • @workflowsguy — reported #398 (Query "Save to Wiki" silent-success no-op).

Full Changelog

1.26.0...1.26.1

1.26.0

Choose a tag to compare

@github-actions github-actions released this 06 Aug 01:10
e70eafb

🌟 Karpathy LLM Wiki v1.26.0

Highlights

A MINOR release that introduces the headless ingest CLI as a first-class user-facing tool, ships Russian i18n (11 locales), and tightens the engine with five P0+P1 hardening batches. No breaking changes to the plugin runtime; deprecated CLI flags throw errors rather than silently translate.

  • Headless ingest CLI is now discoverable (pnpm llm-wiki, npm bin llm-wiki). The tools/llm-wiki-cli/ engine previously shipped with no bin, no pnpm script, and no mention in any README — a fresh clone could not find it. Exposes llm-wiki as the executable name, so it can grow beyond ingest into a general wiki-management CLI (lint, query, mutation) without a later rename. Three-state --thinking-mode data-json | plugin-off | server-default replaces the misleading two-state --thinking on|off (the old flag was a footgun — on looked like "enable reasoning" but actually meant "defer to server default"). --max-rounds is renamed to --round-base because the old name actively misled users about what it set.

  • Real wire-level force-disable thinking — 4-layer fallback (PR #411). The previous thinking.type = 'disabled' attempt was silently stripped by the AI SDK's zod schema (verified at @ai-sdk/openai-compatible@2.0.62/dist/index.mjs:322-344). Layer 1: reasoningEffort: 'none' passes the zod filter and emits as reasoning_effort: 'none' on the wire — the only verified-working disable. Layer 2: co-emit thinking / chat_template_kwargs for the Anthropic path. Layer 3: on HTTP 400 mentioning reasoning_effort / thinking / chat_template, retry once with reasoningEffort stripped; per-baseURL cache prevents infinite loops. Layer 4: prompt-level "Do not reason step by step" in the dedup prompt. On the 2141-page vault, dedup wall-time moved from 979s → 365s (Batch 2 retry only) → 151s after Layers 1-3 went live (−85% vs baseline, −59% vs Batch 2). Per-call thinkingPolicy ships in v1.26.x PATCH — JSON-repair path always allows reasoning (DocTpoint measured that disabling it produces structurally valid JSON with wrong content).

  • Dual-key bucketed dedup (Batch 1, PR #401). partitionPagesMultiBucket partitions pages into tp-prefix + lh-link-hash buckets before the O(n²) generateDuplicateCandidates scan. ≥95% recall on synthetic N=200 pages vs 80-90% on the previous single-bucket baseline; memory peak O(N² candidates) → O(B² per bucket). checkCancelled hook at bucket boundary so users can interrupt mid-scan.

  • Cross-type dedup candidate expansion (Batch 2, PR #410). generateDuplicateCandidates now surfaces candidates across entity / concept / file types when the shared-link signal crosses type boundaries. Inline empty-response retry + 500ms backoff on transient burst load. New dedup-threshold inputs in Settings → LLM Configuration → Advanced → Custom ("Duplicate detection thresholds" subsection): shared-link duplicate threshold, body-similarity floor, title/alias similarity threshold — leave blank = constant default. Tier-1 cutoff (0.6) stays constant; user-tunable would let users silently flood or drop LLM candidates.

  • Russian i18n (PR #397). 667 new keys in src/texts/ru.ts; full docs/README_RU.md translation; 11-way language switcher across all READMEs; system-prompt section labels for wiki output. 10→11 locales with strict bidirectional parity enforced by src/__tests__/root/i18n-parity.test.ts.

  • Three DocTpoint contributions round out the surface:

    • PR #357 — source-slug = page-lemma deterministic merge. Re-ingests of a source that produced a page can no longer fail to merge into that page on the second pass.
    • PR #392 — vault-wide link retarget for mergeDuplicates. Previously links from sibling pages pointing at either the surviving or the deleted page weren't rewritten. Now retargeted vault-wide and across every alias form.
    • PR #396created: provenance. create-page.ts and fill-empty-page.ts now take created: from the caller (new Date().toISOString()); never read from the LLM-generated content.

2928 tests passing (213 files). +184 net since v1.25.11.

Changes

Added

  • Headless ingest CLI (pnpm llm-wiki, npm bin llm-wiki): tools/llm-wiki-cli/ shipped as a discoverable executable. Three-state --thinking-mode enum, renamed --round-base flag, 57 new tests in main.test.ts. PRs #372 + #387.
  • 🛠️ Tools H2 section in all 11 READMEs pointing at tools/llm-wiki-cli/README.md.
  • Russian i18n, full UI + wiki-output + README (PR #397): 667 keys in src/texts/ru.ts, full docs/README_RU.md, 11-way language switcher, system-prompt section labels for wiki output. i18n-parity test enforces bidirectional coverage.
  • Real wire-level force-disable thinking — 4-layer fallback (PR #411): see Highlights above.
  • Dual-key bucketed dedup (PR #401, Batch 1): partitionPagesMultiBucket partitions pages into tp-prefix + lh-link-hash buckets before O(n²) scan. 10 new tests in duplicate-detection.test.ts + dedup-phase.test.ts pin ≥95% recall on synthetic N=200.
  • Cross-type dedup candidate expansion (PR #410, Batch 2): shared-link signal crosses entity / concept / file boundaries; new settings inputs for shared-link / body-similarity / title-alias thresholds. 11 new tests pin the threshold contract.
  • Dedup parse-failure routing (PR #411, Batch 7): structured type: 'parse-failure' discriminator on dedupFailures entries; isRateLimitFailure accepts a structured item and bails early on type: 'parse-failure'. CR-3 wiring fix made the structured branch reachable in production (detectRateLimitFailures + dedup-phase consumer now pass the full item, not f.reason string).
  • Source-slug = page-lemma deterministic merge (DocTpoint, PR #357, closes #348): re-ingest of a source cannot fail to merge into its own subject page on the second pass.
  • Vault-wide link retarget for mergeDuplicates (DocTpoint, PR #392, closes #386): sibling-page links pointing at the surviving or deleted page are now retargeted vault-wide and across every alias form before the delete.
  • created: provenance fix (DocTpoint, PR #396, closes #388): create-page.ts + fill-empty-page.ts take created: from the caller; never read from the LLM-generated content.
  • Wiki-folder-scope prefix fix (Hashim1999164, PR #384): unanchored prefix leaks closed at the predicate level.
  • FolderSuggestModal leak fix (#383 PR #384 follow-up, PR #389): isAtOrInFolderScope primitive closes the wiki-folder-as-pickable-source leak.
  • Bottom "Advanced settings" panel (PR #395): hosts the 3 dedup threshold inputs + lintDedupIncludeSources toggle. Separate from the LLM Advanced section (which retains temperature, repetitionPenalty, forcePdfSupport).

Changed

  • --thinking on|off--thinking-mode data-json | plugin-off | server-default. The old flag was a two-state surface that hid the three outcomes the plugin can actually produce. The new flag makes all three states explicit. The legacy --thinking throws a deprecation error pointing at the new flag and the v1.27.0 removal target; it does NOT silently translate.
  • --max-rounds--round-base. The old name was actively misleading: it set the granularity's maxBatchesBase field, not a ceiling. Renamed to describe what the flag actually sets. The internal GRANULARITY_CONFIG.maxBatchesBase field is unchanged (engine contract, not CLI surface). Legacy --max-rounds throws a deprecation error.
  • Picker exclusion rule centralised (PR #389): FileSuggestModal, FolderSuggestModal, and MultiFileSuggestModal previously each open-coded the wiki + configDir filter. They now share a single isExcludedFromSourcePicker primitive in src/core/folder-scope.ts.
  • Dedup threshold constants extracted + user-tunable (PR #395 + PR #410): LINT_DEDUP_JACCARD_LINK_THRESHOLD, LINT_DEDUP_JACCARD_BODY_GATE, LINT_DEDUP_BIGRAM_THRESHOLD extracted from src/wiki/lint/duplicate-detection.ts to src/constants.ts. generateDuplicateCandidates now accepts a DuplicateDetectionThresholds options-object.

Fixed

  • Two-marker (verb + field) classifier for reasoning-field 400 errors (Batch 6 CR-2): the previous single-substring classifier included the bare word thinking, which collided with model names (kimi-k2-thinking, qwen3-235b-a22b-thinking-2507, glm-4.6-thinking). Any 400 on these models — bad model name, context-length exceeded, max_tokens mismatch — was misclassified as a reasoning-field rejection, permanently marked the baseURL as "strip", and consumed the 400 so the token-key fallback never fired. The new two-marker classifier rejects all four false positives while still catching real rejections.
  • CR-3 wiring fix: isRateLimitFailure structured-form branch was previously unreachable in production because consumers passed f.reason (string), not the full item. Both detectRateLimitFailures and the dedup-phase consumer now pass the full item; regression test in rate-limit.test.ts exercises the discriminator through detectRateLimitFailures.
  • ReasoningStripProber Map→Set (PR #411 simplify): Map<string, true> converted to Set<string>; removed the dead invalidate(baseUrl?) overload (zero production callers — only tests used it).
  • Auto-maintain Phase 2 extracted to module function (normalizeSourcesInFolder in src/core/sources-normalizer.ts): previously inline in runStartupCheck which sleeps 3 seconds and depends on the wikiEngine/plugin surfaces — neither testable from the startup surface. The new module function mirrors the Phase 3 shape (findIncompletePages).

Internal

  • Dead-code-as-docs governance (Batch 4 policy): exported helpers with zero production importers have a one-release half-life. PR #406 deleted the v1.25.10 PATCH P1-1/P1-2 unwired helpers (lint-analysis-cache.ts + lint-smart-skip.ts) + stale...
Read more

1.25.11

Choose a tag to compare

@github-actions github-actions released this 31 Jul 07:34
081666c

🌟 Karpathy LLM Wiki v1.25.11

Highlights

A focused PATCH on v1.25.10 carrying three locked items from the 2026-07-30 triage, plus a code-quality cleanup pass. No new features, no API surface changes, no breaking changes — every change ships with a reproduction on a real-world corpus or a measurable improvement:

  • Freshly generated pages now stamp sources: provenance at create time (#365). Previously the create-page path relied on the LLM to remember to write the sources: frontmatter field. When the model forgot — common with smaller / quantized providers — the page entered the wiki with no provenance trail, and later re-ingest could not tell which source had produced which page. A new appendSourceSlugToFrontmatter helper at src/wiki/page-factory/create-page.ts splices the source slug into the existing frontmatter in a single linear pass — output is byte-shape identical to merge-page.ts:93, so pages written through either path now look the same to downstream readers.

  • Internal README links now resolve in the Obsidian Community Plugin browser (#375). The plugin's manifest README.md is rendered by the in-app Community Plugin browser, which strips relative cross-file links. 10 READMEs (EN + 9 locales) + docs/PDF-OCR-GUIDE.md now use absolute https://github.com/green-dalii/obsidian-llm-wiki/blob/main/... URLs everywhere a user might click between documents. Image refs are intentionally exempted — GitHub serves them via the absolute URL anyway.

  • Status bar now reports the active stage by name (#169). "Reading source…" / "Extracting entities…" / "Running LLM analysis…" / "Generating pages…" — each stage now has its own fine-grained label (15 keys × 10 locales: 7 ingest + 3 PDF + 5 lint SCAN phases). Not ETA, never ETA — the wall-clock variance across LLM providers and corpus sizes makes time estimates unreliable, but knowing which phase the plugin is in helps users decide whether to wait or interrupt.

  • Query turn indicator redesigned as a windowed dot stack. The previous v1.23.2 indicator grew to ~3k px tall on a 200-turn conversation and overflowed the history container, losing its purpose as a "where am I in the conversation" anchor. The new design keeps one dot per turn but constrains the indicator to a fixed window inside the history container (25% from top, 50% from bottom of the panel). As the user scrolls through the conversation, dots roll past the window edges — the same ChatGPT/Grok "where am I in the conversation" feel — without the dot stack itself growing unboundedly. Hover tooltips still show the question text; clicking a dot scrolls the corresponding turn into view.

2744 tests passing (204 files). +31 net since v1.25.10.

Changes

Fixed

  • Freshly generated pages now stamp sources: provenance at create time (#365). The appendSourceSlugToFrontmatter helper splices the source slug into the existing frontmatter in a single linear pass — output is byte-shape identical to merge-page.ts:93. 5 new tests in create-page.test.ts pin the byte-identical contract.
  • Internal README links now resolve in the Obsidian Community Plugin browser (#375). Relative cross-file links are broken in the in-app reader; the patch moves them to absolute https://github.com/... URLs. 12 new tests in readme-links.test.ts enforce the invariant across all 10 READMEs + PDF-OCR-GUIDE.md. Image refs intentionally exempted.
  • Frontmatter fence guard accepts malformed closing-fence shapes. A previous parser tolerated a malformed --- line at end-of-file on the read path; this patch tightens the guard so the create path emits a single, canonical --- closing fence.

Added

  • Fine-grained status-bar stage labels (#169). STAGE_KEYS canonical ordered list (15 keys) lives in src/core/ingest-stages.ts. buildIngestStatusBarText accepts a 4th stage parameter. analysis-phase migrated from the old generic lintStatusAnalyzing to the new lintStageAnalyzing. NOT ETA — labels are stage names, never wall-clock estimates. 7 new tests in ingest-stages.test.ts + status-bar.test.ts pin the 4-arg contract.
  • Turn-indicator redesigned as a windowed dot stack (Query UX). The previous v1.23.2 indicator grew to ~3k px tall on a 200-turn conversation and overflowed the history container, losing its purpose as a "where am I in the conversation" anchor. The new design keeps one dot per turn but resolves the indicator's top/bottom offsets to a fixed window inside the history container (25% from top, 50% from bottom of the panel). As the user scrolls, dots roll past the window edges, giving the ChatGPT/Grok "where am I in the conversation" feel. Hover tooltips still show the question text; clicking a dot scrolls the corresponding turn into view.
  • EN README banner polish. Restored the "Obsidian Review Perfect Score" badge line + "Local-first • No backend • GDPR-Friendly" privacy line; tightened the comparison table from 12 rows to 8 (collapsing 4 near-duplicates); added a star CTA at the bottom of Quick Start.
  • Ecosystem section (new EN H2). Documents MinerU online conversion as an alternative to local PDF OCR for users who cannot run local OCR pipelines.

Internal

  • 5-agent audit applied 4 cleanups (4× simplify angle + 1× code-review max-effort):
    • F2 — 4× frontmatter re-parse eliminated on the create-page path; the new appendSourceSlugToFrontmatter helper is single-pass linear.
    • F4analysis-phase migrated to lintStageAnalyzing; old generic lintStatusAnalyzing key removed.
    • F5 — 30 dead i18n entries deleted across 10 locales (lintStatusReading, lintStatusDuplicates, lintStatusScanningLinks × 10).
    • F6 — dead fitIndicatorToContainer alias export removed from turn-indicator.ts (zero callers).
  • Indicator redesign iterated. Three earlier variants (v1 dynamic gap, v2 12-slot sliding window, v4 percentage-based translation) were tried; v4's percentage-based positioning was broken because the JS path in syncIndicatorWindowPx writes explicit pixel values on every buildTurnIndicator call, overriding the CSS percentage fallback. The shipped version (v6 windowed-dot-stack) uses percentages for layout intent but always resolves them to pixels in JS — a pattern that survives container-resize without the brittle CSS-dependency. The three earlier iterations' findings are preserved in turn-indicator.ts's v1.25.11 PATCH header comment.

Installation

  1. From Community Plugins: Obsidian Settings → Community Plugins → Browse → search "Karpathy LLM Wiki"
  2. From Community Plugin Website: community.obsidian.md/plugins/karpathywiki
  3. Manual: Download main.js, manifest.json, styles.css from the release assets

Upgrading from v1.25.10

No migration needed. Replace main.js, manifest.json, and styles.css in your vault's .obsidian/plugins/karpathywiki/ directory.

Existing wikis are not touched on upgrade. Pages generated by older versions can be re-ingested to retroactively stamp the sources: field; the formatter on the read path tolerates the missing field gracefully, so existing wikis continue to load and query without re-ingest.

Tests

  • 2744 tests passing (204 files). +31 net since v1.25.10:
    • +7 ingest-stages.test.ts + status-bar.test.ts (4-arg buildIngestStatusBarText + STAGE_KEYS enumeration)
    • +12 readme-links.test.ts (absolute-URL invariant across 10 READMEs + PDF-OCR-GUIDE.md)
    • +5 create-page.test.ts (byte-identical output vs merge-page.ts:93 for appendSourceSlugToFrontmatter)
    • +2 frontmatter.test.ts (fence guard)
    • +5 misc tests from the simplify follow-up e02a33d

Contributors

Code contributors (commits in the 1.25.10 → 1.25.11 range):

  • @green-dalii — All 12 commits in this PATCH range. Implementation: #365 Plan A appendSourceSlugToFrontmatter helper (commit 9289bdd), #375 absolute-URL rewrite (commit 7588034), #169 fine-grained status-bar stage labels (commit 98f180c), 5-agent audit follow-up (commits c191878, e02a33d), docs polish (commits 5b18655, 94c4c72), turn-indicator UX restore (commit a300d1d), release-prep (commit 527a46f).

Issue reporters (issues closed by this release):

  • @DocTpoint — reported #365 "New pages rely on the model to write sources: — the create path never stamps provenance". The diagnosis identified the create path as a separate write-side concern from the merge path's existing merge-page.ts:93 provenance helper, which motivated the Plan A splice approach over a full frontmatter re-parse.
  • @Gojob2987 — reported #375 "Bug: README internal links non-functional in Obsidian community plugin browser". The reported behaviour (clicking internal links in the in-app reader did nothing) traced to the Obsidian Community Plugin browser stripping relative cross-file links — absolute https://github.com/... URLs work in all rendering contexts.
  • @Indexed-Apogrypha — reported #169 "Better status reporting, watch files as they are generated". The reported gap (the status bar showed only "Running…" without saying which phase) drove the 15-key fine-grained stage system.

Full Changelog

1.25.10...1.25.11

1.25.10

Choose a tag to compare

@github-actions github-actions released this 29 Jul 08:23
16ea3b9

🌟 Karpathy LLM Wiki v1.25.10

Highlights

A focused PATCH on v1.25.9 carrying bug fixes only — no new features, no API surface changes. Every item in this release has a measured reproduction on a real-world corpus:

  • Mentions [[|]] parser + formatter fixed (#363). Measured on a 417-note corpus (9272 writes): 119 frozen pages, 104 of the 144 unparseable lines were empty backlinks. The auto-provenance builder wrote a MentionWithProvenance.source_path: '' straight through formatMentionsSection, emitting [[|]]. BULLET_RE required a non-empty target, so the line failed to parse, computeReingestMentions returned preserveRaw, and the page never accumulated another quote — silently, on every subsequent re-ingest. Two coordinated fixes: the formatter now routes both branches through a single renderCitation(leftPath) helper that emits no link when the path is empty; the parser's BULLET_RE makes the citation segment optional and accepts an empty target, so the next merge fills attribution back from the source being ingested.

  • Ingest-a-folder stopped pulling in sibling files sharing a name prefix (#364). The bare path.startsWith(folder.path) leaked three cases: sibling folder with shared prefix (Notizen also matched Notizen-temp/x.md), file sitting beside the folder (Notizen.md also matched Notizen), and the folder itself. The new folder-scope helper anchors on a trailing slash and treats the vault root as a wildcard ancestor — 11 mutation-tested cases including Notizen.md beside the folder.

  • Frontmatter re-touch no longer strips unknown top-level fields (#356). A previous fix's call to mergeFrontmatter accidentally dropped fields the plugin did not author (e.g. redirect_to:, custom user fields). extractPassthroughLines + replaceOrInsertYamlListField + CANONICAL_FRONTMATTER_KEYS separate plugin keys from user keys and re-emit the user's verbatim.

  • Lint fix-runners batched by pageGenerationConcurrency (#367 P0-1). Five fix-runners now slice their input into batches of pageGenerationConcurrency and resolve each batch through Promise.allSettled so a single failure never poisons the rest. concurrency = 1 (the v1.25.9 default) preserves prior behaviour; users who raise it to 4–8 in Settings see wall-clock drop roughly by (n / concurrency) on a 2000-page vault. P1-1 analysis cache + P1-2 smart-skip helpers ship as dead code in this release; the controller wire lands in v1.26.0 MINOR (existing length > 0 guards already provide equivalent skip semantics today).

2713 tests passing (202 files). +91 net since v1.25.9.

Changes

Fixed

  • Mentions section stopped silently truncating pages with empty citation targets (#363). The two halves are one fix, not two: shipping only the formatter half would have traded one unparseable shape for another. Round-trip interlock tests pin the formatter ↔ parser contract so neither half can ship alone again.
  • Ingest-a-folder stopped pulling in sibling files that share a name prefix (#364). Anchor on a trailing slash; treat the vault root as a wildcard ancestor.
  • Frontmatter re-touch no longer strips unknown top-level fields on re-touch (#356). User fields are separated from plugin keys and re-emitted verbatim.
  • Merge triage can no longer drop a page's own primary source on a skip judgement (#312 part 2). New isSourceOwnPageLemma({ pageName, pageAliases, sourceBasename, sourceContext }) predicate compares slug keys against the page basename + curated aliases. SourceContext is optional everywhere — lint pipeline callers pass nothing and see no change.
  • Slug comparison keys use a Turkish-aware case fold when the vault opts in (#366 phase 1). New slugKeys(name, aliases, { turkishFold }) returns the comparison-key set; [[İsim]] and [[isim]] collapse to the same key in Turkish vaults. File-name outputs stay byte-identical — computeSlug is unchanged; the fold is comparison-only.

Performance

  • Lint fix-runners batched by pageGenerationConcurrency (#367 P0-1). Five fix-runners (runAliasCompletion, runDeadLinkFixes, runEmptyPageFixes, runOrphanFixes, runDuplicateMergeFixes, runRetagViolations) now slice into batches and resolve through Promise.allSettled. P1-1 analysis cache + P1-2 smart-skip helpers ship as dead code; controller wire deferred to v1.26.0.
  • Alias hardening floor lowered from 3 to 2 chars (MIN_ALIAS_LENGTH = 2). Two-char aliases are real-world (ML, HD, CD, AI, UI, OS, DB); rejecting them at the floor would be over-aggressive. The constant lives in src/constants.ts, not in Settings.

Changed

  • Custom tag vocabulary clarified as a hint, not an enforcement gate (#368). Schema docs and the Settings UI hint now spell this out in user language. Root cause is a docs / semantic mismatch, not an enforcement bug.

Installation

  1. From Community Plugins: Obsidian Settings → Community Plugins → Browse → search "Karpathy LLM Wiki"
  2. From Community Plugin Website: community.obsidian.md/plugins/karpathywiki
  3. Manual: Download main.js, manifest.json, styles.css from the release assets

Upgrading from v1.25.9

No migration needed. Replace main.js, manifest.json, and styles.css in your vault's .obsidian/plugins/karpathywiki/ directory.

Existing wikis are not touched on upgrade. Mentions sections on pages with [[|]] citations heal themselves automatically on the next re-ingest: the parser now accepts that shape, and computeReingestMentions fills the empty source_path from the source being merged.

Tests

  • 2713 tests passing (202 files). +91 net since v1.25.9:
    • +11 folder-scope.test.ts (prefix derivation + 7 predicate cases, including Notizen.md beside the folder)
    • +6 mentions-formatter-roundtrip.test.ts (#363 — empty and absent citations describe block, including round-trip interlock tests)
    • +84 lint fix-runner concurrency tests (5 fix-runners × per-batch-path)
    • −10 net deletions: removed 4 ingest-folder-boundary tests and replaced 3 dedec51 data-layer-fallback tests that were co-dependent on the now-removed behaviour

Contributors

Code contributors (commits in the 1.25.9 → 1.25.10 range):

  • @green-dalii — 10-item PATCH scope lock and the majority of the implementation: #356 frontmatter passthrough (commit 6736b06), #363 admission criterion + parser tolerance (initial 2-commit split in dedec51 + f3c61ab), #364 folder-boundary helper (initial in 728f235), #366 Turkish fold helper (507e895), #367 lint-perf P0-1 fix-runners parallelisation (ece6007, 17982b7, dbe9e13, 76f2475) + P1-1/P1-2 helpers, #368 schema docs + settings UI hint (83dec0e), DocTpoint §4 merge/contradictory route split (e3861b5), alias hardening (f9a680e, b3e0b79), simplify audit + 4-agent pass, release-prep.
  • @eucher7e22848 "fix(llm): stop silent truncation in chat and ingest" (PR #352) renamed TOKENS_QUERY_LLM_SELECTTOKENS_QUERY_ANSWER = 8000 so reasoning models stop truncating answers mid-sentence, and wired onFinish through all three Query call sites to surface finishReason === 'length' as a console warning. 43479d8 "fix(ingest): keep source-page tags inside the closed vocabulary" (PR #349) keeps the LLM-generated source-page tags within the active tag vocabulary. Both landed in the 2026-07-26 PATCH batch and form the base this PATCH refines.
  • @DocTpointCo-authored-by: DocTpoint trailers on 98afe42 and 292d42e adopted his PR #370 (folder-scope mutation-tested helper for #364) and PR #371 (renderCitation single-render-gate design for #363). The render-layer renderCitation(leftPath) replaces the data-layer m.source_path || sourcePath fallback that silently rewrote attribution of empty-sourcePath mentions — strictly more correct, and the design now includes round-trip interlock tests. Both source PRs closed in favour of the merged result.

Issue reporters (issues closed by this release):

  • @Chris-Hestro — reported #363 "Mentions in Source renders — [[|]] when mentions_with_provenance[].source_path is blank" with the failing render output that drove the formatter + parser tolerance fix.
  • @DocTpoint — reported #364 "Folder ingest has no folder boundary — sibling folders sharing a name prefix are ingested too", measured 50x folder mis-ingest on a real-world corpus, and contributed the folder-scope helper that the fix is built on.
  • @Guru35 — reported three issues from an 11k-page Turkish vault corpus: #366 "Slug derivation mismatch between link-writing and file-naming produces broken [[wikilinks]] at scale — degrades PPR retrieval" (16.5% broken wikilinks), #367 "Lint hangs indefinitely on large vaults (11.6k pages) — no candidates, zero API calls in 60 min", and #368 "Custom tag vocabulary not hard-enforced — invalid type tags written during ingest" (9% vocabulary drift). The Guru35 morning batch of these three issues shaped the v1.25.10 PATCH scope alongside the DocTpoint batch.

Full Changelog

1.25.9...1.25.10