15 KiB
Notes/Files Plugin Architecture Plan
This document captures the architectural decisions for Notes, Files, and Workbench routing. Most items described below are now implemented; this document remains as a reference for the canonical data model and rules.
Current Readiness
All key components are implemented:
- Files Core API with safe vault-scoped text and binary file operations.
- Notes plugin as a semantic view over canonical
Notes/Markdown files. - Files UI plugin with folder navigation and Workbench-based file opening.
- Default Editor plugin providing text/markdown openProviders.
- File Preview plugin for images.
- Workbench open/edit routing with provider selection.
- UI hosts for file actions, note actions, context menus, search providers, activity providers, and status bar items.
- File watcher for external filesystem changes.
- Trash plugin for restore and permanent delete.
Not yet available:
- Real plugin isolation (bundled plugins run in shared JS context; sidecar/sandbox is a future milestone).
- Chunked streaming/large-file import (bounded to 2 MB text / 8 MB bytes).
- Production-grade auto-update and release channel.
Canonical Notes Model
Notes are ordinary human-readable Markdown files inside the vault. They must be visible and editable outside Verstak.
Canonical rules:
- Notes are
.mdfiles, not opaque records. - Canonical folder name is exactly
Notes. - Do not create lowercase
notes. - Do not store user notes in
.verstak/notes/. - Do not store user notes in
.verstak/plugin-data/verstak.notes/. - Do not use UUID-only filenames for notes.
- The note title is the source of truth.
- The filename is a normalized projection from the title.
RenameNotemust update both the title and the.mdfilename.
Canonical scoped paths:
- Notes live under
<workspace>/Notes/. Overview.mdis allowed as an ordinary Markdown filename, but the platform must not create it automatically or treat it as a dedicated UI entity.<workspace>is the top-level physical folder name under the vault root.- Files plugin workspace views are scoped with
workspaceRootPath, which is the selected top-level workspace folder name.
Visibility requirements:
- Notes UI must show notes as semantic notes.
- Files UI must show the same
.mdfiles as ordinary files. - External file managers must show the same
.mdfiles. - Outside Verstak, the files must remain useful as normal Markdown.
There is no canonical metadata workspace tree. Adding note as a workspace node
type is not part of the next milestone. The official Notes plugin can index and
manage Markdown files inside canonical Notes/ folders under each top-level
workspace.
Title To Filename Contract
The title is the source of truth. The filename is derived from the title when a note is created or renamed.
Normalization rules:
- Replace spaces with
_. - Replace typographic dashes with
-. - Allow only letters, digits,
.,_, and-. - Append
.mdif the normalized name does not already end with.md. - Reject empty normalized names.
- Preserve canonical
Notesfolder casing.
Examples:
| Title | Filename |
|---|---|
Overview |
Overview.md |
Meeting Notes |
Meeting_Notes.md |
Plan — Phase 1 |
Plan_-_Phase_1.md |
A/B Test: Result |
AB_Test_Result.md |
Collision Policy
Same-folder collisions must not be solved silently with _2, _3, or timestamp
suffixes.
Creating or renaming a note must return a conflict error if the normalized target
filename already exists in the target Notes/ folder. The UI should show a clear
dialog or notification and ask the user to change the title.
Required conflict metadata:
- requested title;
- normalized filename;
- target vault-relative path;
- existing vault-relative path.
Files Service Model
Files service is the raw vault file layer. It works with vault-relative paths and does not understand note semantics.
Rules:
- All public Files API paths are canonical vault-relative slash paths.
- Backslashes are rejected instead of normalized.
- Absolute POSIX paths, Windows drive paths, and UNC/network paths are rejected.
..traversal is rejected.- Null bytes are rejected.
.verstak/is reserved case-insensitively and hidden/forbidden by default.- Access to
.verstak/is allowed only through internal APIs, not through the normal plugin Files API. - Symlink read/write/move/trash operations are forbidden in Milestone 6a.
Metadata may report a final symlink as
type: "symlink". - Writes must be atomic: write a temp file in the same directory, close it, then rename.
- Delete must follow the trash policy until permanent delete is explicitly designed.
- Trash metadata records
originalPath,deletedAt,originalType,trashId, andbasenamefor future restore work. Restore is deferred. - Binary files are deferred for write/streaming APIs. Milestone 6a lists binary metadata but read/write is UTF-8 text only with a 2 MB read limit.
Minimum Files methods:
ListVaultFiles(relativeDir).GetVaultFileMetadata(relativePath).ReadVaultTextFile(relativePath).WriteVaultTextFile(relativePath, content, options).CreateVaultFolder(relativePath).MoveVaultPath(fromRelativePath, toRelativePath).TrashVaultPath(relativePath).
Milestone 6a status: implemented in internal/core/files and exposed to bundled
plugins as api.files.list, api.files.metadata, api.files.readText,
api.files.writeText, api.files.createFolder, api.files.move, and
api.files.trash. It is still text-only for reads/writes and has no watcher,
binary streaming, external editor integration, or Files UI plugin.
Later Files methods:
WatchVaultFiles(scope)once watcher/event delivery is ready.ReadVaultFileBytes/WriteVaultFileBytesfor binary files.OpenExternal(relativePath)with explicit permission and UX confirmation.RevealInFileManager(relativePath).
Official Notes Plugin Model
The official Notes plugin is a semantic layer over Markdown files managed through
the public Files plugin API. There is no core desktop Notes service or
verstak/core/notes/v1 capability in v2.
Rules:
- A note physically is a
.mdfile. - Notes plugin and Files API must not create two sources of truth.
- Notes plugin reads/writes the same files that Files API lists.
- The note title is the semantic source of truth and is projected to the filename.
If frontmatter or a first-heading convention is introduced later,
RenameNotemust keep that visible title metadata and the filename synchronized. - Other note metadata should be derived from the file path and filesystem metadata, or from Markdown frontmatter if a future milestone introduces it.
- If a note is changed through Files API or an external editor, the future watcher/indexer must observe it.
- Until watcher/indexer exists, external changes require reload/rescan.
Minimum Notes plugin behavior:
- list Markdown files in a scoped canonical
Notes/folder; - create a Markdown note by writing the normalized filename atomically with
overwrite: false; - rename a note by moving the file to the normalized filename;
- open and edit notes through Workbench providers;
- never special-case
Overview.mdin the UI.
Later Notes plugin behavior:
- search notes;
- list backlinks;
- resolve note links;
- export notes.
Notes Vs Files Relationship
Files owns safe raw vault file access. Notes owns note semantics.
The same physical note must be visible through both surfaces:
- Files sees
Project/Notes/Overview.mdas a file. - Notes sees
Project/Notes/Overview.mdas a note with titleOverview.
There must be no duplicate note content stored in plugin settings, plugin data,
or a separate .verstak note database. Indexes and caches may exist later, but
they must be rebuildable from the canonical Markdown files.
Capabilities And Permissions
Existing permissions that remain useful:
vault.readfor existing vault-level read policy.vault.writefor existing vault-level write policy.vault.watchfor future watcher support.ui.registerfor sidebar/views/settings contributions.commands.registerfor bundled command handlers.events.publishandevents.subscribefor frontend/backend event flows.
New permissions required before real Notes/Files plugins ship:
files.readfiles.writefiles.deleteworkbench.open
Future Notes semantic permissions are deferred until a real Notes plugin/API ships:
notes.readnotes.writenotes.delete
Those permissions are still policy checks until sidecar/sandbox work provides a real isolation boundary.
Recommended capabilities:
verstak/core/files/v1verstak/files/v1provided by the official Files plugin.verstak/notes/v1provided by the official Notes plugin.
Frontend Components And Extension Points
Contribution points already registered but not fully hosted:
fileActionsnoteActionscontextMenuEntriessearchProvidersactivityProvidersstatusBarItems
UI work needed:
- Files view host with tree/list modes and selection state.
- Notes view host with note list, open/edit entry points, and preview/details region.
- Context menu host that merges core actions with plugin contributions.
- Command palette host for contributed commands.
- Search provider host with cancellation/debounce and result ownership.
- Status bar host for lightweight plugin state.
- Selection/event model for active file, active note, and active workspace node.
The first implementation should host only the contribution points needed by the official Notes and Files plugins.
Open/Edit Resource Model
Files and Notes must not embed a concrete editor or viewer. They request that the Workbench open or edit a resource. The Workbench/provider registry selects the plugin component.
Required model:
- Files plugin lists files and calls open/edit for a vault file.
- Notes plugin presents Markdown files under canonical
Notes/folders and calls open/edit for the same vault file with notes context. .mdor.markdowninside a canonicalNotes/folder opens in markdown mode with notes context..mdor.markdownoutsideNotes/opens in generic markdown mode.- Plain text opens in
generic-textmode. - The same editor provider may support text, generic markdown, and notes-context markdown.
- User preferences can select another provider for text, markdown, and notes-context markdown.
- Community editor plugins can replace the default editor through the same provider registry.
- Core desktop owns registry, routing, Workbench host slot/tab, and preferences.
- Core desktop does not own concrete Files UI, Notes UI, Markdown editor, or file preview UI.
Minimal contribution extension:
{
"contributes": {
"openProviders": [
{
"id": "verstak.platform-test.markdown-diagnostic",
"title": "Platform Test Markdown Diagnostic",
"priority": 100,
"component": "MarkdownDiagnosticProvider",
"supports": [
{
"kind": "vault-file",
"extensions": [".md", ".markdown"],
"contexts": ["generic-markdown", "notes-markdown"]
},
{
"kind": "vault-file",
"mime": ["text/plain"],
"extensions": [".txt", ".log", ".json", ".yaml", ".yml", ".toml", ".ini", ".conf"],
"contexts": ["generic-text"]
}
]
}
]
}
}
Open request shape:
type OpenResourceRequest = {
kind: "vault-file";
path: string;
mode?: "view" | "edit";
mime?: string;
extension?: string;
context?: {
sourcePluginId?: string;
sourceView?: "files" | "notes" | string;
isInsideNotesFolder?: boolean;
notesScopePath?: string;
notesMode?: boolean;
};
};
Provider selection rules:
- Match resource kind.
- Match extension and/or mime.
- Prefer providers that explicitly support the request context.
- Apply user preference for text, markdown, or notes-context markdown when the preferred provider is enabled and still supports the resource.
- Otherwise choose highest priority.
- Break ties deterministically by plugin id, then provider id.
- If no provider matches, return a Workbench
no-providerstate rather than hardcoding a core editor. - If a preferred provider plugin is disabled or unavailable, fall back to the deterministic default and surface a non-blocking preference warning later.
Initial preferences:
defaultTextEditorProvider;defaultMarkdownEditorProvider;defaultNotesMarkdownEditorProvider.
Per-extension overrides are deferred.
Migration Risks
- Adding
noteas a workspace node type is a workspace schema migration and is explicitly out of scope for the next milestone. - The canonical path rules must be locked before writing real files into user vaults.
- Rename behavior can break external links if link rewriting is not designed.
- Case/folder path ownership must be clear before scoped
Notes/folders are created. - Raw Files API can expose
.verstakinternals unless reserved paths are blocked. - File writes need atomic behavior and conflict handling before sync.
- Large/binary files require streaming or byte APIs; text APIs are not enough.
- External editor changes require reload/rescan until watcher/indexer exists.
- Bundled frontend plugins are trusted/cooperative and not isolated from the shared JS context.
Test Plan
Backend Go tests:
- Vault-relative path normalization and traversal rejection.
- Reserved
.verstakpath behavior. - Files list/read/write/mkdir/move/trash with vault closed/open states.
- Atomic text writes and temp-file cleanup on failure.
- Notes
Notes/folder casing and no lowercasenotes. - Title to filename normalization.
- note create/rename conflict errors without silent suffixes.
- Notes and Files read the same physical
.mdfile. - Permission checks for
files.*,notes.*,vault.read, andvault.write.
Frontend/unit tests:
- SDK and plugin API shape for Files and Notes draft methods.
- Readable errors for missing permissions, closed vault, missing file, missing note, reserved path, and collision.
- Contribution host rendering for note/file actions and context menus.
Playwright e2e tests:
- Create a text file, reload, and verify it is visible in Files.
- Create a note, reload, and verify it is visible in both Notes and Files.
- Rename a note and verify title plus filename change together.
- Attempt same-folder collision and verify user-facing conflict handling.
- External file change requires reload/rescan until watcher exists.
Implementation Order
- Define canonical vault-relative path rules and reserved path policy.
- Implement Files core service with safe list/read/write/mkdir/move/trash.
- Define open/edit resource request, provider contribution shape, and provider selection rules.
- Extend contribution registry/types with
openProviders. - Add Workbench open/edit routing and a provider-hosted tab/slot.
- Add preferences for text, markdown, and notes-context markdown provider ids.
- Use
platform-testdiagnostic provider to verify routing; real default editor plugin is deferred to Milestone 6c. - Build official Files plugin that calls open/edit resource.
- Build official Notes plugin as a contextual view over Markdown files in
canonical
Notes/folders. - Implement future Notes semantic helpers only as a facade over Markdown files, never as a second source of truth.