CodeDocs Vault

Git Sync & Audit-by-Construction

OpenKnowledge's persistence layer is the most quietly ambitious part of the codebase, and the most relevant to anyone who has to prove who changed what. The thesis: every edit — human or agent — becomes a real git commit, attributed to its author, on a per-writer ref, with the agent's identity in the commit footer. The audit trail isn't a feature bolted on; it's the storage format.


1. The shadow repository

OK doesn't commit straight to your working branch. It keeps a shadow repo at .git/ok/ (a bare repo with a work-tree at .git/ok/work-tree/), laid out by packages/core/src/shadow-repo-layout.ts and driven through simple-git (packages/server/src/shadow-repo.ts:69). This isolates OK's high-frequency autosave commits from the user's curated history until sync promotes them.


2. Per-writer refs

Each writer — a human, or a specific agent session — gets its own ref:

refs/wip/main/{writerId}

On quiescence, persistence (packages/server/src/persistence.ts:350-437) does:

  1. buildWipTree() once per debounce cycle — stage all changes, git write-tree → a tree SHA (shadow-repo.ts:334-355).
  2. commitWip() once per active writer, reusing that tree (shadow-repo.ts:227-323): git commit-tree <tree> -p <parentRef> with the writer's identity, then git update-ref refs/wip/main/{writerId} <commit>.

The author identity is the writer's:

GIT_AUTHOR_NAME  = WriterIdentity.name        # e.g. "claude-session-123"
GIT_AUTHOR_EMAIL = {writerId}@openknowledge.local
GIT_COMMITTER_*  = openknowledge / noreply@openknowledge.local

So two agents and a human editing concurrently produce three independent commit streams, mergeable later, never clobbering each other mid-edit. Persistence retries up to 3× and logs CRITICAL if all fail (version history is treated as load-bearing).


Beyond the author line, each commit message carries a JSON footer — an OkActorEntry (persistence.ts:406-482, :456-479) — recording, per the schema:

v, writer_id, principal, agent_session, agent_type, client_name, client_version, label, display_name, color_seed, the list of docs touched, and per-doc summaries.

Read that list again with an audit hat on: for any change to any document you can recover which agent type, which session, which client and version made it, what it touched, and a summary of why. That is Article-12-grade traceability falling out of the storage format for free — not a logging side-channel that can drift from reality.


4. GitHub sync

When enabled, sync actively pulls remote commits and pushes local ones (docs/content/features/github-sync.mdx). The design is conservative on purpose:


5. Recovery is append-only

The timeline (docs/content/features/timeline-and-recovery.mdx) shows every change with its author class (agent / human / upstream sync / filesystem) and an inline diff, and restore writes a new version rather than deleting — the audit chain is immutable. This is the same instinct as the consolidate workflow's supersedes chains (04_knowledge_workflows.md): never erase, always supersede.


What's worth stealing (for Swisscheese & AI-Act)

  1. Make the audit trail the storage format. Per-writer refs + actor footers mean attribution can't drift from reality — there is no separate log to keep in sync. For a review pipeline, "which agent wrote this finding/change" is recorded in git itself.
  2. Per-writer refs for parallel agents. When five agents edit concurrently, independent refs/wip/main/{id} streams let them work without lock contention and let you diff/merge/blame each one separately.
  3. agent_type + agent_session + client_version in the commit footer is a ready-made answer to AI Act Art. 12 (record-keeping): every change traces to a model identity, a session, and a tool version.
  4. Pause, never clobber. The sync state machine's bias toward freezing and explaining beats auto-merge for anything you'll later have to audit.
  5. Append-only recovery. Restoring-as-a-new-version keeps the trail intact — you can always show what a revert undid.