---
phase: 14-chat-integration-and-display
plan: 03
type: execute
wave: 1
depends_on: []
files_modified:
  - jam-ui/src/store/features/sessionChatSlice.js
  - jam-ui/src/store/features/__tests__/sessionChatSlice.test.js
autonomous: true
gap_closure: true

must_haves:
  truths:
    - "Chat history includes attachments (persist across page refresh, visible when joining session)"
    - "Attachment messages from REST API display with metadata (filename, type)"
    - "Attachment messages from REST API are clickable"
  artifacts:
    - path: "jam-ui/src/store/features/sessionChatSlice.js"
      provides: "REST API attachment field transformation in fetchChatHistory.fulfilled"
      contains: "music_notation"
    - path: "jam-ui/src/store/features/__tests__/sessionChatSlice.test.js"
      provides: "Test coverage for REST API attachment transformation"
      contains: "music_notation"
  key_links:
    - from: "sessionChatSlice.js fetchChatHistory.fulfilled"
      to: "JKChatMessage.js isAttachmentMessage"
      via: "attachmentId and attachmentName fields"
      pattern: "attachmentId.*music_notation"
---

<objective>
Close verification gap: Transform REST API `music_notation` nested object to flat attachment fields

Purpose: Fix inconsistent user experience where attachments display correctly via WebSocket (real-time) but not via REST API (page refresh/join session). REQ-2.5 "Chat history includes attachments" is blocked by this gap.

Output: Updated sessionChatSlice.js with REST API attachment transformation matching WebSocket path
</objective>

<execution_context>
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/14-chat-integration-and-display/14-VERIFICATION.md

# Prior work reference
@.planning/phases/14-chat-integration-and-display/14-01-SUMMARY.md
@.planning/phases/14-chat-integration-and-display/14-02-SUMMARY.md

# Source files
@jam-ui/src/store/features/sessionChatSlice.js
@jam-ui/src/components/client/JKSessionScreen.js (lines 575-596 for WebSocket transform reference)
@web/app/views/api_chats/show.rabl (REST API response format)
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add REST API attachment transformation (TDD)</name>
  <files>
    jam-ui/src/store/features/__tests__/sessionChatSlice.test.js
    jam-ui/src/store/features/sessionChatSlice.js
  </files>
  <action>
    **RED phase - Write failing test first:**

    Add test case to sessionChatSlice.test.js in the `fetchChatHistory` describe block:

    ```javascript
    it('should transform music_notation nested object to attachment fields', async () => {
      // Mock REST API response with attachment message
      getChatMessages.mockResolvedValueOnce({
        messages: [
          {
            id: 'msg-1',
            user_id: 'user-1',
            user: { name: 'Test User' },
            message: null,  // Attachment messages may have no text
            created_at: '2024-01-15T12:00:00Z',
            channel: 'session',
            purpose: 'Notation File',
            music_notation: {
              id: 'notation-uuid-123',
              file_name: 'sheet-music.pdf',
              attachment_type: 'notation'
            }
          }
        ],
        next: null
      });

      const store = createTestStore();
      await store.dispatch(fetchChatHistory({ channel: 'session-1', sessionId: 'session-1' }));

      const messages = store.getState().sessionChat.messagesByChannel['session-1'];
      expect(messages).toHaveLength(1);
      expect(messages[0].attachmentId).toBe('notation-uuid-123');
      expect(messages[0].attachmentName).toBe('sheet-music.pdf');
      expect(messages[0].attachmentType).toBe('notation');
      expect(messages[0].purpose).toBe('Notation File');
      // Note: attachmentSize not available from REST API, should be undefined/null
    });
    ```

    Run test to confirm it fails (attachmentId will be undefined).

    **GREEN phase - Implement transformation:**

    Update `fetchChatHistory.fulfilled` handler in sessionChatSlice.js (around line 312-319).

    Change the `transformedMessages` mapping from:
    ```javascript
    const transformedMessages = messages.map(m => ({
      id: m.id,
      senderId: m.user_id,
      senderName: m.user?.name || 'Unknown',
      message: m.message,
      createdAt: m.created_at,
      channel: m.channel
    }));
    ```

    To:
    ```javascript
    const transformedMessages = messages.map(m => ({
      id: m.id,
      senderId: m.user_id,
      senderName: m.user?.name || 'Unknown',
      message: m.message,
      createdAt: m.created_at,
      channel: m.channel,
      // Attachment fields from REST API (music_notation nested object)
      purpose: m.purpose,
      attachmentId: m.music_notation?.id,
      attachmentName: m.music_notation?.file_name,
      attachmentType: m.music_notation?.attachment_type,
      attachmentSize: null  // Not available in REST API response
    }));
    ```

    Run test to confirm it passes.

    **REFACTOR phase (optional):**
    - Consider extracting field mapping to a helper function if useful
    - Add JSDoc comment explaining the transformation matches WebSocket format
  </action>
  <verify>
    cd jam-ui && npm run test:unit -- sessionChatSlice.test.js
    # All tests pass including new attachment transformation test
  </verify>
  <done>
    - Test case added for REST API attachment transformation
    - fetchChatHistory.fulfilled transforms music_notation to flat attachment fields
    - Mapping matches WebSocket path: attachmentId, attachmentName, attachmentType, purpose
    - All sessionChatSlice tests pass (new test + existing 85+ tests)
  </done>
</task>

<task type="auto">
  <name>Task 2: Verify gap closure with integration test</name>
  <files>
    jam-ui/test/chat-attachments/chat-history-attachments.spec.ts (new or existing)
  </files>
  <action>
    **Option A - If Playwright attachment tests exist:**
    Run existing chat attachment integration tests to verify the fix works end-to-end.

    **Option B - Manual verification:**
    If no existing integration test covers this scenario, verify manually:
    1. Start jam-ui dev server
    2. Join a session that has existing attachment messages
    3. Verify attachment messages display with metadata (filename, clickable link)
    4. Refresh the page
    5. Verify attachment messages still display correctly after refresh

    **Option C - Add focused integration test (if time permits):**
    Create test that:
    1. Mocks REST API response with attachment message
    2. Verifies JKChatMessage renders with attachment styling
    3. Verifies filename is clickable

    Document verification approach and results.
  </action>
  <verify>
    # If integration test exists:
    cd jam-ui && npx playwright test chat --config=playwright.chrome.config.ts

    # If manual verification:
    Document steps taken and observed behavior
  </verify>
  <done>
    - REST API attachment transformation verified working
    - Attachment messages display correctly after page refresh
    - REQ-2.5 "Chat history includes attachments" now satisfied
    - Gap closure confirmed
  </done>
</task>

</tasks>

<verification>
**Phase 14 Gap Closure Complete When:**
1. Unit test verifies music_notation transformation
2. All sessionChatSlice tests pass
3. Attachment messages display correctly via both paths:
   - WebSocket (real-time): Already working
   - REST API (page refresh): Now working
4. REQ-2.5 satisfied: Chat history includes attachments
</verification>

<success_criteria>
- fetchChatHistory.fulfilled transforms music_notation nested object to flat attachment fields
- Field mapping matches WebSocket path (attachmentId, attachmentName, attachmentType, purpose)
- Unit tests pass with new attachment transformation test
- Attachments persist across page refresh (REST API path)
- Attachments visible when joining session with existing messages
</success_criteria>

<output>
After completion, create `.planning/phases/14-chat-integration-and-display/14-03-SUMMARY.md`
</output>
