Memory & Rules

Memory & Rules lives in both organization and project settings. Members propose source-backed facts, rules, preferences and decisions. Organization administrators manage organization memory; project administrators and organization administrators manage project memory. Agents require an explicit memory grant and can propose, search and read; they cannot approve, pin, supersede, restore or retract active memory.

Each item includes its source pointer, quotation, author, timestamp and complete-source SHA-256, trust, verification state, validity, expiry, tags and version history. A human statement is stored as an attestation by the signed-in user. Real message, ticket and comment sources are read from the same local database and verified against the submitted metadata. Private conversation evidence stays in its conversation. A shared redacted statement must be separately authored by a human.

The settings page supports search, kind/status/topic/person filters, inspection, proposing, approval/rejection, edit/supersession, retraction/restoration, pinning, version history, audit inspection and paginated JSON export. Edits create a new version and preserve the prior version. Expected-version preconditions reject stale edits. Every durable write is audited; secret-shaped input is rejected before storage. The PostgreSQL memory role cannot update/delete audit rows or bypass RLS.

Unapproved drafts remain visible only to their proposing principal and authorized managers, including after rejection. Restoring a rejected draft returns it to proposed for explicit approval; restoring previously approved history returns it to active. Approved history remains readable within its original audience. Migration 11711_memory_draft_privacy applies this publication boundary to existing rows too.

The service defaults on. MEMORY_SERVICE_ENABLED=false is the server kill switch; the routes and MCP discovery obey it. Existing agent-knowledge memory and its owner decisions keep their established contracts.

REST and library

The reusable policy/retrieval library is apps/api/src/modules/memory/service.ts; the shared schemas/types are packages/shared/src/memory-service.ts.

REST bases are /v1/orgs/:orgId/memory and /v1/projects/:projectId/memory. The URL chooses a target, while live authenticated membership and key grants determine authority. Project endpoints inherit applicable organization rules. Person memory belongs only to the signed-in person; an agent never inherits its sponsor's private notes. Conversation reads and updates require the conversationId context and live membership.

  • GET base: scoped search/list, q, kind, scope, status, tags, sourceAuthorId, createdBy, pinned, limit, after, and context filters. The person filter selects the source author; createdBy separately selects who submitted the memory, which can be an agent key's owner.
  • POST base: proposal with scope, kind, title, body, source and optional tags, validity, pin and idempotency key.
  • GET base/:id: source, trust, verification and version chain.
  • PATCH base/:id: administrator action (approve, supersede, retract, restore), expectedVersion, reason; supersede requires a complete replacement proposal.
  • GET base/context: caller capabilities.
  • GET base/rules: deterministic active rules/decisions under the caller's context.
  • GET base/status: service and retrieval capabilities.
  • GET base/export: export page with next cursor.
  • GET base/audit: authorized audit records.

Real-source input uses source:{kind,ref,quote,authorId,occurredAt,hash}. Supported source kinds are chat_message, issue, issue_comment, and human_statement. An attestation needs only {kind:'human_statement',quote:'the statement'}; its author, time and hash come from the server. The body is a summary; the quote is evidence, so a title/body is never itself proof.

MCP and agent access

Use memory_propose, memory_update, and rules_for_context. Search and read use the existing memory_search and memory_get with store:'memory'; without the selector the existing agent-knowledge behavior is preserved. Both remote MCP and stdio profiles honor the explicit memory resource grant. A memory grant does not authorize administration; the signed-in administrator performs governance through REST/the UI.

Retrieval

PostgreSQL GIN full-text search and pgvector HNSW indexes cover local 128-dimensional vectors and approved 768-dimensional semantic vectors. Index candidates and exact distance fallback run inside the allowed organization/project/person/conversation context. Every memory table access switches into a dedicated NOLOGIN NOSUPERUSER NOBYPASSRLS role with transaction-local principal settings and FORCE RLS. A missing application scope predicate cannot expose another tenant's rows. Exact scoped fallback protects retrieval from tenant crowding in approximate indexes; there is no global memory cache. When pgvector is unavailable, bounded local scoring remains usable and reports vectorIndex:false.

With an approved existing organization embedding provider, the service stores and retrieves semantic vectors and reports hybrid_semantic. Without provider credentials it uses credential-free feature-hash vectors plus full text, reports hybrid_local and semantic:false. Local feature hashes do not provide pretrained semantic understanding. Candidate limits and provider notices are returned explicitly. Live local acceptance uses the credential-free mode; no real model credentials are needed or read.

Ranked RAG results use a 500-candidate budget. List/export use stable keyset pages; export query text uses full-text filtering rather than bounded semantic ranking. Deterministic context loads pinned items of every kind, then rules and decisions, and continues past stale-source pages. A four-page scan budget per category produces an explicit directive_scan_budget_exhausted notice if older rows remain. Bot context includes retrieval notices and refuses malformed responses.

Superseded/retracted/expired/source-stale items cannot enter the bot's active context. Source freshness is checked against the current database. Chat/comment memory also reapplies canonical author-block and AI-summary provenance visibility, including both block directions. Hidden personal source quotations are excluded from list, detail/history, rules, export and write responses; proposals cannot capture them. Ordinary hash-edited stale history remains inspectable, and a separately authored human attestation remains organizational policy data. The bot context combines deterministic rules with active query retrieval; provenance and as-of time travel with every line in a bounded data fence.

Automated capture and rule hooks

tools/memory-rules/client.mjs supplies bot context loading, packet assembly, a tested pinned-rule compiler and source-backed proposal capture. The event bridge calls it on ordinary bot turns independently of the older follow-up-memory switch. Natural memory directives are heuristic candidates; capture never promotes them. It re-reads full canonical chat messages by messageId and ticket comments by commentId, so capture does not depend on history length, and records conversation-private/project-scoped proposals. The optional filters on the existing persona-message/comment read endpoints preserve membership, watermarks, blocks, archive and category gates; generic MCP call_api supports both query arguments. Supersession needs a human administrator to approve the new decision and replace the previous rule. The bridge host kill switch is orgs.<orgId>.memoryService.enabled:false; memoryService.projectId supplies the trusted project context, falling back to the configured follow-up project.

tools/memory-rules/hooks.example.json is an installable Claude Code hook configuration. It emits tested pinned rules at session startup/resume/compaction, prompt submission and subagent start, and denies structured agent memory_update calls. hook.mjs uses host-configured GOODQA_MEMORY_API_URL, GOODQA_MEMORY_ORG_ID, optional GOODQA_MEMORY_PROJECT_ID/GOODQA_MEMORY_CONVERSATION_ID, and GOODQA_MEMORY_KEY_FILE. The caller's hook JSON cannot select a tenant. Hooks supply context; server RBAC and RLS enforce the write/read boundaries even if an agent ignores it. Direct Bash/HTTP writes are governed by the server. There is no claim that a printed hook instruction proves model obedience.

No host hook configuration or running bridge is modified by this branch. For a lane, emit the compiled pinned pack as prompt text and call the same client before turns. For runtimes without these Claude hook events, the client library is the integration surface; no unsupported hook schema is claimed.

We use essential cookies to keep you signed in. See our Privacy Policy for details.