Skip to content

Core Data: fetch one author-scoped autosave in getAutosave - #30

Closed
whyisjake wants to merge 5 commits into
adamsilverstein:fix/autosave-loadingfrom
whyisjake:fix/62057-autosave-resolution
Closed

Core Data: fetch one author-scoped autosave in getAutosave#30
whyisjake wants to merge 5 commits into
adamsilverstein:fix/autosave-loadingfrom
whyisjake:fix/62057-autosave-resolution

Conversation

@whyisjake

@whyisjake whyisjake commented Jul 28, 2026

Copy link
Copy Markdown

What

Targets adamsilverstein:fix/autosave-loading rather than trunk, so it stacks on WordPress#71367.

Reworks getAutosave to fetch its own author-scoped record instead of narrowing getAutosaves, which is closer to what @Mamaduka suggested — with one wrinkle that turns out to be load-bearing.

Trac ticket: https://core.trac.wordpress.org/ticket/62057

Why the current approach breaks autosaving

getAutosave currently delegates with a third argument:

await resolveSelect.getAutosaves( postType, postId, 1 );

That records resolution under the key [ postType, postId, 1 ]. But hasFetchedAutosaves looks up a two-argument key:

select( STORE_NAME ).hasFinishedResolution( 'getAutosaves', [ postType, postId ] )

EquivalentKeyMap never matches the two, so hasFetchedAutosaves stays false forever. isEditedPostAutosaveable in @wordpress/editor returns false while that is false, so autosaving never enables. There is no error and nothing in the current test suite catches it — the failure is silent.

packages/core-data/src/test/autosaves.js in this PR reproduces it against a real registry. Against the current branch those tests fail with hasFetchedAutosaves returning false; with this change they pass.

The multi-author question

@Mamaduka also asked what happens when the current author did not make the latest autosave. It is a real problem, and it is the reason per_page=1 alone is not enough.

Every consumer of getAutosave passes the current user's ID, and the selector filters on it:

autosaves?.find( ( autosave ) => autosave.author === authorId )

So "the most recent autosave by anyone" is the wrong record whenever a post has more than one editor — the selector filters it out and returns undefined. Scoping the request by author fixes this, and it needs author support on the endpoint, which is being added in the companion core patch on the Trac ticket. WordPress stores exactly one autosave per author, so per_page=1 combined with author is well defined.

Changes

  • getAutosave fetches ?context=edit&per_page=1&author=<id> directly.
  • The getAutosaves resolver is restored to its original behavior, so its fetch is unchanged for anyone who reaches it first. See the trade-off below for the case where getAutosave runs first — that one is a real behavior change.
  • getAutosave dispatches finishResolution( 'getAutosaves', [ postType, postId ] ) from a finally, so every exit path reports completion and hasFetchedAutosaves continues to work.
  • Reverts the two test assertions expecting per_page=1 from a two-argument getAutosaves call. Those currently fail on this branch — the revised resolver only appends per_page when the third argument is passed, so a two-argument call produces the original path.

Trade-off worth reviewing

Reporting completion against the getAutosaves key also suppresses that resolver. fulfillSelector bails when hasStartedResolution is true, and that is simply "resolution state exists" — which this dispatch creates. So once getAutosave has run for a post, a later direct call to the getAutosaves selector returns the single-author array this resolver stored and never refetches the full collection.

In this repo nothing reads state.autosaves except getAutosave and hasFetchedAutosaves, so there is no in-tree impact. It is a genuine behavior change for third parties calling getAutosaves after the editor has mounted, and it is the part of this PR I would most like a second opinion on.

The alternative is changing hasFetchedAutosaves to observe getAutosave instead, which means giving it an implicit current-user dependency it does not have today. That seemed worse, but I do not feel strongly.

A related consequence: receiveAutosaves replaces rather than merges state.autosaves[ postId ], so a single-author response overwrites a previously fetched full collection. Same reasoning — no in-tree consumer today, worth knowing.

Why finally

The completion dispatch is in a finally rather than on the success path. The delegation it replaces completed getAutosaves resolution unconditionally, and hasFinishedResolution treats an errored resolution as complete, so a failed request still counted as fetched. Reporting only on success would leave isEditedPostAutosaveable false for the rest of the session after one network failure or early return — the same class of silent breakage this PR is fixing. packages/core-data/src/test/autosaves.js covers the failed-request and unknown-author paths.

Ordering constraint

The author parameter does not exist on the endpoint yet. Until the core patch lands, this request returns the same thing it does today — WordPress ignores unknown query parameters — so nothing breaks, but the author scoping is inert.

Separately, the core-side preload path must not be narrowed until this change syncs into core. The preload cache is keyed on the normalized path, so a core preload of ?context=edit&per_page=1&author=N against a client requesting ?context=edit misses on every editor load: the preload is computed and discarded and the request goes to the network anyway. The core patch is split in two on the Trac ticket for this reason.

Testing instructions

npm run test:unit packages/core-data/src/test/autosaves.js
npm run test:unit packages/core-data/src/test/resolvers.js

packages/core-data/src/test/autosaves.js is the meaningful one — it drives a real registry rather than a mocked dispatch, so it exercises finishResolutionhasFinishedResolutionhasFetchedAutosaves end to end.

To see the regression this fixes, revert getAutosave to the version on this branch and re-run autosaves.js: the first two tests fail with hasFetchedAutosaves returning false.

Disclosure

This change was developed with AI assistance (Claude Code), per the WordPress AI guidelines. The commit carries a Co-Authored-By trailer for the same reason.

I have reviewed every line and can speak to the approach and its trade-offs — in particular the finishResolution call, which is the one decision here I would most like a second opinion on. The unit and integration tests above were run locally against this branch, along with the wider packages/core-data and packages/editor/src/store suites (585 tests, all passing) and ESLint.

adamsilverstein and others added 4 commits August 26, 2025 17:16
Delegating to getAutosaves with a third argument records resolution under
[ postType, postId, 1 ], while hasFetchedAutosaves looks up [ postType, postId ].
The keys never match, so hasFetchedAutosaves stays false and
isEditedPostAutosaveable never enables autosaving. The failure is silent.

Have getAutosave fetch its own record instead, scoped to the author whose
autosave the selector actually returns, and mark getAutosaves resolution
finished so hasFetchedAutosaves keeps working. getAutosaves itself is left
unchanged, so calling it directly still returns the full collection.

Adds packages/core-data/src/test/autosaves.js, which drives a real registry
rather than a mocked dispatch so the resolution path is exercised end to end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a198cf8-c652-43b7-8fc2-13857becabff

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: whyisjake <whyisjake@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

The completion dispatch sat on the success path only, so an early return
(unknown author, post type without autosave support) or a rejected request
skipped it. hasFetchedAutosaves then stayed false and
isEditedPostAutosaveable never enabled autosaving for the session.

This was a regression against the delegation it replaced: that completed
getAutosaves resolution unconditionally, and hasFinishedResolution treats an
errored resolution as complete, so even a failed fetch counted as fetched.

Move the dispatch into a finally, and correct the docblock, which claimed
getAutosaves was unaffected. Reporting against its key also suppresses that
resolver via hasStartedResolution, so a later direct call returns the stored
single-author array rather than refetching.

Two existing tests asserted the dispatch was NOT made on the early-return
paths, encoding the bug as intended behavior. They now assert the opposite,
and cover the failed-request path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@adamsilverstein

Copy link
Copy Markdown
Owner

Merged these changes into WordPress#71367 - work continues there.

@adamsilverstein

Copy link
Copy Markdown
Owner

Thanks @whyisjake - i merged these changes into the other PR.

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.

2 participants