nyxcore-systems
7 min read

Unlocking Cross-Project Insights: Our Journey to AI-Powered Consolidation

We just shipped a powerful new consolidation feature, designed to gather scattered insights across projects, extract patterns with AI, and make them searchable and exportable. Dive into the technical journey, challenges, and triumphs.

AIPrismaTypeScriptNext.jstRPCFeature DevelopmentLessons LearnedDeveloper Productivity

Every developer knows the feeling: a brilliant insight, a critical lesson learned, or a clever solution tucked away in a project's documentation, a commit message, or a private note. Over time, these nuggets of wisdom get siloed, making it hard to leverage collective knowledge across your broader ecosystem of projects.

That's the problem we set out to solve with our latest feature: Cross-Project Consolidation.

Our goal was ambitious: build a system that can accumulate "letters to myself" or blog posts from various projects, apply AI to extract meaningful patterns, create a searchable overview, and even export these insights as prompt hints for future use. I'm thrilled to announce that this feature is now complete, committed (252204a), and ready for its public debut!

Let's dive into the technical deep end and explore how we brought this vision to life.

The Core Idea: From Scattered Notes to Structured Wisdom

Imagine you're working on multiple projects, each generating its own set of learnings. What if you could pull all these together and ask an AI to find common themes, recurring challenges, or successful strategies? That's the power of consolidation. It's about turning disparate data points into actionable intelligence.

At its heart, the system performs a few key functions:

  1. Aggregation: Collect content (like our internal "letters to myself" or project summaries) from selected projects.
  2. AI Pattern Extraction: Use an LLM to identify recurring patterns, themes, and key takeaways from the aggregated content.
  3. Searchable Overview: Present these patterns in an intuitive, searchable interface.
  4. Exportable Hints: Allow users to export these distilled insights as actionable prompts or documentation.

Behind the Scenes: A Technical Walkthrough

Building this feature touched nearly every part of our stack, from the database schema to the user interface.

Data Model with Prisma

We started by laying the groundwork in prisma/schema.prisma. We introduced two new models:

  • Consolidation: Represents a single consolidation run, linking to the user and tenant, and referencing the projects it aggregated.
  • ConsolidationPattern: Stores the individual patterns extracted by the AI, linked back to its parent Consolidation.

Crucially, we implemented tenant and user relations with cascade delete to ensure data integrity and proper cleanup when a user or tenant is removed.

prisma
// Example snippet (simplified)
model Consolidation {
  id        String    @id @default(cuid())
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  name      String
  status    String // e.g., "pending", "completed", "failed"
  tenantId  String
  userId    String
  user      User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  tenant    Tenant    @relation(fields: [tenantId], references: [id], onDelete: Cascade)
  patterns  ConsolidationPattern[]
  projects  Project[] // Many-to-many through a join table
}

model ConsolidationPattern {
  id              String        @id @default(cuid())
  createdAt       DateTime      @default(now())
  updatedAt       DateTime      @updatedAt
  patternText     String
  category        String?
  relevanceScore  Float?
  consolidationId String
  consolidation   Consolidation @relation(fields: [consolidationId], references: [id], onDelete: Cascade)
}

The Brain: AI Service with tRPC

The intelligence of the feature resides in src/server/services/consolidation-service.ts. This service handles the heavy lifting of interacting with our AI models:

  • extractPatterns(content: string): This function takes the aggregated text and sends it to our LLM. We're currently using a temperature of 0.2 for more consistent, less creative outputs, and we specifically prompt for structured JSON output to make parsing reliable.
  • generatePromptHints(patterns: ConsolidationPattern[]): Once patterns are extracted, this function formats them into useful markdown, categorized for easy consumption.

On the API front, src/server/trpc/routers/consolidation.ts exposes a full suite of endpoints:

  • Standard CRUD operations for consolidations.
  • generate and regenerate procedures, which are llmProcedure extensions, specifically designed to handle AI calls and their asynchronous nature.
  • patterns.search: A powerful endpoint for paginated and filterable searching of extracted patterns.
  • export: Endpoints to download patterns in markdown, JSON, or formatted prompt hints.
  • availableProjects and byProject for filtering and displaying relevant data.

This router was then seamlessly registered in src/server/trpc/router.ts.

The User Interface: Intuitive & Informative

