Skip to main content

Command Palette

Search for a command to run...

Day 7 of Demolishing My Stack of Unfinished Projects: Bugginator

Updated
9 min read
Day 7 of Demolishing My Stack of Unfinished Projects: Bugginator

Published: February 4, 2026

When I created the Bugginator repository on December 6, 2022, I had a straightforward idea: build a bug tracker that I'd actually use for my own projects. Three years, four major rewrites, and 233 commits later, I can finally say it's deployed, functional, and integrated with my project management ecosystem. This is the story of a project that refused to die — and what it took to finally finish it.

The Graveyard of Good Intentions

Every developer has that stack of unfinished side projects. GitHub repos with an "Initial commit" from two years ago, a flurry of weekend activity, and then silence. Bugginator was dangerously close to becoming one of those projects. Here's the commit timeline:

YearCommitsWhat Happened
202239Initial Flowbite + Next.js prototype, sidebar, dark theme toggle
202324Mostly Dependabot bumps. One weekend of auth work in January, then abandoned
20242Two Dependabot security patches. Zero feature work
202537Rewrote from scratch with Next.js 15, added NeonDB, NextAuth, profile system
2026131Exploded with features: ClickRise MCP, two-way sync, demo dashboard, dark mode, telemetry, easter eggs

That 2024 row — two commits, both automated — is the valley of death. The project sat dormant for nearly two years. What brought it back?

The First Life: Flowbite Prototype (2022)

The original Bugginator was a Flowbite React component showcase more than a bug tracker. Built on Next.js 12, it was essentially a kitchen sink demo of UI components:

2022-05-05 chore(build): Add Next.js starter
2022-05-05 feature(route): Add kitchen sink of flowbite-react components
2022-07-22 feat(component): toggle Sidebar from Header
2022-12-09 feat(next.js): upgrade to app/ directory (beta)

I was learning Next.js and the App Router was brand new. The bug tracking functionality was an afterthought — the real goal was playing with new tech. By December 2022, I had a sidebar, a dark theme toggle, and not much else. Then it sat. For over a year.

The Second Life: Full-Stack Rewrite (August 2025)

In August 2025, I picked up Bugginator again and did what developers do — rewrote everything from scratch. In a single day (August 17), I pushed 20 commits:

2025-08-17 feat: Complete bug tracker application setup with NeonDB, Drizzle ORM, shadcn/ui, and NextAuth
2025-08-17 feat: Upgrade to Next.js 15 and React 19
2025-08-17 feat: implement Neon database integration with passkey authentication
2025-08-17 Add comprehensive profile page with password change, profile image upload
2025-08-17 Fix authentication loop by integrating NextAuth properly
2025-08-17 Add reports page and bug details page with modals
2025-08-17 Add database seeding scripts for projects and sample bugs

The new stack was dramatically different from the 2022 prototype:

  • Next.js 15 with React 19 and the App Router

  • NeonDB (serverless PostgreSQL) replacing... nothing (the original had no database)

  • Drizzle ORM for type-safe database queries

  • NextAuth.js for authentication

  • shadcn/ui replacing Flowbite

The database schema was designed properly this time, with relational tables for bugs, projects, users, comments, and attachments:

export const bugs = pgTable('bugs', {
  id: uuid('id').primaryKey().defaultRandom(),
  title: text('title').notNull(),
  description: text('description').notNull(),
  status: bugStatusEnum('status').default('open'),
  priority: bugPriorityEnum('priority').default('medium'),
  type: bugTypeEnum('type').default('bug'),
  projectId: uuid('project_id').references(() => projects.id),
  reporterId: uuid('reporter_id').references(() => users.id),
  assigneeId: uuid('assignee_id').references(() => users.id),
  estimatedTime: integer('estimated_time'),
  actualTime: integer('actual_time'),
  dueDate: timestamp('due_date'),
  resolvedAt: timestamp('resolved_at'),
  createdAt: timestamp('created_at').defaultNow(),
  updatedAt: timestamp('updated_at').defaultNow(),
})

