bug(obsidian): obsidian-export leaves stale notes for hard-deleted and renamed observations
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 62/100
Research direction
Start in internal/obsidian/exporter.go: the cleanup loop only visits rows from store.Export() with DeletedAt set, and the export loop overwrites state.Files[obs.ID] without comparing the old path. Reproduce with the numbered steps (hard delete, then a PATCH rename, then export) and check .engram-sync-state.json. Done when hard-deleted and renamed notes are removed or moved, orphaned hubs are pruned, and the export output reports the deletions.
Written by the indexing model from the issue text.
Description
Existing Issue Search
- I searched existing issues and will add evidence to a canonical tracker when applicable.
📝 Observed Consequence
engram obsidian-export leaves Markdown notes in the vault that no longer correspond to any live observation, and keeps reporting Deleted: 0. Two triggers:
- Hard-deleted observations (
engram delete <id> --hard,engram delete project <name> --hard, or any hard deletion that happens between two exports): the note, and its session hub, stay in the vault forever. Their entry also stays in.engram-sync-state.json. - Renamed observations (title updated, so the slug changes): the next export writes a new file and overwrites the state entry with the new path. The old file stays on disk and is no longer tracked by the state, so not even a state-based cleanup can find it.
Soft deletion works correctly: engram delete <id> followed by an export removes the note (Deleted: 1).
On a real vault, after a bulk hard deletion and normal use, this accumulated several hundred stale notes plus orphan session and topic hubs, all shown in Obsidian search and Graph View as if they were live memories.
🔄 Steps to Reproduce
- Isolated store and vault:
T=$(mktemp -d); e() { HOME="$T/home" ENGRAM_DATA_DIR="$T/data" engram "$@"; } e save "keep me" "first body" --project demoande save "hard gone" "second body" --project demoe obsidian-export --vault "$T/vault" --all --graph-config skip→Created: 2e delete 2 --harde obsidian-export --vault "$T/vault" --all --graph-config skip→Deleted: 0;engram/demo/manual/hard-gone-2.mdstill exists and.engram-sync-state.jsonstill maps"2"to it.- Rename observation 1 through the HTTP API:
e serve 17499 &, thencurl -X PATCH 'localhost:17499/observations/1?expected_project=demo' -H 'content-type: application/json' -d '{"title":"keep me renamed"}', then stop the server. e obsidian-export --vault "$T/vault" --all --graph-config skip→Created: 1, Deleted: 0; bothkeep-me-1.mdandkeep-me-renamed-1.mdexist, and the state only knowskeep-me-renamed-1.md.
✅ Expected Behavior
After an export, the vault contains exactly one note per live observation:
- Notes tracked in the state whose observation no longer exists in the store at all (hard-deleted) are removed, like soft-deleted ones.
- When an observation's target path changes, the previously tracked file is removed (or moved) before the state entry is overwritten.
- Session/topic hubs that no longer reference any live observation are removed.
❌ Actual Behavior
# after hard delete of #2
Obsidian export complete
Created: 0
Updated: 0
Deleted: 0
Skipped: 1
Hubs: 1
engram/demo/manual/hard-gone-2.md <- still present
engram/demo/manual/keep-me-1.md
# after renaming #1
Created: 1
Updated: 0
Deleted: 0
engram/demo/manual/keep-me-renamed-1.md
engram/demo/manual/keep-me-1.md <- stale, untracked
engram/demo/manual/hard-gone-2.md <- still present
Operating System
macOS
Release Version (Report Context)
3.4.0
Agent / Client
Other
📋 Relevant Logs
No response
💡 Additional Context
Client: Pi (via gentle-engram); the exporter is client-independent.
Current main (8589a5f), internal/obsidian/exporter.go:
- The cleanup loop only visits rows returned by
store.Export()that haveDeletedAt != nil. Hard-deleted rows are not returned, so theirstate.Filesentries are never reconciled. - The export loop computes
relPathfrom the current slug and assignsstate.Files[obs.ID] = relPathwithout comparing it with the previously tracked path.
A possible approach: after the export loop, remove every tracked state.Files entry whose ID is not a live observation, and when the tracked path for a live ID differs from the new relPath, delete the old file (with the same containment checks used today).
Workaround: compare the state's file IDs with live observation IDs, then delete the orphaned files and prune the state by hand, after taking a backup.
- Dominant language
- Go
- Stars
- 7.1k
- Forks
- 729
- Avg merge
- 11h 17m
- Merged PRs (30d)
- 334
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Gentleman-Programming/engram
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Gentleman-Programming/engram#1899 ·
Maintainers usually reply within 1 day
-
status:needs-review type:bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Gentleman-Programming/engram#1687 ·
Maintainers usually reply within 1 day
-
priority:medium status:needs-review type:feature
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
Gentleman-Programming/engram#863 · 1 comment ·
Maintainers usually reply within 1 day
-
status:needs-review type:bug
Difficulty 3/5 1-2 days Newbie friendliness 70/100
Gentleman-Programming/engram#1897 ·
Maintainers usually reply within 1 day
-
status:needs-review type:bug
Difficulty 4/5 3-5 days Newbie friendliness 25/100
Gentleman-Programming/engram#1896 ·
Maintainers usually reply within 1 day
All issues in Gentleman-Programming/engram
Similar issues
-
documentation hacktoberfest priority: low skill: good first issue status: ready for dev
Difficulty 2/5 Half a day Newbie friendliness 85/100
hyperledger-labs/fabric-smart-client#2142 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
numberly/vault-db-injector#107 ·
Maintainers usually reply within 1 day
-
area/import kind/bug
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
[DOC] Wrong sg_egress_ports range in the Terraform Databricks Guide for AWS Hub & Spoke with Network FirewallPossibly taken @akhil9tiet claimed this today. Opendocs
Difficulty 1/5 Under an hour Newbie friendliness 72/100
databricks/terraform-provider-databricks#6080 · 1 comment ·
Maintainers usually reply within 3 days
-
kind/documentation
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
kubernetes-sigs/dra-driver-nvidia-gpu#1569 ·
Maintainers usually reply within 1 day