A powerful backend needs an equally capable frontend. We integrated the consolidation feature across our application:

  • Sidebar Navigation: A new "Consolidation" link with a Layers icon was added to src/components/layout/sidebar.tsx for easy access.
  • Consolidation List Page (/dashboard/consolidation): This page provides an overview of all past consolidations, complete with status and pattern count badges.
  • New Consolidation Form (/dashboard/consolidation/new): A multi-step form allows users to select multiple projects for analysis, guiding them through the consolidation creation process with a sticky submit button for convenience.
  • Consolidation Detail Page (/dashboard/consolidation/[id]): This is the hub of the feature, featuring three distinct tabs:
    • Overview: Provides a summary and visual distribution bars of pattern categories.
    • Patterns: A searchable list of extracted patterns, complete with type filter chips and expandable cards for detailed views.
    • Export: Offers options to copy prompt hints to the clipboard, download markdown or JSON files, and preview the output.
  • Project Analysis Tab (/dashboard/projects/[id]): To integrate the feature into existing workflows, we added a fourth "Analysis" tab to individual project detail pages, showing consolidations relevant to that specific project.

Navigating the Rapids: Lessons Learned & Challenges

No development journey is without its bumps. Here are a few critical insights from this session:

The Peculiarities of Prisma's Json Type

One unexpected hurdle was working with Prisma's Json? fields. While TypeScript's Record<string, unknown> seems like a natural fit for JSON objects, it's not directly assignable to Prisma's Json? type. We quickly learned that for nullable JSON fields, you must explicitly cast to Prisma.InputJsonValue for non-null values or use Prisma.JsonNull for null. This is a crucial type safety detail that, if overlooked, can lead to runtime errors or unexpected behavior.

typescript
// Incorrect:
// data: { jsonField: myRecordObject }

// Correct:
import { Prisma } from '@prisma/client';

// For non-null JSON
data: { jsonField: myRecordObject as Prisma.InputJsonValue }

// For null JSON
data: { jsonField: Prisma.JsonNull }

The Limits of AI-Assisted Development

We heavily leverage AI agents in our development process. While they excel at simpler, well-defined tasks (like generating list pages or minor component additions), we hit a wall with more complex UI pages. Two background agents tasked with creating the ui-new-page and ui-detail-page for consolidation simply stalled and never wrote their files.

This reinforced a critical lesson: AI agents are powerful tools, but they have their limits, especially with highly complex, multi-component UIs requiring intricate state management and routing. For these larger tasks, manual intervention and a human developer's holistic understanding of the application architecture are still indispensable. It's a reminder to balance automation with direct development, especially for core features.

Embracing the Linter's Wisdom

Our linter proved to be an invaluable, albeit sometimes opinionated, teammate. It automatically fixed several issues that improved our codebase:

  • Removing an unused utils variable from trpc.useUtils().
  • Adding type="button" to filter chip buttons, preventing unintended form submissions.
  • Consistently using cn() for conditional class names, leading to cleaner Tailwind CSS usage.
  • Refactoring a PromptHintsPreview component to use useEffect with a cleanup function, ensuring proper resource management.

The takeaway? Don't fight the linter. Its suggestions often point to best practices and subtle bugs, leading to more robust and maintainable code. It's a constant learning companion.

What's Next?

With the core feature shipped and pushed to origin/main, our database ready for action, and TypeScript reporting 0 new errors (aside from one pre-existing Badge variant issue), we're now moving into the testing and refinement phase:

  1. End-to-End Testing: Verify that creating a consolidation from the UI correctly triggers AI extraction and stores patterns.
  2. Export Functionality: Test prompt hints copying to clipboard and markdown/JSON file downloads.
  3. Mobile Responsiveness: Ensure sticky buttons, filter chip wrapping, and pattern cards render correctly on smaller screens (e.g., 375px viewport).
  4. Workflow Integration: Test the flow from a project detail page's "Analysis" tab to initiating a new consolidation.
  5. Future Considerations: Explore integrating consolidation patterns into our workflow step types, and consider adding a global search across all patterns for even broader discoverability.
  6. Housekeeping: Fix that persistent pre-existing Badge variant error in the discussions page.

Conclusion

Building the Cross-Project Consolidation feature has been an exciting and insightful journey. We've leveraged AI to transform scattered information into structured knowledge, enhancing developer productivity and decision-making. The challenges we faced, from Prisma type quirks to AI agent limitations, only strengthened our approach and provided valuable lessons.

We're incredibly excited about the potential of this feature to help our users unlock deeper insights from their work. Stay tuned for more updates as we continue to refine and expand its capabilities!