Skip to content

Plan: CreatorTrack Workstream A, generic task primitives (dev-capable without a dev mode)

URL: https://mkdocs.justinsforge.com/memory/plans/creatortrack-task-dev-primitives-2026-07-11/

Date: 2026-07-11 Branch / slot: feat/task-dev-primitives, worktree /home/justinwieb/forge-suite-wt/feat/task-dev-primitives, dev server http://100.97.43.104:3062, DB ct_feat_task_dev_primitives (own clone). Off main b67e7ca. Design authority: handoff Workstream A (read its Workstream A section, it is the approved design). This plan is the execution breakdown.

North Star (governs every choice): CreatorTrack becomes a real project/dev tracker WITHOUT ever looking like one. Every primitive must make obvious sense for a content creator too. NEVER build a "dev mode", a "software" label, or a dev-only preset. Same code, same UI, configured differently by the user. If a choice would read as "this is for programmers", reframe it generically.

Out of scope: anything in Workstreams B (done), C, D, E. No merge to main, no deploy. This is an isolated worktree build.

Critical constraints (CT dev gotchas, do not trip on these)

  • Commit after EVERY task with a conventional message. The dev-slot reaper deletes idle/over-cap worktrees and eats UNCOMMITTED work (there are already 5 live slots vs a cap of 4). Committed work is safe on the branch. Never leave a finished piece uncommitted.
  • Dev server is already running on :3062 via next dev --webpack (turbopack symlink-breaks in a worktree, webpack is required, do not switch). Do NOT start another server. If types look stale, the fix is a stale .next dev cache, not a code change.
  • The slot uses WORKSPACE_DEV_AUTH=1; you are auto-logged-in as the owner over http. NEVER attempt a fresh Google login over http.
  • Migrations: python3 ~/forge/scripts/forge_workspace_new_migration.py "desc" to create, PGDATABASE=ct_feat_task_dev_primitives WORKSPACE_MIGRATIONS_DIR=db/migrations python3 ~/forge/scripts/forge_workspace_migrate.py to apply (idempotent DDL only). Apply ONLY to this slot's DB.
  • Verify each task with npx tsc --noEmit (clean) AND a real check against :3062 (open the affected view, confirm the behavior). Do not report a piece done on tsc alone when it has a runtime surface.
  • No em dashes anywhere. Match surrounding code style.

Key existing structures (verified in handoff)

  • A task is a row in the generic nodes table (db/schema.ts:99, type="task"). Typed fields live in collection_props (defs) / collection_values (values) / collection_relations (links) (db/schema.ts:149-183). Logic: lib/tasks.ts. Default props seeded in lib/system-apps.ts (~line 253).
  • Board: components/apps/shared/KanbanBoard.tsx -> components/apps/projects/ProjectBoardView.tsx, groups by Status only today.
  • Relations are hardwired self-only (blockedBy, parentTask).
  • Note-to-task: components/apps/tasks/ConvertToTaskModal.tsx.
  • CleanTask shape: lib/tasks.ts:21-74.

Task list (dependency/leverage order; commit each)

