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)¶
- Node engine + tree: app.nodes, generalize doc pages, sidebar lists nodes, create/nest/rename any node. (Re-confirm the RLS gate on app.nodes.)
- Database engine + table view: columns + rows + table. Re-create "Tasks" as a database to prove it (board + calendar next).
- Board + calendar views: kanban + the habit calendar. Re-create "Habits" as a database + heatmap widget.
- Widget blocks: button, embed_view, chart, heatmap. Build a Personal dashboard.
- Connected views: Email (Gmail), Finance (spine), then CRM/commerce.
- 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¶
- 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?
- Priority after the engine: which connected tab first , Email, Finance, or CRM?
- Keep the current shipped tasks/habits rows, or truly start fresh (retire those tables) given the rework? (Leaning: fresh start, you said so.)