Skip to content

Workspace architecture spec: a custom Notion (everything-is-a-page + rich widgets)

URL: https://mkdocs.justinsforge.com/memory/plans/workspace-notion-architecture-2026-06-16/

CANON UPDATE (2026-06-18) , renamed to CreatorTrack. The product formerly called "The Suite" / "Workspace" is now CreatorTrack, built and previewed at dev.creatortrack.ai. Build order: reusable UI components first (one shared kit), then a Notion-class workspace/page framework as the foundation, then apps (finance, clips, CRM, etc.) that plug into that page framework as configs/modules; we iterate back-and-forth across kit, framework, and apps until each aspect's features fully work. The data layer formerly "lifeos" is now the core database (core); "workspace" survives only as the internal RLS data-tenant term. Full canon (with tech-stack overview): creator-suite-vision-strategy.

Owner: Justin. Drafted 2026-06-16. Status: APPROVED + IN BUILD. Node engine live (app.nodes, everything-is-a-page, empty workspaces, + New menu). Apps built on it: Tasks (TickTick-style), Habits (icons/goal/streak/heatmap), Time Tracker (start-stop + Google Calendar sync), Meals (calories/macros + USDA autofill). Doc/block editor on app.blocks. Bot capture repointed to app.tasks. Next: GCal two-way task sync, then Email / CRM / Calendar apps; rebuild remaining as node types. Old fixed-section schemas (tasks/habits/docs/inbox) retired in UI, drop pending.

Decision of record: BUILD CUSTOM (not Affine/AppFlowy). Reason (Justin, 2026-06-16): he wants one customizable surface that can hold pages AND email AND a CRM AND finance views AND any tool we dream up, all glued by him + Claude. A finished clone can't be extended that way; a custom app on the Postgres spine can.


1. The one idea that fixes everything

Everything is a page (a node in a tree). Nothing is a fixed section. A workspace starts empty; you add what you want. "Tasks" and "Habits" are not special code, they are databases you create. The sidebar is the page tree. This kills the two big mistakes: fixed finance-style tabs, and tools forced into business workspaces.

"Everything is a page" is the ORGANIZING model, not a limit on richness. What a page renders is open: a page can be plain notes, or a database with a calendar view, or a dashboard of animated charts and buttons. The tree files things; page-types + widgets provide the richness.

2. Node model (replaces the per-domain tables)

One table is the spine of the UI: nodes.

app.nodes - id, workspace_id, parent_id (self-nest = the tree), position - type: doc | database | view | dashboard | connected - title, icon (emoji or glyph) - props jsonb (type-specific config) - archived, created_by, created_at, updated_at - RLS: core.apply_workspace_rls('app.nodes')

Page types: - doc: freeform content = ordered blocks (generalize the existing doc_blocks). - database: owns a schema (columns) + rows. The Notion "database." - view: a saved lens on a database (table/board/calendar/gallery) with filter/sort/group in props. Views are child nodes of a database (or embedded). - dashboard: a page whose blocks are widgets (charts, counters, buttons, embedded views). Animations live here. - connected: a page backed by a live external source (Gmail, finance tables, Shopify) via a mapping in props; rendered like a database, read-mostly.

3. The generic database engine (the part I should have built first)

  • app.db_columns (node_id -> database, key, name, type, options jsonb, position). Column types: text, number, select, status, multiselect, date, checkbox, person, relation, url, formula(later).
  • app.db_rows (id, node_id, workspace_id, props jsonb {col_key: value}, position, timestamps). A row may itself have block content (it is a page). RLS by workspace.
  • Views render rows from props with filter/sort/group:
  • table view = grid of columns
  • board view = group by a select/status column (kanban)
  • calendar view = place rows by a date column <- the habit calendar
  • gallery/list = later
  • "Tasks" = a database with columns Status/Priority/Due/Project. "Habits" = a database with columns Goal/Unit/Cadence + a logs database. Ten more tomorrow, no new code.

4. Blocks + widgets (richness INSIDE a page)

Generalize blocks beyond text. A page's content is a list of blocks; block types: - text set: paragraph, heading1-3, bullet, numbered, todo, quote, code, divider (already built) - widget blocks (the new power): - button , action: log_habit / create_row / run_worker / draft_email / open_url / call_endpoint. Buttons that do real things. - embed_view , embed a database view inline (e.g. show today's tasks on a dashboard). - heatmap , habit-style calendar heatmap over a database's date+value columns. - chart , bar/line/donut over a database or a connected source. - callout, toggle, image, bookmark , later. - Widgets are where animations + interactivity live. This is strictly more than Notion, because we render them.

