Skip to content

fix(config): read the daily-notes and Tasks-format settings on every call - #630

Merged
aliasunder merged 12 commits into
mainfrom
fix/config-readers-read-per-call
Oct 3, 2026
Merged

aliasunder merged 12 commits into
mainfrom
fix/config-readers-read-per-call

Conversation

@aliasunder

@aliasunder aliasunder commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Problem

daily-notes.ts and task-format-config.ts kept their first successful read of the .obsidian settings file for the life of the process. A user who renamed their daily notes folder in Obsidian, or switched the Tasks plugin between emoji and Dataview format, kept getting the old setting until the server restarted:

Setting changed in Obsidian What the server kept doing
Daily notes folder vault_delete_note and vault_move_note protected the old folder and left the new one unprotected; vault_get_daily_note and the daily-review prompt looked in the old folder
Daily notes date format vault_get_daily_note built filenames in the old format
Tasks plugin format and date toggles vault_create_task and vault_update_task wrote the old format

ARCHITECTURE.md already promised the folder was "read at operation time"; the code contradicted it.

Changes

  • Both readers read their file on every call and take the caller's logger, the way the trash reader does. A missing file, or .obsidian being a file rather than a folder, takes the defaults silently. A file that exists but cannot be read or parsed logs a warn on the request's logger.
  • The protected-path guard fails closed. resolveEffectiveProtectedPaths resolves PROTECTED_PATHS, then DAILY_NOTES_FOLDER (the file is no longer read when the folder is set by env, even with the format unset), then a strict read of daily-notes.json that throws cannot read daily notes config from .obsidian/daily-notes.json when the file is broken. Every other consumer falls back to the defaults with a warn, since a wrong folder there costs a missed note, not a deleted one. vault_delete_note and vault_move_note list the new error.
  • Task writes classify with the index's boot-time status registry. createSearchIndex defaults the registry to the built-in statuses and exposes it as statusRegistry; createTask and updateTask take it as a required param and overwrite the file's registry with it. Listings and writes therefore always agree; the format and date toggles go live; a status-type change still needs a restart, and the "no checkbox symbol" and NON_TASK error bullets on both task tools now say so. DEFAULT_STATUS_REGISTRY moved to obsidian-markdown/tasks.ts, the leaf both search/ and vault-operations/ may import.
  • The daily-review prompt reads the settings once per run and hands the resolved folder and format to getDailyNote, so the note path and the link classification cannot see two versions of the file.
  • trash-config.ts treats a missing .obsidian/ folder (ENOTDIR) as a missing file, matching the other two readers. A directory at the config path still throws, as its tests pin.
  • ARCHITECTURE.md describes the per-call read, the fail-closed guard, and the boot-time registry.

Behaviour change

A daily-notes.json that exists but cannot be parsed used to read as the defaults, which protected Daily Notes while the user's folder was whatever the broken file said. Delete and move are now refused until the file is repaired, or DAILY_NOTES_FOLDER or PROTECTED_PATHS is set. Only Obsidian, the Sync client, or the vault owner's own tools can write that file.

Tool-definition size

The two refusal bullets and the restart remedies on four task error bullets (the two "no checkbox symbol" bullets and the two NON_TASK bullets) add 515 characters to the default tool list. CHARS_PER_TOOL_ALLOWANCE rises from 4040 to 4055 to hold them. The text documents refusals a caller has to be able to recover from, and no existing description was cut to make room.

Combo Tools Total chars Cap at 4055 per tool
default 33 133,810 133,815
embedding-off 33 132,536 133,815

