Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "tree-ring-memory",
"interface": {
"displayName": "Tree Ring Memory"
},
"plugins": [
{
"name": "tree-ring-memory",
"source": {
"source": "local",
"path": "./"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
7 changes: 4 additions & 3 deletions .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tree-ring-memory",
"version": "0.3.3",
"version": "0.3.6",
"description": "Local-first memory lifecycle, project bootstrap, and receipt-backed harness guidance for coding agents using Tree Ring Memory v0.15+.",
"author": {
"name": "TerminallyLazy",
Expand All @@ -22,6 +22,7 @@
"skills"
],
"skills": "./skills/",
"hooks": "./hooks/codex-hooks.json",
"interface": {
"displayName": "Tree Ring Memory",
"shortDescription": "Local-first memory lifecycle guidance for Codex agents.",
Expand All @@ -34,8 +35,8 @@
"Interactive"
],
"websiteURL": "https://terminallylazy.github.io/Tree-Ring-Memory/",
"privacyPolicyURL": "https://github.com/TerminallyLazy/tree-ring-memory-codex-plugin/blob/main/PRIVACY.md",
"termsOfServiceURL": "https://github.com/TerminallyLazy/tree-ring-memory-codex-plugin/blob/main/TERMS.md",
"privacyPolicyURL": "https://github.com/TerminallyLazy/Tree-Ring-Memory/blob/main/plugins/tree-ring-memory/PRIVACY.md",
"termsOfServiceURL": "https://github.com/TerminallyLazy/Tree-Ring-Memory/blob/main/plugins/tree-ring-memory/TERMS.md",
Comment on lines +38 to +39

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Users reach incorrect legal pages 🐞 Bug ≡ Correctness

The changed plugin manifest sends privacy and terms users to a plugins/tree-ring-memory path in
the separate framework repository, while the package's published submission identifies the plugin
repository's root legal documents. The skills-only manifest duplicates those legal URLs and
additionally points its repository field at the framework rather than the published plugin source,
so both distribution forms expose inconsistent package provenance and policy links.
Agent Prompt
## Issue description
The repository plugin manifest now points legal-policy links at a different repository path than the source and legal documents declared for this package. The separately generated skills-only manifest repeats these policy URLs and declares the framework repository as its package source.

## Issue Context
The submission material identifies the plugin repository and root-level privacy and terms documents as the published locations. Keep the repository and policy metadata consistent across both manifest variants.

## Fix Focus Areas
- .codex-plugin/plugin.json[38-39]
- packaging/codex-skills-only/.codex-plugin/plugin.json[9-10]
- packaging/codex-skills-only/.codex-plugin/plugin.json[36-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

"defaultPrompt": [
"Recall durable project context before changing release behavior.",
"Capture this validated lesson without storing a transcript.",
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ permissions:
contents: read

env:
TREE_RING_VERSION: "0.15.0"
TREE_RING_LINUX_X86_64_SHA256: "9b47873268dbb94712a49b02bd785cc69507facee1e879e46e6922778b4afbe6"
TREE_RING_VERSION: "0.15.7"
TREE_RING_LINUX_X86_64_SHA256: "5c9da887e212b1a7222205d2a661910276171a927d7677f09ec097f2cddd26b7"

jobs:
validate:
Expand Down
34 changes: 26 additions & 8 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,33 @@

Effective August 23, 2026

The Tree Ring Memory plugin is an instruction package for AI coding agents. It
does not operate a hosted service, create a user account, collect analytics,
send telemetry, or include a remote MCP server.
The Tree Ring Memory repository plugin packages instructions and local
lifecycle-hook registrations for AI coding agents. It does not operate a hosted
service, create a user account, collect analytics, send telemetry, or include a
remote MCP server.

When an agent runs the separately installed Tree Ring Memory CLI, the CLI stores
the memory content the user chooses in a local SQLite database under the
configured Tree Ring root. The project does not receive that database or its
contents. Data leaves the local environment only when the user or another tool
explicitly exports, syncs, publishes, or otherwise transmits it.
The repository hooks run only when a session or subagent starts or stops. They
forward the host's lifecycle JSON through standard input to the separately
installed Tree Ring Memory CLI and wait synchronously for at most 10 seconds.
They do not register for user prompts, tool calls, or `SessionEnd`; persist hook
input; capture prompts or transcripts; or run in the background. The lifecycle
parser never inspects or persists `transcript_path`, `last_assistant_message`,
prompts, or transcript content.

Each stop event enforces one agent-mediated memory checkpoint. It asks the
active agent to evaluate already-grounded work rather than deriving a summary
from hook input. Only a concise, durable candidate classified as normal
sensitivity may be written automatically with strict `tree-ring capture`.
That command fixes agent scope, requires identity and provenance, tags the
memory as automatic capture, and rejects sensitive content. If no candidate
passes, no durable memory is created.

When an agent runs an explicit command in the separately installed Tree Ring
Memory CLI, including an identity-bound strict capture approved by the
checkpoint gates, the CLI stores accepted memory content in a local SQLite
database under the configured Tree Ring root. The project does not receive that
database or its contents. Data leaves the local environment only when the user
or another tool explicitly exports, syncs, publishes, or otherwise transmits it.

The plugin instructs agents to avoid transcripts, credentials, secrets, private
keys, raw chain-of-thought, and unnecessary sensitive personal data. It also
Expand Down
74 changes: 72 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ This plugin packages one Codex skill that teaches agents when to recall, write,
audit, consolidate, and forget project memory using the open-source
[Tree Ring Memory](https://github.com/TerminallyLazy/Tree-Ring-Memory) CLI.

Plugin `0.3.3` requires Tree Ring Memory CLI **>= 0.15.0**. The minimum adds
Plugin `0.3.6` requires Tree Ring Memory CLI **>= 0.15.0**. The minimum adds
verified project-local bootstrap and scope-preserving CLI updates on top of the
receipt-backed harness, schema-v3, coordinated-write, and idempotency contracts
used by this guidance.

The public ZIP is a skills-only package. It intentionally omits
The repository plugin includes native lifecycle hooks and requires CLI `0.15.6`
or newer for automatic recall. Build the separate public-directory ZIP with
`python3 packaging/build-codex-skills-only.py tree-ring-memory-codex-skills-only.zip`.
That generated ZIP is a skills-only package. It intentionally omits
`interface.screenshots`, which OpenAI's ZIP ingestion does not accept for this
package type; the logo and composer icon remain available.

Expand Down Expand Up @@ -201,3 +204,70 @@ webhooks, analytics, credentials, or networked runtime code.

See [PRIVACY.md](PRIVACY.md), [TERMS.md](TERMS.md), and
[SECURITY.md](SECURITY.md) for data handling, use terms, and disclosures.

## Install The Hook-Capable Plugin

```bash
codex plugin marketplace add TerminallyLazy/tree-ring-memory-codex-plugin
codex plugin add tree-ring-memory@tree-ring-memory
```

Review and trust the Tree Ring hook definitions in Codex, then start a new session.


## Automatic Lifecycle Hooks

The repository plugin registers exactly `SessionStart`, `SubagentStart`,
`Stop`, and `SubagentStop`. Each hook forwards its event JSON directly to the
local CLI and waits synchronously for at most 10 seconds. It does not register
prompt, tool, compaction, or `SessionEnd` hooks; run a background service;
scrape chats; or ship an MCP server.

Session start covers startup, resume, and compaction rehydration when the host
reports those sources. Subagent start gives each worker an independent,
receipt-backed preflight. Codex requires review and trust of the installed hook
definition before it runs. Claude Code loads the hook with the enabled plugin.

Startup recall loads a bounded brief of shared project guidance and this
agent's durable memories, including captures from earlier sessions. Workflow
and session memories remain limited to their matching scope. It does not
depend on memories containing special startup keywords. Use targeted recall
when the task changes; the startup brief is not an exhaustive search.

Stop and subagent-stop enforce one agent-mediated memory checkpoint. The
lifecycle parser uses only stable harness identity and project fields; it never
inspects or persists `transcript_path`, `last_assistant_message`, prompts, or
transcript content. The checkpoint asks the active agent to evaluate its
already-grounded work. If and only if that evaluation yields a concise,
durable, normal-sensitivity candidate, the agent automatically runs the exact
strict `tree-ring capture` command template returned by the lifecycle handler.
Strict capture fixes agent scope, requires identity and provenance, adds an
automatic-capture tag, and rejects sensitive candidates. No candidate means no
memory write. This is one bounded checkpoint, not a recorder or automatic
summary of every turn.

The hook wrapper resolves the Git project root when available, prefers that
project's `.tree-ring/bin/tree-ring`, and otherwise uses `tree-ring` from
`PATH`. It then invokes the shared lifecycle entry point with the project-local
`.tree-ring` root. An unavailable or incompatible CLI is not active-harness
proof and cannot be reported as a successful checkpoint or capture.

When project activation has already installed the managed lifecycle definition
in `.codex/hooks.json` or `.claude/settings.json`, that project definition owns
recall and stop checkpoints. The marketplace wrapper detects the exact managed
marker and exits without invoking the CLI, preventing duplicate context,
receipts, checkpoint continuations, or capture attempts when the host merges
project and plugin hooks.

`integrations status --verbose` reports the last validated recall's result
count and query class. A zero-result receipt proves the check ran; it does not
prove that useful context was found. A skills-only plugin installation has no
automatic lifecycle hooks; enable the repository plugin or configure the
project with the CLI to obtain them. A newly configured Codex hook still needs
the host's trust flow and a new session before automatic execution can be
verified.

Installer onboarding requires CLI 0.15.7 or newer to create the activation
manifest and native project hooks in the same install action. On earlier 0.15
CLIs, run `tree-ring init` explicitly after the installer. Onboarding readiness
is configuration; a fresh host receipt is required to prove automatic recall.
17 changes: 14 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Supported Versions

The `main` branch is the supported version of this Codex plugin wrapper.
The `main` branch is the supported version of this agent plugin package.

The Tree Ring Memory framework and CLI are maintained in the canonical
repository:
Expand All @@ -25,8 +25,19 @@ or personal data in a public issue.

## Data Handling

This wrapper plugin contains guidance files only. It does not run a background
service, include remote MCP servers, collect telemetry, or store credentials.
This wrapper plugin contains guidance files and bounded local lifecycle-hook
registrations. It does not run a background service, include remote MCP
servers, collect telemetry, or store credentials. The hooks run only at
`SessionStart`, `SubagentStart`, `Stop`, and `SubagentStop`, forward standard
input directly to the local Tree Ring CLI, and do not persist prompts,
transcripts, `last_assistant_message`, or hook input. They are synchronous,
bounded to 10 seconds, and never run as a `SessionEnd` or background recorder.

Stop hooks enforce one agent-mediated checkpoint. They may supply an exact
strict `tree-ring capture` template only for concise durable candidates. Strict
capture fixes agent scope, requires harness identity and provenance, accepts
only normal sensitivity, and tags the result as automatic capture. A missing,
sensitive, ambiguous, or ungrounded candidate must not be stored.

Tree Ring Memory is designed for explicit agent-mediated memory actions. Store
only concise decisions, lessons, warnings, and evidence references that are
Expand Down
11 changes: 10 additions & 1 deletion SUBMISSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Long description:

## Release Notes

Version 0.3.3 of the v0.15-compatible skills-only package adds verified
Version 0.3.6 of the v0.15-compatible skills-only package adds verified
project-local bootstrap, project-root-safe initialization, and scope-preserving
CLI update guidance. It retains the ZIP-ingestion fix that omits unsupported
`interface.screenshots`, plus receipt-backed harness readiness, same-host
Expand All @@ -94,3 +94,12 @@ skill remains guidance-only and explicitly forbids claims that a command ran.
OpenAI's Claude-plugin migration guide asks local-execution plugins to contact
their OpenAI partner; disclose this boundary in the submission rather than
representing the package as a hosted integration.

## Repository Lifecycle Distribution

The Git marketplace package now includes SessionStart, SubagentStart, Stop, and
SubagentStop hooks backed by CLI 0.15.6. The public-directory upload must use
`packaging/build-codex-skills-only.py`; it excludes hooks and does not promise
automatic invocation. Updating the Git repository does not update a previously
reviewed directory snapshot. Submit the versioned ZIP through the directory
review channel separately.
13 changes: 7 additions & 6 deletions TERMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ Effective August 23, 2026
The Tree Ring Memory plugin is open-source software distributed under the MIT
License. By using it, you agree to the license and these operational terms.

The plugin provides agent instructions only. It is not a hosted memory service,
backup service, access-control system, medical or legal record system, or
guarantee that an AI host will follow every instruction. The user is responsible
for choosing what to store, maintaining backups, controlling local filesystem
access, reviewing agent actions, and complying with applicable policies and
law.
The repository plugin provides agent instructions and local lifecycle-hook
registrations. The separate OpenAI upload artifact is skills-only. Neither is a
hosted memory service, backup service, access-control system, medical or legal
record system, or guarantee that an AI host will follow every instruction. The
user is responsible for choosing what to store, maintaining backups, controlling
local filesystem access, reviewing agent actions and hook trust, and complying
with applicable policies and law.

Tree Ring Memory is provided without warranty, including warranties of fitness,
availability, accuracy, non-infringement, or data durability, to the maximum
Expand Down
26 changes: 26 additions & 0 deletions hooks/codex-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/sh
set -eu

if command -v git >/dev/null 2>&1; then
project_root=$(git rev-parse --show-toplevel 2>/dev/null || true)
if [ -n "$project_root" ]; then
cd "$project_root"
fi
fi

# Project activation owns lifecycle recall and checkpoints when its managed hook
# is present. The marketplace hook stands down to prevent duplicate handling.
if [ -f .codex/hooks.json ] && {
grep -Fq 'Tree Ring Memory managed lifecycle v2"' .codex/hooks.json ||
grep -Fq 'Tree Ring Memory managed lifecycle v3"' .codex/hooks.json ||
grep -Fq 'Tree Ring Memory managed lifecycle v4"' .codex/hooks.json
}; then
exit 0
Comment on lines +13 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Projects can lose automatic memory checks 🐞 Bug ≡ Correctness

codex-hook.sh treats any occurrence of a managed-lifecycle marker string in .codex/hooks.json as
proof that an active managed hook owns the events. A project configuration that merely includes that
text in an unrelated description or value makes the marketplace hook exit before invoking the CLI,
leaving recall and stop checkpoints unavailable.
Agent Prompt
## Issue description
The marketplace wrapper disables itself based on an unstructured substring match anywhere in `.codex/hooks.json`, rather than confirming that the managed lifecycle definition is present and owns the relevant hook events.

## Issue Context
The wrapper exits before calling the lifecycle CLI. Documentation says this stand-down behavior is intended only when project activation installed the managed lifecycle definition.

## Fix Focus Areas
- hooks/codex-hook.sh[13-18]
- scripts/validate-lifecycle.py[112-147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

fi

tree_ring=tree-ring
if [ -x .tree-ring/bin/tree-ring ]; then
tree_ring=.tree-ring/bin/tree-ring
fi

exec "$tree_ring" --root .tree-ring integrations hook --harness codex --input-json-stdin
55 changes: 55 additions & 0 deletions hooks/codex-hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"description": "Load bounded Tree Ring context at startup and enforce one agent-mediated memory checkpoint at stop.",
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "\"${PLUGIN_ROOT}/hooks/codex-hook.sh\"",
"timeout": 10,
"statusMessage": "Loading Tree Ring context",
"additionalContextLimit": 6000
}
]
}
],
"SubagentStart": [
{
"hooks": [
{
"type": "command",
"command": "\"${PLUGIN_ROOT}/hooks/codex-hook.sh\"",
"timeout": 10,
"statusMessage": "Loading Tree Ring worker context",
"additionalContextLimit": 6000
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "\"${PLUGIN_ROOT}/hooks/codex-hook.sh\"",
"timeout": 10,
"statusMessage": "Checking Tree Ring memory"
}
]
}
],
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "\"${PLUGIN_ROOT}/hooks/codex-hook.sh\"",
"timeout": 10,
"statusMessage": "Checking Tree Ring worker memory"
}
]
}
]
}
}
50 changes: 50 additions & 0 deletions packaging/build-codex-skills-only.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""Build the OpenAI upload artifact without repository lifecycle hooks."""

from __future__ import annotations

import argparse
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo


PLUGIN = Path(__file__).resolve().parents[1]
PROFILE = PLUGIN / "packaging" / "codex-skills-only"
PACKAGE_ROOT = Path("tree-ring-memory")
FIXED_TIMESTAMP = (2026, 1, 1, 0, 0, 0)


def write_file(archive: ZipFile, source: Path, destination: Path) -> None:
info = ZipInfo(str(PACKAGE_ROOT / destination), FIXED_TIMESTAMP)
info.compress_type = ZIP_DEFLATED
info.external_attr = 0o100644 << 16
archive.writestr(info, source.read_bytes())


def build(destination: Path) -> None:
destination.parent.mkdir(parents=True, exist_ok=True)
with ZipFile(destination, "w") as archive:
write_file(
archive,
PROFILE / ".codex-plugin" / "plugin.json",
Path(".codex-plugin/plugin.json"),
)
for path in sorted((PLUGIN / "skills").rglob("*")):
if path.is_file():
write_file(archive, path, path.relative_to(PLUGIN))
for path in sorted((PLUGIN / "assets").rglob("*")):
if path.is_file():
write_file(archive, path, path.relative_to(PLUGIN))
for name in ("LICENSE", "PRIVACY.md", "SECURITY.md", "TERMS.md"):
write_file(archive, PLUGIN / name, Path(name))


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("output", type=Path, help="Destination ZIP path")
args = parser.parse_args()
build(args.output.resolve())


if __name__ == "__main__":
main()
Loading