# Implementation Plan

## Validation Checklist

- [x] All specification file paths are correct and exist
- [x] Context priming section is complete
- [x] All implementation phases are defined
- [x] Each phase follows TDD: Prime → Test → Implement → Validate
- [x] Dependencies between phases are clear (no circular dependencies)
- [x] Parallel work is properly tagged with `[parallel: true]`
- [x] Activity hints provided for specialist selection `[activity: type]`
- [x] Every phase references relevant SDD sections
- [x] Every test references PRD acceptance criteria
- [x] Integration & E2E tests defined in final phase
- [x] Project commands match actual project setup
- [x] A developer could follow this plan independently

---

## Specification Compliance Guidelines

### How to Ensure Specification Adherence

1. **Before Each Phase**: Read the specification sections linked in the Prime Context step
2. **During Implementation**: Reference specific SDD sections in each task
3. **After Each Task**: Run `./test.sh --testsuite unit` to verify tests pass
4. **Phase Completion**: Run full test suite + PHPStan to verify quality gates

### Deviation Protocol

If implementation cannot follow specification exactly:
1. Document the deviation and reason
2. Get approval before proceeding
3. Update SDD if the deviation is an improvement
4. Never deviate without documentation

## Metadata Reference

- `[parallel: true]` - Tasks that can run concurrently
- `[component: component-name]` - For multi-component features
- `[ref: document/section; lines: 1, 2-3]` - Links to specifications
- `[activity: type]` - Activity hint for specialist agent selection

---

## Context Priming

*GATE: You MUST fully read all files mentioned in this section before starting any implementation.*

**Specification**:

- `docs/specs/038-shift-role-assignment/product-requirements.md` - Product Requirements
- `docs/specs/038-shift-role-assignment/solution-design.md` - Solution Design

**Key Design Decisions**:

- ADR-1: Clock-in confirmation is Step 6 inside existing clock-in modal (not a separate modal)
- ADR-2: Position + tasks bundled in clock-in API response (no extra frontend API calls)
- ADR-3: Enrichment failures are non-blocking — clock-in always succeeds
- ADR-4: Syncfusion DropDownList for position selector in schedule editor
- ADR-5: New dedicated endpoint `GET /api/:typeNum/schedule/employees/:employeeId/positions`

**Implementation Context**:

- Commands to run:
  - `./test.sh` — Run all tests
  - `./test.sh --testsuite unit` — Unit tests only
  - `./test.sh --testsuite integration` — Integration tests only
  - `cd userfrosting && ./vendor/bin/phpstan analyse` — Static analysis
  - `php userfrosting/conductor build-css --minify` — CSS build (after CSS changes)
- Patterns to follow:
  - Controller → Repository → Model layered pattern
  - Existing `sendErrorResponse(message, httpCode, errorCode)` for API errors
  - Existing `jsonResponse(data, httpCode)` for success responses
  - Syncfusion EJ2 component initialization pattern in ScheduleCalendar.js
- Key files to read before starting:
  - `userfrosting/src/BuyerKiosk/Scheduling/Controllers/SchedulingController.php` (lines 605-810)
  - `userfrosting/src/BuyerKiosk/Workbook/Controllers/TimePunchController.php` (lines 428-1190)
  - `userfrosting/src/BuyerKiosk/Scheduling/Repositories/EmployeePositionRepository.php`
  - `public_html/js/scheduling/ScheduleCalendar.js` (lines 355-395, 1255-1270, 1742-1860)
  - `public_html/js/workspace/modules/workbook/time-punch.js` (lines 693-750, 1053-1062)
  - `userfrosting/templates/themes/default/workspace/partials/modals/clock-in-modal.html`

---

## Implementation Phases

### Phase 1: Backend Position Validation (Foundation)

