From d9fd65c926bb7b93f2bc6bf2bc63ba36a3d2b3ce Mon Sep 17 00:00:00 2001 From: lucaronin Date: Wed, 18 Mar 2026 01:58:33 +0100 Subject: [PATCH] fix: read owner/cadence from properties in ai-context Co-Authored-By: Claude Opus 4.6 (1M context) --- src-tauri/src/vault/getting_started.rs | 6 +++- src-tauri/src/vault/mod_tests.rs | 45 ++++++++++++++++++++------ src-tauri/src/vault/parsing.rs | 15 +++++++-- src/utils/ai-context.test.ts | 4 +-- src/utils/ai-context.ts | 6 +++- 5 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src-tauri/src/vault/getting_started.rs b/src-tauri/src/vault/getting_started.rs index 91556138..81d4ba55 100644 --- a/src-tauri/src/vault/getting_started.rs +++ b/src-tauri/src/vault/getting_started.rs @@ -567,7 +567,11 @@ mod tests { let entry = crate::vault::parse_md_file(&vault_path.join("AGENTS.md")).unwrap(); // No frontmatter title → extracted from H1 heading - assert!(entry.title.contains("Vault Instructions"), "title should come from H1: {}", entry.title); + assert!( + entry.title.contains("Vault Instructions"), + "title should come from H1: {}", + entry.title + ); // Config files have no frontmatter type field — type is None assert_eq!(entry.is_a, None); } diff --git a/src-tauri/src/vault/mod_tests.rs b/src-tauri/src/vault/mod_tests.rs index 4735d4e7..8b5e2c40 100644 --- a/src-tauri/src/vault/mod_tests.rs +++ b/src-tauri/src/vault/mod_tests.rs @@ -76,8 +76,14 @@ fn test_parse_full_frontmatter_scalars() { let dir = TempDir::new().unwrap(); let entry = parse_test_entry(&dir, "laputa.md", FULL_FM_CONTENT); assert_eq!(entry.status, Some("Active".to_string())); - assert_eq!(entry.properties.get("Owner").and_then(|v| v.as_str()), Some("Luca")); - assert_eq!(entry.properties.get("Cadence").and_then(|v| v.as_str()), Some("Weekly")); + assert_eq!( + entry.properties.get("Owner").and_then(|v| v.as_str()), + Some("Luca") + ); + assert_eq!( + entry.properties.get("Cadence").and_then(|v| v.as_str()), + Some("Weekly") + ); } #[test] @@ -509,7 +515,10 @@ fn test_created_at_from_filesystem() { let entry = parse_md_file(&dir.path().join("test.md")).unwrap(); // created_at should be set from filesystem metadata (not None) - assert!(entry.created_at.is_some(), "created_at should come from filesystem"); + assert!( + entry.created_at.is_some(), + "created_at should come from filesystem" + ); } // --- Type relationship tests --- @@ -806,8 +815,14 @@ Priority: High entry.properties.get("Priority").and_then(|v| v.as_str()), Some("High") ); - assert_eq!(entry.properties.get("Owner").and_then(|v| v.as_str()), Some("Luca")); - assert_eq!(entry.properties.get("Cadence").and_then(|v| v.as_str()), Some("Weekly")); + assert_eq!( + entry.properties.get("Owner").and_then(|v| v.as_str()), + Some("Luca") + ); + assert_eq!( + entry.properties.get("Cadence").and_then(|v| v.as_str()), + Some("Weekly") + ); } #[test] @@ -1058,7 +1073,10 @@ fn test_single_element_array_owner_unwraps_to_scalar() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Responsibility\nOwner:\n - Luca\n---\n# Test\n"; let entry = parse_test_entry(&dir, "test.md", content); - assert_eq!(entry.properties.get("Owner").and_then(|v| v.as_str()), Some("Luca")); + assert_eq!( + entry.properties.get("Owner").and_then(|v| v.as_str()), + Some("Luca") + ); assert_eq!(entry.is_a, Some("Responsibility".to_string())); } @@ -1067,7 +1085,10 @@ fn test_single_element_array_cadence_unwraps_to_scalar() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Procedure\nCadence:\n - Weekly\n---\n# Test\n"; let entry = parse_test_entry(&dir, "test.md", content); - assert_eq!(entry.properties.get("Cadence").and_then(|v| v.as_str()), Some("Weekly")); + assert_eq!( + entry.properties.get("Cadence").and_then(|v| v.as_str()), + Some("Weekly") + ); assert_eq!(entry.is_a, Some("Procedure".to_string())); } @@ -1085,8 +1106,14 @@ fn test_scalar_fields_unchanged() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Project\nOwner: Luca\nCadence: Daily\nStatus: Done\n---\n# Test\n"; let entry = parse_test_entry(&dir, "test.md", content); - assert_eq!(entry.properties.get("Owner").and_then(|v| v.as_str()), Some("Luca")); - assert_eq!(entry.properties.get("Cadence").and_then(|v| v.as_str()), Some("Daily")); + assert_eq!( + entry.properties.get("Owner").and_then(|v| v.as_str()), + Some("Luca") + ); + assert_eq!( + entry.properties.get("Cadence").and_then(|v| v.as_str()), + Some("Daily") + ); assert_eq!(entry.status, Some("Done".to_string())); } diff --git a/src-tauri/src/vault/parsing.rs b/src-tauri/src/vault/parsing.rs index 2cda42d7..49700b1d 100644 --- a/src-tauri/src/vault/parsing.rs +++ b/src-tauri/src/vault/parsing.rs @@ -358,17 +358,26 @@ mod tests { #[test] fn test_extract_title_fallback_to_filename() { - assert_eq!(extract_title(None, "", "fallback-title.md"), "Fallback Title"); + assert_eq!( + extract_title(None, "", "fallback-title.md"), + "Fallback Title" + ); } #[test] fn test_extract_title_empty_fm_falls_back_to_h1() { - assert_eq!(extract_title(Some(""), "# From H1\n", "empty-h1.md"), "From H1"); + assert_eq!( + extract_title(Some(""), "# From H1\n", "empty-h1.md"), + "From H1" + ); } #[test] fn test_extract_title_empty_fm_no_h1_falls_back_to_filename() { - assert_eq!(extract_title(Some(""), "No heading here.", "empty-h1.md"), "Empty H1"); + assert_eq!( + extract_title(Some(""), "No heading here.", "empty-h1.md"), + "Empty H1" + ); } // --- extract_snippet tests --- diff --git a/src/utils/ai-context.test.ts b/src/utils/ai-context.test.ts index fc800e82..704ad0c7 100644 --- a/src/utils/ai-context.test.ts +++ b/src/utils/ai-context.test.ts @@ -11,8 +11,6 @@ const makeEntry = (overrides: Partial = {}): VaultEntry => ({ belongsTo: [], relatedTo: [], status: null, - owner: null, - cadence: null, archived: false, trashed: false, trashedAt: null, @@ -157,7 +155,7 @@ describe('buildContextualPrompt', () => { }) describe('buildContextSnapshot', () => { - const active = makeEntry({ path: '/vault/a.md', title: 'Alpha', isA: 'Project', status: 'active', owner: 'Alice' }) + const active = makeEntry({ path: '/vault/a.md', title: 'Alpha', isA: 'Project', status: 'active', properties: { Owner: 'Alice' } }) const entries = [ active, makeEntry({ path: '/vault/b.md', title: 'Beta', isA: 'Person' }), diff --git a/src/utils/ai-context.ts b/src/utils/ai-context.ts index 2722a625..6c867c93 100644 --- a/src/utils/ai-context.ts +++ b/src/utils/ai-context.ts @@ -85,7 +85,11 @@ function entryFrontmatter(e: VaultEntry): Record { const fm: Record = {} if (e.isA) fm.type = e.isA if (e.status) fm.status = e.status - if (e.owner) fm.owner = e.owner + // Owner and cadence are now stored in properties, not first-class fields + const owner = e.properties?.Owner ?? e.properties?.owner + const cadence = e.properties?.Cadence ?? e.properties?.cadence + if (owner) fm.owner = typeof owner === 'string' ? owner : String(owner) + if (cadence) fm.cadence = typeof cadence === 'string' ? cadence : String(cadence) if (e.belongsTo.length > 0) fm.belongsTo = e.belongsTo if (e.relatedTo.length > 0) fm.relatedTo = e.relatedTo if (Object.keys(e.relationships).length > 0) fm.relationships = e.relationships