Skip to content

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, composeDateTime with the Chicago offset) into lib/datetime.ts as the single source of truth. Re-export them from taskDate.ts (keeping dueHasTimeClient/splitDueClient/composeDueClient names as thin aliases) so Composer.tsx and lib/tasks.ts keep working unchanged. No behavior change.
  • Verification: cd /home/justinwieb/forge-suite && npx tsc --noEmit passes, and node -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?: boolean to the PropConfig union so a date prop can opt into time-of-day editing. Type-only change.
  • Verification: cd /home/justinwieb/forge-suite && npx tsc --noEmit passes.
  • 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?.time is true, render the existing date <input> alongside TimePicker (from components/ui/TimePicker), using splitDateTime/composeDateTime from lib/datetime to read/write the single value via db.actions.setCell(rowId, prop.id, ...). Clearing the time collapses the value back to YYYY-MM-DD. When config.time is falsy, behavior is unchanged (date-only).
  • Verification: cd /home/justinwieb/forge-suite && npx tsc --noEmit passes; 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 the Due and Reminder prop seeds so newly created Tasks databases get time-aware cells. (Existing databases are handled in Task 5, since ensureProp is insert-if-absent and never updates config.)
  • Verification: cd /home/justinwieb/forge-suite && npx tsc --noEmit passes; grep confirms both seeds carry time: true: grep -n "time: true" lib/system-apps.ts returns 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 in 20260702T165227_tasks_app_attachments_and_notifications.sql.
  • Verification: apply with python3 /home/justinwieb/forge/scripts/forge_workspace_migrate.py then confirm zero remaining unflagged rows: re-running the migrate script is a no-op, and a SELECT 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.tsx header comment) and update comments/order.
  • What: Stop seeding the Notes column on new Tasks databases. Fix any stale references so nothing assumes a Notes prop exists.
  • Verification: cd /home/justinwieb/forge-suite && npx tsc --noEmit passes; grep -rn '"Notes"' components/apps/tasks lib/system-apps.ts returns 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_values row whose prop is a Tasks Notes (type='text') column with non-empty text, INSERT INTO app.blocks (nodeId, type, content, position) a paragraph block on that row's node with the Notes text and position = coalesce(max(position),0)+1 (append to bottom); (2) DELETE FROM app.collection_values for those Notes propIds; (3) DELETE FROM app.collection_props WHERE name='Notes' AND type='text' scoped to Tasks databases. Confirm the collection_values.value jsonb 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 in BEGIN/COMMIT so a partial failure rolls back.
  • Verification: apply with python3 /home/justinwieb/forge/scripts/forge_workspace_migrate.py; confirm SELECT count(*) FROM app.collection_props WHERE name='Notes' AND type='text'; returns 0, and spot-check one previously-Notes task in the app (/preview-site or :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.md or the CreatorTrack surface reference), noting: page body is the canonical notes surface (no Notes property), and config.time on a date prop enables the TimePicker in DateCell (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 .md touched.
  • Verification: bash /home/justinwieb/forge/scripts/forge_eval_run.sh passes; 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