- [ ] T1 **Backend Position Validation for Shift CRUD**

    - [ ] T1.1 Prime Context
        - [ ] T1.1.1 Read SchedulingController createShift() and updateShift() `[ref: SDD/Internal API Changes; SchedulingController.php lines 605-810]`
        - [ ] T1.1.2 Read EmployeePositionRepository methods `[ref: SDD/ICO-2; EmployeePositionRepository.php lines 33-68]`
        - [ ] T1.1.3 Read existing shift creation test patterns `[ref: tests/Unit/Scheduling/]`
        - [ ] T1.1.4 Read existing `isset($data['positionId'])` handling in createShift (line 647) and updateShift (line 760) — validation must be inserted BEFORE these existing set calls `[ref: Codex review - input handling]`

    - [ ] T1.2 Write Tests `[activity: backend-test]`
        - [ ] T1.2.1 Test: createShift rejects positionId not in employee's positions → 400 INVALID_POSITION `[ref: PRD Feature 3 AC; SDD Test Scenario 2]`
        - [ ] T1.2.2 Test: createShift accepts positionId that employee holds → 201 success `[ref: PRD Feature 1 AC; SDD Test Scenario 1]`
        - [ ] T1.2.3 Test: createShift with null positionId (no position) → 201 success (backward compat) `[ref: SDD Test Scenario 8]`
        - [ ] T1.2.4 Test: createShift for open shift (null employeeId) with any positionId → 201 success `[ref: PRD Feature 1 AC; SDD Test Scenario 3]`
        - [ ] T1.2.5 Test: updateShift rejects invalid positionId → 400 INVALID_POSITION `[ref: PRD Feature 2 AC]`
        - [ ] T1.2.6 Test: updateShift accepts valid positionId → 200 success `[ref: PRD Feature 2 AC]`
        - [ ] T1.2.7 Test: updateShift with employee change — position validation against new employee `[ref: SDD Edge Case: Employee change clears invalid position]`
        - [ ] T1.2.8 Test: updateShift employeeId set to null (open shift) — positionId is preserved, no validation error `[ref: Codex review - edge case]`
        - [ ] T1.2.9 Test: createShift with inactive position → 400 INVALID_POSITION (position must be active) `[ref: Codex review - inactive positions]`

    - [ ] T1.3 Implement `[activity: backend-api]`
        - [ ] T1.3.1 Add `validateEmployeePosition(int $employeeId, int $positionId): bool` private method to SchedulingController — uses EmployeePositionRepository::getPositionIdsForEmployee() `[ref: SDD/Implementation Examples]`
        - [ ] T1.3.2 Add position validation block in `createShift()` BEFORE the existing `isset($data['positionId'])` block (line 647) `[ref: SDD/Implementation Examples]` **NOTE: existing code already handles positionId set/get — validation must wrap the existing logic, not duplicate it**
        - [ ] T1.3.3 Add position validation block in `updateShift()` BEFORE the existing `array_key_exists('positionId', $data)` block (line 760) `[ref: SDD/Internal API Changes]`
        - [ ] T1.3.4 Ensure open shifts (null employeeId) skip position validation `[ref: SDD Business Rule 4]`
        - [ ] T1.3.5 Ensure validation also checks position isActive via PositionRepository::findById() — reject inactive positions `[ref: Codex review - inactive positions]`

    - [ ] T1.4 Validate `[activity: run-tests]`
        - [ ] T1.4.1 Run `./test.sh --testsuite unit` — all new tests pass
        - [ ] T1.4.2 Run `./test.sh` — no regressions in existing tests
        - [ ] T1.4.3 Run `cd userfrosting && ./vendor/bin/phpstan analyse src/BuyerKiosk/Scheduling/Controllers/SchedulingController.php`

---

### Phase 2: Employee Positions API Endpoint

