ENG-1656 Rename folder for imported vault - #1130
Conversation
Use .dg.metadata for folder identity and auto-rename legacy import/ folders on load and import so multi-user lab vaults are easier to tell apart. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
PR size/scope checkThis PR is over our review-size guideline.
Please split this into smaller PRs unless there is a clear reason the changes need to land together. If keeping it as one PR, please add a brief justification covering:
|
Return undefined when owner username is not cached, treat legacy collision-suffixed folders as eligible for migration, and resolve lint warnings in changed files. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
A few issues picked up, noted here and in comments. I won't block on them, but combined probably worth another review and discussion in the dev meeting. Especially the last question.
Preserve pre-existing custom import folder names
discourse-graph\apps\obsidian\src\utils\importFolderMetadata.ts:252-253
The task description states: "We should rename the folder when its original name is just the vault name.". For vaults where a user manually renamed an import folder before thismigratedflag existed, the metadata will not havemigrated: true, so this branch treats the custom basename as stale and renames it to the generateduser-spacename as soon as a username is available. Since the previous resolver intentionally used.dg.metadatato find folders independent of their names, this discards existing custom organization instead of marking those folders as custom/migrated.
Migration picks first author not majority
resolveUserNameFromFolderreturns the username for the first markdown file (in vault iteration order) with a resolvable authorId, while imports useresolveOwnerUserNameto pick the most frequent author in the space. Migration can rename folders with the wrong user prefix, and migrated: true prevents correction on later imports.
Question: why do imports derive the folder prefix from resolveOwnerUserName()?
- As written, imports still create one folder per
spaceUri, not one folder per author. For a multi-author space,resolveOwnerUserName()picks the most frequent author in the current import batch, whilemigrateImportFolderNames()appears to use the first knownauthorIdfound in existing files. That means a space likediscourse-graphscould be renamed to one contributor’s username, and future imports from that same space would continue landing in that single folder via.dg.metadata.spaceUri. - Is that intended? If the desired schema is
{owner-username}-{spaceName}, should we fetch the actual space owner and have one folder per author-spaceName instead of deriving it from imported node authors?
cc @maparent
|
Actually, re "Migration picks first author not majority". Why are we using top author/first author instead of querying for the actual synced author? Why are we guessing instead of going to the source (username in supabase)? |
mdroidian
left a comment
There was a problem hiding this comment.
Suggested PR scope change: https://linear.app/discourse-graphs/issue/ENG-1656/rename-folder-for-imported-vault#comment-c8adda2b
… retry - Replace as-cast with isImportFolderMetadata type guard that validates both spaceUri and spaceName - Inline tryParse wrapper; no behaviour change - Add comment explaining why trailing-comma retry exists Co-authored-by: Cursor <cursoragent@cursor.com>
Remove resolveOwnerUserName (which picked the most-frequent node author) in favour of getSpaceOwnerNames, which queries LocalAccess joined with my_accounts to get the actual person account for each space. Also remove the on-load fetchUserNames call that was only needed for the migration flow. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace two-step LocalAccess/my_accounts query with fetchUserNames (already called pre-import in the modal) so plugin.settings.userNames is warm, then resolve the owner name via the first node authorId for the space. Co-authored-by: Cursor <cursoragent@cursor.com>
|
did not look in depth, but immediate reaction to reading this: |
mdroidian
left a comment
There was a problem hiding this comment.
I truly don’t think deriving ownerUserName from the first imported node’s is the right move here, but I won't block on it.
Also, @trangdoan982 don't forget to update the title/body to match the PR's new intent.
|
Finally reading this seriously. I understand the motivation, and I think it's appropriate for Obsidian vaults which have a defined username. "multi-user" vaults currently refers to Roam vaults, where there is no name collision by design. This may not be true of future multi-user vaults. So… Intuition: I think that vault owner in the name is only appropriate for mono-user vaults. For Roam, maybe we should prefix with the platform, and leave it at that. And always prefix with (foreign) platform. Will we need another distinguisher for an eventual non-roam multi-user platform? We'll see when we get there, but I doubt that most common author will be the right one. (Admin authors? Org? I know we don't have those yet.) |
| }): Promise<{ folderPath: string; metadata: ImportFolderMetadata } | null> => { | ||
| const importExists = await adapter.exists(IMPORT_ROOT); | ||
| if (!importExists) return map; | ||
| if (!importExists) return null; |
There was a problem hiding this comment.
I think that's the right call, but flagging that means we're not resilient to the user renaming the folder.
There was a problem hiding this comment.
Worth splitting into two cases, because the answer differs:
Renaming an individual import folder is handled. Folder identity is carried by .dg.metadata (spaceUri), not by the basename. findImportFolderBySpaceUri scans every child of import/ and matches on spaceUri, so import/alice-vault → import/whatever is found again on the next import and reused rather than duplicated. That is the reason the auto-rename migration could be dropped in 72bf26d.
Renaming or moving the import root is not handled — and I think that is the case you are pointing at, since the line is the if (!importExists) return null; guard. IMPORT_ROOT is the fixed string "import" and adapter.list is non-recursive, so renaming the root to imported/, moving it, or nesting import folders one level deeper makes existing imports invisible and the next import recreates them under a fresh import/. Same if .dg.metadata is deleted.
Not fixing that here — making the root configurable or the scan recursive is a bigger change than this ticket — but I recorded it as a known limitation on IMPORT_ROOT in 0f388d8 so it does not have to be rediscovered. Happy to file a follow-up if you think it is worth handling.
…authors Address review feedback on owner name resolution: - Replace the first-node authorId lookup with a LocalAccess -> my_accounts query scoped to the source space. Node authors identify who wrote a given node, which can differ from the account the source space belongs to, so the folder could be named after the wrong person. - Omit the owner prefix when it matches the space name. Obsidian defaults the account name to the vault name, which is also the space name, so the folder would otherwise read "my-vault-my-vault" for anyone who has not set a username in settings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r-for-imported-vault
Folder identity lives in .dg.metadata, so renaming an individual import folder is already safe. The fixed, non-recursive import root is not — record that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Addressed the outstanding review feedback — summary of what changed and what I deliberately did not change. Owner name no longer comes from node authors (@mdroidian, @maparent). Owner prefix suppressed when redundant (@maparent). Confirmed your reading of
Username changes after import (@maparent) — not handled, agreed it is a separate low-priority issue. Noted in the PR body under out of scope rather than silently dropped. Multi-user / Roam vaults (@maparent) — not addressed. Let me know if team has decided on a naming convention for Roam-Obsidian sync for folder name Known limitation recorded in code: Also updated the title to the repo convention ( Merged current @maparent this needs your re-review to clear the change request. |
https://www.loom.com/share/39deddfaa529440d96484590a8c0d53f
Summary
Imported vaults all landed in
import/{spaceName}, which is just the source vault's name and does not say whose vault it was. This adds the owner to the folder name and moves folder identity off the folder name entirely.{ownerName}-{spaceName}underimport/.LocalAccessfor the sourcespace_id, joined tomy_accountsfiltered onagent_type = "person". Node authors are deliberately not used: they tell us who wrote a given node, which can differ from the account the source space belongs to.my-vault-my-vaultfor anyone who has not set a username in settings. Same fallback when the owner lookup returns nothing, so a blocked query or a space with no person account degrades to the old{spaceName}naming rather than failing..dg.metadata(spaceUri,spaceName, optionaluserName) rather than the folder name, so a user who renames an import folder keeps it — the next import finds it again byspaceUriand reuses it instead of creating a duplicate..dg.metadatawhose name matches the space name are adopted by writing metadata into them, rather than being abandoned.spaceUriis resolved by keeping the more recently modified one.Out of scope
Dropped or deferred after review discussion:
migratedmetadata flag that came up in review.importroot —IMPORT_ROOTis a fixed path and only its immediate children are scanned, so moving the root or nesting import folders deeper hides existing imports and the next import recreates them. Recorded as a known limitation in the code (0f388d8).Test plan
Automated (run on the merge result, current
mainmerged in):tsc --noEmitclean forapps/obsidianeslintclean on both changed files (0 errors, 0 new warnings)Manual — needs a re-check since the naming rules changed after the Loom was recorded:
import/{username}-{spaceName}/import/{spaceName}/, notimport/{spaceName}-{spaceName}/import/{spaceName}/with no.dg.metadata→ adopted, metadata written, folder not renamed