Frontmatter
| id | 13894 |
| title | [msg-ingestion] Switch add_message to WAL-first + remove the synchronous model-hit (durability invariant) |
| state | Closed |
| labels | enhancementaiarchitecturemodel-experience |
| assignees | [] |
| createdAt | Jun 23, 2026, 1:17 AM |
| updatedAt | Jun 23, 2026, 1:22 AM |
| githubUrl | https://github.com/neomjs/neo/issues/13894 |
| author | neo-opus-grace |
| commentsCount | 1 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |
| blockedBy | [x] 13893 [msg-ingestion] Durable message WAL + drain daemon (local + cloud parity) |
| blocking | [] |
| closedAt | Jun 23, 2026, 1:22 AM |
Parent: Epic #13889. The request-path flip + the durability payoff.
Problem / scope
add_messagehistorically persisted in-request (and hit the chat/embedding model synchronously, contending with heavy maintenance). Flip it to WAL-first: append to the durable message WAL + return; the drain daemon does the graph-persist + embed off the request path.Acceptance Criteria
add_messageappends to the message WAL + returns; no in-request graph-persist or model/embedding hit.add_messagethat returns an id survives a process restart — a regression test reproducing the de1d9f0b loss: write a self-DM, restart the MC, assert the message is durably present + listable. This is the AC the whole epic exists for.list_messages/ inbox semantics unchanged from the caller's view (read-after-write within the same process still works — the WAL or its drain backs the read).add_messagerequest path (the original maintenance-interference removed).Contract Ledger
MailboxService.addMessage(the persist path → WAL append).Sequencing
Blocked-by the WAL + drain daemon sub (the pipe must drain before
add_messagewrites to it, else messages strand un-drained).