- [ ] T2 **New GET Endpoint for Employee Positions**

    - [ ] T2.1 Prime Context
        - [ ] T2.1.1 Read existing scheduling routes `[ref: userfrosting/routes/scheduling.php]`
        - [ ] T2.1.2 Read EmployeePositionRepository::getPositionsForEmployee `[ref: EmployeePositionRepository.php lines 33-48]`
        - [ ] T2.1.3 Read PositionRepository::findActive for all-positions fallback `[ref: PositionRepository.php lines 74-89]`
        - [ ] T2.1.4 Read SchedulingController auth patterns — `checkWriteAuth()` (line 269) for write ops; read endpoints use `checkAccess('uri_schedule')` + store DB connection via `$this->db` (set in constructor) `[ref: Codex review - auth blocker]`

    - [ ] T2.2 Write Tests `[activity: backend-test]`
        - [ ] T2.2.1 Test: GET returns positions for employee with multiple positions `[ref: SDD/Internal API Changes - Get Employee Positions]`
        - [ ] T2.2.2 Test: GET returns empty array for employee with no positions `[ref: PRD Feature 3 Edge Case: No positions assigned]`
        - [ ] T2.2.3 Test: GET requires uri_schedule permission — 403 without it `[ref: SDD/Security]`
        - [ ] T2.2.4 Test: GET 404 for non-existent employee `[ref: SDD/Error Handling]`
        - [ ] T2.2.5 Test: GET returns only active positions (filters out isActive=0) `[ref: Codex review - inactive positions]`
        - [ ] T2.2.6 Test: GET returns positions sorted by sortOrder ASC `[ref: Codex review - sort order]`

    - [ ] T2.3 Implement `[activity: backend-api]`
        - [ ] T2.3.1 Add `getEmployeePositions(int $employeeId)` method to SchedulingController — **MUST include auth check**: verify user session and `checkAccess('uri_schedule')` permission, use `$this->db` for store-scoped DB access `[ref: SDD/Internal API Changes; Codex review - auth blocker]`
        - [ ] T2.3.2 Add route `GET /api/:typeNum/schedule/employees/:employeeId/positions` to scheduling.php `[ref: SDD/Interface Specifications]`
        - [ ] T2.3.3 Return `{ positions: [{positionId, name, color, sortOrder}] }` format — filter to active positions only, sorted by sortOrder `[ref: SDD/Internal API Changes]`

    - [ ] T2.4 Validate `[activity: run-tests]`
        - [ ] T2.4.1 Run `./test.sh --testsuite unit` — all new tests pass
        - [ ] T2.4.2 Run `./test.sh` — no regressions

---

### Phase 3: Clock-In Response Enrichment (Backend)

- [ ] T3 **Enrich Clock-In Response with Position + Tasks**

    - [ ] T3.1 Prime Context
        - [ ] T3.1.1 Read TimePunchController::clockInForBuyerKiosk response builder `[ref: TimePunchController.php lines 1049-1190]`
        - [ ] T3.1.2 Read findScheduledShiftForUser return format (already includes positionId/positionName) `[ref: TimePunchController.php lines 428-486]`
        - [ ] T3.1.3 Read DailyTaskAssignmentService::resolveEffectiveAssignment `[ref: DailyTaskAssignmentService.php lines 75-105]`
        - [ ] T3.1.4 Read PositionRepository::findById for color lookup `[ref: PositionRepository.php]`

    - [ ] T3.2 Write Tests `[activity: backend-test]`
        - [ ] T3.2.1 Test: Clock-in response includes shift.positionName and shift.positionColor when shift has position `[ref: PRD Feature 4 AC; SDD Test Scenario 4]`
        - [ ] T3.2.2 Test: Clock-in response includes tasks array with task names and completion status `[ref: PRD Feature 4 AC; SDD Test Scenario 4]`
        - [ ] T3.2.3 Test: Clock-in response includes taskSummary with total and completed counts `[ref: PRD Feature 7 AC]`
        - [ ] T3.2.4 Test: Clock-in with no position → shift.positionName is null, tasks is empty `[ref: SDD Test Scenario 5]`
        - [ ] T3.2.5 Test: Unscheduled clock-in → shift is null, tasks is empty `[ref: SDD Test Scenario 6]`
        - [ ] T3.2.6 Test: Clock-in succeeds even if task resolution throws exception (graceful degradation) `[ref: ADR-3; SDD Test Pattern 3]`
        - [ ] T3.2.7 Test: Clock-in response includes shift time range in local format (e.g., "9:00 AM") `[ref: SDD/Internal API Changes]`

    - [ ] T3.3 Implement `[activity: backend-api]`
        - [ ] T3.3.1 Add private `getTasksForPosition(int $positionId, int $userId): array` method to TimePunchController `[ref: SDD/Data Processing Pattern]`
        - [ ] T3.3.2 Add private `buildShiftEnrichment(?array $scheduledShift): array` method to TimePunchController `[ref: SDD/Implementation Examples - Enriched Clock-In Response]`
        - [ ] T3.3.3 Modify clockInForBuyerKiosk() jsonResponse to include `shift`, `tasks`, `taskSummary` fields `[ref: SDD/Internal API Changes - Clock In]`
        - [ ] T3.3.4 Wrap enrichment in try/catch — on failure, log error with context (typeNum, employeeId, shiftId) using `error_log()` and return empty defaults `[ref: ADR-3; Codex review - enrichment logging]`

    - [ ] T3.4 Validate `[activity: run-tests]`
        - [ ] T3.4.1 Run `./test.sh --testsuite unit` — all new tests pass
        - [ ] T3.4.2 Run `./test.sh` — no regressions (especially existing clock-in tests)
        - [ ] T3.4.3 Run PHPStan on TimePunchController

