{"slug": "what-building-a-bot-with-real-memory-taught-me-about-storage-that-cannot-be", "title": "What building a bot with real memory taught me about storage that cannot be edited", "summary": "A developer building a memory-persistent Telegram support bot for the Walrus Sessions 8 hackathon found that the hardest problems were in the storage layer, not the language model. Using DeepSeek for inference and Walrus for append-only storage, the developer documented three failure modes: asynchronous writes that return a job id before data is readable, reads that intermittently return empty for populated namespaces, and second-precision timestamps that caused tied lines to be folded unpredictably, silently dropping a reminder. The fixes — an outbox that confirms writes via read-back, growing retry waits, and deterministic tie-breaking on line metadata — are demonstrated by a repo script that prints before-and-after output on identical input.", "body_md": "A support bot asks for your order number for the third time. A companion app has\n\nno idea what you told it yesterday. Most of us have met a chatbot that forgets,\n\nand the usual fix is a chat history table.\n\nI have been building one that does not forget, for the Walrus Sessions 8\n\nhackathon. The theme is chatbots that remember, and the memory lives on Walrus\n\nrather than in a database I run. The bot talks on Telegram, uses DeepSeek for the\n\nlanguage model, and stores everything it knows as lines of text with tags on\n\nthem.\n\nI assumed the language model would be the hard part. It was not. The hard part\n\nwas timing, and the ways a storage layer can tell you something that is not true.\n\n**Memory you cannot edit**\n\nThere is no update and no delete. A line, once written, stays. So the bot does\n\nnot change its mind by editing anything. When you finish a task, it writes a\n\nsecond line saying the task is done, and the reader folds every line about that\n\ntask down to the newest one.\n\nThat works, but only if the fold is predictable. Mine was not.\n\n**A write is a job, not a write**\n\nThe call that stores a line returns in about a second. It does not mean the line\n\nis stored. What comes back is a job id and a status of running.\n\nI watched one job to the end. It sat pending for the first 1.2 seconds, went to\n\nrunning at 3.2 seconds, and only reached uploaded at 26.1 seconds. That last\n\nstate is the first one where the line can be read back.\n\nSo a client on this has to keep its own outbox, hold each line until a read\n\nproves the world can see it, and decide what \"accepted\" means. In my case a\n\nfailed job and a slow job look identical for the first four minutes, which is a\n\ngap I cannot close from the client side.\n\n**A read that returns nothing**\n\nThis one cost me a user's trust in a way I did not notice at first. The bot told\n\nsomeone their memory was empty. It was not. That namespace held three lines,\n\nwritten half an hour earlier.\n\nI asked the same question again 3 seconds later and got all three back. The\n\nretriever sometimes answers an empty list for a query that matches, and then\n\nmatches. My first retry waited 0.6 seconds, which sits inside that gap, so the\n\nread gave up and the bot reported an empty memory.\n\nThe waits now grow, 0.6 then 2 then 3 seconds, and there is a test that checks a\n\nnamespace which really is empty still stops rather than paying all of that on\n\nevery message.\n\n**Two lines, one second, one lost reminder**\n\nTimestamps were precise to the second. Every line the model produces in one turn\n\nis stamped from a single clock reading, so a turn that mentions the same task\n\ntwice produces two lines with identical timestamps.\n\nThe fold keeps the newest line, and with two lines tied there was nothing left to\n\nsort by except the order the storage layer returned them in. That order is not\n\nguaranteed. One of the two orders kept the line without the reminder time, so a\n\nreminder vanished and nothing on screen said so.\n\nI found it by accident, when the same deadline appeared twice with different\n\nwording. The fix was to stop guessing: the tie is now broken by what a line knows\n\nbefore it is broken by the text of the line, so both orders produce the same\n\nanswer.\n\n**What changed**\n\nWith those three fixed, the bot does what it says it does. Reminders survive a\n\nrestart, a finished task stays finished, and when someone asks what it knows it\n\nanswers from the stored lines rather than from a cached guess.\n\nThere is a script in the repo that prints the before and after side by side on\n\nthe same input, because a paragraph claiming a bug is fixed is worth less than\n\ntwo printed lines that show it.\n\n**The session**\n\nIt is called Walrus Sessions 8: Chatbots That Remember, and it runs until October 9. \n\nIf you have wanted to try agent memory that is not a vector store on your own\n\nmachine, it is a good excuse to build something small.\n\nRegister directly on DeepSurge:\n\n[https://www.deepsurge.xyz/hackathons/c0141a4a-21be-4009-bc63-7c168608c849](https://www.deepsurge.xyz/hackathons/c0141a4a-21be-4009-bc63-7c168608c849)", "url": "https://wpnews.pro/news/what-building-a-bot-with-real-memory-taught-me-about-storage-that-cannot-be", "canonical_source": "https://dev.to/ar1madotsui/what-building-a-bot-with-real-memory-taught-me-about-storage-that-cannot-be-edited-5c9g", "published_at": "2026-09-23 20:58:19+00:00", "updated_at": "2026-09-23 21:28:46.227080+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-tools", "large-language-models"], "entities": ["Walrus", "DeepSeek", "Telegram", "Walrus Sessions 8", "DeepSurge"], "alternates": {"html": "https://wpnews.pro/news/what-building-a-bot-with-real-memory-taught-me-about-storage-that-cannot-be", "markdown": "https://wpnews.pro/news/what-building-a-bot-with-real-memory-taught-me-about-storage-that-cannot-be.md", "text": "https://wpnews.pro/news/what-building-a-bot-with-real-memory-taught-me-about-storage-that-cannot-be.txt", "jsonld": "https://wpnews.pro/news/what-building-a-bot-with-real-memory-taught-me-about-storage-that-cannot-be.jsonld"}}