-
Notifications
You must be signed in to change notification settings - Fork 467
feat(desktop): store humans as markdown files with YAML frontmatter #2791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Change human persister to store each human as humans/<UUID>.md - Store structure info (user_id, created_at, name, email, etc.) as YAML frontmatter - Store memo field as markdown body content - Add automatic migration from humans.json to new format - Add cleanup of orphan human files when humans are deleted - Add yaml dependency for frontmatter parsing - Add 'text' operation type to persister utils for raw text file writes - Update tests for new markdown file format Co-Authored-By: yujonglee <[email protected]>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
✅ Deploy Preview for hyprnote canceled.
|
✅ Deploy Preview for hyprnote-storybook canceled.
|
✅ Deploy Preview for howto-fix-macos-audio-selection canceled.
|
| const dirExists = await exists(humansDir); | ||
| if (dirExists) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Migration incompleteness bug: If both humans.json and the humans/ directory exist, the migration exits early without deleting the old JSON file. This can occur if a previous migration was interrupted after creating the directory but before deleting the JSON file. On subsequent runs, humans.json will persist indefinitely, potentially causing data inconsistency.
Fix: Change the logic to attempt cleanup of humans.json whenever it exists and the directory exists:
const dirExists = await exists(humansDir);
if (dirExists) {
// Directory exists - cleanup old JSON if present
try {
await remove(humansJsonPath);
console.log("[HumanPersister] Cleaned up old humans.json file");
} catch (error) {
if (!isFileNotFoundError(error)) {
console.error("[HumanPersister] Failed to cleanup humans.json:", error);
}
}
return;
}| const dirExists = await exists(humansDir); | |
| if (dirExists) { | |
| return; | |
| } | |
| const dirExists = await exists(humansDir); | |
| if (dirExists) { | |
| // Directory exists - cleanup old JSON if present | |
| try { | |
| await remove(humansJsonPath); | |
| console.log("[HumanPersister] Cleaned up old humans.json file"); | |
| } catch (error) { | |
| if (!isFileNotFoundError(error)) { | |
| console.error("[HumanPersister] Failed to cleanup humans.json:", error); | |
| } | |
| } | |
| return; | |
| } |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
- Fix Content<Schemas> return type using 'as unknown as' pattern - Add missing DirEntry properties (isFile, isSymlink) in test mocks - Fix exists mock to accept string | URL parameter type - Remove unused asTableChanges import Co-Authored-By: yujonglee <[email protected]>
…n-persister-markdown
- Add frontmatter crate as dependency to export plugin - Add parse_frontmatter, serialize_frontmatter, export_frontmatter, and export_frontmatter_batch commands - Update human persister to use plugin commands instead of yaml npm package - Add frontmatter-batch operation type to createSessionDirPersister - Remove yaml npm dependency from desktop package.json - Update tests to mock new plugin commands Co-Authored-By: yujonglee <[email protected]>
Co-Authored-By: yujonglee <[email protected]>
Co-Authored-By: yujonglee <[email protected]>
| "[HumanPersister] Failed to parse frontmatter:", | ||
| result.error, | ||
| ); | ||
| return { frontmatter: {}, body: content }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .trim() call modifies user data by removing leading/trailing whitespace from memo content. This creates an asymmetric transformation:
Issue: When a memo is saved with intentional trailing/leading whitespace → loaded (trimmed) → saved again without modification, the whitespace is permanently lost.
Impact: Data loss for users who intentionally format their memos with specific whitespace.
Fix:
body: result.data.content, // Remove .trim()If trimming is needed for display purposes, it should be done in the UI layer, not during data persistence.
| return { frontmatter: {}, body: content }; | |
| body: result.data.content, |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
| } | ||
| return { | ||
| frontmatter: result.data.frontmatter as Record<string, unknown>, | ||
| body: result.data.content.trim(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The body content is trimmed after parsing, which will cause data loss on round-trip if the memo field contains intentional trailing/leading whitespace or newlines. When saving (collect.ts:46), the memo is stored as-is, but when loading, it's trimmed. This creates data integrity issues.
// Fix: Remove the trim() to preserve exact memo content
body: result.data.content,| body: result.data.content.trim(), | |
| body: result.data.content, |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
…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]>
feat(desktop): store humans as markdown files with YAML frontmatter
Summary
Changes the human persister to store each human as an individual markdown file at
humans/<UUID>.mdinstead of a singlehumans.jsonfile. Structure info is stored as YAML frontmatter, and thememofield becomes the markdown body content.Example output format:
Key changes:
collect.ts,load.ts,migrate.ts,utils.tsin the human persister directoryfrontmatterRust crate via export plugin (no npm yaml dependency)parse_frontmatter,serialize_frontmatter,export_frontmatter,export_frontmatter_batchfrontmatter-batchoperation type inpersister/utils.tsfor batch writeshumans.json(deletes old file after migration)Updates since last revision
Review & Testing Checklist for Human
humans.jsondata. The migration deletes the old JSON file after batch export - verify no data loss occurs if the app crashes mid-migration.Recommended test plan:
humans.jsonfile with test data in the app's data directory.mdfiles are created inhumans/directoryNotes
pnpm-lock.yamlchanges are pnpm re-resolving transitive dependencies after removing the yaml package---delimiters and sorts keys alphabeticallyLink to Devin run: https://app.devin.ai/sessions/b519cf12b5db4537b1c8a6f706af4943
Requested by: yujonglee (@yujonglee)