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_dayL523,intake_dayL544,habits_dayL592 already carry raw entries; confirm id passthrough),/home/justinwieb/forge/scripts/forge_telegram_inbox_brain.py(tool_food_todayL928,tool_food_summaryL1033,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_daypassesentriesthrough raw so the id is already present in data; the fix is in the bot render fns that currently omit it. Addidto 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 viafood_day, verify id present, thenop:"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 resolveworkspaceIdvia_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 inlist_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(TOOLSL2233,TOOL_HANDLERSL2979) - 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 toTOOL_HANDLERSas"edit_food": lambda p: tool_edit_food(**p)etc. Ensure they survive theRETIRED_TOOL_NAMESfilter (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_NAMESL47,COORDINATOR_HOT_TOOL_NAMESL99, 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 toCAPTURE_TOOL_NAMEStoo (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)"printsTrue 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_fooda throwaway "E2E test bite" with wrong macros, (2)food_todayread the id, (3)edit_foodto 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.pyprintsPASSfor 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(appendURL: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.shpasses;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