But then September came, and I got distracted by other projects. Sound familiar?

The Third (and Final) Life: January 2026

The breakthrough came in January 2026 when two things converged: I had built ClickRise into a proper project management platform, and I was tired of Bugginator being on my "unfinished" list. In a 10-day sprint spanning January 25 to February 4, I pushed 131 commits and took Bugginator from a half-working prototype to v1.23.0.

Authentication Migration: NextAuth to Neon Auth

One of the first orders of business was replacing NextAuth with Better Auth (Neon Auth). NextAuth had been fighting me with cookie domain issues and session persistence problems:

2026-01-25 feat: implement Neon Auth with Better Auth replacing NextAuth
2026-01-25 fix: migrate from NextAuth to Better Auth client hooks
2026-01-25 fix: use same-origin URLs for auth client to fix cookie domain
2026-01-25 feat: consolidate auth_user and users tables with sync mechanism

Better Auth was dramatically simpler. No more wrestling with callback URLs and JWT configuration.

The ClickRise Integration: From Webhooks to MCP

The most significant technical decision was how Bugginator would communicate with ClickRise. I started with webhooks — the classic approach — but quickly realized the limitations:

Webhooks require:

  • An always-on server to receive them (Vercel cold-starts break this)

  • Signature verification to prevent spoofing

  • Retry logic for missed events

  • A publicly accessible endpoint

MCP (Model Context Protocol) gives you:

  • Pull-based architecture: fetch when needed

  • Works with cold-started serverless functions

  • No signature verification overhead

  • Built-in JSON-RPC structure

The MCP client speaks JSON-RPC to ClickRise's MCP endpoint:

async function mcpCall<T>(
  method: string,
  toolName: string,
  args: Record<string, unknown> = {}
): Promise<McpResponse<T>> {
  const config = getClickRiseConfig()

  const response = await fetch(MCP_ENDPOINT, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${config.apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method,
      params: {
        name: toolName,
        arguments: args,
      },
      id: requestId++,
    }),
  })

  const jsonResponse = await response.json()

  // MCP returns { content: [{ type: 'text', text: '...' }] }
  if (jsonResponse.result?.content?.[0]?.text) {
    const parsed = JSON.parse(jsonResponse.result.content[0].text)
    return { success: true, data: parsed.data }
  }
}

The client exposes typed methods for every operation: listProjects(), listTasks(), createTask(), updateTask(), and even listBugTasks() which filters tasks by the bug tag.

Two-Way Sync: Bugs as Tasks, Tasks as Bugs

The killer feature is two-way synchronization between Bugginator bugs and ClickRise tasks. When you create a bug in Bugginator, it becomes a task in ClickRise. When you update a task's status in ClickRise, the bug status updates in Bugginator.

This required careful status mapping since the two systems use different terminology:

// ClickRise: todo, in_progress, review, done, archived
// Bugginator: open, in_progress, review, resolved, closed, reopened

export function mapClickRiseStatusToBug(status: ClickRiseTaskStatus): BugStatus {
  const statusMap: Record<ClickRiseTaskStatus, BugStatus> = {
    todo: 'open',
    in_progress: 'in_progress',
    review: 'review',
    done: 'resolved',
    archived: 'closed',
  }
  return statusMap[status] || 'open'
}

There's also a shouldSyncStatus() guard that prevents infinite sync loops — if the mapped statuses are already equivalent, no sync is triggered.

The Evolution in Real Time

You can trace the ClickRise integration evolution through the commit history. I went back and forth between approaches before landing on MCP:

2026-01-25 feat: implement Phase 4 - ClickRise webhook handler
2026-01-26 feat: implement two-way sync between ClickRise tasks and Bugginator bugs
2026-02-02 refactor: replace webhook infrastructure with MCP client for ClickRise sync
2026-02-02 fix: switch from MCP to REST API for ClickRise integration
2026-02-04 fix: switch to MCP for fetching ClickRise projects and bugs
2026-02-04 refactor: replace all REST API calls with MCP for ClickRise integration

