nyxcore-systems
7 min read

Flow State, Feature Freight Train: A Late-Night Dev Session Unpacked

Join me on a late-night journey through a feature-packed dev session, from fixing middleware quirks to building a powerful todo importer, and the crucial lessons learned along the way.

Next.jstRPCPostgreSQLDeveloper WorkflowProductivityMiddlewareSQLFeature DevelopmentAI Personas

There's a certain magic to coding in the quiet hours. The world sleeps, distractions fade, and the code editor becomes a portal to pure focus. Last night, around 3:30 UTC, I found myself deep in one of those sessions, pushing a significant chunk of usability improvements, a critical reporting feature, and a much-needed import pipeline for our internal todo system.

By the time the birds started chirping, all the features were implemented, typechecks passed, and the commit message was practically writing itself. This post is a dive into what got done, the snags I hit, and the lessons I'm carrying forward.

The Feature Freight Train: What We Shipped

The primary goal of this session was multifaceted: enhance user experience, provide better project oversight, and streamline how we convert raw notes into actionable development tasks. Here's a rundown of the key deliverables:

1. Persona Specialization & Enhanced Context

Our system leverages specialized personas to guide various workflows. Until now, their profiles felt a bit generic. This session, I backfilled all 9 core personas with 1-3 specific specializations, a category, and defining traits. For example, Sasha is now explicitly System Design / Scalability / Clean Architecture.

This enrichment directly translates to better UI. When comparing personas in a workflow, their buttons now clearly display not just their name, but their top two specializations. It's a small change, but it provides immediate context, helping users select the right expert for the job.

typescript
// Simplified example from workflows/[id]/page.tsx
<button className="flex flex-col items-center p-2 border rounded">
  <span>{persona.name}</span>
  {persona.specializations && persona.specializations.length > 0 && (
    <span className="text-xs text-gray-500">
      {persona.specializations.slice(0, 2).join(' / ')}
    </span>
  )}
</button>

2. Workflow Creation & Project Association

Creating a new workflow needed a direct link to a project. I implemented a project selector in the workflows/new page, allowing users to associate a new workflow with an existing project right from the start. This moves us away from a "YOLO" (You Only Live Once) workflow creation mode to a more structured, project-centric approach.

3. Project Reports Tab: Gaining Insight

This was a big one. Every project now has a dedicated "Reports" tab, complete with a BarChart2 icon. This tab lists all completed project workflows along with key statistics, providing a high-level overview of project progress and outcomes. A "Generate Report" button also preps us for more sophisticated reporting capabilities down the line via a ReportGeneratorModal.

4. The Todo Importer: From Markdown to Action Points

This is arguably the most significant piece of work from the session. My personal todo/ directory is full of markdown files, often structured with ### N. Title headings, followed by details like Type, Priority, Prompt Essence, and a Description.

I built a new TodoImporterService (src/server/services/todo-importer.ts) that parses these markdown files. It intelligently extracts:

  • Type → category (e.g., Feature, Bug, Refactor)
  • PriorityP0, P1, P2
  • Prompt Essence (a concise summary for AI context)
  • Description (the full details)

This service feeds into a new importFromTodo tRPC mutation in src/server/trpc/routers/action-points.ts. This mutation deduplicates items by title, marks them as isAutoDetected: true, and appends the Prompt Essence.

Finally, on the ActionPointsTab within any project, there's now an "Import todo/" button. Clicking it triggers the mutation, showing a neat alert with import and skip counts. This completely transforms how I manage my backlog, pulling structured tasks directly from my notes. I even seeded it with a todo/backlog-2026-02-26.md file containing 15 structured items for immediate testing!

Hitting the Wall (and Bouncing Back): Lessons Learned

No dev session is complete without a few head-scratchers. These "pain points" are often where the most valuable learning happens.

Lesson 1: next/image and Authentication Middleware (The Invisible Blocker)

The Problem: I was trying to use next/image to optimize persona portraits stored in public/images/personas/. Instead of serving optimized images, I was getting HTTP 400 errors with the message "not a valid image."

