Skip to content

feat: add manually dispatched drafts - #3575

Merged
ymichael merged 15 commits into
mainfrom
bb/prototype-adding-follow-up-drafts-thr_934d2si9vc
Sep 14, 2026
Merged

ymichael merged 15 commits into
mainfrom
bb/prototype-adding-follow-up-drafts-thr_934d2si9vc

Conversation

@ymichael

@ymichael ymichael commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Human comments

What was wrong

There was no way to save a composed message into the normal queue without either sending it or pretending it was scheduled far in the future. That produced misleading schedule text and could eventually auto-send.

What changed

Adds the built-in Drafts plugin with a one-click “Save draft…” action in both new-thread and follow-up composers. The action has no modal. It submits through the composer with plugin-owned JSON, and the Drafts dispatch hook turns that submission into the existing plugin queue wait.

Plugins now run before core scheduling, busy-thread, provisioning, host, and interaction waits. experimental_submit({ experimental_data }) is generic: core validates and passes the JSON to dispatch hooks for the initial attempt but does not interpret or persist it. Later Drafts rechecks recognize the durable existing waitingOn: { kind: "plugin", pluginId: "drafts" } queue state. Send now keeps using the existing hook bypass.

There is no queued-message schema change, new draft queue type, or Drafts-specific core branch. Send Later continues to use sendAt unchanged.

How you verified

  • Live browser smoke tests for new-thread and established follow-up drafts
  • Queue UI and source CLI showed Held by Drafts · Draft with no scheduled time
  • New rows stored the normal plugin wait and no plugin submission payload
  • Production worktree build, affected typechecks, 45 dispatch/queue/retry tests, 520 DB tests, 4,610 app tests, and Drafts/SDK/domain/server-contract tests

Fixes #

AGENT GENERATED

…ollow-up-drafts-thr_934d2si9vc

# Conflicts:
#	apps/app/src/views/thread-detail/ThreadDetailPromptArea.tsx
#	packages/db/drizzle/meta/0119_snapshot.json
#	packages/db/drizzle/meta/_journal.json
#	packages/domain/src/plugin-sdk-version.ts
#	packages/plugin-sdk/package.json
…ollow-up-drafts-thr_934d2si9vc

# Conflicts:
#	apps/app/src/views/thread-detail/ThreadDetailPromptArea.tsx
#	packages/db/src/data/queued-thread-messages.ts
#	packages/domain/src/plugin-sdk-version.ts
#	packages/plugin-sdk/package.json
…ollow-up-drafts-thr_934d2si9vc

# Conflicts:
#	packages/domain/src/plugin-sdk-version.ts
#	packages/plugin-sdk/package.json
…ollow-up-drafts-thr_934d2si9vc

# Conflicts:
#	docs/api_to_audit.md
@ymichael
ymichael merged commit 25baf41 into main Sep 14, 2026
16 checks passed
@ymichael
ymichael deleted the bb/prototype-adding-follow-up-drafts-thr_934d2si9vc branch September 14, 2026 22:41
ymichael added a commit that referenced this pull request Sep 15, 2026
## Human comments

## Before / After

Both captures use the supported product journey **Fork into new thread →
enter the shown prompt → Save draft… → Save draft** in the real source
Chromium app. The QA threads were validated as M4 / Codex / gpt-5.6-sol
/ high / default / full. Assets are pinned to the separate evidence-only
root commit
[`722baf85893192feef86a5fccb94e3d8de74ad03`](https://github.com/get-bb/bb/tree/722baf85893192feef86a5fccb94e3d8de74ad03);
PR head contains no screenshot commit.

| Before — broken baseline | After — repaired |
| --- | --- |
| **Provenance:** baseline endpoint
`054de3ae9c4d9551c845ada6549da5490af78988`, captured before source
edits.<br><br>**Shown prompt:** “Fork draft before fix 054de3a — must
remain queued and must not run.”<br><br>**Observed:** the fork is active
(`Working…`), the prompt was accepted, provider output and command-tool
work began, and the queue was empty.<br><br>![Before: Save draft
incorrectly starts the provider
turn](https://github.com/ghraw/get-bb/bb/722baf85893192feef86a5fccb94e3d8de74ad03/before-fork-save-draft-result.jpg)
| **Provenance:** repaired commit
`065b5bf3ed3799efe2c2531a031878a6e78cdd74`, same source-app
journey.<br><br>**Shown queued draft:** “Fork draft after fix 054de3a —
must remain queued and must not run.”<br><br>**Observed:** the fork is
inactive, with exactly one queued row held by **Drafts · Draft**.
CLI/server evidence confirmed the inherited timeline remained at seq 5
with no matching requested or accepted provider turn and no new tool
work.<br><br>![After: Save draft leaves one exact queued draft without
starting a
turn](https://github.com/ghraw/get-bb/bb/722baf85893192feef86a5fccb94e3d8de74ad03/after-fork-save-draft-queued.jpg)
|

## What was wrong

The fork flow introduced in
[#3575](#3575) rebuilt the new-thread
submission through `buildForkThreadRequest` but omitted the composer's
`pluginSubmission`. That stripped the Drafts plugin marker before the
initial dispatch hook could see it, so **Save draft** displayed success
while core treated the fork prompt as an ordinary message and
immediately started a provider turn. Root and follow-up drafts were
unaffected because those paths forwarded the submission unchanged.

## What changed

`buildForkThreadRequest` now requires callers to account for
`pluginSubmission` and preserves it when present. `RootComposeView`
forwards the composer's value into the shared builder. The opaque plugin
data still exists only during dispatch-hook processing and is not
persisted in the accepted core message. No server, daemon protocol, CLI,
SDK, or documentation contract changed.

Added focused coverage for plugin submission on fork dispatch, an
ordinary-fork control with no plugin submission, and controls proving
root/new-thread and follow-up composers still forward plugin
submissions.

## How you verified

- Red/green fork assembler regression: 6/6 passed after the fix.
- Root/new-thread and follow-up composer suites: 81/81 passed.
- Drafts plugin tests: 6/6, plus typecheck.
- Server dispatch-hook suite: 15/15 passed.
- Turbo typechecks for `@bb/client-core` and `@bb/app` passed.
- Real source Chromium journey: **Fork into new thread → Save draft**
now creates an inactive fork with one exact queued draft and no accepted
turn/tool work; **Send now** dispatches exactly once.
- Source CLI smoke on the production worktree build: ordinary fork
produced one request/acceptance, no queue or tools, and passed the
M4/Codex/gpt-5.6-sol/high/default/full validator.

> AGENT GENERATED
albrand added a commit to albrand/bb that referenced this pull request Sep 17, 2026
…e merge

- parity goldens: only the lines the merged bridges now emit deterministically
  (Claude estimated contextWindow deltas, restored cacheReadInputTokens, Codex
  coordination wait label); session-id churn from the replay was rejected.
  Claude pinned event counts follow the added deltas.
- contract: the optional-field group limit is 32, covering upstream
  lifecycleOwnerThreadId and the fork send refusal group.
- app: a plugin wait has no glyph, as upstream get-bb#3575 decided.
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