Skip to content

feat(plugin-api): read-only read_config for module self-tuning to app settings#41

Open
ForrestThump wants to merge 1 commit into
Epistates:mainfrom
ForrestThump:feat/plugin-api-read-config
Open

feat(plugin-api): read-only read_config for module self-tuning to app settings#41
ForrestThump wants to merge 1 commit into
Epistates:mainfrom
ForrestThump:feat/plugin-api-read-config

Conversation

@ForrestThump

@ForrestThump ForrestThump commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What

Adds one read-only method to the curated plugin surface:

// VaultHost (default returns None), with a VaultApi passthrough
async fn read_config(&self, relative_path: &str) -> PluginResult<Option<Vec<u8>>>;

It lets a compiled-in module read a vault application-config file — e.g.
.obsidian/plugins/obsidian-tasks-plugin/data.json — so it can tune itself to
the user's app/plugin settings instead of duplicating them into module config.

Why

The note APIs deliberately exclude dotfolders: list_notes returns vault-relative
paths (no filesystem anchor) and the scanner skips .obsidian and non-note
extensions. So there is currently no way for a module to read app config.
Without this, every module has to re-ask the user for settings the app already
stores.

Design (kept deliberately minimal)

  • Name-addressed, not enumerable. A module reads a config path it already
    knows (every plugin knows its own settings path). There is intentionally no
    list_configs
    — enumeration would be an authority amplifier (discover then
    read other plugins' stored secrets), and no consumer needs it. Keeping it
    name-addressed makes the capability capability-safe: you can only read a path
    you can already name.
  • .obsidian/-scoped + traversal-guarded in PluginVaultHost, reusing the
    vault manager's existing resolve_path path-traversal detection.
  • Ok(None) on absent, not Err — a missing file is a normal outcome (the
    target plugin may not be installed); the caller decides whether that is
    recoverable or fatal.
  • Non-breaking: the trait method has a default returning None, so a host
    that doesn't support config reads degrades gracefully rather than erroring.

Tests

  • A ContractProvider contract test seeds an .obsidian/ file and asserts it
    reads back by path, separately from the note space.
  • Live-validated against a real Obsidian vault. The consuming tasks module
    (see below) has a fail-closed, env-gated live test that reads a real 2648-byte
    Tasks data.json and self-tunes to it — resolving taskFormat → emoji, the
    #task global filter, and matching 61 real tasks, with source == "obsidian-data" (i.e. it genuinely read the file, not a heuristic fallback).
    Full real-vault scan in ~0.5s. Test:
    live_tasks_against_real_obsidian_vault.

Context / consumer

This is the enabling capability for a tasks module that self-tunes to a user's
Obsidian Tasks settings (metadata format: emoji vs dataview; global filter),
rather than requiring separate configuration. The full module is not part of this
PR — it's here for reference and as end-to-end proof of the capability:
ForrestThump/turbovault @ feat/plugin-tasks.

I saw fix/plugin-api-review-followups is in flight (SEP-986 tool-name
validation, etc.) — happy to rebase/align this with wherever the API review
lands, or fold it into that work if you'd prefer.

🤖 Generated with Claude Code

@ForrestThump
ForrestThump force-pushed the feat/plugin-api-read-config branch 2 times, most recently from 94282e9 to a300442 Compare July 19, 2026 19:49
…ings

The curated plugin surface exposes only the markdown note-space: `list_notes`
returns vault-relative paths, the scanner excludes `.obsidian`, and no vault
root is available anywhere. A module therefore has no way to read the host
app's config — e.g. the Obsidian Tasks plugin's `data.json` — so it cannot
self-tune to the user's settings.

Add `VaultHost::read_config(relative_path) -> Option<Vec<u8>>`, a read-only door
onto the vault's non-note config space, plumbed through the `VaultApi` facade.
It is a default method returning `None`, so it is non-breaking and any host that
declines config reads degrades gracefully. `PluginVaultHost` implements it
scoped to `.obsidian/` with the manager's existing path-traversal guard.

This is a fork-local addition to the plugin boundary, flagged for upstream
review before it is proposed to turbovault-core.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Suap8WwcX94Uu6E2cAL85Z
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