The Diagnosis: Digging deeper, I realized our authentication middleware was intercepting the internal next/image fetch requests for these static assets. Since these internal requests didn't carry authentication tokens, the middleware was redirecting them to /login (a 307 redirect), which next/image then interpreted as a malformed image response. Sneaky!

The Fix: The solution was to explicitly exclude the images/ path from the middleware's matcher.

typescript
// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // ... auth logic here ...
  return NextResponse.next();
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - images (our public images directory)
     * - favicon.ico (favicon file)
     */
    '/((?!api|_next/static|_next/image|images|favicon.ico).*)',
  ],
};

Takeaway: Always remember that Next.js middleware can intercept any request, including internal ones for static assets or optimized images. If you encounter unexpected redirects or errors with static content, check your middleware's matcher configuration. Any new public/ subdirectories serving static assets might need this same exclusion.

Lesson 2: Ambiguous Columns in Raw SQL JOINs

The Problem: In a new dashboard query, I was joining workflow_steps ws with workflows w and encountered a 42702: column reference "output" is ambiguous error from PostgreSQL.

The Diagnosis: Both the workflow_steps table and the workflows table had a column named output. When I selected output without specifying which table it came from, PostgreSQL didn't know which one I meant.

The Fix: Qualify the ambiguous column with its table alias. In my case, I needed the output from workflow_steps, so ws.output did the trick, along with ws.digest to avoid similar future issues.

sql
-- Before (problematic)
SELECT output, digest FROM workflow_steps ws JOIN workflows w ON ws.workflow_id = w.id;

-- After (fixed)
SELECT ws.output, ws.digest FROM workflow_steps ws JOIN workflows w ON ws.workflow_id = w.id;

Takeaway: It's a classic SQL pitfall, but a crucial reminder: always qualify column names with their table aliases when performing JOINs, especially when columns share names across tables. Even if a column seems unique at first, future schema changes could introduce ambiguity, leading to hard-to-debug errors. Be explicit, always.

What's Next?

With a solid set of features pushed, the immediate next steps involve thorough testing:

  1. Commit and Push: Get these changes out!
  2. Test Todo Import: Verify that the 15 items from backlog-2026-02-26.md are correctly imported into a project's Action Points.
  3. Comprehensive QA: Run through all new features in the browser, using my internal backlog as a checklist.
  4. RLS Policies: Implement Row Level Security for the project_notes table to ensure data isolation.
  5. Cleanup: Tidy up .gitignore for log files and other temporary artifacts.

This session was a great reminder of the iterative nature of development – fixing small bugs, building significant features, and learning from every roadblock. The satisfaction of seeing a complex todo importer come to life, or making the persona selection more intuitive, makes those late-night hours truly worth it.

Happy coding!

json
{
  "thingsDone": [
    "Middleware image fix (excluded images/ from matcher)",
    "Dashboard SQL fix (qualified ambiguous output column)",
    "NyxCore portrait replaced",
    "Persona specializations backfilled (all 9 personas)",
    "Workflow compare persona labels enhanced (name + top 2 specializations)",
    "Workflow create project selector implemented",
    "Project Reports tab added with list of completed workflows",
    "Todo importer service (parses markdown to structured data)",
    "Action points import mutation (deduplicates, auto-detects, appends prompt essence)",
    "Import button in ActionPointsTab (calls mutation, shows counts)",
    "Backlog file (todo/backlog-2026-02-26.md) created for testing"
  ],
  "pains": [
    "next/image failed with 400 due to auth middleware redirecting internal static asset fetches",
    "PostgreSQL 'column reference \"output\" is ambiguous' in raw SQL JOIN"
  ],
  "successes": [
    "All features implemented and typechecked",
    "Enhanced usability for workflow creation and persona selection",
    "New project reporting capability",
    "Robust markdown-based todo importer pipeline",
    "Overcame middleware and SQL ambiguity challenges with clear solutions"
  ],
  "techStack": [
    "Next.js",
    "tRPC",
    "PostgreSQL",
    "TypeScript",
    "Tailwind CSS (implied by flex-col)",
    "Markdown parsing (custom service)",
    "Lucide Icons (BarChart2)"
  ]
}