Task 1: Generalize the board group-by to any select/status field

  • Files: components/apps/shared/KanbanBoard.tsx, components/apps/projects/ProjectBoardView.tsx, the tasks board view wiring.
  • What: Board groups by Status only today. Generalize so the board can group by ANY select-or-status field (Status, Priority, Type, Milestone, or any user select prop). Add a group-by picker in the board header. Drag between columns writes the value of the grouped field (for Status keep current behavior; for a generic select, set that prop's value). This single change IS "milestones", "swimlanes", and creator campaign-columns at once. Ungrouped/null values get a "No " column.
  • Verification: npx tsc --noEmit; on :3062 open a workspace's tasks board, switch group-by to Priority, drag a card between columns, confirm the priority actually changes and persists on reload.
  • Commit: feat(tasks): board group-by any select/status field

Task 2: Seed default Type and Milestone select fields (generic, no dev label)

  • Files: lib/system-apps.ts (default-prop seed ~253), lib/tasks.ts if CleanTask needs the field surfaced.
  • What: Add two default select props to the tasks collection: Type and Milestone, both EMPTY-optioned by default (user adds their own options; a dev types bug/feature/chore, a creator types Reel/Long-form/Sponsored). No preset dev options, no label implying software. They become immediately usable as board group-by fields (Task 1). Ensure existing workspaces get them via the seed/backfill path the app already uses for default props (match how Status/Priority/Tags are seeded, idempotent).
  • Verification: npx tsc --noEmit; on :3062 open a task, confirm Type + Milestone fields appear and are editable; confirm the board group-by picker (Task 1) now lists them.
  • Commit: feat(tasks): default Type and Milestone select fields
  • Files: db/schema.ts (if a new prop kind enum value is needed), lib/tasks.ts, the collection prop UI + the task field render components.
  • What: Add an external-URL prop kind: stores a URL string, renders as a clickable link (opens new tab, safe rel). Dev links a PR/commit; a creator links a published video/Drive asset/deal. Add it as an addable prop kind (not a forced default). Validate/normalize the URL (require http(s) or coerce).
  • Verification: npx tsc --noEmit; on :3062 add a URL field to a task, paste a link, confirm it renders as a working external link.
  • Commit: feat(tasks): external-URL field type

Task 4: Task -> page (node-to-node) relation

  • Files: db/schema.ts (collection_relations), lib/tasks.ts, the relation-field UI.
  • What: Relations are hardwired self-only (blockedBy/parentTask). Generalize collection_relations so a task can relate to a type="page" (or other node type) node, not only another task. Add a relation-field UI to link a task to a page/doc (a picker that searches nodes). Dev links an issue to its spec page; a creator links a task to a script/brand doc. Reuse the existing self-relation machinery; un-hardwire the target type.
  • Verification: npx tsc --noEmit; on :3062 create a page, then from a task add a relation to that page, confirm the link resolves both directions.
  • Commit: feat(tasks): task-to-page node relation

Task 5: Bulk paste -> tasks

  • Files: components/apps/tasks/ConvertToTaskModal.tsx (extend) or a new paste handler in the tasks views.
  • What: Paste a newline-separated list; each non-empty line becomes a task node in the current list/project/workspace. Dev pastes a bug list; a creator pastes a content-idea dump. Trim blank lines; respect the active workspace/list. Idempotent per-invocation (no double-create on a single paste).
  • Verification: npx tsc --noEmit; on :3062 paste a 3-line list into the tasks view, confirm exactly 3 tasks are created with those titles.
  • Commit: feat(tasks): bulk paste to tasks

Task 6: Dated timeline / gantt view (net-new)

  • Files: new view component under components/apps/tasks/ (or components/apps/shared/), register it in the tasks view switcher alongside list/board/matrix/database/search.
  • What: A timeline/gantt view: tasks laid out as dated bars across a horizontal time axis using start + due/dueEnd (from CleanTask). Draw dependency links from the existing blockedBy relation. Group rows by any select field (reuse Task 1's group-by), so a dev sees a release timeline and a creator sees a content calendar timeline, same view. Support drag to reschedule (updates start/due) if feasible; if drag is too big for this pass, ship read-only bars + a note, and commit that, then add drag as a follow-up commit. Framed generically (call it "Timeline", not "Gantt/Sprint").
  • Verification: npx tsc --noEmit; on :3062 add a Timeline view to the tasks app, give two tasks dates and a blockedBy link, confirm the bars render on the axis and the dependency draws.
  • Commit: feat(tasks): timeline view with dated bars and dependencies (+ a follow-up commit if drag-reschedule lands separately)

Task 7: Register and document

  • Files: /home/justinwieb/forge/memory/general/reference_creatortrack_task_primitives.md (new, mkdocs URL line, status BUILT-on-branch-not-deployed), /home/justinwieb/forge/MEMORY.md index line under CreatorTrack.
  • What: Document the new primitives, the "no dev-mode label" framing, files touched, and that Timeline is the gantt. Note merge-day: this touches db/schema + migrations, so prod needs the migration applied on merge.
  • Verification: the reference file has the URL line; MEMORY.md line resolves.
  • Commit: docs(memory): creatortrack task primitives reference (forge repo, separate from the worktree)

Reporting

Write a running report to /home/justinwieb/forge/comms/results/creatortrack-task-dev-primitives.md after each task (task, commit hash, verification result). When all done, report: branch name, per-task commits, what to review, and anything that had to ship partial (e.g. timeline drag deferred).