Repository navigation
PROD-3078: keep target-only edits to templates, URL redirections and list order - #261
Merged
Merged
Conversation
… order Page templates, URL redirections and list order now follow the rule the other types use: a change on the source only updates the target, a change on the target only is kept with a warning, a change on both sides is a conflict, and --overwrite lets the source win. Before, any difference was settled by the source, so a target-only edit was overwritten without --overwrite and without a warning. These types have no version or modified date, so the mapping now records the last synced state: - Templates and redirections: a hash of each side's normalised shape (sourceHash, targetHash, fingerprintVersion), the same fields the "differs" checks compare, without per-instance IDs. A template's target hash comes from what the server saved. - List order: a new `listorder` mapping per locale with each list's source order and the target order the run left. A reorder is a change in the relative order of the items both have, so an item added or deleted on either side isn't one. The fields are source*/target* pairs, so a reverse sync reads them transposed. A mapping from an older CLI has no record: the first sync lets the source win, as before (preflight labels it "no sync record yet"), and records it; lists and templates already in sync are recorded without a write. A template kept on the target (or in conflict) holds back the pages that use it only when it has no section for one of their zones, which would otherwise be placed by position or folded into the main zone. A mapped redirection deleted on the target is now a conflict, like a deleted template; --overwrite recreates it. Kept changes are reported as before, with the new types template, urlRedirection and listOrder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every round now also reorders a list and renames a template section's display name on the side being synced to. drift:listOrder and drift:template pass when the sync keeps the edit with a target-changed warning, or reports a conflict when the round's own changes touched it too. A template section rename is also in the change catalogue for the random tiers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before the first sync the run adds a redirection to A. Every round then points it somewhere new on the side being synced to, and drift:urlRedirection passes when the sync keeps the edit with a target-changed warning. The Management SDK can only delete redirections, so the run saves through the Management API's batch endpoint and reads the Fetch API's redirection list, as the CLI does, waiting for each edit to show there before syncing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…al reorder Round 1 of the first run graded drift:listOrder as failed although the CLI decided nothing wrong: the sync's pull didn't show the reorder yet. An order change reaches the Fetch API's sync feed later than the item's new version, and the runner only waited for versions. The runner now also waits until the feed shows each reordered item at its new itemOrder. Swapping two items by saving them can leave both with the same itemOrder, which the CLI rightly reads as no defined order rather than a reorder. The list order drift now moves a list's last item to the top through the list item-order endpoint, as drag and drop does, and checks the order it left. The change is also in the catalogue for the random tiers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run 3 showed that saving two items with swapped itemOrder values can leave the list in its order, so the round's own list swap silently did nothing. content.reorder now moves a list's last item to the top through the list item-order endpoint, as the Manager's drag and drop does, and is the one reorder in the catalogue. The list order drift avoids a list the round already reordered on the other side: the same move on both sides leaves the two in the same order, with nothing to report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…an edit Redirections and list order are pulled from the Fetch API, which shows a save or a reorder a few minutes after it's made. A sync run in that window pulled the state the last sync had replaced: on the target it was reported as a kept target change (the live test's preflight right after a sync listed the list that sync had just reordered), and in a reverse sync, where that side is the source, it would have synced the old state back. When a sync replaces a target redirection or list order, the record now also keeps what it replaced (targetReplacedHash, targetReplacedContentIDs; a reverse sync reads them as source*). A side that still shows it is waiting on the Fetch API, not edited: with no other change the entity is skipped until a pull shows the new state, which clears it. A real change on the other side still counts. Templates are read from the Management API and don't need it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's content drift passes `avoid` as a Set of content IDs; the list order drift took a list of list names under the same key. It now uses `avoidLists`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A preflight writes nothing, so a new model gets no mapping. The container pusher then skipped its lists as "target model mapping not found", and hasValidMappings dropped the lists' new items without a preflight entry, so the plan showed only the model while the real sync created all of them. In preflight, a container whose model the model pusher recorded as planned is now planned as a create and recorded as planned itself, and an item whose container and model are both planned counts as having valid mappings. Real runs are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jules-exel
approved these changes
Nov 4, 2026
This was referenced Nov 4, 2026
Merged
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PROD-3078. Follow-up to #258: page templates, URL redirections and list order now follow the same rule as content, pages, models, containers, assets and galleries, so conflict detection and
--overwritebehave the same for every type.The rule
targetChangeKeptin--jsonSummary)--overwriteBefore this, these three types couldn't tell which side changed, so any difference was settled by the source: a target-only edit was overwritten without
--overwriteand without a warning.How
These types have no version or modified date, so the mapping now records the last synced state.
sourceHash/targetHash/fingerprintVersionon the existing mapping record. Each is a sha1 of a normalised shape: the fields the "differs" checks already compare, without per-instance IDs, in a stable order. A template's target hash comes from what the server saved.listordermapping per locale, with each list's source order and the target order the run left. A reorder is a change in the relative order of the items the record and the list both have, so adding or deleting an item isn't one.source*/target*pairs, so they transpose with the IDs.targetReplacedHash,targetReplacedContentIDs). A side still showing it is skipped until the Fetch API catches up, and that pull clears it.Behaviour changes beyond the ticket
--overwriterecreates it. Before, it was recreated silently.Also in this PR: preflight plans a new model's lists and items
Found testing 1.3.0-beta.1 on a clone pair. A preflight writes nothing, so a model it would create gets no mapping: the container pusher skipped the model's lists ("target model mapping not found"), and
hasValidMappingsdropped their new items without a preflight entry. The plan showed only the model; the real sync created all of them. Now, in preflight only, a container whose model is planned is planned (and recorded as planned), and an item whose container and model are planned is planned too. Real runs are unchanged.On the clone pair, for a new model with a list and 3 items: beta.1 planned
Models: create 1and skipped the list; this branch plansModels: create 1,Containers: create 1,Content: create 3. Unit tests incontainer-pusher.test.tsandhas-valid-mappings.test.ts.CI
The live permutation test now drifts each of the three on the receiving side every round: a list reorder, a template section display-name rename, and a redirection edit (a redirection is added to A before the first sync).
drift:listOrder,drift:templateanddrift:urlRedirectionpass only when the edit is kept with a warning, or reported as a conflict.Getting this reliable needed two harness fixes:
itemOrder. The order shows up later than the items' new versions.content.reorderuses the list item-order endpoint, the drag-and-drop path. Saving items with swappeditemOrdervalues can leave the list unchanged.Testing
--overwrite;🤖 Generated with Claude Code