Plan: CT task detail, drop redundant Notes property + make Due/Reminder cells time-aware¶
URL: https://mkdocs.justinsforge.com/memory/plans/ct-task-notes-body-and-datetime-cells-2026-07-03/
Date: 2026-07-03
Approved spec: A CreatorTrack task is already a page (block-editor body), so the separate Notes text column is redundant; remove it from the Tasks seed, migrate existing Notes text to the bottom of each task's page body, and drop the column + its values atomically. Separately, Due/Reminder already store an optional time-of-day (the Tasks composer's TimePicker writes an ISO datetime into the same date value) but the property/peek panel renders a date-only <input type="date">, so time can't be seen or edited after creation. Add an optional time flag to a prop's config, make the generic DateCell render a TimePicker when that flag is set, flag Tasks Due + Reminder, and backfill the flag onto existing Tasks databases.
Repo: /home/justinwieb/forge-suite (Next.js + Postgres via forge_workspace_migrate.py). Commits land in the forge-suite repo, not the forge repo.
Out of scope:
- Generic time support for date props in non-Tasks databases (Tasks Due/Reminder only this round; the config.time mechanism is generic but only enabled on those two).
- Timezone selection UI (Chicago offset stays baked into the existing compose helpers).
- Reworking the composer's TimePicker UX or the reminder-derivation logic.
- Any change to the block editor itself beyond appending one paragraph block during migration.
Task list¶
Task 1: Extract date+time compose/split into a shared lib¶
- Files: create
/home/justinwieb/forge-suite/lib/datetime.ts; edit/home/justinwieb/forge-suite/components/apps/tasks/taskDate.ts - What: Move the client date+time primitives (
dueHasTime,splitDateTime,composeDateTimewith the Chicago offset) intolib/datetime.tsas the single source of truth. Re-export them fromtaskDate.ts(keepingdueHasTimeClient/splitDueClient/composeDueClientnames as thin aliases) soComposer.tsxandlib/tasks.tskeep working unchanged. No behavior change. - Verification:
cd /home/justinwieb/forge-suite && npx tsc --noEmitpasses, andnode -e "const d=require('./lib/datetime.ts'==='')"is not runnable for TS, so instead:cd /home/justinwieb/forge-suite && npx tsx -e "import {composeDateTime,splitDateTime,dueHasTime} from './lib/datetime'; const v=composeDateTime('2026-07-04','09:30'); console.assert(dueHasTime(v), 'hasTime'); const s=splitDateTime(v); console.assert(s.date==='2026-07-04'&&s.time==='09:30', 'roundtrip'); console.log('ok', v)" - Commit:
refactor(workspace): extract shared date+time helpers to lib/datetime
Task 2: Add optional time flag to PropConfig¶
- Files:
/home/justinwieb/forge-suite/lib/collection.ts(PropConfig, lines ~90-103) - What: Add
time?: booleanto thePropConfigunion so a date prop can opt into time-of-day editing. Type-only change. - Verification:
cd /home/justinwieb/forge-suite && npx tsc --noEmitpasses. - Commit:
feat(workspace): add optional config.time flag to PropConfig
Task 3: Make DateCell time-aware when config.time¶
- Files:
/home/justinwieb/forge-suite/components/workspace/database/Cell.tsx(DateCell, lines ~160-169) - What: When
prop.config?.timeis true, render the existing date<input>alongsideTimePicker(fromcomponents/ui/TimePicker), usingsplitDateTime/composeDateTimefromlib/datetimeto read/write the single value viadb.actions.setCell(rowId, prop.id, ...). Clearing the time collapses the value back toYYYY-MM-DD. Whenconfig.timeis falsy, behavior is unchanged (date-only). - Verification:
cd /home/justinwieb/forge-suite && npx tsc --noEmitpasses; then/preview-site(or the running :3070 dev app), open a task peek, confirm Due shows a time control and setting/clearing a time persists on reload. - Commit:
feat(workspace): render TimePicker in DateCell when prop.config.time is set
Task 4: Flag Tasks Due + Reminder with config.time in the seed¶
- Files:
/home/justinwieb/forge-suite/lib/system-apps.ts(Due seed line ~225; Reminder seed lines ~252-257) - What: Add
config: { time: true }to theDueandReminderprop seeds so newly created Tasks databases get time-aware cells. (Existing databases are handled in Task 5, sinceensurePropis insert-if-absent and never updates config.) - Verification:
cd /home/justinwieb/forge-suite && npx tsc --noEmitpasses; grep confirms both seeds carrytime: true:grep -n "time: true" lib/system-apps.tsreturns 2 lines. - Commit:
feat(tasks): seed Due and Reminder with config.time on new Tasks databases
Task 5: Migration, backfill config.time onto existing Due/Reminder props¶
- Files: create
/home/justinwieb/forge-suite/db/migrations/20260703THHMMSS_workspace-lifeos-spine-phase01_tasks_due_reminder_time_flag.sql(use the real UTC timestamp at write time) - What: Idempotent
UPDATE app.collection_props SET config = coalesce(config,'{}'::jsonb) || '{"time":true}'::jsonb WHERE name IN ('Due','Reminder') AND type = 'date' AND (config->>'time') IS DISTINCT FROM 'true';Follow the header convention in20260702T165227_tasks_app_attachments_and_notifications.sql. - Verification: apply with
python3 /home/justinwieb/forge/scripts/forge_workspace_migrate.pythen confirm zero remaining unflagged rows: re-running the migrate script is a no-op, and aSELECT count(*) FROM app.collection_props WHERE name IN ('Due','Reminder') AND type='date' AND (config->>'time') IS DISTINCT FROM 'true';returns 0. - Commit:
migrate(tasks): backfill config.time on existing Due/Reminder props
Task 6: Remove Notes from the Tasks seed¶
- Files:
/home/justinwieb/forge-suite/lib/system-apps.ts(remove{ name: "Notes", type: "text" }, line ~228); audit/home/justinwieb/forge-suite/components/apps/tasks/for hardcoded"Notes"references (e.g.Peek.tsxheader comment) and update comments/order. - What: Stop seeding the
Notescolumn on new Tasks databases. Fix any stale references so nothing assumes a Notes prop exists. - Verification:
cd /home/justinwieb/forge-suite && npx tsc --noEmitpasses;grep -rn '"Notes"' components/apps/tasks lib/system-apps.tsreturns only intentional (or zero) hits. - Commit:
refactor(tasks): drop redundant Notes property from Tasks seed
Task 7: Migration, copy Notes text to page body then drop the Notes prop (atomic)¶
- Files: create
/home/justinwieb/forge-suite/db/migrations/20260703THHMMSS_workspace-lifeos-spine-phase01_tasks_notes_to_body.sql(real UTC timestamp; ordered after Task 5's file) - What: In one transaction: (1) for every
collection_valuesrow whose prop is a TasksNotes(type='text') column with non-empty text,INSERT INTO app.blocks (nodeId, type, content, position)aparagraphblock on that row's node with the Notes text andposition = coalesce(max(position),0)+1(append to bottom); (2)DELETE FROM app.collection_valuesfor those Notes propIds; (3)DELETE FROM app.collection_props WHERE name='Notes' AND type='text'scoped to Tasks databases. Confirm thecollection_values.valuejsonb shape for a text prop at implementation time (string vs{text}) and extract accordingly. Idempotent: guard the block insert so a re-run finds no Notes props to copy (props already deleted), and wrap inBEGIN/COMMITso a partial failure rolls back. - Verification: apply with
python3 /home/justinwieb/forge/scripts/forge_workspace_migrate.py; confirmSELECT count(*) FROM app.collection_props WHERE name='Notes' AND type='text';returns 0, and spot-check one previously-Notes task in the app (/preview-siteor :3070) shows the old note text as a paragraph at the bottom of its page body. Re-running the migrate script is a no-op. - Commit:
migrate(tasks): move existing Notes text into page body and drop Notes column
Task 8: Register and document¶
- Files:
/home/justinwieb/.claude/projects/-home-justinwieb-forge/memory/MEMORY.md(index line); append to the CreatorTrack workspace topic file (/home/justinwieb/forge/memory/general/reference_forge_data_lifeos_spine.mdor the CreatorTrack surface reference), noting: page body is the canonical notes surface (no Notes property), andconfig.timeon a date prop enables theTimePickerinDateCell(Tasks Due/Reminder flagged). - What: Record the two model changes so future sessions don't re-add a Notes column or hand-roll time handling. Include the
URL:line on any indexed.mdtouched. - Verification:
bash /home/justinwieb/forge/scripts/forge_eval_run.shpasses;grep -n "config.time"in the topic file returns the new note. - Commit:
docs(workspace): record notes-in-body model and config.time date cells