Skip to content

Push Publishing receiver bypasses workflow on ingest — optionally fire default workflow actions when receiving content #35837

@Neehakethi

Description

@Neehakethi

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

  1. User publishes content on authoring → workflow fires → step = Published, state = live
  2. Content is push-published to receiver
  3. Receiver ingests the bundle → setLive() called directly, no workflow fired
  4. 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:

  1. Look up the workflow scheme assigned to the incoming content type on the receiver
  2. Resolve the default action for the applicable system action (PUBLISH, UNPUBLISH, ARCHIVE) — same logic already used by /api/v1/workflow/fire/DEFAULT/PUBLISH
  3. Fire that action on the received content via WorkflowAPI (skipping actionlets flagged as sender-side only, to avoid side-effect duplication)
  4. 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

  1. Set up two dotCMS environments with Push Publishing configured (sender + receiver)
  2. On sender: create a content item and leave it in "Draft" workflow step
  3. On sender: run the Publish workflow action → content becomes live, step = "Published"
  4. Push-publish the content to the receiver
  5. On receiver: verify content is live on the front end ✓
  6. 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions