Cerebro

Persistent institutional memory, exposed over MCP.

1. What Cerebro is

Cerebro is a knowledge layer that both people and AI agents read from and write to. It stores organizational knowledge — decisions, rules, procedures, product behavior, customer specifics, past resolutions — as retrievable memories, each with attribution and traceability.

1.1 The problem it solves

Every new conversation with an agent starts from zero. Someone has to re-explain which customer this is, what was decided last quarter, why the workaround exists, which rule forbids the obvious approach. That re-explaining is expensive, it's inconsistent between people, and it silently fails when the person who knew is unavailable.

Cerebro moves that context out of individual heads and chat logs into a queryable store. An agent that calls Cerebro first arrives already informed. An agent that doesn't is guessing — confidently, and in a house style that makes the guess hard to spot.

Concretely. Someone discovers that deleting from the Data Sources panel is a hard delete, not a detach. They save it once, into arpia_product, as Product Knowledge. Six weeks later a different person asks a different agent "what happens if I drop a data source?" — and the answer comes back correct, with a link to the memory and the doc it came from. Nobody re-derived it. Nobody had to remember who knew.

1.2 Mental model

init is the map · load and search are the query · save writes · update corrects · config is the structure.

Two design principles govern the whole API:

Organizational, not personal. Cerebro holds knowledge the organization needs, not user configuration. "We markup LLM pricing at cost × 1.30" belongs in Cerebro. "I prefer bullet points" does not.

Discovery before action. init returns the map — which domains exist, which knowledge types are valid, which org-wide rules are in force. Every other call depends on that map. Init without Load is just discovery; Load without Init is guessing.

1.3 What Cerebro is not

NotWhy it matters
A document storeIt holds distilled knowledge, not files. Link to the document via references[]; don't paste it into content
A search engineGeneral knowledge doesn't belong here. "How do I parse JSON in Python" is not a Cerebro question
A chat logNothing is captured automatically. Every write is a deliberate, explicitly requested act
A preferences storeExplicitly out of scope, by design
Editable indefinitelyA memory is editable for 15 days, then frozen (§3.3). Write carefully the first time

1.4 How to read this document

It draws on two sources of truth, and they disagree in places:

  1. The MCP tool descriptions and API schemas — authoritative for the contract: fields, formats, windows, rejections. Sections 3–8.
  2. Observed behavior in live sessions — authoritative for defects. Section 9 is entirely empirical and appears in no schema. An agent reading only the tool descriptions will not know any of it.

Where the two conflict, this document states both and marks the resolution as pending. It does not average them.


2. Quick start

The shortest useful path, three calls:

// 1. Get the map. Once per conversation, before anything else.
{ "action": "initalize" }

// 2. See what already exists in a scope, without pulling content.
{ "mode": "list", "domain": ["arpia_product", "arpia_docs"] }

// 3. Ask a specific question and get a sourced answer.
{ "query": "what is the difference between a Repository and a Data Source?",
  "domain": ["arpia_product", "arpia_docs"] }

Four rules that prevent most errors:

  1. Init first. Without it, you don't know which domains or types are valid, and org-wide rules never load.
  2. Never infer intent to write. save and update run only when the user explicitly asks.
  3. Never guess a domain, type, or mid. If it isn't in loaded context, look it up or ask.
  4. Never pass exactly one domain. A single-element array silently excludes global, so the immutable rules don't fire (§9.3). Pass two or more, or omit domain entirely.

One thing to know up front: all six tools declare additionalProperties: true with no enumerated schema properties. Validation is server-side only — malformed payloads fail at runtime, not at the MCP layer. There is no safety net.


3. Core concepts

3.1 Hierarchy

Organization
   └── Domain (arpia_product, arpia_ciso, global, ...)
          └── Memory (mid_xxxxxx)
                 ├── knowledge_type_id  (Procedure, Immutable Rule, ...)
                 ├── parent_mid          (causal antecedent)
                 ├── references[]        (external URLs with a role)
                 ├── expires_at          (optional expiration)
                 └── status              (APPROVED | INACTIVE)

mid format is not documented. Schema examples show both 6 and 8 hexadecimal characters (mid_1df233, mid_a1b2c3d4). Do not infer a fixed length, and never construct a mid — copy it verbatim from an Init, Load, or Search result.

3.2 Object reference

