---
name: "AEM"
description: "Always-on rules for AEM as a Cloud Service Java bundles, HTL components, OSGi configs, front-end clientlibs and dispatcher configuration."
applyTo: "**/core/src/**/*.java,**/ui.apps/**,**/ui.config/**,**/ui.frontend/**,**/dispatcher/**"
---

# AEM rules

Write for AEM as a Cloud Service first: immutable `/apps`, no admin sessions, auto-scaled disposable publish pods.

## HTL and components

- Never use `@ context='unsafe'`. It disables XSS protection and is a review blocker.
- Use the right XSS context: `context='html'` for RTE output, `'uri'` for links, `'attribute'`/`'text'` where the default is not inferred, `'scriptString'`/`'styleString'` inside `<script>`/`<style>`.
- Keep logic out of HTL: no string-built markup or URLs; compute them in the Sling Model.
- Build standard elements as proxies of a pinned Core Component version (`sling:resourceSuperType=".../v3/..."`); never reference `core/wcm/components` directly from content.
- Use the Style System for visual variants, not new components or dialog dropdowns.
- Set allowed components on the layout container policy, not in code.

## Sling Models

- Adapt component models from `SlingHttpServletRequest`, expose an interface, and keep the `Impl` in an internal package.
- Use injector annotations (`@ValueMapValue`, `@ChildResource`, `@OSGiService`) with `DefaultInjectionStrategy.OPTIONAL`, and handle missing values. Authors leave fields empty.
- Keep `@PostConstruct` cheap: no queries, remote calls or writes during render.
- Extend Core Components through delegation (`@Via(type = ResourceSuperType.class)`), not by copying their code.

## OSGi services and access

- Components are singletons: no request state in fields; `ResourceResolver` and `Session` are not thread-safe.
- Never use `getAdministrativeResourceResolver` or `loginAdministrative`. Use a service user mapping with least-privilege ACLs defined in repoinit.
- Close every service resolver you open (try-with-resources). Leaked sessions degrade the instance.
- Configure services with `@Designate`/`@ObjectClassDefinition` and `.cfg.json` in `ui.config` under the fixed runmodes (`config.author`, `config.publish.prod`, ...).
- Keep secrets out of Git: use `$[secret:NAME]` and `$[env:NAME]` set via Cloud Manager variables.
- Register servlets by resource type (`@SlingServletResourceTypes`), not by path; use `SlingSafeMethodsServlet` for GET.
- Avoid unindexed queries; add a versioned `<name>-custom-<n>` Oak index in code when a query needs one.
- Run long or scheduled work as Sling Jobs, and make it safe to run on any pod; never write `/apps` or `/libs` at runtime.

## Dispatcher

- Start filters from deny-all (`/0001 { /type "deny" /url "*" }`) and allow only needed paths, extensions and selectors.
- Never allow `/crx`, `/system`, `/bin` wholesale, `.infinity.json`, `.query.json` or unrestricted selectors on publish.
- Cache static and clientlib content with long TTLs; set invalidation rules deliberately and never cache personalized or authenticated responses.
- Validate changes with the SDK dispatcher validator before pushing; Cloud Manager rejects invalid configs.

## Front end and clientlibs

- Set `allowProxy="{Boolean}true"` on clientlibs and reference them via `/etc.clientlibs`; publish denies reads on `/apps`.
- Embed component clientlibs into the site clientlib; do not include them per component in HTL.
- Load CSS in `<head>` and JS with `defer`; keep author-only code in `cq.authoring.*` categories.
- Never hand-edit generated clientlib folders; change sources in `ui.frontend`.
- Make scripts work in the page editor: initialize on DOM content, tolerate components being added or removed.

Go deeper: for larger tasks use the aem-component-development, aem-backend-development, aem-frontend-development, aem-dispatcher, aem-testing and aem-code-review skills.
