Skip to content

fix(export): adopt a pre-manifest vault's own notes instead of orphaning them (#2863) - #2914

Open
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/obsidian-vault-manifest-migration
Open

fix(export): adopt a pre-manifest vault's own notes instead of orphaning them (#2863)#2914
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/obsidian-vault-manifest-migration

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

Fixes #2863.

Thanks @ — the diagnosis was exact, including that the leftover notes carry graphify's own frontmatter, which is what makes them recoverable.

The bug

#1506 added .graphify_obsidian_manifest.json so a re-export can update the notes graphify owns while refusing to touch the user's. A vault created by an earlier graphify has no manifest, so _owned starts empty — and every note graphify itself wrote last time reads as a user file.

Reproduced on a 3-node graph: build a vault, drop the manifest to stand in for a pre-#1506 vault, rename two nodes, re-export.

before:  6 notes for a 3-node graph
         Alpha.md, Beta.md, "Beta renamed.md", Gamma.md, "Gamma renamed.md", community
         WARNING: skipped 3 pre-existing file(s) graphify did not create ...

after:   exactly the current generation, no stale notes
         Alpha.md, "Beta renamed.md", "Gamma renamed.md", _COMMUNITY_Community 0.md

The warning is the part I'd flag as worst: it names files graphify wrote itself and tells the user it didn't. Someone cleaning up by hand has no way to tell the two generations apart, because both carry graphify frontmatter.

The change

A one-time migration, run only when no manifest exists, adopting a file solely on graphify's own signature. Two shapes, because the notes are not alike:

  • Node notes carry YAML frontmatter with a tag in the graphify/ namespace. Only that block is inspected, so a user's note that merely mentions graphify in prose is not adopted — there is a test for exactly that.
  • Community notes carry no frontmatter at all (I assumed they did, and the first version of this fix silently left every one behind until the reproduction showed it). They are matched on graphify's filename prefix and the Dataview query it writes into every one. Requiring both means a user's own _COMMUNITY_notes.md is not adopted on the name alone.

Scoped to top-level *.md — the only files identifiable from their content.

.obsidian/graph.json is deliberately left unowned

graphify writes one, but so does Obsidian, and with no manifest there is no way to tell whose it is. Guessing wrong costs the user their vault configuration, which is far worse than one file staying unowned.

So a pre-manifest vault still reports one skipped file. That warning is now correct: for that file graphify genuinely cannot confirm it was the author. Worth knowing rather than being surprised by it.

Tests

tests/test_obsidian_vault_migration.py (16 tests): no second generation after a re-export, stale notes for renamed nodes gone, no false "did not create" warning, and the manifest written so the migration happens once.

Then the safety half, which is the part that matters: a user's own Alpha.md survives untouched, a note merely mentioning graphify is not adopted, a user's _COMMUNITY_*.md without the query marker is not adopted, .obsidian/graph.json is preserved byte-for-byte, a vault that already has a manifest is unaffected, and a fresh directory still works.

Reverting export.py and keeping the tests fails 4 (6 more skip, since the detector does not exist pre-fix).

Validation

Windows 11, Python 3.12, branched off b2cd362 (0.9.48).

  • Full suite: 16 failed, 4835 passed -> 16 failed, 4851 passed. Identical failure set — no regressions; the +16 are the new tests. Both runs were taken on this machine against this commit, rather than reusing an older baseline.

…ing them (Graphify-Labs#2863)

Graphify-Labs#1506 added .graphify_obsidian_manifest.json so a re-export can update the notes
graphify owns while refusing to touch the user's. A vault created by an EARLIER
graphify has no manifest, so _owned starts empty and every note graphify itself
wrote last time reads as a user file. The re-export writes fresh notes BESIDE
the stale ones, and warns that graphify "did not create" files it did.

Reproduced on a 3-node graph: build a vault, drop the manifest to simulate a
pre-Graphify-Labs#1506 vault, rename two nodes, re-export.

  before: 6 notes for a 3-node graph -- Alpha, Beta, "Beta renamed", Gamma,
          "Gamma renamed", community -- plus
          "WARNING: skipped 3 pre-existing file(s) graphify did not create"
  after:  exactly the current generation, no stale notes, no false warning

The migration runs once, only when no manifest exists, and adopts a file solely
on graphify's own signature:

  - node notes carry YAML frontmatter with a tag in the graphify/ namespace;
    only that block is inspected, so a user's note that merely mentions
    graphify in prose is not adopted;
  - community notes carry NO frontmatter at all, so they are matched on
    graphify's filename prefix AND the Dataview query it writes into every one.
    Requiring both keeps a user's own _COMMUNITY_*.md from being adopted on the
    name alone.

Scoped to top-level *.md, the only files identifiable from their content.

.obsidian/graph.json is deliberately NOT adopted. graphify writes one, but so
does Obsidian, and with no manifest there is no way to tell whose it is --
guessing wrong costs the user their vault configuration, which is a far worse
outcome than one file staying unowned. It is therefore still reported as skipped
on a pre-manifest vault; that warning is now correct, because for that one file
graphify genuinely cannot confirm it was the author.
Copilot AI lite review requested due to automatic review settings August 20, 2026 21:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.

Formal verification. 1 change(s) tested, no difference found (not proven).


Graphify review — findings

Adds a one-time migration in to_obsidian so vaults exported before the ownership manifest existed don't accumulate a second generation of notes: when no .graphify_obsidian_manifest.json is found, _adopt_pre_manifest_notes seeds _owned from top-level *.md files that _is_graphify_note identifies by their graphify/ frontmatter tag (or, for community notes, the filename prefix plus the Dataview query marker). Deliberately excludes .obsidian/graph.json from adoption since Obsidian also writes it and ownership is ambiguous without a manifest. Adds tests/test_obsidian_vault_migration.py covering the bug, user-note safety, unchanged manifest/fresh-vault behavior, and the detector.

Worth a look

  • Detector treats non-tag YAML list items as graphify ownership tagsgraphify/export.py:581 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Graphify tag detector authorizes any frontmatter list itemgraphify/export.py:599 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 513 functions depend on the 71 functions this change touches.

Health — this change adds coupling hotspots:

  • new: _rebuild_code() — 98 callers, 50 callees
  • new: build_merge() — 46 callers, 14 callees
  • new: to_obsidian() — 36 callers, 13 callees
  • new: to_json() — 50 callers, 7 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _make_graph() — 32 callers, 6 callees
  • new: run_pipeline() — 8 callers, 13 callees
  • new: to_canvas() — 17 callers, 4 callees
  • …and 5 more — each is listed as a finding

Verification — 513 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 360 function(s) in the blast radius were not formally verified this run

Formal verification

No difference found (not proven): No behavior difference found in to\_obsidian (not a proof).

The verifier ran both versions of to\_obsidian on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 13 more finding(s) on lines outside this diff (see the check run).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Obsidian vault ownership manifest (#1506) has no migration path: pre-manifest vaults end up holding two generations of notes

2 participants