ObjectWhat it isCritical consideration
OrganizationMulti-tenant isolation boundaryNo memory crosses organizations. Returned as organization_id / organization_name
DomainGrouping and permission scopeEvery save and retrieval is bound to a domain. Has a domain_slug (lowercase, underscores) and a human domain_namenot interchangeable across actions (§4.6)
Memory (mid)Atomic unit of knowledgeEditable for 15 days from creation, then frozen
knowledge_type_idSemantic classificationValid set is per-organization, from Init's available_types. Do not invent values
parent_midCausal link to an antecedentOnly the primary, canonical antecedent — never "related to." Memories form a tree
references[]External links: { url, title, role }Powers attribution in Search. Replaced wholesale on update, never merged
expires_atAuto-expiration, YYYY-MM-DD HH:MM:SSThe only automatic hygiene mechanism
statusAPPROVED or INACTIVECase-insensitive input, normalized to uppercase. No other value accepted. INACTIVE retires a memory
Scope (access)read_only or read_writePer user, per domain. Grantable but not revocable (§4.6)

3.3 Memory lifecycle

   save
    │
    ▼
 APPROVED ──── editable window: 15 days ────► FROZEN
    │            (title, content, domain,        │
    │             type, references,              │  no further edits
    │             expires_at, parent_mid,        │
    │             status)                       │
    │                                           │
    ├──► expires_at reached ──► auto-expired    │
    │                                           │
    └──► status: INACTIVE ──► retired ◄─────────┘ (see Gap 7)

The window is the single most consequential fact in this document. After 15 days a memory cannot be corrected. The documented recovery — retire it and write a replacement — may itself be blocked by the freeze; see Gap 7 in §12. Plan on writes being permanent.

Practical consequences:

  • Set expires_at at creation for anything provisional. Adding it later is only possible inside the window.
  • Run a mode:"list" check before saving. A duplicate you catch today is an edit; a duplicate you catch in a month is permanent.
  • Draft in the conversation, get confirmation, then save. There is no cheap undo.

3.4 Knowledge types

The authoritative list is available_types from cerebro_init. The table below is a convenience reference and may lag — if Init disagrees, Init wins.

TypeIntended useExample
Immutable RuleNon-negotiable organizational ruleProhibition of npm install without safeguards
Business RuleBusiness rule, may changeMarkup formula for LLM model pricing
ProcedureRepeatable sequence of stepsCommittee Minutes (Actas) generation flow
Customer KnowledgeCustomer-specific contextELCONIX configuration particularities
Product KnowledgeProduct behaviorRepository vs. Data Source distinction
FactVerified, stable data pointOria = Ontology Reasoning Interactive Agent
TipPractical learningOperational shortcut discovered in the field
Todo ItemPending workOpen [CONFIRMAR] fields in an Acta

Type is not cosmetic — it determines how a memory is prioritized during retrieval.


4. The six tools

4.1 cerebro_init — discovery

When: once per conversation, before any Load, Search, Save, or Update.

Invocation posture — the schema and ARPIA policy disagree.

PostureBehaviorAuthority
Context-triggeredInit fires automatically when the first message names a customer/project/product/team, implies scoped work ("I'm working on…"), or asks about organizational knowledgeThe tool description. The documented default
Explicit-invocation-onlyNo Cerebro tool fires — including Init — until the user asks ("check Cerebro," "load the context")ARPIA operating policy for interactive agent sessions, overriding the above

Policy is stricter than the schema, so following policy never violates the contract. Under explicit-invocation-only, an agent that needs Cerebro should say so and wait. Rules 2–4 in §2 hold under both.

Do not call: Init already ran this conversation; the question is general knowledge; the conversation won't touch institutional knowledge.

{ "action": "initalize" }

⚠️

The literal value is initalize — missing the second i. The correctly spelled initialize is not documented as supported. Do not "fix" it.

Response fields

FieldMeaning
userAuthenticated user's email
organization_idTenant identifier
organization_nameHuman-readable org name
available_typesKnowledge types this user may save
domainDomains this user can save to and retrieve from (field is singular)
last_saved_memoriesMost recent memories — useful for inferring active work
global_immutable_knowledgeOrg-wide rules spanning all domains

How to use it: validate every requested scope against domain and every type against available_types. If the user names something absent from either, ask — don't guess. Use last_saved_memories to infer intent when a query is broad.

Presentation: never dump the Init response to the user unless asked. It is a silent planning input.


