feat: agent memory & work log (Phase D)#25
Merged
Conversation
Closes the memory loop and persists the audit trail. Phase B left a
deny-only log line in policy.require_action; Phase D promotes that stub
to a persisted agent_action_log row covering both allow and deny. Adds
a per-agent key/value memory store the agent writes via a new
update-memory skill and reads back as injected role_context on every
dispatch — knowledge now survives container restarts. Pulls Phase C's
reviewed_prs dedup index forward so the autonomous PR-review loop can
build on a stable base.
Schema (migration 004 + schema.sql):
- agent_memory (agent_id, key, value, updated_at) — unique(agent_id, key)
- agent_action_log (agent_id, action, outcome, metadata jsonb, created_at)
with a CHECK constraint pinning outcome to allowed | denied
- reviewed_prs (agent_id, owner, repo, pr_number, reviewed_at)
with unique(agent_id, owner, repo, pr_number)
- RLS closed-by-default policies, mirrored across both files
Models:
- AgentMemoryModel.get / upsert / list_by_agent / delete
- ActionLogModel.record / list_by_agent
- ReviewedPRModel.exists / record / list_by_agent
Policy:
- require_action persists allow + deny rows. Audit writes are
best-effort — a DB hiccup logs locally and lets the request proceed,
trading audit completeness for availability; the policy decision
itself never depends on the DB.
Dispatcher:
- dispatch_task now loads the calling agent's memory and injects it
into role_context (best-effort; a memory-load failure does not
block dispatch).
Gateway:
- GET / POST /gateway/memory — agent-authed, policy-gated via
agent.memory.{read,write}. The agent's own row only — memory is
scoped per agent_id.
- POST /github/review now inserts a reviewed_prs row on success,
guarded by exists() so re-reviews don't double-write.
Skill + template:
- New update-memory skill — agent POSTs key/value pairs via the agent
bearer token. Server-side persistence is what lets preferences
survive container restart (SOUL.md cannot).
- code-review-engineer.yaml: skills += update-memory;
allowed_actions += agent.memory.read, agent.memory.write.
Tests: 124/124 pass. 11 new — policy audit allow + deny + audit-failure
swallowed; memory write/read/auth/policy/empty-key; reviewed_prs
records on success + skips on dup; dispatcher injects memory + best-
effort failure.
Live smoke test against the running backend (12/12 green): write/read
round-trip, upsert idempotent on key, 401 on bad/missing token, 403
when a secretary role hits /memory, agent_action_log carries the
allow + deny rows with role metadata.
Closes #8.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner
Author
|
@claude review |
|
|
||
| create unique index if not exists idx_agents_agent_token on agents(agent_token); | ||
|
|
||
| -- ── Phase D — agent memory & work log ──────────────────────────────────────── |
There was a problem hiding this comment.
Missing doc updates — CLAUDE.md compliance
CLAUDE.md L87 requires:
When behavior or setup changes, update the relevant md (
README.md,LOCAL_SETUP.md,ROADMAP.md, this file) in the same change.
This PR introduces three categories of changes that cross that threshold but no doc files are updated:
- Setup change —
LOCAL_SETUP.mdshould note that migration004must be re-run in Supabase to createagent_memory,agent_action_log, andreviewed_prs. The operator note lives only in the PR description right now and won't be discoverable after merge. - Architecture change —
CLAUDE.md's architecture section lists the backend routers/services. The two newGET /gateway/memoryandPOST /gateway/memoryendpoints and the three new model files (AgentMemoryModel,ActionLogModel,ReviewedPRModel) are worth a line there. - Behavioral change — agents now carry persistent memory across container restarts via the
update-memoryskill; this is non-trivial new platform behavior worth a mention inREADME.md.
This was referenced May 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase D — Agent memory & work log
Closes the memory loop and persists the audit trail. Phase B left a deny-only log line in
policy.require_action; Phase D promotes that stub to a persistedagent_action_logrow covering both allow and deny. Adds a per-agent key/value memory store the agent writes via a newupdate-memoryskill and reads back as injectedrole_contexton every dispatch — knowledge now survives container restarts. Pulls Phase C'sreviewed_prsdedup index forward so the autonomous PR-review loop can build on a stable base.Schema (migration
004+schema.sql)agent_memory (agent_id, key, value, updated_at)—unique(agent_id, key)agent_action_log (agent_id, action, outcome, metadata jsonb, created_at)with a CHECK constraint pinningoutcometoallowed | deniedreviewed_prs (agent_id, owner, repo, pr_number, reviewed_at)withunique(agent_id, owner, repo, pr_number)Models
AgentMemoryModel.get / upsert / list_by_agent / deleteActionLogModel.record / list_by_agentReviewedPRModel.exists / record / list_by_agentPolicy
require_actionpersists allow + deny rows. Audit writes are best-effort — a DB hiccup logs locally and lets the request proceed, trading audit completeness for availability; the policy decision itself never depends on the DB.Dispatcher
dispatch_tasknow loads the calling agent's memory and injects it intorole_context(best-effort; a memory-load failure does not block dispatch).Gateway
GET/POST /gateway/memory— agent-authed, policy-gated viaagent.memory.{read,write}. Per-agent only — memory is scoped byagent_id.POST /github/reviewnow inserts areviewed_prsrow on success, guarded byexists()so re-reviews don't double-write.Skill + template
update-memoryskill — agent POSTs key/value pairs via the agent bearer token. Server-side persistence is what lets preferences survive container restart (SOUL.mdcannot).code-review-engineer.yaml:skills += update-memory;allowed_actions += agent.memory.read, agent.memory.write.Tests
125/125 pass. 11 new:
policy— audit allow + deny + audit-failure swallowedgateway— memory write/read/auth/policy/empty-key;reviewed_prsrecords on success + skips on dupdispatcher— memory injection + best-effort failureLive smoke (12/12 green against the running backend)
Write/read round-trip; upsert idempotent on key; 401 on bad/missing token; 403 when a
secretaryrole hits/memory;agent_action_logcarries the allow + deny rows withrolemetadata.Operator note
Migration
004needs to be re-run in the Supabase SQL Editor to pick up the three new tables (idempotent —create table if not existsmakes re-runs safe). Agent image was rebuilt locally to include the new skill —docker build -t openclaw/agent:latest backend/agent-runtime/.Out of scope, follow-up
Memory compaction (LLM reflection / LRU / clustering) is tracked in #23 — the forward-compatible defaults this PR sets (
updated_atper row, all-keys injection at dispatch) keep that path open without schema changes.Closes #8.
🤖 Generated with Claude Code