Files
chore/.github/specs/archive/feat-child-confirm-chore.md
Ryan Kegel ebaef16daf
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m23s
feat: implement long-term user login with refresh tokens
- Introduced a dual-token system for user authentication: a short-lived access token and a long-lived rotating refresh token.
- Created a new RefreshToken model to manage refresh tokens securely.
- Updated auth_api.py to handle login, refresh, and logout processes with the new token system.
- Enhanced security measures including token rotation and theft detection.
- Updated frontend to handle token refresh on 401 errors and adjusted SSE authentication.
- Removed CORS middleware as it's unnecessary behind the nginx proxy.
- Added tests to ensure functionality and security of the new token system.
2026-03-01 19:27:25 -05:00

39 KiB

Feature: Chore Completion Confirmation + Task Refactor

Overview

Goal: Refactor the "task" concept into three distinct entity types — Chore, Kindness, and Penalty — and implement a chore completion confirmation flow where children can confirm chores and parents approve them.

User Stories:

  • As a child, I can click on a chore and confirm that I completed it. I see a PENDING banner (yellow) until a parent confirms.
  • As a child, I can click an already PENDING chore and cancel my confirmation.
  • As a child, I see a COMPLETED banner (green) on a chore that a parent has approved. That chore is disabled for the rest of the day.
  • As a parent, I see pending chore confirmations in the Notifications tab alongside pending reward requests.
  • As a parent, I can click a PENDING chore to approve it (awarding points) or reject it (resetting to available).
  • As a parent, I can click a non-pending/non-completed chore and award points directly (current behavior). The child view then shows the COMPLETED banner.
  • As a parent, I can reset a completed chore from the kebab menu so the child can confirm it again (points are kept).
  • As an admin, I can view full tracking history in the database/logs for all confirmation lifecycle events.

Rules: .github/copilot-instructions.md


Design Decisions (Resolved)

Task Refactor → Chore / Kindness / Penalty

Decision: Full refactor.

Old Concept New Concept Behavior
Task with is_good=true (schedulable) Chore Scheduled, expirable, confirmable by child
Task with is_good=true (ad-hoc, e.g. "Child was good today") Kindness Parent-only award, not confirmable
Task with is_good=false Penalty Parent-only deduction
  • The is_good field is removed. Entity type itself determines point direction.
  • The Task model is retained in the backend but gains a type field: 'chore' | 'kindness' | 'penalty'.
  • task_api.py is split into chore_api.py, kindness_api.py, penalty_api.py.
  • Existing is_good=true tasks are auto-classified as chore; is_good=false as penalty.
  • Kindness items must be manually created post-migration (acceptable).

Merged Pending Table

Decision: Single PendingConfirmation model replaces PendingReward.

  • Both pending reward requests and pending chore confirmations live in one pending_confirmations table, differentiated by entity_type.
  • The /pending-rewards endpoint is replaced by /pending-confirmations.
  • pending_rewards.json DB file is replaced by pending_confirmations.json.

"Completed Today" Tracking

Decision: PendingConfirmation record with status='approved' + approved_at timestamp.

  • An approved PendingConfirmation record persists (DB-backed, survives restart) and serves as the "completed today" marker.
  • The frontend checks if approved_at is today to determine the COMPLETED state.
  • On reset, the record is deleted (status returns to available).
  • Multi-completion history is preserved via TrackingEvent — each confirm/approve/reset cycle generates tracking entries. Query TrackingEvent by child_id + entity_id + date + action='approved' to count completions per day.

Navigation

Decision: Sub-nav under "Tasks" tab.

  • Top-level nav stays 4 items: Children | Tasks | Rewards | Notifications
  • The "Tasks" tab opens a view with 3 sub-tabs: Chores | Kindness | Penalties
  • Each sub-tab has its own list view, edit view, and assign view.
  • No mobile layout changes needed to the top bar.

Chore Confirmation Scoping

  • Each PendingConfirmation is scoped to a single child. If a chore is assigned to multiple children, each confirms independently.
  • Expired chores cannot be confirmed.
  • A chore that is already PENDING or COMPLETED today cannot be confirmed again (unless reset by parent).

Data Model Changes

Backend Models

