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:
| Year | Commits | What Happened |
| 2022 | 39 | Initial Flowbite + Next.js prototype, sidebar, dark theme toggle |
| 2023 | 24 | Mostly Dependabot bumps. One weekend of auth work in January, then abandoned |
| 2024 | 2 | Two Dependabot security patches. Zero feature work |
| 2025 | 37 | Rewrote from scratch with Next.js 15, added NeonDB, NextAuth, profile system |
| 2026 | 131 | Exploded 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):
startedAtandresolvedAttimestamps for measuring bug resolution timeBug 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:
| Field | Value |
| Status | Completed |
| Dev Progress | Deployed |
| Progress | 87% (26/30 tasks) |
| Version | 1.23.0 |
| Total Commits | 233 |
| Featured | Yes |
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.






