Claude-skill-registry calendar-interface-architect
COMPREHENSIVE calendar system skill combining TypeScript interface architecture,
git clone https://github.com/majiayu000/claude-skill-registry
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"
skills/data/calendar-interface-architect/SKILL.mdCalendar 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:
- Property 'isDueDate' does not exist in type 'CalendarEvent'
- TaskInstance type mismatches in calendar operations
- Missing temporal interface properties for scheduling
- 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
which applies sidebar filterstaskStore.filteredTasks - 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
field for deadline metadata, stay in inbox until manually scheduleddueDate - Calendar Events: Use
array for time-specific commitments with scheduled dates/timesinstances - State Management: Maintain clear distinction between inbox state (
) and scheduled stateisInInbox: true
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:
- Today Group Test: Move task to "Today" in canvas, verify it appears in calendar inbox "Today" filter but NOT in calendar time slots
- Drag Test: Drag task from calendar inbox to calendar time slot, verify it appears as scheduled event
- Multi-view Test: Verify behavior is consistent across day, week, and month views
- State Test: Verify task properties (dueDate, instances, isInInbox) are correct throughout workflow
- 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:
- Data model separation principlestask-calendar-separation.md
- Vue.js calendar implementation patternsvue-calendar-best-practices.md
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
- Technical tests passing
- Visual confirmation via Playwright/screenshots
- Specific test scenarios executed
- Clear description of what was changed
Remember: The user is the final authority on whether something is fixed. No exceptions.