plugins/aem/6.5-lts/skills/aem-workflow/workflow-launchers/SKILL.md
Configure and deploy Workflow Launchers that automatically start workflows in response to JCR content changes on AEM 6.5 LTS
npx skillsauth add adobe/skills workflow-launchersInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Developers and integrators configuring cq:WorkflowLauncher nodes that auto-start workflows on JCR content events on AEM 6.5 LTS — DAM asset processing on upload, review workflows on page edit, custom auto-trigger patterns, or overlays that disable/replace OOTB launcher behavior.
/conf/global/settings/workflow/launcher/config/ (preferred) or /apps/settings/workflow/launcher/config/. Legacy /etc/workflow/launcher/config/ still works but should be migrated.Launchers depend on three upstream concerns — verify all three before expecting a launcher to start a working instance:
workflow= property must already be deployed and synced to /var/workflow/models/<name>.WorkflowProcess and ParticipantStepChooser referenced by that model must be registered as an OSGi service. Missing services produce Process not found on first instance execution./var/workflow/models/<name> (verify via Tools → Workflow → Models)./conf/global/ or /apps/settings/.filter.xml covering the launcher path with mode="merge"./conf/global/settings/workflow/launcher/config/ (or /apps/settings/...) for deploying custom launchers via content package.workflow-administrators (or equivalent) — enable/disable launchers in the Tools → Workflow → Launchers UI./var/workflow/models/ for runtime path lookup.Use this table to route a developer's intent to the right launcher pattern:
| Developer intent | Pattern |
|---|---|
| "Start a workflow when an asset is uploaded to DAM" | NODE_ADDED on nt:file under /content/dam(/.*)?/jcr:content/renditions/original |
| "Trigger review when a page is edited under /content/my-site" | NODE_MODIFIED on cq:PageContent under /content/my-site(/.*)?/jcr:content |
| "Disable an OOTB DAM launcher I don't need" | Overlay at /conf/global/.../launcher/config/<same-name> with enabled={Boolean}false |
| "Replace OOTB launcher behavior with my own workflow" | Overlay at /conf/global/.../launcher/config/<same-name> with new workflow= |
| "Auto-start on every replication event" | Use a Replication Trigger instead — see workflow-triggering Section 5 |
Launchers are event-based (JCR observation). Use a different mechanism when the trigger is not an event-driven content change:
| Developer intent | Use this instead |
|---|---|
| "Run my workflow nightly" or any time-based schedule | Sling Scheduler + WorkflowSession API — see workflow-triggering programmatic-api.md |
| "Run this once for all 500 existing pages" | A one-shot servlet or scheduled job that starts workflows in a capped loop |
| "When workflow X completes, run workflow Y" | A WorkflowProcess step in workflow X that triggers Y, not a second launcher (avoids race conditions) |
| "Run when content is replicated to publish" | Replication Trigger (workflow-triggering Section 5), not a launcher on /var/audit/... |
| "Run on events under /var/, /jcr:system, or anonymous-user events" | These paths and the anonymous user are excluded by WorkflowLauncherListener — launchers cannot fire here |
A Workflow Launcher (cq:WorkflowLauncher) is a JCR node that registers a JCR event listener. When a node event occurs at a path matching the launcher's glob pattern, node type, and conditions, the Granite Workflow Engine enqueues a workflow start.
The listener is managed by WorkflowLauncherListener (an OSGi service). It reads all active launcher configurations at startup and re-evaluates them when configurations change.
JCR Event (NODE_ADDED / NODE_MODIFIED / NODE_REMOVED)
↓
WorkflowLauncherListener (OSGi EventListener)
↓ matches: glob, nodetype, event type, conditions
Workflow Engine: enqueue WorkflowData
↓
Workflow Instance created at /var/workflow/instances/
| Property | Type | Description |
|---|---|---|
| eventType | Long | 1 = NODE_ADDED, 2 = NODE_MODIFIED, 4 = NODE_REMOVED, 8 = PROPERTY_ADDED, 16 = PROPERTY_CHANGED, 32 = PROPERTY_REMOVED |
| glob | String | Glob pattern matched against the event node path (e.g., /content/dam(/.*)?) |
| nodetype | String | JCR node type the event node must be (e.g., dam:AssetContent) |
| conditions | String[] | Additional JCR property conditions on the event node |
| workflow | String | Runtime path of the workflow model /var/workflow/models/<id> |
| enabled | Boolean | Whether the launcher is active |
| description | String | Human-readable description |
| excludeList | String | Comma-separated list of entries that suppress the launcher for matching events. Two entry formats can be mixed: bare JCR property names (skip events whose only changed property matches, e.g. jcr:lastModified) and event-user-data:<value> (skip events tagged with that JCR observation userData, e.g. event-user-data:changedByWorkflowProcess). |
| runModes | String[] | Restrict to specific run modes — honoring is unreliable on 6.5 LTS; prefer config.author/ packaging |
| transient | Boolean | Run the launched workflow as transient — no /var/workflow/instances/ node unless persistence is forced. Use for high-volume launchers |
| noProcess | Boolean | Match events but do not start the workflow — silence a launcher without removing it |
On AEM 6.5 LTS, launcher configurations can live at:
| Path | Notes |
|---|---|
| /libs/settings/workflow/launcher/config/ | OOTB launchers — do not edit directly |
| /conf/global/settings/workflow/launcher/config/ | Recommended for new custom launchers |
| /apps/settings/workflow/launcher/config/ | Alternative overlay location |
| /etc/workflow/launcher/config/ | Legacy path (AEM 6.0–6.2); still supported but migrate away |
Resolution order: /conf/global → /apps → /libs
Maven project location:
ui.content/src/main/content/jcr_root/conf/global/settings/workflow/launcher/config/
my-custom-launcher/
.content.xml
Or for overlay-based approach under /apps:
ui.apps/src/main/content/jcr_root/apps/settings/workflow/launcher/config/
my-custom-launcher/
.content.xml
Node structure (.content.xml):
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root
xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
jcr:primaryType="cq:WorkflowLauncher"
eventType="{Long}1"
glob="/content/dam(/.*)?/jcr:content/renditions/original"
nodetype="nt:file"
workflow="/var/workflow/models/dam/update_asset"
enabled="{Boolean}true"
description="Start DAM update workflow on new original rendition upload"/>
<!-- For author-only restriction, package this .content.xml under config.author/.
The runModes property on cq:WorkflowLauncher is unreliable on 6.5 LTS. -->
Filter in filter.xml:
<filter root="/conf/global/settings/workflow/launcher/config/my-custom-launcher"/>
To disable or modify an OOTB launcher:
/libs/settings/workflow/launcher/config/<launcher-name> to /conf/global/settings/workflow/launcher/config/<launcher-name> (or /apps/settings/...)enabled="{Boolean}false")Verified against /libs/settings/workflow/launcher/config/ on AEM 6.5 LTS. Inspect any launcher in CRXDE Lite or via Tools → Workflow → Launchers for the full property set.
| Launcher | eventType | nodetype | glob | workflow |
|---|---|---|---|---|
| update_asset_create | 1 (NODE_ADDED) | nt:file | /content/dam(/((?!/subassets).)*/)renditions/original | /var/workflow/models/dam/update_asset |
| update_asset_mod | 16 (PROPERTY_CHANGED) | nt:file | /content/dam(/((?!/subassets).)*/)renditions/original | /var/workflow/models/dam/update_asset |
| dam_xmp_writeback | 16 (PROPERTY_CHANGED) | nt:unstructured | /content/dam(/.*)/jcr:content/metadata | /var/workflow/models/dam-xmp-writeback |
To listen for both ADD and MODIFY, combine event types:
eventType="{Long}3" <!-- 1 (ADD) + 2 (MODIFY) = 3 -->
conditions="[property=cq:type,value=publicationevent,type=STRING]"
Condition format: property=<name>,value=<value>,type=<JCR_TYPE> (type is optional, defaults to STRING).
Launchers are the surface where workflow load is automated. A single bad glob can flood the workflow job queue with one content edit. Apply these before deploying any launcher:
/content(/.*)?) paired with eventType=2 (NODE_MODIFIED) fires on every property change under /content. Always pair the glob with a specific nodetype (cq:PageContent, dam:AssetContent) and conditions that match only the events you care about./jcr:content/renditions/original) when you only want one trigger per upload.Session with setUserData("changedByWorkflowProcess") before the write so WorkflowLauncherListener ignores the resulting events. The session parameter on WorkflowProcess.execute() is a WorkflowSession, not a JCR Session — adapt it first: javax.jcr.Session jcrSession = session.adaptTo(javax.jcr.Session.class); jcrSession.getWorkspace().getObservationManager().setUserData("changedByWorkflowProcess");. If you write through a service-user ResourceResolver instead, tag that resolver's underlying Session — it is a different Session instance and the flag does not propagate. This is the most robust pattern (works across model changes, no static config to keep in sync). Use the launcher's excludeList only when you can statically name every model that might re-trigger; use a JCR property flag when the workflow writes to a different node than the launcher watches. See condition-patterns.md for code.transient="true" on the workflow model (see workflow-model-design Architecture Considerations). Persistent workflows in this regime bloat /var/workflow/instances quickly.enabled={Boolean}false overlay), package the launcher under config.author/ for run-mode restriction, or restrict with conditions tied to a prod-only marker.Local development only. The
curl -u admin:adminexample below targets a local author atlocalhost:4502with the default admin password. Never run it against a shared, stage, or production instance, and never with defaultadmin:admincredentials outside an isolated dev box.
/conf/global/settings/workflow/launcher/config/ and /apps/settings/workflow/launcher/config/ in CRXDE LiteWorkflowLauncherListener service/var/workflow/launcher/ for active event registrationscurl -u admin:admin http://localhost:4502/etc/workflow/launcher.json to list allAfter deploying a launcher, confirm it's loaded and firing:
| Surface | How |
|---|---|
| Author UI | Tools → Workflow → Launchers — your launcher should appear with the Enabled flag set |
| CRXDE | Confirm the node exists at /conf/global/settings/workflow/launcher/config/<name> (or /apps/) with all properties intact |
| Trigger test | Manually create the event (upload a file to the watched path; edit a page) and check Tools → Workflow → Instances for a new instance |
| Logs | Enable DEBUG for com.adobe.granite.workflow.core.launcher in the Felix console — every launched instance produces a log line |
If the event happens but no instance appears:
dam:AssetContent node (/content/dam/.../jcr:content), not on dam:Asset itself. Inspect the event node path in CRXDE before tuning the glob.nodetype= doesn't match the event node's primary type. Confirm in CRXDE./conf/global may shadow the one you deployed at /apps/. Check the resolution order in Launcher Storage Paths above.condition-patterns.md.If the launcher fires (you see the log line) but the workflow doesn't progress, the issue is downstream — most commonly a referenced WorkflowProcess is not registered (see workflow-development Dependencies). For full diagnosis: workflow-debugging.
| Reference | What It Covers |
|---|---|
| references/workflow-launchers/launcher-config-reference.md | Full property spec and XML templates |
| references/workflow-launchers/condition-patterns.md | Common condition patterns, glob syntax, event type codes |
| ../workflow-orchestrator/references/workflow-foundation/architecture-overview.md | Granite Workflow Engine overview (canonical) |
| ../workflow-orchestrator/references/workflow-foundation/65-lts-guardrails.md | 6.5 LTS constraints and legacy path guidance (canonical) |
| ../workflow-orchestrator/references/workflow-foundation/jcr-paths-reference.md | Where launchers live in the JCR (canonical) |
tools
Identifies which items (pages, campaigns, products, channels, regions) had the biggest increases or decreases for a key metric between two time periods. Use this skill when someone asks "what's up and what's down," "which campaigns moved the most," "top gainers and losers," "what pages are trending," "show me what changed by channel," or any variation of identifying the biggest movers and decliners for a metric.
tools
Compares the performance of two or more audience segments across key metrics side by side. Use this skill when someone wants to compare audiences, cohorts, or groups — for example, "how do mobile users compare to desktop users on conversion," "compare new vs. returning visitors," "show me the difference between these two segments," "compare these audiences on our KPIs," or "which segment performs better." Also trigger for "segment comparison," "audience comparison," or "cohort comparison."
business
Produces a compact KPI digest showing how key metrics changed over a period and what's driving the movement. Use this skill when someone asks for a performance summary, a weekly recap, a morning briefing, a KPI update, or any variation of "how did we do this week/month." Also trigger for requests like "give me a performance overview," "what moved in the last 7 days," "pull our KPI report," or "summarize our metrics."
testing
Analyzes a multi-step conversion funnel to find where users drop off and which steps have the worst leakage. Use this skill when someone describes a journey or funnel and asks about conversion rates, drop-off, fallout, or step completion. Trigger for phrases like "analyze our onboarding funnel," "where are users dropping off," "what's our checkout conversion rate," "funnel analysis," "show me fallout between these steps," or "which step loses the most users."