nyxcore-systems
7 min read

Navigating Multi-Tenancy, Model Catalogs, and Data Flow: A Development Sprint Recap

A deep dive into a recent development sprint, tackling critical multi-tenancy bugs, expanding our AI model catalog, refining data enrichment, and learning valuable lessons along the way.

multi-tenancyprismatrpccode-analysisLLMmodel-catalogfrontendbackenddevopsbug-fixdata-flow

It’s always satisfying to look back at a focused development session and see the tangible progress. This past sprint was particularly packed, touching everything from core multi-tenancy logic and robust data processing to expanding our AI model capabilities and preparing for significant feature rollouts. Our main goals were clear: fortify multi-tenant access, broaden our AI model offerings, fine-tune our note enrichment data flow, and lay the groundwork for a smoother user experience.

Here’s a breakdown of what we accomplished and the lessons we learned.

Fortifying Our Foundations: Key Implementations

A significant portion of this sprint was dedicated to enhancing stability and expanding core features.

Preventing Redundant Code Analysis Runs

Imagine kicking off a complex code analysis on a large codebase, only for it to run again unnecessarily, consuming resources and potentially overwriting fresh results. We identified a few scenarios where this could happen, particularly with auto-triggered analyses.

To combat this, we've implemented robust guards in src/server/trpc/routers/code-analysis.ts and src/server/services/project-sync-service.ts. Now, before initiating a new codeAnalysisRun.create(), our system checks for active runs or recently completed ones (within the last 3 minutes). This not only saves computational cycles but also ensures data freshness. As a bonus, these guards also sweep up any orphaned runs that might have gotten stuck for more than 10 minutes, keeping our analysis queue clean.

A Critical Multi-Tenant Fix: The userId Filter Purge

This was a subtle but critical bug that could lead to "project not found" errors in multi-tenant environments. The root cause? Our findFirst guards in various trpc mutations (like updating, deleting, or publishing projects and blog posts) were incorrectly filtering by userId instead of tenantId.

Consider a scenario where a user, "oliver.baer+ckbnyx," creates a project, but then logs in as "oliver.baer" (which might resolve to a different userId despite being the same tenant). The userId filter would prevent them from accessing their own project.

The fix involved a systematic audit and removal of userId: ctx.user.id from all findFirst guards in src/server/trpc/routers/projects.ts. Now, these guards correctly check tenantId only, ensuring that any user within a given tenant can access and manage their shared projects. The userId is now only explicitly used when creating new data, linking it to the specific user who initiated the creation. This enhances both security and usability in our multi-tenant architecture.

Streamlining Project Syncs: Cleaning Up Orphaned Processes

Long-running processes, especially those involving external integrations like GitHub, can sometimes get stuck. We observed cases of orphaned project syncs that could hog resources or even block new syncs due to unique constraint violations on previousSyncId.

To address this, we enhanced our sync initiation logic in src/server/trpc/routers/projects.ts. We added an $executeRaw query to clean up any syncs that have been running for more than 10 minutes and, crucially, to NULL their previousSyncId. This frees up the unique constraint, allowing new syncs to proceed without issue. Our status checks now also correctly account for "running" syncs, giving us a clearer picture of the system's state.

Expanding Our AI Horizon: The Model Catalog Gets an Upgrade

The world of AI models is constantly evolving, and so are we! We've significantly expanded our MODEL_CATALOG in src/lib/constants.ts to include a wider range of cutting-edge LLMs:

  • Anthropic: Claude Opus 4.6, Sonnet 4.6, Sonnet 4.5
  • OpenAI: GPT-5, GPT-5 Mini, o3, o4 Mini, GPT-4.1, GPT-4.1 Mini, GPT-4.1 Nano
  • Google: Gemini 2.5 Flash-Lite

Alongside this expansion, we've updated our default adapters. New projects will now default to claude-sonnet-4-6 for Anthropic and gpt-4.1-mini for OpenAI, reflecting a balance of capability and efficiency. gpt-4.1-mini also became our new FAST_MODELS default, ensuring speedy responses where performance is paramount.

Enhancing Source Management: GitHub Path UI