4.2 cerebro_load — broad context

Retrieves a context package for a scope. Broad by nature. Requires prior Init.

When: the user opens work on a known scope — "working on ProjectX today," "continue the ERP migration," "load context for Customer A."

Don't call: that scope is already in the conversation; the user asked one specific question (→ Search); the requested scope matches no domain from Init (→ ask).

Four modes. Every domain array below carries two or more entries. This is the §9.3 mitigation, not a style choice — the schema's own examples use single-element arrays and would trigger the defect. Copy these shapes.

// Mode 1 — semantic retrieval within a scope (default)
{ "query": "natural language question or topic",
  "domain": ["arpia_ciso", "arpia_compliance"] }

// Mode 2 — specific memory by ID
{ "mid": "mid_1df233" }

// Mode 3 — latest activity in a scope, no specific query
{ "domain": ["arpia_docs", "arpia_product"] }

// Mode 4 — inventory: metadata only, no content
{ "mode": "list", "domain": ["arpia_docs", "arpia_product"] }

If the work genuinely concerns one domain, pad the array with a second relevant domain, or omit domain and filter the results. Never pass a one-element array.

mode:"list" is the most underused and most valuable mode. Use it for discovery and onboarding, duplicate detection before any save, and as the safe default when Load-vs-Search is ambiguous. Given that writes are effectively permanent (§3.3), the cost of skipping it is asymmetric.

After the call: do not dump results verbatim. Use them to inform the response. The user wants a working agent, not a memory dump.


4.3 cerebro_search — specific answer

Returns a synthesized answer plus structured attribution: the contributing memories and their external references. Narrow by nature.

When: a specific question institutional knowledge could answer — decisions, rules, processes, standards, past resolutions. Also when a prior Load didn't cover it, or the conversation moved outside the loaded scope.

Don't call: the answer is already in the conversation; the question is general knowledge; the user wants a broad scope load (→ Load) or wants to write (→ Save).

// Scoped (two or more domains — see §9.3)
{ "query": "natural language question", "domain": ["arpia_compliance", "arpia_ciso"] }

// Across all accessible domains, global rules included
{ "query": "natural language question" }

Query construction: build it intentionally — include the system names, components, and domain terms the user mentioned.

Presentation (critical): present the synthesized answer and its attribution block as-is. Do not paraphrase, summarize, or strip the contributing memories or external references — that structure is what makes the answer auditable, and it is the only defense against the failure mode in §9.1. This is the one place Cerebro requires verbatim pass-through to the user; Init and Load require the opposite.

If empty: say Cerebro has no knowledge on the topic yet, and offer to save what the conversation uncovers. Do not fill the gap with general knowledge.


4.4 cerebro_save — create only

Creates only. Never edits. Every call makes a new memory. A payload containing a mid is rejected, with a pointer to cerebro_update.

Reactive only. Called when the user explicitly says "save," "remember," "store," "persist," or equivalent. Never infer the intent.

FieldRequiredNotes
titleYesInfer from context
knowledge_type_idYesFrom Init's available_types
contentYesThe knowledge body
domainYesA single domain string — unlike Load/Search, which take arrays
parent_midNoLinks the new memory as a child of an existing one
referencesNoArray of { url, title, role }
expires_atNo"YYYY-MM-DD HH:MM:SS"
// Minimum
{ "title": "Memory title", "knowledge_type_id": "Procedure",
  "content": "Content...", "domain": "arpia_ciso" }

// With causal antecedent and references
{ "title": "Q2 follow-up on auth refresh design",
  "knowledge_type_id": "Tip",
  "content": "Continuation of the original auth refresh notes...",
  "domain": "arpia_product",
  "parent_mid": "mid_a1b2c3d4",
  "references": [
    { "url": "https://docs.arpia.ai/...", "title": "Original doc", "role": "context" }
  ] }

// With expiration
{ "title": "Temporary access note", "knowledge_type_id": "Tip",
  "content": "Vendor VPN credentials valid for the Q4 audit only.",
  "domain": "arpia_ciso", "expires_at": "2026-12-31 00:00:00" }

API asymmetry — intentional, do not "correct" it. save takes domain as a single string; load and search take an array. A memory is written to exactly one domain. The two-domain rule in §9.3 applies to retrieval only.

Multi-domain knowledge: if it fits several domains, issue several saves. There is no multi-domain write.

