Files

9.0 KiB

LeafWiki migration

Objective

Migrate the owner's existing LeafWiki knowledge into ItWorx Knowledge Hub without modifying the source and without silently losing or overwriting content.

LeafWiki versions and deployments may differ. The importer must be discovery-driven and adapter-versioned rather than assuming one undocumented fixed schema.

Safety contract

  • Source mount is read-only at the operating-system/container level.
  • SQLite files are opened with read-only/immutable semantics.
  • No source file is renamed, normalized, repaired or touched.
  • The importer never writes cache, lock, journal or temporary files beside the source.
  • A complete source fingerprint is captured before and after.
  • Import writes only to an isolated target workspace/database.
  • Every target record is attributable to an import ID and source item.
  • Re-running an accepted plan is idempotent.
  • Rollback removes only that import's creations/changes according to the recorded plan.
  • Conflicts are explicit; no silent overwrite.

Supported source discovery

Inventory recursively:

  • Markdown files;
  • SQLite databases, detected by header rather than extension only;
  • images and other assets;
  • JSON/YAML/config manifests;
  • directory hierarchy;
  • symlinks, which are recorded but not followed outside the source root;
  • unknown files.

For SQLite, inspect read-only:

  • SQLite version and pragmas safe to read;
  • tables/views/indexes;
  • columns and foreign keys;
  • row counts;
  • candidate content, tree, tag, revision and attachment tables;
  • application version markers where present.

The bundled tools/leafwiki-inspector provides a neutral first inventory. It is not the final importer.

Adapter architecture

IImportSource
  Discover()
  Fingerprint()
  SelectAdapter()

ILeafWikiAdapter
  Analyze()
  BuildPlan()
  ReadItem()

LeafWikiFilesystemMarkdownAdapter
LeafWikiSqliteAdapter_<known-version>
LeafWikiHybridAdapter_<known-version>

A known adapter must validate its expected signature before reading semantic columns. An unknown signature produces a preflight blocker, not guessed mappings.

A filesystem-first adapter can import Markdown hierarchy even when DB enrichment is unknown, but the preflight must clearly state which tags/revisions/metadata would be omitted and require explicit acceptance.

Implemented adapters

Adapter name Applies when Recovers
synthetic-sqlite a SQLite file with the recognized synthetic schema pages and hierarchy from the database
leafwiki-filesystem-v5 schema.json declares version 5 (or, absent that file, Markdown carries leafwiki_id frontmatter) titles from leafwiki_title (YAML-escape decoded), directory hierarchy, the author's sibling order from each directory's .order.json, and assets
filesystem-markdown any source containing Markdown hierarchy from directory structure only; titles from title: frontmatter, first heading, or filename

The catalog tries them in that order and takes the first that reports it applies, so a version-validated adapter is always preferred over the signature-free fallback, and an unrecognized LeafWiki version falls back rather than being guessed at (docs/adr/ADR-032-leafwiki-v5-import-fidelity.md).

Target-side transformations

An import writes Markdown that must work in this application, so two changes are made to the imported copy — never to the source:

  • an asset link whose bytes were genuinely imported (](/assets/<page-id>/<file>)) becomes ](/api/v1/attachments/<reference-id>/content); an asset that was rejected or unreadable keeps its original link and is reported as a diagnostic;
  • a leading level-1 heading is dropped when it exactly repeats the page title, because this application renders the title above the body and would otherwise show it twice. A heading that differs from the title is content and is kept.

Source fingerprint

A deterministic manifest includes:

  • normalized relative path;
  • item type;
  • byte length;
  • SHA-256;
  • symlink target descriptor without traversal;
  • for SQLite: schema fingerprint and selected row-count summary.

Sort entries ordinally and hash the manifest. Do not include source absolute paths in user-facing logs unless administrator-only and redacted.

Preflight plan

Preflight produces:

  • adapter and version;
  • source fingerprint;
  • discovered counts;
  • proposed target hierarchy;
  • title/slug mapping;
  • Markdown encoding/line-ending diagnostics;
  • link-resolution plan;
  • attachment mapping and hashes;
  • tags/revision metadata availability;
  • duplicate/conflict classifications;
  • unsupported/ambiguous items;
  • estimated target changes;
  • deterministic plan hash.

