From 4eca4cb545313dfc8d92ddf0ea5146856978674e Mon Sep 17 00:00:00 2001 From: Test Date: Sat, 28 Mar 2026 10:55:14 +0100 Subject: [PATCH] =?UTF-8?q?docs:=20backfill=20ADRs=200011=E2=80=930015=20(?= =?UTF-8?q?historical=20decisions)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0011: Keyword search only (remove QMD semantic indexing) 0012: Underscore convention for system properties 0013: BlockNote as the rich text editor 0014: Wikilink-based relationship model 0015: Note type system (types as files) Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/adr/0011-keyword-search-only.md | 29 +++++++++++++++++ docs/adr/0012-underscore-system-properties.md | 30 +++++++++++++++++ docs/adr/0013-blocknote-editor.md | 32 +++++++++++++++++++ docs/adr/0014-wikilink-relationships.md | 31 ++++++++++++++++++ docs/adr/0015-note-type-system.md | 31 ++++++++++++++++++ docs/adr/README.md | 5 +++ 6 files changed, 158 insertions(+) create mode 100644 docs/adr/0011-keyword-search-only.md create mode 100644 docs/adr/0012-underscore-system-properties.md create mode 100644 docs/adr/0013-blocknote-editor.md create mode 100644 docs/adr/0014-wikilink-relationships.md create mode 100644 docs/adr/0015-note-type-system.md diff --git a/docs/adr/0011-keyword-search-only.md b/docs/adr/0011-keyword-search-only.md new file mode 100644 index 00000000..8affd2e7 --- /dev/null +++ b/docs/adr/0011-keyword-search-only.md @@ -0,0 +1,29 @@ +--- +type: ADR +id: "0011" +title: "Keyword search only (remove QMD semantic indexing)" +status: active +date: 2026-03-24 +--- + +## Context + +Laputa originally included QMD (Query Markdown) semantic indexing — a system that parsed markdown structure to enable semantic queries beyond simple keyword matching. The indexing step added startup latency, code complexity, and maintenance burden without delivering significantly better search results for the typical vault usage pattern (finding notes by title or content fragments). + +## Decision + +**Remove QMD semantic indexing entirely. Search is keyword-based only, using `walkdir` to scan all `.md` files with no indexing step required.** + +## Options considered + +- **Option A** (chosen): Keyword search via walkdir — pros: zero indexing overhead, no startup delay, simple implementation, sufficient for title + content matching / cons: no semantic ranking, no fuzzy matching beyond case-insensitive substring +- **Option B**: Keep QMD semantic indexing — pros: richer query capabilities / cons: startup delay, complex codebase, rarely used features, maintenance burden +- **Option C**: External search engine (Tantivy, MeiliSearch) — pros: fast full-text search with ranking / cons: external dependency, index management, overkill for current scale + +## Consequences + +- Vault startup is faster — no indexing step +- Search is simple and predictable: title matches ranked higher than content matches +- No semantic query features (field-specific search, boolean operators) +- The `search_vault` Tauri command runs a blocking scan and returns results sorted by relevance score +- Re-evaluate if vaults grow large enough that scan-based search becomes too slow, or if users need structured queries diff --git a/docs/adr/0012-underscore-system-properties.md b/docs/adr/0012-underscore-system-properties.md new file mode 100644 index 00000000..219e1a19 --- /dev/null +++ b/docs/adr/0012-underscore-system-properties.md @@ -0,0 +1,30 @@ +--- +type: ADR +id: "0012" +title: "Underscore convention for system properties" +status: active +date: 2026-03-24 +--- + +## Context + +As Laputa added more internal configuration stored in note frontmatter (type icons, colors, pinned properties, sidebar labels), these system fields cluttered the user-facing Properties panel alongside user-defined fields. There was no convention to distinguish system-internal fields from user-visible ones. + +## Decision + +**Any frontmatter field whose name starts with `_` is a system property: hidden from the Properties panel, not exposed in search or filters, but editable in the raw editor. All future system-level frontmatter fields must use the `_field_name` convention.** + +## Options considered + +- **Option A** (chosen): Underscore prefix convention (`_icon`, `_color`, `_order`) — pros: simple, familiar (Unix hidden files), no schema needed, parsers just filter on prefix / cons: relies on naming discipline, no enforcement beyond convention +- **Option B**: Separate YAML block or nested key (e.g., `system:` namespace) — pros: clean separation / cons: breaks flat key-value assumption, complicates frontmatter parsing +- **Option C**: Store system config in separate sidecar files — pros: clean frontmatter / cons: doubles file count for type notes, harder to keep in sync + +## Consequences + +- Frontmatter parsers (Rust and TypeScript) filter `_*` fields before passing properties to the UI +- System properties are still plain YAML — readable and editable by humans and external tools +- Power users can modify system properties via the raw editor +- Examples: `_icon`, `_color`, `_order`, `_sidebar_label`, `_pinned_properties` +- All new features that need per-note or per-type configuration must use this convention +- Re-evaluate if the number of system properties grows large enough to warrant a nested namespace diff --git a/docs/adr/0013-blocknote-editor.md b/docs/adr/0013-blocknote-editor.md new file mode 100644 index 00000000..6659ada6 --- /dev/null +++ b/docs/adr/0013-blocknote-editor.md @@ -0,0 +1,32 @@ +--- +type: ADR +id: "0013" +title: "BlockNote as the rich text editor" +status: active +date: 2026-02-14 +--- + +## Context + +Laputa needed a rich text editor that could render markdown with inline formatting, support custom inline content types (wikilinks), and provide a block-based editing experience similar to Notion. The editor must work inside a Tauri WebView and support a roundtrip pipeline: markdown on disk <-> block tree in editor. + +## Decision + +**Use BlockNote as the rich text editor with a custom wikilink inline content type, paired with CodeMirror 6 as an alternative raw markdown editor.** + +## Options considered + +- **Option A** (chosen): BlockNote + CodeMirror 6 — pros: block-based editing, React-native, extensible schema for wikilinks, good markdown import/export, CodeMirror for power users / cons: BlockNote is newer/less mature, markdown roundtrip has lossy edges +- **Option B**: ProseMirror directly — pros: battle-tested, maximum control / cons: much more boilerplate, no block-based UI out of the box +- **Option C**: TipTap — pros: ProseMirror-based with better DX / cons: less block-oriented, custom inline types require more work +- **Option D**: Milkdown — pros: markdown-first / cons: smaller community, less extensible schema + +## Consequences + +- Custom `WikiLink` inline content type defined via `createReactInlineContentSpec` in `editorSchema.tsx` +- Markdown-to-BlockNote pipeline: `splitFrontmatter → preProcessWikilinks → tryParseMarkdownToBlocks → injectWikilinks` +- Save pipeline: `blocksToMarkdownLossy → postProcessWikilinks → prepend frontmatter → disk write` +- Placeholder tokens (`\u2039`/`\u203A`) used to preserve wikilinks through markdown parsing +- Raw editor mode (CodeMirror 6) available via toggle for direct markdown + frontmatter editing +- `blocksToMarkdownLossy` — some formatting may not survive roundtrip perfectly (known limitation) +- Re-evaluate if BlockNote's markdown fidelity issues become a significant data loss risk diff --git a/docs/adr/0014-wikilink-relationships.md b/docs/adr/0014-wikilink-relationships.md new file mode 100644 index 00000000..b5f6c528 --- /dev/null +++ b/docs/adr/0014-wikilink-relationships.md @@ -0,0 +1,31 @@ +--- +type: ADR +id: "0014" +title: "Wikilink-based relationship model" +status: active +date: 2026-02-14 +--- + +## Context + +Laputa needs a way to express relationships between notes (belongs-to, related-to, custom relationships). The relationship model must be plain-text friendly, work without a database, and be extensible without code changes. + +## Decision + +**Relationships are expressed as `[[wikilinks]]` in frontmatter fields. Any frontmatter field containing `[[target]]` values is automatically detected as a relationship field — no hardcoded field name lists.** + +## Options considered + +- **Option A** (chosen): Wikilink syntax in frontmatter, dynamic detection — pros: extensible (any field name works), human-readable, no schema needed, Obsidian-compatible syntax / cons: relies on string parsing, `[[` in non-relationship fields could cause false positives +- **Option B**: Explicit relationship schema (config file listing relationship fields) — pros: no false positives / cons: requires configuration, new relationships need config changes +- **Option C**: Database-backed graph — pros: fast traversal, proper graph queries / cons: violates filesystem-as-source-of-truth principle (ADR-0002) + +## Consequences + +- Standard relationships: `belongs_to`, `related_to` (with wikilink values) shown in Properties panel +- Custom relationships: any frontmatter key with `[[wikilink]]` values captured in `relationships` HashMap +- Wikilink resolution uses multi-pass matching: filename stem -> alias -> exact title -> humanized title +- Outgoing links: `[[wikilinks]]` in note body extracted separately for backlink detection +- Backlinks panel scans `allContent` for notes referencing the current note +- No hardcoded field names — relationship detection is purely syntactic +- Re-evaluate if false-positive relationship detection becomes a user-facing issue diff --git a/docs/adr/0015-note-type-system.md b/docs/adr/0015-note-type-system.md new file mode 100644 index 00000000..3847035e --- /dev/null +++ b/docs/adr/0015-note-type-system.md @@ -0,0 +1,31 @@ +--- +type: ADR +id: "0015" +title: "Note type system (types as files)" +status: active +date: 2026-02-14 +--- + +## Context + +Laputa organizes notes by entity type (Project, Person, Procedure, Topic, etc.). The type system needs to support custom types, per-type configuration (icon, color, sort order, templates), and be editable without code changes. Following the filesystem-as-source-of-truth principle (ADR-0002), type definitions should be stored as plain files. + +## Decision + +**Each entity type is defined by a type document in `type/` (e.g., `type/project.md`) with `type: Type` frontmatter. Notes declare their type via the `type:` frontmatter field. Type is never inferred from folder location (ADR-0006).** + +## Options considered + +- **Option A** (chosen): Types as markdown files in `type/` folder — pros: editable by users, follows filesystem principle, type metadata is frontmatter, type documents are navigable notes / cons: type discovery requires scanning `type/` folder, type document format must be documented +- **Option B**: Type definitions in a config JSON/YAML file — pros: single source, fast to parse / cons: not a note, not navigable, breaks the "everything is a markdown file" principle +- **Option C**: Hardcoded type list in source code — pros: simplest / cons: no customization, every new type requires a code change + +## Consequences + +- Type documents define: `_icon`, `_color`, `_order`, `_sidebar_label`, template, sort, view, visibility +- Sidebar section groups auto-generated from type documents — no code change needed for new types +- Changing a note's type requires only updating `type:` in frontmatter (no file moves per ADR-0006) +- The Rust backend adds a `"Type"` relationship entry linking each note to its type document +- Type documents are themselves navigable — viewing one shows an "Instances" section listing all notes of that type +- Legacy `Is A:` field accepted as alias for `type:` for backwards compatibility +- Re-evaluate if the type system needs inheritance or multi-type support diff --git a/docs/adr/README.md b/docs/adr/README.md index 60c455f9..01d5bb4a 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -66,3 +66,8 @@ proposed → active → superseded | [0008](0008-canary-release-channel.md) | Canary release channel for early testing | active | | [0009](0009-local-feature-flags.md) | Local feature flags (no remote dependency) | active | | [0010](0010-codescene-code-health-gates.md) | CodeScene code health gates in CI and git hooks | active | +| [0011](0011-keyword-search-only.md) | Keyword search only (remove QMD semantic indexing) | active | +| [0012](0012-underscore-system-properties.md) | Underscore convention for system properties | active | +| [0013](0013-blocknote-editor.md) | BlockNote as the rich text editor | active | +| [0014](0014-wikilink-relationships.md) | Wikilink-based relationship model | active | +| [0015](0015-note-type-system.md) | Note type system (types as files) | active |