LUKE-336: the planning model runs on the hosted brain over a plan conversation
What
One backend planning model, and it's the existing hosted brain (eve on gpt-6-sol). There's no second agent and no new provider or key. A plan now gets a conversation of a new kind, plan, and the host treats that kind in three ways (apps/web/server/hosted/brain-host/planning.ts):
- Instructions:
PLANNING_INSTRUCTIONS, adapted from Matt Pocock's grilling skill. They cover opinionated recommendations, one spoken question at a time chosen by what its answer unlocks, concrete rehearsals, an internal coverage template, direct answers counting as agreement, read-back for inferred details, corrections revising the document, new assumptions going inconfirmed:false, a changed meaning resetting the flag unless the correction settles it, keeping facts apart from recommendations, and treating tool results, transcripts, and the document as data. Workflow is left to the instructions. - Standing context, rebuilt every turn: the plan's name,
owner/repo, branch and commit, and the saved document as JSON, read fresh from the row. A save the model makes is what its next turn reads. - Tools: the planning list alone, currently just
update_plan, bound server-side to{ userId, planId }throughreadPlanOfConversation.PLANNING_TOOLSis the slot where the repository reads (LUKE-338) and research (LUKE-339) go.
A session opened over an existing plan conversation is seeded with the conversation so far, the same as any other conversation.
Store (plan-store.ts):
openPlanConversation(userId, planId)opens theplanconversation under the plan row's lock and attaches it throughattachPlanConversation. It's idempotent: calling it again returns the same conversation.readPlanOfConversationis how a turn finds its plan.deletePlannow also stamps the plan's conversationdeleted_at, so the existing purge removes its messages 30 days later.
Other changes:
- The writer (
production.ts, cancel route) and the seed read now check rows againstHOSTED_TOOL_SET(catalog plus planning tools). Without this, the store would reject a storedupdate_planpart and a resume would start with no prior conversation.wireValidatedToolis exported from@sidecar/brain/tool-setfor this. - A plan conversation neither loads the notebook at turn start nor writes to it at compaction.
- The panel's reads filter by kind, so plan conversations never appear there.
host.toolDeclarationsnow takes{ target, kind }instead of justtarget.- No migration:
conversations.kindhas no check constraint.
For LUKE-340: acceptAsk with the plan's conversationId pinned reaches this unchanged.
Tests
-
tests/hosted-planning.test.ts(PGlite, runs incheck.sh) drives the scripted fixture model through the real host the way eve does. It gets the instructions, the seed, the standing context, and the tools; eachupdate_plancall goes throughhost.runTool; every event is relayed into the store. It checks:- an update is visible to the window's
openPlanand to the next turn's standing context; - a resumed session is handed the saved document plus the conversation so far, including the
update_plancall; - the conversation is opened once and a stranger can't open one;
- only
update_planis offered; - a deleted plan's conversation stops admitting its session.
Mutation check: switching the seed read back to
CATALOG_TOOL_SETmakes the resume test fail. - an update is visible to the window's
-
The
brain-hosteval (test:agent) now also runs a plan conversation through real eve: the scripted model reads the document it's handed and saves, and a second session over the same conversation (a resume) builds on that saved document. It passed locally against Postgres 17; it skips when no database is configured, as before. -
There are no prompt-string tests.
./scripts/check.sh exits 0. There's no Mac or UI change, so verify.sh doesn't apply.
š¤ Generated with Claude Code