---
title: "Memory is a default Git-backed Service with file-first Agent access"
version: "en"
---

> Documentation Index
> Fetch the complete documentation index at: https://botharness.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Memory is a default Git-backed Service with file-first Agent access

> Status: Accepted

# Memory is a default Git-backed Service with file-first Agent access

> Superseded in part by ADR-0060 and ADR-0068. ADR-0068 makes the checked-out Git working tree current Memory, including code and binary files, without an accepted-commit gate. Earlier accepted-commit, Markdown-only, and branch-admission paragraphs below remain historical rationale.

## Original decision (superseded in part by the update below)

A PersonaBot needs only its Host-owned identity, the system-defined base runtime prompt, a Bot Inbox, an Orchestrator Session, and optional Assignment Sessions to chat and work. Persona and Memory are optional: their Provider may be absent without disabling the DM → Orchestrator → Assignment path. Memory is exposed through an application-defined Cordis Service Definition whose Git-backed Provider can live in a separate package and can be consumed by BotHarness or other trusted Plugins.

## Decisions

- A Memory Repository is generic and independently durable. It has its own identity and lifecycle; attachment, detachment, archive, and deletion are distinct. V1 binds at most one private repository to a PersonaBot, but the Service Definition does not encode `botSlug` as repository identity.
- All Memory documents are ordinary Markdown. There is no generated or privileged `MEMORY.md`. Persona is optional ordinary Memory content; when supplied during creation it becomes a conventional `persona.md` document pinned by default, but authorized Agents and Humans may edit, unpin, rename, or delete it.
- Pin state is versioned Markdown frontmatter. Pinned bodies enter future system prompts within a Human-configurable, repository-level UTF-8 byte budget and the active model's final token preflight. A pin or pinned-file mutation that exceeds either limit fails with machine-readable fields and an LLM-readable remedy; content is never silently truncated.
- Every mutation goes through the Memory Service, uses optimistic concurrency, and produces one semantic Git commit with actor and cause. `memory_pin` and `memory_unpin` are explicit operations so raw file writes cannot bypass budget checks.
- Every Memory Service operation is wrapped by application-defined Cordis Events. `memory/before-operation` is a waterfall hook that may enrich, rewrite, or reject; `memory/after-operation` is emitted for success or failure, after the durable commit when one exists. Event payloads contain metadata rather than file bodies; Git history remains the durable authority.
- V1 trusts internal Plugins that can resolve the Service and requires repository id, actor, and cause on mutations; it does not add a general ACL system. Orchestrators receive read-write tools when a repository is attached. Assignment Sessions receive no Memory access by default and may receive explicit `read` or full `read-write` access; their Subagents do not inherit it automatically.
- Without a Memory Provider, Persona input and the Memory navigation destination are absent. Supplying Persona through an API without that capability fails as `memory-unavailable`; Core never creates a second pending-Persona store.

## Consequences

This supersedes ADR-0003, ADR-0004, and ADR-0014. It refines ADR-0002 from a per-Bot directory into an attachable repository and removes the generated-`MEMORY.md` consequence from ADR-0021. The first V1 tracer bullet deliberately proves a name-only PersonaBot can complete DM → Orchestrator → Assignment → report → DM reply before the optional Memory package is expanded.

## Update (2026-09-21) — default repository, file-first access, and accepted commits

The original optional-capability decision is superseded in four places: a usable PersonaBot no longer exists without Memory; PersonaBot creation now initializes a real Git Memory Repository; v1 Agents do not receive model-visible `memory_read`, `memory_write`, `memory_pin`, or `memory_unpin` Tools; and Cordis Events do not wrap every filesystem operation or watch `.git`. The `Consumer → Service Definition → Provider` seam remains, but the Git-backed Provider is a required profile capability and PersonaBot creation fails closed when it cannot initialize or reconcile the repository.

Every PersonaBot owns one Memory Repository in v1. The Orchestrator Session always uses that repository root as its `cwd`, so the Agent reads, searches, edits, and versions Memory through ordinary filesystem, Shell, `grep`, and `git` capabilities. This deliberately keeps Memory file-first: the model does not need a parallel CRUD vocabulary, and repository contents remain understandable with normal developer tools. Persona remains conventional optional content within this always-present repository; no generated or privileged `MEMORY.md` is introduced.

The application-defined Memory Service owns repository lifecycle, validation, pin-budget enforcement, reconciliation, accepted commit creation, history, and queries for Human UI and trusted Plugin Consumers. Direct working-tree changes are provisional. A raw Git commit becomes an effective **Memory Commit** only after Memory reconciliation validates repository invariants, actor/cause attribution, and the resulting pinned-context budget. Rejected or uncommitted state does not enter pinned context or accepted history projections. This makes the accepted Memory Commit, rather than a file save or filesystem notification, the durable product boundary.

Application-defined Cordis Events report reconciliation and accepted or rejected Memory Commits after the relevant durable boundary. They carry metadata and commit identity, not file bodies, and are rebuildable from Git history plus reconciliation state. The Provider never interprets `.git` filesystem events as authoritative Memory events; missed live notifications are recovered by query/reconciliation instead of a watcher-derived second truth.

