Skip to Content
EngineeringDecisions (ADRs)ADR 0033 Three memory tiers on our own table: promotable kinds, two-party curation, purpose limitation, never training data

ADR 0033: Three memory tiers on our own table: promotable kinds, two-party curation, purpose limitation, never training data

Status
Accepted
Date
Deciders
Founder

Context

The founder asked for three tiers, all built: what a twin learned with a person in a program; how that person likes to work anywhere in the organisation; what everyone in a workbench role shares. Fact (verified in source): GET/DELETE P/agent-memory exist under program.agent_memory.read|manage, the read is universal through PROGRAM_SCOPE_READS, and the routes return nothing today. Memory can become surveillance, a prompt-injection channel or training data unless the rules are structural.

Decision

  • One table memory_records (tenant isolation application-level in R4: every query filters organization_id and the namespace’s organisation prefix is compared with the token on every call, pinned by the two-organisation test; a PostgreSQL row-level-security policy keyed on a session setting is R5, because no such policy exists anywhere in the schema and the application role sets no session variable, so a policy would need every request and worker session to set the organisation first; amended 2026-09-16 after the build review; tsvector; no embeddings in R4) with tier ∈ {individual, person, role} and namespaces org/{org}/program/{program}/user/{user}, org/{org}/user/{user}, org/{org}/role/{workbench_role_key}.
  • Tier 1 admits episodic refs, preferences, vocabulary, rubric adjustments and lessons; tier 2 admits preference, vocabulary and rubric_adjustment only (promotion of episodic or lesson records returns 422 kind_not_promotable, the scrubber runs regardless); tier 3 holds rubric versions, vocabulary and curated lessons, never program facts, personal data or user ids. In R4 the only tier 1 to tier 2 path is the person’s own POST P/agent-memory/{id}/promote; memory.propose_note admits tier individual only and memory.propose_promotion is designed (tool specification present, not_implemented for every target tier, absent from every harness toolset and from ALWAYS_ALLOWED_TOOLS; recorded 2026-09-16).
  • Nothing is saved without a “Remember this?” interrupt; auto-keep is opt-in per program (R5, default off). Fixed phrase: “Your twin saves nothing unless you say so.”
  • Oversight reads of another person’s tier 1 require program.twins.read, are audited and shown to the person (“Viewed by <Name> on <date>.”), and are for incident and audit review only; tier 2 is excluded from oversight; no endpoint aggregates memory or feedback per person. Tier 3 curation is two-party: an org.role_memory.manage holder accepts and a current holder of the workbench role confirms (R5).
  • No-training statement: “Human decisions, inbox feedback, lens scorecards and memory are never used to train or tune any model.”
  • Injection safety: summaries pass the sanitiser, injection-pattern list and markup allowlist on propose and again on injection; the brief is data with the fixed prefix <<<memory:...>>>; the harness prompt states memory never changes rubric, refusal or citation behaviour; a program-run brief drops tier-2 records that carry a ref.
  • Delegates read and write only their own tiers; a principal’s memory is never touched by a delegate; notes proposed under delegation are tagged on_behalf_of.

Consequences

  • Routes GET /me/memory, DELETE /me/memory/{id}, POST P/agent-memory/{id}/promote in R4; role-memory routes, propose-share and the org keys in R5.
  • Retention: tier 1 soft-forgotten 90 days after membership ends; tier 2 deleted with the user; tier 3 superseded, never edited; tombstones by source_ref; job memory_expire.
  • Tests: two-organisation isolation, two-program tier-2 brief, kind_not_promotable, one test per scrubber item, injection fixture through memory.propose_note.

Alternatives considered

  • A managed memory service: deferred; the table is enough for R4 and keeps the rules testable in one place.
  • Auto-keep by default with opt-out: saves before the person sees it; rejected.
  • Curation by organisation admins alone: IT roles would write a scientific rubric; rejected for the two-party rule.

Follow-ups

  • USER_SETTING_MEMORY_AUTOKEEP (R5, opt-in per program).
  • Row-level security on memory_records with a session setting written in get_session and the worker (R5); until then the docs and the validation package say “organisation column plus namespace check”.
  • org.role_memory.read|manage at PERSONA_VERSION 4 (R5).