/scheduler
AEM Cloud Service expert skill for Sling Scheduler. Routes to path-a.md (Runnable + OSGi properties) or path-b.md (Sling Jobs via JobManager). Covers classification, CS-specific constraints (no @SlingScheduled, multi-pod runOn, Boolean type hint), review checklist,
$ npx -y skills add adobe/skills --skill scheduler --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/scheduler
Context preview
The summary Claude sees to decide when to auto-load this skill.
AEM Cloud Service expert skill for Sling Scheduler. Routes to path-a.md (Runnable + OSGi properties) or path-b.md (Sling Jobs via JobManager). Covers classification, CS-specific constraints (no @SlingScheduled, multi-pod runOn, Boolean type hint), review checklist,
SKILL.md
scheduler.SKILL.mdname: scheduler
description: AEM Cloud Service expert skill for Sling Scheduler. Routes to path-a.md (Runnable + OSGi properties) or path-b.md (Sling Jobs via JobManager). Covers classification, CS-specific constraints (no @SlingScheduled, multi-pod runOn, Boolean type hint), review checklist, troubleshooting fingerprints, and common pitfalls.
license: Apache-2.0
Scheduler — AEM as a Cloud Service
Overview
Schedules in AEM CS **must be declared as OSGi component properties** — `Scheduler.schedule()` is not persisted across restarts. There is **no `@SlingScheduled` annotation** in the CS SDK.
Three properties control every scheduler:
| Property | Required value | Why | |----------|---------------|-----| | `scheduler.expression` | Valid Quartz cron string | Declares the trigger | | `scheduler.concurrent:Boolean=false` | `:Boolean` type hint required | Without the hint OSGi treats it as String and concurrent runs are not suppressed | | `scheduler.runOn` | `SINGLE` or `LEADER` for any write or external call | Default `ALL` fires on every publish pod simultaneously |
---
Classification — choose before making any changes
**Path A — Runnable + OSGi component properties** when ALL are true:
- Cron is a hardcoded constant or single `@AttributeDefinition`-backed value
- Only one schedule per class
- Class `implements Runnable`
- No `ScheduleOptions.config()`, no per-execution job payload
→ Read [path-a.md](path-a.md) and follow its steps.
**Path B — Sling Jobs via JobManager** when ANY is true in the **legacy source**:
- Cron comes from runtime config (`config.cronExpression()`)
- Multiple cron expressions per class
- Legacy code needs per-execution job data, config-driven scheduling, or a Scheduler + JobConsumer split
- Business logic needs job context or properties at execution time
- `@Modified` re-registers schedules with new config values
→ Read [path-b.md](path-b.md) and follow its steps.
**One pattern per session.** If the codebase has both kinds, fix one class at a time.
---
Discovery
Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook:
bash ../scripts/analyze.sh <workspace-root> --pattern scheduler
**Match criteria (what the detector flags):**
- A class that **`implements org.apache.sling.commons.scheduler.Job`** (import-aware).
- The **OSGi-property scheduler** shape — a class that **`implements Runnable`** and carries an OSGi `@Component` declaring a `scheduler.expression` / `scheduler.name` / `scheduler.period` property.
- A file that **imports `org.apache.sling.commons.scheduler.Scheduler`** (programmatic use via an injected `Scheduler`) but has no class-level match — one finding at the file's primary type.
Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause and same-file `@Component`; reached-via-base-class and constant-valued properties are not resolved.
Resolution contract
**guided** — `apply (guided)`. The analyzer locates and reports each scheduler class; remediation is judgment-based and routed by the Classification above to **Path A** ([path-a.md](path-a.md), Runnable + OSGi properties) or **Path B** ([path-b.md](path-b.md), Sling Jobs via `JobManager`). Open the chosen path and apply its steps in an apply session.
| Site shape | Disposition | |---|---| | Single-schedule, hardcoded cron, `implements Runnable` | apply (guided) → path-a.md | | Config-driven cron, multiple schedules, `implements Job`, or `ScheduleOptions.config()` | apply (guided) → path-b.md | | Already Sling Jobs via `JobManager` with single-execution guard | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` |
---
Review Checklist
Use the path-specific checklists in [path-a.md](path-a.md) and [path-b.md](path-b.md) for scheduler mechanics.
Cross-cutting checks:
- [ ] The chosen path matches the source shape: Path A for a single hardcoded cron + `Runnable`; Path B for config-driven or multi-schedule legacy code
- [ ] No `@SlingScheduled` annotation
- [ ] No `scheduler.schedule()`, `scheduler.unschedule()`, or `scheduler.EXPR()` calls in the migrated code
- [ ] `getServiceResourceResolver` used — not `getAdministrativeResourceResolver`
- [ ] ResourceResolver in try-with-resources — not stored as a field
- [ ] OSGi DS R6 annotations (`org.osgi.service.component.annotations`) — no Felix SCR
- [ ] Service user subservice name matches a `ServiceUserMapperImpl.amended` config
Path A checks:
- [ ] `scheduler.expression` is a valid Quartz cron string (6 or 7 fields)
- [ ] `scheduler.concurrent:Boolean=false` present with the `:Boolean` type hint
- [ ] `scheduler.runOn=SINGLE` or `LEADER` set when the job writes to repo or calls external systems
Path B checks:
- [ ] The job topic constant is shared between the Scheduler and JobConsumer classes
- [ ] Job properties are read with `job.getProperty("key", Type.class)`
- [ ] `JobResult.OK`, `FAILED`, or `CANCEL` is returned from the consumer
---
Troubleshooting
| Symptom | Log to search | Fix direction | |---------|--------------|--------------| | Scheduler never fires after deployment | none (silent) | Inspect the component's runtime state: if `UNSATISFIED`, the cause is usually a missing config or a misspelled config field name (`scheduler_expression()` in Path A, `cronExpression()` in Path B); if `ACTIVE` but no executions, the cron property name is misspelled or `scheduler.expression` is invalid | | Fires N times per trigger (N = pod count) | none | Add `scheduler.runOn=SINGLE` to `@Component` property array | | Two instances run simultaneously | none | Add `scheduler.concurrent:Boolean=false` — the `:Boolean` type hint is mandatory | | Stops firing with no code change; unrelated workflows also stall | `RejectedExecutionException` in `sling-default` pool entries | Thread pool starvation — see diagnosis below |
**Thread pool starvation root cause chain
Read more
name: scheduler description: AEM Cloud Service expert skill for Sling Scheduler. Routes to path-a.md (Runnable + OSGi properties) or path-b.md (Sling Jobs via JobManager). Covers classification, CS-specific constraints (no @SlingScheduled, multi-pod runOn, Boolean type hint), review checklist, troubleshooting fingerprints, and common pitfalls. license: Apache-2.0
Scheduler — AEM as a Cloud Service
Overview
Schedules in AEM CS **must be declared as OSGi component properties** — `Scheduler.schedule()` is not persisted across restarts. There is **no `@SlingScheduled` annotation** in the CS SDK.
Three properties control every scheduler:
| Property | Required value | Why | |----------|---------------|-----| | `scheduler.expression` | Valid Quartz cron string | Declares the trigger | | `scheduler.concurrent:Boolean=false` | `:Boolean` type hint required | Without the hint OSGi treats it as String and concurrent runs are not suppressed | | `scheduler.runOn` | `SINGLE` or `LEADER` for any write or external call | Default `ALL` fires on every publish pod simultaneously |
---
Classification — choose before making any changes
**Path A — Runnable + OSGi component properties** when ALL are true:
- Cron is a hardcoded constant or single `@AttributeDefinition`-backed value
- Only one schedule per class
- Class `implements Runnable`
- No `ScheduleOptions.config()`, no per-execution job payload
→ Read [path-a.md](path-a.md) and follow its steps.
**Path B — Sling Jobs via JobManager** when ANY is true in the **legacy source**:
- Cron comes from runtime config (`config.cronExpression()`)
- Multiple cron expressions per class
- Legacy code needs per-execution job data, config-driven scheduling, or a Scheduler + JobConsumer split
- Business logic needs job context or properties at execution time
- `@Modified` re-registers schedules with new config values
→ Read [path-b.md](path-b.md) and follow its steps.
**One pattern per session.** If the codebase has both kinds, fix one class at a time.
---
Discovery
Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook:
bash ../scripts/analyze.sh <workspace-root> --pattern scheduler
**Match criteria (what the detector flags):**
- A class that **`implements org.apache.sling.commons.scheduler.Job`** (import-aware).
- The **OSGi-property scheduler** shape — a class that **`implements Runnable`** and carries an OSGi `@Component` declaring a `scheduler.expression` / `scheduler.name` / `scheduler.period` property.
- A file that **imports `org.apache.sling.commons.scheduler.Scheduler`** (programmatic use via an injected `Scheduler`) but has no class-level match — one finding at the file's primary type.
Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause and same-file `@Component`; reached-via-base-class and constant-valued properties are not resolved.
Resolution contract
**guided** — `apply (guided)`. The analyzer locates and reports each scheduler class; remediation is judgment-based and routed by the Classification above to **Path A** ([path-a.md](path-a.md), Runnable + OSGi properties) or **Path B** ([path-b.md](path-b.md), Sling Jobs via `JobManager`). Open the chosen path and apply its steps in an apply session.
| Site shape | Disposition | |---|---| | Single-schedule, hardcoded cron, `implements Runnable` | apply (guided) → path-a.md | | Config-driven cron, multiple schedules, `implements Job`, or `ScheduleOptions.config()` | apply (guided) → path-b.md | | Already Sling Jobs via `JobManager` with single-execution guard | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` |
---
Review Checklist
Use the path-specific checklists in [path-a.md](path-a.md) and [path-b.md](path-b.md) for scheduler mechanics.
Cross-cutting checks:
- [ ] The chosen path matches the source shape: Path A for a single hardcoded cron + `Runnable`; Path B for config-driven or multi-schedule legacy code
- [ ] No `@SlingScheduled` annotation
- [ ] No `scheduler.schedule()`, `scheduler.unschedule()`, or `scheduler.EXPR()` calls in the migrated code
- [ ] `getServiceResourceResolver` used — not `getAdministrativeResourceResolver`
- [ ] ResourceResolver in try-with-resources — not stored as a field
- [ ] OSGi DS R6 annotations (`org.osgi.service.component.annotations`) — no Felix SCR
- [ ] Service user subservice name matches a `ServiceUserMapperImpl.amended` config
Path A checks:
- [ ] `scheduler.expression` is a valid Quartz cron string (6 or 7 fields)
- [ ] `scheduler.concurrent:Boolean=false` present with the `:Boolean` type hint
- [ ] `scheduler.runOn=SINGLE` or `LEADER` set when the job writes to repo or calls external systems
Path B checks:
- [ ] The job topic constant is shared between the Scheduler and JobConsumer classes
- [ ] Job properties are read with `job.getProperty("key", Type.class)`
- [ ] `JobResult.OK`, `FAILED`, or `CANCEL` is returned from the consumer
---
Troubleshooting
| Symptom | Log to search | Fix direction | |---------|--------------|--------------| | Scheduler never fires after deployment | none (silent) | Inspect the component's runtime state: if `UNSATISFIED`, the cause is usually a missing config or a misspelled config field name (`scheduler_expression()` in Path A, `cronExpression()` in Path B); if `ACTIVE` but no executions, the cron property name is misspelled or `scheduler.expression` is invalid | | Fires N times per trigger (N = pod count) | none | Add `scheduler.runOn=SINGLE` to `@Component` property array | | Two instances run simultaneously | none | Add `scheduler.concurrent:Boolean=false` — the `:Boolean` type hint is mandatory | | Stops firing with no code change; unrelated workflows also stall | `RejectedExecutionException` in `sling-default` pool entries | Thread pool starvation — see diagnosis below |
**Thread pool starvation root cause chain
Repo: adobe/skills
Other skills on adobe-skills.
- /aa-conversion-funnel-analysis
Analyzes a multi-step conversion funnel to find where visitors drop off and which steps have the worst leakage. Use this skill when someone describes a journey and asks about conversion rates, drop-off, fallout, or step completion. Trigger for "analyze our checkout funnel,"
Open skill - /aa-executive-briefing
Generates a concise, executive-ready performance summary covering key metrics, trends, and what's driving movement. Use this skill when someone needs to produce a briefing, executive summary, performance narrative, or stakeholder readout — for example, "write an exec summary of
Open skill - /aa-kpi-pulse
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
Open skill - /aa-segment-performance-comparator
Compares the performance of two or more audience segments across key metrics side by side. Use this skill when someone wants to compare audiences or visitor groups — for example, "how do mobile visitors compare to desktop on conversion," "compare new vs. returning visitors,"
Open skill - /aa-top-movers-watchlist
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,"
Open skill - /cja-dimension-analysis
Comprehensive dimension analysis and reporting for CJA. Use this skill whenever the user wants to analyze one or more dimensions — including cardinality, distribution/skew, trends, anomalies, data quality errors, comparisons, and forecasting. Also trigger when someone asks "what
Open skill

