Commit Graph

1248 Commits

Author SHA1 Message Date
Test
14a4d371e6 fix: mock move_note_to_type_folder collision handling + Rust collision content test
The mock handler now appends -2, -3, etc. when the target path already
exists, matching the Rust unique_dest_path logic.  Previously it would
silently overwrite the existing note's content in MOCK_CONTENT.

Also adds a Rust test that verifies both the moved note and the
pre-existing note retain their respective content after a collision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:39:18 +01:00
Test
9199ceaa35 fix: prevent data corruption when changing note type — preserve tab content instead of re-reading from disk
After runFrontmatterOp updates the frontmatter and sets the tab content,
move_note_to_type_folder only changes the file location (not its content).
Re-reading via loadNoteContent(result.new_path) was redundant and dangerous:
if the path collided or a stale cache intervened, it could load a different
note's content into the tab — the root cause of the data-corruption bug.

Also fixes stale-closure issue: replaceEntry no longer spreads the captured
`entries` array (which could be stale after the await), avoiding reverting
the isA field that runFrontmatterOp already updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:37:53 +01:00
Test
6af18655de style: rustfmt formatting fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260311.388
2026-03-11 17:09:36 +01:00
Test
90bf73524c fix: bump cache version + handle Yes/No in TS frontmatter parser
Root cause: commit 4743537 added a custom Rust deserializer for
Archived/Trashed Yes/No strings but did not bump CACHE_VERSION.
Existing vaults had stale cached entries with archived: false from the
old parser, and since the cache version (5) matched, stale values were
served without re-parsing from disk.

- Bump CACHE_VERSION 5 → 6 to force full rescan on next vault load
- Add Yes/No handling to TypeScript parseScalar (Inspector display)
- Add integration tests: cached vault path with Archived/Trashed: Yes
- Add stale cache version invalidation test
- Add frontmatter.test.ts for TS Yes/No boolean parsing
- Add Playwright smoke test for archived note filtering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:07:22 +01:00
Test
c1f7f7ec6f test: Playwright smoke test for create note crash fix
Covers all acceptance criteria:
- Click '+' next to type section → note created, no crash
- Cmd+N → note created, no crash
- Custom type → note created, no crash
- Rapid double-click → both notes created, no crash

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260311.387
2026-03-11 16:50:05 +01:00
Test
d1021b9131 fix: prevent crash in handleCreateNoteImmediate — slugify fallback + try/catch
- slugify now returns 'untitled' instead of empty string when input has only
  special characters, preventing invalid paths like '/vault//note.md'
- handleCreateNoteImmediate wrapped in try/catch — worst case shows a toast
  error instead of crashing the app
- Added Rust test for deeply nested directory creation in save_note_content
- Regression tests for slugify edge cases and handleCreateNoteImmediate with
  special-character types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 16:50:05 +01:00
Test
b9139e2d57 test: Playwright smoke test for theme live reload on save
Verifies that editing a theme note frontmatter in raw mode and pressing
Ctrl+S immediately updates CSS vars on the DOM. Also verifies saving a
non-theme note does not affect the active theme.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260311.386
2026-03-11 16:36:56 +01:00
Test
c692c5d067 fix: live-reload CSS vars when saving active theme note in editor
When user edits a theme note directly in the editor and presses Cmd+S,
the app now immediately re-applies CSS variables — no manual reload
needed. Added notifyThemeSaved(path, content) to ThemeManager; wired
into onNotePersisted callback so saving the active theme updates
cachedThemeContent, triggering useThemeApplier.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 16:31:03 +01:00
Test
b86f6d5b88 test: add Playwright smoke test for rapid note switching latency
Validates that rapid keyboard navigation and click-based note switching
don't produce stale content or crash the editor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260309.385
2026-03-09 13:28:46 +01:00
Test
3426cbc882 test: add unit tests for prefetch cache, optimistic rollback, rapid switching
- Prefetch: content served from cache, cache cleared on vault reload,
  deduplication of concurrent requests
- Optimistic rollback: trash/archive/restore/unarchive roll back
  updateEntry on disk write failure with error toast
- Optimistic ordering: updateEntry called before frontmatter writes
- Rapid switching: sequence counter prevents stale active tab when
  notes are opened faster than IPC resolves

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:28:46 +01:00
Test
57ff0f18f8 feat: optimize note open and trash/archive latency
Latency root causes:
1. handleSelectNote/handleReplaceActiveTab awaited IPC before updating
   activeTabPath — zero visual feedback for 50-200ms file I/O
