From 387ca56af65c8e8a1a08eacc55b0dd5fce6ca360 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Tue, 7 Apr 2026 08:02:31 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20add/update=20ADRs=20for=20H1-as-title?= =?UTF-8?q?=20and=20Trash=20removal=20(guard=20=E2=80=94=20from=20commits?= =?UTF-8?q?=20d50f3479,=209c87eca2,=20d0c3a6b8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/adr/0007-title-filename-sync.md | 3 +- .../adr/0042-trash-auto-purge-safety-model.md | 3 +- docs/adr/0044-h1-as-title-primary-source.md | 46 +++++++++++++++++++ docs/adr/0045-permanent-delete-no-trash.md | 37 +++++++++++++++ docs/adr/README.md | 5 +- 5 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 docs/adr/0044-h1-as-title-primary-source.md create mode 100644 docs/adr/0045-permanent-delete-no-trash.md diff --git a/docs/adr/0007-title-filename-sync.md b/docs/adr/0007-title-filename-sync.md index c857d959..3f959fc6 100644 --- a/docs/adr/0007-title-filename-sync.md +++ b/docs/adr/0007-title-filename-sync.md @@ -2,8 +2,9 @@ type: ADR id: "0007" title: "Title equals filename (slug sync)" -status: active +status: superseded date: 2026-03-15 +superseded_by: "0044" --- ## Context diff --git a/docs/adr/0042-trash-auto-purge-safety-model.md b/docs/adr/0042-trash-auto-purge-safety-model.md index 28ae4738..852abf0e 100644 --- a/docs/adr/0042-trash-auto-purge-safety-model.md +++ b/docs/adr/0042-trash-auto-purge-safety-model.md @@ -2,8 +2,9 @@ type: ADR id: "0042" title: "Trash auto-purge safety model" -status: active +status: superseded date: 2026-04-05 +superseded_by: "0045" --- ## Context diff --git a/docs/adr/0044-h1-as-title-primary-source.md b/docs/adr/0044-h1-as-title-primary-source.md new file mode 100644 index 00000000..b03acee9 --- /dev/null +++ b/docs/adr/0044-h1-as-title-primary-source.md @@ -0,0 +1,46 @@ +--- +type: ADR +id: "0044" +title: "H1 as primary title source — filename as stable identifier" +status: active +date: 2026-04-07 +supersedes: "0007" +--- + +## Context + +ADR-0007 established that the `title:` frontmatter field is the source of truth for display titles, with filenames derived from it via slugification and kept in sync bidirectionally. This model had a key assumption: the user explicitly types a title before writing content. + +In practice this created friction: new notes required a title upfront, the TitleField was always visible cluttering the editor, and the "title = filename slug" contract was fragile when users renamed files externally. The team wanted a more natural writing flow where you just start writing — like most text editors — and the title emerges from the document. + +A pair of commits on 2026-04-06 (`377a3f8d`, `7daf6898`) implemented a fundamentally different model. + +## Decision + +**The first `# H1` heading in the note body is the canonical display title. The `title:` frontmatter field is legacy/backward-compat only. New notes are created with filename `untitled-{type}-{timestamp}.md` and no `title:` in frontmatter. On save, if the note has an H1, the file is auto-renamed to a slug derived from it (collision-safe with `-2`, `-3` suffixes).** + +Title resolution priority (Rust `extract_title`): +1. H1 on the first non-empty line of the body +2. Frontmatter `title:` field (legacy, backward-compat) +3. Slug-to-title derivation from filename stem + +The `has_h1: bool` field on `VaultEntry` signals the frontend to hide `TitleField` and the icon picker when an H1 is present, since the H1 serves as the title surface. + +The breadcrumb bar shows the **filename stem** (not display title) so users always know the actual file identifier. + +Auto-rename (`auto_rename_untitled` Tauri command) fires on save for `untitled-*` files that gain an H1, converting them to a human-readable slug. + +## Options considered + +- **Option A — H1 as primary title + auto-rename on save** (chosen): natural writing flow, filename eventually reflects content, TitleField hidden when H1 present. Downside: auto-rename can surprise users; breadcrumb must show filename to stay honest. +- **Option B — Keep `title:` frontmatter as source of truth** (ADR-0007, now superseded): explicit, deterministic. Downside: forces upfront titling, TitleField always visible, friction for quick capture. +- **Option C — UUID-based filenames, title only in H1**: filenames never change, no rename logic needed. Downside: vault unreadable in Finder/terminal, breaks the plain-files principle (ADR-0002). + +## Consequences + +- New notes start as `untitled-note-{timestamp}.md` — the vault may accumulate untitled files if users abandon drafts without writing an H1 +- `TitleField` component is hidden when `has_h1 = true`; icon picker is also hidden (icons only make sense on titled notes) +- Frontmatter `title:` still parsed for backward-compat; existing vaults with explicit titles continue to work +- Auto-rename on save introduces a file rename side-effect during editing — wikilinks pointing to the old filename may break until the rename propagates +- The breadcrumb filename display makes the system more honest but slightly more technical for non-power users +- Re-evaluate if users find auto-rename disorienting or if wikilink breakage during rename becomes a reliability concern diff --git a/docs/adr/0045-permanent-delete-no-trash.md b/docs/adr/0045-permanent-delete-no-trash.md new file mode 100644 index 00000000..843b39ec --- /dev/null +++ b/docs/adr/0045-permanent-delete-no-trash.md @@ -0,0 +1,37 @@ +--- +type: ADR +id: "0045" +title: "Permanent delete with confirm modal — no Trash system" +status: active +date: 2026-04-07 +supersedes: "0042" +--- + +## Context + +ADR-0042 designed a Trash auto-purge safety model (soft-delete with 30-day retention, OS trash via `trash` crate, audit log). This was built on top of a Trash system that treated deletion as a two-phase operation: move to trash → auto-purge after 30 days. + +The Trash system was subsequently identified as unnecessary complexity: it required `trashed`/`trashedAt` frontmatter fields, sidebar filtering, editor banners, inspector components, dedicated smoke tests, and a `trash` crate dependency. The safety guarantee users actually need is a **confirmation prompt before irreversible action**, not a soft-delete buffer — especially given notes live in a git repo (vault git history is already a recovery mechanism per ADR-0034 and ADR-0014). + +Commit `e581ad36` on 2026-04-06 removed the entire Trash system (123 files changed, ~3164 lines deleted). + +## Decision + +**Delete is permanent and immediate, gated only by a confirmation modal (`useDeleteActions`). Notes with `trashed: true` in existing vault frontmatter are treated as normal notes (the flag is ignored by the parser). The `trash` crate dependency is removed.** + +The confirmation modal is the sole safety gate. No soft-delete, no Trash view, no auto-purge scheduler, no `.laputa/purge.log`. + +## Options considered + +- **Option A — Permanent delete + confirm modal** (chosen): simple, honest, no hidden state. Git history provides recovery. Removes ~3000 lines of code and a platform-specific dependency. Downside: no in-app recovery path for users who don't know about git. +- **Option B — OS Trash via `trash` crate** (ADR-0042, now superseded): soft-delete to OS Trash, user can recover from macOS Trash app. Downside: additional dependency, complex auto-purge scheduler, misleading "auto-purge" promise that was never actually implemented. +- **Option C — `.laputa/deleted/` archive folder**: custom recovery mechanism inside vault. Downside: clutters vault, users wouldn't know to look there, still requires manual cleanup. + +## Consequences + +- Users who accidentally delete a note must recover from git history (`git checkout HEAD -- path/to/note.md`) — this is a power-user action +- `trashed`/`trashedAt` frontmatter fields in existing vaults are silently ignored — no migration needed, no data loss +- The `trash` crate is removed from `Cargo.toml` — build times improve marginally +- Smoke tests for trash flows are deleted; delete-related test coverage is now purely the confirm modal behavior +- The Trash view, sidebar filter, note banners, and bulk-trash actions are all gone — simpler UI surface +- Re-evaluate if user feedback shows significant accidental deletion incidents, or if git-based recovery proves too inaccessible for non-technical users diff --git a/docs/adr/README.md b/docs/adr/README.md index 5b79f797..a6939312 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -97,4 +97,7 @@ proposed → active → superseded | [0039](0039-git-history-for-note-dates.md) | Git history as source of truth for note creation/modification dates | active | | [0040](0040-custom-views-yml-filter-engine.md) | Custom Views — .laputa/views/*.yml with YAML filter engine | active | | [0041](0041-filekind-all-files-in-vault-scanner.md) | fileKind field — scan all vault files, not just markdown | active | -| [0042](0042-trash-auto-purge-safety-model.md) | Trash auto-purge safety model | active | +| [0042](0042-trash-auto-purge-safety-model.md) | Trash auto-purge safety model | superseded → [0045](0045-permanent-delete-no-trash.md) | +| [0043](0043-reactive-vault-state-on-save.md) | Reactive vault state: editor changes propagate immediately to all UI | active | +| [0044](0044-h1-as-title-primary-source.md) | H1 as primary title source — filename as stable identifier | active | +| [0045](0045-permanent-delete-no-trash.md) | Permanent delete with confirm modal — no Trash system | active |