diff --git a/README.md b/README.md
index afffde8..8266856 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,13 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi
- Standardized sprint date parsing across Kanban, Backlog, Sprint Board, Archive, and task detail sprint pickers.
- `POST /api/sprints` and `PATCH /api/sprints` now normalize `startDate`/`endDate` to `YYYY-MM-DD` before writing to Supabase.
+### Feb 23, 2026 updates
+
+- Removed non-live fallback data paths from the task store; board data now comes from Supabase-backed APIs only.
+- Removed legacy schema expectations from API code and docs (`legacy_id`, `meta` dependency, non-existent task columns).
+- Task detail now includes explicit Project + Sprint selection; selecting a sprint auto-aligns `projectId`.
+- API validation now accepts UUID-shaped IDs used by existing data and returns clearer error payloads for failed writes.
+
### Sprint date semantics (important)
- Sprint dates are treated as local calendar-day boundaries:
@@ -53,8 +60,8 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi
- Tasks use labels (`tags: string[]`) and can have multiple labels.
- Tasks support attachments (`attachments: TaskAttachment[]`).
-- Tasks now track `createdById`, `createdByName`, `createdByAvatarUrl`, `updatedById`, `updatedByName`, and `updatedByAvatarUrl`.
-- Tasks now track assignment via `assigneeId`, `assigneeName`, `assigneeEmail`, and `assigneeAvatarUrl`.
+- Tasks persist identity references via `createdById`, `updatedById`, and `assigneeId`.
+- Display fields (`createdByName`, `updatedByName`, `assigneeName`, avatar/email) are resolved from `users` data in API/UI, not stored as separate task table columns.
- There is no active `backlog` status in workflow logic.
- A task is considered in Backlog when `sprintId` is empty.
- Current status values:
@@ -118,7 +125,7 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi
### Labels
-- Project selection UI for tasks was removed in favor of labels.
+- Labels are independent of project/sprint selection.
- You can add/remove labels inline in:
- New Task modal
- Task Detail page
@@ -134,6 +141,7 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi
- `/tasks/{taskId}`
- Task detail is no longer popup-only behavior.
- Each task now has a shareable deep link.
+- Task detail includes explicit Project and Sprint dropdowns.
### Attachments
@@ -200,8 +208,12 @@ During drag, the active target column shows expanded status drop zones for clari
## Persistence
- Client state is managed with Zustand.
-- Persistence is done via `/api/tasks`.
-- API reads/writes Supabase tables.
+- Persistence is handled through Supabase-backed API routes:
+ - `/api/tasks`
+ - `/api/projects`
+ - `/api/sprints`
+- No local task/project/sprint fallback dataset is used.
+- API reads/writes Supabase tables only.
## Run locally
diff --git a/SUPABASE_SETUP.md b/SUPABASE_SETUP.md
index 2d737a4..02ea0c3 100644
--- a/SUPABASE_SETUP.md
+++ b/SUPABASE_SETUP.md
@@ -149,7 +149,7 @@ Or push to git and let Vercel auto-deploy.
### After (Supabase):
- PostgreSQL database hosted by Supabase
-- JWT-based session tokens
+- Supabase Auth credentials + app-managed `gantt_session` cookie sessions
- Works on multiple servers/Vercel edge functions
- Real-time subscriptions possible (future enhancement)
@@ -164,6 +164,7 @@ Or push to git and let Vercel auto-deploy.
- Users can only see/modify their own data
- Admin operations use service role key
-3. **Password hashing remains the same**
- - We use scrypt hashing (same as SQLite version)
- - Passwords are never stored in plain text
+3. **Password handling**
+ - Passwords are managed by Supabase Auth.
+ - Do not add `public.users.password_hash`; keep password data out of public tables.
+ - App login state uses secure, HTTP-only session cookies.
diff --git a/scripts/README.md b/scripts/README.md
index 0059f54..40cc966 100644
--- a/scripts/README.md
+++ b/scripts/README.md
@@ -41,17 +41,17 @@ A unified CLI that covers all API operations.
# Create task
./scripts/gantt.sh task create
[status] [priority] [project-id]
-./scripts/gantt.sh task create "Fix bug" open high 1
+./scripts/gantt.sh task create "Fix bug" open high
# Create from natural language
./scripts/gantt.sh task natural "Fix login bug by Friday, assign to Matt, high priority"
# Update any field
./scripts/gantt.sh task update
-./scripts/gantt.sh task update abc-123 status done
-./scripts/gantt.sh task update abc-123 priority urgent
-./scripts/gantt.sh task update abc-123 title "New title"
-./scripts/gantt.sh task update abc-123 assigneeId
+./scripts/gantt.sh task update status done
+./scripts/gantt.sh task update priority urgent
+./scripts/gantt.sh task update title "New title"
+./scripts/gantt.sh task update assigneeId
# Delete task
./scripts/gantt.sh task delete
@@ -61,7 +61,7 @@ A unified CLI that covers all API operations.
# Attach file
./scripts/gantt.sh task attach
-./scripts/gantt.sh task attach abc-123 ./research.pdf
+./scripts/gantt.sh task attach ./research.pdf
```
### Project Commands
@@ -78,9 +78,9 @@ A unified CLI that covers all API operations.
# Update project field
./scripts/gantt.sh project update
-./scripts/gantt.sh project update abc-123 name "New Name"
-./scripts/gantt.sh project update abc-123 description "New desc"
-./scripts/gantt.sh project update abc-123 color "#ff0000"
+./scripts/gantt.sh project update name "New Name"
+./scripts/gantt.sh project update description "New desc"
+./scripts/gantt.sh project update color "#ff0000"
# Delete project
./scripts/gantt.sh project delete
@@ -100,9 +100,9 @@ A unified CLI that covers all API operations.
# Update sprint field
./scripts/gantt.sh sprint update
-./scripts/gantt.sh sprint update abc-123 name "New Sprint Name"
-./scripts/gantt.sh sprint update abc-123 status active
-./scripts/gantt.sh sprint update abc-123 startDate "2026-02-25"
+./scripts/gantt.sh sprint update name "New Sprint Name"
+./scripts/gantt.sh sprint update status active
+./scripts/gantt.sh sprint update startDate "2026-02-25"
# Delete sprint
./scripts/gantt.sh sprint delete
@@ -242,13 +242,13 @@ export API_URL=http://localhost:3001/api
```bash
# Add completion comment
-./scripts/gantt.sh task comment abc-123 "Completed. See attached notes."
+./scripts/gantt.sh task comment "Completed. See attached notes."
# Attach notes
-./scripts/gantt.sh task attach abc-123 ./completion-notes.md
+./scripts/gantt.sh task attach ./completion-notes.md
# Mark done
-./scripts/gantt.sh task update abc-123 status done
+./scripts/gantt.sh task update status done
```
### Find Tasks
@@ -294,6 +294,7 @@ Task IDs are UUIDs like `33ebc71e-7d40-456c-8f98-bb3578d2bb2b`. Find them:
- In the URL when viewing a task
- From `task list` output
- From `task create` output
+- `projectId`, `sprintId`, and `assigneeId` fields are UUID values as well.
## Tips
diff --git a/src/app/tasks/[taskId]/page.tsx b/src/app/tasks/[taskId]/page.tsx
index bf2a7c5..bd7f625 100644
--- a/src/app/tasks/[taskId]/page.tsx
+++ b/src/app/tasks/[taskId]/page.tsx
@@ -356,10 +356,9 @@ export default function TaskDetailPage() {
const sortedSprints = useMemo(
() =>
sprints
- .filter((sprint) => sprint.projectId === editedTask?.projectId)
.slice()
.sort((a, b) => parseSprintStart(a.startDate).getTime() - parseSprintStart(b.startDate).getTime()),
- [sprints, editedTask?.projectId]
+ [sprints]
)
const handleAttachmentUpload = async (event: ChangeEvent) => {
@@ -474,6 +473,24 @@ export default function TaskDetailPage() {
})
}
+ const setEditedTaskSprint = (sprintId: string) => {
+ if (!editedTask) return
+ if (!sprintId) {
+ setEditedTask({
+ ...editedTask,
+ sprintId: undefined,
+ })
+ return
+ }
+
+ const sprint = sprints.find((entry) => entry.id === sprintId)
+ setEditedTask({
+ ...editedTask,
+ sprintId,
+ projectId: sprint?.projectId || editedTask.projectId,
+ })
+ }
+
const handleSave = async () => {
if (!editedTask) return
if (!editedTask.projectId) {
@@ -819,7 +836,7 @@ export default function TaskDetailPage() {