---

### Phase 4: Frontend - Position Dropdown in Schedule Editor

- [ ] T4 **Position Selection in Syncfusion Schedule Editor** `[component: schedule-calendar]`

    - [ ] T4.1 Prime Context
        - [ ] T4.1.1 Read ScheduleCalendar.js onPopupOpen customization `[ref: ScheduleCalendar.js lines 1742-1860]`
        - [ ] T4.1.2 Read existing employee dropdown injection pattern (addOpenShiftOptionToEditor) `[ref: ScheduleCalendar.js]`
        - [ ] T4.1.3 Read Syncfusion DropDownList API docs for initialization and change events
        - [ ] T4.1.4 Read existing empDropdownObj.change handler for task tab `[ref: ScheduleCalendar.js lines 1848-1858]`

    - [ ] T4.2 Implement Position Dropdown `[activity: frontend-js]`
        - [ ] T4.2.1 Add `fetchEmployeePositions(employeeId)` method — calls `GET /api/:typeNum/schedule/employees/:employeeId/positions` `[ref: SDD/ADR-5]`
        - [ ] T4.2.2 Add `fetchAllActivePositions()` method — calls `GET /api/:typeNum/schedule/positions` (for open shifts) `[ref: SDD Business Rule: open shifts allow any position]`
        - [ ] T4.2.3 Add `addPositionDropdownToEditor(popup, shiftData)` method — creates Syncfusion DropDownList after employee dropdown `[ref: SDD/Implementation Examples - Position Dropdown; ADR-4]`
        - [ ] T4.2.4 Call `addPositionDropdownToEditor` from `onPopupOpen` inside the existing setTimeout block (after `addOpenShiftOptionToEditor`) `[ref: SDD/Implementation Gotchas]`
        - [ ] T4.2.5 Wire employee dropdown `change` event to re-fetch positions for new employee and rebuild dropdown `[ref: SDD/Implementation Gotchas; PRD Feature 2 AC]`
        - [ ] T4.2.6 Handle auto-select when employee has exactly one position `[ref: PRD Feature 1 AC]`
        - [ ] T4.2.7 Handle empty positions state — show "No positions assigned" placeholder `[ref: PRD Feature 3 Edge Case]`
        - [ ] T4.2.8 Read selected positionId from dropdown in `onActionBegin` and include in createShift/updateShift payload `[ref: ScheduleCalendar.js lines 1255-1320]`

    - [ ] T4.3 Cleanup `[activity: frontend-js]`
        - [ ] T4.3.1 Destroy DropDownList on popup close to prevent memory leaks (in `onPopupClose`) `[ref: SDD/Implementation Gotchas]`

    - [ ] T4.4 Validate `[activity: manual-test]`
        - [ ] T4.4.1 Create shift for employee with multiple positions — verify dropdown shows only their positions
        - [ ] T4.4.2 Change employee in editor — verify position dropdown refreshes
        - [ ] T4.4.3 Create open shift — verify all positions shown
        - [ ] T4.4.4 Employee with one position — verify auto-selected
        - [ ] T4.4.5 Submit invalid position via DevTools (bypass frontend) — verify backend rejects `[ref: PRD Feature 3 AC]`

---

### Phase 5: Frontend - Clock-In Confirmation Screen

