project.kind = source of truth (retire date-derived label) — List/Plan foundation
Seen in 1 project by 1 person
About
Make projects.kind the single source of truth for what a project IS, and retire the date-derived projectKindLabel heuristic. Foundation for the user-facing List/Plan terminology rename (copy threading is a SEPARATE follow-up — do NOT do copy here).
Why
projects.kind already exists (text, CHECK list|decision|plan, DEFAULT 'list', migration 20260502100001) and the public section routes off it. But the protected app ignores it and derives plan-vs-list from dates via projectKindLabel(project) (11 call sites). NewProjectModal never sets kind, so new projects sit at the 'list' default while the in-app UI calls them plans once they have dates → the stored kind and the in-app label can DISAGREE (a dated project is a "plan" in-app but kind='list' in the DB and would route publicly as a list). This unifies on the stored column.
Decisions (locked unless a reviewer overrides)
- D1 create UX:
NewProjectModalgets a List / Plan segmented toggle, default List. Do NOT add 'decision' to the create flow; existing decision projects keep working via their current path. - D2 kind ⊥ dates:
kindis explicit;start_date/end_datemean schedulability (DayView/itinerary) ONLY. Adding/removing dates must NOT changekind. - D3 backfill: new migration (unique HHMMSS, check history) —
UPDATE public.projects SET kind='plan' WHERE kind='list' AND (start_date IS NOT NULL OR end_date IS NOT NULL);Idempotent. Leaves 'decision' and any explicitly-set kind untouched. Preserves current in-app classification. - D4 retire projectKindLabel: replace all 11
projectKindLabel(...)usages with reads ofproject.kind. Tab mapping: Plans tab → kind='plan', Shortlists tab → kind='list'. Sites incl: components/projects/ProjectListView.tsx, app/app/(protected)/projects/[projectId]/kindIndex.ts, .../settings/page.tsx (thewasPlanlogic at ~L304 — base it on stored kind, not dates), lib/mcp/tools/projects.ts (2 sites). RemoveprojectKindLabelfrom @pickism/shared once unused (keepisSchedulable/isLocatable). - Upsert:
create_project(apps/web/app/api/projects/route.ts + lib/mcp/tools/projects.ts) accepts optionalkind(validated ∈ {list,plan,decision}, default 'list') and persists it;update_projectallows changingkind. Settings page gets a kind control (replace the date-inferred wasPlan). - CHECK stays list|decision|plan. The "both list and plan" case is intentionally NOT modeled yet.
Verify
- shared + web tsc clean; shared tests; full web vitest (wrap any newly kind-dependent tests appropriately); add tests for: create persists kind, update changes kind, backfill rule, the retired-label sites read kind. Apply the backfill migration to the dev DB (
supabase migration up, never reset) and confirm. - This touches
supabase/migrations/**→ review-gated, do NOT auto-merge. Open the PR, hand-off comment, state=review, keep worktree. Prod migration-first is the reviewer's step.
Out of scope (separate follow-up)
The EN/FR copy threading — rendering kindLabel(project.kind) (with FR gender/articles) in the ~85 'project' strings. That task depends on this one.
Links
No links shared yet.
Listed in
Bookmarked in
Not in any public bookmark categories yet.