Threadnote 4 owns ~/.threadnote. The legacy 3.x home is input to a one-time, non-destructive migration and is not a
runtime dependency.
threadnote migrate
threadnote migrate --applyPost-update prompts are evidence-driven. A fresh current install, an empty or runtime-only ~/.openviking, and a home
with completed matching receipts produce no migration prompt or manual follow-up command. Threadnote rechecks the
evidence under the post-update lock before announcing or running an action.
The eligibility probe is bounded and does not enumerate an arbitrary legacy-home fan-out. It recognizes the default
local account, the account and user currently selected by THREADNOTE_ACCOUNT / THREADNOTE_USER, canonical
resource and memory roots, seed state, managed-share metadata, model candidates, and resumable receipts. If a legacy
installation used a non-default account that is no longer selected, run the migration with that identity once, for
example THREADNOTE_ACCOUNT=<old-account> THREADNOTE_USER=<old-user> threadnote migrate --apply.
Upgrades from earlier 4.0 betas do not depend on ~/.openviking still existing. The same migration command resumes a
pending receipt, flattens the short-lived ~/.threadnote/data/viking/<account> layout, and adopts a verified model from
the earlier ~/.threadnote/threadnote/models/ layout. Completed receipts make each step idempotent. Lexical, vector,
and code-graph SQLite stores are derived data: repair validates their schema, and incompatible or stale indexes rebuild
from canonical files or current repository source instead of treating old database bytes as canonical migration input.
Canonical account moves use the same per-account mutation lock as normal agent writes. Threadnote moves material files
individually, keeps OS/runtime metadata out of canonical data, and never recursively deletes the old beta tree. Empty
directories and ignored metadata may therefore remain under data/viking as a non-authoritative scaffold; eligibility
ignores it, while any later material write there is preserved and reported as a conflict instead of being deleted.
The migration:
- inventories canonical resources, memories, seed state, share metadata, and applicable settings;
- rejects absolute symlinks and relative symlinks or paths that escape the source;
- excludes server, interpreter-environment, PID, socket, cache, and transient files;
- checks target free space for source bytes plus a bounded safety margin;
- copies into a staging sibling of the final home, or performs a no-overwrite resumable recovery when beta.1 already
created
~/.threadnote; - records source size, modification time, and SHA-256;
- verifies every staged hash and detects source changes during the copy;
- isolates legacy Git share worktrees under
share/worktrees/, rewrites their Git pointers, and retains a separate canonical Markdown copy; - records and verifies the transformed staging-tree hash;
- writes a checksummed migration receipt;
- atomically promotes the staging directory;
- flattens canonical content to
~/.threadnote/data/<account>without recursively deleting the old beta scaffold; - adopts any verified 3.x managed GGUF generation model into the role-aware model store and preserves its selection;
- installs and selects the core BGE embedding model if no valid embedding selection already exists;
- rebuilds the derived lexical SQLite and vector indexes from the migrated canonical content.
Interrupted staging and beta-home recovery are resumable. Re-running after success is idempotent. If an earlier beta
already wrote or subsequently updated a resource at the same logical canonical data/ path, that current Threadnote
copy wins while the older copy remains available in the untouched legacy home. The recovery receipt records how many
current entries were preserved. An existing managed-share checkout is one atomic authority boundary: when its owned
worktree points at its owned Git directory, recovery preserves the entire current repository rather than overlaying
individual legacy Git files. This covers the index and split indexes, refs and packed refs, objects and packs, reflogs,
configuration, hooks, in-progress operation state, linked-worktree administration, and future Git repository entries
without relying on a filename allowlist. The complete legacy repository remains byte-for-byte available under
~/.openviking. A partial gitdir/worktree pair left by an interrupted earlier beta is recoverable only when every
existing entry is a byte-for-byte subset of that preserved legacy repository (allowing only Threadnote's deterministic
Git-pointer and worktree-path rewrites). Known transient Git operation files and locks do not establish authority and are
removed from a legacy-derived partial copy. The owner-executable bit must also match the deterministic legacy copy.
Recovery holds the same cross-process share lock used by agents, stages and verifies missing files outside the repository,
and atomically installs them before rechecking the legacy source. Any other current-only or different file, unsafe entry
type, or unrelated Git pointer still stops recovery before copying.
When neither half exists, the eligible legacy checkout is migrated using the normal transient-file exclusions. Disjoint
account trees are merged, identical overlaps are verified, and different non-canonical settings or metadata outside
managed repositories still stop the migration instead of being overwritten. The source is never automatically removed.
The standalone installer separately retires verified executable dependencies after activating the new release. It can
stop and uninstall a detected npm-distributed Threadnote package, including an early Node-based 4.0 beta, remove a
Threadnote-owned OpenViking uv/pipx/user-local pip tool, and unload the io.threadnote.openviking LaunchAgent. This
cleanup does not remove ~/.openviking, its models, canonical resources, memories, or migration receipts. A rollback
that needs the old executable runtime must reinstall Threadnote 3/OpenViking and point it at the preserved home.
After promotion:
threadnote doctor
threadnote recall "latest handoff"
threadnote models list
threadnote index statusLexical and vector recall are immediately available after install or repair completes. Both indexes are derived data
under ~/.threadnote/indexes/; canonical memories and resources remain ordinary files under
~/.threadnote/data/<account>.