Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions docs/library-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Comprehensive reference for the mnemonic Python library modules.
- [paths](#paths) - Path resolution and filesystem layout
- [config](#config) - Configuration management
- [memory_reader](#memory_reader) - Memory file parsing
- [migrate_filenames](#migrate_filenames) - Memory file migration utilities
- [search](#search) - Memory search and ranking
- [relationships](#relationships) - Relationship types and linking
- [ontology](#ontology) - Custom ontology support
Expand Down Expand Up @@ -156,6 +157,112 @@ Dictionary with keys:

---

### migrate_filenames

**File**: `lib/migrate_filenames.py`

Migration utilities for transitioning memory files from `{uuid}-{slug}.memory.md` to `{slug}.memory.md` naming convention. Handles collision detection, content merging, and idempotent execution.

**Key Functions**:

````python
from lib.migrate_filenames import migrate_all, is_migration_complete, migrate_file
from pathlib import Path

# Check if migration already completed
memory_root = Path.home() / ".claude" / "mnemonic"
Comment on lines +170 to +173
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The examples hardcode memory_root = Path.home() / ".claude" / "mnemonic", but this repo’s canonical way to locate the memory root is lib.config.get_memory_root() (supports user-configured memory_store_path). Consider updating the examples to use get_memory_root() to avoid documenting a potentially incorrect path for users with custom configs.

Suggested change
from pathlib import Path
# Check if migration already completed
memory_root = Path.home() / ".claude" / "mnemonic"
from lib.config import get_memory_root
# Check if migration already completed
memory_root = get_memory_root()

Copilot uses AI. Check for mistakes.
if not is_migration_complete(memory_root):
# Perform dry run first
results = migrate_all(memory_root, dry_run=True)
print(f"Would migrate {len(results)} files")

# Execute migration
results = migrate_all(memory_root, dry_run=False)
for result in results:
print(f"{result.action}: {result.original.name} → {result.target.name}")
````

**Migration Behavior**:
- **Rename**: `{uuid}-decision.memory.md` → `decision.memory.md` (preserves git history)
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The migration behavior bullet says rename “preserves git history”, but the implementation only attempts git mv and falls back to Path.rename() when git mv fails. Please qualify this claim to match the actual behavior.

Suggested change
- **Rename**: `{uuid}-decision.memory.md``decision.memory.md` (preserves git history)
- **Rename**: `{uuid}-decision.memory.md``decision.memory.md` (attempts to preserve git history via `git mv` when available)

Copilot uses AI. Check for mistakes.
- **Merge**: If `decision.memory.md` already exists, merges content from UUID-prefixed file
- **Idempotent**: Marks completion with `.migration_slug_only_complete` marker file

**API Reference**:

#### `migrate_all(mnemonic_root: Path, dry_run: bool = False) -> list[MigrationResult]`
Migrate all UUID-prefixed memory files in directory tree.

**Parameters**:
- `mnemonic_root`: Root directory to scan recursively
- `dry_run`: If True, return results without modifying files

**Returns**: List of `MigrationResult` objects with fields:
- `original` (Path): Source file path
- `target` (Path): Destination file path
- `action` (str): One of "renamed", "merged", "skipped"
- `merged_with` (Optional[Path]): Target file if merged

**Behavior**:
- Skips if `.migration_slug_only_complete` marker exists
- Uses `git mv` when possible to preserve history
- Merges content on collision (appends under "Merged Content" heading)
- Creates marker file on completion

#### `migrate_file(file_path: Path, dry_run: bool = False) -> MigrationResult`
Migrate a single memory file.

**Parameters**:
- `file_path`: Path to memory file
- `dry_run`: If True, return result without modifying files

**Returns**: `MigrationResult` object

**Collision Handling**:
When target file exists, merges content by:
1. Keeping existing frontmatter (preserves original ID, dates)
2. Appending incoming body under separator
3. Adding provenance note with incoming UUID
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Collision handling says the provenance note includes the incoming UUID, but the implementation’s provenance note is based on the incoming frontmatter id: (and doesn’t use the filename UUID). Please update this section to reflect what is actually recorded.

Suggested change
3. Adding provenance note with incoming UUID
3. Adding provenance note with incoming frontmatter `id`

Copilot uses AI. Check for mistakes.

#### `is_migration_complete(mnemonic_root: Path) -> bool`
Check if migration has been run.

**Returns**: True if `.migration_slug_only_complete` marker exists

#### `mark_migration_complete(mnemonic_root: Path) -> None`
Create marker file to prevent re-running migration.

**Use Cases**:
- **Batch Migration**: Run once during setup to clean up legacy filenames
- **Continuous Integration**: Check `is_migration_complete()` before operations
- **Manual Recovery**: Use `migrate_file()` for selective migration after conflicts

**Example - Bulk Migration**:

````python
from lib.migrate_filenames import migrate_all, migration_summary
from pathlib import Path

memory_root = Path("~/.claude/mnemonic").expanduser()

# Execute with logging
results = migrate_all(memory_root, dry_run=False)
summary = migration_summary(results)
Comment on lines +242 to +249
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

migration_summary() is used in the bulk migration example, but it isn’t included anywhere in the API reference for migrate_filenames. Either document migration_summary(results: list[MigrationResult]) -> dict alongside the other functions or avoid using it in the example to keep the section self-contained.

Copilot uses AI. Check for mistakes.

print(f"Migration complete:")
print(f" Renamed: {summary['renamed']} files")
print(f" Merged: {summary['merged']} files")
print(f" Skipped: {summary['skipped']} files")
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The bulk migration example prints a “Skipped” count, but migrate_all() only processes UUID-prefixed files (should_migrate() filter), so results from migrate_all() will never include the "skipped" action (it’s only returned by migrate_file() when called on a non-UUID file). Consider removing/adjusting the skipped count in this example or clarifying when "skipped" can occur.

Suggested change
print(f" Skipped: {summary['skipped']} files")

Copilot uses AI. Check for mistakes.
````

**Design Notes**:
- Atomic writes via `.tmp` files to prevent corruption
- Git-aware (uses `git mv` when possible)
- Collision-safe (merges rather than overwrites)
- Idempotent by default (marker file prevents re-runs)
- CLI support: `python -m lib.migrate_filenames --dry-run`
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The CLI note suggests running python -m lib.migrate_filenames --dry-run but the module’s __main__ currently hardcodes the root to Path.home() / ".claude" / "mnemonic" (and also supports --force). Consider documenting the hardcoded root and/or mentioning --force so readers understand the CLI’s limitations and available flags.

Suggested change
- CLI support: `python -m lib.migrate_filenames --dry-run`
- CLI support (operates on `~/.claude/mnemonic`): `python -m lib.migrate_filenames --dry-run` (add `--force` to bypass the idempotency marker)

Copilot uses AI. Check for mistakes.

---

### search

**File**: `lib/search.py`
Expand Down Expand Up @@ -633,6 +740,7 @@ def test_memory_operations(test_resolver):
| `paths.py` | Path resolution | `get_memory_dir()`, `get_search_paths()` |
| `config.py` | Configuration | `get_memory_root()`, `MnemonicConfig.load()` |
| `memory_reader.py` | Parsing | `get_memory_metadata()`, `get_memory_summary()` |
| `migrate_filenames.py` | File migration | `migrate_all()`, `migrate_file()`, `is_migration_complete()` |
| `search.py` | Search/ranking | `search_memories()`, `find_related_memories_scored()` |
| `relationships.py` | Linking | `add_bidirectional_relationship()`, `get_inverse()` |
| `ontology.py` | Validation | `load_ontology_data()`, `validate_memory_against_ontology()` |
Expand All @@ -649,6 +757,9 @@ from lib.config import get_memory_root
# Memory reading
from lib.memory_reader import get_memory_metadata, get_memory_summary

# Migration
from lib.migrate_filenames import migrate_all, is_migration_complete

# Search
from lib.search import search_memories, find_related_memories_scored

Expand Down