Do not save when:

  • The user didn't ask.
  • Nothing identifiable was decided or discovered → ask what to persist.
  • It's a personal preference — Cerebro is organizational.
  • A near-duplicate may exist → run load mode:"list" first. If a match exists, that's an edit → cerebro_update.

Content fidelity (hard rule): if a draft was shown to the user before saving, content and title must be character-identical to what was displayed. Not a re-summary, paraphrase, or shortened version.

Continuations are new saves. An addition to an existing memory is a new save, optionally linked via parent_mid. Reach for update only to change the existing record itself.

After the call: confirm in natural language. Surface the returned mid only if a follow-up needs it.

Choosing a parent_mid

Link only the primary, canonical antecedent:

New memoryParent
DONE / PROGRESS / SHIPPEDthe original TODO
Bug-fix documentationthe bug or the delivered feature
Postmortemthe incident memory
Replacement / refinementthe design it supersedes
Correctionthe corrected memory
Next stepthe previous step

Omit when the relationship is loose ("related to," "same topic") or when several parents would apply — reference it textually in the body instead.

The parent must exist, be in the same organization, with no self-references. When in doubt, omit — it can be attached later via update, inside the 15-day window.


4.5 cerebro_update — correct in place

Edits a record in place. Never creates.

Requires a mid. If you don't have one, don't guess — find it via Search or Load, confirm with the user it's the right record, then update.

Reactive only. Called when the user explicitly asks to change, fix, correct, relink, re-domain, expire, or deactivate an existing memory. Use save instead when the user wants to record something new, even if it continues an existing memory.

Editable fields: title, content, domain, knowledge_type_id, references, expires_at, parent_mid, status. At least one must be present or the call is rejected.

Partial updates: send only what changes; omitted fields are untouched.

// Content only — most common
{ "mid": "mid_a1b2c3d4", "content": "Corrected content goes here." }

// Several fields at once
{ "mid": "mid_a1b2c3d4", "title": "Updated title",
  "content": "Updated content.", "knowledge_type_id": "Tip" }

// Move domain — needs read_write on the TARGET
{ "mid": "mid_a1b2c3d4", "domain": "arpia_product" }

// Set an expiration
{ "mid": "mid_a1b2c3d4", "expires_at": "2026-12-31 00:00:00" }

// Clear an expiration — empty string, NOT null
{ "mid": "mid_a1b2c3d4", "expires_at": "" }

// Attach or relink a parent
{ "mid": "mid_a1b2c3d4", "parent_mid": "mid_1df233" }

// Detach parent, memory becomes a root — empty string, NOT null
{ "mid": "mid_a1b2c3d4", "parent_mid": "" }

// Replace the references list — full replace; [] is rejected
{ "mid": "mid_a1b2c3d4",
  "references": [ { "url": "https://docs.arpia.ai/...",
                    "title": "Original doc", "role": "context" } ] }

// Retire a memory
{ "mid": "mid_a1b2c3d4", "status": "INACTIVE" }

Semantics that bite

  • Clearing vs. changing. For expires_at and parent_mid, "" is an explicit clear; omitting leaves as-is. Use the empty string, not JSON null.
  • References are replaced, not merged. Sending references overwrites the entire list — to add one, resend the existing ones too. An empty array [] is rejected, so references cannot be cleared this way.
  • Editing window: 15 days from creation, no exceptions. Including parent_mid does not waive it. Past the window the record is frozen. On that failure, do not retry.
  • Moving domains requires read_write on the target domain, else rejection.
  • status accepts only APPROVED or INACTIVE.

Response fields: updated_fields reports what actually changed. references_saved, embedding_status, and parent_mid report "unchanged" when not part of the edit.

Correcting a memory past the 15-day window

The documented path: mark the record status: "INACTIVE", then carry the correction forward with a new save, parent_mid-linked to the retired record.

⚠️

Unresolved contradiction — verify before relying on this. The same source states that past 15 days the record is frozen, and that the recovery is an update setting status: "INACTIVE" on that frozen record. Both cannot be true unless status is exempt from the window, which is not documented. See Gap 7 in §12. Until a live test resolves it, assume retirement may fail.

Fallback requiring no update call:

  1. Write a new memory with the correct content.
  2. Set parent_mid to the memory being corrected — the "Correction" case in §4.4.
  3. State the supersession explicitly in the body: "Supersedes mid_xxxxxx, which stated X incorrectly."
  4. Log the bad mid for an administrator to retire out-of-band.

