---
phase: 09-etapes
plan: 03
type: execute
wave: 3
depends_on:
  - 09-02-PLAN.md
files_modified:
  - backend/internal/db/queries/tasks.sql
  - backend/internal/web/handlers_tasks.go
  - backend/internal/web/handlers_files.go
  - backend/templates/tasks.templ
  - backend/templates/tablos.templ
  - backend/internal/web/handlers_tasks_test.go
autonomous: true
requirements: [ETAPE-03, ETAPE-05]

must_haves:
  truths:
    - "D-11/D-12: task create and edit forms include an Etape selector with No etape"
    - "D-13: quick dropdowns on task cards are not implemented in this phase"
    - "D-05/D-06: filters support concrete etape ids and Unassigned"
    - "D-09/D-10: task update preserves current status and position while changing assignment"
    - "UI-SPEC: selector label is Etape and empty option is No etape"
  artifacts:
    - path: "backend/templates/tasks.templ"
      provides: "Etape selector in task create/edit fragments"
    - path: "backend/internal/web/handlers_tasks.go"
      provides: "task assignment parsing and same-tablo validation"
    - path: "backend/internal/web/handlers_files.go"
      provides: "Tasks tab handler updated to load etapes and filtered tasks"
---

<objective>
Vertical slice 3: a user can assign and unassign tasks from etapes in the task create/edit forms, and the Tasks tab filter works for All, Unassigned, and each etape while preserving the existing kanban layout.
</objective>

<context>
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/09-etapes/09-CONTEXT.md
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/09-etapes/09-RESEARCH.md
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/09-etapes/09-UI-SPEC.md
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/09-etapes/09-PATTERNS.md
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/09-etapes/09-02-PLAN.md
</context>

<threat_model>
T-09-01 Cross-tablo etape assignment: create/update handlers must validate submitted etape id with `GetEtapeByID(id, tablo_id)` before writing it to a task.
T-09-02 Cross-user access: filtering by an etape id from another user's tablo must return 404 or ignore it without leaking the etape title.
T-09-03 CSRF: task create/edit forms continue to include CSRF tokens after adding the selector.
T-09-04 XSS: etape option labels must be templ escaped.
</threat_model>

<tasks>

<task type="auto">
  <name>Task 1: Add RED coverage for assignment and filters</name>
  <files>
    - backend/internal/web/handlers_tasks_test.go
  </files>
  <read_first>
    - backend/internal/web/handlers_tasks_test.go
    - backend/internal/web/handlers_etapes_test.go
    - backend/templates/tasks.templ
    - .planning/phases/09-etapes/09-UI-SPEC.md
  </read_first>
  <action>
    Add failing tests:
    1. `TestTaskEditRendersEtapeSelector` verifies GET edit renders label `Etape`, option `No etape`, and existing etape title.
    2. `TestTaskUpdateAssignsAndUnassignsEtape` posts task update with an etape id, verifies DB assignment, then posts with empty etape value and verifies null assignment.
    3. `TestTaskAssignmentRejectsForeignEtape` creates two tablos and verifies assigning a task to another tablo's etape fails without updating the task.
    4. `TestTasksTabUnassignedFilter` verifies `/tablos/{id}/tasks?etape=unassigned` renders unassigned tasks and hides assigned task titles while keeping all four status headers.
  </action>
  <verify>
    <automated>cd backend && go test ./internal/web -run 'TestTaskEditRendersEtapeSelector|TestTaskUpdateAssignsAndUnassignsEtape|TestTaskAssignmentRejectsForeignEtape|TestTasksTabUnassignedFilter' -count=1</automated>
  </verify>
  <acceptance_criteria>
    - `handlers_tasks_test.go` contains all four named tests.
    - Tests fail before implementation because selector/update/filter behavior is incomplete.
  </acceptance_criteria>
</task>

<task type="auto">
  <name>Task 2: Implement form assignment and filter-aware task loading</name>
  <files>
    - backend/internal/db/queries/tasks.sql
    - backend/internal/web/handlers_tasks.go
    - backend/internal/web/handlers_files.go
    - backend/templates/tasks.templ
    - backend/templates/tablos.templ
  </files>
  <read_first>
    - backend/internal/db/queries/tasks.sql
    - backend/internal/web/handlers_tasks.go
    - backend/internal/web/handlers_files.go
    - backend/templates/tasks.templ
    - backend/templates/tablos.templ
    - .planning/phases/09-etapes/09-RESEARCH.md
    - .planning/phases/09-etapes/09-UI-SPEC.md
  </read_first>
  <action>
    Extend task create/edit template signatures to receive etapes and selected etape id. Render a select labeled `Etape` with first option `No etape`.
    Extend `TaskCreateHandler`, `TaskEditHandler`, and `TaskUpdateHandler` to load etapes for the owned tablo, parse optional `etape_id`, validate same-tablo ownership, and preserve existing status/position.
    Update `TabloTasksTabHandler` and full detail rendering to load etapes, counts, and current filter. Support `?etape=unassigned` and `?etape={uuid}`. Use existing four kanban columns for filtered task slices.
    Do not add quick assignment dropdowns on task cards.
  </action>
  <verify>
    <automated>cd backend && just generate && go test ./internal/web -run 'TestTaskEditRendersEtapeSelector|TestTaskUpdateAssignsAndUnassignsEtape|TestTaskAssignmentRejectsForeignEtape|TestTasksTabUnassignedFilter|TestTaskUpdate|TestTasksKanbanRenders' -count=1</automated>
  </verify>
  <acceptance_criteria>
    - `backend/templates/tasks.templ` contains visible label text `Etape` and option text `No etape`.
    - `backend/internal/web/handlers_tasks.go` validates submitted etape ids against the current tablo before insert/update.
    - `TaskUpdateHandler` still preserves existing status and position.
    - `TabloTasksTabHandler` supports `etape=unassigned` and concrete etape UUID filters.
    - Targeted tests listed in verify pass.
  </acceptance_criteria>
</task>

</tasks>

<verification>
Run:
- `cd backend && just generate`
- `cd backend && go test ./internal/web -count=1`
- `git diff --check`
</verification>

<success_criteria>
- ETAPE-03 is fully delivered.
- ETAPE-05 filtering behavior is delivered for All, Unassigned, and specific etapes.
- Existing task create/update tests still pass.
</success_criteria>
