Skip to content

Plan: Full CRUD parity for the coordinator/brain bot across all CreatorTrack entities

URL: https://mkdocs.justinsforge.com/memory/plans/creatortrack-bot-crud-parity-2026-07-04/

Date: 2026-07-04 Approved spec: Add per-entity narrow edit_* / delete_* bot tools that wrap forge-suite update/delete ops that already exist server-side (no new server code). Tools are self-documenting per entity. Deletes are soft-verify: fetch the target row first, return its summary, error on id-miss; prefer archive where forge-suite offers it (archive_habit, archive_goal, task trash). Read tools surface the stable row id so the LLM has a handle. Entities: food/meal, habit (def + log), task, note/page/block, intake, goals, journal, CRM, knowledge, CT calendar. Out of scope: any new forge-suite server endpoints; Google Calendar (already has full CRUD tools); bulk/batch edits beyond what an op natively takes; a UI; changing capture-create semantics.

Server contract (verified from /home/justinwieb/forge-suite/app/api, 2026-07-04)

Apps are POST op-dispatch (body carries op + workspaceId + top-level id + nested sub-object). Core content is REST on /[id].

Entity Update Delete/Archive GET id field
meals op=update_entry {workspaceId,entryId,entry:{...}} op=delete_entries {workspaceId,entryIds:[]} (hard) entries[].id (num)
habits (def) op=update_habit {workspaceId,defId,habit:{...}} op=archive_habit {workspaceId,defId} (archive only) habits[].id == defId
habits (log) op=upsert_log/set_note/skip {workspaceId,defId,date,...} op=clear_log {workspaceId,defId,date} keyed by defId+date
intake (no edit-entry; check-off model) op=uncheck {workspaceId,itemId,date?} items[].id (num)
goals op=update_goal {workspaceId,goalId,goal:{...}} op=archive_goal {workspaceId,goalId} (archive only) goals[].id == goalId
journal op=update_entry {workspaceId,entryId,entry:{...}} op=delete_entry {workspaceId,entryId} (hard) entries[].id STRING -> coerce Number
crm op=update_person {workspaceId,personId,person:{...}} op=delete_people {workspaceId,personIds:[]} (hard) people[].id (num)
calendar op=update {workspaceId,id,event:{...}} (owner-only, local rows) op=delete {workspaceId,id} events[].id (num)
tasks PATCH /api/tasks/[id] {title?,status?,due?,priority?,tags?,done?,archived?} DELETE /api/tasks/[id] (trash) / ?forever=1 (hard) tasks[].id == path
node PATCH /api/node/[id] {title?,icon?,...} (no delete on this route) id path
block PATCH /api/block/[id] {content?,type?,props?,position?} DELETE /api/block/[id] (hard) blocks[].id (num)
knowledge PATCH /api/knowledge/[id] {section?,read?} (no title/text edit; that's node/block) DELETE /api/knowledge/[id] (soft trash) notes[].id (num)

Gotchas: meals/crm/goals/habits/journal wrap fields in a sub-object (entry/person/goal/habit); id is top-level. journal entries[].id is a STRING but entryId must be a NUMBER. crm interaction id is a STRING. Apps require workspaceId:number; tasks/knowledge resolve active workspace when omitted. justin workspace id = 67 (resolved via _resolve_ws_id).

Architecture

Three layers, mirroring the existing create path: 1. scripts/forge_workspace_capture.py — thin update_* / delete_* methods over api_call(method, path, body) (already exists, L401). Delete methods first read the day/list, resolve+verify the id, then mutate. 2. scripts/forge_telegram_inbox_brain.py ("core") — tool_edit_* / tool_delete_* wrapper fns returning bot-formatted strings; add JSON schema to TOOLS (L2233); add handler to TOOL_HANDLERS (L2979). 3. scripts/forge_telegram_brain.py — add new names to COORDINATOR_HOT_TOOL_NAMES (L99) + CAPTURE_TOOL_NAMES (L47) where a capture-surface bot should also see them; add prompt guidance rows.

Tool-name convention (locked): edit_food, delete_food, edit_habit, archive_habit, edit_task, delete_task, edit_note (page/block text), delete_block, edit_intake (semantics: uncheck a dose), edit_goal, archive_goal, edit_journal, delete_journal, edit_contact, delete_contact, edit_calendar_local, delete_calendar_local, edit_knowledge (section/read), delete_knowledge. Read tools that already exist gain an id in each row.

Task list

Task 1: Surface stable ids in the food/intake/habit read tools

  • Files: /home/justinwieb/forge/scripts/forge_workspace_capture.py (food_day L523, intake_day L544, habits_day L592 already carry raw entries; confirm id passthrough), /home/justinwieb/forge/scripts/forge_telegram_inbox_brain.py (tool_food_today L928, tool_food_summary L1033, tool_habits_checkin/reads that render entries)
  • What: Ensure each rendered entry/row line the LLM sees includes its numeric id (e.g. #4123), since edit/delete target by id. food_day passes entries through raw so the id is already present in data; the fix is in the bot render fns that currently omit it. Add id to the human-readable output and to any structured return.
  • Verification: cd /home/justinwieb/forge/scripts && python3 -c "import forge_telegram_inbox_brain as c; print(c.tool_food_today())" shows an id token per food line.
  • Commit: feat(ct-crud): surface entry ids in food/intake/habit bot read tools

Task 2: Capture-client update/delete for the op-dispatch apps (meals, journal, goals, crm, calendar)

  • Files: /home/justinwieb/forge/scripts/forge_workspace_capture.py
  • What: Add update_food(entry_id, brand="life", **fields) -> api_call("POST","/api/apps/meals",{workspaceId,op:"update_entry",entryId,entry:{...}}); delete_food(entry_id, brand) -> read-back via food_day, verify id present, then op:"delete_entries",entryIds:[entry_id]. Same pattern: update_journal/delete_journal (coerce id to int), update_goal/archive_goal, update_contact/delete_contact, update_calendar_local/delete_calendar_local. All resolve workspaceId via _resolve_ws_id(brand) and fail loud on missing ws.
  • Verification: cd /home/justinwieb/forge/scripts && python3 -c "import forge_workspace_capture as w; print([n for n in dir(w) if n.startswith(('update_','delete_','archive_'))])" lists the new methods; then a live round-trip: log a throwaway meal, edit its calories, read back, delete it (single command, prints before/after).
  • Commit: feat(ct-crud): capture-client update/delete for meals/journal/goals/crm/calendar

Task 3: Capture-client update/delete for habits + intake + REST content (tasks/node/block/knowledge)

  • Files: /home/justinwieb/forge/scripts/forge_workspace_capture.py
  • What: update_habit(def_id, brand, **fields) (op:update_habit), archive_habit(def_id, brand), clear_habit_log(def_id, date, brand); uncheck_intake(item_id, date, brand) (op:uncheck); update_task(task_id, **fields) (PATCH /api/tasks/{id}), delete_task(task_id, forever=False) (DELETE /api/tasks/{id}[?forever=1]); update_note/edit_block(block_id, content=...) (PATCH /api/block/{id}), delete_block(block_id); set_knowledge_section(note_id, section)/delete_knowledge(note_id). Delete/destructive helpers read-back-and-verify where a cheap read exists.
  • Verification: cd /home/justinwieb/forge/scripts && python3 -c "import forge_workspace_capture as w; print([n for n in dir(w) if n.startswith(('update_','delete_','archive_','uncheck_','clear_','set_','edit_'))])"; live: create a throwaway task, PATCH its title, DELETE it, confirm gone in list_tasks.
  • Commit: feat(ct-crud): capture-client update/delete for habits/intake/tasks/blocks/knowledge

Task 4: Brain tool_edit_* / tool_delete_* handler functions

  • Files: /home/justinwieb/forge/scripts/forge_telegram_inbox_brain.py
  • What: Add bot-facing wrapper fns (return formatted strings, fail-loud on {ok:False}) for each entity: tool_edit_food, tool_delete_food, tool_edit_task, tool_delete_task, tool_edit_habit, tool_archive_habit, tool_edit_goal, tool_archive_goal, tool_edit_journal, tool_delete_journal, tool_edit_contact, tool_delete_contact, tool_uncheck_intake, tool_edit_note, tool_delete_block, tool_edit_knowledge, tool_delete_knowledge, tool_edit_calendar_local, tool_delete_calendar_local. Each calls the Task 2/3 capture method. Delete fns echo the verified target summary ("Deleted: Green Curry, 300 cal").
  • Verification: cd /home/justinwieb/forge/scripts && python3 -c "import forge_telegram_inbox_brain as c; print([n for n in dir(c) if n.startswith('tool_edit_') or n.startswith('tool_delete_') or n.startswith('tool_archive_') or n.startswith('tool_uncheck_')])" lists all handlers.
  • Commit: feat(ct-crud): brain edit/delete tool handlers for all CreatorTrack entities

Task 5: Register the tools in the TOOLS catalog + TOOL_HANDLERS dispatch

  • Files: /home/justinwieb/forge/scripts/forge_telegram_inbox_brain.py (TOOLS L2233, TOOL_HANDLERS L2979)
  • What: Add one JSON schema entry per new tool to TOOLS (name, description that states "edit/delete an existing X by id; call the matching read tool first to get the id", input_schema with the id + editable fields). Add each to TOOL_HANDLERS as "edit_food": lambda p: tool_edit_food(**p) etc. Ensure they survive the RETIRED_TOOL_NAMES filter (L2920).
  • Verification: cd /home/justinwieb/forge/scripts && python3 -c "import forge_telegram_inbox_brain as c; names={t['name'] for t in c.TOOLS}; want={'edit_food','delete_food','edit_task','delete_task','edit_habit','archive_habit','edit_goal','archive_goal','edit_journal','delete_journal','edit_contact','delete_contact','uncheck_intake','edit_note','delete_block','edit_knowledge','delete_knowledge','edit_calendar_local','delete_calendar_local'}; missing=want-names; print('MISSING',missing); assert not missing; print('handlers ok', want<=set(c.TOOL_HANDLERS))" exits clean.
  • Commit: feat(ct-crud): register edit/delete tools in brain catalog + dispatch

Task 6: Expose tools to the coordinator + capture surfaces and add prompt guidance

  • Files: /home/justinwieb/forge/scripts/forge_telegram_brain.py (CAPTURE_TOOL_NAMES L47, COORDINATOR_HOT_TOOL_NAMES L99, prompt tables ~L210/L269/L364)
  • What: Add every new tool name to COORDINATOR_HOT_TOOL_NAMES (coordinator OS is the surface Justin used). Add the food/task/habit edits to CAPTURE_TOOL_NAMES too (capture bot should fix its own mislogs). Add prompt rows: the render-label map (e.g. | edit_food | \Food Item Updated:` |,| delete_food | `Food Item Deleted:` |) and a short "Correcting a prior log" guidance block: read first (food_today/list_tasks) to get the id, thenedit_/delete_` by id; never re-log a correction as a new entry.
  • Verification: cd /home/justinwieb/forge/scripts && python3 -c "import forge_telegram_brain as b; print('edit_food' in b.COORDINATOR_HOT_TOOL_NAMES, 'delete_food' in b.COORDINATOR_HOT_TOOL_NAMES, 'edit_food' in b.CAPTURE_TOOL_NAMES)" prints True True True; python3 -c "import forge_telegram_brain" imports clean (prompt strings valid).
  • Commit: feat(ct-crud): expose edit/delete tools to coordinator + capture, add prompt guidance

Task 7: End-to-end live verification against prod origin

  • Files: none (test only); scratchpad script at /tmp/claude-1000/-home-justinwieb-forge/.../scratchpad/ct_crud_e2e.py
  • What: Drive the full correction path the way the coordinator would: (1) log_food a throwaway "E2E test bite" with wrong macros, (2) food_today read the id, (3) edit_food to correct macros, (4) read back and assert changed, (5) delete_food, (6) assert gone. Repeat abbreviated for task (create/patch/delete) and habit (create/update/archive). Print PASS/FAIL per entity. This is the "test the path before claiming done" gate.
  • Verification: cd /home/justinwieb/forge/scripts && python3 /tmp/claude-1000/-home-justinwieb-forge/*/scratchpad/ct_crud_e2e.py prints PASS for food, task, habit; leaves no test rows behind.
  • Commit: (no commit; verification only, or commit the e2e script under scripts/ if kept)

Task 8: Register and document

  • Files: /home/justinwieb/.claude/projects/-home-justinwieb-forge/memory/MEMORY.md (index), /home/justinwieb/forge/memory/general/reference_creatortrack_bot_api.md (extend with the CRUD matrix + tool names), /home/justinwieb/forge/memory/daily/2026-07-04.md
  • What: Document the new edit/delete tool family and the server-contract matrix in reference_creatortrack_bot_api.md (append URL: line if new). Add/refresh the MEMORY.md index line. Note the create-only-gap-closed fact.
  • Verification: bash /home/justinwieb/forge/scripts/forge_eval_run.sh passes; grep -c edit_food /home/justinwieb/forge/memory/general/reference_creatortrack_bot_api.md >= 1.
  • Commit: docs(ct-crud): document bot CRUD parity tool family + server contract matrix