Task Model (Modified)

File: backend/models/task.py

@dataclass
class Task(BaseModel):
    name: str
    points: int
    type: Literal['chore', 'kindness', 'penalty']  # replaces is_good
    image_id: str | None = None
    user_id: str | None = None
  • is_good: boolremoved
  • type: Literal['chore', 'kindness', 'penalty']added
  • Migration: is_good=Truetype='chore', is_good=Falsetype='penalty'

PendingConfirmation Model (New — replaces PendingReward)

File: backend/models/pending_confirmation.py

@dataclass
class PendingConfirmation(BaseModel):
    child_id: str
    entity_id: str            # task_id or reward_id
    entity_type: str           # 'chore' | 'reward'
    user_id: str
    status: str = "pending"    # 'pending' | 'approved' | 'rejected'
    approved_at: str | None = None  # ISO 8601 UTC timestamp, set on approval
  • Replaces PendingReward (which had child_id, reward_id, user_id, status)
  • entity_id generalizes reward_id to work for both chores and rewards
  • entity_type differentiates between chore confirmations and reward requests
  • approved_at enables "completed today" checks

TrackingEvent Model (Extended Types)

File: backend/models/tracking_event.py

EntityType = Literal['task', 'reward', 'penalty', 'chore', 'kindness']
ActionType = Literal['activated', 'requested', 'redeemed', 'cancelled', 'confirmed', 'approved', 'rejected', 'reset']

New actions:

  • confirmed — child marks chore as done
  • approved — parent approves chore completion (points awarded)
  • rejected — parent rejects chore completion (no point change)
  • reset — parent resets a completed chore (no point change)

ChildOverride Model (Extended Types)

File: backend/models/child_override.py

entity_type: Literal['task', 'reward']  # → Literal['chore', 'kindness', 'penalty', 'reward']

ChildTask DTO (Modified)

File: backend/api/child_tasks.py

class ChildTask:
    def __init__(self, name, type, points, image_id, id):
        self.id = id
        self.name = name
        self.type = type        # replaces is_good
        self.points = points
        self.image_id = image_id

SSE Event Types (New)

File: backend/events/types/event_types.py

class EventType(Enum):
    # ... existing ...
    CHILD_CHORE_CONFIRMATION = "child_chore_confirmation"

New payload class — File: backend/events/types/child_chore_confirmation.py

class ChildChoreConfirmation(Payload):
    # child_id: str
    # task_id: str
    # operation: 'CONFIRMED' | 'APPROVED' | 'REJECTED' | 'CANCELLED' | 'RESET'

Error Codes (New)

File: backend/api/error_codes.py

class ErrorCodes:
    # ... existing ...
    CHORE_EXPIRED = "CHORE_EXPIRED"
    CHORE_ALREADY_PENDING = "CHORE_ALREADY_PENDING"
    CHORE_ALREADY_COMPLETED = "CHORE_ALREADY_COMPLETED"
    PENDING_NOT_FOUND = "PENDING_NOT_FOUND"
    INSUFFICIENT_POINTS = "INSUFFICIENT_POINTS"

Frontend Models

File: frontend/vue-app/src/common/models.ts

// Task — modified
export interface Task {
  id: string;
  name: string;
  points: number;
  type: "chore" | "kindness" | "penalty"; // replaces is_good
  image_id: string | null;
  image_url?: string | null;
}

// ChildTask — modified
export interface ChildTask {
  id: string;
  name: string;
  type: "chore" | "kindness" | "penalty"; // replaces is_good
  points: number;
  image_id: string | null;
  image_url?: string | null;
  custom_value?: number | null;
  schedule?: ChoreSchedule | null;
  extension_date?: string | null;
}

// PendingConfirmation — new (replaces PendingReward)
export interface PendingConfirmation {
  id: string;
  child_id: string;
  child_name: string;
  child_image_id: string | null;
  child_image_url?: string | null;
  entity_id: string;
  entity_type: "chore" | "reward";
  entity_name: string;
  entity_image_id: string | null;
  entity_image_url?: string | null;
  status: "pending" | "approved" | "rejected";
  approved_at: string | null;
}