Making it easier to manage your codebase sources is key. We've improved the UI for GitHub integration in src/app/(dashboard)/dashboard/projects/[id]/page.tsx. The Sources Tab now features a prominent "Import from GitHub" button when empty, and the settings form includes an editable "Scan path" input for githubPath. This allows users to specify subdirectories for analysis, with an empty path gracefully displaying as "/" for the repository root. We've already updated our internal "CodeMCP" project to scan its root directory ("") for consistency.

Unlocking Deeper Insights: Note Enrichment Analysis (Discovery)

Our goal is to provide truly intelligent insights by enriching user notes with relevant project data. During this sprint, we deep-dived into the src/server/services/note-enrichment.ts flow.

The Key Finding: While the enrichment process correctly loads consolidation patterns, it was missing critical contextual data: code_patterns, memory_entries, and workflow_insights. This is a significant gap, as these elements contain the rich, project-specific intelligence needed to make note enrichment truly powerful. Identifying this gap is a crucial step towards making our AI-powered insights even smarter.

Lessons Learned: Navigating the Development Landscape

Every sprint comes with its challenges, and this one was no exception. These "pain points" often turn into the most valuable lessons.

The Deployment Push-Pull: Always git push First!

The Challenge: We tried deploying directly to production via ssh root@... && git pull && docker compose build. Six commits mysteriously deployed as "no-ops" – production showed old code, and git pull claimed "up to date."

The Lesson: The deploy script pulls from GitHub, not the local environment. If commits haven't been pushed to the remote origin/main branch, the server won't see them. The workaround was simple: Always git push origin main BEFORE initiating an SSH deploy. We've since integrated this into our one-liner commit and deploy scripts, preventing future headaches. Deployment hygiene is paramount!

Prisma's updateMany and Relation Fields: When Raw SQL is Your Friend

The Challenge: We attempted to use prisma.projectSync.updateMany() to set previousSyncId (a relation field) to null on orphaned syncs. Prisma's batch updateMany operations, however, don't directly support setting relation fields to null in this manner.

The Lesson: Sometimes, for specific database operations that touch relation fields in a bulk way, raw SQL is still the most efficient and direct path. We successfully used $executeRaw with a raw SQL query to achieve the desired cleanup, demonstrating the importance of knowing when to drop down to the database layer.

The Elusive "Project Not Found" Bug: A Systemic Root Cause

The Challenge: We initially thought "project not found" errors were confined to specific import operations. However, further investigation revealed they affected all mutations that involved project lookups (update, delete, notes.create, blog operations, etc.).

The Lesson: A bug that seems localized can often have deeper, systemic roots. Our initial focus on import logic missed the true culprit: the widespread use of userId filters in findFirst guards across the entire projects.ts router. This led to a systematic grep and removal of all userId filters in these guards, replacing them with tenantId checks. This thorough approach resolved the issue comprehensively and fortified our multi-tenancy logic.

What's Next: The Road Ahead

Our work continues with exciting new developments and critical refinements:

  1. CRITICAL: Expand Note Enrichment: The most immediate and impactful next step is to wire code_patterns, workflow_insights, and memory_entries into our src/server/services/note-enrichment.ts service. This will leverage our existing wealth of project intelligence to provide incredibly rich context for user notes, using the same robust templating pattern as our workflow engine.
  2. UI Modernization: ProviderModelPicker Rollout: We're migrating away from older provider/model selection UIs. This involves replacing legacy button grids in docs-pipeline/page.tsx and refactor/page.tsx, and updating src/components/discussion/provider-picker.tsx to use the more versatile ProviderModelPicker component across discussions and workflows.
  3. Cleanup: Once all consumers are migrated, we'll delete the old src/components/discussion/provider-picker.tsx and remove the now-unused discussions.availableProviders server procedure.
  4. API Verification: We're keeping an eye on our Anthropic API account, awaiting a pending payment to clear, which will fully restore our service limits.
  5. Future Enhancement: We're considering auto-importing sources during project syncs, moving beyond the current manual "Re-import" button for a more seamless experience.

This sprint was a testament to the continuous evolution of our platform. By tackling critical bugs, expanding our AI capabilities, and refining our data pipelines, we're building a more robust, intelligent, and user-friendly product. Stay tuned for more updates as we continue to push the boundaries of what's possible!