This document specifies the CodeCart round-trip protocol so that any AI product, agent framework, browser extension or script can read, write and stay compatible with a user's memory wallet. The protocol is deliberately tiny: it must survive being emitted by any LLM inside a plain chat reply.
- Carry block — what the user pastes into an AI chat: active cards + an instruction asking the model to emit updates.
codecartblock — what the model appends to its replies: memory updates in a fenced code block.- Wallet file — the exported JSON that holds all cards; the user's canonical, portable store.
[My personal memory — CodeCart wallet, <date>. Use it to personalize every answer.]
- (preference) Answer concisely, conclusion first
- (fact) I run a small online store
- (boundary) Never suggest options over my $200/month budget
---
<instruction asking the model to append a codecart block — see the app for canonical wording>
Card types: preference (how to answer), fact (stable info), status (current focus, expected to change), boundary (hard rule).
The model appends, at the end of a reply:
```codecart
+ <type> | <content>
~ <fragment copied from an outdated memory line> => <corrected content>
- <fragment of a memory line to retire>
```
+adds a card.<type>is one of the four types; if omitted (+ <content>), consumers should default tofact— but only inside a fenced block, to avoid false positives.~supersedes: the consumer finds the active card whose text contains<fragment>(case-insensitive), archives it, and adds the corrected card. This is what keeps wallets current instead of ever-growing.-retires a card without replacement (fenced block only).- Recommended model rules (already in the carry instruction): max 5 lines, only durable personal info, write contents in the user's language, omit the block when nothing qualifies.
Parsers should accept the lines outside a fence too, but only when an explicit <type> | prefix is present.
{
"version": 2,
"cards": [
{
"id": "m3k9x1ab2",
"type": "preference",
"text": "Answer concisely, conclusion first",
"status": "active",
"created": 1720300000000,
"updated": 1720300000000
}
]
}status is active or archived. Consumers must carry only active cards, and must merge imports by skipping duplicate text values (case-insensitive, trimmed).
import json
with open("codecart-wallet.json", encoding="utf-8") as f:
wallet = json.load(f)
carry = "\n".join(
f"- ({c['type']}) {c['text']}"
for c in wallet["cards"] if c["status"] == "active"
)
# Prepend `carry` to your system prompt; parse ```codecart``` blocks
# from model output with the grammar above and merge back.- The block tag is always the literal string
codecart. - Unknown card types must be treated as
fact, unknown JSON fields must be preserved on round-trip. - v1.3 legacy exports (
nodeswith+CLAIM/+ANCHORsemantics) map as:anchor → boundary,claim → fact,isSuperseded → archived.