// EntityType — extended
export type EntityType = "chore" | "kindness" | "penalty" | "reward";

// ActionType — extended
export type ActionType =
  | "activated"
  | "requested"
  | "redeemed"
  | "cancelled"
  | "confirmed"
  | "approved"
  | "rejected"
  | "reset";

// SSE event payload — new
export interface ChildChoreConfirmationPayload {
  child_id: string;
  task_id: string;
  operation: "CONFIRMED" | "APPROVED" | "REJECTED" | "CANCELLED" | "RESET";
}

Backend Implementation

API Changes

New Files

File Description
backend/api/chore_api.py CRUD for chores (type='chore'). Routes: /chore/add, /chore/<id>, /chore/<id>/edit, /chore/list, DELETE /chore/<id>
backend/api/kindness_api.py CRUD for kindness acts (type='kindness'). Routes: /kindness/add, /kindness/<id>, /kindness/<id>/edit, /kindness/list, DELETE /kindness/<id>
backend/api/penalty_api.py CRUD for penalties (type='penalty'). Routes: /penalty/add, /penalty/<id>, /penalty/<id>/edit, /penalty/list, DELETE /penalty/<id>
backend/models/pending_confirmation.py PendingConfirmation dataclass
backend/events/types/child_chore_confirmation.py SSE payload class
backend/api/pending_confirmation.py Response DTO for hydrated pending confirmation

Modified Files

File Changes
backend/models/task.py is_goodtype field
backend/models/tracking_event.py Extend EntityType and ActionType literals
backend/models/child_override.py Extend entity_type literal
backend/api/child_tasks.py is_goodtype field in DTO
backend/api/child_api.py Add chore confirmation endpoints, replace /pending-rewards with /pending-confirmations, update trigger-task to set COMPLETED state, update all is_good references to type
backend/api/task_api.py Deprecate/remove — logic moves to entity-specific API files
backend/api/error_codes.py Add new error codes
backend/events/types/event_types.py Add CHILD_CHORE_CONFIRMATION
backend/db/db.py Add pending_confirmations_db, remove pending_reward_db
backend/main.py Register new blueprints, remove task_api blueprint

New Endpoints (on child_api.py)

Method Route Actor Description
POST /child/<id>/confirm-chore Child Body: { task_id }. Creates PendingConfirmation(entity_type='chore', status='pending'). Validates: chore assigned, not expired, not already pending/completed today. Tracking: action='confirmed', delta=0. SSE: CHILD_CHORE_CONFIRMATION (CONFIRMED).
POST /child/<id>/cancel-confirm-chore Child Body: { task_id }. Deletes the pending confirmation. Tracking: action='cancelled', delta=0. SSE: CHILD_CHORE_CONFIRMATION (CANCELLED).
POST /child/<id>/approve-chore Parent Body: { task_id }. Sets status='approved', approved_at=now(). Awards points (respects overrides). Tracking: action='approved', delta=+points. SSE: CHILD_CHORE_CONFIRMATION (APPROVED) + CHILD_TASK_TRIGGERED.
POST /child/<id>/reject-chore Parent Body: { task_id }. Deletes the pending confirmation. Tracking: action='rejected', delta=0. SSE: CHILD_CHORE_CONFIRMATION (REJECTED).
POST /child/<id>/reset-chore Parent Body: { task_id }. Deletes the approved confirmation record. Tracking: action='reset', delta=0. SSE: CHILD_CHORE_CONFIRMATION (RESET).
GET /pending-confirmations Parent Returns all pending PendingConfirmation records for the user, hydrated with child/entity names and images. Replaces /pending-rewards.

Modified Endpoints

Endpoint Change
POST /child/<id>/trigger-task When a parent triggers a chore directly (no pending), create an approved PendingConfirmation so child view shows COMPLETED. Update entity_type references from 'task' to the actual type.
POST /child/<id>/request-reward Create PendingConfirmation(entity_type='reward') instead of PendingReward.
POST /child/<id>/cancel-request-reward Query PendingConfirmation by entity_type='reward' instead of PendingReward.
POST /child/<id>/trigger-reward Query/remove PendingConfirmation by entity_type='reward' instead of PendingReward.
GET /child/<id>/list-tasks Add pending_status and approved_at fields to each chore in the response (from PendingConfirmation lookup).
PUT /child/<id>/set-tasks Accept type parameter instead of `type: 'good' 'bad'`.