Step 3 is what prevents a future reader from acting on the stale record. It is not optional.

Invocability status

cerebro_update is declared in the MCP tool surface with the full contract above. However, a session verified Aug 14, 2026 exposed only five invocable tools — init, config, load, search, save — with update absent. Either the surface changed since, or that verification was session-specific.

Declared and invocable are not the same thing. Confirm with one live call before designing a workflow that depends on update. Tracked as Gap 1 in §12.


4.6 cerebro_config — domain structure

Creates domains, renames them, grants a user access.

When: the user wants to create or rename a domain, grant access, or references a domain that doesn't appear in their Init response.

Don't call: the domain already exists in Init (just use it); the user asks about domain contents (→ Load/Search); the user asks what domains exist (→ Init).

// Create a domain
{ "action": "create_domain", "domain_slug": "elconix", "domain_name": "ELCONIX" }

// Grant a user access — "domain" here is the NAME, not the slug
{ "action": "add_user_domain", "domain": "ELCONIX",
  "user": "target_username", "scope": "read_only | read_write" }

The schema's comment for the first action reads "Crate a Domain" — a typo in the source, not a distinct action.

Inference rules

  • domain_slug: lowercase, underscores, no spaces or special characters. "ELCONIX customer stuff"elconix; "backend team knowledge"backend_team.
  • domain_name: infer from context; ask only if genuinely ambiguous.
  • add_user_domain matches by name, not slug. The most common config error — passing the slug targets nothing. The caller must already hold read_write on that domain; if not, the call fails. Report the lack of permission; do not retry. Ask for scope if unspecified.
  • Rename: confirm the existing domain appears in Init first; if not, ask before calling.

Rename is documented but not specified. The tool description references an existing_domain field, but no rename payload appears in the schema. Do not construct one by analogy — confirm the exact shape first. Tracked as Gap 8 in §12.

Grants only — no revocation. There is no action to revoke access or change an existing scope (read_onlyread_write). Direct the user to an administrator; do not attempt a workaround. Treat every grant as permanent when deciding who to add.

CRITICAL — after every successful call, re-call cerebro_init. A new or renamed domain, or a newly granted access, will not appear in Load or Save until the session context refreshes. Config without an Init refresh = stale domain map. This is the only sanctioned second Init in a conversation. Under explicit-invocation-only posture (§4.1), tell the user the re-init is required and get the go-ahead.

After the call: confirm in natural language ("Domain 'elconix' is now available"). Don't expose the raw response. On rename, remind the user that prior saves or external tools referencing the old slug need updating.


5. Choosing the right tool

First relevant message
        │
        ▼
   cerebro_init ───────────────────────────── once per conversation
        │
        ├─ "I'm starting on X" / broad scope ──────────► load (Mode 1 or 3)
        ├─ "what's the rule for Y?" / one question ────► search
        ├─ "what exists in this domain?" /
        │   duplicate check before saving ─────────────► load (mode:"list")
        ├─ "save / remember this" ─────────────────────► save
        │     └─ continues an existing memory? ────────► save + parent_mid
        ├─ "fix / correct / retire that record" ───────► update (needs mid)
        └─ "create a domain" / "give Jane access" ─────► config ──► re-run init
Signal in the messageTool
"I'm starting on X" / "load the context for Y"Load (Mode 1 or 3)
"what's the rule for X?" / "what did we decide about Y?"Search
"what's in domain Z?" / duplicate checkLoad mode:"list"
"save / remember this"Save
"save this, it continues the earlier note"Save + parent_mid
"fix / correct / retire that record"Update (needs mid)
"create a domain" / "give Jane access"Config → then re-run Init
It's already in the conversationNeither — don't re-query
It's general, not organizational, knowledgeNeither — Cerebro is not Google

Load vs. Search, in one line: Load answers "give me everything for this scope"; Search answers "answer this question."

Save vs. Update, in one line: Save creates — including follow-ups and continuations; Update changes an existing record in place.


6. Canonical workflows

A. Scoped work session

  1. cerebro_init
  2. Validate the user's scope against domain
  3. cerebro_load — Mode 1, ≥2 domains + query
  4. Work; cerebro_search for anything outside the loaded scope
  5. On explicit request, cerebro_save

