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:
buildWipTree()once per debounce cycle — stage all changes,git write-tree→ a tree SHA (shadow-repo.ts:334-355).commitWip()once per active writer, reusing that tree (shadow-repo.ts:227-323):git commit-tree <tree> -p <parentRef>with the writer's identity, thengit 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).
3. The commit footer is the audit record
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:
- It commits your edits with the configured git identity and pushes them so collaborators see them.
- Conflict handling surfaces in three places at once — a
⚠tab badge, a pinned Conflicts section in the file tree, and a unified diff view — and the conflicted doc is frozen for humans and agents alike until a side is chosen. - It pauses (rather than clobbers) on: unresolved conflicts, uncommitted local changes that a pull would overwrite, unsettled external on-disk edits, a detached HEAD, diverged/force-pushed history, auth errors, missing push permission, and protected branches. Each maps to a specific status-indicator state. The recurring principle: pause and tell the user, never silently overwrite.
- Auth: reuses the GitHub CLI's credentials if present, else a device-flow OAuth
token stored in the OS keychain (
08_desktop_runtime.md).
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)
- 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.
- 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. agent_type+agent_session+client_versionin 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.- Pause, never clobber. The sync state machine's bias toward freezing and explaining beats auto-merge for anything you'll later have to audit.
- Append-only recovery. Restoring-as-a-new-version keeps the trail intact — you can always show what a revert undid.