Experiment Memory peon-notify

Normalizing vault paths in peon.json and fixing the _obsidian_dir() helper will eliminate orphaned notes caused by project-name-to-directory mismatches

vault-integrationpath-mappingops
Hypothesis

Normalizing vault paths in peon.json and fixing the _obsidian_dir() helper will eliminate orphaned notes caused by project-name-to-directory mismatches

Result: confirmed
Key Findings

Orphaned notes eliminated. All session-generated notes now land in correct vault directories. The _obsidian_dir() mapping is the critical bridge between Claude Code's project naming and Obsidian's directory structure.

Changelog

DateSummary
2026-04-06Audited: added Changelog, domain tag ops, stamped last_audited
2026-03-28Initial creation

Hypothesis

Normalizing vault paths in peon.json and fixing the _obsidian_dir() helper will eliminate orphaned notes caused by project-name-to-directory mismatches. The session backfill pipeline (iteration 2) indexed 286 sessions, but 23 had incorrect project attribution because Claude Code’s project directory naming convention diverges from the vault’s directory structure. For example, Claude Code stores sessions under -Users-pluto-Documents-dakka (path-encoded), but the vault expects notes under projects/dakka/. Without a reliable mapping between these two naming systems, session-generated notes end up in nonexistent directories and become orphaned: invisible to Obsidian’s graph and link resolution.

Method

The fix involved three changes:

1. peon.json vault_path normalization: The vault_path field in peon.json was previously an absolute path (/Users/pluto/vault) but the hook scripts were inconsistently using it, sometimes appending a trailing slash and sometimes not. Normalized to always include a trailing slash, and updated all hook scripts to use ${vault_path} without modification.

2. _obsidian_dir() helper function: Added a new helper that maps Claude Code project names to vault directory paths. The mapping handles three edge cases:

Edge caseClaude Code nameVault directoryMapping rule
Hyphenated names-Users-pluto-Documents-jobs-applyprojects/jobs-apply/Extract last path segment after Documents-
Nested packages-Users-pluto-Documents-dakka-packages-gorkprojects/dakka/Sub-project merging: strip package suffix
Symlinked repos-Users-pluto-code-context-curatorprojects/context-curator/Handle non-Documents roots by taking the last segment

The helper first strips the leading -Users-pluto- prefix, then applies the Documents/code root detection, then applies sub-project merging, and finally returns the canonical vault directory path.

3. Retroactive orphan fix: Ran a one-time script to relocate the 23 orphaned notes from their incorrect locations to the correct vault directories. Each note’s internal links were updated to reflect the new path.

Results

Confirmed. After deploying the fix:

MetricBeforeAfter
Orphaned notes230
Project mapping accuracy91.9% (263/286)100% (286/286)
_obsidian_dir() edge cases handled03
Notes in correct vault directory263286

All subsequent session-generated notes (tested over 48 hours, 12 new sessions) landed in the correct vault directories without manual intervention.

Findings

  1. The _obsidian_dir() mapping is the critical bridge between two naming worlds. Claude Code uses filesystem-path-encoded project names (because it stores session data in directories named after the project path). Obsidian uses human-readable directory names. Without an explicit mapping function, every component that writes to the vault must independently solve this translation, which leads to inconsistencies.

  2. Sub-project merging must happen in _obsidian_dir(), not downstream. The initial implementation attempted to handle sub-project merging in the note-writing code, but this meant every hook that writes notes needed its own merging logic. Moving merging into _obsidian_dir() centralizes the logic and ensures consistency.

  3. Trailing slash normalization is trivial but high-impact. Two of the 23 orphaned notes were caused solely by the missing trailing slash on vault_path, which resulted in paths like projects/dakkasessions/ instead of projects/dakka/sessions/. This class of bug is embarrassingly simple but difficult to detect without automated path validation.

  4. Symlinked repos require special handling. Projects cloned into ~/code/ instead of ~/Documents/ produce Claude Code project names with a different root prefix. The _obsidian_dir() helper must recognize both roots, which means maintaining a list of known root directories. Currently hardcoded to Documents and code; a future improvement would auto-detect roots from peon.json configuration.

Next Steps

The vault path mapping is now complete and all session-generated notes land correctly. The peon-notify system has reached a stable state with three completed iterations: CodeGuard (iteration 1), session backfill (iteration 2), and path mapping (iteration 3). Future work will focus on higher-level vault automation: the context-curator skill sync system, which builds on peon-notify’s hook infrastructure to keep vault skills synchronized with their source definitions. See experiments/context-curator/2026-04-02-skill-sync-system.