- [ ] T5 **Enhanced Clock-In Confirmation with Role and Tasks** `[component: time-punch]`

    - [ ] T5.1 Prime Context
        - [ ] T5.1.1 Read clock-in modal template `[ref: workspace/partials/modals/clock-in-modal.html]`
        - [ ] T5.1.2 Read time-punch.js clockIn() success handler `[ref: time-punch.js lines 693-750]`
        - [ ] T5.1.3 Read showSuccessMessage pattern `[ref: time-punch.js lines 1056-1062]`
        - [ ] T5.1.4 Read existing modal states (Steps 1-5) to understand the pattern `[ref: clock-in-modal.html]`

    - [ ] T5.2 Implement Modal Template `[activity: frontend-html]`
        - [ ] T5.2.1 Add Step 6 `clockStateConfirmation` div to `userfrosting/templates/themes/default/workspace/partials/modals/clock-in-modal.html` — includes success heading, position badge (`confirmPositionBadge`), shift time (`confirmShiftTime`), task list (`confirmTaskList`), dismiss button `[ref: SDD/ADR-1; PRD Feature 4 AC]`
        - [ ] T5.2.2 Add identical Step 6 to `userfrosting/templates/themes/default/workbook/modals/clock-in-modal.html` `[ref: SDD/Risks - two modal copies]` **CRITICAL: Both files must have identical Step 6 markup — diff after to verify**

    - [ ] T5.3 Implement CSS `[activity: frontend-css]`
        - [ ] T5.3.1 Create `public_html/css/admin/modules/clock-in-confirmation.css` with styles for confirmation panel `[ref: SDD/Directory Map]`
        - [ ] T5.3.2 Style position badge with dynamic color (inline style from API), shift time, task list with checkboxes, dismiss button, auto-dismiss progress bar `[ref: PRD Feature 6 AC]`
        - [ ] T5.3.3 Add dark mode support matching existing clock-in modal dark mode styles `[ref: clock-in-modal.html lines 712-800]`
        - [ ] T5.3.4 Run `php userfrosting/conductor build-css --minify` to build CSS `[ref: CLAUDE.md]`

    - [ ] T5.4 Implement JavaScript `[activity: frontend-js]`
        - [ ] T5.4.1 Add `showClockInConfirmation(responseData)` method to TimePunch class `[ref: SDD/Implementation Examples - Clock-In Confirmation Screen]`
        - [ ] T5.4.2 Add `renderConfirmationTasks(tasks, taskSummary)` method — builds task list HTML `[ref: PRD Feature 4 AC, Feature 7 AC]`
        - [ ] T5.4.3 Add `dismissConfirmation()` method — clears timer, hides confirmation, reloads punch state `[ref: PRD Feature 4 AC - dismiss behavior]`
        - [ ] T5.4.4 Update existing `hideAllStates()` (line 1019 in time-punch.js) — add `'clockStateConfirmation'` to the states array `[ref: SDD/Component Structure Pattern; Codex review - step counting]` **NOTE: hideAllStates already exists with 7 state IDs — just append the new one**
        - [ ] T5.4.5 Replace `this.showSuccessMessage('Successfully clocked in!')` in `clockIn()` with `this.showClockInConfirmation(data)` `[ref: time-punch.js line 731]`
        - [ ] T5.4.6 Replace `this.showSuccessMessage('Successfully clocked in!')` in `submitManagerOverride()` with `this.showClockInConfirmation(data)` `[ref: time-punch.js line 1216]`
        - [ ] T5.4.7 Implement 15-second auto-dismiss timer with cleanup `[ref: PRD Feature 4 AC - auto-dismiss]`
        - [ ] T5.4.8 Wire "Got it" button click to `dismissConfirmation()` `[ref: PRD Feature 4 AC]`
        - [ ] T5.4.9 Fallback: if `clockStateConfirmation` div not found, fall back to toast `[ref: SDD/Implementation Examples - fallback]`

    - [ ] T5.5 Validate `[activity: manual-test]`
        - [ ] T5.5.1 Clock in with position assigned — confirmation shows position badge with color
        - [ ] T5.5.2 Clock in without position — shows "No specific role assigned"
        - [ ] T5.5.3 Clock in unscheduled (override) — shows "Unscheduled Shift"
        - [ ] T5.5.4 Clock in with tasks — task list renders with completion status
        - [ ] T5.5.5 Clock in without tasks — shows "No tasks assigned for today"
        - [ ] T5.5.6 Auto-dismiss fires after 15 seconds
        - [ ] T5.5.7 "Got it" button dismisses immediately
        - [ ] T5.5.8 After dismiss, kiosk returns to clocked-in state
        - [ ] T5.5.9 Clock out still uses simple toast (not confirmation screen)

---

### Phase 6: Schedule Display Enhancement

