Skip to content

Fixes from the sync permutation test: PROD-3042, PROD-3043, reverse-sync deletes - #255

Merged
5PK merged 5 commits into
release/1.3.0-beta.0from
fix/perm-test-findings
Oct 30, 2026
Merged

5PK merged 5 commits into
release/1.3.0-beta.0from
fix/perm-test-findings

Conversation

@5PK

@5PK 5PK commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

Into release/1.3.0-beta.0 (#254). Fixes and a test for findings from the two-direction sync permutation test (PROD-3016), plus the latest #251 commit so this branch's check runs the same test.

What's in it

  • Merge of CI: live sync test on throwaway instances, gating promotion to latest #251's latest commit (effb0a6, change rounds in both directions). The release branch was one commit behind, so its check didn't run the two-direction rounds.

  • PROD-3043: a dynamic page's title. The page payload filled a missing title with the menu text, then the page name. A dynamic page without its own title got its name on the target, and verify reported pageFieldMismatch on every run.

    • A dynamic page's title comes from dynamic.titleFormula, and the Management API accepts it empty, so a dynamic page now sends its source title as is.
    • Other page types keep the fallback.
    • A target that already took the name keeps it until its source page changes, or a sync with --overwrite.
  • PROD-3042: false "target changed" conflicts on containers. Saving a page (UpdatePageItem) aligns the requiresApproval setting of its components' non-shared containers with the page's, and stamps lastModifiedDate on each container it changes. The next run then saw those containers as changed on the target and wanted --overwrite.

    • After the page push (when it saved a page), the CLI re-reads the containers.
    • A mapping that was in sync with the target pull at the start of the run, and has a newer target date now, takes the new date.
    • A container already changed on the target before the run keeps its conflict.
    • It's bookkeeping only: an API error is a logged warning.
  • PROD-2992: --syncDeletes on reverse-sync. A new hermetic test, plus a reverse switch on the sync harness that uses the CLI's own enableReverseSync. The test fails without the flag on the reverse run.

    • Note for the permutation test: --syncDeletes is opt-in, so its rounds only check deletes when the harness passes the flag, on sync and reverse-sync alike.

Tests

  • Unit: 3 title cases in process-page.test.ts, 9 in refresh-container-dates.test.ts, and the reverse-sync case in sync-deletes.test.ts.
  • Full suite: 140 suites, 2,644 tests pass. tsc and eslint are clean.
  • Live, on a clone pair:
    • PROD-3043: a source dynamic page with its title cleared synced to an empty title on the target, where 1.3.0 sends the page name. Restoring it synced back.
    • PROD-3042: the containers on this pair already match their pages' setting, so no date moved and nothing was advanced. The preflight after a page sync showed 0 container conflicts. The reported case appears in the two-direction rounds, which this branch's check runs.

Not in this PR

  • PROD-3041 (a client-side timeout leaves an inconsistent mapping): a separate resilience change, related to PROD-2541.
  • The 4 image differences verify still reports on the Blog Starter: they point at a CDN outside the instance (PROD-2773 warns about them). It's a limit of the test's source data, not a sync defect.

🤖 Generated with Claude Code

joelvarty and others added 5 commits October 30, 2026 13:28
After the first sync, the test now runs rounds that alternate direction:
odd rounds edit the source and sync forward, even rounds edit the target
and reverse-sync. Every round also edits the receiving side (drift) and
one item on both sides (a conflict).

- ci/permutation/changes.mjs: change catalogue, discovered from whatever
  each instance holds. Content edit, add, add a linked chain, delete,
  publish/unpublish; list and component reordering; page edit, add,
  delete; component removal; model field add, label change and rename.
- run.mjs: per-round sync, verify and no-op checks, plus conflict and
  drift outcomes (kept-and-reported passes, silently kept fails), and
  apply failures as findings. quick = fixed two-round set; beta and
  nightly = seeded random rounds, replayable with the printed seed.
- Workflow: always installs dependencies (the change engine uses the
  Management SDK), takes a seed input, longer time budgets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nc test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page payload filled a missing title with menuText, then the page
name, so a dynamic page without a title of its own got its name on the
target (e.g. 'posts-dynamic') and verify reported pageFieldMismatch on
every run. A dynamic page's title comes from dynamic.titleFormula, and
the Management API accepts it empty (checked on a clone: saved with an
empty title, stored as ''), so a dynamic page now sends its source title
as is. Other page types keep the fallback.

A target that already took the name keeps it until its source page
changes, or a sync with --overwrite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saving a page (UpdatePageItem) aligns the requiresApproval setting of
the non-shared containers of its components with the page's, and stamps
lastModifiedDate on each container it changes. The container pusher
compares that date with the mapping, so on the next run those containers
looked changed on the target and every following sync wanted --overwrite.

After the page push, when it saved a page, the containers are re-read
(getContainerList) and each mapping that was in sync with the target
pull at the start of the run, and has a newer target date now, takes the
new date. A container already changed on the target before the run keeps
its conflict. Bookkeeping only: an API error is a logged warning.

On a clone pair whose containers already match their pages' setting no
date moves and nothing is advanced; the two-direction permutation test
is where the reported case shows up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A delete made on the target is propagated to the source by
reverse-sync --syncDeletes: same pair and mapping files, deletes on the
original source, mappings removed in their forward orientation. The sync
harness gains a reverse switch that runs the CLI's own enableReverseSync.
The test fails without --syncDeletes on the reverse run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@5PK
5PK deployed to cli-dev-live October 30, 2026 17:58 — with GitHub Actions Active
@5PK
5PK deployed to cli-dev-live October 30, 2026 18:05 — with GitHub Actions Active
@5PK
5PK marked this pull request as ready for review October 30, 2026 18:44
@5PK
5PK merged commit 29b8298 into release/1.3.0-beta.0 Oct 30, 2026
5 of 6 checks passed
@5PK
5PK deployed to cli-dev-live October 30, 2026 18:44 — with GitHub Actions Active
@5PK
5PK deployed to cli-dev-live October 30, 2026 18:49 — with GitHub Actions Active
5PK added a commit that referenced this pull request Nov 2, 2026
…ge title fix

Conflict in process-page.ts: #255's PROD-3043 kept the source title only
for dynamic pages; #253 keeps it for every page (null as ""), since the
Management API's title is optional. Took #253's, and updated PROD-3043's
static-page test to match: an untitled static page stays untitled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
cli-dev-live — 29b82986 Deployed Oct 30, 2026 by 5PK via Release leases #9
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