Database Migration

A one-time migration script (backend/scripts/migrate_tasks_to_types.py):

  1. For each record in tasks.json: if is_good=True → set type='chore', if is_good=False → set type='penalty'. Remove is_good field.
  2. For each record in pending_rewards.json: convert to PendingConfirmation format with entity_type='reward', entity_id=reward_id. Write to pending_confirmations.json.
  3. For each record in tracking_events.json: update entity_type='task''chore' or 'penalty' based on the referenced task's old is_good value.
  4. For each record in child_overrides.json: update entity_type='task''chore' or 'penalty' based on the referenced task's old is_good value.

Backend Tests

Test File: backend/tests/test_chore_api.py (New)

  • test_add_chore — PUT /chore/add with name, points → 201, type auto-set to 'chore'
  • test_add_chore_missing_fields — 400 with MISSING_FIELDS
  • test_list_chores — GET /chore/list returns only type='chore' tasks
  • test_get_chore — GET /chore/<id> → 200
  • test_get_chore_not_found — 404
  • test_edit_chore — PUT /chore/<id>/edit → 200
  • test_edit_system_chore_clones_to_user — editing a user_id=None chore creates a user copy
  • test_delete_chore — DELETE /chore/<id> → 200, removed from children's task lists
  • test_delete_chore_not_found — 404
  • test_delete_chore_removes_from_assigned_children — cascade cleanup

Test File: backend/tests/test_kindness_api.py (New)

  • test_add_kindness — PUT /kindness/add → 201, type auto-set to 'kindness'
  • test_list_kindness — returns only type='kindness' tasks
  • test_edit_kindness — PUT /kindness/<id>/edit → 200
  • test_delete_kindness — cascade removal

Test File: backend/tests/test_penalty_api.py (New)

  • test_add_penalty — PUT /penalty/add → 201, type auto-set to 'penalty'
  • test_list_penalties — returns only type='penalty' tasks
  • test_edit_penalty — PUT /penalty/<id>/edit → 200
  • test_delete_penalty — cascade removal

Test File: backend/tests/test_chore_confirmation.py (New)

Child Confirm Flow

  • test_child_confirm_chore_success — POST /child/<id>/confirm-chore with { task_id } → 200, PendingConfirmation record created with status='pending', entity_type='chore'
  • test_child_confirm_chore_not_assigned — 400 ENTITY_NOT_ASSIGNED when chore is not in child's task list
  • test_child_confirm_chore_not_found — 404 TASK_NOT_FOUND when task_id doesn't exist
  • test_child_confirm_chore_child_not_found — 404 CHILD_NOT_FOUND
  • test_child_confirm_chore_already_pending — 400 CHORE_ALREADY_PENDING when a pending confirmation already exists
  • test_child_confirm_chore_already_completed_today — 400 CHORE_ALREADY_COMPLETED when an approved confirmation exists for today
  • test_child_confirm_chore_expired — 400 CHORE_EXPIRED when chore is past its deadline
  • test_child_confirm_chore_creates_tracking_event — TrackingEvent with action='confirmed', delta=0, entity_type='chore'
  • test_child_confirm_chore_wrong_type — 400 when task is kindness or penalty (not confirmable)

Child Cancel Flow

  • test_child_cancel_confirm_success — POST /child/<id>/cancel-confirm-chore → 200, pending record deleted
  • test_child_cancel_confirm_not_pending — 400 PENDING_NOT_FOUND
  • test_child_cancel_confirm_creates_tracking_event — TrackingEvent with action='cancelled', delta=0

Parent Approve Flow

  • test_parent_approve_chore_success — POST /child/<id>/approve-chore → 200, points increased, status='approved', approved_at set
  • test_parent_approve_chore_with_override — uses custom_value from override instead of base points
  • test_parent_approve_chore_not_pending — 400 PENDING_NOT_FOUND
  • test_parent_approve_chore_creates_tracking_event — TrackingEvent with action='approved', delta=+points
  • test_parent_approve_chore_points_correctpoints_before + task points == points_after on child