No target mutation occurs during preflight.

Content mapping

Pages and hierarchy

Preference order:

  1. authoritative LeafWiki tree metadata from a validated adapter;
  2. Markdown directory hierarchy;
  3. explicit operator mapping.

Title sources:

  1. validated metadata;
  2. frontmatter title;
  3. first H1;
  4. filename.

Keep original relative path and legacy ID as provenance.

Markdown

  • Preserve original Markdown bytes as an import artifact/hash.
  • Decode according to detected/declared encoding; report replacements.
  • Convert only syntax with a deterministic documented rule.
  • Preserve unknown constructs as fenced/raw text rather than discard them.
  • Convert internal links after all page identities are mapped.
  • Record unresolved/ambiguous links.

Tags

Import normalized tags when the adapter can prove their relation. Preserve original display value.

Revisions

When reliable legacy revisions exist:

  • import them in chronological order;
  • preserve source timestamp/author label as provenance;
  • mark source attribution;
  • never assign legacy author labels to current user identities without explicit mapping.

If revisions cannot be safely reconstructed, import current content only and report it.

Attachments

  • Hash and validate each asset.
  • Map references after page identities exist.
  • Reject path traversal.
  • Preserve safe original filename and source path provenance.
  • Deduplicate through normal attachment storage.
  • Report missing references and unreferenced assets.

Conflict policy

Classifications:

  • no target conflict;
  • exact duplicate by source provenance/hash;
  • same title/path, different content;
  • same attachment hash;
  • ambiguous link;
  • unsupported source item.

Default actions:

  • exact rerun duplicate: skip idempotently;
  • same hash attachment: reuse blob, create intended logical reference;
  • title/path conflict: create under an explicit import namespace or require plan choice;
  • existing user page: never overwrite;
  • ambiguous link: leave unresolved and report.

Execution

  • Persist the accepted immutable plan.
  • Use bounded transactions/checkpoints to avoid one giant lock.
  • Ensure each item is idempotent.
  • Create provenance with the target row.
  • Defer cross-links until page mapping exists.
  • Allow cancellation between batches.
  • Resume only when source and plan fingerprints still match.
  • On failure, keep a diagnosable job state; do not mark complete.

Validation

At minimum compare:

  • discovered vs planned vs imported/skipped/failed counts;
  • hierarchy/root/subtree mapping;
  • current page content hashes after deterministic conversion;
  • link resolved/unresolved counts;
  • tags;
  • attachment bytes/hashes and references;
  • revision counts when imported;
  • target searchability;
  • representative rendered pages;
  • source fingerprint before/after.

A validation mismatch blocks completion.

Rollback

Rollback uses import provenance and the accepted plan:

  • remove imported relations;
  • remove imported current entities when they were created by the job;
  • preserve any subsequent user-edited target by moving it to a review/conflict state rather than deleting it;
  • release attachment references and clean blobs only through normal retention;
  • append audit records;
  • never touch pre-existing target content;
  • never touch source.

Test full rollback and partial-failure rollback.

Real migration procedure

  1. Update and back up the existing LeafWiki.
  2. Create an immutable/read-only snapshot.
  3. Keep LeafWiki running normally during dry runs.
  4. Run inspector and preflight.
  5. Resolve all blockers.
  6. Import into a non-production target.
  7. Validate representative school pages, links, files and search with the user.
  8. Rehearse rollback.
  9. Schedule cutover.
  10. Place LeafWiki read-only.
  11. Capture a final snapshot/fingerprint.
  12. Run final import or delta strategy supported by the adapter.
  13. Validate and take a Knowledge Hub backup.
  14. Keep LeafWiki read-only through the rollback window.
  15. Retire only after explicit user sign-off.

Synthetic fixtures

Tests must cover:

  • filesystem-only Markdown tree;
  • duplicate titles;
  • nested pages;
  • UTF-8 and special characters;
  • wiki links and unresolved links;
  • attachments and missing attachments;
  • SQLite database with known synthetic schema;
  • unknown SQLite schema;
  • symlink escape attempt;
  • corrupted database/file;
  • rerun idempotency;
  • rollback after user edit;
  • source hash immutability.