Skip to content

[codex] Add Cmd+Enter steer sends - #9

Merged
SawyerHood merged 2 commits into
mainfrom
bb/cmd-enter-steer-prompt-send-thr_xpufja7wgb
May 14, 2026
Merged

SawyerHood merged 2 commits into
mainfrom
bb/cmd-enter-steer-prompt-send-thr_xpufja7wgb

Conversation

@SawyerHood

@SawyerHood SawyerHood commented May 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a Meta/Cmd+Enter submit path for follow-up prompt boxes that dispatches steer sends without changing normal Enter behavior.
  • Sends queued follow-up drafts through the draft-send lifecycle with explicit mode: "steer", preserving claim/delete handling and queue order.
  • Makes draft-send mode required in the server contract so callers must explicitly choose "auto" or "steer".
  • Documents queued steer batch partial-failure behavior: queued drafts are sent sequentially, so any earlier accepted draft sends remain sent/deleted if a later queued draft or current draft send fails; the current draft is restored when it was part of the failed batch.
  • Covers direct draft steers, queued-only steers, queued-plus-current draft steers, repeated Cmd+Enter protection, and the server draft-send contract.

Validation

  • pnpm exec turbo run test --filter=@bb/server-contract -- contract.test.ts
  • NODE_OPTIONS=--no-experimental-webstorage pnpm exec turbo run test --filter=@bb/app --force
  • pnpm exec turbo run test --filter=@bb/server
  • pnpm exec turbo run typecheck --filter=@bb/server-contract --filter=@bb/server --filter=@bb/app

@SawyerHood
SawyerHood marked this pull request as ready for review May 14, 2026 23:23
@SawyerHood
SawyerHood merged commit 6de9bd4 into main May 14, 2026
1 check failed
ymichael added a commit that referenced this pull request Oct 30, 2026
## Human comments

## What was wrong

Several cache owners wrote a server reply or the thread-detail bootstrap
straight into the React Query cache with `setQueryData` while a fetch of
the same key could still be in flight. React Query writes fetch results
without checking whether they're older than what's cached (query-core
`query.ts`), so the older fetch landed last and put stale data back.
Effects included: an environment or host status that stayed stale after
opening a thread, a plugin settings form resetting its draft to the old
value, a cancelled plugin install flipping back to running, new plugin
update jobs dropping out of the list, an `idle` app update phase
switching off the progress poll, and the AI service selection flickering
back. These are items #2–#10 from the direct cache write audit
(@thread:thr_fjr5x8dzaz). #1 (thread tabs) is fixed separately in #6611.

## What changed

Two fixes, depending on whether the data has a version field:

- **Environment and Host keep the newer copy by `updatedAt`** (#2, #3,
#10). The bootstrap ingest (`thread-detail-cache-owner.ts`), the rename
reply (`applyEnvironmentUpdateResult`), and the
`useEnvironment`/`useHosts` fetches all compare `updatedAt`. The
fetch-side check is what catches a fetch that started before the write
and lands after it. To make it testable, those fetches now live in
`environmentQueryOptions` and `hostsQueryOptions`. Host `status` comes
from live daemon sessions and a disconnect doesn't change `updatedAt`,
so on a tie the cached copy wins over the bootstrap. The bootstrap only
fills an empty cache or replaces strictly older data. Rename replies and
fetch results still win ties.
- **`setQueryDataOverInFlightFetch`** (`cache-effect-utils.ts`) for data
with no version (#4–#9): it writes the value, and if a fetch of the key
is in flight it cancels that fetch (the cache keeps the written value)
and refetches. With nothing in flight it's a plain write. Used by
`applyPluginSettingsView`, `applyPluginInstallJob`,
`applyPluginUpdateJob`, `hydrateAppUpdateStatus`,
`hydrateSystemVersionCache` and `writeCachedAiServices`, so every caller
gets the fix. Where the audit suggested an unconditional
cancel-write-invalidate (#7) or a cancel before the write (#8, #9), this
helper refetches only when a fetch was actually in flight. That avoids
an extra request on every write. Forced version and app-update checks
update the server's cache, so the trailing unforced refetch returns the
forced result.

No wire, CLI or plugin API changes.

Not covered here: audit items #11 (plugin safe mode) and #12 (prompt
history), both low risk. Also not covered: a lint rule restricting
direct `setQueryData` calls to the helper, which would stop new call
sites from bringing the race back.

## How you verified

- New tests use real `QueryClient`/`QueryObserver` instances: start a
fetch that resolves late with older data, do the write, let the fetch
land, then check that the newer value survives (and, for the helper,
that the trailing refetch runs).
- `thread-detail-cache-owner.test.ts` (new): late environment and
host-list fetches after a bootstrap, an older bootstrap over a newer
cache, and the equal-`updatedAt` host status case.
- `environment-workspace-cache-owner.test.ts`: a late environment fetch
after a rename, and an older rename reply over a newer cache.
- `in-flight-fetch-writes.test.ts` (new): one row per cache-owner write
for #4–#9, plus helper tests for the no-fetch case (no extra request)
and a fetch of an unobserved query.
- With each source change reverted, every race test failed on the stale
data and passed once the fix was restored.
- `pnpm exec turbo run typecheck lint test --filter=@bb/app`: passed
(647 files, 6515 tests; 0 lint errors, no new warnings). After rebasing
onto `0472409243` I re-ran the three changed test files (21 tests
passed).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

> AGENT GENERATED

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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