Skip to content

feat(dashboard): Personal productivity dashboard#1

Open
genesiscz wants to merge 1 commit intomasterfrom
feature/dashboard
Open

feat(dashboard): Personal productivity dashboard#1
genesiscz wants to merge 1 commit intomasterfrom
feature/dashboard

Conversation

@genesiscz
Copy link
Owner

@genesiscz genesiscz commented Jan 11, 2026

Summary by CodeRabbit

  • New Features

    • Launched dashboard infrastructure with timer functionality, offline-first synchronization, and real-time updates.
    • Added comprehensive feature suite including mood tracking, sleep logging, spending snapshots, nutrition tracking, and decision logging.
    • Introduced personal AI assistant with task management, blocker detection, deadline alerts, and achievement system.
  • Documentation

    • Added extensive architecture guides, API documentation, and implementation patterns for development reference.

Copilot AI review requested due to automatic review settings January 11, 2026 17:58
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 11, 2026

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive dashboard monorepo structure with documentation, planning documents, and a complete multi-app setup including a Next.js docs site, Nitro API server, and TanStack Start web application with integrated features like TRPC, Convex, PowerSync, and authentication.

Changes

Cohort / File(s) Summary
Dashboard Documentation Foundation
.claude/dashboard/README.md, .claude/dashboard/architecture.md, .claude/dashboard/build.md, .claude/dashboard/components.md, .claude/dashboard/patterns.md, .claude/dashboard/routing.md, .claude/dashboard/stack.md, .claude/dashboard/state.md, .claude/dashboard/structure.md
Comprehensive guides covering dashboard architecture, build system, component catalog, design patterns, file-based routing, tech stack, state management, and project structure.
AI Assistant Design Documents
.claude/ideas/assistant/001-assistant-personal-ai-master.md, .claude/ideas/assistant/002-assistant-phase1-detailed.md, .claude/ideas/assistant/003-assistant-phase2-roadmap.md, .claude/ideas/assistant/004-assistant-phase3-roadmap.md
Multi-phase specification for a Personal AI Assistant feature including context parking, deadline hierarchy, communication logs, decision logs, critical path visualization, blockers, energy tracking, and gamification.
Dashboard Feature Concepts
.claude/ideas/dashboard/001-sleep-recovery-dashboard.md, .claude/ideas/dashboard/002-nutrition-hydration-logger.md, .claude/ideas/dashboard/003-mood-energy-tracker.md, .claude/ideas/dashboard/004-spending-snapshot-dashboard.md, .claude/ideas/dashboard/005-accountability-network.md, .claude/ideas/dashboard/006-portfolio-showcase.md, .claude/ideas/dashboard/007-skill-leveling-system.md, .claude/ideas/dashboard/008-relationship-calendar.md, .claude/ideas/dashboard/009-investment-dashboard.md, .claude/ideas/dashboard/010-debt-snowball-tracker.md, .claude/ideas/dashboard/011-creative-metrics.md, .claude/ideas/dashboard/012-event-coordination-hub.md
Feature specifications for wellness, productivity, and personal tracking dashboards with data models, UI components, integrations, and success metrics.
Project Planning Documents
.claude/plans/2026-01-10-Dashboard.md, .claude/plans/2026-01-13-badge-progress-ui.md, .claude/plans/2026-01-13-blocker-detector.md, .claude/plans/2026-01-13-critical-path-visualizer.md, .claude/plans/2026-01-13-decision-log.md, .claude/plans/2026-01-13-distraction-tracker.md, .claude/plans/2026-01-13-energy-heatmap.md, .claude/plans/2026-01-13-escalation-alerts.md, .claude/plans/2026-01-13-handoff-compiler.md, .claude/plans/2026-01-13-kanban-board.md, .claude/plans/2026-01-13-micro-celebrations.md, .claude/plans/2026-01-13-weekly-review-dashboard.md
Implementation roadmaps for the full-stack dashboard, including component plans, data models, UI/UX specifications, and integration strategies for multiple features.
CLI Documentation & Misc
CodeExamples.md, Useful.md, findings.md, progress.md
Documentation for CLI tools usage, findings from implementation work, development progress tracking, and technical decisions.
Dashboard Root Configuration
src/dashboard/.gitignore, src/dashboard/.npmrc, src/dashboard/README.md
Monorepo configuration and setup instructions for the dashboard project.
Docs App Setup
src/dashboard/apps/docs/...
Complete Next.js documentation site with configuration, pages, styling, and ESLint setup.
Server App (Nitro)
src/dashboard/apps/server/.env.example, src/dashboard/apps/server/.gitignore, src/dashboard/apps/server/README.md, src/dashboard/apps/server/nitro.config.ts, src/dashboard/apps/server/package.json, src/dashboard/apps/server/server/routes/...
Nitro-based API server with WebSocket support, health checks, timer CRUD operations, sync endpoints, and user authentication routes.
Web Template Configuration
src/dashboard/apps/web-template/.cta.json, src/dashboard/apps/web-template/.cursorrules, src/dashboard/apps/web-template/.env.example, src/dashboard/apps/web-template/.gitignore, src/dashboard/apps/web-template/.vscode/settings.json, src/dashboard/apps/web-template/README.md, src/dashboard/apps/web-template/components.json, src/dashboard/apps/web-template/tsconfig.json
Project configuration, environment templates, and tooling setup for the TanStack Start web template.
Web Template Database & Integration
src/dashboard/apps/web-template/convex/..., src/dashboard/apps/web-template/db/..., src/dashboard/apps/web-template/neon-vite-plugin.ts, src/dashboard/apps/web-template/src/db.ts, src/dashboard/apps/web-template/src/env.ts, src/dashboard/apps/web-template/src/integrations/...
Convex schema and operations, PostgreSQL initialization, Neon database integration, environment configuration, and provider integrations for Convex, TRPC, TanStack Query, and WorkOS.
Web Template Components & UI
src/dashboard/apps/web-template/src/components/..., src/dashboard/apps/web-template/src/components/ui/...
React components including Header, form components, chat interface, guitar recommendations, and shadcn/ui primitives (Button, Input, Label, Select, Slider, Switch, Textarea).
Web Template Data & Hooks
src/dashboard/apps/web-template/src/data/..., src/dashboard/apps/web-template/src/db-collections/..., src/dashboard/apps/web-template/src/hooks/..., src/dashboard/apps/web-template/src/lib/...
Data generators, database collections, React hooks for forms, chat, audio/TTS, authentication, and AI model selection utilities.
Web Template Routing & Layout
src/dashboard/apps/web-template/src/routes/__root.tsx, src/dashboard/apps/web-template/src/router.tsx, src/dashboard/apps/web-template/src/routeTree.gen.ts, src/dashboard/apps/web-template/src/routes/api.trpc.$.tsx, src/dashboard/apps/web-template/src/routes/demo/..., src/dashboard/apps/web-template/src/routes/example.guitars/..., src/dashboard/apps/web-template/src/routes/index.tsx, src/dashboard/apps/web-template/src/routes/mcp.ts
TanStack Router configuration, generated route tree, root layout with provider integrations, and demo/example routes covering TRPC, Convex, database operations, AI features, and guitar showcase.
Web Template Styling & Utilities
src/dashboard/apps/web-template/src/styles.css, src/dashboard/apps/web-template/src/utils/..., src/dashboard/apps/web-template/src/mcp-todos.ts
Global Tailwind styling with theme variables, utility functions (class merging, MCP handling), and MCP-backed todo storage with pub-sub.
Web App Documentation
src/dashboard/apps/web/.claude/CLAUDE.md, src/dashboard/apps/web/.claude/docs/patterns/type-sharing.md, src/dashboard/apps/web/.claude/docs/systems/database.md, src/dashboard/apps/web/.claude/docs/systems/events.md, src/dashboard/apps/web/.cta.json, src/dashboard/apps/web/.cursorrules, src/dashboard/apps/web/.env.example, src/dashboard/apps/web/.gitignore, src/dashboard/apps/web/.vscode/settings.json
Documentation and configuration for the web app covering database architecture, event systems, type-sharing patterns, and development setup.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/dashboard

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a personal productivity dashboard feature with task management capabilities, including streak tracking, badge rewards, and context parking functionality. The implementation provides a comprehensive task management system with local storage support and gamification elements.

Changes:

  • Added task management with urgency levels (critical, important, nice-to-have) and status tracking
  • Implemented streak tracking and badge system for user motivation
  • Created context parking feature for saving work-in-progress state
  • Built celebration modal with confetti animation for task completion

Reviewed changes

Copilot reviewed 156 out of 307 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/dashboard/apps/web/src/routes/assistant/-lib/storage/index.ts Storage adapter initialization and singleton pattern
src/dashboard/apps/web/src/routes/assistant/-lib/storage/config.ts Storage sync configuration constants
src/dashboard/apps/web/src/routes/assistant/-hooks/useTaskStore.ts Task store with CRUD operations and completion tracking
src/dashboard/apps/web/src/routes/assistant/-hooks/useStreak.ts Streak management and badge checking logic
src/dashboard/apps/web/src/routes/assistant/-hooks/useContextParking.ts Context parking modal keyboard shortcut handler
src/dashboard/apps/web/src/routes/assistant/-components/TaskForm.tsx Task creation/editing form with urgency selection
src/dashboard/apps/web/src/routes/assistant/-components/TaskCard.tsx Task display card with status and urgency indicators
src/dashboard/apps/web/src/routes/assistant/-components/ContextParkingModal.tsx Modal for capturing work context
src/dashboard/apps/web/src/routes/assistant/-components/CelebrationModal.tsx Task completion celebration with confetti
src/dashboard/apps/web/src/routes/api.sync.upload.ts PowerSync upload endpoint for timer sync
src/dashboard/apps/web/src/lib/timer-sync.server.ts Server-side timer synchronization logic

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +141 to +158
// Build dynamic update
const updates = Object.entries(data)
.filter(([key]) => key !== 'id')
.map(([key]) => `${key} = ?`)
.join(', ')
const values = Object.entries(data)
.filter(([key]) => key !== 'id')
.map(([, value]) => value)