Tests

  • daily-notes.test.ts and task-format-config.test.ts: static imports, no module resets; each reader follows a change between two reads, picks up a file that appears later, warns on the caller's logger (not the root) for malformed JSON and for a directory at the path, and takes the defaults silently when .obsidian is a file.
  • vault-crud-tools.test.ts: DAILY_NOTES_FOLDER skips the file, DAILY_NOTES_FORMAT alone does not, and an unreadable file rejects.
  • task-mutations.test.ts: the 225 calls pass a registry; the tests that set a custom status supply the map instead of writing data.json; two new tests write a data.json whose registry disagrees with the supplied one and assert the supplied one wins on the written checkbox and on card placement.
  • search-index.test.ts: the index exposes the registry it was built with, and the built-in map when none was given.
  • daily-review-prompt.test.ts: one read per run, with the forward-reference classification proving the resolved folder reached the link pass.
  • Integration: a daily folder change from Journal to Diary mid-run flips which delete is refused and where vault_get_daily_note looks; an emptied daily-notes.json refuses delete and move with the exact message and leaves both notes untouched until repaired (the warning carries the delete's request id); a data.json rewritten with taskFormat: "dataview" and > retyped as TODO makes the next create write [due:: …] while the [>] line is still rejected as NON_TASK.
  • Mutation checks, each committed → mutated → run → reverted: restoring either cache, making the guard tolerant, and switching writes to the file's registry each failed the tests above and only those.

After merging main: npm test passes with 109 files and 4,355 tests. npm run lint and npm run build are clean. Tool-surface snapshots regenerated.

Live test

Same probe run twice against a throwaway vault, once on the released :remote image (v0.54.7) and once on the :test image built from the merge commit (3d699dfc), rewriting .obsidian/daily-notes.json and the Tasks plugin data.json between calls on one running server.

Step Released image This change
vault_get_daily_note after the folder setting changes Daily Notes → Journal still resolves Daily Notes/2026-10-02.md resolves Journal/2026-10-02.md
vault_delete_note on a note in the new folder moved to trash refused: cannot delete protected path "Journal/2026-10-02.md"
vault_delete_note on a note in the old folder refused as protected moved to trash
vault_delete_note with daily-notes.json unreadable served from the stale cache refused: cannot read daily notes config from .obsidian/daily-notes.json
vault_create_task after taskFormat changes to dataview ➕ 2026-10-03 [created:: 2026-10-03]
vault_update_task to done after setDoneDate: false ✅ 2026-10-03 stamped no completion date

Test deploy of the merge commit (run 37091538492): the instance's container was re-created from the :test image (image created 2026-10-03T02:58:43Z, same as the pushed tag) and rebuilt its index on boot.

🤖 Generated with Claude Code

…call

Both readers kept their first successful read of the .obsidian file for
the life of the process, so a renamed daily notes folder lost its delete
and move protection and a Tasks format switch kept writing the old
signifiers until a restart.

- daily-notes.ts and task-format-config.ts read their file on every call
  and take the caller's logger; a missing file (or .obsidian being a file)
  takes the defaults, an unreadable one warns.
- The protected-path guard fails closed: a daily-notes.json that exists
  but cannot be read refuses the delete or move until it is repaired or
  DAILY_NOTES_FOLDER / PROTECTED_PATHS is set. DAILY_NOTES_FOLDER alone
  now skips the file.
- Task writes classify with the index's boot-time status registry
  (createSearchIndex exposes it; createTask/updateTask require it), so
  listings and writes always agree; format and date toggles go live.
- The daily-review prompt reads the settings once per run.
- trash-config.ts treats ENOTDIR as a missing file too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/vault-mcp/vault-operations/daily-notes.ts Outdated
@umm-actually

umm-actually Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

umm-actually re-reviewed at f7000e2

No new findings (3 tracked finding(s) across all runs).

Context notes
  • Priority docs already in context: ARCHITECTURE.md
  • Priority docs not included: deploy/railway/README.md, deploy/render/README.md, deploy/remote/README.md (missing, unreadable, or over budget)
  • 9 changed file(s) excluded from review: src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/default.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/disabled-tools.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/embedding-off.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/file-tools-off+embedding-off.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/file-tools-off.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/memory-off+embedding-off.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/memory-off+file-tools-off+embedding-off.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/memory-off+file-tools-off.json (diff_exclude_paths input), src/vault-mcp/mcp-core/__tests__/__snapshots__/tool-surface/memory-off.json (diff_exclude_paths input)

umm-actually · z-ai/glm-5.3-flash

aliasunder and others added 2 commits September 30, 2026 18:27
…n server

Ship-Check: code-quality · claude-opus-4-6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two readability pauses from the stranger read: readdirOrNull's re-stat
result gets a named boolean, and moveTaskBlock's docstring numbers the
extract-reinsert steps.

Ship-Check: triage · claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/vault-mcp/mcp-core/tools/vault-crud-tools.ts
aliasunder and others added 6 commits September 30, 2026 18:44
A daily-notes.json or Tasks data.json holding the JSON literal null
parsed but threw on the first property access, so the guard refused
every delete and move as "unreadable" while [] or a bare string took the
fallbacks. Both readers now shape-check the parse result through a
shared isRecord guard, matching the trash reader.

Ship-Check: pr-monitor · claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fig readers

search-helpers.ts carried its own copy of the plain-object predicate; it
now imports src/utils/is-record.ts, which excludes arrays as that copy
did. canvas.ts keeps its own because obsidian-markdown/ is a leaf layer
that may not import utils/ at runtime.

Ship-Check: pr-monitor · claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The guard grew a third positional argument with the caller's logger; it
now takes `{ config, vaultPath }` plus the logger, the data layer's
(params, logger) shape.

Ship-Check: pr-monitor · claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ield fallback clause

Ship-Check: bug-check · claude-opus-4-6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keeps main's rewritten vault_delete_note error bullets and adds this branch's daily-notes bullet. Shortens the two daily-notes refusal bullets and the two restart remedies, raises CHARS_PER_TOOL_ALLOWANCE from 4040 to 4055 for the two refusals this branch documents, and regenerates the tool-surface snapshots.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The delete guard and daily-notes paragraphs each ran several rules together; each is now a lead line plus labelled bullets, like the vault_move_note block beside them. The Protected paths bullet under Data integrity names .obsidian/daily-notes.json as a source of the default folder, matching the guard.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/vault-mcp/mcp-core/tools/task-tools.ts Outdated
aliasunder and others added 3 commits September 30, 2026 22:15
The two NON_TASK error bullets gave no remedy. The status registry is read once at boot, so a checkbox retyped in the Tasks plugin stays rejected until the server restarts; both bullets now say so. Tool-surface snapshots regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The delete and move guard block now covers only the guard: the daily notes lookup order points to the Daily notes section, the vault_update_properties sentence sits with the edit tools, and the vault_delete_memory pointer is its own line. The Tasks plugin settings move from the Daily notes block to the Tasks section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@aliasunder
aliasunder merged commit 3d699df into main Oct 3, 2026
18 checks passed
@aliasunder
aliasunder deleted the fix/config-readers-read-per-call branch October 3, 2026 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant