Full disclosure up front: I am a founder (Yempik), and this is about something I built and open-sourced. If that is not your thing, no hard feeling...
For further actions, you may consider blocking this person and/or reporting abuse
Memory architecture is the load-bearing piece, and most setups conflate two stores that should be split — episodic context vs decision lifecycle. What shape did yours land on?
You named the exact split I had to learn the hard way, because my first version conflated them and it rotted. The shape I landed on is two stores with two different write policies, not just two folders. The context area (who we are, positioning, tone) is allowed to be lossy: it gets rewritten and consolidated, because losing a paraphrase of it costs nothing and I can re-derive it. The decision lifecycle is append-only and never edited in place, because a decision's value is the reason and the supersession trail, and a summary throws those away first. The one that surprised me was assumptions: they sit between the two, so they get their own file, otherwise they get read as settled facts on one side or as commitments on the other and both are wrong. The hard part turned out to be the read, not the write: once the decision log grows, how do you keep a superseded decision from being re-surfaced as live? Do you re-read the whole lifecycle each session, or index it and trust the index?
The two-write-policies framing is sharper than the two-stores one I'd been working with — losing a paraphrase of context costs nothing, losing the reason or the supersession trail of a decision costs everything, and treating them as the same kind of „store" hides that. Adopting that distinction.
Assumptions as their own file is the move I hadn't made and probably needed to. Treating them as soft-decisions or hardened-context both miss — they're conditional commitments, lighter than a locked decision, heavier than positioning. Going to draft a separate lane for them.
On the read problem: I've been hybridizing — index for normal queries (status-filtered decision list), full lifecycle re-read only when the hook detects a new prompt contradicting an existing locked entry. The index has to be cheap to invalidate and the re-read has to be triggered, not periodic, or the operator pays the cost on every turn. Where it breaks: superseded entries with stale pointers to live ones can still surface during the contradiction check if the supersession chain isn't walked in full. Honest answer: that's where mine still has a leak.
The read is exactly where it leaks for me too, and walking the supersession chain at read time is the part I gave up on, because any early exit surfaces a dead entry as if it were live. What worked better was making 'live' a status I filter on instead of a chain I walk: the new entry records supersedes #X as a backward pointer written once, the old entry flips to status superseded at supersession time, and the read is just 'status is not superseded'. A dead entry cannot resurface as live because its own line says it is dead, so there is no chain to walk in full or to walk halfway. My leak is the other one you would expect: the status drifts from reality when a decision gets revoked in the real world and nobody writes the revocation, so the entry stays accepted while being dead in practice. I treat the contradiction check as the place to force that reconciliation, not just a re-read, because a new prompt fighting a locked entry is the strongest signal that the entry, not the prompt, might be stale. When your hook fires on a contradiction, how do you decide who wins, the locked entry or the new prompt that disagrees with it?
Hook flags, doesn't decide. The contradiction surface gets surfaced to me with both sides — the locked entry's claim plus its verifiable_by provenance, and the new prompt's framing of the conflict. Locked has presumption of validity, but only as a tiebreaker, not as an override. The operator authorizes one of four transitions: keep lock (prompt rejected), supersede lock (prompt becomes new entry), refine lock (claim narrowed to exclude the contradicting case), or escalate (open question, neither resolved).
Your reframing is the move I'd been missing: a contradicting prompt isn't an attack on the lock, it's the strongest stress test the lock has had since being authored. That's exactly when revocation drift gets exposed — the prompt is fighting an entry that real-world events already invalidated, and nobody captured it. Going to bias the hook UI toward making that the default question: „is the entry still true?" instead of „is the prompt wrong?"
The status-filter-instead-of-chain-walk approach is sharper than what I've got. I've been walking, and your point about early exits is exactly the failure mode. Stealing the design.
Hook flags, doesn't decide is the right call, and the four transitions map cleanly onto what I keep by hand. The gap I still see is that all four only fire when a prompt actually collides with the entry, so they are reactive. The case that bites me has no collision at all: an entry nobody contests for six weeks while the world moved on, so the hook never fires and it keeps reading as live. So I pair your contradiction trigger with a second, prompt-independent one that walks load-bearing entries by age, not by conflict, and asks your exact default question, is this still true, on the oldest ones. To your earlier point about the operator paying every turn, I keep that pass off the per-turn path: it batches into a boundary reconcile, not a per-prompt check, and only touches entries above an age threshold, so it stays bounded. The one I have not settled is your fourth transition: while an entry is escalated and neither side has won, what does it read as in the meantime? If it stays live it can still drive behavior while contested, if it goes dormant you have quietly dropped a real constraint during the deliberation window. Which way do you lean for that limbo, presumption-of-validity until resolved, or filtered-out until reconfirmed?