Skip to content

Durability: journaled plan->apply->recover transactions for multi-note operations #194

Description

@CryptoJones

What claude-obsidian does better

AgriciDaniel/claude-obsidian
spends its single largest module (claude_obsidian/transaction.py, 4,680 lines)
on one contract, stated honestly in its own docstring:

Multi-file filesystem updates cannot be truly atomic on common filesystems.
This module therefore provides a stronger, honest contract: one process-held
mutation lock, precondition hashes, a durable journal, atomic per-file
replace, and deterministic rollback/recovery for the complete operation.

The operator-visible shape is: read every target and record its expected
SHA-256 → merge the whole change into one bundle → transaction inspect
transaction apply → and, after an interrupted run,
transaction recover [--force-stale-lock]. A changed target is a conflict,
never a silent overwrite.

The gap in omind

omind has the first half and not the second. OmiStore gives us same-dir temp

  • os.replace, an advisory flock write lock, and version preconditions — all
    good. What is missing is the journal and the rollback, so a multi-note
    operation interrupted midway leaves partial state with no recovery path.

store.create_and_disable_sources (the omind consolidate --apply write path)
concedes exactly this in its own docstring:

All versions and the target's nonexistence are checked before the first
write [...] A process crash can still leave extra recoverable copies,
never a hard-deleted source.

That is a fair trade — it fails toward keeping data — but "extra recoverable
copies" still means a human has to notice and reconcile by hand. The same shape
applies to mesh merges and any future migration that rewrites many notes.

Proposed scope

Generalize what omind consolidate already prototypes (it has machine-local
JSON plans and an explicit --apply PLAN_ID that revalidates both source
versions) into a store-level primitive:

  • A Transaction that collects (path, expected_version, new_content) writes.
  • A durable journal in the state dir recording every target's prior bytes/hash
    before the first os.replace.
  • Commit under the existing single write_lock(), atomic per file.
  • omind recover — replay the journal backwards if an apply did not reach its
    commit record; no-op when the journal is clean.

Callers to migrate: create_and_disable_sources, the mesh merge driver, and
omind migrate.

Deliberately not copying

claude-obsidian additionally requires the operator to echo an
approved_plan_sha256 back to --apply. That is right for a product whose
mutations are driven by an LLM against a research vault; for omind it would put
a hash-copying step in front of routine memory writes. Keep plan/apply for
consolidate, keep the journal everywhere, skip the hash handshake.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions