Claude-skill-registry calendar-interface-architect

COMPREHENSIVE calendar system skill combining TypeScript interface architecture,

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/calendar-interface-architect" ~/.claude/skills/majiayu000-claude-skill-registry-calendar-interface-architect-2c7fff && rm -rf "$T"
manifest: skills/data/calendar-interface-architect/SKILL.md
source content

Calendar Interface Architect

This comprehensive skill handles all calendar-related issues in the Pomo-Flow application:

  • TypeScript Interface Architecture: Fix CalendarEvent and TaskInstance type issues
  • Inbox Synchronization: Debug calendar inbox showing 0 tasks
  • Scheduling Fixes: Prevent tasks with due dates from incorrectly appearing in calendar grid

Quick Context

  • Complexity: high
  • Duration: 25-45 minutes
  • Dependencies: typescript, vue, pinia, @vue/runtime-core, vite, date-fns, luxon

Activation Triggers

  • Keywords: calendar, CalendarEvent, isDueDate, task instance, temporal, inbox shows 0 tasks, calendar filtering
  • Files: src/composables/calendar/.ts, src/views/CalendarView.vue, src/types/recurrence.ts
  • Contexts: typescript, calendar, interface, temporal, scheduling, inbox, filtering

SECTION 1: TypeScript Calendar Interface Architecture

Critical Calendar Interface Issues

IMMEDIATE Calendar Blocking Issues

CURRENT COMPILATION ERRORS:

  1. Property 'isDueDate' does not exist in type 'CalendarEvent'
  2. TaskInstance type mismatches in calendar operations
  3. Missing temporal interface properties for scheduling
  4. Calendar composables failing due to interface problems

Why Calendar Issues Block Application:

// PROBLEM: Missing properties in CalendarEvent interface
const calendarEvent: CalendarEvent = {
  title: 'Task',
  date: '2024-01-01',
  isDueDate: true  // Property does not exist on CalendarEvent
}

// CONSEQUENCE: Calendar functionality breaks, rendering fails

Phase 1: Temporal Type Analysis (Critical)

// Comprehensive temporal type system
export interface BaseTemporalEntity {
  id: string
  title: string
  description?: string
  createdAt: Date
  updatedAt: Date
}

export interface CalendarEvent extends BaseTemporalEntity {
  date: string // YYYY-MM-DD format
  time?: string // HH:MM format
  duration?: number // Duration in minutes
  isDueDate: boolean // Whether this represents a task due date
  isAllDay: boolean // Whether this is an all-day event
  color?: string // Event color for UI
  location?: string // Event location
  attendees?: string[] // Event attendees
  recurrencePattern?: RecurrencePattern
  metadata?: Record<string, unknown> // Additional event data
}

export interface TaskCalendarEvent extends CalendarEvent {
  taskId: string // Reference to the original task
  taskStatus: Task['status']
  taskPriority: Task['priority']
  taskProgress: number // Task completion progress
  subtaskProgress?: number[] // Individual subtask progress
  isRecurring: boolean // Whether this is a recurring task instance
  parentTaskId?: string // For recurring task instances
  instanceId?: string // Unique instance identifier
}

Phase 2: TaskInstance Type System (Critical)

// Complete TaskInstance interface for calendar operations
export interface TaskInstance {
  id: string // Unique instance identifier
  parentTaskId: string // Reference to original task
  scheduledDate: string // YYYY-MM-DD format
  scheduledTime?: string // HH:MM format
  duration?: number // Duration in minutes
  status: 'scheduled' | 'completed' | 'skipped' | 'in_progress'
  isRecurring: boolean // True for recurring task instances
  isModified?: boolean // True if this instance was modified from pattern
  isSkipped?: boolean // True if this instance is skipped
  recurrenceExceptionId?: string // Link to exception if this is an exception
  pomodoroTracking?: {
    completed: number
    total: number
    duration: number // Duration per pomodoro
  }
  completionData?: {
    completedAt?: Date
    completedBy?: string
    notes?: string
    actualDuration?: number
  }
  metadata?: Record<string, unknown>
}

// Task factory for calendar instances
export interface TaskInstanceFactory {
  createFromTask: (task: Task, date: string, time?: string) => TaskInstance
  createRecurringInstance: (task: Task, pattern: RecurrenceRule, date: string) => TaskInstance
  createException: (instance: TaskInstance, modifications: Partial<TaskInstance>) => TaskInstance
}

