nyxcore-systems
6 min read

Building a Workflow's Brain: Crafting a Smarter Memory System

We just wrapped up a critical phase in building our project's memory system – a suite of features designed to make workflows intelligently leverage past insights. Dive into the journey, the fixes, and the lessons learned.

Next.jstRPCReactUI/UXWorkflow AutomationDebuggingFrontendBackendPostgreSQLpgvector

Just rolled off an intense, but incredibly rewarding, development session. The goal? To embed a robust "memory system" directly into our workflow creation process. Imagine a system that doesn't just execute steps, but learns from every project, every decision, every insight, making future workflows inherently smarter. That's what we're building, and this session marked a huge leap forward.

We've just completed the core UI and integration for this project-workflow memory system. From selecting past insights to ensuring they're correctly saved and presented, the entire end-to-end flow is now functional and tested. Let's break down how we got here, the hurdles we overcame, and what's next on the horizon.

The Vision: A Workflow with Memory

At the heart of this system is the idea of injecting relevant past insights – our "memories" – directly into new workflow steps. This required a new UI component, backend plumbing, and careful integration into existing features.

Introducing the MemoryPicker

Our new MemoryPicker component (src/components/workflow/memory-picker.tsx) is the gateway to this intelligence. It's designed to be intuitive and powerful:

  • Search & Filter: A search bar and category filter chips allow users to quickly narrow down relevant insights.
  • Visual Cues: Severity-colored badges provide at-a-glance context for each memory.
  • Expandable Details: Users can dive into the full detail and suggestions of an insight without leaving the picker.
  • Injection Preview: Crucially, it shows a preview of how the selected {{memory}} will be injected into the workflow prompt, ensuring clarity.

This component now seamlessly integrates into our workflow creation page (src/app/(dashboard)/dashboard/workflows/new/page.tsx), allowing users to select multiple memories that are then wired into the create mutation (thanks to a foresightful memoryIds field already in our schema!). The picker itself queries our memory.listInsights tRPC endpoint, keeping our data fetching clean and typesafe.

Saving the Past: Ensuring Insights Stick

What good is memory if you can't save new ones? Our SaveInsightsDialog is where key takeaways from a completed workflow step are captured. However, this is where we hit our first significant snag.

Lesson Learned: Don't Underestimate undefined

The Problem: After completing a review step in a workflow and clicking "Approve & Continue," the SaveInsightsDialog simply didn't appear. The workflow immediately advanced to the next step, leaving valuable insights unsaved.

The Root Cause: Our extractKeyPoints() function, responsible for parsing insights from the workflow output, was doing its job, but the resulting key points had no action field – it was undefined. Our filter in save-insights-dialog.tsx (and a duplicate in workflows/[id]/page.tsx) was too strict: kp.action === "keep" || kp.action === "edit". Since undefined isn't "keep" or "edit", all key points were silently filtered out, leading to an empty dialog that never rendered.

The Fix: A simple but critical adjustment to the filter logic:

typescript
// Old (buggy) filter:
// keyPoints.filter(kp => kp.action === "keep" || kp.action === "edit");

// New (robust) filter:
keyPoints.filter(kp => !kp.action || kp.action === "keep" || kp.action === "edit");

By explicitly checking for !kp.action (which covers undefined, null, or an empty string, though undefined was the culprit here), we ensured that un-actioned key points were still considered saveable. This small change unblocked the entire insight-saving flow.

Takeaway: When filtering data, especially from dynamic sources or different processing stages, always consider edge cases like undefined or null values. A slightly more permissive filter can prevent frustrating silent failures.

Streamlining the Backend: Consolidation is Key

During this process, we also noticed a duplicate saveInsights mutation in our src/server/trpc/routers/workflows.ts. This was quickly addressed by removing the redundant one and consolidating calls to trpc.memory.saveInsights, which had richer validation. This ensures a single source of truth for saving insights and reduces potential for inconsistencies.

A Tidy UI: Collapsible Context Sections

To manage the increasing complexity of our workflow creation page, we introduced a new CollapsibleSection component. This now wraps our Consolidations, Personas, Docs, and Memory Insights sections. By collapsing them by default, we've significantly cleaned up the UI, making the page less overwhelming while still providing quick access to details via a header Badge showing the selection count. It's a small UX win that makes a big difference.

The Proof is in the Pudding: End-to-End Validation

The real moment of truth came with the full end-to-end test:

  1. Created a new workflow.
  2. Ran through Analyze and Design Features steps.
  3. Completed the Review step, where 10 key points were extracted.
  4. Approved the review, triggering the now-fixed SaveInsightsDialog.
  5. Saved the 10 insights, which were correctly stored in our workflow_insights table.
  6. Created a new workflow, and verified that the MemoryPicker on the create page now proudly displayed those 10 newly saved insights.

Success! The entire cycle, from creation to insight extraction, saving, and subsequent retrieval, is now fully operational.

Looking Ahead: Refining and Expanding

While we've hit a major milestone, the journey continues. Here are some immediate next steps and future considerations:

Lesson Learned: Guarding Against Duplicate Saves

The Problem: During testing, I noticed that clicking the "Save insights" button multiple times led to duplicate records being created in the database. I had to manually clean up 30 records (3x duplicates) with SQL.

The Fix (Planned): This is a classic race condition or UI/UX oversight. The immediate fix will involve either:

  1. Disabling the save button after the first click to prevent subsequent submissions.
  2. Implementing mutation deduping on the backend to ignore identical concurrent requests or ensure idempotency.

Takeaway: For any action that modifies data, especially involving user interaction, always implement defensive measures against accidental duplicate submissions.


Immediate Next Steps:

  • Duplicate Save Guard: Implement the disabled guard on the save button or mutation deduping mentioned above.
  • Phase 2: Vector Search Integration: Recreate our Docker Postgres with the pgvector/pgvector:pg16 image, install the pgvector extension, and add the embedding column. This will unlock powerful vector similarity search for our memories, making the MemoryPicker even smarter.
  • Template Resolution Test: Create a workflow with selected memory insights and verify that the {{memory}} template variable is correctly resolved and injected into the step execution prompt.
  • Project-Scoped Insight Filtering: Enhance the MemoryPicker to filter insights by projectId, ensuring users only see memories relevant to their current project.
  • Auto-Pull Memory Files from Git: A user-requested feature: develop a system to query Git for new commits and automatically pull .memory/ files, keeping our memory database constantly updated with the latest project knowledge.

This session has laid a solid foundation for a truly intelligent workflow system. We're moving from just automating tasks to building workflows that learn, adapt, and leverage accumulated wisdom. The path ahead promises even more exciting challenges, especially as we dive deeper into vector embeddings and AI-driven memory retrieval. Stay tuned!

json
{
  "thingsDone": [
    "MemoryPicker UI component developed and integrated",
    "SaveInsightsDialog bug fix for undefined 'action' field",
    "Consolidated saveInsights tRPC mutation",
    "Implemented collapsible context sections in workflow creation UI",
    "Completed full end-to-end testing of memory system flow"
  ],
  "pains": [
    "SaveInsightsDialog not appearing due to strict filter on 'action' field",
    "Duplicate records created on multiple clicks of the save button"
  ],
  "successes": [
    "All memory system UI complete and tested end-to-end",
    "Critical bug in insight saving logic resolved",
    "Improved UI organization with collapsible sections",
    "Validated entire workflow-to-memory-to-retrieval cycle"
  ],
  "techStack": [
    "Next.js",
    "React",
    "TypeScript",
    "tRPC",
    "PostgreSQL",
    "Docker",
    "pgvector (planned)"
  ]
}