Skip to content

ENG-1656: Rename imported vault folders to owner-username-spaceName#1130

Open
trangdoan982 wants to merge 10 commits into
mainfrom
eng-1656-rename-folder-for-imported-vault
Open

ENG-1656: Rename imported vault folders to owner-username-spaceName#1130
trangdoan982 wants to merge 10 commits into
mainfrom
eng-1656-rename-folder-for-imported-vault

Conversation

@trangdoan982

@trangdoan982 trangdoan982 commented Jun 18, 2026

Copy link
Copy Markdown
Member

https://www.loom.com/share/39deddfaa529440d96484590a8c0d53f

Summary

  • Rename imported vault folders under import/ from {spaceName} to {owner-username}-{spaceName} so multi-user lab vaults are easier to distinguish
  • Use .dg.metadata (spaceUri, userName, migrated) for folder identity instead of the folder name, respecting user-customized names
  • Auto-migrate legacy folders on plugin load and during import; retry migration after sync fetches userNames

Test plan

  • Fresh import from a space with a single author creates import/{username}-{vaultName}/
  • Existing import/{vaultName}/ folder with .dg.metadata renames on plugin reload
  • Manually renamed import folder is left unchanged on subsequent load/import

Made with Cursor


Open in Devin Review

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>
@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
discourse-graph Skipped Skipped Jun 22, 2026 1:03pm

Request Review

@linear-code

linear-code Bot commented Jun 18, 2026

Copy link
Copy Markdown

ENG-1656

@supabase

supabase Bot commented Jun 18, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@graphite-app

graphite-app Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

PR size/scope check

This PR is over our review-size guideline.

  • Recommended: ~200 lines changed
  • Acceptable limit: up to 400 lines when well-scoped/self-contained
  • Preferred file count: fewer than 5 files

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:

  • What single problem this PR solves
  • Why the files/changes are coupled

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread apps/obsidian/src/utils/importNodes.ts Outdated
Comment thread apps/obsidian/src/utils/importFolderNaming.ts Outdated
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>

@mdroidian mdroidian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 this migrated flag existed, the metadata will not have migrated: true, so this branch treats the custom basename as stale and renames it to the generated user-space name as soon as a username is available. Since the previous resolver intentionally used .dg.metadata to 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

  • resolveUserNameFromFolder returns the username for the first markdown file (in vault iteration order) with a resolvable authorId, while imports use resolveOwnerUserName to 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, while migrateImportFolderNames() appears to use the first known authorId found in existing files. That means a space like discourse-graphs could 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

Comment thread apps/obsidian/src/types.ts Outdated
Comment thread apps/obsidian/src/utils/importFolderMetadata.ts Outdated
Comment thread apps/obsidian/src/utils/importFolderMetadata.ts Outdated
Comment thread apps/obsidian/src/utils/importFolderMetadata.ts Outdated
Comment thread apps/obsidian/src/utils/importFolderMetadata.ts Outdated
@mdroidian

mdroidian commented Jun 21, 2026

Copy link
Copy Markdown
Member

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 mdroidian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

… 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>
@trangdoan982 trangdoan982 requested a review from mdroidian June 22, 2026 13:05

Copy link
Copy Markdown
Collaborator

did not look in depth, but immediate reaction to reading this: migrated: true is an anti-pattern, it should be version: 1, so we can know to do subsequent migrations.

@mdroidian mdroidian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

nodes: ImportableNode[],
plugin: DiscourseGraphPlugin,
): string | undefined => {
const authorId = nodes.find((n) => n.authorId !== undefined)?.authorId;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don’t think deriving ownerUserName from the first imported node’s authorId matches the intent here. This gives us “an author of one selected node,” not the username/account associated with the source space. If the selected nodes are authored by multiple people, or the first selected node was authored by someone other than the vault account, the import folder could be named after the wrong person.

For Obsidian, the configured username is uploaded as PlatformAccount.name via create_account_in_space, and that account is linked to the vault’s space through LocalAccess(space_id, account_id). Since this flow already has the source spaceId, could we derive the username from that space/account relationship instead? For example, query LocalAccess for this spaceId, join to my_accounts, prefer the relevant agent_type === "person" account, and fall back to spaceName if the lookup is unavailable or ambiguous.

Copy link
Copy Markdown
Collaborator

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.)
All that said, I won't block on this, as we're only sharing obsidian vaults at this point in time and I'm happy with this heuristic for obsidian vaults. But I want to flag that the "most common author" may not be the right intuition to extend. (Is there a page where options are enumerated and discussed?)

@maparent maparent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Other than the author name comment, lgtm.
I also want to flag that we're not handling the case where the username changes. I also think that's a different issue, and of low priority.

plugin: DiscourseGraphPlugin,
): string | undefined => {
const authorId = nodes.find((n) => n.authorId !== undefined)?.authorId;
return authorId !== undefined

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I have a concern, let me know if I'm missing something. We are looking at first author; but in obsidian first author is the default author, which is created in src/utils/supabaseContext.ts:101, and unless we've set a username in settings, the default name is the same as the vault name (L80). So we'd basically repeat the vault name in most cases. Can we detect that case, and consider that the author name is undefined if it's identical to the vault name?

}): Promise<{ folderPath: string; metadata: ImportFolderMetadata } | null> => {
const importExists = await adapter.exists(IMPORT_ROOT);
if (!importExists) return map;
if (!importExists) return null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think that's the right call, but flagging that means we're not resilient to the user renaming the folder.

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.

3 participants