All AEM as a Cloud Service skills: component development, Workflow model design, development, triggering, launchers, debugging, and triaging, Dispatcher config authoring, advisory, incident response, performance tuning, and security hardening, Rapid Development Environment (RDE) deployment, log inspection, snapshots, and troubleshooting via `aio aem rde`, Jackrabbit Oak query/index tuning (property, Lucene index definitions), and extracting/inventorying JCR queries (XPath, SQL2, QueryBuilder) across a codebase.
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Read this fully before editing. Control plane: SKILL.md.
Per invocation, a deduplicated list of repo-relative Java paths:
{
"files": [
"core/src/main/java/com/example/core/models/HeroModel.java"
]
}Sources:
SKILL.md.The decision table and skip-file policy apply identically in both sources.
For each @Inject-annotated field in the file, inspect the field's declared Java type and pick a replacement:
| Field declared type | Replacement | Intent |
|---|---|---|
String, String[], boolean, Boolean, int, Integer, long, Long, double, Double, float, Float, Calendar, Date | @ValueMapValue | Read a property from the adapted resource's ValueMap |
Any interface type whose fully-qualified package starts with NONE of: org.apache.sling.api.*, org.apache.sling.models.*, javax.*, java.*, com.day.cq.*, com.adobe.cq.*, org.osgi.service.component.* | @OSGiService | Inject an OSGi service reference |
org.apache.sling.api.resource.Resource, org.apache.sling.api.resource.ResourceResolver, org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.api.SlingHttpServletResponse | @SlingObject | Inject the Sling-provided object. @SlingObject, not @Self — these resolve correctly whether the model adapts from a request or a resource. @Self would try to adapt the adaptable to this type and returns null for request-adapted models (compiles fine, fails at runtime). |
Any other type → trigger the skip-file policy below. Do not guess. In particular com.day.cq.wcm.api.Page / Component and other org.apache.sling.* / com.day.cq.* types are not handled here — they typically need @ScriptVariable or adaptation, so they skip-file (see below). @Self (adapting the adaptable to another Sling Model) is also out of scope and skips.
The three supported annotations all live in org.apache.sling.models.annotations.injectorspecific — see imports.
If an @Inject field also carries any of the following, apply the per-companion transform during field rewrite. These are the only companion forms currently supported.
| Existing companion | Transform |
|---|---|
@Named("foo") (javax.inject.Named) | Merge into the new annotation as the name = "foo" attribute. Drop @Named from the field. |
@Default(...) (org.apache.sling.models.annotations.Default) | Keep unchanged; place alongside the new annotation. |
@Via("...") (org.apache.sling.models.annotations.Via) | Keep unchanged; place alongside the new annotation. |
@Optional (org.apache.sling.models.annotations.Optional, field-level) | Drop @Optional and add injectionStrategy = InjectionStrategy.OPTIONAL to this field's new injector-specific annotation (merge with any name/other attributes). Do not touch the class-level @Model — changing defaultInjectionStrategy would make every field optional and silently turn previously-required fields into null-injected ones. Per-field injectionStrategy preserves exact semantics. |
Any companion annotation not in this table → treat as an ambiguous field and trigger skip-file.
After all field transforms are computed for a file — and before writing the file — update the imports:
org.apache.sling.models.annotations.injectorspecific.ValueMapValueorg.apache.sling.models.annotations.injectorspecific.OSGiServiceorg.apache.sling.models.annotations.injectorspecific.SlingObjectinjectionStrategy = InjectionStrategy.OPTIONAL:
org.apache.sling.models.annotations.injectorspecific.InjectionStrategy (skip if already imported)javax.inject.Inject — if no @Inject remainsjavax.inject.Named — if no @Named remainsorg.apache.sling.models.annotations.Optional — if no @Optional remains (field-level)Edit imports one line at a time — never as a contiguous block. Each import …; statement is a
unique line; anchor every add/remove on that single line. Do not match or rewrite a multi-line
"import block": real files interleave unrelated imports (e.g. org.slf4j.Logger sitting between the
javax.inject and injectorspecific groups), so a block-shaped anchor is not unique and the surgical
edit fails or misfires.
import javax.inject.Inject; (and the other unused lines) by that
full line — one removal per import.import …; as its own line — one insertion per import.Place each new import to best-effort match the file's existing ordering convention (most AEM projects order by package), but the placement is advisory — correctness never depends on imports being grouped or contiguous. Tolerate interleaved unrelated imports.
If any @Inject-annotated field in the file fails to match a decision-table row, or carries a companion annotation not in the Companion Annotations table, skip the entire file. Do not apply partial migrations. Rationale: a half-migrated file where some @Injects were replaced and others were not produces a diff the reviewer cannot efficiently distinguish from intentional mixed usage.
Common ambiguous cases that trigger skip-file:
List<X>, Collection<X>, arrays of non-ValueMap types — likely @ChildResource, not currently supportedorg.apache.sling.* / com.day.cq.* types other than the four @SlingObject types in the decision table — e.g. com.day.cq.wcm.api.Page, Component, Style, or another Sling Model — these need @ScriptVariable, @Self (model-to-model) adaptation, or @ChildResource depending on the exact type; not currently disambiguated@Inject on constructor parameters or setter methods — not field injectionObject or generic type parameters (e.g. T)@Source)When skip-file triggers, record the skip in the run's skip-report with reason:
inject-ambiguous-field: <relative-file-path> has @Inject on <fieldName> of type <fieldType> [with companion <annotation>] which does not match any decision-table ruleContinue with the remaining files; the entire skipped file is left untouched on disk.
For each file in findings[].files:
@Inject. For each field, consult the Decision Table and Companion Annotations sections and compute the replacement annotation and companion adjustments.@Model updates.After all files are processed, continue the Verify & summarize step in ../references/runbook.md.
A class mixing all three patterns, plus @Optional and @Named:
Before (core/src/main/java/com/example/core/models/HeroModel.java):
package com.example.core.models;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Optional;
import com.example.core.services.ContentService;
@Model(adaptables = SlingHttpServletRequest.class)
public class HeroModel {
@Inject
private String title;
@Inject
@Named("cta-label")
private String ctaLabel;
@Inject
@Optional
private String subtitle;
@Inject
private ContentService contentService;
@Inject
private Resource resource;
// ...
}After:
package com.example.core.models;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import com.example.core.services.ContentService;
@Model(adaptables = SlingHttpServletRequest.class)
public class HeroModel {
@ValueMapValue
private String title;
@ValueMapValue(name = "cta-label")
private String ctaLabel;
@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
private String subtitle;
@OSGiService
private ContentService contentService;
@SlingObject
private Resource resource;
// ...
}What changed, in order:
javax.inject.Inject, javax.inject.Named, and org.apache.sling.models.annotations.Optional imports removed (no remaining usages).ValueMapValue, OSGiService, SlingObject, and InjectionStrategy (for the optional field).@Model is unchanged — the optional field is handled per-field, so previously-required fields stay required.@Named("cta-label") was merged into @ValueMapValue(name = "cta-label").title (String) → @ValueMapValue.subtitle (String, was @Optional) → @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) — optional preserved on this field only.contentService (interface from an application package com.example.core.services) → @OSGiService.resource (org.apache.sling.api.resource.Resource) → @SlingObject — resolves for both request- and resource-adapted models (@Self would return null here).Use a surgical text-level replace, not a source reformatter, to preserve the developer's exact formatting, comments, and import order.
Concretely, per field site:
@Annotation[(...)] lines that sit on consecutive lines ending at the field declaration line.@Default if present, then @Via if present).Then, once all field sites in the file are rewritten:
@Model(...) for @Optional; the optional field carries injectionStrategy = InjectionStrategy.OPTIONAL on its own annotation instead.import …; line and insert each required import as its own line. Do not anchor on a
multi-line import block — interleaved unrelated imports (e.g. Logger) make a block anchor
non-unique and the edit fails.If at any point the transform for a field does not cleanly fit (e.g. the annotation block contains an unexpected companion, or the annotation/field block does not match the form described above — single-line annotations on consecutive lines ending at the field declaration), trigger skip-file for the whole file — per policy.
.tessl-plugin
skills
aem-rde
aem-workflow
code-assessment
content-distribution
create-component
assets
references
dispatcher
incident-response
references
dispatcher-foundation
performance-tuning
references
dispatcher-foundation
security-hardening
references
dispatcher-foundation
technical-advisory
references
dispatcher-foundation
workflow-orchestrator
references
dispatcher-foundation
ensure-agents-md
references
migration
references
dispatcher
legacy-ui
template-modernization
validation
scripts
dispatcher-tool
validate-migration