Phase 3: Calendar State Management (Critical)

// Calendar-specific state management
export interface CalendarState {
  currentDate: Date
  selectedDate: Date | null
  viewMode: 'month' | 'week' | 'day' | 'agenda'
  events: CalendarEvent[]
  taskInstances: TaskInstance[]
  loading: boolean
  error: string | null
  filters: CalendarFilters
}

export interface CalendarFilters {
  showCompleted: boolean
  showInbox: boolean
  projectIds: string[]
  priorities: Task['priority'][]
  statuses: Task['status'][]
}

export interface CalendarActions {
  setCurrentDate: (date: Date) => void
  setSelectedDate: (date: Date | null) => void
  setViewMode: (mode: CalendarState['viewMode']) => void
  addEvent: (event: CalendarEvent) => void
  removeEvent: (eventId: string) => void
  updateEvent: (eventId: string, updates: Partial<CalendarEvent>) => void
  loadTaskInstances: (startDate: Date, endDate: Date) => Promise<void>
  createTaskInstance: (task: Task, date: string, time?: string) => Promise<void>
}

SECTION 2: Calendar Inbox Synchronization

Problem Identification

You're experiencing the classic Vue 3 calendar filtering failure:

  • Inbox shows: 7 tasks (actual data)
  • Calendar inbox shows: 0 tasks (filtered out)
  • Root cause: Calendar uses
    taskStore.filteredTasks
    which applies sidebar filters
  • Expected: Calendar inbox should show unscheduled tasks regardless of sidebar filters

Diagnostic Protocol

Step 1: Verify the Calendar Filter Issue

Run this diagnostic code in browser console:

// Check calendar vs inbox data discrepancy
async function diagnoseCalendarInboxSync() {
  console.group('Calendar-Inbox Sync Diagnosis')

  try {
    // 1. Check task store state
    const taskStore = window.Vue?.config?.globalProperties?.$pinia?._s?.get('tasks')
    if (!taskStore) {
      console.error('Task store not found')
      return
    }

    const allTasks = taskStore.tasks || []
    const filteredTasks = taskStore.filteredTasks || []
    console.log(`All tasks: ${allTasks.length}`)
    console.log(`Filtered tasks: ${filteredTasks.length}`)
    console.log(`Tasks filtered out: ${allTasks.length - filteredTasks.length}`)

    // 2. Check inbox vs calendar eligibility
    const inboxTasks = allTasks.filter(task =>
      task.isInInbox !== false &&
      !task.canvasPosition &&
      task.status !== 'done'
    )

    const calendarEligible = allTasks.filter(task => {
      const hasInstances = task.instances && task.instances.length > 0
      const hasLegacy = task.scheduledDate && task.scheduledTime
      return hasInstances || hasLegacy
    })

    const calendarInboxTasks = allTasks.filter(task =>
      task.isInInbox !== false &&
      !task.canvasPosition &&
      task.status !== 'done' &&
      !((task.instances?.length || 0) + ((task.scheduledDate && task.scheduledTime) ? 1 : 0) > 0)
    )

    console.log(`Inbox tasks: ${inboxTasks.length}`)
    console.log(`Calendar scheduled tasks: ${calendarEligible.length}`)
    console.log(`Calendar inbox tasks: ${calendarInboxTasks.length}`)

    // 3. Identify the problem
    console.group('Problem Analysis')
    if (inboxTasks.length > 0 && filteredTasks.length === 0) {
      console.error('ISSUE: All tasks filtered out - calendar will show 0 tasks')
    }

    if (filteredTasks.length < allTasks.length) {
      console.warn('Sidebar filters are hiding tasks from calendar')
    }
    console.groupEnd()

    return {
      allTasks: allTasks.length,
      filteredTasks: filteredTasks.length,
      inboxTasks: inboxTasks.length,
      calendarInboxTasks: calendarInboxTasks.length,
      hasIssue: inboxTasks.length > 0 && filteredTasks.length === 0
    }

  } catch (error) {
    console.error('Diagnosis failed:', error)
  } finally {
    console.groupEnd()
  }
}

// Run diagnosis
await diagnoseCalendarInboxSync()

Issue Types

Issue A: Calendar Uses filteredTasks

Symptom: Calendar shows 0 tasks when sidebar has active filters Cause:

useCalendarDayView.ts
uses
taskStore.filteredTasks
Fix: Use
taskStore.tasks
with calendar-specific filtering

Issue B: Sidebar Filters Hide Calendar Tasks

Symptom: Calendar inbox count doesn't match inbox count Cause: Project/smart view filters affect calendar visibility Fix: Calendar should be independent of sidebar filters

Issue C: Done Tasks Hidden Everywhere

Symptom: Tasks disappear when marked as done Cause: Global "hide done" filter affects calendar Fix: Only hide done tasks from calendar timeline, not inbox

Specific Fixes

Fix 1: Calendar Data Source Independence

// Replace in useCalendarDayView.ts around line 81
// OLD CODE:
const filteredTasks = taskStore.filteredTasks

// NEW CODE:
const calendarTasks = computed(() => {
  const allTasks = taskStore.tasks

  // Calendar-specific filtering - independent of sidebar
  return allTasks.filter(task => {
    // Only hide tasks that should never appear in calendar
    if (task.status === 'done') return false

    // Check if task is scheduled for current date
    const hasInstances = task.instances && task.instances.length > 0
    const hasLegacy = task.scheduledDate && task.scheduledTime

    return hasInstances || hasLegacy
  })
})

Fix 2: Calendar Inbox Panel Fix

// In CalendarInboxPanel.vue or similar
const calendarInboxTasks = computed(() => {
  const allTasks = taskStore.tasks

  return allTasks.filter(task => {
    // Task is in inbox and not scheduled
    const isInInbox = task.isInInbox !== false && !task.canvasPosition
    const isUnscheduled = !((task.instances?.length || 0) + ((task.scheduledDate && task.scheduledTime) ? 1 : 0) > 0)
    const isNotDone = task.status !== 'done'

    return isInInbox && isUnscheduled && isNotDone
  })
})

SECTION 3: Calendar Scheduling Fixes

Core Principles

1. Data Model Separation

  • Tasks: Use
    dueDate
    field for deadline metadata, stay in inbox until manually scheduled
  • Calendar Events: Use
    instances
    array for time-specific commitments with scheduled dates/times
  • State Management: Maintain clear distinction between inbox state (
    isInInbox: true
    ) and scheduled state

2. Visual Separation

  • Inbox Tasks: Show as cards/panels, draggable to calendar for scheduling
  • Calendar Events: Show as time blocks with specific start/end times
  • Visual Cues: Use different styling, icons, and interaction patterns

3. Workflow Separation

  • Canvas Smart Groups: Organize tasks by relevance (Today, Tomorrow, etc.)
  • Calendar Inbox: Action queue for tasks that need scheduling
  • Calendar Grid: Commitments with specific time slots

State Flow Architecture

Task Creation -> Smart Groups -> Calendar Inbox -> Manual Scheduling -> Calendar Grid
     |              |              |                    |              |
  New Task     dueDate=Today   isInInbox=true    Create Instance   Show in Calendar

Smart Group Behavior (Canvas "Today" Group)

// When task is moved to "Today" smart group:
moveTaskToSmartGroup(taskId, 'today') {
  const updates = {
    dueDate: '2025-11-08',    // Set deadline for organization
    isInInbox: true,          // Keep in inbox for manual scheduling
    // CRITICAL: Do NOT create instances
  }

  updateTask(taskId, updates)
}

Inbox to Calendar Flow

// When task is manually scheduled from inbox:
scheduleTaskFromInbox(taskId, date, time) {
  // 1. Create calendar instance
  createTaskInstance(taskId, {
    scheduledDate: date,
    scheduledTime: time
  })

  // 2. Remove from inbox state
  updateTask(taskId, { isInInbox: false })

  // 3. Task now appears in calendar grid
}

Common Violations and Solutions

Violation 1: Tasks with dueDate appear in calendar

Problem:

// Wrong: Creating calendar events from dueDate
if (task.dueDate === today) {
  createCalendarEvent(task)  // This should NOT happen
}

Solution:

// Correct: Only create events from instances
if (task.instances?.some(inst => inst.scheduledDate === today)) {
  createCalendarEvent(task)  // Only for explicitly scheduled tasks
}

Violation 2: Tasks in both inbox and calendar

Problem:

