Plan: Wire LifeOS + inbox-capture bots into all of CreatorTrack (prod), on a permanent Justin's Workspace¶
URL: https://mkdocs.justinsforge.com/memory/plans/lifeos-bots-creatortrack-capstone-2026-07-02/
Repos: bots in /home/justinwieb/forge (scripts/forge_telegram_*), app in /home/justinwieb/forge-suite (CreatorTrack Suite, RLS Postgres lifeos on CT109). Target: prod https://app.creatortrack.ai.
Goal¶
The Telegram inbox-capture and LifeOS coordinator bots can create/log across every CreatorTrack surface as Justin, writing into ONE permanent canonical workspace on prod. Decisions locked with Justin (2026-07-02): create a fresh pinned "Justin's Workspace"; full capstone in one build.
Reality of the surface (what "everything" resolves to)¶
Writable today via documented prod API (docs/API-BOTS.md) — bot just needs auth + workspace:
- Tasks POST /api/tasks; Projects/lists POST /api/tasks/projects
- Pages POST /api/node, append POST /api/node/{id}/append
- Knowledge / inbox note POST /api/knowledge (and /api/quick-note)
- Habits POST /api/apps/habits (create_habit, upsert_log, increment, clear_log, set_note, update_habit, archive_habit)
- Food log POST /api/apps/meals (create_entry {name,date,meal,macros}, update_entry, goals)
Not a native write store (set expectations, don't fake it):
- Calendar has no create-event endpoint. Calendar app = read overlay of Google + tasks. "Put it on my calendar" => create a timed CreatorTrack task (see companion plan tasks-due-time-calendar-reminders-2026-07-02) and/or push to Google Calendar via the existing forge_google_user client. Bot exposes both paths; no new native calendar store.
- Fitness is auto-fed by Hevy + wellness-PG sync (/api/fitness read-only, only /refresh). Bot reads/reports fitness; it does not hand-log workouts.
Owner gating: meals/calendar/fitness/finances require PERSONAL_APPS_OWNER_EMAIL ([email protected]). Token minted as user id 1 (owner) clears this.
Architecture facts¶
- Token auth:
lib/api-tokens.tsmintToken()=ctk_+ 32 base64url random bytes; only SHA-256 stored incore.api_tokens.createApiToken(identity, name)mints + returns plaintext ONCE. Bearerctk_resolves to a full user identity (identityForToken), so a token acts AS Justin. - Workspace resolve:
resolveWorkspaceId(id, slug?)-> slug match (archived=false) else user's first membership (lib/system-apps.ts:874). Bots will pass the canonical slug explicitly. - App seeding:
installSystemApp(id, workspaceId, kind)for eachSystemAppKindinSYSTEM_APP_KEYSprovisions a surface (idempotent). - Bot brains already use a
tool_*function pattern; inbox brain importsforge_workspace_capture as _spine(forge_telegram_inbox_brain.py:56). Capture client default URL is devhttp://localhost:3060/api/capture(forge_workspace_capture.py:29) — must move to prod. - Current state:
core.api_tokens= 0 rows; capture bot writes to dev; coordinator has zero CreatorTrack tools.
Non-goals¶
- No native calendar-event store; no bot-written workouts (read-only fitness).
- No change to RLS model; the token rides the same RLS identity a logged-in Justin has.
- Finances stay out of bot scope for now (sensitive; revisit separately).
Phase 0 - Permanent canonical "Justin's Workspace"¶
- Create workspace with a stable, suffix-free slug
justin(name "Justin's Workspace", kindpersonal), owned by user id 1. Because the app's create route auto-appends a random suffix, create it aslifeos_admindirectly (INSERTcore.workspaces+ ownercore.membershipsrow for user 1) so the slug is exactlyjustinand stable. Guard:slug='justin'unique; abort if taken. - Provision ALL surfaces: loop
installSystemAppover everySYSTEM_APP_KEYSkind (tasks, habits, meals, knowledge, calendar, journal, goals, crm, ...) viaasUser(user1)so RLS + provisioning run exactly as the app would. - Pin it against purge/dup: add a
core.workspaces.pinned boolean not null default falsecolumn (idempotent migration) and set it true for this row; makecore.purge_expired_workspaces/ any workspace-delete path skippinned=true. This is the "permanent" guarantee. - Record the resulting
id+ slug in the CreatorTrack reference file.
Verify: SELECT id,slug,pinned FROM core.workspaces WHERE slug='justin' returns the row; the app's workspace list shows it with all apps; a delete/purge attempt is refused.
Commit (app repo): feat(workspace): pinned flag + canonical Justin workspace guard (migration + purge guard). Workspace-row creation itself is a data op (scripted, not a migration).
Phase 1 - Mint the machine-auth token¶
- Mint a
ctk_token as user id 1 named "LifeOS Bots" by invokingcreateApiTokenthrough the app's node runtime (a one-offtsx/node script inforge-suitethat callslib/api-tokens.createApiToken), capturing the plaintext once. - Store it at
~/.forge-secrets/creatortrack-bot.env(chmod 600):CREATORTRACK_API_TOKEN=ctk_...,CREATORTRACK_BASE_URL=https://app.creatortrack.ai,CREATORTRACK_WORKSPACE_SLUG=justin. Never in git.
Verify: curl -H "Authorization: Bearer ctk_..." https://app.creatortrack.ai/api/workspace/list returns the workspaces incl. justin; a meals GET with the token returns 200 (owner gate passes), a scratch non-owner token gets 403.
Commit: none (secret only).
Phase 2 - Expand + repoint the capture client to prod¶
scripts/forge_workspace_capture.py: loadcreatortrack-bot.env; default base URL -> prod; auth viaAuthorization: Bearer ${CREATORTRACK_API_TOKEN}against the documented/api/*routes (not the legacy/api/capturedev door). Default workspace ->CREATORTRACK_WORKSPACE_SLUG. Keep existing function signatures (create_task,log_habit,save_to_inbox) so no bot edits break; ADD:create_habit,create_page,append_page,quick_note,log_food,create_project,list_workspaces. Each is a thin idempotent POST with fail-loud error surfacing (doctrine: no catch-and-ignore).- Preserve a rollback: env flag
CREATORTRACK_CAPTURE_URLstill overrides for dev testing.
Verify: from Console shell, call each new client fn against prod; confirm rows land in the justin workspace (query core.* / app.* as admin).
Commit: feat(capture): prod Bearer client covering tasks/habits/pages/knowledge/meals
Phase 3 - Inbox-capture bot: full-surface tools¶
scripts/forge_telegram_inbox_brain.py: addtool_*wrappers for the new client fns (tool_log_habitalready exists via spine; addtool_create_habit,tool_log_food,tool_quick_note,tool_create_page,tool_create_project). Register them in the brain's tool schema so the LLM can pick them from a natural capture ("ate 2 eggs", "did my meditation", "note: idea for video").- Update the brain's system prompt with the surface menu + the canonical-workspace note, and the calendar/fitness caveats (calendar => timed task; fitness read-only).
Verify (untested-until-real-msg honesty): send real Telegram captures for each surface; confirm each lands correctly and the bot replies with a reaction/confirmation per feedback_telegram_bot_ux_reactions.
Commit: feat(inbox-bot): capture into habits/meals/knowledge/pages on prod
Phase 4 - LifeOS coordinator: CreatorTrack write tools¶
scripts/forge_telegram_brain.py(coordinator): add the same CreatorTrack tool set (tasks, projects, pages, knowledge, habits, food log) + read tools (list_tasks,habits_overview,fitness_read) using the prod Bearer client. Respect the coordinator's existing tool-catalog registration +MAX_CONCURRENT_BRAINS.- Calendar tool: route "schedule X at T" to a timed CreatorTrack task (companion plan) AND/OR
forge_google_userGoogle event, per the bot's existing calendar tools (tool_create_calendar_event). Fitness tool = read-only report.
Verify: coordinator conversation creates/reads across surfaces on prod; brain latency stays in normal band (median ~10s).
Commit: feat(coordinator): CreatorTrack read+write tools on prod
Phase 5 - E2E QA + register¶
- From Telegram, exercise every surface end-to-end against prod's
justinworkspace: task (+timed), project, page + append, inbox note, habit def + log, food entry. Confirm each via the app UI and via admin SQL. - Confirm owner-gated meals works with the token; confirm a purge/delete cannot remove the pinned workspace.
- Register:
memory/general/reference_agent_integrations.md(add the bot token + canonical workspace), a newreference_creatortrack_bot_api.mdtopic +MEMORY.mdindex line, and updatedocs/API-BOTS.mdif any client-observed behavior differs. Updatememory/daily/2026-07-02.md.
Commit: docs(memory): CreatorTrack bot API + canonical workspace wiring
Risks / watch-items¶
- Token = full Justin identity. Store chmod 600, never log, never commit. One token, revocable via
/api/tokensif leaked. - One-poller-per-token rule for the bots (do not start a second poller to test;
feedback_telegram_bot_token_ownership). - Idempotency: captures must dedup (same message twice = same end state); the capture layer already keys on this — preserve it in new fns.
- Owner gate could 403 meals/calendar if the token user isn't owner — mint as user id 1 specifically.
- Pinned guard must cover BOTH the lazy purge and the explicit workspace-delete route, or "permanent" leaks.
- Calendar/fitness expectations documented so the bot never claims a native calendar event or a logged workout it didn't make.
[Claude Code]