---
phase: 09-etapes
plan: 02
type: execute
wave: 2
depends_on:
  - 09-01-PLAN.md
files_modified:
  - backend/internal/db/queries/etapes.sql
  - backend/internal/web/handlers_etapes.go
  - backend/internal/web/router.go
  - backend/templates/etapes.templ
  - backend/templates/tablos.templ
  - backend/internal/web/handlers_etapes_test.go
autonomous: true
requirements: [ETAPE-01, ETAPE-02, ETAPE-04, ETAPE-05]

must_haves:
  truths:
    - "D-01/D-02/D-03/D-04: etape management remains a compact top strip, not a card panel or separate page"
    - "D-08: etape order is independent through etapes.position"
    - "D-14: deleting an etape keeps tasks and moves them to Unassigned"
    - "D-05/D-06: chip active state and counts update after create/edit/delete/reorder"
    - "UI-SPEC: delete confirmation copy says tasks stay in the tablo and move to Unassigned"
  artifacts:
    - path: "backend/internal/web/handlers_etapes.go"
      provides: "Etape edit, delete, and reorder handlers"
    - path: "backend/templates/etapes.templ"
      provides: "Etape edit/delete/reorder fragments"
    - path: "backend/internal/web/handlers_etapes_test.go"
      provides: "CRUD, delete-unassign, reorder, and ownership tests"
---

<objective>
Vertical slice 2: a user can manage etapes from the top strip: edit title/description, delete an etape without deleting tasks, and reorder etape chips. After this plan, etapes are a usable organizing surface independent of task assignment form refinements.
</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-01-PLAN.md
</context>

<threat_model>
T-09-02 Cross-user etape access: all edit/delete/reorder routes must call `loadOwnedTablo` and bind etape id to tablo id.
T-09-03 CSRF: edit/delete/reorder forms must include `@ui.CSRFField(csrfToken)`.
T-09-04 XSS: title and description output must be templ escaped.
T-09-05 Data loss: delete route must delete only from `etapes`; tasks survive through nullable foreign key behavior.
</threat_model>

<tasks>

<task type="auto">
  <name>Task 1: Add RED coverage for etape management</name>
  <files>
    - backend/internal/web/handlers_etapes_test.go
  </files>
  <read_first>
    - backend/internal/web/handlers_etapes_test.go
    - backend/internal/web/handlers_tasks_test.go
    - backend/internal/web/handlers_files.go
    - .planning/phases/09-etapes/09-UI-SPEC.md
  </read_first>
  <action>
    Add failing tests:
    1. `TestEtapeUpdateChangesTitleAndDescription` posts `/tablos/{id}/etapes/{etape_id}` and verifies rendered/body and DB state.
    2. `TestEtapeDeleteUnassignsTasks` creates an etape and assigned task, posts delete, verifies task row still exists with null etape id.
    3. `TestEtapeReorderPersistsPosition` creates two etapes, posts `/tablos/{id}/etapes/reorder`, and verifies `ListEtapesByTablo` ordering.
    4. `TestEtapeOwnershipReturns404` verifies a non-owner receives 404 for edit/delete/reorder attempts.
  </action>
  <verify>
    <automated>cd backend && go test ./internal/web -run 'TestEtapeUpdateChangesTitleAndDescription|TestEtapeDeleteUnassignsTasks|TestEtapeReorderPersistsPosition|TestEtapeOwnershipReturns404' -count=1</automated>
  </verify>
  <acceptance_criteria>
    - `handlers_etapes_test.go` contains all four named tests.
    - Tests fail before implementation because update/delete/reorder routes or behavior are missing.
  </acceptance_criteria>
</task>

<task type="auto">
  <name>Task 2: Implement edit, delete-unassign, and reorder</name>
  <files>
    - backend/internal/db/queries/etapes.sql
    - backend/internal/web/handlers_etapes.go
    - backend/internal/web/router.go
    - backend/templates/etapes.templ
    - backend/templates/tablos.templ
  </files>
  <read_first>
    - backend/internal/db/queries/etapes.sql
    - backend/internal/web/handlers_etapes.go
    - backend/internal/web/handlers_tasks.go
    - backend/templates/etapes.templ
    - backend/templates/tasks.templ
    - backend/templates/tablos.templ
  </read_first>
  <action>
    Add `UpdateEtape`, `DeleteEtape`, and `UpdateEtapePosition` queries. Implement GET edit, POST update, GET delete-confirm, POST delete, and POST reorder handlers.
    Delete confirmation must use heading `Delete etape?`, body `Tasks in this etape will stay in the tablo and move to Unassigned.`, submit label `Delete etape`, and cancel label `Keep etape`.
    Reordering may use explicit up/down controls or a compact Sortable.js strip. If Sortable.js is reused, use a separate group from `kanban` and do not target `.sortable-column`.
    After delete/update/reorder, return a Tasks-tab or etape-strip fragment that refreshes chip titles, counts, active state, and kanban contents.
  </action>
  <verify>
    <automated>cd backend && just generate && go test ./internal/web -run 'TestEtapeUpdateChangesTitleAndDescription|TestEtapeDeleteUnassignsTasks|TestEtapeReorderPersistsPosition|TestEtapeOwnershipReturns404' -count=1</automated>
  </verify>
  <acceptance_criteria>
    - `backend/internal/db/queries/etapes.sql` contains `UpdateEtape`, `DeleteEtape`, and `UpdateEtapePosition`.
    - `backend/templates/etapes.templ` contains `Delete etape?` and `Tasks in this etape will stay in the tablo and move to Unassigned.`
    - Non-owner etape management tests expect and receive 404.
    - Delete-unassign test proves task row remains after etape deletion.
    - 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-02 is delivered for edit/delete/reorder.
- ETAPE-04 is delivered through delete-unassign behavior.
- Etape chip order and counts remain usable from the Tasks tab.
</success_criteria>