// Wrong: Task appears in both places
const updates = {
  dueDate: today,
  isInInbox: true,
  instances: [{ scheduledDate: today, scheduledTime: '09:00' }]
}

Solution:

// Correct: Clear state transition
const updates = {
  dueDate: today,
  isInInbox: false,      // Remove from inbox
  instances: [{ scheduledDate: today, scheduledTime: '09:00' }]
}

Violation 3: Smart groups create scheduling

Problem:

// Wrong: Smart group creates time-based scheduling
moveTaskToSmartGroup(taskId, 'today') {
  createTaskInstance(taskId, { scheduledDate: today, scheduledTime: '09:00' })
}

Solution:

// Correct: Smart group only sets deadline
moveTaskToSmartGroup(taskId, 'today') {
  updateTask(taskId, { dueDate: today })
  // Task stays in inbox for manual scheduling
}

Implementation Patterns

Pattern 1: Calendar Event Factory

// Factory for creating calendar events from different sources
export class CalendarEventFactory {
  static fromTask(task: Task, date: string, time?: string): TaskCalendarEvent {
    return {
      id: generateId(),
      title: task.title,
      description: task.description,
      date,
      time,
      duration: task.estimatedDuration || 25,
      isDueDate: true,
      isAllDay: !time,
      color: this.getTaskEventColor(task.priority),
      taskId: task.id,
      taskStatus: task.status,
      taskPriority: task.priority,
      taskProgress: task.progress,
      isRecurring: false,
      createdAt: new Date(),
      updatedAt: new Date()
    }
  }

  static fromTaskInstance(instance: TaskInstance): TaskCalendarEvent {
    const task = taskStore.getTask(instance.parentTaskId)
    if (!task) {
      throw new Error(`Task not found: ${instance.parentTaskId}`)
    }

    return {
      id: instance.id,
      title: task.title,
      description: task.description,
      date: instance.scheduledDate,
      time: instance.scheduledTime,
      duration: instance.duration,
      isDueDate: true,
      isAllDay: !instance.scheduledTime,
      color: this.getTaskEventColor(task.priority),
      taskId: task.id,
      taskStatus: task.status,
      taskPriority: task.priority,
      taskProgress: task.progress,
      isRecurring: instance.isRecurring,
      parentTaskId: instance.parentTaskId,
      instanceId: instance.id,
      createdAt: new Date(),
      updatedAt: new Date()
    }
  }

  private static getTaskEventColor(priority: Task['priority']): string {
    switch (priority) {
      case 'high': return '#ef4444'
      case 'medium': return '#f59e0b'
      case 'low': return '#10b981'
      default: return '#6b7280'
    }
  }
}

Pattern 2: Type-Safe Calendar Store

// Pinia store for calendar state with full type safety
export const useCalendarStore = defineStore('calendar', () => {
  const state = ref<CalendarState>({
    currentDate: new Date(),
    selectedDate: null,
    viewMode: 'month',
    events: [],
    taskInstances: [],
    loading: false,
    error: null,
    filters: {
      showCompleted: true,
      showInbox: true,
      projectIds: [],
      priorities: [],
      statuses: []
    }
  })

  const actions: CalendarActions = {
    setCurrentDate: (date: Date) => {
      state.value.currentDate = date
    },

    addEvent: (event: CalendarEvent) => {
      state.value.events.push(event)
    },

    async createTaskInstance(task: Task, date: string, time?: string): Promise<void> {
      try {
        const instance = taskStore.createTaskInstance(task, date, time)
        state.value.taskInstances.push(instance)
      } catch (error) {
        state.value.error = error instanceof Error ? error.message : 'Failed to create instance'
      }
    }
  }

  // Computed properties
  const filteredEvents = computed((): TaskCalendarEvent[] => {
    return state.value.taskInstances
      .filter(instance => passesFilters(instance, state.value.filters))
      .map(instance => CalendarEventFactory.fromTaskInstance(instance))
  })

  return {
    state: readonly(state),
    actions,
    filteredEvents
  }
})

Pattern 3: Date Type Safety

// Type-safe date utilities for calendar operations
export class CalendarDateUtils {
  static parseDateKey(dateKey: string): Date {
    const [year, month, day] = dateKey.split('-').map(Number)
    if (isNaN(year) || isNaN(month) || isNaN(day)) {
      throw new Error(`Invalid date format: ${dateKey}`)
    }
    return new Date(year, month - 1, day)
  }

  static formatDateKey(date: Date): string {
    return format(date, 'yyyy-MM-dd')
  }

  static isValidTime(time: string): boolean {
    const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/
    return timeRegex.test(time)
  }

  static parseTime(time: string): { hours: number; minutes: number } {
    if (!this.isValidTime(time)) {
      throw new Error(`Invalid time format: ${time}`)
    }

    const [hours, minutes] = time.split(':').map(Number)
    return { hours, minutes }
  }

  static addTimeToDate(date: string, time: string, duration: number): Date {
    const baseDate = this.parseDateKey(date)
    const { hours, minutes } = this.parseTime(time)

    const result = new Date(baseDate)
    result.setHours(hours, minutes, 0, 0)
    result.setMinutes(result.getMinutes() + duration)

    return result
  }
}

Testing Requirements

Mandatory Playwright Testing

Before claiming any fix works, perform these visual tests:

  1. Today Group Test: Move task to "Today" in canvas, verify it appears in calendar inbox "Today" filter but NOT in calendar time slots
  2. Drag Test: Drag task from calendar inbox to calendar time slot, verify it appears as scheduled event
  3. Multi-view Test: Verify behavior is consistent across day, week, and month views
  4. State Test: Verify task properties (dueDate, instances, isInInbox) are correct throughout workflow
  5. Inbox Count Test: Verify calendar inbox shows same count as main inbox

Test Verification Checklist

  • Playwright MCP server is running and accessible
  • Task moved to "Today" appears in Calendar Inbox
  • Task moved to "Today" does NOT appear in calendar time slots
  • Manual drag from inbox to calendar works correctly
  • Task state properties are correct after each operation
  • Behavior is consistent across all calendar views
  • Calendar inbox shows correct task count regardless of sidebar filters

Validation Commands

# TypeScript compilation check
npx tsc --noEmit --skipLibCheck

# Calendar-specific tests
npm run test -- --grep "calendar"

# Development server test
npm run dev

Reference Materials

See the

references/
directory for detailed documentation:

  • task-calendar-separation.md
    - Data model separation principles
  • vue-calendar-best-practices.md
    - Vue.js calendar implementation patterns

Expected Outcomes

After successful execution:

  • Complete CalendarEvent Interface: All required properties including isDueDate
  • TaskInstance Type System: Full type safety for task instances
  • Calendar Functionality: All calendar views work properly
  • Scheduling Integration: Tasks can be scheduled on calendar
  • Temporal Type Safety: All date/time operations are type-safe
  • Inbox Sync: Calendar inbox shows correct task count
  • Task-Calendar Separation: Tasks with dueDate stay in inbox until manually scheduled

MANDATORY USER VERIFICATION REQUIREMENT

Policy: No Fix Claims Without User Confirmation

CRITICAL: Before claiming ANY issue, bug, or problem is "fixed", "resolved", "working", or "complete", the following verification protocol is MANDATORY:

Step 1: Technical Verification

  • Run all relevant tests (build, type-check, unit tests)
  • Verify no console errors
  • Take screenshots/evidence of the fix

Step 2: User Verification Request

REQUIRED: Use the

AskUserQuestion
tool to explicitly ask the user to verify the fix:

"I've implemented [description of fix]. Before I mark this as complete, please verify:
1. [Specific thing to check #1]
2. [Specific thing to check #2]
3. Does this fix the issue you were experiencing?

Please confirm the fix works as expected, or let me know what's still not working."

Step 3: Wait for User Confirmation

  • DO NOT proceed with claims of success until user responds
  • DO NOT mark tasks as "completed" without user confirmation
  • DO NOT use phrases like "fixed", "resolved", "working" without user verification

Step 4: Handle User Feedback

  • If user confirms: Document the fix and mark as complete
  • If user reports issues: Continue debugging, repeat verification cycle

Prohibited Actions (Without User Verification)

  • Claiming a bug is "fixed"
  • Stating functionality is "working"
  • Marking issues as "resolved"
  • Declaring features as "complete"
  • Any success claims about fixes

Required Evidence Before User Verification Request

  1. Technical tests passing
  2. Visual confirmation via Playwright/screenshots
  3. Specific test scenarios executed
  4. Clear description of what was changed

Remember: The user is the final authority on whether something is fixed. No exceptions.