Commit Graph
10 Commits
Author SHA1 Message Date
Hermes a5814a37af sync daemon: prune empty /data mirror when a non-ID vault folder is deleted (delete+create renames) 2026-10-07 11:00:16 -04:00
Hermes 1938b889e2 daemon: relocate any non-.md file that lands in /vault to matching /data path
/vault is notes-only by design; binaries saved there by hand, by an
agent, or by a sync client now get moved to /data automatically
instead of sitting in the wrong tree. Wired into live inotify (file
create + file move) and the periodic reconcile sweep so pre-existing
drift self-heals too. Leaves the vault copy in place with a warning
ntfy if a same-named file already exists at the /data destination.
2026-09-25 20:14:32 -04:00
Hermes cf6196d525 add rename-recovery grace window: same-ID delete+create pairing
Root cause of the F.13.13 'Budget & cash-flow tracking -> tracker'
false-archive incident (2026-09-24): watchdog's inotify cookie-pairing
failed on a PLAIN, unloaded, single-folder Obsidian rename -- no burst,
no stress test, just an ordinary rename -- producing a bare on_deleted
and a bare on_created ~12s apart instead of one on_moved. This is the
same underlying gap as the earlier Untitled-placeholder bug, but proves
it is not just a high-load phenomenon: any rename can split this way.

Fix: handle_vault_deleted no longer archives an ID-folder deletion
immediately. It holds the delete in _pending_id_deletes, keyed by the
JD ID code (title-independent, e.g. "F.13.13"), for
RENAME_GRACE_SECONDS (15s). handle_vault_created checks this dict on
every create; if a create for the SAME ID code lands within the
window, _resolve_as_rename moves /data's old-named folder to the new
name (and renames its folder-note file to match) instead of archiving
-- recovering the rename watchdog failed to pair. If nothing claims the
pending delete within the window, _finalize_pending_delete archives it
exactly as before (real deletions are unaffected).

Tested live both ways: (1) delete+create with same ID within the
window -> recovered as a rename, test file content preserved,
no archive slot created; (2) delete with no matching create -> correctly
archived after the grace window expires, same as pre-fix behavior.
2026-09-24 12:51:34 -04:00
Hermes 77e4271660 add periodic full-tree reconcile sweep as inotify backstop
Root cause of the R.12.22 mkdir going unnoticed was never pinned down --
kernel-level inotifywait on the exact same directory caught the same
mkdir instantly, ruling out mount/fs issues, but watchdog's own internal
bookkeeping missed it with zero log line. Rather than keep chasing a
specific inotify failure mode, make the daemon resilient regardless of
cause: a reconcile() sweep walks the whole /data and /vault tree every
3 minutes (plus once at startup) and calls the same idempotent
handle_data_created/handle_vault_created used for live events. Both are
check-target-state-first by construction, so this is a no-op on an
already-synced tree and only acts on genuine misses.

Does not attempt rename reconstruction -- a missed rename looks
identical to a delete+create after the fact, which is genuinely
ambiguous. Live on_moved handling remains the only rename path;
reconcile's job is strictly to guarantee creates are never lost.
2026-09-24 12:04:11 -04:00
Hermes 8ffba8f7ee daemon: never mirror Obsidian's transient 'Untitled' placeholder; strip stub inbox/needs-review noise
Two fixes:

1. Untitled folder junk + phantom-rename bug (Ryan reported 2026-09-22):
   Obsidian's 'New folder' action does mkdir Untitled immediately, then
   creates the real-named folder afterward -- either as a genuine OS
   rename() or as a second separate mkdir + move-in, depending on
   Obsidian's internal path (confirmed both happen). Previously we
   mirrored 'Untitled' to /data like any other folder, so:
     - the two-separate-creates case left a permanent 'Untitled' orphan
       in /data (its delete event was silently dropped -- handle_vault_deleted
       only tracks ID-shaped names) and fired two separate 'new folder'
       ntfy pings instead of the one rename ping the user expected.
     - the real-rename case (once Untitled stopped being mirrored) then
       had no data_src to rename, so the folder never reached /data at all.

   Fix: UNTITLED_RE skips mirroring 'Untitled'/'Untitled N' outright in
   handle_vault_created. handle_vault_moved falls back to a create-mirror
   at the destination when there's no data_src to rename, covering the
   genuine-rename-from-Untitled case cleanly.

2. Stub /vault notes for /data-first folders (Ryan: 'I don't want needs
   review and a fix-me message, it just needs to create the empty folder
   note and give it the automatic alias'): handle_data_created now writes
   just , matching the
   existing convention across the vault (e.g. R.32.22 Alcohol.md). Dropped
   the needs-review banner and the #fix-me inbox flagging entirely, along
   with the now-dead _find_nearest_inbox_note/INBOX_ID_RE.

Verified both Untitled scenarios live: real OS rename from Untitled now
mirrors as a single create at the destination name; two-separate-creates
(Untitled then real name, Untitled later deleted) leaves no orphan in
/data and produces exactly one ntfy for the real folder.
2026-09-22 10:53:25 -04:00
Hermes 49c46042b5 daemon: decouple watchdog event capture from processing via a queue
Fixes silent rename-event loss on large folder trees. Previously
on_created/on_moved/on_deleted handlers ran synchronously inside
watchdog's own callback thread (git commits, network calls, sleeps),
starving the inotify reader under bursty activity. When the reader
falls behind, watchdog can't pair moved_from+moved_to within its
matching window and drops the rename entirely instead of firing a
degraded delete+create -- so large renames vanished with no log line
and no ntfy at all.

Fix: watchdog handlers now just enqueue (kind, path) tuples onto a
queue.Queue and return immediately; a single dedicated worker thread
drains the queue and does all the slow work. Reader thread never
blocks regardless of downstream latency.

Also adds file-level journal frontmatter automation: new dated entries
under R.16.11 (YYYY-MM-DD.md) get  auto-injected
if missing (matches existing corpus format exactly -- full month name,
no zero-padded day). Idempotent: files that already have a title are
left untouched; files with other frontmatter get title prepended.

Verified: reran the exact 300-subfolder rename repro that silently
failed before the fix -- now completes as a single clean MIRRORED
rename with no orphaned /data state.
2026-09-22 00:36:09 -04:00
Hermes 90fbc240af vault_sync_daemon: mirror full /vault structure forward, not just JD-shaped folders
Forward (/vault->/data) mirroring was gated to only AREA/CATEGORY/ID-shaped
names, so arbitrary sub-structure Ryan creates inside an ID (e.g. dated trip
folders under F.21.21) never propagated to /data. Removed the name-shape
gate; the existing parent-must-exist check is sufficient safety, plus an
explicit '0 System area' exclusion since that tree has no /data root at all.
2026-09-19 13:40:56 -04:00
Hermes d89aeb7d26 vault_sync_daemon: drop unreliable finally-block stop ntfy, add ExecStopPost notify script 2026-09-19 13:34:37 -04:00
Hermes 9f3a99a42e vault-cli: extract to standalone repo, add flake.nix, move one-off scripts to scripts/one-off/ 2026-09-16 13:12:01 -04:00
Hermes 8e8ad466a8 vault-cli: commit one-off migration scripts before extraction to standalone repo 2026-09-16 13:06:57 -04:00