Notion Decommission, Phase 2 (interactive + write-back paths)¶
URL: https://mkdocs.justinsforge.com/memory/plans/notion-decommission-phase2-2026-07-03/
Date: 2026-07-03. Author: [Claude Code]. Follows notion-decommission-2026-07-03 (phase 1 repointed reads). This scopes the remaining Notion-page-id-keyed interactive + write paths.
Headline finding¶
Almost nothing needs to be BUILT in forge-suite. CreatorTrack already exposes every habit + task endpoint the bots need, all callable with the ctk_ Bearer PAT (which resolves AS the owner via currentUser() on the normal /api/* surface). The spine scripts/forge_workspace_capture.py ALREADY has the generic api_call(method, path, body) ctk_ helper (L368), _get (L347), habits_day (L451), list_tasks, create_project. So Phase 2 is overwhelmingly PART B (bot/cron repointing), not PART A.
Genuine product decisions for Justin: (1) time-blocks have no CreatorTrack home; (2) whether morning/wellness reports pull from CreatorTrack /api/fitness/data or straight from the lifeos wellness.* Postgres schema (already fed by forge-wellness-pg-sync).
PART A: CreatorTrack endpoints (forge-suite)¶
A0. NOTHING to build for habits/tasks/orient. Already live (ctk_ Bearer):¶
- Habits read:
GET /api/apps/habits?workspaceId=67&since=YYYY-MM-DD->{ ok, ids, habits[], logs[], groups[] }(app/api/apps/habits/route.ts:17). Day's completion state = filterlogs[]bydate. - Habit toggle done:
POST /api/apps/habits { op:"upsert_log", workspaceId:67, defId, date, value:1 }(route L49). Un-do ={ op:"clear_log", ... }(L78). Alsoskip,increment,set_note. - Task list:
GET /api/tasks?view=<view>&workspace=<slug>(app/api/tasks/route.ts:9). - Task create:
POST /api/tasks { title, workspaceId, due?, priority?, tags?, notes?, projectId?, reminder? }(L34). - Task update/complete:
PATCH /api/tasks/{id}—{ done:true }toggles the checkbox (app/api/tasks/[id]/route.ts:20); or{ status?, title?, due?, priority?, projectId?, tags?, notes?, archived? }(L31-48).DELETE /api/tasks/{id}= archive. - Docs already cover all of the above:
forge-suite/docs/API-BOTS.md(Tasks §39, Habits §88).
A1. (OPTIONAL, product decision) Morning-report / wellness reads¶
GET /api/fitness/data (app/api/fitness/data/route.ts:9) takes NO params (active-workspace scoped; for the ctk_ owner that resolves to justin). If the migrated forge_morning_report.py / forge_wellness_daily_summary.py need a specific date or explicit workspace, add optional ?workspaceId=&date= query params to that route. RECOMMENDATION: skip this build; point the reports at the lifeos wellness.* Postgres schema directly (already populated by forge-wellness-pg-sync), matching the "grep-from-script -> Forge/PG" routing test. Only build the fitness/data params if Justin wants the reports to read through CreatorTrack.
A2. (PRODUCT DECISION) Time-blocks¶
No CreatorTrack equivalent. Nearest surface: Calendar app (GET /api/apps/calendar?workspaceId&start&end -> {events, tasks, settings}, app/api/apps/calendar/route.ts:30) = timed tasks overlay (due date only, no duration/block object). Options for Justin:
- (a) Retire time-blocks entirely (calendar = timed tasks is the replacement).
- (b) Build a real time-blocks collection + /api/apps/timeblocks CRUD in forge-suite (new app; sizable).
Do NOT migrate forge_time_blocks_* until Justin picks. This is the one true PART A build candidate and it is gated on his decision.
PART B: bot + cron repointing (forge/scripts). Do AFTER confirming A0 endpoints live in the target env.¶
Order matters: repoint reads+writes first, then flip the instantiate cron off.
B1. Habit checklist forms + callbacks (the core coupled path)¶
Files: forge_telegram_checklist.py, forge_habits_callbacks.py, and form builders tool_habits_form/tool_today_form/tool_habits_checkin in forge_telegram_inbox_brain.py.
- Read side: replace the Notion habit-row fetch with _spine.habits_day(date) (already wraps GET /api/apps/habits). Each pending item now carries defId (from habits[]/logs[]) not a Notion page_id.
- Form state: forge_telegram_checklist.py already persists form state to a file (_form_path, HABITS_LEDGER_PATH) because callback_data is 64-byte capped. Store {defId, date} per item there instead of the Notion page_id. Callback_data stays chk:t:<form_id>:<idx> (index into the stored list) — no wire-format change needed.
- Write-back: forge_habits_callbacks.py:_handle_done (L91) currently does _n8n_post("notion-update-page", {page_id, properties:{Quantity:1}}). Replace with forge_workspace_capture.api_call("POST", "/api/apps/habits", {"op":"upsert_log","workspaceId":67,"defId":defId,"date":date,"value":1}). _handle_skip -> op:"skip". _handle_defer stays local-ledger-only (no CreatorTrack analog; keep as-is or drop). Delete _n8n_post/notion-update-page usage.
- Task checklist (kind=="task", checklist L19): "Done" -> api_call("PATCH", f"/api/tasks/{id}", {"done": True}) instead of Notion Status=Done. Item id becomes the CreatorTrack task id from list_tasks.
- Drop _notion_dbs()/_notion_db_url() and the Notion source-URL buttons; point URL buttons at tasks.justinsforge.com/CreatorTrack instead.
B2. tool_update_task (brain L1429)¶
Replace the Notion find+update with: resolve task via _spine.list_tasks(...), then api_call("PATCH", f"/api/tasks/{id}", patch). No new endpoint needed.
B3. forge_orient.py¶
- Reads (inbox/tasks/goals/areas): swap
NotionAPIfor_spine.list_tasks+_spine.search_notes+GET /api/apps/goals. - Writes (task field updates on execute):
api_call("PATCH", f"/api/tasks/{id}", {...}). - Remove
NotionAPI/NOTION_DBSimport once both sides are swapped.
B4. Wellness crons (repoint)¶
forge_morning_report.py,forge_wellness_daily_summary.py,forge_workout_recovery_stamp.py,forge_fitness_weekly_retro.py,forge_wellness_gap_repair.py,forge_heartbeat_random.py,forge_telegram_datepick.py: repoint Notion reads to the lifeoswellness.*PG schema (preferred) orGET /api/fitness/dataper A1.- Timer
forge-morning-report-snooze-check.timer: KEEP (repoint its script's reads only). forge-wellness-pg-sync.timer: KEEP unchanged (canonical feed).
B5. Hevy->Notion mirror (RETIRE)¶
forge_hevy_backfill.py, forge_hevy_notion_setup.py, forge_hevy_poll.py, forge_hevy_read.py and tool_sync_workouts/sync_now in the brain. Superseded by forge-wellness-pg-sync -> CreatorTrack Fitness (read-only). No active systemd timer exists for these (already dormant). Delete the scripts + remove the two brain tools (add to RETIRED_TOOL_NAMES).
B6. Habit instantiation / streak crons (RETIRE, gated on B1)¶
CreatorTrack does NOT pre-instantiate daily habit rows: logs are created on demand, keyed by (defId, date) via upsert_log. Streaks are computed client/lib-side (components/apps/habits/habitsMath, lib/habits.ts). Therefore, once B1 lands:
- forge-habits-instantiate.timer (04:00) — RETIRE (systemctl disable --now; the Notion rows it creates are read by nothing after B1). Do NOT disable before B1 or live forms break.
- forge-habits-streaks.timer (23:55) — RETIRE (CreatorTrack computes streaks itself).
- forge-habits-alias-backfill.timer (03:30) — RETIRE (Notion-alias-specific).
- Remove the matching unit files under ~/.config/systemd/user/ (or /etc/systemd/system/): forge-habits-instantiate.{timer,service}, forge-habits-streaks.{timer,service}, forge-habits-alias-backfill.{timer,service}, plus their .service.d/ drop-ins. Delete forge_habits_instantiate.py and streak/alias scripts.
B7. Time-blocks (BLOCKED on A2 decision)¶
forge_time_blocks_tools.py, forge_time_blocks_provision.py, forge_time_daily_aggregator.py: do nothing until Justin decides retire vs build. No time-blocks timer is currently active.
Deletion gate: scripts/forge_notion_api.py¶
Delete NotionAPI/_notion_api/NOTION_DBS only after every live importer below is repointed/retired:
| Importer | Cleared by |
|---|---|
| forge_habits_callbacks.py, forge_telegram_checklist.py | B1 |
| brain tool_update_task, retired page tools, dormant n8n("notion-...") fallbacks | B2 (+ delete dormant fallbacks) |
| forge_orient.py | B3 |
| forge_food_log.py | already off Notion (phase 1) — confirm no residual import |
| forge_hevy_*, forge_hevy_notion_setup.py | B5 (retire) |
| forge_time_blocks_*, forge_time_daily_aggregator.py | B7 (blocked) |
| forge_workout_recovery_stamp.py, forge_fitness_weekly_retro.py, forge_habits_streaks.py | B4 / B6 |
Final check before delete: grep -rniE 'notion' scripts/ returns zero live code refs (comments/handoffs OK). forge_notion_api.py is the LAST thing to go, after B7 is resolved.
Recommended execution order¶
- Confirm A0 endpoints answer to the bots' ctk_ token in the target env (dev :3060/:3070, then prod
app.creatortrack.ai). - B1 (forms + callbacks) — highest value, unblocks the instantiate crons.
- B2, B3 (task update + orient).
- B6 (disable/retire the three habit crons) — only after B1 verified live.
- B4 (wellness reports) + B5 (retire Hevy mirror).
- Get Justin's A2 decision -> B7.
- Delete
forge_notion_api.pyonce the grep gate is clean.