Files
Orchestrator/.kiro/specs/call-list/tasks.md
2026-06-30 20:37:40 +02:00

11 KiB

Implementation Plan: Call List Feature

Overview

This plan implements the Call List feature for OrgMyLife — a dedicated phone-call tracking system with a new CallItem model, CRUD API, phone number extraction service, frontend "📞 Calls" tab, and Daily Digest integration. Tasks are ordered so each step builds on the previous, with no orphaned code.

Tasks

  • 1. Database model and migration

    • 1.1 Add CallItem model to app/models.py

      • Add CallItem SQLAlchemy class with fields: id, person, reason, phone_number, original_task_id (FK → tasks.id), priority, status, created_at
      • Follow existing model patterns (Column types, server_default for timestamps)
      • Requirements: 3.1, 3.2, 3.3
    • 1.2 Create Alembic migration 005_add_call_items.py

      • Create call_items table with all columns, FK constraint, and indexes on id and status
      • Include downgrade() that drops the table and indexes
      • Requirements: 3.1, 3.2
  • 2. Phone extractor service

    • 2.1 Create app/services/phone_extractor.py

      • Implement extract_phone_number(text: str) -> str | None with regex patterns for: +49 international, 0xxx domestic, (0xxx) parenthesized area code
      • Implement normalize_phone_number(raw: str) -> str to strip separators and standardize
      • Implement parse_phone_number(text: str) -> dict | None to decompose into country_code, area_code, subscriber
      • Return first match when multiple numbers found; return None when no match
      • Requirements: 4.1, 4.2, 4.3, 4.4, 4.5
    • * 2.2 Write property test: Phone number extraction (Property 2)

      • Property 2: Phone number extraction from German formats
      • Generate random valid German phone numbers in +49, 0xxx, and (0xxx) formats embedded in arbitrary text
      • Assert extract_phone_number returns a non-null result matching the first occurrence
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_phone_extractor.py
      • Validates: Requirements 4.1, 4.2, 4.3, 4.4
    • * 2.3 Write property test: Phone number round-trip (Property 3)

      • Property 3: Phone number parse-format round-trip
      • Generate valid German phone number strings, parse → normalize → parse again
      • Assert the two parse results are equivalent
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_phone_extractor.py
      • Validates: Requirements 4.6
  • 3. Call List API routes and service logic

    • 3.1 Add Pydantic models for Call List in app/main.py

      • CallItemCreate: task_id (optional), person (optional), reason (optional), phone_number (optional), priority (default 3)
      • CallItemUpdate: phone_number, priority, status, resolve_task (bool)
      • CallItemResponse: id, person, reason, phone_number, original_task_id, priority, status, created_at
      • Implement field truncation (person≤200, reason≤500, phone_number≤30) and priority clamping [1,4] in model_post_init
      • Requirements: 9.1, 9.2, 9.3, 9.4, 9.5, 9.6
    • 3.2 Implement POST /api/calls endpoint

      • If task_id provided: load task, extract sender name from description, extract phone number via phone_extractor, create CallItem with person=sender, reason=task.title, original_task_id=task.id
      • If no task_id: require person and reason fields, return 422 if missing
      • Assign default priority=3, status="pending"
      • Require session auth (401 if unauthenticated)
      • Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 2.1, 2.5
    • 3.3 Implement GET /api/calls endpoint

      • Return all CallItems with status="pending", ordered by priority ASC then created_at ASC
      • Require session auth
      • Requirements: 2.2, 2.5
    • 3.4 Implement PUT /api/calls/{id} endpoint

      • Update specified fields (phone_number, priority, status)
      • Validate status is "pending" or "done" only; reject others with 400
      • If status="done" and resolve_task=true and original_task_id exists: mark original task as "completed" and trigger existing side effects (email archiving, Nextcloud sync)
      • If status="done" and resolve_task=false: only update CallItem
      • Return 404 if ID not found; require session auth
      • Requirements: 2.3, 2.5, 2.6, 3.3, 8.1, 8.2, 8.3
    • 3.5 Implement DELETE /api/calls/{id} endpoint

      • Remove CallItem from database
      • Return 404 if ID not found; require session auth
      • Requirements: 2.4, 2.5, 2.6
    • * 3.6 Write property test: Field truncation (Property 5)

      • Property 5: Field length truncation
      • Generate strings longer than max lengths for person, reason, phone_number
      • Assert stored values are truncated to 200, 500, 30 chars respectively and equal the first N chars of input
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_call_list.py
      • Validates: Requirements 9.1, 9.2, 9.3
    • * 3.7 Write property test: Priority clamping (Property 6)

      • Property 6: Priority clamping
      • Generate arbitrary integers, assert stored priority is clamped to [1, 4]
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_call_list.py
      • Validates: Requirements 9.4
    • * 3.8 Write property test: Status constraint (Property 7)

      • Property 7: Status constraint
      • Generate arbitrary status strings, assert only "pending" and "done" are accepted
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_call_list.py
      • Validates: Requirements 3.3
  • 4. Checkpoint - Ensure all tests pass

    • Ensure all tests pass, ask the user if questions arise.
  • 5. Frontend: Calls tab and phone icon on tasks

    • 5.1 Add "📞 Calls" navigation button to frontend/index.html

      • Insert new nav button between "Projects" and "Daily Digest"
      • Add #calls-view section with call items list container
      • Requirements: 5.1
    • 5.2 Implement Calls tab view in frontend/js/app.js

      • Add loadCalls() function: fetch GET /api/calls, render list with person, reason, phone (as tel: link), priority badge
      • Add "Mark Done" button per item (sends PUT with status="done", prompts about resolving original task if original_task_id exists)
      • Add "Delete" button per item (sends DELETE)
      • Add inline edit for phone_number field (sends PUT on blur/enter)
      • Order items by priority then creation date
      • Wire "📞 Calls" nav button to switchTab("calls")
      • Requirements: 5.2, 5.3, 5.4, 5.5, 5.6, 5.7
    • 5.3 Add 📞 icon button to task items in Task List view

      • Add phone icon button to existing task action buttons in loadAllTasks() render
      • On click: send POST /api/calls with { task_id: <id> }
      • Show confirmation indicator (brief flash or text) on success
      • Requirements: 6.1, 6.2, 6.3
    • 5.4 Add CSS styles for Calls view in frontend/css/style.css

      • Style call item cards, tel: links, inline phone edit, priority badges
      • Match existing card/list styling patterns
      • Requirements: 5.2, 5.3
  • 6. Daily Digest integration

    • 6.1 Extend generate_daily_digest() in app/services/digest.py

      • Import CallItem model
      • Query all CallItems with status="pending"
      • Add "pending_calls" section to response dict with person and reason for each item
      • Omit section if no pending calls exist
      • Requirements: 7.1, 7.2, 7.3
    • 6.2 Render pending calls in Digest frontend view

      • In loadDigest(), render the pending_calls section when present in API response
      • Display as a "📞 Calls to make" card with person name and reason per item
      • Requirements: 7.1, 7.2
    • * 6.3 Write property test: Digest includes all pending calls (Property 8)

      • Property 8: Digest includes all pending calls
      • Generate a non-empty set of pending CallItems, run generate_daily_digest
      • Assert response contains pending_calls with an entry for each pending item (person + reason)
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_call_digest.py
      • Validates: Requirements 7.1, 7.2
  • 7. Create-from-task logic and completion flow tests

    • * 7.1 Write property test: Create-from-task field mapping (Property 1)

      • Property 1: Create-from-task field mapping
      • Generate tasks with origin "gmail"/"dhive_email" and description starting with "From: {name}"
      • Assert created CallItem has: person=extracted sender, reason=task.title, original_task_id=task.id, priority=3, status="pending"
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_call_list.py
      • Validates: Requirements 1.1, 1.2, 1.3, 1.5
    • * 7.2 Write property test: Call list ordering (Property 4)

      • Property 4: Call list ordering
      • Generate sets of pending CallItems with varying priorities and creation dates
      • Assert GET /api/calls returns items sorted by priority ASC, then created_at ASC
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_call_list.py
      • Validates: Requirements 2.2
    • * 7.3 Write property test: Completion without task resolution (Property 9)

      • Property 9: Call completion without task resolution
      • Generate CallItems with original_task_id, mark as done with resolve_task=false
      • Assert only CallItem status changes; original task status remains unchanged
      • Use Hypothesis with @settings(max_examples=100)
      • Place in OrgMyLife/tests/test_call_list.py
      • Validates: Requirements 8.3
    • * 7.4 Write unit tests for API edge cases

      • Test 401 for unauthenticated requests to all /api/calls endpoints
      • Test 404 for non-existent call item IDs
      • Test 422 for POST without person/reason (when no task_id)
      • Test DELETE removes item from database
      • Test phone extractor returns None for text without numbers
      • Place in OrgMyLife/tests/test_call_list.py
      • Requirements: 2.5, 2.6, 4.5, 9.5, 9.6
  • 8. Final checkpoint - Ensure all tests pass

    • Ensure all tests pass, ask the user if questions arise.

Notes

  • Tasks marked with * are optional and can be skipped for faster MVP
  • Each task references specific requirements for traceability
  • Checkpoints ensure incremental validation
  • Property tests validate universal correctness properties from the design document (Hypothesis, min 100 examples)
  • Unit tests validate specific examples and edge cases
  • The project uses Python (FastAPI + SQLAlchemy + Alembic) for backend and vanilla HTML/CSS/JS for frontend
  • Test files: tests/test_phone_extractor.py, tests/test_call_list.py, tests/test_call_digest.py