5. Connected views (why custom wins: email / CRM / finance in one place)

A connected node declares { source, query/mapping } in props and renders like a database: - Email tab , source = Gmail via the existing forge_gmail.py; rows = threads; actions (archive/label/draft) as row buttons. - Finance , source = the spine's finance schema; read-only table/chart views. - CRM , either a native database you build (people + interactions) or a connected view over a contacts source; relations link companies <-> people. - Commerce , connected view over datalayer/Shopify. The spine stays the single source of truth; connected views are windows onto it.

6. What gets reworked from the shipped Phase 2

  • docs.docs/doc_blocks -> generalize into app.nodes (type=doc) + app.blocks.
  • tasks/projects/habits/inbox hardcoded tables -> RETIRE as bespoke tables; re-create as databases via the engine (fresh start, per Justin , no data to keep).
  • The page-tree sidebar (already built) stays; it now lists ALL nodes, not just docs.
  • The fixed TOOLS nav (Tasks/Habits/...) goes away. Workspaces start empty; the owner adds a "Tasks" database, a "Habits" database, an Email tab, etc. Optionally a one-click "starter kit" seeds a sensible Personal set you can delete.
  • Bot capture writes a ROW into the target workspace's "Tasks"/"Inbox" database via a generic create_row API (keeps the WORKSPACE_WRITE_TARGET flag + brand map).

7. Build order (incremental, each step usable + verified, RLS-gated)

  1. Node engine + tree: app.nodes, generalize doc pages, sidebar lists nodes, create/nest/rename any node. (Re-confirm the RLS gate on app.nodes.)
  2. Database engine + table view: columns + rows + table. Re-create "Tasks" as a database to prove it (board + calendar next).
  3. Board + calendar views: kanban + the habit calendar. Re-create "Habits" as a database + heatmap widget.
  4. Widget blocks: button, embed_view, chart, heatmap. Build a Personal dashboard.
  5. Connected views: Email (Gmail), Finance (spine), then CRM/commerce.
  6. Polish: slash menu, drag-reorder, page icons/emoji, templates, animations.

Each step: build -> verify through the app (incl. a member-isolation RLS check) -> commit. No big-bang.

8b. Pages can be bespoke APPS, not just Notion tables (Justin, 2026-06-16)

Critical refinement: a page's UI is open. Some pages are generic (doc, database table). Others are full purpose-built apps that share the data engine underneath but render their own interface, including their own in-page secondary sidebar:

  • Tasks = TickTick-style. Clicking Tasks opens a page with its OWN left sub-sidebar: Lists (add/rename lists), Tags, smart filters (Today/Upcoming). Tasks are concrete to-dos; ones with a due date/time two-way sync to Google Calendar ([email protected]) via the existing Google integration.
  • Inbox = thoughts/ideas only (distinct from Tasks). Quick capture, triage to a task or page.
  • CRM = Trello-style board: customers/deals as draggable cards across columns; people <-> company relations; interaction log.
  • Email = a real client page over ALL Justin's accounts (forge_gmail.py), with per-thread buttons: AI summary, auto-draft reply, archive/label.
  • Calendar = live Google Calendar ([email protected]), the same source Tasks syncs into.
  • Habits = database + heatmap/calendar widget.
  • Time tracker = timer widget + a time-entries database (start/stop buttons).
  • Meal + calorie tracker = food-log database + calorie/nutrition lookup (USDA data already wired in forge).
  • Fitness = integrate the EXISTING custom fitness.justinsforge.com (embed as a page now; fold in natively later). Do not rebuild from scratch.

Architecture impact: add page sub-type/app rendering. The node still lives in the tree (everything is a page), but its renderer can be a custom app component with its own internal navigation. The generic database engine (section 3) is the shared data layer for all of them; connected views (section 5) back Email/Calendar/Finance.

8c. Personal workspace, confirmed contents (Justin's list)

Inbox, To-do/Tasks (TickTick-style + GCal sync), Habits, Calendar (GCal), Email (all accounts + AI buttons), CRM (Trello-style), Time tracker, Meal/calorie tracker, Fitness (existing custom app integrated).

9. Open questions for Justin

  1. Starter kit: when you make a new workspace, start fully empty, or offer a one-click "Personal starter" (Tasks + Habits + a few pages) you can delete?
  2. Priority after the engine: which connected tab first , Email, Finance, or CRM?
  3. Keep the current shipped tasks/habits rows, or truly start fresh (retire those tables) given the rework? (Leaning: fresh start, you said so.)