Summary
When content is ingested on a Push Publishing receiver (delivery environment), the system calls setLive() / setWorking() directly, bypassing the workflow engine entirely. This leaves the content's workflow step out of sync with its actual content state on the receiver — content can be live but still showing a "Draft" workflow step.
This is a systemic instance of the broader State vs. Step problem and was explicitly called out as one of the highest-impact sources of state/step divergence:
"Push Publishing or Push Removing Content (this is a biggie)" — Will Ezell, #eng March 2026
This issue proposes an opt-in configuration to allow the receiver to fire the content type's default workflow action during ingest, keeping state and step in sync.
Current behavior
- User publishes content on authoring → workflow fires → step = Published, state = live ✓
- Content is push-published to receiver
- Receiver ingests the bundle →
setLive() called directly, no workflow fired
- On receiver: state = live ✗ step = Draft (unchanged from authoring draft state)
Content is correctly live on the front end of the delivery environment, but the workflow step display is wrong. Any workflow-triggered side effects (notifications, indexing actionlets, integrations) are also silently skipped.
The same problem occurs in reverse when push-removing content: the receiver calls the equivalent of setWorking() / setDeleted() without firing an UNPUBLISH or ARCHIVE workflow action.
Expected behavior
An opt-in flag (suggested: PUSH_PUBLISH_USE_DEFAULT_WORKFLOW_ON_RECEIVER) that, when enabled, causes the receiver to:
- Look up the workflow scheme assigned to the incoming content type on the receiver
- Resolve the default action for the applicable system action (
PUBLISH, UNPUBLISH, ARCHIVE) — same logic already used by /api/v1/workflow/fire/DEFAULT/PUBLISH
- Fire that action on the received content via
WorkflowAPI (skipping actionlets flagged as sender-side only, to avoid side-effect duplication)
- If no default action is configured → fall back to current behavior (direct
setLive()) and log a warning
"Optionally" is critical — unconditionally firing actionlets on receivers would cause double-notifications, push-publish loops, and hits to external APIs that customers have designed for authoring-side use only.
Reproduction steps
- Set up two dotCMS environments with Push Publishing configured (sender + receiver)
- On sender: create a content item and leave it in "Draft" workflow step
- On sender: run the Publish workflow action → content becomes live, step = "Published"
- Push-publish the content to the receiver
- On receiver: verify content is live on the front end ✓
- On receiver: open the content item in the UI → workflow step still shows "Draft" ✗
Also reproducible in a single-environment setup via page cascade-publish (same root cause, same bypass).
Code references
PublishContentActionlet.java:75 — sets WORKFLOW_IN_PROGRESS = true on related content before cascade-publish, bypassing workflow
ESContentletAPIImpl.java:1200–1212 — workflow skipped entirely when WORKFLOW_IN_PROGRESS = true; only setLive() is called
WorkflowAPIImpl — default action resolution logic to reuse on the receiver side
Impact
- Severity: High — affects all push-publishing customers; workflow step UI is permanently wrong for received content unless manually corrected
- Workaround: On the receiver, manually open each affected content item and run the Publish workflow action to resync the step. Not feasible at scale.
- Customer report: Freshdesk ticket #37387 — customer reports content appearing live on front end while UI shows "Draft"; also observes inconsistent behavior when removing content from pages in remote-publishing setups
Related
Summary
When content is ingested on a Push Publishing receiver (delivery environment), the system calls
setLive()/setWorking()directly, bypassing the workflow engine entirely. This leaves the content's workflow step out of sync with its actual content state on the receiver — content can be live but still showing a "Draft" workflow step.This is a systemic instance of the broader State vs. Step problem and was explicitly called out as one of the highest-impact sources of state/step divergence:
This issue proposes an opt-in configuration to allow the receiver to fire the content type's default workflow action during ingest, keeping state and step in sync.
Current behavior
setLive()called directly, no workflow firedContent is correctly live on the front end of the delivery environment, but the workflow step display is wrong. Any workflow-triggered side effects (notifications, indexing actionlets, integrations) are also silently skipped.
The same problem occurs in reverse when push-removing content: the receiver calls the equivalent of
setWorking()/setDeleted()without firing anUNPUBLISHorARCHIVEworkflow action.Expected behavior
An opt-in flag (suggested:
PUSH_PUBLISH_USE_DEFAULT_WORKFLOW_ON_RECEIVER) that, when enabled, causes the receiver to:PUBLISH,UNPUBLISH,ARCHIVE) — same logic already used by/api/v1/workflow/fire/DEFAULT/PUBLISHWorkflowAPI(skipping actionlets flagged as sender-side only, to avoid side-effect duplication)setLive()) and log a warning"Optionally" is critical — unconditionally firing actionlets on receivers would cause double-notifications, push-publish loops, and hits to external APIs that customers have designed for authoring-side use only.
Reproduction steps
Also reproducible in a single-environment setup via page cascade-publish (same root cause, same bypass).
Code references
PublishContentActionlet.java:75— setsWORKFLOW_IN_PROGRESS = trueon related content before cascade-publish, bypassing workflowESContentletAPIImpl.java:1200–1212— workflow skipped entirely whenWORKFLOW_IN_PROGRESS = true; onlysetLive()is calledWorkflowAPIImpl— default action resolution logic to reuse on the receiver sideImpact
Related
WORKFLOW_IN_PROGRESSbypass) — worth fixing in the same pass