Repository navigation
Stage a Builder publish or unpublish from the entry's status - #8168
Conversation
A save that should leave the live page alone now picks its request from the publication state Builder reports right before the write: a draft entry gets a plain save that Builder's reads return, and a published entry gets an autosave. Builder keeps autosaves out of its reads, so Content records the live value each saved field replaced and keeps its saved value through refreshes and body reads while Builder still reports that value. The pre-write check is now field-level for saves: when Builder's lastUpdated moved, a save blocks only if a field it writes changed, or the body changed or cannot be compared. Publish and unpublish stay strict.
…reshes and recovery
…ep the went-live note on recovery
…lder's own entry name
…he row was written with
…y an unedited page when Builder's body is empty
…tes, and block live updates whose publication state changed
…ed body over one the live check never read
A read-only "Builder status" select property (Published, Draft, Unpublished changes) now sits first in Builder collections, in the table, the board, and the page info sidebar. Attach, refresh, body reads, and saves keep it current, and rows leaving the source lose it. It's never mapped to a Builder field. After a save, the row's stored publication state follows the write. The save dialog's row labels use the mockup's wording and are translated, and the agent-facing Stage only description no longer says it never writes to Builder.
…waps A refresh now stores its rows and their statuses in one transaction, so a save can no longer land between them and leave the status stale. The status property is added when a Builder source is attached, even to an empty model. A save whose reply leaves out the publication state keeps the stored one; only an autosave falls back to the live check. Copies of a row or page don't carry its Builder status, and a row keeps a status only while some Builder source in the collection still has it, so replacing the Builder source with a local one clears it. Setup discovery reports the status as a system definition agents can't edit, and the French save labels say "entrée publiée".
The save dialog's footer counted autosaves and draft saves as "draft saves" and its button said "Push N updates", while the rows say "Save". The footer now says "2 saves", the button says "Save 2 to Builder" when every selected row is a save, and the Stage only description says "Saves changes to Builder — never publishes." A save to a published entry isn't a draft. The footer and button labels move into the catalog, translated in all 10 locales.
Picking Published or Draft in a Builder row's status stages a publish or unpublish that the source review preselects and sends. A staged change survives refreshes until Builder reaches it, and clears on picking the current status, turning publication changes off, or any executed publish or unpublish. A refresh now builds on the rows it holds locked, so a pick or save landing during its read isn't lost. Disconnecting the last Builder source removes the status property and its view references.
A publish or unpublish with no field or body change hashes the same every time, so the second one for an entry reused the applied first one's id in the source listing, and a third failed prepare on a duplicate key. The listing and prepare now pick the first revision id no stored change set holds.
|
Here's a visual recap of what changed: Open the full interactive recap |
…y on publish A push now settles only the staged publish or unpublish it sent, so one picked while it was in flight survives. A publish is blocked when Builder's live check reports editor autosaves Content didn't make, as the status picker already is. Publishes and unpublishes send the entry's name like other writes, so a title change in the same push renames the Builder entry.
…row lock, and clear a pick from Unpublished changes
|
Recap of the review-summary items that weren't posted as threads:
|
…ts on these lines
…-status-editing # Conflicts: # templates/content/actions/delete-document-property.ts # templates/content/app/components/editor/DocumentProperties.tsx # templates/content/parity/matrix.md
…ts on these lines
…y's missing update time
# Conflicts: # templates/content/actions/duplicate-page.ts
…s' into alice/content-builder-status-editing # Conflicts: # templates/content/parity/matrix.md # templates/content/shared/database-table-columns.test.ts
…-status-editing # Conflicts: # templates/content/actions/_builder-cms-unpublished-changes.test.ts # templates/content/actions/_builder-cms-unpublished-changes.ts # templates/content/actions/_builder-cms-write-adapter.ts # templates/content/actions/_builder-status-property.ts # templates/content/actions/_database-source-utils.ts # templates/content/actions/disconnect-content-database-source.ts # templates/content/actions/execute-builder-source-execution.test.ts # templates/content/actions/execute-builder-source-execution.ts # templates/content/docs/product/capabilities/content.source.adapters.md
|
We've been automatically notified and are looking into it. Push a new commit to re-trigger the review, or contact support@builder.io if this keeps happening. Error ID: |
…ore a publish is sent
|
On the three low items in the latest review summary:
|
There was a problem hiding this comment.
Builder reviewed your changes and found 1 potential issue 🟡
Review Details
Incremental review summary
This update tightens the status-pick action by checking page permissions inside the transaction and re-reading the source row under a row lock. It also adds a second page-edit check immediately before Builder dispatch, with a regression test for access revoked during live preflight; that prior comment is resolved. The staging/review/write architecture remains sensible, but refresh and disconnect are not serialized consistently. I classify this as high risk because the feature can mutate external publication state and relies on access checks and durable intent state.
New findings
- 🟡 MEDIUM: A refresh can replace source rows after disconnect removes the source, leaving rows associated with a deleted source.
- LOW: Status-pick and disconnect locking can deadlock because they acquire source and row locks in opposite order.
- LOW: The status-property ensure path can re-create the property after the last Builder source is disconnected.
- LOW: Disabling transitions commits before clearing intents; a clear failure can let old picks reappear after re-enabling.
The concurrent row-removal finding is fixed by the locked re-read and 409 path, and was resolved. Other previously reported issues were not reposted. 🧪 Browser testing: Skipped — dev server unavailable on 5f7a7fb4df5704440d00ba929782a50b25c1c189; setup retry failed building @agent-native/toolkit. Update the setup command in project settings before a later visual-verification attempt.
|
Builder's reviews also raised these in their summaries, without a thread:
|

