Skip to content

File a thread under Planning when a message asks for a plan - #4

Merged
matthewdias merged 2 commits into
mainfrom
plan-mode-filing
Sep 13, 2026
Merged

matthewdias merged 2 commits into
mainfrom
plan-mode-filing

Conversation

@matthewdias

Copy link
Copy Markdown
Owner

Threads started with /plan sat in Inbox for their whole planning session. Filing is the agent's job, and in plan mode it can't do it: bb stages set is a mutating call, and plan mode doesn't allow those.

What changed

  • server.ts: a message.dispatch hook files the thread under planStageId when a send opens with the provider's plan command mention. It always returns proceed, decides synchronously and does the move after the hook returns, because the hook is fail-closed and holds a server-wide lock. Plan command names come from providers.list() on a background refresh, falling back to "plan".
  • stages.ts: new planStageId config key, default "planning". No migration needed, since an absent key falls through to the default.
  • app.tsx, README, PLUGIN_OVERVIEW, SKILL.md: the new trigger, and why it's the only one that ships on.
  • Separate commit: the lockfile now records thread-badges at its released 0.2.0.

Verification

  • npm run check passes.
  • Ran the predicate over 400 recorded prompts: 4/4 plan sends caught, 0 false positives. Mutations: a wrong command name or source: "skill" drops it to 0/4. A plain text match on /plan gives 43 false positives.
  • The start === 0 guard never changed an outcome in that corpus, so real data doesn't prove it.
  • Linked locally and the plugin loads and runs. A live /plan filing a thread has not been tested yet.

Not in this PR

  • Nothing moves a thread out of Planning when the plan is approved.
  • If "when a turn starts" is turned on, its later move overrides this one.

🤖 Generated with Claude Code

matthewdias and others added 2 commits September 13, 2026 14:33
The v0.2.0 release bumped its package.json without regenerating the
lockfile, so every install rewrote the workspace entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Filing is the agent's job: it reads the stage table and runs
`bb stages set`. Plan mode is the one moment it cannot, because that is a
mutating shell call, so a thread started with /plan sat in Inbox through
its whole planning session and was only filed once the plan was approved,
by which point the honest stage is Building.

No event or thread field says a turn is a plan. The message does: bb's
plan action is a provider-declared slash command, carried on the send as
a structured command mention. So a message.dispatch hook watches for that
mention opening the message and files the thread under planStageId. It
always proceeds, decides synchronously and moves off the pass, because the
hook is fail-closed and holds a server-wide lock. Command names come from
each provider's composer actions on a background refresh, falling back to
"plan".

Matching the mention rather than the text matters: over 400 recorded
prompts it caught all four plan sends with no false positives, where a
text match on "/plan" flagged 43 messages that only discussed it.

This trigger ships on, unlike the other three. They guess a stage from a
thread's runtime; asking for a plan is the user saying what comes next.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@matthewdias
matthewdias merged commit ef7c2b1 into main Sep 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant