Skip to content

Flush pending auto-saves before publishing a card - #3039

Open
a-abdellatif98 wants to merge 1 commit into
basecamp:mainfrom
a-abdellatif98:fix/2778-flush-autosave-before-publish
Open

Flush pending auto-saves before publishing a card#3039
a-abdellatif98 wants to merge 1 commit into
basecamp:mainfrom
a-abdellatif98:fix/2778-flush-autosave-before-publish

Conversation

@a-abdellatif98

Copy link
Copy Markdown

Fixes #2778.

Creating a card with Cmd/Ctrl+Enter right after typing the title publishes it as "Untitled" on the board. Reloading shows the real title.

Root cause

Two things combine.

The title textarea has keydown.enter->auto-save#submit:prevent, but Stimulus keyboard filters require an exact modifier match:

keyFilterDissatisfied(e,t){const[s,r,n,i]=u.map((e=>t.includes(e)));return e.metaKey!==s||e.ctrlKey!==r||e.altKey!==n||e.shiftKey!==i}

So keydown.enter is skipped whenever meta or ctrl is held. No blur fires either, because focus stays in the textarea, and the 3s AUTOSAVE_INTERVAL timer has not elapsed yet.

Meanwhile clicker#click publishes immediately, and the publish button_to is a separate form that carries only creation_type. The title lives in #card_form and is never part of that request.

The title therefore only reaches the server from auto_save_controller#disconnect, which runs while Turbo is already swapping in the board page. The card is published with title == nil, Card#set_default_title writes "Untitled", and the board renders that. The late PATCH then renames the card, which also leaves debris: Card::Eventable#track_title_change records a card_title_changed event plus its system comment and webhook delivery.

Fix

clicker awaits the auto-save controller through an outlet before clicking publish.

auto_save_controller#submit now resolves once any in-flight save has landed, not only one it started itself. Previously it returned immediately when #dirty was false, so if the 3s timer had already fired and a PATCH was in flight, an external "flush now" caller would not actually wait. Storing the promise in #saving unconditionally means submit() has one code path whether it just started a save, one was already running, or nothing ever ran, since await undefined resolves immediately.

Relationship to #2812 and #2895

Same outlet wiring, which is the only place the two async handlers can be sequenced. Two deliberate differences:

  • Flush auto-save before publishing card on Cmd (Ctrl) + Enter #2812 resets its saved promise in a finally. That is wrong when two saves overlap: the first save's finally nulls out the second save's promise, so a subsequent submit() returns without waiting. Storing #saving unconditionally with no reset avoids that.
  • Fix race between auto-save and publish on Cmd+Enter #2895's test aliases CardsController#update to insert a sleep 0.5. That is not needed. On main the PATCH is only sent from disconnect(), which by construction runs after the publish request, so the failure is already deterministic. Dropping the alias also avoids leaking a patched controller if teardown is skipped.

Testing

New system test, written first and confirmed failing on unmodified code:

FAIL CardCreationTest#test_creating_a_card_with_the_keyboard_shortcut_publishes_the_title_typed_last
  expected to find css "h3" but there were no matches

The failure screenshot showed the board card titled "Untitled", confirming the mechanism rather than a selector problem. The test also asserts no card_title_changed event is left behind, which pins the phantom-event half of the bug.

After the fix:

bin/rails test test/system/card_creation_test.rb   1 test, 5 assertions, 0 failures
PARALLEL_WORKERS=1 bin/rails test:system           17 tests, 72 assertions, 0 failures
bin/rails test                                     1549 tests, 5849 assertions, 0 failures, 1 skip

Known gaps

  • Clicking "Create card" with the pointer still races. Blur starts the PATCH but does not block the click, so the same lost update is possible through a narrower window. Closing that properly means intercepting the publish form submission or sending the title with the publish request, which seemed beyond this issue.
  • Overlapping saves still have no ordering guarantee. Serializing them looked like scope creep here.
  • Only ctrl+enter is exercised. The meta+enter and ctrl/meta+shift+enter variants share identical wiring but have no coverage.
  • The Hotwire Native bridge submit path was not tested, and only the SQLite/OSS half of the suite was run.

Cmd/Ctrl+Enter went straight to the publish button: Stimulus keyboard
filters only match when no modifier is held, so the title textarea's
keydown.enter->auto-save#submit action never fired, and the title only
reached the server when the form controller disconnected during the
navigation that follows publishing. The board then rendered the card as
"Untitled", and the late PATCH recorded a phantom card_title_changed
event plus its system comment.

The clicker now awaits the auto-save controller through an outlet, and
auto-save#submit resolves once any in-flight save has landed, not just
the one it starts itself.
Copilot AI balanced review requested due to automatic review settings August 16, 2026 08:44

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a regression test and updates the keyboard-shortcut publish flow to ensure the last typed card title is persisted before publishing.

Changes:

  • Added a system test validating Ctrl+Enter publishing uses the latest typed title and does not emit a title-changed event.
  • Wired the publish buttons’ keyboard shortcut handler to trigger the card form auto-save before clicking publish.
  • Updated the auto-save controller to expose an awaitable “in-flight save” via #saving.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
test/system/card_creation_test.rb New system test covering shortcut-based publish behavior and event leakage regression
app/views/cards/container/footer/_create.html.erb Connects keyboard shortcut click handler to the card form auto-save outlet
app/javascript/controllers/clicker_controller.js Awaits auto-save submission (if present) before clicking the target button
app/javascript/controllers/auto_save_controller.js Tracks in-flight save promise to allow callers to await ongoing saves

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 18 to 24
async submit() {
if (this.#dirty) {
await this.#save()
this.#save()
}

await this.#saving
}
#save() {
this.#resetTimer()
await submitForm(this.element)
this.#saving = submitForm(this.element)
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.

Incomplete card title when a new card is saved with Cmd+Enter keyboard shortcut

2 participants