Webhooks to MCP. MCP to REST. REST back to MCP. Three approaches in three days. The final commit message says it all: "replace all REST API calls with MCP." Sometimes you have to try the wrong thing twice to be sure the right thing is right.

Everything Else That Shipped

Beyond ClickRise, January 2026 was a feature avalanche:

  • Demo Dashboard (v1.3.0-1.4.0): A comprehensive demo for unauthenticated visitors to explore the full UI without signing up

  • Dark Mode (v1.7.0): Theme toggle across landing page, dashboard, and demo pages

  • Toast Notifications (v1.8.0): Replaced browser alerts with Sonner for a polished UX

  • Pagination (v1.11.0): Both bugs and projects pages now paginate properly

  • Time Tracking (v1.14.0): startedAt and resolvedAt timestamps for measuring bug resolution time

  • Bug Game Easter Egg (v1.21.0): An interactive 3D bug-crushing game hidden in the app, complete with different bug colors, sizes, and shapes

  • Telemetry (v1.22.0-1.23.0): Session tracking for demo visits and bug game plays

The Tech Stack Today

Bugginator v1.23.0 runs on:

  • Next.js 16 with React 19 and Turbopack

  • TypeScript (~700K lines including generated code)

  • NeonDB (serverless PostgreSQL) via Drizzle ORM

  • Better Auth (Neon Auth) for authentication

  • shadcn/ui + Tailwind CSS for the UI

  • ClickRise MCP for project management integration

  • Sonner for toast notifications

  • Vitest for testing

  • Zod for runtime validation

  • Vercel for deployment

  • Semantic Release for automated versioning (23 releases and counting)

Lessons from Finishing a 3-Year Project

1. Rewrites aren't always bad

The 2022 Flowbite prototype taught me Next.js fundamentals. The 2025 rewrite taught me NeonDB and Drizzle. The 2026 sprint built on all of that knowledge. Each "restart" carried forward everything I'd learned.

2. Integration creates motivation

Connecting Bugginator to ClickRise gave it a real purpose. It wasn't just a demo anymore — it was part of my development workflow. When a tool is useful, you want to finish it.

3. Ship the ugly version first

Many of those 131 commits in January were fixes to things that should have been right the first time. But shipping a broken version and iterating was infinitely better than waiting for perfection.

4. Track everything

Here's the ClickRise data for Bugginator right now:

FieldValue
StatusCompleted
Dev ProgressDeployed
Progress87% (26/30 tasks)
Version1.23.0
Total Commits233
FeaturedYes

87% complete, 26 out of 30 tasks done. Those last 4 tasks? They're real polish items, not blockers. The app is live, functional, and integrated.

5. The dormant period is part of the process

Those two lonely Dependabot commits in 2024 felt like failure at the time. Looking back, the dormant period was when I was building ClickRise, learning about MCP, and developing the architecture patterns that made the final sprint possible. Sometimes a project needs to wait for you to be ready.

What's Next for Bugginator

The remaining tasks focus on polish and advanced features:

  • Real-time notifications via MCP updates and Sonner notifications

  • Bulk operations for triaging multiple bugs at once

  • GitHub integration to auto-create bugs from GitHub Issues

  • Analytics dashboard with bug resolution trends and team velocity metrics

But honestly? The biggest win is that Bugginator is no longer on my unfinished stack. One down, a few more to go.


This is Day 7 of my "Demolishing My Stack of Unfinished Projects" series. Last time in Day 6, I shipped SolarGlobe — a 3D Solar Energy Management Platform. The series documents my ongoing effort to clear my backlog of started-but-not-finished side projects — one shipped app at a time.

Unfinished Projects

Part 2 of 8

In this series, I will complete dozens of side projects that I have started and have not finished for one reason or another

Up next

Day 6 of Demolishing My Stack of Unfinished Projects: SolarGlobe

A 3D Solar Energy Management Platform