2. Trash/archive called updateEntry AFTER two sequential IPC calls —
   note stayed visible in list for 100-400ms

Optimizations:
- Content prefetch cache: hover on NoteItem and keyboard arrow
  navigation pre-load note content via IPC. When user clicks, content is
  already in memory — eliminates the IPC round-trip entirely.
- Optimistic trash/archive/restore/unarchive: updateEntry runs
  immediately, frontmatter writes happen async. On failure, UI rolls
  back and shows error toast.
- Rapid-switch safety: sequence counter (navSeqRef) ensures only the
  latest navigation sets activeTabPath — prevents stale content flash
  when user clicks multiple notes in quick succession.
- Prefetch cache cleared on vault reload to prevent stale content after
  external edits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:28:46 +01:00
Test
abf6b51369 fix: scope Playwright selectors to dialog overlays to avoid sidebar matches
The `span.truncate` selector was matching sidebar note titles in addition
to search results, causing false positives in the full-text search test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260309.384
2026-03-09 12:59:57 +01:00
Test
02c784b286 style: apply cargo fmt to is_file_trashed tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 12:44:37 +01:00
Test
fc4ba24c4e fix: exclude trashed notes from search results and autocomplete
Trashed notes were appearing in search (Cmd+F), Quick Open (Ctrl+P),
wikilink autocomplete ([[), and person mention autocomplete (@).

Rust: add is_file_trashed() to check frontmatter, filter search_vault results.
Frontend: filter trashed entries from useNoteSearch, baseItems in both
editor views, and mock search_vault handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 12:42:56 +01:00
Test
eaeb6e5d40 fix: simplify cache-invalidation smoke test — container visibility only v0.20260309.383 2026-03-09 12:20:24 +01:00
Test
64fe0f1c25 chore: rotate Tauri signing keypair — fix CI release builds 2026-03-09 12:20:24 +01:00
Test
474353718a fix: handle Archived/Trashed Yes/No string values in frontmatter parser
The Rust YAML parser only accepted boolean values (true/false) for the
archived and trashed fields. When the vault writes Archived: Yes or
Trashed: Yes (YAML string, not boolean), serde silently returned None
and the note appeared as non-archived/non-trashed.

Add a custom deserializer that accepts both booleans and string
representations (Yes/yes/YES/true/1 → true, No/no/false/0 → false).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.20260309.382
2026-03-09 11:59:27 +01:00
Test
a933e6a787 fix: add reload_vault to vault API proxy for browser mock
The vault-api proxy maps Tauri commands to HTTP endpoints when a vault
API server is running. Without this, reload_vault bypassed the proxy
and Playwright route interceptors couldn't catch vault reload calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 11:14:34 +01:00
Test
79689839b2 style: apply cargo fmt to new tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 11:14:34 +01:00
Test
b10e9facad fix: reload vault invalidates cache and rescans from filesystem
Reload Vault (Cmd+K) now calls the new `reload_vault` Tauri command which
deletes the cache file before scanning, guaranteeing a full filesystem
rescan. Previously it called `list_vault` which used incremental git-based
cache updates that could miss recent changes (e.g. trashing a note then
reloading showed stale data).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 11:14:34 +01:00
Test
2a32d2b5ad fix: resolve TypeScript overload errors in zoomCursorFix
Use untyped function references to avoid conflicts with
posAtCoords overloaded type signatures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:54:42 +01:00
Test
11a4e1593b test: add Playwright smoke test for CodeMirror cursor at non-100% zoom
Covers clicking at 150%, 80%, and double-click word selection at 125%.
Verifies cursor lands near the click point (within first 30 chars of line)
and that word selection produces a non-empty range.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:51:57 +01:00
Test
080e5ff62d fix: bypass CodeMirror posAtCoords for accurate cursor at non-100% CSS zoom
CSS zoom on document.documentElement causes a coordinate space mismatch
between mouse event clientX/Y (viewport space) and Range.getClientRects()
(CSS space), breaking CodeMirror's click-to-position mapping. The previous
requestMeasure() fix only recalibrated cached geometry, not this mismatch.

New approach: zoomCursorFix extension patches posAtCoords/posAndSideAtCoords
on the EditorView instance to use document.caretRangeFromPoint() — the
browser's native, zoom-aware API — with coord-adjustment fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:43:46 +01:00
Test
60fd4d9ade fix: embed conversation history in AI agent chat messages
The AI chat panel (AiPanel → useAiAgent) was sending each message as a
standalone request with no prior context. Root cause: useAiAgent.sendMessage
called streamClaudeAgent with raw text, never embedding history.

- Add agentMessagesToChatHistory() to convert AiAgentMessage[] to ChatMessage[]
- Embed trimmed history in each agent request via formatMessageWithHistory
- Use messagesRef/statusRef to avoid stale closures in async callbacks
- Also fix useAIChat (dead code path) with same ref pattern
- Update mock layers to detect history presence for testability
- Add Playwright smoke tests verifying history accumulates and resets

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:21:49 +01:00
Test
3583cb9518 ci: retrigger release — fix Tauri signing key secret 2026-03-09 10:00:59 +01:00
Test
1f497e4b18 test: fix flaky command palette smoke test — use reindex instead of settings
Settings command is disabled in mock environment (onOpenSettings not wired),
causing the 'typing filters the command list' test to always fail.
Reindex Vault is always enabled and already tested in indexing-reindex-status.spec.ts.
2026-03-09 09:28:48 +01:00
Test
13b325217b docs: consolidate VISION.md into docs/ — remove duplicate root file 2026-03-09 09:25:48 +01:00
Test
1714da402e fix: prune stale cache entries on vault open, not just cache write
Remove the early return in update_same_commit that skipped filesystem
validation when git reported no changes. Add prune_stale_entries to
finalize_and_cache so every vault scan path validates entries exist on
disk and deduplicates by case-folded path. Prevents ghost notes after
deleting files outside the app (e.g., via Finder).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 03:06:55 +01:00
Test
7b75cb79c4 fix: add 1 retry for Playwright smoke tests to handle server startup timing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:58:24 +01:00
Test
a66eedbecd style: rustfmt vault/mod.rs test formatting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:52:56 +01:00
Test
dc92fd1f57 fix: disk-first writes in useEntryActions, document three-layer model
Move updateEntry() calls after handleUpdateFrontmatter/handleDeleteProperty
in handleCustomizeType, handleRenameSection, and handleToggleTypeVisibility
so React state only updates after the disk write succeeds. This prevents
state-disk divergence when writes fail.

Expand ARCHITECTURE.md "Three representations, one authority" section with
ownership rules, invariants table, and recovery mechanisms. Add
reload_vault_entry to the commands table (62 total).

Add Playwright smoke test for Reload Vault in Cmd+K palette.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:52:56 +01:00
Test
4012a65a73 feat: wire Reload Vault into Cmd+K palette and menu bar
Adds a "Reload Vault" command that forces a full rescan from filesystem,
bypassing cache. Available via Cmd+K and Vault menu. Wired through
useAppCommands → useCommandRegistry and useMenuEvents.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:52:56 +01:00
Test
b2da813923 feat: add reload_vault_entry Tauri command
Re-reads a single .md file from disk and returns a fresh VaultEntry.
Used after failed optimistic updates to restore the true filesystem state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:52:56 +01:00
Test
e461a91721 refactor: remove hardcoded RELATIONSHIP_KEYS — detect wikilink fields dynamically
Any frontmatter field whose value contains [[wikilinks]] now renders as a
relationship chip automatically. Fields with plain-text values always render
as editable properties, even if they were formerly hardcoded relationship keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:33:57 +01:00
Test
edf24898ae test: add Playwright smoke test for exact-match search ranking
Verifies that searching "Writing" in Quick Open shows the exact title
match first, followed by prefix matches. Also adds Refactoring test
entries to mock data and demo vault.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:53:00 +01:00
Test
1c8542bd01 feat: flip canonical type field — make type: primary, Is A: the alias
The Rust parser now treats `type:` as the canonical frontmatter field
for entity type, with `Is A:` and `is_a:` accepted as legacy aliases.
Previously it was the other way around, creating an asymmetric
read/write cycle since the frontend and all 8800+ vault notes already
use `type:`.

- Flip serde attribute: rename="type", alias="Is A", alias="is_a"
- Update theme defaults, getting-started vault, and type definitions
- Add round-trip tests for both type: and Is A: parsing
- Update mock data and TypeScript tests to use canonical form

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:38:17 +01:00
Test
aef98f17eb feat: move vault cache to ~/.laputa/cache/ and make writes atomic
Cache files are now stored outside the vault directory at
~/.laputa/cache/<vault-hash>.json, preventing them from polluting
the user's git repo. Writes use atomic tmp+rename to avoid corruption.
Legacy .laputa-cache.json files are auto-migrated and cleaned up on
first run.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:04:14 +01:00
Test
5c85bc41f6 test: add Playwright smoke test for exact-match search ranking
Verifies that searching "Writing" in Quick Open shows the exact title
match first, followed by prefix matches. Also adds Refactoring test
entries to mock data and demo vault.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:15:08 +01:00
Test
60f3139b3e fix: ensure exact title match always ranks first in search
Title exact match gets exclusive tier 0 — alias exact match is capped
at tier 1, so a note titled "Refactoring" always appears above notes
with "Refactoring" as an alias or prefix. The 5-tier ranking is:
0=title exact, 1=alias exact, 2=title prefix, 3=alias prefix, 4=fuzzy.

Also adds ranking to editor wikilink autocomplete (enrichSuggestionItems)
and trims whitespace in searchRank comparisons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:03:54 +01:00
Test
e40c09a2ef style: apply rustfmt to rename.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:52:30 +01:00
Test
f90f703096 test: add Playwright smoke test for move-note-to-type-folder
Covers type change → move toast confirmation and type selector visibility
in the properties panel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:52:30 +01:00
Test
eaf31ff61c feat: move note to type folder when Is A changes
When the user changes a note's type via the Properties panel,
the note file is automatically moved to the corresponding type folder.
Shows a toast confirming the move. No move if already in correct folder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:52:30 +01:00
Test
db50f779c9 feat: add move_note_to_type_folder backend command
Adds a new Tauri command that moves a note file to the folder
corresponding to its new type when Is A is changed. Handles:
- folder creation, filename collision (-2 suffix), wikilink updates,
  and no-op when already in the correct folder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:52:30 +01:00
Test
e228bd3a52 docs: add Refactoring strategic context to VISION.md
Laputa as proof-of-work for Refactoring's credibility:
- Building publicly validates the author's authority to write about
  building software with AI (not theory — demonstrated practice)
- Open source makes the work visible: GitHub commits are public evidence
- Success converts to reputation/acquisition for Refactoring via
  sponsorships, paid subs, and brand authority
- Strategy: build the tool you describe, make the work visible
2026-03-08 21:52:30 +01:00
Test
3a0fd0620f docs: add Phase 1b — Tauri dev QA for filesystem/native tasks
Claude Code must also test with pnpm tauri dev (not just Playwright)
when the task touches: filesystem, AI context pipeline, MCP server,
git integration, or native Tauri commands.

Playwright tests mock-tauri handlers — they cannot catch bugs in the
real file read/write layer. Phase 1b closes this gap.

Lesson from ai-chat-empty-body: bug was in MCP server reading from disk,
invisible to Playwright. Phase 1b would have caught it in attempt 1.
2026-03-08 21:52:30 +01:00
Test
e2489b8957 feat: exact-match-first ranking in search and wikilink autocomplete
Add searchRank/bestSearchRank utilities that compute a tier (0=exact,
1=prefix, 2=fuzzy-only). Both useNoteSearch and WikilinkChatInput now
sort by rank tier first, then by fuzzy score, ensuring notes with exact
title or alias matches always surface above partial/fuzzy matches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:00:58 +01:00
Test
11f8731d32 docs: strengthen Phase 1 QA requirements in CLAUDE.md
- Require Claude Code to write a new task-specific Playwright test for
  every task (not just run existing smoke tests)
- Test must fail before fix and pass after — proves coverage
- Clarify that Phase 1 is Claude Code's quality gate, not Brian's
- Brian's Phase 2 is a reinforcement check; if he finds a bug that
  Phase 1 should have caught, that is a Phase 1 failure

Lesson from ai-chat-empty-body: 5 QA cycles happened because Phase 1
never verified that the AI actually received note content end-to-end.
2026-03-08 20:54:44 +01:00
Test
6f7a7d71d8 docs: add 'why this, why now, why us' section to VISION.md
Strongest possible answer to 'why are you the right person to build this':
- Generalist CTO who can build end-to-end
- 300+ articles = battle-tested PKM system at scale
- Refactoring distribution (~200K subscribers) = built-in audience
- Not theorized — the method is proven by the output that exists
2026-03-08 20:48:58 +01:00
Test
aef1924407 Merge branch 'main' of https://github.com/refactoringhq/laputa-app 2026-03-08 20:47:19 +01:00
Test
2173df6f0d docs: clarify that evergreen notes are one output type, not the only one
The capture→organize→express framework is output-agnostic:
- Writers: evergreen notes as building blocks for articles
- Builders: project knowledge graph and shipped work
- Operators: procedures and responsibility systems
What varies is the expression layer; the discipline is universal.
2026-03-08 20:37:21 +01:00