Parent Reject Flow

  • test_parent_reject_chore_success — POST /child/<id>/reject-chore → 200, pending record deleted, points unchanged
  • test_parent_reject_chore_not_pending — 400 PENDING_NOT_FOUND
  • test_parent_reject_chore_creates_tracking_event — TrackingEvent with action='rejected', delta=0

Parent Reset Flow

  • test_parent_reset_chore_success — POST /child/<id>/reset-chore → 200, approved record deleted, points unchanged
  • test_parent_reset_chore_not_completed — 400 when no approved record exists
  • test_parent_reset_chore_creates_tracking_event — TrackingEvent with action='reset', delta=0
  • test_parent_reset_then_child_confirm_again — full cycle: confirm → approve → reset → confirm → approve (two approvals tracked)

Parent Direct Trigger

  • test_parent_trigger_chore_directly_creates_approved_confirmation — POST /child/<id>/trigger-task with a chore → creates approved PendingConfirmation so child view shows COMPLETED

Pending Confirmations List

  • test_list_pending_confirmations_returns_chores_and_rewards — GET /pending-confirmations returns both types
  • test_list_pending_confirmations_empty — returns empty list when none exist
  • test_list_pending_confirmations_hydrates_names_and_images — response includes child_name, entity_name, image IDs
  • test_list_pending_confirmations_excludes_approved — only pending status returned
  • test_list_pending_confirmations_filters_by_user — only returns confirmations for the authenticated user's children

Test File: backend/tests/test_task_api.py (Modified)

  • Update all existing tests that use is_good to use type instead
  • test_add_task → split into test_add_chore, test_add_kindness, test_add_penalty (or remove if fully migrated to entity-specific APIs)
  • test_list_tasks_sorted → update sort expectations for type field

Test File: backend/tests/test_child_api.py (Modified)

  • Update tests referencing is_good to use type
  • Update set-tasks tests for new type parameter values ('chore', 'kindness', 'penalty')
  • Update list-tasks response assertions to check for pending_status and approved_at fields on chores
  • Update trigger-task tests to verify PendingConfirmation creation for chores
  • Update request-reward / cancel-request-reward / trigger-reward tests to use PendingConfirmation model
  • Replace pending-rewards endpoint tests with pending-confirmations

Frontend Implementation

New Files

File Description
src/components/task/ChoreView.vue Admin list of chores (type='chore'). Same pattern as current TaskView.vue with blue theme.
src/components/task/KindnessView.vue Admin list of kindness acts (type='kindness'). Yellow theme.
src/components/task/PenaltyView.vue Admin list of penalties (type='penalty'). Red theme.
src/components/task/ChoreEditView.vue Create/edit chore form. Fields: name, points, image. No is_good toggle.
src/components/task/KindnessEditView.vue Create/edit kindness form. Fields: name, points, image.
src/components/task/PenaltyEditView.vue Create/edit penalty form. Fields: name, points, image.
src/components/task/TaskSubNav.vue Sub-nav component with Chores / Kindness / Penalties tabs. Renders as a tab bar within the Tasks view area.
src/components/child/ChoreAssignView.vue Assign chores to child (replaces TaskAssignView with type='good').
src/components/child/KindnessAssignView.vue Assign kindness acts to child.
src/components/child/PenaltyAssignView.vue Assign penalties to child (replaces TaskAssignView with type='bad').
src/components/child/ChoreConfirmDialog.vue Modal dialog for child to confirm chore completion. "Did you finish [chore name]?" with Confirm / Cancel buttons.
src/components/child/ChoreApproveDialog.vue Modal dialog for parent to approve/reject pending chore. Shows chore name, child name, points. Approve / Reject buttons.

Modified Files