- [ ] T6 **Position Badge on Schedule Shift Blocks** `[component: schedule-calendar]`

    - [ ] T6.1 Prime Context
        - [ ] T6.1.1 Read getEventTemplate() `[ref: ScheduleCalendar.js lines 355-395]`
        - [ ] T6.1.2 Read loadShiftsForDate() to understand how position data flows to the template `[ref: ScheduleCalendar.js]`

    - [ ] T6.2 Implement `[activity: frontend-js]`
        - [ ] T6.2.1 Verify `position` field is already included in shift data from API (it should be from the schedulePositions JOIN) `[ref: SDD - findScheduledShiftForUser already returns positionName]`
        - [ ] T6.2.2 Update `getEventTemplate()` to show position with color badge — add `background-color` style from position color data `[ref: PRD Feature 5 AC; SDD/Building Block View]`
        - [ ] T6.2.3 Ensure shifts without position still display correctly (no badge) `[ref: PRD Feature 5 AC - backward compatible]`

    - [ ] T6.3 Validate `[activity: manual-test]`
        - [ ] T6.3.1 Shifts with positions show colored position badge
        - [ ] T6.3.2 Shifts without positions show no badge (same as before)
        - [ ] T6.3.3 Open shifts show position name (already working — verify no regression)

---

### Phase 7: Integration & End-to-End Validation

- [ ] T7 **Full System Validation**

    - [ ] T7.1 All Unit Tests `[activity: run-tests]`
        - [ ] T7.1.1 Run `./test.sh --testsuite unit` — all tests pass
        - [ ] T7.1.2 Run `./test.sh --testsuite integration` — all integration tests pass

    - [ ] T7.2 Static Analysis `[activity: run-tests]`
        - [ ] T7.2.1 Run `cd userfrosting && ./vendor/bin/phpstan analyse` — no new errors

    - [ ] T7.3 CSS Build `[activity: build]`
        - [ ] T7.3.1 Run `php userfrosting/conductor build-css --minify` — builds successfully

    - [ ] T7.4 End-to-End Flow Tests `[activity: manual-test]`
        - [ ] T7.4.1 **E2E: Full Shift Creation with Position** — Create shift with position → verify on schedule → clock in as that employee → see position in confirmation `[ref: PRD Features 1+4+5]`
        - [ ] T7.4.2 **E2E: Two Shift Leads Scenario** — Schedule two shift leads for same day → assign one as "Shift Lead" and other as "Buyer" → each clocks in → each sees their correct role `[ref: PRD Problem Statement]`
        - [ ] T7.4.3 **E2E: Edit Shift Position** — Create shift → edit to change position → verify schedule updates → clock in → see updated position `[ref: PRD Feature 2]`
        - [ ] T7.4.4 **E2E: Backward Compatibility** — Existing shifts without positions → display unchanged → clock in works → simple confirmation (no position shown) `[ref: SDD Test Scenario 8]`
        - [ ] T7.4.5 **E2E: Unscheduled Clock-In** — Employee with no shift → manager override → confirmation shows "Unscheduled Shift" `[ref: SDD Test Scenario 6]`
        - [ ] T7.4.6 **E2E: Position Removed After Shift Created** — Create shift with position → remove that position from employee's assigned positions → employee clocks in → verify graceful degradation (shift still shows position from record, clock-in succeeds with position display) `[ref: Codex review - enhancement]`

    - [ ] T7.5 Security Validation `[activity: security-review]`
        - [ ] T7.5.1 Verify position validation cannot be bypassed by direct API call (test with curl/Postman) `[ref: PRD Feature 3 AC - defense in depth]`
        - [ ] T7.5.2 Verify employee positions endpoint requires uri_schedule permission `[ref: SDD/Security]`

    - [ ] T7.6 Performance Validation `[activity: performance-test]`
        - [ ] T7.6.1 Verify clock-in response time remains under 2 seconds with enrichment `[ref: SDD/Quality Requirements]`
        - [ ] T7.6.2 Verify position dropdown loads in under 200ms `[ref: SDD/Quality Requirements]`

    - [ ] T7.7 Specification Compliance `[activity: business-acceptance]`
        - [ ] T7.7.1 Review all PRD Feature 1-7 acceptance criteria are met
        - [ ] T7.7.2 Review all SDD Test Scenarios 1-8 pass
        - [ ] T7.7.3 Verify no regressions in WhenIWork provider flow
        - [ ] T7.7.4 Verify schedule panel cache invalidation still works correctly

    - [ ] T7.8 Documentation `[activity: documentation]`
        - [ ] T7.8.1 Update spec README with completion status
        - [ ] T7.8.2 Check if mobile app API updates file needs updating `[ref: CLAUDE.md - Inter-Agent Communication]`
