You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two complementary safety mechanisms for kbx writes:
Auto-git-commit: When kbx writes to memory files, optionally auto-commit the changes to git with a generated commit message — giving a full auditable history and git revert as the rollback mechanism.
COW atomicity: For batch operations touching multiple files, write to temp files first then atomic rename. Complete success or complete rollback — no partial writes.
kbx writes to memory files frequently — adding facts, updating entity roles, creating notes, writing open items, running corrections across 50+ files. Currently these writes are fire-and-forget:
No audit trail: Who changed what, when? git log only helps if someone remembers to commit. Automated pipelines (debriefs, sync) write silently.
No rollback: A bad kbx correct --apply across 78 files can't be undone without manually restoring from backups or git checkout (if you committed beforehand).
Partial failures: If kbx correct --apply fails on file 40 of 78, the first 39 are already modified. The operation is half-done, half-not — the worst state.
With auto-commit + COW:
Every kbx write produces a git commit → git log --author=kbx shows full history
git revert <hash> cleanly undoes any change
Batch operations are atomic — all files change together or none do
Combined: COW completes successfully → single git commit with all changes
Inspiration: OpenViking uses a COW commit pattern for session mutations — copies directory trees to temp, makes all changes there, then atomic swap. Their commit_async() guarantees that live data is never corrupted by partial writes.
Cache the git detection result for the session — don't re-check on every write.
1.3 Operations That Trigger Commits
Commits on write (memory file mutations):
Command
Commit message
kbx memory add "title"
kbx: create-note "title"
kbx memory add "fact" --entity "Name"
kbx: add-fact Name
kbx memory edit-fact <id> --text "..."
kbx: edit-fact Name
kbx memory delete-fact <id>
kbx: delete-fact Name
kbx person create "Name"
kbx: create-person Name
kbx person edit "Name" --role "..."
kbx: edit-role Name
kbx project create "Name"
kbx: create-project Name
kbx project edit "Name" --meta "..."
kbx: edit-project Name
kbx note edit "title" --body "..."
kbx: edit-note "title"
kbx note delete "title"
kbx: delete-note "title"
kbx correct "old" "new" --apply
kbx: correct "old" → "new" (N files)
kbx glossary add "TERM" "expansion"
kbx: add-glossary TERM
kbx glossary edit "TERM" "expansion"
kbx: edit-glossary TERM
kbx glossary delete "TERM"
kbx: delete-glossary TERM
No commit (read-only or DB-only):
Command
Why no commit
kbx search, kbx view, kbx list
Read-only
kbx context, kbx entity stale
Read-only
kbx index run
Writes to SQLite/LanceDB, not memory files
kbx sync
Sync writes memory files — but these should be committed as a batch: kbx: sync granola (N meetings)
kbx pin, kbx unpin
DB-only (pin state in SQLite)
Special case — kbx sync: Sync can create/update many meeting files. This should produce a single commit: kbx: sync granola --since 2026-03-01 (12 meetings).
1.4 Batch Operations → Single Commit
Operations that touch multiple files must produce one commit, not one per file:
Important: Use git add <specific files>, never git add -A. Only commit files that kbx actually changed.
1.5 Error Handling
Git commit failures should warn but not block the kbx operation:
try:
transaction.commit()
exceptsubprocess.CalledProcessErrorase:
logger.warning(f"Auto-commit failed: {e}. Changes were written but not committed.")
# Don't raise — the kbx operation itself succeeded
Scenarios:
Git not installed → auto-commit silently disabled (logged at debug level)
Dirty working tree conflicts → warn, changes are written but uncommitted
Lock file contention → warn, retry once, then skip
Pre-commit hook failure → warn with hook output, changes are written but uncommitted
1.6 --no-commit Flag
Skip auto-commit for a single operation:
# Write without committing (even if auto_commit = true)
kbx memory add "quick scratch note" --no-commit
# Useful for: scripted batch operations where you want manual commit controlfornamein names:
kbx memory add "fact" --entity "$name" --no-commit
git commit -am "batch: add facts for all team members"
Part 2 — COW Atomicity for Batch Operations
2.1 Single-File Writes
For operations that modify one file, use temp file → atomic rename:
importtempfileimportosdefatomic_write(target_path: str, content: str):
"""Write to temp file, then atomic rename."""dir_name=os.path.dirname(target_path)
fd, tmp_path=tempfile.mkstemp(dir=dir_name, suffix=".tmp")
try:
withos.fdopen(fd, "w") asf:
f.write(content)
os.replace(tmp_path, target_path) # atomic on POSIXexceptException:
os.unlink(tmp_path) # clean up on failureraise
os.replace() is atomic on POSIX filesystems — the target file is either the old content or the new content, never a partial write.
2.2 Batch Operations (Multi-File)
For operations touching multiple files (kbx correct --apply, bulk entity updates, sync), use a staged write buffer with commit-or-rollback semantics:
classBatchWriter:
""" Collects writes in temp files. On commit(), atomically replaces all targets. On rollback() or exception, cleans up temp files — originals untouched. """def__init__(self, memory_dir: str):
self.memory_dir=memory_dirself.pending: list[tuple[str, str]] = [] # (target_path, tmp_path)defwrite(self, target_path: str, content: str):
"""Stage a write — content goes to temp file, not target."""dir_name=os.path.dirname(target_path)
fd, tmp_path=tempfile.mkstemp(dir=dir_name, suffix=".kbx-cow")
withos.fdopen(fd, "w") asf:
f.write(content)
self.pending.append((target_path, tmp_path))
defcommit(self):
"""Atomically replace all targets with their temp files."""# Phase 1: Verify all temp files exist and are validfortarget, tmpinself.pending:
ifnotos.path.exists(tmp):
raiseRuntimeError(f"Temp file missing: {tmp}")
# Phase 2: Atomic replace (each os.replace is atomic individually)committed= []
try:
fortarget, tmpinself.pending:
os.replace(tmp, target)
committed.append(target)
exceptException:
# Partial commit — log which files were already replacedlogger.error(
f"Batch commit failed after {len(committed)}/{len(self.pending)} files. "f"Committed: {committed}"
)
self._cleanup_remaining()
raiseself.pending.clear()
defrollback(self):
"""Discard all staged writes — originals untouched."""self._cleanup_remaining()
self.pending.clear()
def_cleanup_remaining(self):
for_, tmpinself.pending:
try:
os.unlink(tmp)
exceptFileNotFoundError:
passdef__enter__(self):
returnselfdef__exit__(self, exc_type, exc_val, exc_tb):
ifexc_typeisnotNone:
self.rollback()
# If commit() wasn't called, rollback on exitifself.pending:
self.rollback()
Usage in kbx correct --apply:
withBatchWriter(memory_dir) asbatch:
forfile_path, new_contentincorrections:
batch.write(file_path, new_content)
# All writes staged as temp files — originals untouchedbatch.commit()
# All files atomically replaced# If any exception occurs before commit(), rollback() runs automatically
Entity relations (feat: explicit typed entity relations for graph-style queries #70): Relation writes go to SQLite (not memory files), so they're already atomic via SQLite transactions. No COW needed, but auto-commit could optionally record relation changes if they modify entity files.
Debrief pipeline: Automated debriefs that write open items to entity files should use BatchWriter to stage all entity updates, then commit once.
Implementation Phases
Phase 1 — atomic_write for single files: Replace direct file writes with temp → rename pattern across all kbx write paths. ~1 day
Phase 2 — BatchWriter for multi-file ops: Implement BatchWriter, migrate kbx correct --apply. ~1-2 days
Phase 3 — Auto-git-commit: Config, git detection, WriteTransaction, commit message formatting. Wire into all write operations. ~2 days
Should auto-commit be on by default once implemented, or off by default with explicit opt-in? Off-by-default is safer for existing users.
Should the git author be configurable, or always use a fixed identity like kbx <kbx@localhost>? Fixed identity makes git log --author=kbx filtering trivial.
Should there be a kbx writes log command that shows recent auto-committed changes (wrapper around git log --author=kbx)?
For the COW partial-commit failure case (file 40 of 78 fails on os.replace), should the system attempt to restore the already-replaced files from git? This adds complexity but improves the atomicity guarantee.
Should kbx sync auto-commit be a separate config option? Sync can produce large commits (12+ meeting files) that may be better handled differently from single-fact writes.
Summary
Two complementary safety mechanisms for kbx writes:
git revertas the rollback mechanism.Motivation
kbx writes to memory files frequently — adding facts, updating entity roles, creating notes, writing open items, running corrections across 50+ files. Currently these writes are fire-and-forget:
git logonly helps if someone remembers to commit. Automated pipelines (debriefs, sync) write silently.kbx correct --applyacross 78 files can't be undone without manually restoring from backups orgit checkout(if you committed beforehand).kbx correct --applyfails on file 40 of 78, the first 39 are already modified. The operation is half-done, half-not — the worst state.With auto-commit + COW:
git log --author=kbxshows full historygit revert <hash>cleanly undoes any changeInspiration: OpenViking uses a COW commit pattern for session mutations — copies directory trees to temp, makes all changes there, then atomic swap. Their
commit_async()guarantees that live data is never corrupted by partial writes.Design
Part 1 — Auto-Git-Commit on Write
1.1 Configuration
Placeholders for
auto_commit_message_format:{operation}add-fact,edit-role,create-note,correct,add-open-item{target}{command}memory add "title" --entity "Name"){file_count}{timestamp}Default format examples:
kbx: add-fact Person Akbx: edit-role Person Akbx: create-note "Active Strategic Initiatives"kbx: correct "OldTerm" → "NewTerm" (47 files)kbx: add-open-item Person B (from: Team Standup)1.2 Git Detection
Auto-commit only activates if:
auto_commit = truein configgit -C <memory_dir> rev-parse --git-dirsucceeds)--no-commitflag is not setCache the git detection result for the session — don't re-check on every write.
1.3 Operations That Trigger Commits
Commits on write (memory file mutations):
kbx memory add "title"kbx: create-note "title"kbx memory add "fact" --entity "Name"kbx: add-fact Namekbx memory edit-fact <id> --text "..."kbx: edit-fact Namekbx memory delete-fact <id>kbx: delete-fact Namekbx person create "Name"kbx: create-person Namekbx person edit "Name" --role "..."kbx: edit-role Namekbx project create "Name"kbx: create-project Namekbx project edit "Name" --meta "..."kbx: edit-project Namekbx note edit "title" --body "..."kbx: edit-note "title"kbx note delete "title"kbx: delete-note "title"kbx correct "old" "new" --applykbx: correct "old" → "new" (N files)kbx glossary add "TERM" "expansion"kbx: add-glossary TERMkbx glossary edit "TERM" "expansion"kbx: edit-glossary TERMkbx glossary delete "TERM"kbx: delete-glossary TERMNo commit (read-only or DB-only):
kbx search,kbx view,kbx listkbx context,kbx entity stalekbx index runkbx synckbx: sync granola (N meetings)kbx pin,kbx unpinSpecial case —
kbx sync: Sync can create/update many meeting files. This should produce a single commit:kbx: sync granola --since 2026-03-01 (12 meetings).1.4 Batch Operations → Single Commit
Operations that touch multiple files must produce one commit, not one per file:
Important: Use
git add <specific files>, nevergit add -A. Only commit files that kbx actually changed.1.5 Error Handling
Git commit failures should warn but not block the kbx operation:
Scenarios:
1.6
--no-commitFlagSkip auto-commit for a single operation:
Part 2 — COW Atomicity for Batch Operations
2.1 Single-File Writes
For operations that modify one file, use temp file → atomic rename:
os.replace()is atomic on POSIX filesystems — the target file is either the old content or the new content, never a partial write.2.2 Batch Operations (Multi-File)
For operations touching multiple files (
kbx correct --apply, bulk entity updates, sync), use a staged write buffer with commit-or-rollback semantics:Usage in
kbx correct --apply:2.3 Combined: COW + Auto-Commit
The two mechanisms compose naturally:
Failure modes:
2.4 Existing Operations to Migrate
kbx correct --applykbx synckbx person editkbx memory add --entitykbx note editkbx glossary add/editIntegration with Other Features
kbx memory similar(feat: kbx memory similar — semantic similarity lookup for dedup #71): Similarity check happens before the write enters the COW buffer. The flow is: check similar → decide to write → stage in COW → commit → auto-commit.Implementation Phases
~1 daykbx correct --apply.~1-2 days~2 days--no-commitflag,auto_commit_message_formatconfig, sync batch commits.~1 dayOpen Questions
kbx <kbx@localhost>? Fixed identity makesgit log --author=kbxfiltering trivial.kbx writes logcommand that shows recent auto-committed changes (wrapper aroundgit log --author=kbx)?os.replace), should the system attempt to restore the already-replaced files from git? This adds complexity but improves the atomicity guarantee.kbx syncauto-commit be a separate config option? Sync can produce large commits (12+ meeting files) that may be better handled differently from single-fact writes.