From Insight to Action: Building Smart Connections Across Projects with AI and tRPC
Dive into our recent development sprint where we built interconnected features – Action Points, Cross-Project Pattern Detection, and AI-powered Personas – to transform raw discussions into actionable project work across our platform.
The journey from a casual team discussion to a concrete, trackable work item can often feel like crossing a chasm. Insights get lost, patterns go unnoticed, and the bridge between 'what we talked about' and 'what we need to do' remains unbuilt. This was the challenge we set out to tackle in our recent development session.
Our goal was ambitious: to implement a suite of interconnected features – Action Points, Cross-Project Pattern Detection, and AI-assisted Persona Management. Together, these would form a robust system for bridging the gap between raw discussions, extracted insights, and actionable work items, not just within a single project, but across an entire organization.
I'm excited to share a deep dive into how we brought these features to life, focusing on the technical decisions, the architecture, and a few lessons learned from a surprisingly smooth development process.
Laying the Foundation: The Data Schema
Every complex feature starts with a solid data model. For Action Points, we needed a comprehensive structure to capture their essence and context.
We introduced the ActionPoint model into our prisma/schema.prisma:
// prisma/schema.prisma
model ActionPoint {
id String @id @default(cuid())
tenantId String
userId String
projectId String
title String
description String?
category String?
priority String?
status String @default("OPEN")
sourceDiscussionId String? // Link to original discussion
sourceInsightId String? // Link to original insight
isAutoDetected Boolean @default(false)
detectedInProjects String[] // Projects where this pattern was detected
workflowId String?
tenant Tenant @relation(fields: [tenantId], references: [id])
user User @relation(fields: [userId], references: [id])
project Project @relation(fields: [projectId], references: [id])
workflow Workflow? @relation(fields: [workflowId], references: [id])
@@index([tenantId, projectId, status])
@@index([tenantId, isAutoDetected])
}
This model provides fields for tracking details, linking back to their origin (discussions or insights), and flagging whether they were automatically detected. Crucially, detectedInProjects allows us to track the reach of cross-project patterns.
We then established relations to User, Tenant, Project, and Workflow models, ensuring every action point is correctly attributed and contextualized. A new Row-Level Security (RLS) policy was also drafted for the action_points table to enforce tenant isolation, a critical security measure.
Understanding Our Users Better: Persona Management
Before diving deep into action, we wanted to ensure teams could truly understand who they were building for. This led to the Persona CRUD (Create, Read, Update, Delete) features, powered by AI.
We extended our src/server/trpc/routers/personas.ts with standard CRUD procedures, but the real magic happened with generateSuggestions. This procedure hooks into our new src/server/services/persona-generator.ts, an LLM-powered service designed to suggest personas based on user input.
// src/server/services/persona-generator.ts (simplified)
import { resolveWorkingProvider } from './llm-provider'; // Our dynamic LLM resolver
export async function generatePersonaSuggestions(description: string) {
const llm = resolveWorkingProvider(); // Get the current best LLM
const prompt = `Based on the following description, suggest three distinct user personas,
including their name, role, primary goal, and a key pain point.
Description: "${description}"`;
const response = await llm.generateText(prompt);
// Parse LLM response into structured persona suggestions
return parsePersonaResponse(response);
}
The resolveWorkingProvider() pattern is key here, allowing us to dynamically switch between different LLM providers (e.g., OpenAI, Anthropic, local models) based on configuration or availability, ensuring resilience and flexibility.
On the frontend, we built a comprehensive persona experience:
- A
List page(src/app/(dashboard)/dashboard/personas/page.tsx) with grid cards, showing built-in and custom personas. - A 3-phase
AI-assisted creation flow(src/app/(dashboard)/dashboard/personas/new/page.tsx): discovery (free-text input for AI suggestions) → selection (choose from AI-generated or refine) → customize (final tweaks). - A
Detail/edit page(src/app/(dashboard)/dashboard/personas/[id]/page.tsx), read-only for built-in personas.
This makes persona creation less of a chore and more of an insightful, guided process.
Making Insights Actionable: Action Points CRUD
With personas in place, the next step was to make 'action' a first-class citizen. Our src/server/trpc/routers/action-points.ts now exposes 8 procedures, covering everything from listing all action points (listAll) to creating new ones (create) and even generating workflows from them (createWorkflow).
A standout feature here is extractFromDiscussion. This procedure leverages src/server/services/action-point-extraction.ts, another LLM-powered service that can parse discussion messages and intelligently identify potential action points.
// src/server/services/action-point-extraction.ts (simplified)
import { resolveWorkingProvider } from './llm-provider';
export async function extractActionPointsFromDiscussion(discussionText: string, projectId: string, userId: string) {
const llm = resolveWorkingProvider();
const prompt = `Review the following discussion transcript and identify any clear action points.
For each action point, provide a concise title, a brief description,
and suggest a category (e.g., 'Bug', 'Feature', 'Refactor', 'Research').
Format your response as a JSON array.
Discussion: "${discussionText}"`;
const llmResponse = await llm.generateStructuredOutput(prompt);
// Validate and persist extracted action points
return llmResponse.map(ap => ({ ...ap, projectId, userId, isAutoDetected: true }));
}
This significantly reduces manual effort, transforming unstructured conversation into structured, actionable items. We integrated these capabilities into the project dashboard, adding an "Actions" tab with components for creating and extracting action points.
The Intelligent Bridge: Cross-Project Pattern Detection
This is where things get really interesting. One of the biggest challenges in larger organizations is identifying recurring themes or problems across different projects. Our new src/server/services/cross-project-scanner.ts addresses this head-on.
This service performs vector similarity search on new, high-severity insights or knowledge exports. If it finds similar patterns (with a configurable threshold of 0.65) in other projects, it automatically creates new ActionPoint records in those projects, flagged with isAutoDetected: true. It also de-duplicates by sourceInsightId and projectId to prevent noise.
// src/server/services/cross-project-scanner.ts (simplified)
import { getVectorEmbedding } from './embedding-service'; // Our embedding provider
import { prisma } from '../db'; // Our Prisma client
export async function triggerCrossProjectScan(insightId: string, insightText: string, sourceProjectId: string) {
const embedding = await getVectorEmbedding(insightText);
// Find similar insights across all projects
const similarInsights = await prisma.$queryRaw`
SELECT id, projectId, embedding
FROM insights
WHERE project_id != ${sourceProjectId} AND id != ${insightId}
ORDER BY embedding <=> ${embedding}
LIMIT 10; // Get top 10 similar insights
`;
for (const similar of similarInsights) {
// Calculate cosine similarity, filter by threshold
if (calculateCosineSimilarity(embedding, similar.embedding) > 0.65) {
// Create auto-detected action point for the other project
await prisma.actionPoint.create({
data: {
title: `Detected pattern from ${sourceProjectId}`,
description: `Similar to insight ${insightId} from project ${sourceProjectId}.`,
projectId: similar.projectId,
// ... other fields
isAutoDetected: true,
detectedInProjects: [sourceProjectId],
sourceInsightId: insightId,
},
});
}
}
}
This scanner is triggered as a fire-and-forget hook after a high-severity insight is persisted (src/server/services/insight-persistence.ts) and after knowledge is exported from a discussion (src/server/services/discussion-knowledge.ts). This ensures that the system is constantly learning and connecting dots in the background without blocking user workflows.
The Unified View: Top-Level Action Points Page
Finally, all these interconnected pieces converge on a new top-level "Action Points" page (src/app/(dashboard)/dashboard/action-points/page.tsx). This page provides a comprehensive, grouped-by-project view of all action points, with powerful filters for status and auto-detection.
Users can easily cycle through statuses, create workflows directly from action points, and see priority/category badges at a glance. It's the central hub for understanding what needs to be done, where, and how it relates to broader organizational patterns.
Smooth Sailing: A Testament to Planning
One of the most satisfying aspects of this session was the "Pain Log" (or lack thereof!). The entire development process, involving 8 new files and 8 modified files across multiple architectural layers, completed cleanly:
- Zero major issues encountered.
- Both parallel agents completed cleanly with zero overlapping file conflicts. This speaks volumes about clear task decomposition and good communication.
npx tsc --noEmitpassed on the first attempt after all changes were integrated. This is a powerful validation of our commitment to TypeScript and type safety, catching potential bugs before they even hit runtime.
This experience reinforces the value of meticulous planning, a robust type system, and well-defined interfaces when tackling complex, interconnected features.
Immediate Next Steps
While the core implementation is solid, the immediate next steps involve rigorous testing and final touches:
- Apply RLS Policy: Manually activate the new
action_pointsRLS policy:psql -f prisma/rls.sql. - Manual QA: Thoroughly test the entire flow: persona creation (AI-assisted), manual action point creation, LLM-based action point extraction from discussions, workflow creation, and the top-level action points page.
- Cross-Project Scanner Test: Verify that persisting a high-severity insight correctly triggers auto-detected action points in other projects.
- Unit Tests: Consider adding dedicated unit tests for
cross-project-scanner.tsandaction-point-extraction.tsto ensure the AI-driven logic remains robust.
Conclusion
This sprint was a significant leap forward in making our platform more intelligent and actionable. By weaving together Action Points, AI-powered Personas, and Cross-Project Pattern Detection, we've built a system that not only captures insights but actively transforms them into a structured, trackable, and discoverable work. It's exciting to see how these intelligent connections will empower teams to move faster and make more informed decisions.
{
"thingsDone": [
"Implemented ActionPoint data model with Prisma",
"Developed AI-assisted Persona CRUD with dynamic LLM provider",
"Created Action Points CRUD, including LLM-powered extraction from discussions",
"Built Cross-Project Scanner using vector similarity search for auto-detection",
"Created a unified Top-Level Action Points dashboard with filtering and workflow integration",
"Integrated RLS for tenant isolation on new tables"
],
"pains": [
"No major issues encountered, development was remarkably smooth."
],
"successes": [
"All 5 development phases completed on schedule",
"Zero overlapping file conflicts during parallel development",
"TypeScript type-checking passed on first attempt (`npx tsc --noEmit`)",
"Seamless integration of LLM services via `resolveWorkingProvider()` pattern",
"Successful implementation of complex cross-project pattern detection"
],
"techStack": [
"Next.js",
"tRPC",
"Prisma",
"PostgreSQL",
"TypeScript",
"LLMs (via dynamic provider)",
"Vector Similarity Search"
]
}