if (updates && values.length > 0) {
// Use raw SQL for dynamic updates
const query = `UPDATE timers SET ${updates} WHERE id = ?`
await db.sql`${query}` // Note: this won't work with template literals
// Fallback to individual field updates
await db.sql`
UPDATE timers SET updated_at = ${data.updated_at ?? new Date().toISOString()}
WHERE id = ${data.id}
`
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dynamic SQL query construction is incorrect. The template literal db.sql cannot interpolate a string variable as a complete query. This will result in SQL injection vulnerability and execution failure. Use the prepared statement pattern with individual field updates or build the query with proper parameter binding.

Suggested change
// Build dynamic update
const updates = Object.entries(data)
.filter(([key]) => key !== 'id')
.map(([key]) => `${key} = ?`)
.join(', ')
const values = Object.entries(data)
.filter(([key]) => key !== 'id')
.map(([, value]) => value)
if (updates && values.length > 0) {
// Use raw SQL for dynamic updates
const query = `UPDATE timers SET ${updates} WHERE id = ?`
await db.sql`${query}` // Note: this won't work with template literals
// Fallback to individual field updates
await db.sql`
UPDATE timers SET updated_at = ${data.updated_at ?? new Date().toISOString()}
WHERE id = ${data.id}
`
// Apply updates per allowed field using parameterized queries
const timerData = data as Record<string, unknown>
const id = timerData.id
if (!id) {
break
}
const updatedAt =
(timerData.updated_at as string | undefined) ??
new Date().toISOString()
for (const [key, value] of Object.entries(timerData)) {
if (key === 'id' || key === 'updated_at') {
continue
}
switch (key) {
case 'name': {
await db.sql`
UPDATE timers
SET name = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'timer_type': {
await db.sql`
UPDATE timers
SET timer_type = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'is_running': {
await db.sql`
UPDATE timers
SET is_running = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'elapsed_time': {
await db.sql`
UPDATE timers
SET elapsed_time = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'duration': {
await db.sql`
UPDATE timers
SET duration = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'laps': {
await db.sql`
UPDATE timers
SET laps = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'user_id': {
await db.sql`
UPDATE timers
SET user_id = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'created_at': {
await db.sql`
UPDATE timers
SET created_at = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'show_total': {
await db.sql`
UPDATE timers
SET show_total = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'first_start_time': {
await db.sql`
UPDATE timers
SET first_start_time = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'start_time': {
await db.sql`
UPDATE timers
SET start_time = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'pomodoro_settings': {
await db.sql`
UPDATE timers
SET pomodoro_settings = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'pomodoro_phase': {
await db.sql`
UPDATE timers
SET pomodoro_phase = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
case 'pomodoro_session_count': {
await db.sql`
UPDATE timers
SET pomodoro_session_count = ${value}, updated_at = ${updatedAt}
WHERE id = ${id}
`
break
}
// Ignore unknown keys to avoid injecting arbitrary column names
default:
break
}

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +67
const awardedBadges: Badge[] = []

for (const badgeType of eligibleBadges) {
const badge = await adapter.awardBadge(userId, badgeType)
awardedBadges.push(badge)
}

Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Badge awarding is done sequentially in a loop with individual awaits. Consider using Promise.all() to award badges concurrently for better performance.

Suggested change
const awardedBadges: Badge[] = []
for (const badgeType of eligibleBadges) {
const badge = await adapter.awardBadge(userId, badgeType)
awardedBadges.push(badge)
}
const awardPromises = eligibleBadges.map((badgeType) =>
adapter.awardBadge(userId, badgeType),
)
const awardedBadges = await Promise.all(awardPromises)

Copilot uses AI. Check for mistakes.
Comment on lines +148 to +151
if (!isEdit) {
resetForm()
}
onOpenChange(false)
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The form reset logic only executes for new tasks but not edits. This could leave stale data in the form if the modal is reopened for editing a different task. Consider resetting in both cases or in the modal's onOpenChange handler.

Suggested change
if (!isEdit) {
resetForm()
}
onOpenChange(false)
handleOpenChange(false)

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +61
// Create particles
for (let i = 0; i < 150; i++) {
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating 150 confetti particles for every celebration could impact performance on lower-end devices. Consider reducing the particle count or making it configurable based on device capabilities.

Suggested change
// Create particles
for (let i = 0; i < 150; i++) {
// Determine particle count based on user preferences and viewport
let particleCount = 150
// Respect reduced motion preference where supported
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
particleCount = 0
} else {
const viewportArea = window.innerWidth * window.innerHeight
// Scale down particles on smaller or lower-area viewports
if (viewportArea < 800 * 600) {
particleCount = 75
}
}
// Create particles
for (let i = 0; i < particleCount; i++) {

Copilot uses AI. Check for mistakes.
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @genesiscz, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request establishes the core infrastructure for a new personal productivity dashboard. It sets up a robust monorepo environment with a modern full-stack framework, integrates advanced data synchronization for offline capabilities, and introduces a unique cyberpunk-themed user interface. The changes also include foundational elements for an intelligent AI assistant, aiming to streamline workflows and boost user efficiency by addressing common productivity challenges.

Highlights

  • New Personal Productivity Dashboard: Introduces a comprehensive personal productivity dashboard application, built as a Turborepo monorepo, featuring a TanStack Start web application with a cyberpunk-inspired UI, a Nitro server backend, and a shared package for common utilities.
  • Offline-First Data Synchronization: Implements offline-first data capabilities and real-time synchronization across devices using PowerSync with SQLite, ensuring data consistency even without a constant network connection.
  • Cyberpunk-Themed UI/UX: Features a distinct cyberpunk aesthetic with glass-morphism cards, neon glow effects, animated gradient orbs, cyber grid backgrounds, and scan lines, enhancing the visual experience.
  • Advanced AI Assistant Foundation: Lays the groundwork for a sophisticated personal AI assistant system, including concepts like a 'Context Parking Lot' for managing context switching, a 'Deadline Hierarchy' for task prioritization, and 'Completion Celebrations' for motivation.
  • Modern Tech Stack Integration: Leverages a modern tech stack including TanStack Start (React 19, Vite, SSR), TanStack Query for server state, TanStack Store for client state, tRPC for type-safe APIs, and WorkOS AuthKit for robust authentication.
  • React Compiler Optimization: The project is configured to use React Compiler, explicitly advising against manual useCallback and useMemo hooks as the compiler handles memoization automatically for performance.
  • Comprehensive Documentation: Extensive documentation has been added for the dashboard's architecture, technology stack, UI components, routing, state management patterns, build system, and detailed roadmaps for the AI assistant's phased development.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a substantial amount of new code and documentation for a personal productivity dashboard. The changes include scaffolding for a monorepo, setting up a TanStack Start web app, a Nitro server, and numerous design documents for future features. My review focuses on a critical bug in the server-side data synchronization logic, opportunities to improve type safety in the WebSocket handler, an architectural inconsistency in the data layer, and a minor correction in one of the design documents. Overall, this is a comprehensive start to a new feature, and the feedback aims to improve its robustness and maintainability.

Comment on lines +299 to +316
case 'PATCH':
if (table === 'timers') {
await db.sql`
UPDATE timers SET updated_at = ${data.updated_at ?? new Date().toISOString()}
WHERE id = ${data.id}
`
}
break

case 'DELETE':
if (table === 'timers') {
await db.sql`DELETE FROM timers WHERE id = ${data.id}`
} else if (table === 'activity_logs') {
await db.sql`DELETE FROM activity_logs WHERE id = ${data.id}`
}
break
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The logic for handling PATCH operations is incorrect. The line await db.sql${query}`` will fail at runtime because db.sql is a tagged template literal and cannot accept a string variable for the query. The code then proceeds to the fallback, which only updates the `updated_at` field, meaning `PATCH` requests will not apply the intended changes.

This is a critical bug in the data synchronization logic. A correct implementation needs to dynamically and safely construct the UPDATE statement. Given the limitations of the db.sql helper, you might need to fetch the existing record, merge the changes, and then perform a full UPDATE with all fields.

Comment on lines +34 to +57
## 14 Core Features

### Communication & Knowledge Layer
1. **Communication Log** - Snapshot important decisions/messages into searchable dashboard
2. **Context Parking Lot** - Quick-save your thoughts when switching tasks, auto-recall later
3. **Decision Log** - Formal decision capture with reasoning and date
4. **Handoff Compiler** - Auto-generate "here is everything" docs for async handoffs

### Deadline & Task Anxiety Layer
5. **Deadline Hierarchy** - Classify deadlines: critical/important/nice-to-have with visual urgency
6. **Critical Path Visualizer** - Show task dependencies and what blocks shipping
7. **Burndown Projection** - Given current velocity, predict deadline hit/miss
8. **Auto-Escalation Alerts** - When deadline approaches and progress lags, suggest solutions

### Motivation & ADHD Support Layer
9. **Completion Celebrations** - Big satisfying feedback when tasks complete
10. **Micro-Celebrations** - Celebrate focus sessions, streaks, milestones
11. **Achievement Badges** - Unlock badges for consistency and achievement
12. **Weekly Review Dashboard** - Progress metrics, trend analysis, AI-generated suggestions

### Deep Work & Focus Layer
14. **Blocker Detector** - Flag when task is blocked, suggest follow-up actions
16. **Energy/Focus Heatmap** - Understand circadian productivity patterns, optimize task timing
17. **Distraction Tracker** - Log interruptions, visualize patterns, suggest mitigation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There appears to be a numbering issue in this list of core features. The title mentions '14 Core Features', but the list numbering is inconsistent, jumping from 12 to 14 and including 16 and 17. Correcting the numbering would improve the clarity of this design document.

Comment on lines +4 to +8
interface Client {
id: string
userId?: string
peer: unknown
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The peer property in the Client interface is typed as unknown. To improve type safety and avoid unsafe casting later in the file, you can import and use the Peer type from h3.

Suggested change
interface Client {
id: string
userId?: string
peer: unknown
}
interface Client {
id: string
userId?: string
peer: Peer
}

Comment on lines +75 to +87
function broadcastToOthers(sender: unknown, message: unknown) {
const messageStr = JSON.stringify(message)

for (const client of clients.values()) {
if (client.peer !== sender) {
try {
(client.peer as { send: (msg: string) => void }).send(messageStr)
} catch (error) {
console.error(`[WebSocket] Failed to send to client ${client.id}:`, error)
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The sender parameter is typed as unknown, which leads to an unsafe type assertion when calling send. By typing sender as Peer (imported from h3), you can eliminate the cast and make the function more robust.

Suggested change
function broadcastToOthers(sender: unknown, message: unknown) {
const messageStr = JSON.stringify(message)
for (const client of clients.values()) {
if (client.peer !== sender) {
try {
(client.peer as { send: (msg: string) => void }).send(messageStr)
} catch (error) {
console.error(`[WebSocket] Failed to send to client ${client.id}:`, error)
}
}
}
}
function broadcastToOthers(sender: Peer, message: unknown) {
const messageStr = JSON.stringify(message)
for (const client of clients.values()) {
if (client.peer !== sender) {
try {
client.peer.send(messageStr)
} catch (error) {
console.error(`[WebSocket] Failed to send to client ${client.id}:`, error)
}
}
}
}

Comment on lines +1 to +201
// TODO: Update field names to match @dashboard/shared types and add pomodoro support
// Will be used by PowerSyncAdapter in /routes/timer/lib/storage/

import { z } from 'zod'
import { db, APP_SCHEMA } from '../db/powersync'

/**
* Timer Schema for validation and type inference
*
* This schema defines the structure of timer data as it's used in the app.
* It handles the transformation from SQLite types (integers for booleans,
* JSON strings for arrays) to proper TypeScript types.
*/
export const timerSchema = z.object({
id: z.string(),
name: z.string(),
type: z.enum(['stopwatch', 'countdown']),
is_running: z.number().transform((val) => val > 0), // SQLite int → boolean
paused_time: z.number(),
countdown_duration: z.number(),
laps: z.string().transform((val) => JSON.parse(val || '[]') as number[]),
user_id: z.string(),
created_at: z.string().transform((val) => new Date(val)),
updated_at: z.string().transform((val) => new Date(val)),
})

export type TimerRecord = z.output<typeof timerSchema>

/**
* Raw timer type as stored in SQLite
*/
export interface TimerRow {
id: string
name: string
type: 'stopwatch' | 'countdown'
is_running: number
paused_time: number
countdown_duration: number
laps: string
user_id: string
created_at: string
updated_at: string
}

/**
* Timer input type for creating/updating timers
*/
export interface TimerInput {
id?: string
name: string
type: 'stopwatch' | 'countdown'
isRunning?: boolean
pausedTime?: number
countdownDuration?: number
laps?: number[]
}

/**
* Serialize a timer for storage in SQLite
*/
export function serializeTimer(input: TimerInput, userId: string): Omit<TimerRow, 'id'> {
const now = new Date().toISOString()
return {
name: input.name,
type: input.type,
is_running: input.isRunning ? 1 : 0,
paused_time: input.pausedTime ?? 0,
countdown_duration: input.countdownDuration ?? 5 * 60 * 1000, // 5 min default
laps: JSON.stringify(input.laps ?? []),
user_id: userId,
created_at: now,
updated_at: now,
}
}

/**
* Timer Collection Queries
*
* These functions provide typed access to the timers table.
* They use PowerSync's reactive queries for real-time updates.
*/

/**
* Get all timers for a user
*/
export async function getTimers(userId: string): Promise<TimerRecord[]> {
const results = await db.getAll<TimerRow>(
'SELECT * FROM timers WHERE user_id = ? ORDER BY created_at DESC',
[userId]
)
return results.map((row) => timerSchema.parse(row))
}

/**
* Get a single timer by ID
*/
export async function getTimer(id: string): Promise<TimerRecord | null> {
const result = await db.getOptional<TimerRow>(
'SELECT * FROM timers WHERE id = ?',
[id]
)
return result ? timerSchema.parse(result) : null
}

/**
* Create a new timer
*/
export async function createTimer(input: TimerInput, userId: string): Promise<string> {
const id = input.id ?? crypto.randomUUID()
const data = serializeTimer(input, userId)

await db.execute(
`INSERT INTO timers (id, name, type, is_running, paused_time, countdown_duration, laps, user_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[id, data.name, data.type, data.is_running, data.paused_time, data.countdown_duration, data.laps, data.user_id, data.created_at, data.updated_at]
)

return id
}

/**
* Update a timer
*/
export async function updateTimer(id: string, updates: Partial<TimerInput>): Promise<void> {
const setClauses: string[] = []
const values: (string | number)[] = []

if (updates.name !== undefined) {
setClauses.push('name = ?')
values.push(updates.name)
}
if (updates.type !== undefined) {
setClauses.push('type = ?')
values.push(updates.type)
}
if (updates.isRunning !== undefined) {
setClauses.push('is_running = ?')
values.push(updates.isRunning ? 1 : 0)
}
if (updates.pausedTime !== undefined) {
setClauses.push('paused_time = ?')
values.push(updates.pausedTime)
}
if (updates.countdownDuration !== undefined) {
setClauses.push('countdown_duration = ?')
values.push(updates.countdownDuration)
}
if (updates.laps !== undefined) {
setClauses.push('laps = ?')
values.push(JSON.stringify(updates.laps))
}

// Always update the timestamp
setClauses.push('updated_at = ?')
values.push(new Date().toISOString())

values.push(id)

await db.execute(
`UPDATE timers SET ${setClauses.join(', ')} WHERE id = ?`,
values
)
}

/**
* Delete a timer
*/
export async function deleteTimer(id: string): Promise<void> {
await db.execute('DELETE FROM timers WHERE id = ?', [id])
}

/**
* Watch timers for real-time updates
* Returns a function to stop watching
*/
export function watchTimers(
userId: string,
callback: (timers: TimerRecord[]) => void
): () => void {
const query = db.watch(
'SELECT * FROM timers WHERE user_id = ? ORDER BY created_at DESC',
[userId],
{ tables: ['timers'] }
)

// Subscribe to changes
const subscription = query.subscribe({
next: (results) => {
const timers = (results.rows?._array ?? []).map((row: TimerRow) =>
timerSchema.parse(row)
)
callback(timers)
},
error: (err) => {
console.error('Timer watch error:', err)
},
})

// Return unsubscribe function
return () => subscription.unsubscribe()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file provides a manual implementation for timer data access, which differs from the approach outlined in the project plan (.claude/plans/2026-01-10-Dashboard.md). The plan suggests using @tanstack/powersync-db-collection to create a collection, which would likely reduce boilerplate and ensure a more consistent data access pattern across the application.

Consider refactoring this to use createCollection with powerSyncCollectionOptions to align with the intended architecture and improve maintainability.

@genesiscz genesiscz force-pushed the feature/dashboard branch 4 times, most recently from 67bec34 to 0f54437 Compare February 9, 2026 02:51
genesiscz added a commit that referenced this pull request Feb 13, 2026
- Fix critical stale cleanup scope bug (Thread #4/#11): project-scoped
  listing no longer deletes other projects' cache entries
- Preserve matchSnippet when merging search results (Thread #19)
- Fix rg-only results getting 0 slots when meta returns 20 (Thread #13)
- Use rmSync with force flag instead of repetitive try/catch (Thread #1)
- Add try/catch fallback for METADATA_VERSION (Thread #3/#6)
- Add -- and -F flags to ripgrep commands (Thread #5/#17)
- Reuse extractTextFromMessage in rgExtractSnippet (Thread #2)
- Add 10MB file size cap + early exit in extraction (Thread #7/#18)
- Add error handling for ripgrep functions (Thread #10/#16)
genesiscz added a commit that referenced this pull request Feb 15, 2026
- Fix critical stale cleanup scope bug (Thread #4/#11): project-scoped
  listing no longer deletes other projects' cache entries
- Preserve matchSnippet when merging search results (Thread #19)
- Fix rg-only results getting 0 slots when meta returns 20 (Thread #13)
- Use rmSync with force flag instead of repetitive try/catch (Thread #1)
- Add try/catch fallback for METADATA_VERSION (Thread #3/#6)
- Add -- and -F flags to ripgrep commands (Thread #5/#17)
- Reuse extractTextFromMessage in rgExtractSnippet (Thread #2)
- Add 10MB file size cap + early exit in extraction (Thread #7/#18)
- Add error handling for ripgrep functions (Thread #10/#16)
genesiscz added a commit that referenced this pull request Feb 15, 2026
#25)

* fix(claude-history): full metadata extraction, rg search, auto-reindex

- Read entire JSONL files for metadata (was limited to 50 lines/64KB)
- Store full firstPrompt (was truncated to 120 chars)
- Index ALL user messages into allUserText field (capped 5000 chars)
- Summary/custom-title: latest wins (captures tail of file)
- Add ripgrep full-content search fallback (rgSearchFiles/rgExtractSnippet)
- Auto-reindex via MD5 hash of lib.ts+cache.ts (METADATA_VERSION)
- Clean up stale cache entries for deleted session files
- Rename DB from stats-cache.db to index.db
- Show match snippets in claude-resume session picker
- Search firstPrompt in matchByIdOrName

* feat(claude-resume): show conversation excerpts in session picker

Adds a second line below each session name showing additional
context: summary (if name is from title), first prompt (if name
is from summary), or match snippet (if from search).

* fix(claude-resume): run metadata + rg search in parallel, merge results

Previously rg only ran as fallback when metadata returned 0 results.
Now both phases run concurrently via Promise.all. Results are deduped
and merged: metadata matches first (ranked by relevance), then rg-only
matches with snippets. Catches content in assistant messages and user
text past the 5000-char metadata cap.

* fix(claude-history): resolve dashed project names, add nerdy stats

- extractProjectName: progressively resolve encoded dir against
  filesystem to recover dashed names (col-fe was showing as "fe")
- Cache resolved project names to avoid repeated filesystem lookups
- Show detailed stats in session picker: session count, project count,
  scope, indexed/stale/reindex status

* feat(claude-resume): nerdy search diagnostics

Show detailed breakdown of where results came from:
- Index matches: count + what was searched (name/branch/project/prompt)
- Content search: meta hits, rg total hits, overlap, rg-only unique hits

* fix(claude-history): address PR #25 review comments

- Fix critical stale cleanup scope bug (Thread #4/#11): project-scoped
  listing no longer deletes other projects' cache entries
- Preserve matchSnippet when merging search results (Thread #19)
- Fix rg-only results getting 0 slots when meta returns 20 (Thread #13)
- Use rmSync with force flag instead of repetitive try/catch (Thread #1)
- Add try/catch fallback for METADATA_VERSION (Thread #3/#6)
- Add -- and -F flags to ripgrep commands (Thread #5/#17)
- Reuse extractTextFromMessage in rgExtractSnippet (Thread #2)
- Add 10MB file size cap + early exit in extraction (Thread #7/#18)
- Add error handling for ripgrep functions (Thread #10/#16)
* fix(claude-history): add cwd to early-exit check, use null for empty allUserText

- Include cwd in early-exit condition to avoid skipping cwd extraction
- Return null instead of "" for allUserText when no user text found
- Anonymize path examples in comments
genesiscz added a commit that referenced this pull request Feb 15, 2026
- Remove unused chalk import from tool-template.md (#1)
- Add language identifiers to fenced code blocks in living-docs SKILL.md (#11)
- Fix react-compiler bail-out patterns to match documented behavior (#12)
- Restore per-file try-catch in collect-files-for-ai to handle permission/disk errors (#13)
genesiscz added a commit that referenced this pull request Feb 15, 2026
…files (#27)

* feat(claude-plugin): add new skills, docs template, and archive old configs

- Add codebase-analysis skill for deep codebase analysis in forked context
- Add living-docs skill for self-maintaining documentation system
- Add typescript-error-fixer skill for fixing TS compilation errors and eliminating 'any' types
- Extract tool-writing guide from CLAUDE.md into .claude/docs/tool-template.md
- Slim down CLAUDE.md "How to Write Tools" section to reference the extracted template
- Archive old agents, commands, and skills to .claude/old/ for historical reference

* feat(claude-plugin): enhance all skills, bump to v1.0.8, fix collect-files

- Bump plugin version to 1.0.8 in marketplace.json and plugin.json
- Add genesis-tools: prefix to all skill names (codebase-analysis, react-compiler-debug, living-docs, typescript-error-fixer)
- typescript-error-fixer: replace tsc with tsgo --noEmit, add mcp-tsc + LSP tool references
- claude-history: add --reindex flag and dashboard reference
- github: expand description for code search/library research triggers, add review fix workflow
- react-compiler-debug: add common bail-out patterns table
- azure-devops: add HAR file analysis workflow and documentation resources section
- timelog: add stale mapping validation guidance
- codebase-analysis: expand from stub to comprehensive skill with analysis types and tools
- living-docs: add context: fork and tooling support section
- collect-files-for-ai: exclude deleted files from git diff with --diff-filter=d, skip missing files gracefully

* fix(claude-plugin): address PR #27 review feedback

- Remove unused chalk import from tool-template.md (#1)
- Add language identifiers to fenced code blocks in living-docs SKILL.md (#11)
- Fix react-compiler bail-out patterns to match documented behavior (#12)
- Restore per-file try-catch in collect-files-for-ai to handle permission/disk errors (#13)

* fix(claude-plugin): address all PR #27 review feedback including archived files

- react-compiler-debug: rewrite bail-out patterns from React Compiler source code
  (BuildHIR.ts, ValidateNoJSXInTryStatement.ts) — 9 verified patterns with source refs
- archived living-docs: remove trailing "Offline", add text lang to fenced block
- archived expo-developer: fix RN version 0.9.0+ -> 0.76+, Reanimated 3 -> 4
- archived frontend-planner: remove hardcoded personal path, use FE_PROJECT_ROOT
- archived implement-v2: add text lang identifier to usage fence
- archived phpstan-analyzer: fix absolute /scripts path to relative scripts/
genesiscz added a commit that referenced this pull request Feb 17, 2026
- Replace duplicate stripAnsi with import from utils/string (#18)
- Fix wrapToWidth ANSI-aware truncation (#1/#3/#22)
- Add await to recursive executeTool call (#9/#20)
- Guard cursor when filtered list is empty (#5/#19)
- Use basename() instead of split("/").pop() (#10/#16)
- Validate --width NaN input (#14)
- Re-display watch message after screen clear (#15)
genesiscz added a commit that referenced this pull request Feb 18, 2026
- Replace duplicate stripAnsi with import from utils/string (#18)
- Fix wrapToWidth ANSI-aware truncation (#1/#3/#22)
- Add await to recursive executeTool call (#9/#20)
- Guard cursor when filtered list is empty (#5/#19)
- Use basename() instead of split("/").pop() (#10/#16)
- Validate --width NaN input (#14)
- Re-display watch message after screen clear (#15)
genesiscz added a commit that referenced this pull request Feb 18, 2026
* fix: clean error formatting and fuzzy match for tools entry point

* feat(tools): add discovery, introspection, and search-select modules

- discovery.ts: scans src/ for tools, extracts descriptions from README.md
- introspect.ts: runs --help on tools and parses Commander output into structured data
- search-select.ts: single-select search prompt with onHighlight callback, modeled on search-multiselect

* feat(tools): interactive browser with search, README preview, and subcommand explorer

* feat(markdown): add render options and CLI flags (--watch, --width, --theme, --no-color)

* fix: improve fuzzy matching to handle trailing dashes and segment matching

"tools cli-" now correctly matches "markdown-cli" by stripping trailing
dashes and checking dash-separated segments.

* feat(markdown-cli): add demo subcommand with interactive template gallery

* Revert "feat(markdown-cli): add demo subcommand with interactive template gallery"

This reverts commit f5c84db.

* feat(markdown): implement theme palette system (dark/light/minimal)

Wire up the --theme flag to actual color palettes that change
mermaid blocks, table borders, and alert colors per theme.

* fix: address PR #28 code review feedback

- Replace duplicate stripAnsi with import from utils/string (#18)
- Fix wrapToWidth ANSI-aware truncation (#1/#3/#22)
- Add await to recursive executeTool call (#9/#20)
- Guard cursor when filtered list is empty (#5/#19)
- Use basename() instead of split("/").pop() (#10/#16)
- Validate --width NaN input (#14)
- Re-display watch message after screen clear (#15)

* fix(markdown-cli/tools): address PR #28 review feedback

- Remove stray n8n.json (unrelated to PR scope)
- fix(markdown-cli): reject invalid --theme values via Commander .choices() (Thread #23)
- fix(tools): guard discoverTools against missing srcDir with existsSync check (Thread #24)
- fix(markdown): use display width for emoji/wide chars in wrapToWidth (Thread #25)
genesiscz added a commit that referenced this pull request Feb 18, 2026
…ety notes

- Add @<author-tag> prefix to batch operations example (Threads #1, #6)
- Add @<author-tag> prefix to background-agent prompt placeholders (Thread #2)
- Add @<author-tag> prefix to --resolve-thread example (Threads #3, #5)
- Add prompt-injection safety note to SKILL.md after Step 5 (Thread #8)
- Add matching safety note to github-pr.md background-agent section (Thread #9)

fix(skills): restrict author tagging to coderabbitai and gemini only

Copilot and other known bots don't respond to @mentions, so tagging
them is noise. Only @coderabbitai and /gemini actually react to being
tagged in PR review replies.
genesiscz added a commit that referenced this pull request Feb 19, 2026
- Replace chalk with picocolors in update tool (Thread #1)
- Replace Node spawnSync with Bun.spawnSync, use import.meta.dir (Thread #2, #8)
- Fix cancel symbol detection in isCancelled helper (Thread #9)
- Simplify validate wrapper in input text mode (Thread #10)
- Add newline/carriage-return escaping in AppleScript notifications (Thread #13)
genesiscz added a commit that referenced this pull request Feb 19, 2026
- Replace chalk with picocolors in update tool (Thread #1)
- Replace Node spawnSync with Bun.spawnSync, use import.meta.dir (Thread #2, #8)
- Fix cancel symbol detection in isCancelled helper (Thread #9)
- Simplify validate wrapper in input text mode (Thread #10)
- Add newline/carriage-return escaping in AppleScript notifications (Thread #13)
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 164


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b61ad988-e52a-48e3-b787-730c1766932a

📥 Commits

Reviewing files that changed from the base of the PR and between f982ace and c3c2afd.

⛔ Files ignored due to path filters (31)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • src/dashboard/apps/docs/app/favicon.ico is excluded by !**/*.ico
  • src/dashboard/apps/docs/public/circles.svg is excluded by !**/*.svg
  • src/dashboard/apps/docs/public/next.svg is excluded by !**/*.svg
  • src/dashboard/apps/docs/public/turborepo.svg is excluded by !**/*.svg
  • src/dashboard/apps/docs/public/vercel.svg is excluded by !**/*.svg
  • src/dashboard/apps/web-template/convex/_generated/api.d.ts is excluded by !**/_generated/**
  • src/dashboard/apps/web-template/convex/_generated/api.js is excluded by !**/_generated/**
  • src/dashboard/apps/web-template/convex/_generated/dataModel.d.ts is excluded by !**/_generated/**
  • src/dashboard/apps/web-template/convex/_generated/server.d.ts is excluded by !**/_generated/**
  • src/dashboard/apps/web-template/convex/_generated/server.js is excluded by !**/_generated/**
  • src/dashboard/apps/web-template/src/logo.svg is excluded by !**/*.svg
  • src/dashboard/apps/web/convex/_generated/api.d.ts is excluded by !**/_generated/**
  • src/dashboard/apps/web/convex/_generated/api.js is excluded by !**/_generated/**
  • src/dashboard/apps/web/convex/_generated/dataModel.d.ts is excluded by !**/_generated/**
  • src/dashboard/apps/web/convex/_generated/server.d.ts is excluded by !**/_generated/**
  • src/dashboard/apps/web/convex/_generated/server.js is excluded by !**/_generated/**
  • src/dashboard/apps/web/public/demo-neon.svg is excluded by !**/*.svg
  • src/dashboard/apps/web/public/example-guitar-flowers.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/example-guitar-motherboard.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/example-guitar-racing.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/example-guitar-steamer-trunk.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/example-guitar-superhero.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/example-guitar-traveling.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/example-guitar-video-games.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/example-ukelele-tanstack.jpg is excluded by !**/*.jpg
  • src/dashboard/apps/web/public/favicon.ico is excluded by !**/*.ico
  • src/dashboard/apps/web/public/logo192.png is excluded by !**/*.png
  • src/dashboard/apps/web/public/logo512.png is excluded by !**/*.png
  • src/dashboard/apps/web/public/tanstack-circle-logo.png is excluded by !**/*.png
  • src/dashboard/apps/web/public/tanstack-word-logo-white.svg is excluded by !**/*.svg
📒 Files selected for processing (269)
  • .claude/dashboard/README.md
  • .claude/dashboard/architecture.md
  • .claude/dashboard/build.md
  • .claude/dashboard/components.md
  • .claude/dashboard/patterns.md
  • .claude/dashboard/routing.md
  • .claude/dashboard/stack.md
  • .claude/dashboard/state.md
  • .claude/dashboard/structure.md
  • .claude/ideas/assistant/001-assistant-personal-ai-master.md
  • .claude/ideas/assistant/002-assistant-phase1-detailed.md
  • .claude/ideas/assistant/003-assistant-phase2-roadmap.md
  • .claude/ideas/assistant/004-assistant-phase3-roadmap.md
  • .claude/ideas/dashboard/001-sleep-recovery-dashboard.md
  • .claude/ideas/dashboard/002-nutrition-hydration-logger.md
  • .claude/ideas/dashboard/003-mood-energy-tracker.md
  • .claude/ideas/dashboard/004-spending-snapshot-dashboard.md
  • .claude/ideas/dashboard/005-accountability-network.md
  • .claude/ideas/dashboard/006-portfolio-showcase.md
  • .claude/ideas/dashboard/007-skill-leveling-system.md
  • .claude/ideas/dashboard/008-relationship-calendar.md
  • .claude/ideas/dashboard/009-investment-dashboard.md
  • .claude/ideas/dashboard/010-debt-snowball-tracker.md
  • .claude/ideas/dashboard/011-creative-metrics.md
  • .claude/ideas/dashboard/012-event-coordination-hub.md
  • .claude/plans/2026-01-10-Dashboard.md
  • .claude/plans/2026-01-13-badge-progress-ui.md
  • .claude/plans/2026-01-13-blocker-detector.md
  • .claude/plans/2026-01-13-critical-path-visualizer.md
  • .claude/plans/2026-01-13-decision-log.md
  • .claude/plans/2026-01-13-distraction-tracker.md
  • .claude/plans/2026-01-13-energy-heatmap.md
  • .claude/plans/2026-01-13-escalation-alerts.md
  • .claude/plans/2026-01-13-handoff-compiler.md
  • .claude/plans/2026-01-13-kanban-board.md
  • .claude/plans/2026-01-13-micro-celebrations.md
  • .claude/plans/2026-01-13-weekly-review-dashboard.md
  • CodeExamples.md
  • Useful.md
  • findings.md
  • progress.md
  • src/dashboard/.gitignore
  • src/dashboard/.npmrc
  • src/dashboard/README.md
  • src/dashboard/apps/docs/.gitignore
  • src/dashboard/apps/docs/README.md
  • src/dashboard/apps/docs/app/globals.css
  • src/dashboard/apps/docs/app/layout.tsx
  • src/dashboard/apps/docs/app/page.tsx
  • src/dashboard/apps/docs/eslint.config.js
  • src/dashboard/apps/docs/next-env.d.ts
  • src/dashboard/apps/docs/next.config.ts
  • src/dashboard/apps/docs/package.json
  • src/dashboard/apps/docs/postcss.config.js
  • src/dashboard/apps/docs/tsconfig.json
  • src/dashboard/apps/server/.env.example
  • src/dashboard/apps/server/.gitignore
  • src/dashboard/apps/server/README.md
  • src/dashboard/apps/server/nitro.config.ts
  • src/dashboard/apps/server/package.json
  • src/dashboard/apps/server/server/routes/_ws.ts
  • src/dashboard/apps/server/server/routes/api/health.ts
  • src/dashboard/apps/server/server/routes/api/sync/upload.ts
  • src/dashboard/apps/server/server/routes/api/timers/[id].ts
  • src/dashboard/apps/server/server/routes/api/timers/index.ts
  • src/dashboard/apps/server/server/routes/api/user.ts
  • src/dashboard/apps/server/server/routes/index.ts
  • src/dashboard/apps/server/tsconfig.json
  • src/dashboard/apps/web-template/.cta.json
  • src/dashboard/apps/web-template/.cursorrules
  • src/dashboard/apps/web-template/.env.example
  • src/dashboard/apps/web-template/.gitignore
  • src/dashboard/apps/web-template/.vscode/settings.json
  • src/dashboard/apps/web-template/README.md
  • src/dashboard/apps/web-template/components.json
  • src/dashboard/apps/web-template/convex/schema.ts
  • src/dashboard/apps/web-template/convex/todos.ts
  • src/dashboard/apps/web-template/convex/tsconfig.json
  • src/dashboard/apps/web-template/db/init.sql
  • src/dashboard/apps/web-template/neon-vite-plugin.ts
  • src/dashboard/apps/web-template/src/components/Header.tsx
  • src/dashboard/apps/web-template/src/components/demo.FormComponents.tsx
  • src/dashboard/apps/web-template/src/components/demo.chat-area.tsx
  • src/dashboard/apps/web-template/src/components/demo.messages.tsx
  • src/dashboard/apps/web-template/src/components/example-AIAssistant.tsx
  • src/dashboard/apps/web-template/src/components/example-GuitarRecommendation.tsx
  • src/dashboard/apps/web-template/src/components/ui/button.tsx
  • src/dashboard/apps/web-template/src/components/ui/input.tsx
  • src/dashboard/apps/web-template/src/components/ui/label.tsx
  • src/dashboard/apps/web-template/src/components/ui/select.tsx
  • src/dashboard/apps/web-template/src/components/ui/slider.tsx
  • src/dashboard/apps/web-template/src/components/ui/switch.tsx
  • src/dashboard/apps/web-template/src/components/ui/textarea.tsx
  • src/dashboard/apps/web-template/src/components/workos-user.tsx
  • src/dashboard/apps/web-template/src/data/demo-table-data.ts
  • src/dashboard/apps/web-template/src/data/demo.punk-songs.ts
  • src/dashboard/apps/web-template/src/data/example-guitars.ts
  • src/dashboard/apps/web-template/src/db-collections/index.ts
  • src/dashboard/apps/web-template/src/db.ts
  • src/dashboard/apps/web-template/src/env.ts
  • src/dashboard/apps/web-template/src/hooks/demo.form-context.ts
  • src/dashboard/apps/web-template/src/hooks/demo.form.ts
  • src/dashboard/apps/web-template/src/hooks/demo.useChat.ts
  • src/dashboard/apps/web-template/src/hooks/useAudioRecorder.ts
  • src/dashboard/apps/web-template/src/hooks/useTTS.ts
  • src/dashboard/apps/web-template/src/hooks/useUser.tsx
  • src/dashboard/apps/web-template/src/integrations/convex/provider.tsx
  • src/dashboard/apps/web-template/src/integrations/tanstack-query/devtools.tsx
  • src/dashboard/apps/web-template/src/integrations/tanstack-query/root-provider.tsx
  • src/dashboard/apps/web-template/src/integrations/trpc/init.ts
  • src/dashboard/apps/web-template/src/integrations/trpc/react.ts
  • src/dashboard/apps/web-template/src/integrations/trpc/router.ts
  • src/dashboard/apps/web-template/src/integrations/workos/provider.tsx
  • src/dashboard/apps/web-template/src/lib/ai-devtools.tsx
  • src/dashboard/apps/web-template/src/lib/demo-store-devtools.tsx
  • src/dashboard/apps/web-template/src/lib/demo-store.ts
  • src/dashboard/apps/web-template/src/lib/example.ai-hook.ts
  • src/dashboard/apps/web-template/src/lib/example.guitar-tools.ts
  • src/dashboard/apps/web-template/src/lib/model-selection.ts
  • src/dashboard/apps/web-template/src/lib/utils.ts
  • src/dashboard/apps/web-template/src/lib/vendor-capabilities.ts
  • src/dashboard/apps/web-template/src/mcp-todos.ts
  • src/dashboard/apps/web-template/src/routeTree.gen.ts
  • src/dashboard/apps/web-template/src/router.tsx
  • src/dashboard/apps/web-template/src/routes/__root.tsx
  • src/dashboard/apps/web-template/src/routes/api.trpc.$.tsx
  • src/dashboard/apps/web-template/src/routes/demo/api.available-providers.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.image.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.mcp-todos.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.names.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.structured.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.tanchat.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.tq-todos.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.transcription.ts
  • src/dashboard/apps/web-template/src/routes/demo/api.tts.ts
  • src/dashboard/apps/web-template/src/routes/demo/convex.tsx
  • src/dashboard/apps/web-template/src/routes/demo/db-chat-api.ts
  • src/dashboard/apps/web-template/src/routes/demo/db-chat.tsx
  • src/dashboard/apps/web-template/src/routes/demo/form.address.tsx
  • src/dashboard/apps/web-template/src/routes/demo/form.simple.tsx
  • src/dashboard/apps/web-template/src/routes/demo/image.tsx
  • src/dashboard/apps/web-template/src/routes/demo/mcp-todos.tsx
  • src/dashboard/apps/web-template/src/routes/demo/neon.tsx
  • src/dashboard/apps/web-template/src/routes/demo/start.api-request.tsx
  • src/dashboard/apps/web-template/src/routes/demo/start.server-funcs.tsx
  • src/dashboard/apps/web-template/src/routes/demo/start.ssr.data-only.tsx
  • src/dashboard/apps/web-template/src/routes/demo/start.ssr.full-ssr.tsx
  • src/dashboard/apps/web-template/src/routes/demo/start.ssr.index.tsx
  • src/dashboard/apps/web-template/src/routes/demo/start.ssr.spa-mode.tsx
  • src/dashboard/apps/web-template/src/routes/demo/store.tsx
  • src/dashboard/apps/web-template/src/routes/demo/structured.tsx
  • src/dashboard/apps/web-template/src/routes/demo/table.tsx
  • src/dashboard/apps/web-template/src/routes/demo/tanchat.css
  • src/dashboard/apps/web-template/src/routes/demo/tanchat.tsx
  • src/dashboard/apps/web-template/src/routes/demo/tanstack-query.tsx
  • src/dashboard/apps/web-template/src/routes/demo/trpc-todo.tsx
  • src/dashboard/apps/web-template/src/routes/demo/workos.tsx
  • src/dashboard/apps/web-template/src/routes/example.guitars/$guitarId.tsx
  • src/dashboard/apps/web-template/src/routes/example.guitars/index.tsx
  • src/dashboard/apps/web-template/src/routes/index.tsx
  • src/dashboard/apps/web-template/src/routes/mcp.ts
  • src/dashboard/apps/web-template/src/styles.css
  • src/dashboard/apps/web-template/src/utils/mcp-handler.ts
  • src/dashboard/apps/web/.claude/CLAUDE.md
  • src/dashboard/apps/web/.claude/docs/patterns/type-sharing.md
  • src/dashboard/apps/web/.claude/docs/systems/database.md
  • src/dashboard/apps/web/.claude/docs/systems/events.md
  • src/dashboard/apps/web/.cta.json
  • src/dashboard/apps/web/.cursorrules
  • src/dashboard/apps/web/.env.example
  • src/dashboard/apps/web/.gitignore
  • src/dashboard/apps/web/.vscode/settings.json
  • src/dashboard/apps/web/README.md
  • src/dashboard/apps/web/components.json
  • src/dashboard/apps/web/convex/schema.ts
  • src/dashboard/apps/web/convex/todos.ts
  • src/dashboard/apps/web/convex/tsconfig.json
  • src/dashboard/apps/web/db/init.sql
  • src/dashboard/apps/web/drizzle.config.ts
  • src/dashboard/apps/web/neon-vite-plugin.ts
  • src/dashboard/apps/web/package.json
  • src/dashboard/apps/web/public/manifest.json
  • src/dashboard/apps/web/public/robots.txt
  • src/dashboard/apps/web/server/routes/_ws-todo.ts
  • src/dashboard/apps/web/server/routes/api/-events.get.ts.old
  • src/dashboard/apps/web/src/__tests__/api.test.ts
  • src/dashboard/apps/web/src/__tests__/drizzle.test.ts
  • src/dashboard/apps/web/src/__tests__/events.test.ts
  • src/dashboard/apps/web/src/components/auth/AuthLayout.tsx
  • src/dashboard/apps/web/src/components/auth/cyberpunk.css
  • src/dashboard/apps/web/src/components/auth/index.ts
  • src/dashboard/apps/web/src/components/dashboard/app-sidebar.tsx
  • src/dashboard/apps/web/src/components/dashboard/dashboard-layout.tsx
  • src/dashboard/apps/web/src/components/dashboard/index.ts
  • src/dashboard/apps/web/src/components/ui/avatar.tsx
  • src/dashboard/apps/web/src/components/ui/badge.tsx
  • src/dashboard/apps/web/src/components/ui/button.tsx
  • src/dashboard/apps/web/src/components/ui/card.tsx
  • src/dashboard/apps/web/src/components/ui/dialog.tsx
  • src/dashboard/apps/web/src/components/ui/dropdown-menu.tsx
  • src/dashboard/apps/web/src/components/ui/feature-card.tsx
  • src/dashboard/apps/web/src/components/ui/input.tsx
  • src/dashboard/apps/web/src/components/ui/label.tsx
  • src/dashboard/apps/web/src/components/ui/scroll-area.tsx
  • src/dashboard/apps/web/src/components/ui/select.tsx
  • src/dashboard/apps/web/src/components/ui/separator.tsx
  • src/dashboard/apps/web/src/components/ui/sheet.tsx
  • src/dashboard/apps/web/src/components/ui/sidebar.tsx
  • src/dashboard/apps/web/src/components/ui/skeleton.tsx
  • src/dashboard/apps/web/src/components/ui/slider.tsx
  • src/dashboard/apps/web/src/components/ui/switch.tsx
  • src/dashboard/apps/web/src/components/ui/textarea.tsx
  • src/dashboard/apps/web/src/components/ui/tooltip.tsx
  • src/dashboard/apps/web/src/components/workos-user.tsx
  • src/dashboard/apps/web/src/db-collections/index.ts
  • src/dashboard/apps/web/src/db-collections/timers.ts
  • src/dashboard/apps/web/src/db.ts
  • src/dashboard/apps/web/src/drizzle/index.ts
  • src/dashboard/apps/web/src/drizzle/migrations/0000_slim_masque.sql
  • src/dashboard/apps/web/src/drizzle/migrations/meta/0000_snapshot.json
  • src/dashboard/apps/web/src/drizzle/migrations/meta/_journal.json
  • src/dashboard/apps/web/src/drizzle/migrations/relations.ts
  • src/dashboard/apps/web/src/drizzle/migrations/schema.ts
  • src/dashboard/apps/web/src/drizzle/schema.ts
  • src/dashboard/apps/web/src/env.ts
  • src/dashboard/apps/web/src/integrations/convex/provider.tsx
  • src/dashboard/apps/web/src/integrations/tanstack-query/devtools.tsx
  • src/dashboard/apps/web/src/integrations/tanstack-query/root-provider.tsx
  • src/dashboard/apps/web/src/integrations/trpc/init.ts
  • src/dashboard/apps/web/src/integrations/trpc/react.ts
  • src/dashboard/apps/web/src/integrations/trpc/router.ts
  • src/dashboard/apps/web/src/integrations/workos/provider.tsx
  • src/dashboard/apps/web/src/lib/ai-example/AIAssistant.tsx
  • src/dashboard/apps/web/src/lib/ai-example/ai-devtools.tsx
  • src/dashboard/apps/web/src/lib/ai-example/ai-hook.ts
  • src/dashboard/apps/web/src/lib/ai-example/guitar-tools.ts
  • src/dashboard/apps/web/src/lib/ai-example/index.ts
  • src/dashboard/apps/web/src/lib/ai-example/model-selection.ts
  • src/dashboard/apps/web/src/lib/ai-example/vendor-capabilities.ts
  • src/dashboard/apps/web/src/lib/assistant/assistant.server.ts
  • src/dashboard/apps/web/src/lib/assistant/components/CelebrationModal.tsx
  • src/dashboard/apps/web/src/lib/assistant/components/ContextParkingModal.tsx
  • src/dashboard/apps/web/src/lib/assistant/components/TaskCard.tsx
  • src/dashboard/apps/web/src/lib/assistant/components/TaskForm.tsx
  • src/dashboard/apps/web/src/lib/assistant/components/index.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/index.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useAssistantQueries.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useBadgeProgress.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useBlockers.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useCelebrations.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useCommunicationLog.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useContextParking.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useDeadlineRisk.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useDecisionLog.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useDistractions.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useEnergyData.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useHandoff.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useStreak.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useTaskStore.ts
  • src/dashboard/apps/web/src/lib/assistant/hooks/useWeeklyReview.ts
  • src/dashboard/apps/web/src/lib/assistant/lib/storage/config.ts
  • src/dashboard/apps/web/src/lib/assistant/lib/storage/index.ts
  • src/dashboard/apps/web/src/lib/assistant/lib/storage/localstorage-adapter.ts
  • src/dashboard/apps/web/src/lib/assistant/lib/storage/types.ts
  • src/dashboard/apps/web/src/lib/assistant/types.ts
  • src/dashboard/apps/web/src/lib/auth-actions.ts
  • src/dashboard/apps/web/src/lib/auth-server.ts
  • src/dashboard/apps/web/src/lib/db/clear-powersync.ts
  • src/dashboard/apps/web/src/lib/db/index.ts

Comment on lines +79 to +83
currentPrice: number
currentValue: number // quantity * currentPrice
totalCost: number // total amount paid for this investment
gains: number // currentValue - totalCost
gainPercent: number // (gains / totalCost) * 100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid making derived valuation fields the primary source of truth.

currentValue, gains, gainPercent, totalValue, totalGains, totalGainPercent, and allocation are all derivable from positions + prices. Storing them directly on the core entities will drift unless you define projection/materialization semantics and recomputation triggers. At minimum, the spec should say whether these are computed-on-read, cached projections, or snapshot-only fields.

Also applies to: 109-114

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/ideas/dashboard/009-investment-dashboard.md around lines 79 - 83,
The schema currently stores derived valuation fields (currentValue, gains,
gainPercent, totalValue, totalGains, totalGainPercent, allocation) which will
drift from positions+prices; update the spec to either (A) remove these from
core entities and make them computed-on-read (document as computed properties
derived from positions and currentPrice), or (B) keep them as cached projections
but document explicit materialization semantics (when and how
recomputation/invalidation happens, who triggers it, and what projection
job/process updates fields like currentValue, gains, gainPercent), or (C) mark
them as snapshot-only with explanation of snapshot lifecycle; reference the
field names above and add the same semantics for the related group at lines
109-114 so all derived fields have a clear computed vs cached vs snapshot
contract.

Comment on lines +84 to +101
lastUpdated: DateTime

// Purchase history for cost basis
purchases: Array<{
date: DateTime
quantity: number
pricePerUnit: number
totalCost: number
}>

// Income
dividendIncome?: number // annual or cumulative
dividendPaymentDates?: DateTime[] // When dividends paid
otherIncome?: number // Real estate rental, staking, etc.

notes?: string
createdAt: DateTime
updatedAt: DateTime
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "009-investment-dashboard.md" -type f

Repository: genesiscz/GenesisTools

Length of output: 119


🏁 Script executed:

cat -n .claude/ideas/dashboard/009-investment-dashboard.md | sed -n '80,140p'

Repository: genesiscz/GenesisTools

Length of output: 1964


🌐 Web query:

TypeScript built-in DateTime type vs Date alternatives

💡 Result:

Is there a built-in TypeScript DateTime type?

No. TypeScript’s “built-in” date/time type is just JavaScript’s runtime Date object (Date), and JS itself doesn’t have a separate DateTime data type beyond Date. [1], [2]

What to use instead (common options)

1) Native Date (built-in)

Use when you just need an instant/timestamp and basic interop with JS/JSON APIs. Be aware Date mixes concerns (UTC instant + local-time rendering) and is easy to misuse around time zones/DST. [1], [2]

2) Temporal (the modern direction)

TC39’s Temporal proposal provides purpose-built types (e.g., “plain date”, “zoned date-time”, “instant”). It’s Stage 3 and commonly used today via polyfill. [3], [4]

  • Polyfill: @js-temporal/polyfill [4]

3) Library “DateTime” types (if you want an object model now)

  • Luxon: DateTime object, immutable API; good ergonomics for zones/formatting. [5]
  • Day.js: small, Moment-like API; immutable core. [6]
  • js-joda: Java java.time-style API (strong conceptual model; immutable). [7]

4) Functional utilities over Date

  • date-fns: functional, modular, immutable-style helpers that operate on native Date (you keep using Date, but with safer utilities). [8]

5) Avoid starting new projects on Moment

Moment is in “maintenance mode / legacy” status (fine to keep, not ideal to start new). [9]

Practical rule of thumb

  • Need “just a timestamp/instant”Date (or Temporal.Instant if you can). [1], [3]
  • Need “date-only / time-only / local scheduling / time zones” → prefer Temporal (polyfill today), otherwise Luxon or js-joda. [3], [4], [5], [7]
  • Want small utilities and keep native Date → date-fns. [8]

Sources: MDN Date [1], MDN date/time guide [2], TC39 Temporal [3], @js-temporal/polyfill [4], Luxon [5], Day.js [6], js-joda [7], date-fns [8], Moment status note [9].


Replace DateTime with a concrete temporal type in this design specification.

DateTime is not a built-in TypeScript type. Specify one of: Date, an ISO-8601 string, Temporal.Instant, or a library type like Luxon's DateTime. Without this specificity, implementation teams across different services will make incompatible choices, leading to model inconsistencies. This appears throughout the specification (lines 84–101, 114–133).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/ideas/dashboard/009-investment-dashboard.md around lines 84 - 101,
Replace the non-existent `DateTime` type with a concrete temporal type across
the spec — pick one consistent format (e.g., ISO-8601 `string`) and update all
occurrences: `lastUpdated`, `purchases.date`, `dividendPaymentDates[]`,
`createdAt`, `updatedAt` (and any other `DateTime` uses around lines 114–133) to
that chosen type; ensure collection types reflect the chosen format (e.g.,
`string[]` for payment dates) and add a short comment noting the expected format
(ISO-8601) so implementers use the same representation across services.

Comment on lines +86 to +92
// Purchase history for cost basis
purchases: Array<{
date: DateTime
quantity: number
pricePerUnit: number
totalCost: number
}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Model transactions, not only purchases.

A buy-only history cannot represent sells, transfers, fees, splits, or DRIP correctly, so quantity, cost basis, and gains become wrong as soon as a user disposes of part of a position. This needs a transaction ledger in the spec before implementation starts.

Proposed spec change
-  // Purchase history for cost basis
-  purchases: Array<{
-    date: DateTime
-    quantity: number
-    pricePerUnit: number
-    totalCost: number
-  }>
+  // Transaction ledger for cost basis and performance
+  transactions: Array<{
+    type: "buy" | "sell" | "dividend" | "fee" | "split" | "transfer_in" | "transfer_out"
+    date: DateTime
+    quantity?: number
+    pricePerUnit?: number
+    amount?: number
+    notes?: string
+  }>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Purchase history for cost basis
purchases: Array<{
date: DateTime
quantity: number
pricePerUnit: number
totalCost: number
}>
// Transaction ledger for cost basis and performance
transactions: Array<{
type: "buy" | "sell" | "dividend" | "fee" | "split" | "transfer_in" | "transfer_out"
date: DateTime
quantity?: number
pricePerUnit?: number
amount?: number
notes?: string
}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/ideas/dashboard/009-investment-dashboard.md around lines 86 - 92,
The current purchases: Array<{ date: DateTime; quantity: number; pricePerUnit:
number; totalCost: number }> only models buys and must be replaced by a generic
transactions ledger: change the field to transactions: Array<TransactionEntry>
and define TransactionEntry to include at minimum { id: string, date: DateTime,
type: 'buy' | 'sell' | 'transfer-in' | 'transfer-out' | 'fee' | 'split' | 'drip'
| 'dividend', quantity: number (signed; positive for inflows, negative for
outflows), pricePerUnit?: number | null, totalAmount?: number, fee?: number,
relatedTransactionId?: string, memo?: string } so that sells use negative
quantities, splits/drips adjust quantity with a ratio or special fields, fees
and transfers can be represented without pricePerUnit, and transactions can be
reconciled for correct cost-basis and gains calculations.

Comment on lines +250 to +255
- API integration for real-time price data (Alpha Vantage, CoinGecko, etc.)
- Portfolio calculations (weighted average cost basis)
- Performance charting (multiple time periods)
- Daily snapshots for historical tracking
- Tax lot tracking (for tax reporting)
- Optional: import from CSV or Robinhood/Fidelity APIs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Clarify the secret-handling policy for price-provider integrations.

The doc proposes third-party market-data APIs, but “No API keys or credentials stored” is too broad and can be read as “put provider keys in the client.” Reword this to distinguish user credentials from server-managed provider secrets.

Suggested wording
-- No API keys or credentials stored
+- No user brokerage credentials stored
+- Provider API secrets, if required, are kept server-side in managed secret storage and never exposed to clients

Also applies to: 265-270

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/ideas/dashboard/009-investment-dashboard.md around lines 250 - 255,
Replace the ambiguous sentence "No API keys or credentials stored" with a
precise policy that distinguishes user-provided credentials from server-managed
provider secrets: state that user account credentials or OAuth tokens must never
be stored in client/browser storage and should be stored only server-side when
necessary with user consent, and that third‑party provider API keys (e.g., for
Alpha Vantage/CoinGecko) are permitted to be held and rotated securely by the
server (environment variables or a secrets manager) and accessed via server-side
proxy endpoints rather than exposed to clients; update the two occurrences (the
existing line and the similar block at the later section) to use this clarified
wording and add a short note recommending token exchange/OAuth flows and use of
a server-side proxy.

- Click to expand details

#### `SupersededChain.tsx`
- Shows decision evolution chain (decision -> superseded by -> etc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Minor grammar convention: "etc." vs "etc"

In American English, the abbreviation typically includes a period: "etc." The current line reads "decision -> superseded by -> etc" and could be updated to "decision -> superseded by -> etc." for consistency with formal conventions.

📝 Suggested correction
-Shows decision evolution chain (decision -> superseded by -> etc)
+Shows decision evolution chain (decision -> superseded by -> etc.)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Shows decision evolution chain (decision -> superseded by -> etc)
- Shows decision evolution chain (decision -> superseded by -> etc.)
🧰 Tools
🪛 LanguageTool

[style] ~31-~31: In American English, abbreviations like “etc.” require a period.
Context: ...ion chain (decision -> superseded by -> etc) - Arrow connectors between cards - Com...

(ETC_PERIOD)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/plans/2026-01-13-decision-log.md at line 31, Update the phrase in
the decision log that reads "Shows decision evolution chain (decision ->
superseded by -> etc)" to use the American English abbreviation with a period:
change "etc" to "etc." so the line becomes "Shows decision evolution chain
(decision -> superseded by -> etc.)".

Comment on lines +100 to +113
useEffect(() => {
const client = getEventClient()
client.connect(userId, [`timer:${userId}`])

const unsubscribe = client.subscribe(`timer:${userId}`, () => {
// Invalidate queries to refetch fresh data
queryClient.invalidateQueries({ queryKey: ['timers'] })
})

return () => {
unsubscribe()
client.disconnect()
}
}, [userId, queryClient])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not disconnect the singleton client from component cleanup.

This example contradicts the singleton guidance below. If multiple components subscribe, one unmount will tear down the shared SSE connection for all of them.

Proposed doc fix
   useEffect(() => {
     const client = getEventClient()
     client.connect(userId, [`timer:${userId}`])

     const unsubscribe = client.subscribe(`timer:${userId}`, () => {
       // Invalidate queries to refetch fresh data
       queryClient.invalidateQueries({ queryKey: ['timers'] })
     })

     return () => {
       unsubscribe()
-      client.disconnect()
     }
   }, [userId, queryClient])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const client = getEventClient()
client.connect(userId, [`timer:${userId}`])
const unsubscribe = client.subscribe(`timer:${userId}`, () => {
// Invalidate queries to refetch fresh data
queryClient.invalidateQueries({ queryKey: ['timers'] })
})
return () => {
unsubscribe()
client.disconnect()
}
}, [userId, queryClient])
useEffect(() => {
const client = getEventClient()
client.connect(userId, [`timer:${userId}`])
const unsubscribe = client.subscribe(`timer:${userId}`, () => {
// Invalidate queries to refetch fresh data
queryClient.invalidateQueries({ queryKey: ['timers'] })
})
return () => {
unsubscribe()
}
}, [userId, queryClient])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web/.claude/docs/systems/events.md` around lines 100 -
113, The cleanup is incorrectly disconnecting the singleton SSE client; update
the effect in which getEventClient() is used so the return cleanup only calls
the subscription's unsubscribe and does NOT call client.disconnect(), leaving
client.connect()/client.disconnect() lifecycle to the singleton manager;
reference getEventClient(), client.connect(...), client.subscribe(...),
unsubscribe and remove client.disconnect() from the effect cleanup (or delegate
disconnect to the singleton's own teardown method) so unmounting one component
doesn't tear down the shared connection.

Comment on lines +121 to +129
| Param | Required | Description |
| ---------- | -------- | ---------------------------------------- |
| `userId` | Yes | User ID for authentication |
| `channels` | No | Comma-separated list of channels |

### Request

```
GET /api/events?userId=user123&channels=timer:user123,notification:user123
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not document userId query params as authentication.

A client-supplied userId in the URL is forgeable and leaks into logs/history. The contract should say authentication comes from the session/token, and any userId used for channel selection must be derived or strictly validated server-side. Otherwise this docs page is teaching an impersonation bug.

Run this read-only check to confirm how src/routes/api.events.ts authenticates the caller and whether it trusts the query-string userId:

#!/bin/bash
set -euo pipefail

fd 'api\.events\.ts$' . -x sh -c '
  echo "== {} =="
  sed -n "1,240p" "{}"
'

echo
echo "== auth/session references around the endpoint =="
rg -n -C3 --type=ts --type=tsx \
  "(auth|session|getUser|requireUser|userId|channels|EventSource|SSE|event stream)" \
  src/dashboard/apps/web

Expected result: the endpoint derives identity from auth/session context and does not treat req.query.userId as the source of truth.

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 128-128: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web/.claude/docs/systems/events.md` around lines 121 -
129, Remove the statement that treats the query param `userId` as authentication
and instead document that authentication is derived from the session/token
(e.g., via server auth helpers such as getUser/requireUser) and not from
`req.query.userId`; update the examples to omit `userId` from the query string,
explain that any user-id used for channel selection must be derived from the
authenticated session or strictly validated server-side (do not trust
`req.query.userId`), and call out that `channels` values should be
validated/normalized on the server before use in the EventSource/SSE handler.

@@ -0,0 +1,4 @@
# These will be automatically created by Neon Launchpad (https://neon.new).
# You will also need to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Finish or remove the incomplete setup note.

Line 2 ends mid-sentence, so the example file leaves setup ambiguous for first-time users. Either complete the instruction or delete it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web/.env.example` at line 2, The comment line "# You will
also need to" in the .env example is an incomplete setup note; either finish the
sentence with the required action or remove the line entirely—update the
.env.example by replacing that line with a complete instruction specifying what
else is needed (e.g., required environment variables or setup steps) or delete
the line if no additional guidance is necessary.

Comment on lines +3 to +4
VITE_DATABASE_URL=
VITE_DATABASE_URL_POOLER=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In Vite (and TanStack Start/Vinxi apps that use Vite conventions), are environment variables prefixed with VITE_ exposed to client-side code? Please cite the official docs.

💡 Result:

Yes.

  • Vite: Env vars are exposed to client code via import.meta.env, and “only variables prefixed with VITE_ are exposed to your Vite-processed code” (e.g. VITE_SOME_KEY is available, DB_PASSWORD is not). [1]
  • TanStack Start (Vinxi/Vite conventions): “Client code can only access variables with the VITE_ prefix” (accessed as import.meta.env.VITE_*). [2]

Sources (official docs):
[1] Vite – Env Variables and Modes (v4.vitejs.dev)
[2] TanStack Start – Environment Variables (tanstack.com)

Citations:


🏁 Script executed:

cat -n src/dashboard/apps/web/.env.example 2>/dev/null || echo "File not found"

Repository: genesiscz/GenesisTools

Length of output: 239


Do not expose database connection strings through VITE_ env vars.

VITE_ variables are exposed to client-side code in Vite and TanStack Start applications. These database URLs would be embedded in the browser bundle, compromising database security. Move these to server-only env vars (DATABASE_URL and DATABASE_URL_POOLER) and access them only from server code.

Proposed fix
- VITE_DATABASE_URL=
- VITE_DATABASE_URL_POOLER=
+ DATABASE_URL=
+ DATABASE_URL_POOLER=
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VITE_DATABASE_URL=
VITE_DATABASE_URL_POOLER=
DATABASE_URL=
DATABASE_URL_POOLER=
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web/.env.example` around lines 3 - 4, The env example
exposes database connection strings as VITE_DATABASE_URL and
VITE_DATABASE_URL_POOLER which makes them available to client bundles; rename
these to server-only keys DATABASE_URL and DATABASE_URL_POOLER in the .env
example and remove any VITE_ variants; then update any usages in server-side
code to read process.env.DATABASE_URL / process.env.DATABASE_URL_POOLER (and
remove or change any import.meta.env.VITE_DATABASE_URL or
VITE_DATABASE_URL_POOLER references in client code) so database URLs are only
accessed from server runtime.

Comment on lines +363 to +368
GenesisTools currently uses **minimist** for argument parsing. Both yargs and @cliffy/command could be considered for future enhancements:

- **Subcommands**: Define hierarchical command structures with `.command()`
- **Options**: Strongly typed options with `.option()` and automatic help generation
- **Action handlers**: Clean separation of command logic with `.action()`
- **yargs**: Better for complex Node.js/Bun CLI tools with many options and commands
- **@cliffy/command**: Better for Deno-based tools that need type safety and built-in validation

For interactive prompts, GenesisTools uses **@inquirer/prompts** which provides:

- **Modern API**: Promise-based with individual prompt functions (`select`, `input`, `confirm`, etc.)
- **Type safety**: Built-in TypeScript support
- **Cancellation handling**: `ExitPromptError` from `@inquirer/core` for clean user cancellation
For GenesisTools' Bun-based environment, **yargs** would be the more natural choice if migration were considered.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check which CLI argument parser GenesisTools uses

# Check package.json for dependencies
echo "=== Checking package.json dependencies ==="
cat package.json | jq '.dependencies, .devDependencies' 2>/dev/null | rg -i 'minimist|commander|yargs'

# Check for imports in source files
echo -e "\n=== Checking imports in src/ ==="
rg -n --type=ts "from ['\"](?:minimist|commander|yargs)" src/ -g '!node_modules' || echo "No matches found"

# Check for require statements
echo -e "\n=== Checking require statements ==="
rg -n --type=ts "require\(['\"](?:minimist|commander|yargs)" src/ -g '!node_modules' || echo "No matches found"

Repository: genesiscz/GenesisTools

Length of output: 11100


Update documentation to reflect actual argument parser in use.

The documentation incorrectly states that GenesisTools uses minimist. The codebase actually uses commander for parsing command-line arguments with subcommands and options, as evidenced by extensive imports throughout src/ and package.json dependencies.

Documentation fix
-GenesisTools currently uses **minimist** for argument parsing. Both yargs and `@cliffy/command` could be considered for future enhancements:
+GenesisTools currently uses **commander** for argument parsing. Both yargs and `@cliffy/command` could be considered for future enhancements:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
GenesisTools currently uses **minimist** for argument parsing. Both yargs and @cliffy/command could be considered for future enhancements:
- **Subcommands**: Define hierarchical command structures with `.command()`
- **Options**: Strongly typed options with `.option()` and automatic help generation
- **Action handlers**: Clean separation of command logic with `.action()`
- **yargs**: Better for complex Node.js/Bun CLI tools with many options and commands
- **@cliffy/command**: Better for Deno-based tools that need type safety and built-in validation
For interactive prompts, GenesisTools uses **@inquirer/prompts** which provides:
- **Modern API**: Promise-based with individual prompt functions (`select`, `input`, `confirm`, etc.)
- **Type safety**: Built-in TypeScript support
- **Cancellation handling**: `ExitPromptError` from `@inquirer/core` for clean user cancellation
For GenesisTools' Bun-based environment, **yargs** would be the more natural choice if migration were considered.
GenesisTools currently uses **commander** for argument parsing. Both yargs and `@cliffy/command` could be considered for future enhancements:
- **yargs**: Better for complex Node.js/Bun CLI tools with many options and commands
- **@cliffy/command**: Better for Deno-based tools that need type safety and built-in validation
For GenesisTools' Bun-based environment, **yargs** would be the more natural choice if migration were considered.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Useful.md` around lines 363 - 368, The documentation incorrectly claims
GenesisTools uses minimist; update Useful.md to state the project uses the
commander library for CLI parsing (referencing the Commander imports found
across src/ and the commander entry in package.json). Replace mentions of
"minimist" and the yargs/@cliffy suggestions for Bun/Deno context with a short
note that commander is used for subcommands and options, and optionally suggest
yargs or `@cliffy/command` only as alternative future options if desired.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

- Incomplete projects: allow WIP status
- Date validation: end date must be after start date
- Image size limits: compress or reject oversized files
- URL validation: check link is accessible
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Avoid backend reachability checks for arbitrary portfolio URLs.

“Check link is accessible” becomes an SSRF footgun if this is implemented by fetching user-supplied URLs from the server. Prefer format validation in the app, or if you later probe URLs server-side, constrain protocols/domains and enforce strict timeouts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/ideas/dashboard/006-portfolio-showcase.md at line 320, The line
"check link is accessible" should not trigger a server-side fetch of arbitrary
user-supplied URLs; replace that SSRF-prone behavior with client-side/format
validation (e.g., URL parsing and allowed-scheme check) and remove any server
code that performs unrestricted HTTP GETs for portfolio links. If you must
perform server-side probes later, implement strict guards around the probe logic
(validate schemes to http/https only, enforce an allowlist or domain
constraints, set very short connect/read timeouts, disable redirects, and run
probes from an isolated service account/network). Ensure the check is
identified/updated wherever the phrase or function implementing "check link is
accessible" appears.

Comment on lines +33 to +58
```
src/dashboard/apps/web/src/routes/assistant/
├── -components/
│ ├── kanban/ # KanbanBoard, KanbanColumn, KanbanCard
│ ├── communication/ # CommunicationLog, LogEntry, LogForm, LogFilters
│ ├── decisions/ # DecisionLog, DecisionCard, DecisionForm
│ ├── critical-path/ # CriticalPathGraph, DependencyNode, PathAnalysis
│ ├── blockers/ # BlockerList, BlockerCard, BlockerModal
│ ├── analytics/ # EnergyHeatmap, WeeklyReview, WeekStats, etc.
│ ├── handoff/ # HandoffDocument, HandoffEditor, HandoffHistory
│ ├── escalation/ # EscalationAlert, RiskIndicator, EscalationWidget
│ ├── celebrations/ # MicroCelebration, CelebrationManager, BadgeCelebration
│ ├── badges/ # BadgeShowcase, BadgeProgress, BadgeCard
│ └── distractions/ # DistractionLog, DistractionStats, DistractionPatterns
├── -hooks/ # Extended with 12 new hooks
├── -lib/storage/ # Extended LocalStorage adapter
├── -types.ts # Extended with all new interfaces
├── tasks/
│ ├── index.tsx # Kanban board (replaced grid)
│ └── $taskId.tsx # Task detail with blockers, handoffs, dependencies
├── next.tsx # What's Next recommendations
├── parking.tsx # Context parking history
├── analytics.tsx # Energy heatmap, weekly review, distractions, badges
├── communication.tsx # Communication log page
└── decisions.tsx # Decision log page
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language identifier to fenced code block.

The code block showing the codebase structure is missing a language specifier. For directory trees, use text or plaintext.

📝 Fix
-```
+```text
 src/dashboard/apps/web/src/routes/assistant/
 ├── -components/
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 33-33: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 33-33: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@findings.md` around lines 33 - 58, The fenced code block showing the
directory tree is missing a language identifier; update the opening fence (the
triple backticks before the directory listing) to include a language such as
text or plaintext (e.g., change "```" to "```text") so the block is properly
marked as plain text; locate the block in the findings.md content that begins
with the directory tree (the triple-backtick fenced block containing
"src/dashboard/apps/web/src/routes/assistant/") and add the language identifier.

Comment on lines +8 to +11
export const metadata: Metadata = {
title: "Create Turborepo",
description: "Generated by create turbo",
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace the scaffold metadata before shipping.

The docs app will publish Create Turborepo / Generated by create turbo as its title and description. That placeholder branding will leak into the browser title, SEO metadata, and link previews for the dashboard docs.

✏️ Suggested metadata update
 export const metadata: Metadata = {
-    title: "Create Turborepo",
-    description: "Generated by create turbo",
+    title: "GenesisTools Dashboard Docs",
+    description: "Documentation for the personal productivity dashboard",
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/docs/app/layout.tsx` around lines 8 - 11, The exported
const metadata: Metadata currently uses scaffold placeholders ("Create
Turborepo", "Generated by create turbo"); update that export in layout.tsx to
use the real site title and description appropriate for the dashboard docs
(e.g., project/product name and a concise description for SEO), and while here
consider adding or updating any other metadata fields (keywords, authors,
openGraph) to ensure accurate browser titles and rich link previews.

Comment on lines +6 to +27
const LINKS = [
{
title: "Docs",
href: "https://turborepo.com/docs",
description: "Find in-depth information about Turborepo features and API.",
},
{
title: "Learn",
href: "https://turborepo.com/docs/handbook",
description: "Learn more about monorepos with our handbook.",
},
{
title: "Templates",
href: "https://turborepo.com/docs/getting-started/from-example",
description: "Choose from over 15 examples and deploy with a single click.",
},
{
title: "Deploy",
href: "https://vercel.com/new",
description: "Instantly deploy your Turborepo to a shareable URL with Vercel.",
},
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace the stock Turborepo/Vercel landing page before shipping this app.

This page still advertises examples/with-tailwind, Turborepo docs, and Vercel, which does not match the dashboard product in this PR. If the docs app is exposed, users will land on unrelated placeholder content and assume the site is unfinished.

Also applies to: 31-101

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/docs/app/page.tsx` around lines 6 - 27, The page is still
using the placeholder LINKS array (constant name LINKS) with Turborepo/Vercel
content—replace that data and any placeholder text in the page component that
renders it with product-specific docs/links/descriptions; update the LINKS array
entries to point to your dashboard's documentation, handbook, templates, and
deployment instructions (or remove unused entries) and revise any
titles/descriptions rendered by the page component to reflect the actual
dashboard product so users don't see the stock landing content.

@@ -0,0 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "next-env.d.ts" -type f 2>/dev/null | head -20

Repository: genesiscz/GenesisTools

Length of output: 105


🏁 Script executed:

# Check if the file exists and show its full content with line numbers
if [ -f "src/dashboard/apps/docs/next-env.d.ts" ]; then
  cat -n "src/dashboard/apps/docs/next-env.d.ts"
else
  echo "File not found at src/dashboard/apps/docs/next-env.d.ts"
fi

Repository: genesiscz/GenesisTools

Length of output: 358


🏁 Script executed:

# Check .gitignore for .next directory handling
if [ -f ".gitignore" ]; then
  echo "=== .gitignore content ==="
  cat ".gitignore" | grep -E "\.next|node_modules|dist" || echo "No .next/.node_modules/.dist patterns found"
fi

Repository: genesiscz/GenesisTools

Length of output: 132


🏁 Script executed:

# Check if there are other next-env.d.ts files or similar declarations
find . -name "*next-env*" -o -name "*next.config*" 2>/dev/null | head -20

Repository: genesiscz/GenesisTools

Length of output: 146


🌐 Web query:

Next.js next-env.d.ts auto-generated import .next routes.d.ts

💡 Result:

In recent Next.js versions (e.g. Next 15.5+ with typedRoutes enabled), Next will generate route typings into .next/types/routes.d.ts and then auto-wire them into TypeScript by updating next-env.d.ts with a line like:

/// <reference path="./.next/types/routes.d.ts" />

This is expected/auto-generated behavior and will be overwritten whenever you run next dev, next build, or next typegen. [1] [2] [3]

What to do (recommended)

  • Do not edit next-env.d.ts manually (it’s regenerated). [1]
  • If you’re seeing ESLint complain (notably @typescript-eslint/triple-slash-reference), it’s a known issue/regression reported against typedRoutes generation. Workarounds are:
    • Exclude next-env.d.ts from linting, or
    • Disable that ESLint rule for next-env.d.ts via an override (target just this file). [3]

Also ensure TS actually picks up the generated types

Your tsconfig.json should include:

  • next-env.d.ts
  • .next/types/**/*.ts (so TS can see generated route/env helper types)

Next.js docs explicitly call this out (especially if you didn’t bootstrap with create-next-app). [2]

Sources:
[1] Next.js TypeScript config (next-env.d.ts is generated/regenerated) (nextjs.org)
[2] Next.js typedRoutes generates .next/types route definitions; include .next/types/**/*.ts in tsconfig (nextjs.org)
[3] Next.js issue: typedRoutes adds /// <reference path="./.next/types/routes.d.ts" /> and triggers ESLint triple-slash rule (github.com)

Citations:


🌐 Web query:

Next.js next-env.d.ts should be committed to source control gitignore

💡 Result:

next-env.d.ts is generated by Next.js and should not be tracked in Git—add it to .gitignore. [1]

# Next.js (TypeScript)
next-env.d.ts

If you already committed it, untrack it once:

git rm --cached next-env.d.ts
git commit -m "Stop tracking next-env.d.ts"

In CI, generate it instead of committing (e.g., run next dev/build, or in newer Next versions use npx next typegen). [2]

Sources: Next.js “Project Structure” docs [1]; Next.js GitHub discussion on next-env.d.ts/CI and next typegen [2].


Remove next-env.d.ts from version control—it is auto-generated by Next.js.

next-env.d.ts is generated by Next.js on every next dev, next build, or next typegen run and should not be committed. Add it to .gitignore and remove it from the repository using git rm --cached src/dashboard/apps/docs/next-env.d.ts. In CI and on clean checkouts, Next.js will regenerate it automatically, so there is no need to track it. Committing it couples source control to ephemeral build output and creates unnecessary merge conflicts when the file is regenerated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/docs/next-env.d.ts` at line 3, Remove the auto-generated
next-env.d.ts from version control and stop tracking it: add next-env.d.ts to
.gitignore, run git rm --cached next-env.d.ts to untrack it, and commit the
.gitignore + removal; note the file contains the import
"./.next/dev/types/routes.d.ts" and will be regenerated by Next.js on
dev/build/typegen so no further code changes are required.

Comment on lines +21 to +35
const { mutate: addTodo } = useMutation({
mutationFn: (todo: string) =>
fetch("/demo/api/tq-todos", {
method: "POST",
body: JSON.stringify(todo),
}).then((res) => res.json()),
onSuccess: () => refetch(),
});

const [todo, setTodo] = useState("");

const submitTodo = useCallback(async () => {
await addTodo(todo);
setTodo("");
}, [addTodo, todo]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/dashboard/apps/web-template/src/routes/demo/tanstack-query.tsx

Repository: genesiscz/GenesisTools

Length of output: 3792


🌐 Web query:

TanStack React Query useMutation mutateAsync isPending API

💡 Result:

useMutation: mutateAsync + isPending (TanStack React Query)

  • mutateAsync(variables, options?) => Promise<TData>
    Use this when you want to await the mutation; it resolves on success and throws on error. [1][2]

  • isPending: boolean
    true while the mutation is running (status === 'pending'). Other states are isIdle, isError, isSuccess. [1]

Example

const mutation = useMutation({ mutationFn: addTodo })

try {
  const data = await mutation.mutateAsync({ title: 'Do Laundry' })
} catch (e) {
  // handle error
}

if (mutation.isPending) {
  // show spinner / disable button
}

[1]

Version note (common confusion)

  • In v5 (current docs) the “loading” state is isPending / status: 'pending'. [1]
  • In v4, the docs show isLoading / status: 'loading', and mutateAsync has the same “returns a Promise” behavior. [2]

Sources: TanStack Query React docs (Mutations guide; useMutation reference). [1][2]


Use mutateAsync and validate trimmed input inside submitTodo.

mutate returns immediately without a Promise, so the await on line 33 doesn't actually wait before clearing the field. The Enter handler (lines 61-65) can still submit whitespace because it bypasses the button's disabled guard. Additionally, there's no pending state guard, allowing concurrent submissions.

Move the trim/empty check into submitTodo, switch to mutateAsync, and gate both Enter and button clicks on the isPending state.

Suggested fix
-    const { mutate: addTodo } = useMutation({
+    const { mutateAsync: addTodo, isPending } = useMutation({
         mutationFn: async (todo: string) => {
             const res = await fetch("/demo/api/tq-todos", {
                 method: "POST",
                 body: JSON.stringify(todo),
             });
@@
     const [todo, setTodo] = useState("");
 
     const submitTodo = useCallback(async () => {
-        await addTodo(todo);
+        const value = todo.trim();
+        if (!value) {
+            return;
+        }
+
+        await addTodo(value);
         setTodo("");
     }, [addTodo, todo]);
@@
                         value={todo}
                         onChange={(e) => setTodo(e.target.value)}
                         onKeyDown={(e) => {
-                            if (e.key === "Enter") {
+                            if (e.key === "Enter" && !isPending) {
                                 submitTodo();
                             }
                         }}
@@
-                        disabled={todo.trim().length === 0}
+                        disabled={isPending || todo.trim().length === 0}
                         onClick={submitTodo}

Also applies to: 61-72

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web-template/src/routes/demo/tanstack-query.tsx` around
lines 21 - 35, The current mutate call (destructured as addTodo) is not awaited
and input trimming/guarding is only on the button, so change the useMutation
call to use mutateAsync (e.g., const { mutateAsync: addTodo, isPending } =
useMutation(...)), move the trim-and-empty check into submitTodo (trim todo,
return early if empty), await addTodo(todo) before calling setTodo(""), and
ensure both the Enter handler and the button click use submitTodo and
early-return when isPending is true to prevent concurrent/whitespace
submissions; reference functions/identifiers: useMutation, mutateAsync (alias
addTodo), submitTodo, todo state, and the Enter key handler.

Comment on lines +36 to +37
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Give destructive surfaces a contrasting foreground.

Both themes set --destructive-foreground almost the same as --destructive, so destructive buttons/alerts will end up with unreadable text and icons.

🔧 Suggested fix
-  --destructive-foreground: oklch(0.577 0.245 27.325);
+  --destructive-foreground: oklch(0.985 0 0);
...
-  --destructive-foreground: oklch(0.637 0.237 25.331);
+  --destructive-foreground: oklch(0.985 0 0);

Also applies to: 72-73

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web-template/src/styles.css` around lines 36 - 37, The
destructive color and its foreground are nearly identical; update the CSS
variable --destructive-foreground (and the second theme's counterpart) to a
high-contrast value versus --destructive so text/icons are readable—adjust the
oklch (or swap to a light/dark value such as near-white or near-black) in the
declarations for --destructive and --destructive-foreground (the variables named
"--destructive" and "--destructive-foreground") so the foreground meets a
sufficient contrast ratio (apply the change in both places where these vars are
defined).

Comment on lines +119 to +122
## React Guidelines

- **No `useCallback`/`useMemo`** - React Compiler handles memoization
- Use plain functions, compiler optimizes automatically
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🌐 Web query:

According to the official React 19 / React Compiler documentation, should teams avoid useCallbackanduseMemo entirely, or are there still supported cases where stable callback/value identity is needed?

💡 Result:

Teams should not avoid useCallback / useMemo entirely. The official React Compiler docs say the compiler reduces the need for manual memoization, but useMemo and useCallback remain supported “escape hatches” when you need precise control over what’s memoized. A specifically called-out supported case is when you need stable callback/value identity to keep an Effect from re-firing even though dependencies haven’t “meaningfully” changed. [1]

This matches the Hook docs, which note that React Compiler “automatically memoizes values and functions,” reducing (not eliminating) the need for manual useCallback / useMemo. [2] [3]

Sources: React Compiler docs (Introduction), useCallback, useMemo. [1] [2] [3]


Rephrase as conditional guidance rather than prohibition.

The React Compiler reduces the need for manual memoization but does not eliminate useCallback and useMemo entirely—they remain supported escape hatches for cases requiring stable callback or value identity, such as preventing Effects from re-firing unnecessarily. Adjust the documentation to reflect this as a default preference rather than an absolute rule, so contributors understand when these hooks are still appropriate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web/.claude/CLAUDE.md` around lines 119 - 122, Update the
"React Guidelines" section to present the no-useCallback/useMemo note as a
recommendation rather than an absolute ban: change the bullet "No
`useCallback`/`useMemo` - React Compiler handles memoization" to a conditional
guidance that the compiler reduces the need for manual memoization but
`useCallback` and `useMemo` remain supported escape hatches for cases requiring
stable identity (e.g., preventing effects from re-firing or stabilizing callback
props); mention that plain functions are the default preferred pattern and
include a brief note on when to keep the hooks (stable callback/value identity,
performance-critical paths).

Comment on lines +2 to +10
"projectName": "tanstack-start",
"mode": "file-router",
"typescript": true,
"tailwind": true,
"packageManager": "pnpm",
"addOnOptions": {},
"git": true,
"version": 1,
"framework": "react-cra",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

This app snapshot is out of sync with the checked-in setup.

src/dashboard/apps/web/.claude/CLAUDE.md documents Bun-based TanStack Start commands for this app, but this file still says "packageManager": "pnpm" and "framework": "react-cra". Rehydrating from .cta.json would recreate metadata that does not match the current app.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/apps/web/.cta.json` around lines 2 - 10, The .cta.json metadata
is stale: update the "packageManager" and "framework" fields to match the
Bun-based TanStack Start setup documented in CLAUDE.md — replace
"packageManager": "pnpm" with "packageManager": "bun" and change "framework":
"react-cra" to the actual current framework string used by the app (as described
in CLAUDE.md) so the "packageManager" and "framework" keys in .cta.json
accurately reflect the checked-in setup.


- Make sharing one `tailwind.config.ts` to apps and packages as easy as possible.
- Make package compilation simple by only depending on the Next.js Compiler and `tailwindcss`.
- Ensure Tailwind classes do not overwrite each other. The `ui` package uses a `ui-` prefix for it's classes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo: "it's" should be "its".

"it's" is a contraction of "it is," but here you need the possessive "its."

📝 Fix
-- Ensure Tailwind classes do not overwrite each other. The `ui` package uses a `ui-` prefix for it's classes.
+- Ensure Tailwind classes do not overwrite each other. The `ui` package uses a `ui-` prefix for its classes.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Ensure Tailwind classes do not overwrite each other. The `ui` package uses a `ui-` prefix for it's classes.
- Ensure Tailwind classes do not overwrite each other. The `ui` package uses a `ui-` prefix for its classes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dashboard/README.md` at line 33, Fix the typo in the README sentence
about Tailwind class prefixes: replace the contraction "it's" with the
possessive "its" in the line that reads about the `ui` package using a `ui-`
prefix for its classes so the sentence correctly uses the possessive form.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants