Tree Ring uses ACTIVATION_PROTOCOL_VERSION = 1. Its default project-local
flow is:
tree-ring init
tree-ring integrations statusinit creates the canonical .tree-ring/ root, discovers maintained adapters,
and attempts create-only publication of project-local owned bridges or bounded
managed blocks plus the activation manifest. For Agent Zero it also
creation-publishes Tree Ring's passive
.tree-ring/activation/agent-zero.json binding and a needs-plugin manifest
record. It does not write Agent Zero core configuration, replace or remove an
existing final entry, or require users to copy a bridge or run integrations link. An existing bridge or manifest that would need mutation is preserved and
reported as needs-user-review. integrations link is an advanced alias for
controlled bridge work, not the default journey.
Advanced commands are tree-ring integrations status --verbose,
tree-ring integrations activate --harness <id> --dry-run,
tree-ring integrations certify, and tree-ring integrations deactivate --harness <id>. Certification records JSON and Markdown evidence; it is not
required for initialization.
The default .tree-ring, explicit ./.tree-ring, and absolute paths to the
same project-local store are equivalent for activation and deactivation.
Different stores and symlinked bridge targets remain rejected.
Keep an existing root AGENTS.md and .tree-ring/AGENTS.md separate. The
project's instructions remain authoritative; Tree Ring's managed root block
only references its local guidance. Create-only initialization preserves
existing project instructions, and a harness without a managed activation
record remains needs-user-review on subsequent status checks. Review and
reconcile that bounded reference without replacing either instruction file.
When initialization creates a missing root AGENTS.md, it records ownership
of only its marked Tree Ring block. Project instructions may later be added or
edited around that unchanged block without invalidating activation retries.
Changing the Tree Ring block itself still requires review. Existing manifests
that own the complete file keep their recorded ownership; this change does not
automatically migrate or replace a previously published manifest.
| State | Meaning |
|---|---|
active |
A maintained bridge has a fresh matching receipt for a new session's scoped recall and rendered safe context. |
configured-awaiting-proof |
A safe bridge is installed, but no qualifying fresh receipt exists. |
active-isolated |
Preflight succeeded against a store that does not match this project's canonical store. |
needs-trust |
The runtime needs its own user approval before project resources load. |
needs-project-mount |
The runtime cannot reach the canonical project root. |
needs-plugin |
The passive Agent Zero binding exists, but its separate compatible tree_ring_memory plugin is absent, disabled, invalid, or not available to this command. |
needs-user-review |
An existing or changed bridge/manifest cannot be safely replaced or removed, or publication durability is indeterminate. |
unsupported |
No maintained adapter can prove the integration. |
failed |
Detection, installation, preflight, or receipt verification has a concrete diagnostic. |
A marker, copied skill, or scan never makes a harness active. Missing,
expired, malformed, or mismatched receipts remain configured-awaiting-proof.
A zero-result recall can be a valid receipt: it proves the check occurred
without inventing context. Hermes and any runtime without a maintained verified
adapter remain non-active.
After successful integrations activate or link, the result reports any
already-valid matching receipt using the same verification as status. Without
that proof, it directs the user to open a new harness session and check
integrations status --verbose; it does not ask them to apply the completed
plan again. Activation does not create a receipt, and existing proof cannot
override a publication review, trust requirement, or other blocked outcome.
.tree-ring/activation.json is the versioned manifest. It contains the schema
and protocol versions, stable store_id, project-root fingerprint, CLI version,
and adapter records with state, capability, bridge path, owned files, and managed
blocks. Receipts live under .tree-ring/activation/receipts/; they contain
version and harness IDs, fingerprinted worker identity, harness-derived
agent/workflow/session IDs, state, timestamp, query class, result count,
selected-memory-ID digest, duration, success status, and matching-store
evidence. They never retain raw prompts, recalled content, secrets, sensitive
values, absolute paths, or coordinator capabilities. Keep at most 100 receipts
per harness/worker and none older than 30 days.
A receipt proves a privacy-safe preflight check, not durable memory creation or an adversarial security boundary. Durable writes remain explicit.
integrations status --verbose includes last_recall_result_count and
last_recall_query_class from the newest validated receipt. These are omitted
when there is no valid receipt. Zero results are reported as zero, rather than
being confused with an invocation that never happened.
As of CLI 0.15.6, automatic preflight applies each memory's declared scope:
| Memory scope | Required match within the selected project store |
|---|---|
| Project, global, manual, DOX, Revolve, tool, eval | Project name; origin agent/workflow/session fields are provenance |
| Agent | Project and agent profile; previous workflows and sessions remain recallable |
| Workflow | Project and workflow ID |
| Session | Project and session ID |
Project-less records and other projects are excluded. For project names that
require a safe lifecycle alias (including names with spaces), recall accepts
both the original basename and the exact normalized alias used by capture.
Worker profiles remain private: a new worker does not inherit a previous
worker's agent-scoped notes. Shared publication remains an explicit decision.
Explicit tree-ring recall flags continue to use exact conjunctive filtering.
With no safe task hint, preflight builds a startup brief without an FTS keyword requirement. It ranks bounded candidates with salience, confidence, recency, source authority, and scar/heartwood preference. With a safe task hint it uses scoped full-text recall. Sensitivity, supersession, expiry, redaction, project, and scope filters run before the candidate limit. Only safe summaries and source references enter the brief. The complete context fits within 6,000 UTF-8 bytes, preserving relevance order and omitting entries that do not fit. The receipt counts and hashes only the memories actually included.
A startup brief cannot replace task-specific recall during a long session. Its context reminds the agent to query again before substantive work or when the task changes; no prompt or tool hook reads or persists user input.
Codex and Claude adapter version 4 prefer .tree-ring/bin/tree-ring for both
hooks and capture instructions, then fall back to PATH. Existing version 3
definitions still need reconciliation: create-only activation preserves those
files and reports needs-user-review. Do not treat a binary update as a hook
upgrade or a new activation receipt. Review the old managed files and manifest
before replacing their definitions; never remove the memory database to
reconfigure hooks.
Bridge lifecycle writes fail closed. Publication creates an absent final path;
it never overwrites or removes an existing bridge or activation manifest, even
when the existing bytes were previously recorded as Tree Ring-owned.
Semantically unchanged manifests are preserved byte-for-byte. Activation or
deactivation that would mutate a final entry makes no such change and returns
needs-user-review with a reconciliation step.
If a directory durability check fails after publication, Tree Ring does not try
to roll the path back because it cannot safely condition removal on the exact
published inode and bytes. It preserves all published disk material, marks each
changed harness needs-user-review in the returned in-memory manifest, and
leaves any manifest that already reached disk intact. Disk and memory can
therefore differ until the user reviews and reconciles the indeterminate state.
The Agent Zero adapter is deliberately passive at project initialization. The
project only receives Tree Ring-owned binding material and its needs-plugin
state; it does not receive plugin code, an Agent Zero core change, a generic
.a0 marker, or a capability descriptor. This keeps a repository portable and
prevents a copied marker or configuration file from impersonating an installed
adapter.
The separately installed tree_ring_memory plugin owns one fixed
activation-capability.json descriptor at its own plugin root. Its path is
absolute, outside the project, and must be a regular non-symlink file. The
plugin enforces that fixed safe location and passes the descriptor only through
its private child-process transport
(TREE_RING_AGENT_ZERO_PLUGIN_MANIFEST) for its internal core init, status,
and preflight calls from the configured mounted project. It strips an inherited
descriptor value from every other child process. It is not a user setting,
project artifact, API/UI payload, receipt field, or model input; users must not
copy, create, edit, or manually pass a descriptor.
Core validates the descriptor's exact schema and the sibling plugin manifest's
name and version before it uses it. A descriptor-scoped init may establish the
passive binding but never turns it into proof or changes its persisted manifest
record from needs-plugin. Only descriptor-scoped runtime status can derive
configured-awaiting-proof, and only a descriptor-scoped new-session preflight
with a matching receipt can report active. That preflight requires both the
live validated descriptor and the exact passive core-owned needs-plugin
binding record. Legacy or non-passive Agent Zero records are deliberately not
auto-migrated and do not qualify. Ordinary host-shell status without that
internal descriptor remains needs-plugin. There is no generic manual
descriptor command.
This is also a release boundary: the descriptor's declared Tree Ring series, the installed core CLI, and the installed plugin release must be compatible. An unreleased source checkout, a stale bundled CLI, or a passive binding does not make Agent Zero capable.
The following JSON shapes are the version-1 interoperability contract. All fingerprints are lowercase, 64-character SHA-256 hex digests. Paths, when they are present in a manifest, are project-relative. These examples deliberately use synthetic IDs and no prompt, recalled context, capability, or absolute path.
{
"schema_version": 1,
"protocol_version": 1,
"store_id": "01234567-89ab-4def-8123-456789abcdef",
"project_root_fingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"cli_version": "0.14.0",
"harnesses": {
"claude-code": {
"state": "configured-awaiting-proof",
"adapter_version": "1",
"adapter_capability": "native-preflight",
"bridge_fingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bridge_path": ".claude/settings.json",
"owned_files": [
{
"path": ".claude/skills/tree-ring-memory/SKILL.md",
"sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
}
],
"managed_blocks": [
{
"path": ".claude/settings.json",
"block_id": "tree-ring-session-start-v1",
"sha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"leading_separator": "\n"
}
]
}
}
}The adapter record is keyed by its canonical harness_id. adapter_version
and bridge_fingerprint are required for receipt-backed activation.
bridge_fingerprint is the lowercase SHA-256 hex digest of a length-delimited
UTF-8 field stream. Each field is encoded as its UTF-8 byte length followed by
its bytes; the length is an unsigned, big-endian, platform-width integer
(usize::to_be_bytes, eight bytes on the supported 64-bit CLI targets).
This is the only executable bridge-fingerprint encoding for protocol version 1.
Earlier draft prose described a canonical-JSON encoding before executable
fingerprints existed; it was not a released manifest encoding and does not
define a legacy protocol-1 variant.
The stream begins with harness_id, then adapter_version. Owned files are
sorted by (path, sha256) and each appends the fields "file", path, and
sha256. Managed blocks follow the files, sorted by
(path, block_id, sha256, leading_separator), and each appends the fields
"block", path, block_id, sha256, and leading_separator. No project
root is included. leading_separator records only adapter-owned whitespace
immediately before a managed block and is restricted to "", "\n", or
"\n\n". It may be omitted from manifest JSON only when empty; omission
deserializes as "", and the empty value still contributes a zero-length field
to the fingerprint. For example, the manifest above contributes one LF byte
for leading_separator, while an omitted field contributes no value bytes.
The following is the first compatible plugin descriptor. It is shown only to define the cross-repository protocol; it remains plugin-owned and internal, not a project file or a user-authored command input.
{
"schema_version": 1,
"kind": "tree-ring-agent-zero-plugin-capability",
"plugin_id": "tree_ring_memory",
"plugin_version": "3.1.0",
"activation_protocol_version": 1,
"tree_ring_version": {
"min": "0.14.0",
"minor": "0.14"
},
"enabled": true
}plugin_version must exactly agree with the installed descriptor's sibling
plugin.yaml; the example's 3.1.0 is the first compatible plugin release.
The plugin enforces the descriptor's fixed safe external location; core
verifies every listed field and that sibling manifest identity before accepting
it. The descriptor never appears in a project manifest, Agent Zero tool input,
Web UI/API response, or receipt.
{
"schema_version": 1,
"protocol_version": 1,
"receipt_id": "receipt-01",
"harness_id": "claude-code",
"adapter_version": "4",
"bridge_fingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"store_id": "01234567-89ab-4def-8123-456789abcdef",
"project_root_fingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"worker_key_fingerprint": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"session": {
"agent_profile": "claude-code",
"workflow_id": "workflow-01",
"session_id": "session-01"
},
"state": "active",
"query_class": "startup_fallback",
"result_count": 0,
"selected_memory_ids_sha256": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"duration_ms": 18,
"status": "success",
"recorded_at": "2026-08-13T12:00:00Z"
}query_class is an approved stable category, never a raw task hint. A
zero-result receipt has result_count: 0 and a digest of the empty selected-ID
set; it is still valid only after preflight successfully renders safe context
and persists the receipt.
Receipt freshness is derived from recorded_at and the 30-day retention
window; no expiry timestamp is serialized.
The managed Codex and Claude Code commands accept exactly SessionStart,
SubagentStart, Stop, and SubagentStop. Start hooks perform receipt-backed
recall. Stop hooks enforce one agent-mediated automatic capture checkpoint. A
session-start hook consumes this privacy-safe projection of the host event:
{
"session_id": "session-01",
"cwd": ".",
"hook_event_name": "SessionStart",
"source": "startup"
}session_id, cwd, and hook_event_name are required. source is host
metadata and is ignored after the host has selected the hook. cwd must resolve
inside the activated project and is not copied into a receipt. Compaction is
rehydrated by the host firing SessionStart again with source: "compact".
A subagent hook adds only the host-owned worker identity fields:
{
"session_id": "session-01",
"cwd": ".",
"hook_event_name": "SubagentStart",
"agent_id": "agent-01",
"agent_type": "reviewer"
}Tree Ring derives a distinct worker identity from agent_id and agent_type;
neither field is accepted as a store, project, capability, or coordinator
identity. Both hosts may include transcript paths, model names, turn IDs, or
other benign metadata. Tree Ring ignores those fields completely: it does not
read, forward, log, persist, or include them in recall. Capability-bearing or
root-selecting fields are rejected instead of ignored.
Lifecycle launchers use the current checkout's project-local .tree-ring,
including in a linked Git worktree. If that directory is genuinely absent, the
hook exits successfully without output or creating memory state. An inherited
hook does not initialize a worktree or borrow the main checkout's memory store.
An existing directory with missing or invalid activation, a file in place of
the directory, or a symlink remains an error requiring repair. Explicit capture
and preflight commands retain their strict activation requirements.
The managed command reads its event input from stdin and writes exactly one JSON object to stdout on successful preflight:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Tree Ring Memory scoped preflight recall:\n- No safe memories matched this scoped query.\nProject source and instructions remain authoritative; verify recalled guidance against them."
}
}For a worker, hookEventName is SubagentStart. additionalContext contains
only the safe context produced for that live session or worker. It must not echo
hook input, task prompts, receipt JSON, capabilities, or unredacted recalled
content. The command fails without emitting a successful lifecycle object if
receipt verification fails.
Stop and SubagentStop events require the same host-owned session and worker
identity fields plus stop_hook_active. Tree Ring ignores transcript paths and
last_assistant_message; neither contributes to a candidate, checkpoint,
receipt, or query. On the first stop attempt, the hook returns a non-error
continuation decision:
{
"decision": "block",
"reason": "Tree Ring automatic capture checkpoint ..."
}The reason directs the active agent to review only outcomes already in its
working context and submit zero to three concise durable candidates through the
strict tree-ring capture command. Zero is correct when nothing reusable
occurred. capture fixes scope to agent; requires project, agent, workflow,
session, idempotency, and agent-checkpoint: provenance; accepts only
normal-sensitivity cambium, scar, or seed candidates; and passes through the
existing coordinated write policy. remember and evidence remain available
as explicit manual surfaces outside this automatic checkpoint.
When the host re-runs the hook with stop_hook_active: true, Tree Ring emits an
empty JSON object so the response can finish without a loop. Hook definitions
are synchronous and bounded to ten seconds. Tree Ring does not register prompt,
tool, or SessionEnd capture hooks, inspect a transcript, or run a background
recorder.
Pi's before_agent_start extension sends local runtime identity to the CLI on
stdin only:
{
"agent_profile": "pi",
"workflow_id": "workflow-01",
"session_id": "session-01",
"task_hint": "project startup constraints"
}agent_profile, workflow_id, and session_id come from Pi's local
session manager, not model text. task_hint is optional; when supplied it is
sent only on stdin for the current preflight, subject to sensitivity rejection
and fallback to the bounded startup brief. The raw value is never written
to a receipt, log, memory, bridge, or response.
The separate tree_ring_memory plugin derives Agent Zero identity on the
server and sends only this request to the project-local command:
{
"agent_profile": "agent-zero-worker",
"workflow_id": "workflow-01",
"session_id": "session-01"
}Agent Zero does not send a task hint, prompt, coordinator capability, token, or model-supplied identity. After descriptor-scoped status validates its internal capability descriptor, the plugin reads the passive relative project binding and derives the three identity fields before invoking preflight. The descriptor is not JSON stdin and cannot be supplied by the model or a user request.
Each adapter validates the field whitelist above before preflight. Benign host
metadata outside that whitelist is ignored and never forwarded. Unknown
capability-bearing fields (including capability, token,
authorization, coordinator_capability, or
TREE_RING_COORDINATOR_TOKEN) are rejected rather than ignored. Inputs that
claim a store, root, bridge fingerprint, harness identity, or receipt state are
also rejected; those values come only from the local manifest and adapter.
The Agent Zero descriptor is separate from stdin: it is accepted only through
the installed plugin's internal absolute-path transport, never through a
generic CLI option or an input field. Raw stdin and lifecycle-hook input are
transient: Tree Ring does not persist or log them, regardless of whether
validation succeeds.
Pi's before_agent_start extension and the Agent Zero
tree_ring_memory plugin both invoke the project-local preflight command with
JSON stdin and consume this response shape. Their harness ID and
context_format differ, but the JSON result is identical:
{
"context": "Tree Ring Memory scoped preflight recall:\n- No safe memories matched this scoped query.\nProject source and instructions remain authoritative; verify recalled guidance against them.",
"state": "active",
"receipt": {
"schema_version": 1,
"protocol_version": 1,
"receipt_id": "receipt-01",
"harness_id": "pi",
"adapter_version": "1",
"bridge_fingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"store_id": "01234567-89ab-4def-8123-456789abcdef",
"project_root_fingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"worker_key_fingerprint": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"query_class": "startup_fallback",
"result_count": 0,
"selected_memory_ids_sha256": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"duration_ms": 18,
"status": "success",
"recorded_at": "2026-08-13T12:00:00Z"
}
}Agent Zero returns this complete shape through its separate plugin:
{
"context": "Tree Ring Memory scoped preflight recall:\n- No safe memories matched this scoped query.\nProject source and instructions remain authoritative; verify recalled guidance against them.",
"state": "active",
"receipt": {
"schema_version": 1,
"protocol_version": 1,
"receipt_id": "receipt-02",
"harness_id": "agent-zero",
"adapter_version": "1",
"bridge_fingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"store_id": "01234567-89ab-4def-8123-456789abcdef",
"project_root_fingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"worker_key_fingerprint": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"query_class": "startup_fallback",
"result_count": 0,
"selected_memory_ids_sha256": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"duration_ms": 18,
"status": "success",
"recorded_at": "2026-08-13T12:00:00Z"
}
}The structured response exposes the serialized ActivationReceiptSummary,
which deliberately omits the persisted receipt's session identity and receipt
state. The top-level state reports the activation result. The plugin derives
the Agent Zero identity server-side; it must not accept a model-supplied
identity. Its binding carries the same protocol version, store_id,
project-root fingerprint, relative memory_root, and JSON stdin/stdout command
contract. A failed preflight emits no successful context or receipt and the
command exits with an error.
An adapter may classify a harness as active only after it verifies the
manifest, bridge, and receipt as one tuple. It must reject the receipt and
report the exact non-active state if any of these equality requirements fail:
receipt.schema_version == 1andreceipt.protocol_version == manifest.protocol_version == 1.receipt.harness_idis exactly the adapter's canonical harness ID.receipt.adapter_version == manifest.harnesses[harness_id].adapter_version.receipt.store_id == manifest.store_id.receipt.project_root_fingerprint == manifest.project_root_fingerprint.receipt.bridge_fingerprint == manifest.harnesses[harness_id].bridge_fingerprint, after recomputing that fingerprint from the installed owned material.receipt.status == "success"andreceipt.stateisactiveoractive-isolated.recorded_atis not in the future and is newer than the 30-day retention cutoff. Freshness is calculated fromrecorded_at; the receipt has no serialized expiry field.
Any adapter, harness, root, or bridge change invalidates prior receipts. A
matching receipt for another accessible store is active-isolated, never
active for the canonical shared project.
Pi project trust is a user decision: report needs-trust and leave global
trust unchanged. Agent Zero uses only the separate tree_ring_memory plugin.
init leaves a passive project binding at needs-plugin; the plugin's fixed
absolute, non-project descriptor is the only way its runtime status/preflight
can use that binding. Tree Ring does not modify Agent Zero core, and a generic
.a0 marker is not an adapter. A missing, invalid, disabled, or release-
incompatible descriptor remains needs-plugin; an inaccessible project root is
needs-project-mount; a different accessible store is active-isolated.
Shared status requires every receipt to match the canonical project store_id.
It is supported only for concurrent processes on the same host and a local
filesystem, not across hosts, NFS/network filesystems, or containers on
different hosts. Use per-host roots and explicit evidence-preserving fan-in.
Maintained adapters detect capability, create only absent owned material,
preflight a new session, and verify its receipt. Deactivation is also
creation-only at the writer boundary: existing final entries are not removed or
replaced automatically, including recorded owned material. Conflicting,
changed, or otherwise contested bridge and manifest entries remain unchanged
and report needs-user-review.