File Changes
src/common/models.ts Replace Task.is_good with Task.type, add PendingConfirmation interface, extend EntityType/ActionType, add ChildChoreConfirmationPayload, replace PendingReward with PendingConfirmation. Add pending_status and approved_at to ChildTask.
src/common/backendEvents.ts Add child_chore_confirmation event listener pattern.
src/common/api.ts Add confirmChore(), cancelConfirmChore(), approveChore(), rejectChore(), resetChore(), fetchPendingConfirmations(). Remove fetchPendingRewards().
src/components/child/ChildView.vue Add chore tap handler → show ChoreConfirmDialog. Add PENDING (yellow) / COMPLETED (green) banner rendering. Handle cancel-confirm on PENDING tap. Filter kindness acts into new scrolling row. Listen for child_chore_confirmation SSE events.
src/components/child/ParentView.vue Add PENDING/COMPLETED banners on chores. Handle approve/reject on PENDING chore tap. Add "Reset" to kebab menu for completed chores. Add "Assign Kindness" button. Update trigger-task to create approved confirmation. Replace is_good filters with type checks. Listen for child_chore_confirmation SSE events.
src/components/notification/NotificationView.vue Fetch from /api/pending-confirmations instead of /api/pending-rewards. Render both pending chores and pending rewards with differentiation (icon/label). Listen for child_chore_confirmation events in addition to existing child_reward_request.
src/layout/ParentLayout.vue "Tasks" nav icon remains, routes to a view housing TaskSubNav with sub-tabs.
src/components/task/TaskEditView.vue Remove or repurpose. Logic moves to entity-specific edit views (no is_good toggle).
src/components/task/TaskView.vue Remove or repurpose into the sub-nav container view.
src/components/child/TaskAssignView.vue Remove. Replaced by ChoreAssignView, KindnessAssignView, PenaltyAssignView.
Router config Add routes for new views. Update existing task routes to chore/kindness/penalty.

Files to Remove

File Reason
backend/models/pending_reward.py Replaced by pending_confirmation.py
backend/api/pending_reward.py Replaced by pending_confirmation.py DTO
backend/data/db/pending_rewards.json Replaced by pending_confirmations.json

ChildView Chore Tap Flow

Child taps chore card
  ├─ Chore expired? → No action (grayed out, "TOO LATE" stamp)
  ├─ Chore COMPLETED today? → No action (grayed out, "COMPLETED" stamp)
  ├─ Chore PENDING? → Show ModalDialog "Cancel confirmation?"
  │    └─ Confirm → POST /child/<id>/cancel-confirm-chore
  └─ Chore available? → Show ChoreConfirmDialog "Did you finish [name]?"
       └─ Confirm → POST /child/<id>/confirm-chore

ParentView Chore Tap Flow

Parent taps chore card
  ├─ Chore PENDING? → Show ChoreApproveDialog
  │    ├─ Approve → POST /child/<id>/approve-chore (awards points)
  │    └─ Reject → POST /child/<id>/reject-chore (resets to available)
  ├─ Chore COMPLETED today? → No tap action. Kebab menu has "Reset"
  │    └─ Reset → POST /child/<id>/reset-chore
  └─ Chore available? → Show TaskConfirmDialog (current behavior)
       └─ Confirm → POST /child/<id>/trigger-task (sets COMPLETED)

Banner Styling

State Banner Text Text Color Background CSS Variable Suggestion
Pending PENDING Yellow (--color-warning) Dark semi-transparent --banner-bg-pending
Completed COMPLETED Green (--color-success) Dark semi-transparent --banner-bg-completed
Expired TOO LATE Red (existing) Gray overlay (existing) (existing styles)

Frontend Tests

Test File: components/__tests__/ChoreConfirmDialog.test.ts (New)

  • renders chore name in dialog
  • emits confirm event on confirm button click
  • emits cancel event on cancel button click

Test File: components/__tests__/ChoreApproveDialog.test.ts (New)

  • renders chore name and points in dialog
  • emits approve event on approve button click
  • emits reject event on reject button click

Test File: components/__tests__/TaskSubNav.test.ts (New)

  • renders three sub-tabs: Chores, Kindness, Penalties
  • highlights active tab based on route
  • navigates on tab click

Test File: components/__tests__/ChoreView.test.ts (New)

  • fetches and renders chore list
  • navigates to edit on item click
  • shows delete confirmation modal
  • refreshes on task_modified SSE event

Test File: components/__tests__/NotificationView.test.ts (Modified)

  • fetches from /api/pending-confirmations
  • renders both pending chores and pending rewards
  • differentiates chore vs reward with label/icon
  • refreshes on child_chore_confirmation SSE event
  • refreshes on child_reward_request SSE event