## Consequences of the update

- The DM → Orchestrator path now depends on successful Memory Repository creation and reconciliation; the earlier name-only/no-Memory tracer-bullet claim is retired.
- The Memory entry is always present in a PersonaBot's Channel sidebar. Provider-unavailable becomes a fail-closed creation/runtime recovery condition rather than a supported capability-absent mode.
- Existing `memory_*` Tools are legacy implementation surface and are not part of the v1 model-facing contract. Human UI and trusted Plugins continue to cross the Host boundary through Memory Service commands and queries.
- Pin metadata may be edited as an ordinary file, but it affects future prompt assembly only after an accepted Memory Commit passes the configured byte budget and model-aware preflight.
- ADR-0002's file-first direction is restored and deepened. ADR-0003 and ADR-0004 remain historical decisions superseded by this ADR; ADR-0014 remains superseded with Persona treated as conventional editable Memory content.
- Workspace authorization and Assignment `cwd` rules are separate from Memory and are recorded in ADR-0048.

## Update (2026-09-22) — no pinned bodies, no generated index, persona is a Session snapshot

ADR-0060 supersedes the pin mechanism above. The system prompt prefix is append-only and carries no derived Memory state: the Memory Tree section and the generated `MEMORY.md` index are removed, pin frontmatter is no longer consumed by prompt assembly, and there is no pin budget or full-body injection. The Agent reads, searches, and versions repository files with ordinary filesystem, Shell, `grep`, and `git` capabilities.

Persona remains conventional content but is delivered differently: each owned Session freezes the `PERSONA.md` body at its first prompt assembly, and that Session's system prompt keeps those bytes for its whole life, including across a Host restart. A Human edit reaches Sessions that have not snapshotted yet. The repository lifecycle, file-first access, accepted-commit boundary, and Memory Service ownership above are unchanged.

## Update (2026-09-25) — accepted Memory heads follow local branches

The accepted ledger remains distinct from raw Git history, but its current head is scoped to each local branch. The first branch-aware slice migrates the former single head into `main`. A clean switch to an existing branch is made by the Orchestrator in its current Session through native Git. The target tip must already be an accepted Memory Commit; otherwise the switch is refused, and a raw commit is never silently promoted. A new branch at an accepted historical commit inherits that commit as its branch baseline when it is first observed. Later accepted work advances only that branch's head and retains actor, cause, and validation records in the shared commit ledger.

The working tree changes as soon as Git switches. Ordinary file tools in the same Session therefore read the new branch's files; a missing file is an ordinary missing-file result. The Session-frozen Persona prompt remains unchanged under ADR-0060. Uncommitted changes block the switch operation without a force reset; the Orchestrator coordinates the conflict in [#263](https://github.com/BotHarness/BotHarness/issues/263), while branch creation is delivered in [#262](https://github.com/BotHarness/BotHarness/issues/262).

## Update (2026-09-25) — continue from a historical commit

The Human may select an exact commit in the raw Git graph and name a new local branch. The Orchestrator creates and checks out that branch in its current DSH Session after checking ownership, Git branch syntax, ref uniqueness, reachability from a local branch, an accepted ancestor, and a clean accepted source branch. No ref is overwritten and unfinished work is retained on refusal.

A branch created at an accepted commit begins with that accepted head and can advance through ordinary validated Memory turns. A branch created at a raw pending commit records only its previously accepted ancestor as the branch baseline. The branch ref and working tree switch immediately, but the current Source Event does not promote that raw point or later edits in the same turn. The graph marks the current raw head as needing repair, accepted history remains unchanged, and subsequent Memory turns wait for explicit Human Repair. Repair archives the whole provisional repository, then resets only the new branch to its accepted ancestor; the original raw ref remains in the restored graph. An interrupted archive move recovers the branch identity from archived Git HEAD. Git refs, checked-out branch, and accepted provenance survive process restart independently.

## Update (2026-09-25) — preserve conflicts while coordinating a switch

A Human's explicit branch choice is carried as Channel message intent, so an Orchestrator turn can start even when the current Memory tree already has provisional edits. The accepted ledger remains the authority: this coordination turn cannot silently accept edits that predated it. The first switch attempt refuses dirty or unaccepted source state and keeps the current branch, staged index, and working files intact. The Orchestrator reports the target and block in the Channel, asks affected active Assignments to pause through their existing addressed request/report seam, and waits for their reports. An Assignment does not write the Memory repository.

Once it is safe, the Orchestrator may preserve uncommitted Memory work in a named native Git stash including untracked files, then retry the same Memory switch in the same DSH Session. The stash remains available for recovery; no force checkout, reset, or discard is authorized by this flow. If Git still refuses, the Channel states the unresolved block and the original branch and files remain in place. A successful retry uses the existing branch acceptance check and reads the switched working tree immediately; the Session-frozen Persona prompt does not change.

Source: https://botharness.ai/dev/adr/0047-memory-is-an-optional-git-backed-service/index.mdx
