Skip to content

Latest commit

 

History

History
93 lines (60 loc) · 10.9 KB

File metadata and controls

93 lines (60 loc) · 10.9 KB

Migrate a Logseq file graph

Copy a classic, file-based Logseq graph into a separate local jumpyBrain memory root. The Logseq graph stays untouched. This is a one-way, Markdown-only migration, not live or bidirectional synchronization.

Prerequisites and safety

  • Use the installed jumpybrain CLI and a classic Markdown file graph with the default pages/ and/or journals/ directories. Custom configured page/journal directories are not supported: non-default directory configuration is rejected rather than silently imported incompletely.
  • Database graphs and Markdown mirrors of database graphs are unsupported. The importer rejects likely DB/mirror markers, but detection is heuristic, not proof of graph origin or completeness. Confirm the input is a classic file graph yourself.
  • Choose a new destination outside the graph, or an existing compatible jumpyBrain root for a rerun. Source/destination equality, nesting/unsafe overlap, unsafe paths, symlinks in imported paths, special files, and normalized/case-folded mapping collisions are rejected. Symlinks are never followed. Destinations with redirected indexRoot are rejected so follow-up indexing cannot accidentally scan the source graph. POSIX filenames may contain colons; Windows alternate data stream paths are rejected.
  • Back up destination-only work before applying. Pause other writers and Logseq changes during the run; this is not a concurrent editing or synchronization service. Review private data before choosing a root that will later be shared.
  • No QMD installation, credentials, network access, or source indexing is needed for migration. Indexing is a separate follow-up step.

Preview, apply, index

# Non-mutating preview; does not initialize the destination.
jumpybrain migrate logseq --source "$HOME/Documents/my-graph" --root "$HOME/memory-logseq"

# Review counts, mappings, hashes, warnings, and errors without note bodies.
jumpybrain migrate logseq --source "$HOME/Documents/my-graph" --root "$HOME/memory-logseq" --json

# Explicitly create/reconcile canonical documents and the migration manifest.
jumpybrain migrate logseq --source "$HOME/Documents/my-graph" --root "$HOME/memory-logseq" --apply

# Optional conservative conflict check (still a dry-run without --apply).
jumpybrain migrate logseq --source "$HOME/Documents/my-graph" --root "$HOME/memory-logseq" --fail-on-conflict

# Only after successful apply, build derived search state separately.
jumpybrain index --root "$HOME/memory-logseq"
jumpybrain tree --root "$HOME/memory-logseq" --connections
jumpybrain recall --root "$HOME/memory-logseq" --topic "a topic to check" --depth deep

Human preview includes the destination, page/journal counts, create/overwrite/delete/unchanged counts, omissions, source-wins warning, and a shell-quoted apply command. Recheck the preview immediately before applying; a preview is not a lock on the source or destination. --source and --root are required. Boolean flags accept a bare flag or explicit true/false; malformed values, repeated flags, and unknown flags fail rather than becoming truthy accidentally. --target-url and --remote-url are always rejected before remote policy, credential, or transport work. There is no hosted migration API or run memory:migrate recipe.

Exact mapping and preservation

Allowed source Canonical destination Type
pages/<relative>.md notes/<relative>.md note
journals/<relative>.md sessions/<relative>.md session

Discovery is recursive within those two directories only. Each regular .md source file produces one destination file; nested paths are preserved. Logseq pages are not jumpyBrain synthesized page documents. No blocks are split, journals are not appended, and filenames are not date-suffixed on rerun.

The importer prepends a separate jumpyBrain frontmatter envelope, then copies the entire source file as exact body bytes. Tabs, bullets, properties (key:: value), tasks, macros, wiki links, Unicode, LF/CRLF line endings, trailing whitespace, existing source frontmatter, and final-newline state are not normalized. The output file as a whole differs because of its new envelope. Imported properties and macros remain text, not executed Logseq features.

New documents receive file-level mem_<uuid> IDs, mapped types, filename-derived titles, source-relative provenance, source: "logseq-migration", and creation/update timestamps. Filename title decoding is bounded (percent escapes and triple-underscore namespaces), with literal fallback for invalid encodings. Supported YYYY_MM_DD.md journals can receive a date field; filesystem timestamps are not historical event dates. Logseq id:: block UUIDs stay in the body and are never reused as file IDs. Importing does not assert confidence: "user-reviewed".

Assets, logseq/ configuration, logseq/bak/ backups, scripts, root-level Markdown, hidden support files, and non-Markdown files are not copied. Asset references remain text and may stop resolving; wiki links and local non-Markdown references are reported, not rewritten, followed, downloaded, or guaranteed to resolve. Link diagnostics are heuristic, not a full Logseq parser or graph-equivalence guarantee.