Test File: components/__tests__/ChildView.test.ts (Modified / New)

  • shows PENDING banner on chore with pending confirmation
  • shows COMPLETED banner on chore completed today
  • opens ChoreConfirmDialog on available chore tap
  • opens cancel dialog on PENDING chore tap
  • does not allow tap on expired chore
  • does not allow tap on COMPLETED chore
  • renders kindness scrolling row
  • refreshes on child_chore_confirmation SSE event

Test File: components/__tests__/ParentView.test.ts (Modified / New)

  • shows PENDING banner on chore with pending confirmation
  • shows COMPLETED banner on approved chore
  • opens ChoreApproveDialog on PENDING chore tap
  • opens TaskConfirmDialog on available chore tap
  • shows Reset in kebab menu for completed chore
  • renders kindness scrolling row
  • shows Assign Kindness button

Future Considerations

  • Recurring chore auto-reset: Automatically clear completed status on schedule rollover (e.g., daily chores reset at midnight).
  • Chore streaks: Track consecutive days a child completes a chore using TrackingEvent history.
  • Multi-completion analytics dashboard: Query TrackingEvent to show completion counts per chore per day/week.
  • Partial credit: Allow parents to award fewer points than the chore's value when approving.
  • Chore delegation: Allow one child to reassign a chore to a sibling.
  • Photo proof: Child attaches a photo when confirming a chore.
  • Kindness auto-classification: Suggested classification when creating new items based on name patterns.

Acceptance Criteria (Definition of Done)

Backend

  • Task model uses type: 'chore' | 'kindness' | 'penalty'is_good removed
  • PendingConfirmation model created, PendingReward model removed
  • pending_confirmations_db created in db.py, pending_reward_db removed
  • Migration script converts existing tasks, pending rewards, tracking events, and overrides
  • chore_api.py, kindness_api.py, penalty_api.py created with CRUD endpoints
  • task_api.py removed or deprecated
  • Child chore confirmation endpoints: confirm-chore, cancel-confirm-chore, approve-chore, reject-chore, reset-chore
  • GET /pending-confirmations returns hydrated pending chores and rewards
  • trigger-task creates approved PendingConfirmation when parent triggers a chore directly
  • Reward request/cancel/trigger endpoints migrated to PendingConfirmation
  • list-tasks response includes pending_status and approved_at for chores
  • TrackingEvent created for every mutation: confirmed, cancelled, approved, rejected, reset
  • Tracking events logged to rotating file logger
  • SSE event CHILD_CHORE_CONFIRMATION sent for every confirmation lifecycle event
  • All new error codes defined and returned with proper HTTP status codes
  • All existing tests updated for type field (no is_good references)
  • All new backend tests pass

Frontend

  • Task and ChildTask interfaces use type instead of is_good
  • PendingConfirmation interface replaces PendingReward
  • Sub-nav under "Tasks" with Chores / Kindness / Penalties tabs
  • ChoreView, KindnessView, PenaltyView list views created
  • ChoreEditView, KindnessEditView, PenaltyEditView edit/create views created
  • ChoreAssignView, KindnessAssignView, PenaltyAssignView assign views created
  • TaskView, TaskEditView, TaskAssignView removed or repurposed
  • ChoreConfirmDialog — child confirmation modal
  • ChoreApproveDialog — parent approve/reject modal
  • ChildView — chore tap opens confirm dialog, cancel dialog for pending, banners render correctly
  • ChildView — expired and completed chores are non-interactive
  • ChildView — kindness scrolling row added
  • ParentView — pending chore tap opens approve/reject dialog
  • ParentView — available chore tap uses existing trigger flow + creates completion record
  • ParentView — kebab menu "Reset" option for completed chores
  • ParentView — "Assign Kindness" button added
  • NotificationView — fetches from /pending-confirmations, renders both types
  • SSE listeners for child_chore_confirmation in all relevant components
  • Banner styles: yellow PENDING, green COMPLETED (using CSS variables)
  • All is_good references removed from frontend code
  • All frontend tests pass
  • Router updated with new routes