Inspiration
Incident response tools are good at storing what happened. They're much worse at remembering what worked. Every postmortem gets written, filed, and mostly forgotten — the next on-call engineer facing a similar outage starts from zero, even if the team solved this exact problem three months ago.
I wanted to build something that treats that gap as an architecture problem, not a search problem: not "can you find the old postmortem," but "can the system itself get measurably smarter as outcomes accumulate." That's what pulled me toward the hackathon's framing of agentic memory — not memory as a chat log, but memory as a first-class pipeline: experience → reflection → knowledge → strategy.
What it does
Tendwa is a compound memory engine. When an incident happens, it's recorded as a raw experience. Periodically, a reflection process clusters related experiences and outcomes and asks: what's the durable pattern here? That produces knowledge — a confidence-weighted statement, linked back to the specific experiences that support it, not just a black-box vector.
When a new incident comes in, Tendwa retrieves relevant knowledge (not raw logs) and an agent reasons over it to produce a recommendation — and critically, it shows which past incidents it's grounded in, not just an answer. Once the outcome is recorded, that feeds back into the next reflection run, reinforcing or updating what the system knows. The loop closes.
The demo uses incident response as the concrete domain, but the engine itself has zero knowledge of "incidents" — that separation is deliberate, so the same memory pipeline could sit under a different domain adapter entirely.
How we built it
- CockroachDB Cloud for everything persistent, including
VECTORcolumns with distributed vector indexing for the hybrid structured + semantic retrieval that pulls relevant knowledge and experiences. - A hand-rolled agent loop (retrieve → reason → propose → observe) — deliberately no orchestration framework, because the loop is simple enough that a framework would add indirection without adding capability.
- A strict engine/domain separation:
packages/enginehas zero references to "incident" anywhere in it; all domain-specific logic lives in a thindomains/incident-responseadapter. - A junction table (
knowledge_evidence) linking every knowledge row back to the specific experiences that support it via a plain foreign-key join — so provenance is queryable, not just implied. - An "LLM proposes, code validates" pattern throughout: the model never writes or executes SQL directly. It returns a structured proposal; application code validates it before anything touches the database, and every reflection run logs both the proposal and what was actually applied.
- Voyage AI embeddings (
voyage-3.5, 1024-dim) for the vectors, Claude for agent reasoning and reflection. - An AWS Lambda wrapper around the reflection job, meant to run on a schedule — separate from the on-demand, per-incident analysis a user triggers directly, since those are genuinely different kinds of operations: one is event-driven, one is background maintenance.
- CockroachDB's Managed MCP Server during development, for live schema inspection — useful given how actively CockroachDB's own vector indexing syntax is still evolving, so we verified against the live cluster rather than trusting cached examples.
- A thin Fastify API and a minimal Next.js dashboard, both intentionally kept free of business logic — the pipeline itself lives in the engine, not scattered across the application layer.
Challenges we ran into
- CockroachDB's vector indexing is its own thing, not pgvector. Even though the wire protocol is Postgres-compatible, copying pgvector syntax (
ivfflat, HNSW tuning params) would have been actively wrong. We treated this as a "confirm against live docs every time" rule rather than trusting memory. - A subtle type-parsing bug: the
pgdriver's default OID 20 (INT8) parser didn't match what we needed, caught and fixed at the root rather than patched around downstream. - Rate limits under deadline pressure. Voyage AI's free tier caps out at 3 requests/minute, and reflection makes at least one embedding call per experience cluster (plus another if it decides to create new knowledge) — with several clusters in a single run, that's enough to hit the limit mid-job. Rather than treating that as a bug to route around, we leaned on a design decision made earlier in the build: the reflection job checkpoints its progress in
agent_state, so a run that errors out from a rate limit can simply be re-run with the exact same command — it resumes from where it stopped instead of restarting or duplicating work. - Getting the crash-resume behavior right in the first place. Reflection involves multiple sequential LLM calls; we needed the job to be safely resumable if it failed partway through, not just fast when everything went well. That decision ended up paying for itself directly when the rate-limit issue above showed up late in the build.
- Resisting scope creep on the frontend. It was tempting to build a full incident-management UI; we kept it to exactly three screens and one job — make the memory pipeline visible, not build a product.
Accomplishments that we're proud of
- A real engine/domain boundary that isn't just aspirational —
packages/enginegenuinely has no domain-specific code in it. - Evidence isn't decorative. Every recommendation the agent gives can be traced back, via a real database join, to the specific past incidents behind it.
- The reflection job is crash-safe and resumable against a live CockroachDB cluster, not just in theory — and it proved itself under real rate-limit pressure during the build, not just in a test.
- A working end-to-end loop: submit an incident → get a grounded recommendation → record an outcome → have it eligible for the next reflection run — all working against real infrastructure, not mocked for the demo.
What we learned
Building the "LLM proposes, code validates" boundary changed how I think about agent reliability — once the model's output is just a structured proposal that application code validates before it ever touches the database, most of the scary failure modes of agentic systems stop being scary. The model can be wrong; it just can't be destructive.
The engine/domain split taught a similar lesson at the architecture level: designing reusable AI infrastructure means being willing to leave a feature undone in the generic engine rather than sneaking in a shortcut that only works for one domain. It's slower up front and pays for itself the moment you imagine a second adapter.
What's next for Tendwa
- Extending beyond a single domain adapter — proving the engine/domain separation holds by building a second adapter (e.g. customer support or on-call triage) without touching
packages/engine. - Keyset pagination and richer filtering on the knowledge and experience lists as usage grows past demo scale.
- Surfacing evidence counts directly on knowledge cards in the dashboard, not just in the API response.
- A real-time reflection status view, so users can see when the system last learned something new — deliberately cut from this build to protect the deadline.
Built With
- amazon-web-services
- aws-lambda
- claude-api
- cockroachdb-cloud
- fastify
- mcp
- next.js
- node.js
- pnpm
- postgresql
- react
- typescript
- vitest
- voyage-api
Log in or sign up for Devpost to join the conversation.