Skip to content

Validate tab_clip vault overrides against the Obsidian registry - #16

Merged
mlsimon734 merged 2 commits into
mainfrom
feat/global-config
Aug 3, 2026
Merged

Validate tab_clip vault overrides against the Obsidian registry#16
mlsimon734 merged 2 commits into
mainfrom
feat/global-config

Conversation

@mlsimon734

Copy link
Copy Markdown
Owner

Summary

tab_clip accepts a vault override, and until now nothing could tell a caller that the name was wrong. The handoff is an obsidian:// URL given to the OS: an unrecognised vault fails inside Obsidian, where neither end of the bridge can observe it, and the call still reports success.

Gullet now checks an explicit vault name against Obsidian's local obsidian.json registry before forwarding it. A readable, understood registry that does not contain the name produces a bad-request listing the vaults that registry knows.

Deliberately a soft check

The registry is undocumented and incomplete, and its location varies outside a standard install. So absent, unreadable, malformed, unfamiliar, or empty (vaults: {}) all preserve the old pass-through behavior — only a registry that positively contradicts the name refuses the call. An empty registry proves nothing about a name the user read off their own vault switcher, so it belongs with the other soft cases rather than producing a rejection listing "(none)".

The error describes known vaults rather than claiming to enumerate every vault on disk. The extension still cannot validate the destination.

Parsed contents are cached by modification time and size (one stat() per call), so a vault added while Gullet is running appears on the next changed-file check.

Notes

  • knownObsidianVaults is optional on ToolContext, matching rivalHubs? — it can refuse a call it can prove wrong, but an absent lookup never changes one, so construction sites need no stub.
  • parseVaultOverride is exported so the vault check reads only the vault field. It previously ran the whole parseTabClipParams, meaning Gullet re-validated tabId and close — which the extension owns — and reported those errors from a function named for the vault.
  • The existing vaultWarningFor path-vs-name check is unchanged.

Verification

bun run typecheck, bun test (398 pass), bun run format:check, bun run lint:js.

No manifest or permission changes. No UI changes.

Cleanup on the check added in b3bb926, no behavior change beyond one
softening.

- The lookup answered with `{name, path}` pairs and nothing ever read
  `path`. It now answers with names, which drops the exported interface,
  the dedup Map, and the `.name` unwrapping at every call site.
- `parseObsidianVaultRegistry` used a private fourth copy of `asRecord`;
  it imports the shared guard the rest of gullet already uses.
- `validateVaultOverride` ran the whole `parseTabClipParams` to read one
  field, so gullet was also re-validating `tabId` and `close` — which
  the extension owns — and reporting those errors from a function named
  for the vault. `parseVaultOverride` is exported instead.
- The lookup did `exists()` and then read `lastModified`/`size`, two
  stats per call; one `stat()` now does both, and a missing file throws
  into the same soft catch.
- `knownObsidianVaults` is optional, matching `rivalHubs?` — it can
  refuse a call it can prove wrong, but an absent lookup never changes
  one, and every construction site no longer needs a stub.
- An empty `vaults: {}` registry is "cannot check" rather than a hard
  rejection listing "(none)". That state proves nothing about a name the
  user read off their own vault switcher, so it belongs with the other
  soft cases.

Verified: bun run typecheck, test (398 pass), format, lint:js.
@mlsimon734
mlsimon734 merged commit 1814269 into main Aug 3, 2026
1 check passed
@mlsimon734
mlsimon734 deleted the feat/global-config branch August 3, 2026 17:42
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