In a Builder row's Info sidebar, the read-only Builder status property becomes a picker when the source publishes updates and allows publication changes. Picking Published or Draft stages a publish or unpublish for that entry. Nothing reaches Builder until the source review pushes it; an unpublish still needs its confirmation.
What changes
set-builder-entry-statusrecords the picked transition on the row and shows the status it will have. A pick needs edit access to the page as well as the collection, since a page can be shared more narrowly than its collection; the picker is offered only with both. Both are checked again once the pick holds the row, since a refresh can hold it a while. It survives refreshes until Builder reports that status. It clears when someone picks the entry's current Builder status (Unpublished changes included), turns publication changes off, or pushes that publish or unpublish. A push that sends the other one clears the pick it started with too, since its review chose over it. A pick made while a push is in flight survives it, and so does one a push that sends neither leaves out. Published can't be picked while Builder reports changes saved in its editor, even alongside Content's own unpublished save, because Content can't see them; a tooltip says to publish from Builder. The push checks Builder again and is blocked the same way if such changes appeared after the pick. A pick re-reads the source setting under the row lock, so turning publication changes off can't leave a hidden pick behind. The clear that turning them off starts re-reads the setting the same way, so if they're back on by then, a new pick stays. A push that only publishes or unpublishes is blocked once its pick is cleared or changed, even after it was approved.metadata_update). A staged change is reviewed against Builder's live values and body, so a publish also sends Content's saved but unpublished values. The review dialog preselects the staged transition. A toggle the reviewer turns off stays off, a different pick preselects the row again, and clearing the pick removes its preselection unless the reviewer changed that row by hand. A review or push that publishes or unpublishes an entry needs edit access to that entry's page, checked again just before the push sends it. One the reviewer can only view refuses the whole review or batch before anything is written or sent. Field and body pushes keep the collection check.Required Builder field author is missing or invalidblock on the live test entry. Publishes and unpublishes send the entry's name like every other write, so a title change in the same push renames the Builder entry too.Live check
Ran on
agent-native-blog-article-testonly, through the test-model guard. Across both runs, the guard allowed 6 Builder writes (PATCH), all to that one entry, and blocked nothing.authorreference was kept.…-2, with every change-set id distinct.Steps 1–8 ran before 9a18a90. Its changes and the ones after it (the Unpublished changes pick, the stricter autosave block, the setting recheck, the cleared-pick block, the page access checks, the moving preselection, the empty-body push, the clear's setting check, and the shared approval) are covered by tests only. Builder didn't report
meta.hasAutosavesfor this entry in any state, so the status read correctly right after each push and after a refresh, and the new autosave check never blocked a publish. The six prepares uploaded 30 encrypted payload files to Builder's file storage. Content uploads them unrecorded, so they don't appear in the space's asset library. The collection is back to stage-only with publication changes off.Not covered
execute-builder-source-executionthrows a bareErrorafter recording the block). That predates this branch and is unchanged here.Checks
templates/content:set-builder-entry-status.db,builder-source-review-gates.db,execute-builder-source-execution,_builder-cms-write-adapter,_builder-cms-unpublished-changes,_builder-status-property.db,_database-source-utils,content-database-source-actions,stage-builder-source-bulk-update.db,cancel-prepared-builder-source-update.db,execute-builder-source-batch,resync-content-database-source.db,content-database-lifecycle.db,database-table-columns, andBuilderSourceReviewDialog.uiall pass. The new race, dialog, repeated-id, staged-pick, editor-autosave, entry-name, setting-recheck, cleared-pick, page-access, push page-access, moving-preselection, leading-slot, empty-body, emptied-body, delayed-clear, and concurrent-approval tests each fail without their fix.tscis clean apart from the existingvirtual:react-router/server-builderror.pnpm guardspasses apart from the existingdead-settings-keysbaseline. Both i18n guards pass. On this branch merged withmain(after #8083 and #8169 landed), strict-lint and compiler-strictness report nothing on its added lines.🤖 Generated with Claude Code