B. Safe save into an unfamiliar domain

  1. cerebro_init
  2. cerebro_load with mode:"list" on the target scope (≥2 domains)
  3. Duplicate found? → cerebro_update. None? → cerebro_save
  4. Confirm in natural language

C. Correcting an existing memory

  1. cerebro_search or cerebro_load to obtain the mid
  2. Confirm with the user it's the right record
  3. Within 15 days of creation? → cerebro_update. Done.
  4. Past 15 days? → the edit is rejected. Do not retry.
  5. Attempt cerebro_update with status: "INACTIVE"may also be rejected; see Gap 7
  6. cerebro_save a new memory carrying the correction, parent_mid-linked to the old record, supersession stated in the body

D. New domain

  1. cerebro_configcreate_domain
  2. cerebro_init again — mandatory refresh
  3. cerebro_save into the now-visible domain

7. Rules for agents

RuleApplies to
Call Init once, first; never twice except after a successful ConfigInit
Never guess a domain, type, or mid — ask the userAll
Don't re-query knowledge already in the conversationLoad, Search
Never dump raw Init or Load output to the userInit, Load
Do present Search's answer and attribution block verbatimSearch
Save and Update are reactive only — never infer intentSave, Update
Displayed draft text must be sent character-identicalSave, Update
Cerebro is organizational; personal preferences don't belongSave
Never pass exactly one domainLoad, Search
Re-run Init after any successful ConfigConfig

8. Failure modes

ConditionBehaviorCorrect response
mid present in a save payloadRejectedReroute to update
update without a midRejectedFind the record first via Search/Load
update with no editable fieldRejectedInclude at least one editable field
Memory older than 15 daysEdit rejected, record frozenDon't retry — attempt INACTIVE (Gap 7) + new Save
status other than APPROVED/INACTIVERejectedUse one of the two
references: [] on updateRejectedReferences can't be cleared this way
null instead of "" to clear a fieldNot the documented clearUse the empty string
Domain move without read_write on targetRejectedTell the user; don't work around it
add_user_domain without caller read_writeFailsReport lack of permission; don't retry
add_user_domain given a slug instead of a nameTargets nothingPass the domain name
Revoke access / change existing scopeNo such actionRefer to an administrator
Search returns nothingEmpty resultSay Cerebro has no knowledge yet; offer to save what emerges
Requested domain not in InitN/AAsk the user, or config to create it
Malformed payload of any kindRuntime error, not schema rejectionadditionalProperties: true — no MCP-layer validation

9. Known defects

Everything in this section is empirical. None of it appears in the schema documentation.

9.1 The confidence trap

The most consequential finding from the Oria evaluations, August 2026: models learn institutional structure accurately and then fabricate specific details — CVE IDs, metrics, dates — without flagging that they are extrapolating.

The risk isn't that the model doesn't know. It's that it sounds exactly as confident when it knows as when it invents. In an executive decision-making context, that is worse than having no memory at all: unaided, a reader discounts an unsourced claim. Wrapped in institutional structure, the same claim reads as retrieved fact.

Mitigation:

  • Verify every numeric value or identifier against the source memory — load by mid.
  • Prefer search over load when attribution matters; only search returns the contributing-memories block.
  • Treat any response without an attribution block as unverified.
  • This applies to this document. An earlier revision asserted that mid values are always 8 hex characters — an inference from a single example, stated as fact, inside the section warning against exactly that. Where a figure here carries no source or verification date, treat it as unverified.

9.2 Empty memories and phantom duplicates

Memories marked [HIDDEN — EMPTY] or [HIDDEN — DRAFT DUPLICATE] remain retrievable by search. Structure present, substance absent — this feeds §9.1 directly. Editing the title does not remove them from the retrieval index.

Mitigation, assuming update is invocable: within 15 days, correct the content or set expires_at. Past 15 days, attempt status: "INACTIVE" — subject to Gap 7.

Open question: it is not documented whether INACTIVE removes a memory from the retrieval index or merely flags it. If it only flags, retirement doesn't solve this and the phantom stays searchable. Tracked as Gap 9.

Prevention beats all of the above: set expires_at at creation for anything provisional, and run load mode:"list" before every save.

9.3 Domain-scoping defect

Confirmed: when exactly one domain is passed, the global domain is silently excluded — meaning the organization-wide immutable rules are not applied, in what is otherwise the most natural way to call the API.

