docs(09): create phase plan
This commit is contained in:
parent
29691e82a3
commit
0e6347ef26
6 changed files with 626 additions and 9 deletions
|
|
@ -2,15 +2,15 @@
|
|||
gsd_state_version: 1.0
|
||||
milestone: v2.0
|
||||
milestone_name: Collaboration, planning, and social sign-in
|
||||
status: planning
|
||||
last_updated: "2026-05-15T20:25:48.078Z"
|
||||
last_activity: 2026-05-15 -- Phase 09 UI-SPEC approved
|
||||
status: executing
|
||||
last_updated: "2026-05-15T20:29:51.890Z"
|
||||
last_activity: 2026-05-15 -- Phase 09 planning complete
|
||||
progress:
|
||||
total_phases: 5
|
||||
completed_phases: 1
|
||||
total_plans: 5
|
||||
total_plans: 9
|
||||
completed_plans: 5
|
||||
percent: 100
|
||||
percent: 56
|
||||
---
|
||||
|
||||
# STATE
|
||||
|
|
@ -30,16 +30,16 @@ See: `.planning/PROJECT.md` (updated 2026-05-15)
|
|||
|
||||
Phase: 9 — Etapes
|
||||
Plan: —
|
||||
Status: Ready for planning
|
||||
Last activity: 2026-05-15 -- Phase 09 UI-SPEC approved
|
||||
Resume file: .planning/phases/09-etapes/09-UI-SPEC.md
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-05-15 -- Phase 09 planning complete
|
||||
Resume file: .planning/phases/09-etapes/09-01-PLAN.md
|
||||
|
||||
## Phase Status
|
||||
|
||||
| # | Phase | Status |
|
||||
|---|-------|--------|
|
||||
| 8 | Social Sign-in | ✓ Complete |
|
||||
| 9 | Etapes | ◐ UI-SPEC approved |
|
||||
| 9 | Etapes | ◆ Ready to execute |
|
||||
| 10 | Events | ○ Pending |
|
||||
| 11 | Individual Planning | ○ Pending |
|
||||
| 12 | Native Tablo Chat | ○ Pending |
|
||||
|
|
|
|||
153
.planning/phases/09-etapes/09-01-PLAN.md
Normal file
153
.planning/phases/09-etapes/09-01-PLAN.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
---
|
||||
phase: 09-etapes
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- backend/migrations/0007_etapes.sql
|
||||
- backend/internal/db/queries/etapes.sql
|
||||
- backend/internal/db/queries/tasks.sql
|
||||
- backend/internal/web/handlers_etapes_test.go
|
||||
- backend/internal/web/handlers_tasks_test.go
|
||||
- backend/internal/web/handlers_etapes.go
|
||||
- backend/internal/web/handlers_tasks.go
|
||||
- backend/internal/web/router.go
|
||||
- backend/templates/etapes.templ
|
||||
- backend/templates/tasks.templ
|
||||
- backend/templates/tablos.templ
|
||||
autonomous: true
|
||||
requirements: [ETAPE-01, ETAPE-02, ETAPE-03, ETAPE-04, ETAPE-05, ETAPE-06]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "D-01/D-02/D-03: Tasks tab renders a compact top etape strip with All, Unassigned, and created etape chips with counts"
|
||||
- "D-05/D-06/D-07: Selecting an etape chip filters the existing four-column kanban without changing TaskColumns"
|
||||
- "D-08/D-09/D-10: task position remains status-scoped; no task position is scoped by etape"
|
||||
- "D-11/D-12: task create form includes an Etape selector with No etape and created etapes"
|
||||
- "D-14/D-15/D-16: migration models etapes as a tablo-owned table with no parent column and tasks.etape_id nullable ON DELETE SET NULL"
|
||||
- "POST /tablos/{id}/etapes creates an etape and the response makes it visible in the strip"
|
||||
- "POST /tablos/{id}/tasks can create a task assigned to the active etape"
|
||||
artifacts:
|
||||
- path: "backend/migrations/0007_etapes.sql"
|
||||
provides: "etapes table plus nullable tasks.etape_id foreign key"
|
||||
- path: "backend/internal/db/queries/etapes.sql"
|
||||
provides: "sqlc etape CRUD queries"
|
||||
- path: "backend/templates/etapes.templ"
|
||||
provides: "EtapeStrip and create form fragments"
|
||||
- path: "backend/internal/web/handlers_etapes.go"
|
||||
provides: "first etape create/list/filter handlers"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Vertical slice 1: a user can create an etape inside a tablo, see it as a compact chip in the Tasks tab, filter the existing kanban by that etape, and create a task assigned to it from the existing add-task flow. This plan also establishes the schema and generated query surface needed for every later etape slice.
|
||||
</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-VALIDATION.md
|
||||
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/09-etapes/09-PATTERNS.md
|
||||
</context>
|
||||
|
||||
<threat_model>
|
||||
T-09-01 Cross-tablo etape assignment: task create/update must reject an `etape_id` that does not belong to the same tablo as the task.
|
||||
T-09-02 Cross-user etape access: non-owners must receive 404 from etape routes through `loadOwnedTablo`.
|
||||
T-09-03 CSRF: every etape/task mutation form must include `@ui.CSRFField(csrfToken)` and use existing CSRF middleware.
|
||||
T-09-04 XSS: etape titles/descriptions must be rendered through normal templ escaped output, never raw HTML.
|
||||
T-09-05 Data loss: deleting an etape must use `ON DELETE SET NULL` behavior for tasks, not task deletion.
|
||||
</threat_model>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add RED coverage for first etape slice</name>
|
||||
<files>
|
||||
- backend/internal/web/handlers_etapes_test.go
|
||||
- backend/internal/web/handlers_tasks_test.go
|
||||
</files>
|
||||
<read_first>
|
||||
- backend/internal/web/handlers_tasks_test.go
|
||||
- backend/internal/web/handlers_tablos_test.go
|
||||
- backend/migrations/0004_tasks.sql
|
||||
- .planning/phases/09-etapes/09-VALIDATION.md
|
||||
</read_first>
|
||||
<action>
|
||||
Add failing DB-backed handler tests for the first slice:
|
||||
1. `TestEtapeCreateRendersChipAndCount` creates an owned tablo, POSTs `/tablos/{id}/etapes` with title `Design`, and expects response/body or subsequent Tasks tab render to contain `Design`, `Unassigned`, and `All`.
|
||||
2. `TestTaskCreateAssignsEtape` creates an etape, POSTs `/tablos/{id}/tasks` with `etape_id`, and verifies `GetTaskByID` returns that etape id.
|
||||
3. `TestEtapeFilterRendersExistingKanbanColumns` creates assigned and unassigned tasks, GETs `/tablos/{id}/tasks?etape={etape_id}`, expects only assigned task title plus all four column labels.
|
||||
Keep tests in the existing integration style using `setupTestDB`, `preInsertUser`, `getCSRFToken`, and authenticated cookies.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd backend && go test ./internal/web -run 'TestEtapeCreateRendersChipAndCount|TestTaskCreateAssignsEtape|TestEtapeFilterRendersExistingKanbanColumns' -count=1</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `backend/internal/web/handlers_etapes_test.go` contains `TestEtapeCreateRendersChipAndCount`.
|
||||
- `backend/internal/web/handlers_tasks_test.go` or `handlers_etapes_test.go` contains `TestTaskCreateAssignsEtape`.
|
||||
- `backend/internal/web/handlers_etapes_test.go` contains `TestEtapeFilterRendersExistingKanbanColumns`.
|
||||
- The targeted tests fail before implementation because etape schema/routes/query fields are missing.
|
||||
</acceptance_criteria>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Implement the thinnest create-filter-assigned-task slice</name>
|
||||
<files>
|
||||
- backend/migrations/0007_etapes.sql
|
||||
- backend/internal/db/queries/etapes.sql
|
||||
- backend/internal/db/queries/tasks.sql
|
||||
- backend/internal/web/handlers_etapes.go
|
||||
- backend/internal/web/handlers_tasks.go
|
||||
- backend/internal/web/router.go
|
||||
- backend/templates/etapes.templ
|
||||
- backend/templates/tasks.templ
|
||||
- backend/templates/tablos.templ
|
||||
</files>
|
||||
<read_first>
|
||||
- backend/migrations/0004_tasks.sql
|
||||
- backend/migrations/0005_files.sql
|
||||
- backend/internal/db/queries/tasks.sql
|
||||
- backend/internal/web/handlers_tasks.go
|
||||
- backend/internal/web/handlers_files.go
|
||||
- backend/internal/web/router.go
|
||||
- backend/templates/tasks.templ
|
||||
- backend/templates/tablos.templ
|
||||
- .planning/phases/09-etapes/09-UI-SPEC.md
|
||||
</read_first>
|
||||
<action>
|
||||
Add `backend/migrations/0007_etapes.sql` with `etapes` table (`id`, `tablo_id`, `title`, `description`, `position`, timestamps), `etapes_tablo_id_position_idx`, nullable `tasks.etape_id` referencing `etapes(id) ON DELETE SET NULL`, and `tasks_tablo_id_etape_id_idx`. Do not add `parent_id`.
|
||||
Add etape sqlc queries for list/create/get/max position. Extend task queries to select and preserve `etape_id`; `InsertTask` accepts nullable etape id; `UpdateTask` accepts nullable etape id.
|
||||
Add minimal etape create/list/filter handlers and route mounting under the protected `/tablos/{id}` group. Create `EtapeStrip` plus minimal create form; wrap Tasks tab in `#tasks-tab`.
|
||||
Extend task create handler/form so `etape_id` can be submitted and validated against the same tablo before insert. When active filter is a concrete etape, the add-task form submits that etape id.
|
||||
Run `cd backend && just generate` after migration/query/template changes.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd backend && just generate && go test ./internal/web -run 'TestEtapeCreateRendersChipAndCount|TestTaskCreateAssignsEtape|TestEtapeFilterRendersExistingKanbanColumns|TestTaskCreate' -count=1</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `backend/migrations/0007_etapes.sql` contains `CREATE TABLE etapes`.
|
||||
- `backend/migrations/0007_etapes.sql` contains `ON DELETE SET NULL`.
|
||||
- `backend/migrations/0007_etapes.sql` does not contain `parent_id`.
|
||||
- `backend/internal/db/queries/etapes.sql` contains `ListEtapesByTablo`, `InsertEtape`, and `GetEtapeByID`.
|
||||
- `backend/internal/db/queries/tasks.sql` selects `etape_id` in `ListTasksByTablo` and `GetTaskByID`.
|
||||
- `backend/templates/tablos.templ` renders a `#tasks-tab` wrapper before `@KanbanBoard`.
|
||||
- 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-01 is partially delivered through create + render chip.
|
||||
- ETAPE-03 is partially delivered through create-task assignment.
|
||||
- ETAPE-05 is partially delivered through etape filtering over existing columns.
|
||||
- ETAPE-06 is delivered at schema level by no nested etape relationship.
|
||||
</success_criteria>
|
||||
130
.planning/phases/09-etapes/09-02-PLAN.md
Normal file
130
.planning/phases/09-etapes/09-02-PLAN.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
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>
|
||||
131
.planning/phases/09-etapes/09-03-PLAN.md
Normal file
131
.planning/phases/09-etapes/09-03-PLAN.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
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>
|
||||
147
.planning/phases/09-etapes/09-04-PLAN.md
Normal file
147
.planning/phases/09-etapes/09-04-PLAN.md
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
phase: 09-etapes
|
||||
plan: 04
|
||||
type: execute
|
||||
wave: 4
|
||||
depends_on:
|
||||
- 09-03-PLAN.md
|
||||
files_modified:
|
||||
- backend/internal/web/handlers_tasks.go
|
||||
- backend/templates/tasks.templ
|
||||
- backend/templates/etapes.templ
|
||||
- backend/internal/web/handlers_tasks_test.go
|
||||
- backend/internal/web/handlers_etapes_test.go
|
||||
autonomous: false
|
||||
requirements: [ETAPE-01, ETAPE-02, ETAPE-03, ETAPE-04, ETAPE-05, ETAPE-06]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "D-08/D-09/D-10: drag/drop reorder preserves etape_id and remains status-scoped"
|
||||
- "D-01 through D-16: all context decisions are represented in source behavior or verified manual behavior"
|
||||
- "UI-SPEC: top strip, chips, active state, selector copy, and delete copy are visually present"
|
||||
- "Research Pitfall 2: every UpdateTask call preserves etape_id unless assignment is intentionally changing"
|
||||
artifacts:
|
||||
- path: "backend/internal/web/handlers_tasks.go"
|
||||
provides: "reorder paths that preserve etape assignment"
|
||||
- path: "backend/internal/web/handlers_tasks_test.go"
|
||||
provides: "filtered reorder and etape preservation regression tests"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Vertical slice 4: harden the completed etape feature by proving drag/drop reorder still works with etape filters and assignments, then run final automated and browser verification. This closes the phase against all ETAPE requirements and the UI contract.
|
||||
</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-VALIDATION.md
|
||||
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/09-etapes/09-03-PLAN.md
|
||||
</context>
|
||||
|
||||
<threat_model>
|
||||
T-09-01 Cross-tablo assignment remains covered by prior tests and must not regress.
|
||||
T-09-05 Data loss remains covered by delete-unassign test and browser verification.
|
||||
T-09-06 Integrity regression: task reorder must never clear or change `etape_id` unless assignment form submitted that change.
|
||||
</threat_model>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add reorder preservation regression tests</name>
|
||||
<files>
|
||||
- backend/internal/web/handlers_tasks_test.go
|
||||
</files>
|
||||
<read_first>
|
||||
- backend/internal/web/handlers_tasks_test.go
|
||||
- backend/internal/web/handlers_tasks.go
|
||||
- .planning/phases/09-etapes/09-RESEARCH.md
|
||||
</read_first>
|
||||
<action>
|
||||
Add failing regression tests:
|
||||
1. `TestTaskReorderPreservesEtapeAssignment` creates assigned task, posts single-task reorder to another status, and verifies `etape_id` remains unchanged.
|
||||
2. `TestFilteredTaskReorderPreservesHiddenTasks` creates tasks in the same status assigned to different etapes, submits reorder for only one visible filtered task, and verifies the hidden task still exists with its original `etape_id`.
|
||||
3. `TestTaskOrderPersistsWithEtapeFilter` verifies filtered board ordering remains stable after reload.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd backend && go test ./internal/web -run 'TestTaskReorderPreservesEtapeAssignment|TestFilteredTaskReorderPreservesHiddenTasks|TestTaskOrderPersistsWithEtapeFilter' -count=1</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `handlers_tasks_test.go` contains all three named regression tests.
|
||||
- Tests fail before implementation if reorder does not preserve `etape_id`.
|
||||
</acceptance_criteria>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Preserve etape assignment through reorder and refresh counts</name>
|
||||
<files>
|
||||
- backend/internal/web/handlers_tasks.go
|
||||
- backend/templates/tasks.templ
|
||||
- backend/templates/etapes.templ
|
||||
</files>
|
||||
<read_first>
|
||||
- backend/internal/web/handlers_tasks.go
|
||||
- backend/templates/tasks.templ
|
||||
- backend/templates/etapes.templ
|
||||
- .planning/phases/09-etapes/09-RESEARCH.md
|
||||
</read_first>
|
||||
<action>
|
||||
Update every `UpdateTaskParams` call in `TaskReorderHandler` to pass the existing task's `etape_id`. Confirm the single-task path and array Sortable.js path both preserve assignment.
|
||||
Ensure reorder responses under an active etape filter return the filtered board or whole Tasks tab consistently enough that chip counts and active state remain correct after HTMX settles.
|
||||
Keep task position assignment status-scoped; do not add `(etape_id, status, position)` ordering semantics.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd backend && just generate && go test ./internal/web -run 'TestTaskReorderPreservesEtapeAssignment|TestFilteredTaskReorderPreservesHiddenTasks|TestTaskOrderPersistsWithEtapeFilter|TestTaskReorderCrossColumn|TestTaskReorderSameColumn|TestTaskOrderPersists' -count=1</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- Every `UpdateTaskParams` call in `handlers_tasks.go` includes the current `EtapeID` or intentional parsed assignment value.
|
||||
- `TaskReorderHandler` does not parse or write assignment from drag/drop payload.
|
||||
- Reorder regression tests and existing reorder tests pass.
|
||||
</acceptance_criteria>
|
||||
</task>
|
||||
|
||||
<task type="manual">
|
||||
<name>Task 3: Final automated and browser verification checkpoint</name>
|
||||
<files></files>
|
||||
<read_first>
|
||||
- .planning/phases/09-etapes/09-UI-SPEC.md
|
||||
- .planning/phases/09-etapes/09-VALIDATION.md
|
||||
- backend/internal/web/handlers_tasks_test.go
|
||||
- backend/internal/web/handlers_etapes_test.go
|
||||
</read_first>
|
||||
<action>
|
||||
Run full generated-code and backend verification, then perform browser verification:
|
||||
1. `cd backend && just generate`
|
||||
2. `cd backend && go test ./... -count=1`
|
||||
3. `git diff --check`
|
||||
4. Start `cd backend && just dev`.
|
||||
5. In a browser, open a tablo Tasks tab, create two etapes, create assigned and unassigned tasks, filter by an etape, filter by `Unassigned`, edit assignment from the task form, drag a filtered task across columns, delete an etape, and confirm affected tasks move to Unassigned rather than disappearing.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd backend && just generate && go test ./... -count=1</automated>
|
||||
<manual>Browser confirms all ETAPE-01..06 success criteria and UI-SPEC interaction contract.</manual>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `cd backend && go test ./... -count=1` exits 0.
|
||||
- `git diff --check` exits 0.
|
||||
- Browser verification confirms top-strip chips, counts, filters, assignment selector, reorder, and delete-unassign behavior.
|
||||
- User approves the checkpoint before Phase 9 is marked complete.
|
||||
</acceptance_criteria>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Run:
|
||||
- `cd backend && just generate`
|
||||
- `cd backend && go test ./... -count=1`
|
||||
- `git diff --check`
|
||||
- browser verification from Task 3
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- All ETAPE-01..06 requirements are complete.
|
||||
- Existing kanban status and ordering behavior remains intact.
|
||||
- UI-SPEC is satisfied in the first functional UI.
|
||||
- Human verification checkpoint is approved.
|
||||
</success_criteria>
|
||||
56
.planning/phases/09-etapes/09-PATTERNS.md
Normal file
56
.planning/phases/09-etapes/09-PATTERNS.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Phase 09 - Pattern Map
|
||||
|
||||
## Purpose
|
||||
|
||||
Concrete code patterns Phase 9 should reuse when adding etapes to the Go/HTMX task board.
|
||||
|
||||
## Closest Analogs
|
||||
|
||||
### Schema And SQLC
|
||||
|
||||
- `backend/migrations/0004_tasks.sql` - task enum/table/index pattern; down migration order matters.
|
||||
- `backend/migrations/0005_files.sql` - tablo-owned child table pattern for resources under a tablo.
|
||||
- `backend/internal/db/queries/tasks.sql` - current task CRUD query shape and `GetTaskByID` ownership binding.
|
||||
- `backend/internal/db/queries/files.sql` - tablo-scoped file query analog if present during execution.
|
||||
|
||||
### Handlers
|
||||
|
||||
- `backend/internal/web/handlers_tasks.go` - task CRUD, inline HTMX fragments, status parsing, reorder behavior.
|
||||
- `backend/internal/web/handlers_files.go` - additional tablo-scoped resource handlers and ownership helpers.
|
||||
- `backend/internal/web/handlers_tablos.go` - canonical `loadOwnedTablo` helper and 404-on-authz-failure pattern.
|
||||
|
||||
### Templates
|
||||
|
||||
- `backend/templates/tasks.templ` - kanban board, task card, create/edit/delete fragments, OOB add-slot reset.
|
||||
- `backend/templates/tablos.templ` - tab content boundary and `TasksTabFragment` integration point.
|
||||
- `backend/internal/web/ui/button.css` and `backend/internal/web/ui` helpers - existing button/card/badge styling.
|
||||
|
||||
### Tests
|
||||
|
||||
- `backend/internal/web/handlers_tasks_test.go` - DB-backed handler tests for task create/update/reorder/delete/ownership.
|
||||
- `backend/internal/web/handlers_files_test.go` - useful analog for tablo-scoped resource ownership tests if present during execution.
|
||||
|
||||
## Reuse Rules
|
||||
|
||||
- Keep `TaskColumns` and the current kanban column DOM stable.
|
||||
- New etape routes should live under `/tablos/{id}/...` and call `loadOwnedTablo` before any etape lookup.
|
||||
- Etape-specific lookup must bind both `id` and `tablo_id`, mirroring `GetTaskByID`.
|
||||
- HTMX filter operations should swap `#tasks-tab` or `#tab-content`, not just `#kanban-board`, so chip active state and counts stay accurate.
|
||||
- Task reorder must preserve `etape_id` once the task row grows that field.
|
||||
- Use `just generate` after migration, sqlc query, or templ changes.
|
||||
|
||||
## Planned File Map
|
||||
|
||||
| Target file | Role | Closest analog |
|
||||
|-------------|------|----------------|
|
||||
| `backend/migrations/0007_etapes.sql` | Add etape table and nullable task assignment | `backend/migrations/0004_tasks.sql`, `backend/migrations/0005_files.sql` |
|
||||
| `backend/internal/db/queries/etapes.sql` | Etape CRUD and position queries | `backend/internal/db/queries/tasks.sql` |
|
||||
| `backend/internal/db/queries/tasks.sql` | Add nullable etape assignment and filtered task lists | existing same file |
|
||||
| `backend/internal/web/handlers_etapes.go` | Etape CRUD/reorder handlers | `backend/internal/web/handlers_tasks.go`, `backend/internal/web/handlers_files.go` |
|
||||
| `backend/internal/web/handlers_tasks.go` | Task assignment and filter-aware task loading | existing same file |
|
||||
| `backend/internal/web/router.go` | Mount etape routes and preserve task routes | existing task/file route groups |
|
||||
| `backend/templates/etapes.templ` | Etape strip, chips, forms, delete confirmation | `backend/templates/tasks.templ` |
|
||||
| `backend/templates/tasks.templ` | Add etape selector and preserve kanban | existing same file |
|
||||
| `backend/templates/tablos.templ` | Expand `TasksTabFragment` signature and wrapper | existing same file |
|
||||
| `backend/internal/web/handlers_etapes_test.go` | Etape behavior and ownership coverage | `backend/internal/web/handlers_tasks_test.go` |
|
||||
| `backend/internal/web/handlers_tasks_test.go` | Assignment/filter/reorder regression coverage | existing same file |
|
||||
Loading…
Reference in a new issue