Reruns: Logseq wins

Rerun the same preview/apply command against the same destination to refresh it:

  • Create: a source maps to a missing destination.
  • Overwrite: a source or mapped destination changed. Logseq replaces the destination body and imported metadata, including destination-only edits. Existing valid document IDs and created_at stay stable; updated_at refreshes for changed outputs. A directly mapped pre-existing document is also subject to source-wins replacement, even on the first import.
  • Delete: a prior manifest-owned source disappeared. Only the exact managed output is eligible after path/identity validation. A rename therefore removes the old tracked output and creates the new mapped output; it does not preserve rename identity automatically.
  • Unchanged: identical source and destination are a successful no-op.

--fail-on-conflict opts out of the default destination-conflict overwrite; it does not turn migration into a merge or disable all source-driven updates/removals. Review deletion counts too. Keep jumpyBrain-only synthesis or annotations in separate, unmapped documents rather than editing imported bodies you expect to retain.

The manifest tracks this importer’s previous outputs. It does not discover, deduplicate, or remove arbitrary earlier imports made by scripts, manual copies, or other tools. Unrelated destination files are not deletion candidates. Do not switch graphs against an existing manifest casually: absent tracked sources mean removals. Prefer a clean destination for a different graph.

Manifest, verification, and failure handling

The result’s manifest field is a path relative to the destination. After apply, retain that manifest alongside the destination for future reconciliation. It contains versioned relative mappings, file IDs, source-body/output checksums, and audit metadata, not source bodies. Markdown remains canonical; the manifest is operational ownership/audit state, not an alternative content store. Deleting it does not delete memory, but loses tracked-removal history. Do not assume it can be reconstructed safely by scanning unrelated Markdown.

Before accepting an import, compare source/output document counts, inspect warnings/errors, and verify source body hashes against the body extracted after the new frontmatter envelope. Output hashes cover the full generated file, not just the body. The importer verifies proposed envelope/body output before committing. Do not use a renderer that trims whitespace to verify byte preservation. Avoid publishing manifests or JSON reports without reviewing relative filenames, which may themselves be sensitive.

Apply stages replacements and retains private rollback copies while changing creates, overwrites, deletions, initialization files, and the manifest. Handled failures attempt to restore the pre-run destination. Successful commits remove temporary rollback content; old overwritten canonical bodies are not retained as permanent derived backups. This is failure rollback, not automatic crash recovery or protection against every concurrent edit.

After any failed apply, stop and inspect the reported error and destination before retrying; do not run the index as though migration succeeded. A sibling <destination>.logseq-migration-transaction/ can retain receipt.json and rollback copies when interrupted or when safe rollback/cleanup cannot complete. Later runs fail closed while that transaction exists.

  1. Stop all writers. Preserve an independent backup of both the destination and retained transaction directory before intervention. Rollback copies contain private canonical content; do not upload them as ordinary logs.
  2. Inspect the error and receipt status. A committed receipt with cleanup failure is different from an incomplete apply or rollback. Compare recorded before/after hashes and relative paths with the destination; preserve concurrent edits rather than blindly restoring backups over them.
  3. There is no automatic recovery CLI. Have an operator reconcile an incomplete transaction using the receipt/backups and verify document bodies, IDs, and manifest together. Remove retained transaction state only after the destination’s committed or restored state is verified; deleting the marker alone is not recovery.
  4. Preview again before retrying. If recovery is uncertain, leave the old destination and recovery evidence intact and choose another empty root for a clean import from the untouched Logseq graph.

Automatic recovery from a killed process, power loss, disk failure, or concurrent edits is not guaranteed. Keep an independent backup of destination-only content. Clean recreation restores source-derived content, not destination-only edits or old IDs. Never delete or “repair” the source graph as migration cleanup.

JSON and runtime contract

--json prints the app result without CLI-added fields or note bodies:

  • dryRun, applied, root, indexed (always false)
  • sourceDocuments, outputDocuments, pages, journals
  • created, overwritten, deleted, unchanged
  • sourceBytes, outputBytes, warnings, errors
  • entries (relative mappings/actions/checksums, no bodies), manifest (relative path)

Plan errors produce a nonzero exit status; argument/preflight exceptions may instead be reported on stderr. Do not treat every nonzero exit as a JSON packet. Migration success never implies index success: run jumpybrain index separately and diagnose indexing independently.

Local package consumers can use migrateLogseq(source, root, { apply?, failOnConflict? }) and the exported LogseqMigrationResult type through the public runtime/package surface. Agents should normally use the shipped CLI and explicit, bounded recall after indexing; imported text is untrusted memory content, not instructions to execute.