Mitigation until fixed: always pass ≥2 domains, or omit domain entirely. Every example in §4 follows that shape. Note that the schema documentation's own examples use single-element arrays — following them literally reproduces the defect.

9.4 Fact inversion in the synthesis layer

cerebro_search has produced fact inversions — asserting the opposite of what the source memory says — in its synthesis layer. This is why the attribution block matters: it is not decorative, it is the verification mechanism. Trimming it for brevity removes the only signal that would catch an inversion.


10. ARPIA domain map

As of Aug 14, 2026 — not re-verified since. Confirm against cerebro_init before relying on it.

DomainUsersPurpose
global(all)Organizational immutable rules
personalindividualUser's own space
arpia_oria6Oria agent — evaluations, defects, design
arpia_core_support5Core platform support
arpia_git3Repository and versioning practices
arpia_product3Product knowledge
guia_de_cerebro2Cerebro usage documentation
csf_risk_governance_outcomes2NIST CSF 2.0, KEV, risk outcomes
arpia_ciso1Security governance
arpia_compliance1ISO 27001 / 42001, SOC 2
arpia_docs1Platform documentation
arpia_feature_updates1Product changes
test2Testing
new_action_test2Testing

10.1 Governance observation

Four high-value domains have a single user (arpia_ciso, arpia_compliance, arpia_docs, arpia_feature_updates). Cerebro's stated purpose is institutional memory; a domain with a single reader is personal memory carrying infrastructure overhead.

This is a bus-factor risk in the most sensitive domains. arpia_compliance in particular should have a second reader — certification knowledge is exactly what an auditor asks for when the person who loaded it is unavailable.

Explicit trade-off: widening access to arpia_ciso exposes unremediated findings to more people. The mitigation isn't to close the domain, it's to split it — open findings in a restricted domain, control and process knowledge in a shared one.

Compounding factor: because access can be granted but never revoked (§4.6), the split must happen before the second reader is added. Adding a reader to arpia_ciso as it stands is a one-way door.


11. Global immutable rules in force

Loaded automatically on every Init. They apply across all domains regardless of working scope — provided the call did not pass exactly one domain (§9.3).

11.1 Backup handling — mid_3d734797

Every backup goes to .backups/ at the project root, chmod 700 on the directory and 600 on files, with .backups/ in .gitignore. Never file.php.bak next to the original. Backup lookups are performed first and only in .backups/.

11.2 npm install prohibition — mid_26ee9c33

npm install / npm i / npm update / npm ci without flags are prohibited. Hierarchy of alternatives: pnpm (--ignore-scripts) → yarn berry (enableScripts:false) → bun (--ignore-scripts) → npm ci --ignore-scripts only as a last resort. Context: the TeamPCP campaign / "Mini Shai-Hulud" worm, March–May 2026, including the first documented attack carrying valid SLSA Build L3 attestations.

Implication for agents: if asked to run a prohibited command, reject the command as formulated, cite the rule, and propose the alternative appropriate to the lockfile present.


12. Open gaps

#GapImpactStatus
1update declared in schema but absent from the Aug 14 verified sessionWhether any memory is correctable from the agent is currently unknownNeeds one live call
2No domain access revocationAccess can only be granted, never removed; every grant is a one-way doorOpen — requires admin
3Scoping excludes global with exactly 1 domainImmutable rules not applied. Schema examples reproduce the defectConfirmed
4Fact inversion in search synthesisExecutive decision riskConfirmed
5Empty memories retrievableFeeds detail fabrication. Fixable within 15 days if Gap 1 resolves favorablyOpen
6Bus factor in single-user domainsCompliance/CISO continuity — blocked behind Gap 2, since the domain split must precede any new grantNot addressed
715-day freeze vs. INACTIVE recovery contradictionThe documented recovery from an expired edit window is itself an update on a frozen record. If retirement fails, expired-window memories are permanently uncorrectable and permanently searchableNeeds one live test
8Rename references an existing_domain field with no documented payloadRenames cannot be performed safely; slug drift accumulatesOpen
9Unknown whether INACTIVE removes a memory from retrievalIf it only flags, retirement does not remediate Gap 5Needs one live test
10No MCP-layer schema validation (additionalProperties: true)All malformed payloads fail at runtime; no early rejectionBy design — behave accordingly


Did this page help you?