docs: add/update ADRs for H1-as-title and Trash removal (guard — from commits d50f3479, 9c87eca2, d0c3a6b8)

This commit is contained in:
lucaronin
2026-04-07 08:02:31 +02:00
parent e0ea0ddad9
commit 387ca56af6
5 changed files with 91 additions and 3 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 |