Skip to content

Conversation

@devin-ai-integration
Copy link
Contributor

refactor(desktop): store organizations as markdown files with YAML frontmatter

Summary

Changes the organization persister from storing all organizations in a single organizations.json file to storing each organization as an individual markdown file (organizations/<UUID>.md) with YAML frontmatter. This follows the same pattern established in PR #2791 for the human persister.

Organization schema fields (user_id, created_at, name) are stored in YAML frontmatter. The markdown body is always empty since organizations have no memo field.

New files:

  • utils.ts - Path helpers and frontmatter parsing
  • collect.ts - Write operation collector for frontmatter batch export
  • load.ts - Load organizations from markdown files + orphan cleanup
  • migrate.ts - Auto-migration from organizations.json on first load

Example output format:

---
created_at: "2024-01-01T00:00:00Z"
name: "Acme Corp"
user_id: "user-123"
---

Review & Testing Checklist for Human

  • Migration safety: Verify the migration logic in migrate.ts correctly handles edge cases (empty JSON, malformed JSON, partial migration failures). The migration deletes organizations.json after migrating - confirm this is the desired behavior.
  • Orphan cleanup correctness: Review cleanupOrphanOrganizationFiles in load.ts to ensure it won't accidentally delete valid organization files if validOrgIds is incorrectly populated.
  • End-to-end test: Run the desktop app locally with existing organizations.json data and verify:
    1. Migration happens automatically on first load
    2. Organizations are readable after migration
    3. New organizations are saved as markdown files
    4. Deleted organizations have their files cleaned up

Notes

  • The parseMarkdownWithFrontmatter function is duplicated from the human persister rather than shared - this matches the existing pattern but could be consolidated in a future refactor.
  • All 236 desktop tests pass, including the rewritten organization persister tests.

Link to Devin run: https://app.devin.ai/sessions/9d5f79c14dcf49bb88c6ffac3866403b
Requested by: yujonglee (@yujonglee)

…ontmatter

- Change organization persister from JSON to markdown files with frontmatter
- Store each organization as organizations/<UUID>.md
- Add auto-migration from organizations.json on first load
- Add cleanup of orphan files when records are deleted
- Follow the same pattern as the human persister from PR #2791

Organization schema fields (user_id, created_at, name) are stored in
YAML frontmatter. The markdown body is always empty since organizations
have no memo field.

Co-Authored-By: yujonglee <[email protected]>
@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Jan 4, 2026

Deploy Preview for hyprnote-storybook canceled.

Name Link
🔨 Latest commit f96f90b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/695a184694c2c70008f25552

@netlify
Copy link

netlify bot commented Jan 4, 2026

Deploy Preview for howto-fix-macos-audio-selection canceled.

Name Link
🔨 Latest commit f96f90b
🔍 Latest deploy log https://app.netlify.com/projects/howto-fix-macos-audio-selection/deploys/695a18461bdc920009c33188

@netlify
Copy link

netlify bot commented Jan 4, 2026

Deploy Preview for hyprnote canceled.

Name Link
🔨 Latest commit f96f90b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/695a18461bdc920009c33183

Comment on lines +25 to +28
const dirExists = await exists(organizationsDir);
if (dirExists) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Critical: Potential data loss during migration

The migration skips if the organizations directory exists, even if it's empty or incomplete. This prevents re-migration if a previous migration failed partway through.

Scenario that breaks:

  1. User has organizations.json with data
  2. Migration starts and creates organizations directory
  3. Migration fails before completing (crash, network issue, etc.)
  4. On next startup, migration is skipped because directory exists
  5. Data from organizations.json is never migrated

Fix:
Check if the directory is non-empty instead of just checking existence:

const dirExists = await exists(organizationsDir);
if (dirExists) {
  // Check if directory has any .md files before skipping
  const entries = await readDir(organizationsDir);
  const hasMdFiles = entries.some(e => !e.isDirectory && e.name.endsWith('.md'));
  if (hasMdFiles) {
    return; // Only skip if there are actual migrated files
  }
}
Suggested change
const dirExists = await exists(organizationsDir);
if (dirExists) {
return;
}
const dirExists = await exists(organizationsDir);
if (dirExists) {
// Check if directory has any .md files before skipping
const entries = await readDir(organizationsDir);
const hasMdFiles = entries.some(e => !e.isDirectory && e.name.endsWith('.md'));
if (hasMdFiles) {
return; // Only skip if there are actual migrated files
}
}

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@yujonglee yujonglee merged commit 4f79a31 into main Jan 4, 2026
20 of 21 checks passed
@yujonglee yujonglee deleted the devin/1767511753-refactor-organization-persister branch January 4, 2026 07:40
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.

2 participants