MyCivicDuty is a civic-information platform — federal, state, and local representatives, voting records, campaign finance, bills, city scorecards, election results. It's a Next.js frontend pulling live data from OpenStates, Congress.gov, FEC, and a handful of other sources. More than a dozen distinct surfaces. We were building it fast.
Fast, here, meant parallel agents. One agent doing a UX pass on the Feed, another on the Bills view, another on the Rep profile pages, all running simultaneously. Each one starting with a detailed design brief: one search input per screen, accent green appears once per region, stat claims on the landing verified before display, source and date attribution at the metric level for every scorecard number.
The brief was good. The problem was that it existed only in context windows — and context windows don't share.
The first obvious problem: state drift
An agent would find something broken, fix it, and log a confirmation. Then another agent would run a pass over the same surface an hour later, not know the fix had happened, re-flag the same issue as still open, and — in the worst case — re-fix it in a way that stepped on the first fix.
We called this state drift. It's not a bug in the agents; it's what happens when agents don't have shared memory. Each one starts fresh. Each one reinvestigates everything. You end up paying the full token cost of re-deriving knowledge that was already derived.
Memory Stack is what we were building. So we used it. Every significant finding from every pass went into the project as a logged memory — a persistent retrievable record, no Slack threads or PR comments involved. By the end of the day the project had 84 memories: 77 flagged risks, 5 insights, 1 decision record, 1 reference. A subsequent agent could pull that context, see what was already verified fixed, and skip those surfaces entirely.
Memory ID 176 — logged at the end of a pre-deploy review pass on June 4 — is a good example of what this looks like in practice. It's a status snapshot: items 1–5 still broken (stale roster entries, a data inflation bug in the FEC money tab, wrong party chips on local reps), items 6 and 7 confirmed partially fixed. Without that entry, the next agent re-investigates all seven. With it, the next agent starts at "two things to verify" instead of "seven things unknown."
The dedup incident
Here's where it got worse before it got better.
During one of the QA passes, a fleet of agents filed 32 near-duplicate "404 missing page" findings. Same issue, detected across slightly different surfaces, logged 32 separate times — into the wrong project. Not a catastrophic failure, but the kind of thing that makes you look at your tooling and ask what went wrong.
Two things, it turned out.
First, the agents had no write-time deduplication. They each independently encountered something that looked like a new finding, logged it, and moved on. By the time we had 32 entries describing the same class of problem in slightly different words, the signal was buried under noise.
Second, the attribution was loose. The agents were logging against a project scope but didn't have strong enough context to confirm which project. So the findings landed in the wrong bucket and weren't immediately surfaceable for the people who needed them.
Both of those are now fixed. Memory Stack does write-time dedup before committing: if what you're logging is semantically close enough to an existing entry, it flags the near-duplicate rather than creating a new one. And attribution is now tied to verified project context, not just whatever the agent inferred from the prompt.
The 32-duplicate incident is genuinely why those two things exist. It's one of those bugs that felt painful at the time and turned out to be useful — it was a concrete demonstration of exactly the problem we were supposed to be solving.
What the brief became
The design brief had rules. But over the course of the build, those rules stopped being a document and became a living log.
The "one search input per screen" rule surfaced violations on three separate surfaces — Feed, Decisions, and a rep profile page — each one logged independently by a different agent. By the time a third agent hit the same issue, it wasn't re-discovering it. It was pulling a known pattern with confirmed violations and known fix status.
That's the difference between context that lives in one session and context that propagates. The brief was still the brief. But every agent pass added specificity: here's where the rule holds, here's where it doesn't, here's what fixing it actually required.
At 84 memories, the MyCivicDuty project captured every surface that had been audited, the status of each finding, and the confirmed passes as well as the failures. Confirmed passes matter. When an agent already knows the top chrome passed its pre-deploy checks and the Meetings empty state satisfies rules 3 and 8, it doesn't need to re-verify those. It focuses on the open items.
What it changed
Two concrete things came out of the mycivicduty build that are now part of how Memory Stack works.
Write-time dedup: before a memory is committed, it's checked for semantic proximity to existing entries in scope. If there's a near-duplicate, you get a flag and a choice. This killed the 32-duplicate problem at the source.
Tighter attribution: project scope is now confirmed context, not inferred context. Agents working inside a project boundary log against that boundary with enough specificity that misattribution is caught before it becomes noise.
Both of those came from a real build doing something agents shouldn't have been able to do in the first place. The civic-tech product was the site; the memory hygiene problem was what we were actually studying.
Memory Stack is available at memory-stack.com. The MyCivicDuty build described here ran as a dogfood project on Memory Stack's MCP integration.
Give your AI tools persistent memory
Memory Stack gives every AI tool you use — Claude, Cursor, ChatGPT — access to the same shared context. No download, no key paste, no config file.
Start for free →