From 2d70f97518d08cc9ae9bc1f12d486e216e571855 Mon Sep 17 00:00:00 2001 From: Himani Chauhan Date: Thu, 25 Jun 2026 23:13:09 +0530 Subject: [PATCH 1/3] docs(aem-cloud-service): add Discovery + Resolution contract to the 5 guided patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The architectural patterns moved from best-practices (scheduler, resource-change-listener, replication, event-migration, asset-manager) lacked the two sections that feed the code-assessment report table — so a discover+report run rendered them as bare file lists while the mechanical patterns (outbound-call-timeouts, unbounded-query) showed File | Finding | Planned action. Each of the 5 now mirrors the mechanical-pattern skeleton: - ## Discovery — analyze.sh --pattern command + match criteria that mirror the merged detector logic exactly (Job/RCL/EventHandler/EventListener implements, OSGi-property scheduler, Replicator import → one finding per file, AssetManager calls → one finding per call site) and the emitted snippet. Drives the Finding column. - ## Resolution contract — labeled `guided`, with a disposition table mapping each site shape to `migrate (guided) → ` or a skip reason (already-compliant, test-scope, workflow-owned, wrong-pattern). Drives the Planned action column. Cross-pattern nuances captured inline: replication is analyzer-only (no BPA subtype); event-migration routes resource-topic handlers to resource-change-listener as skipped: wrong-pattern; RCL notes JCR EventListener is detected under event-migration per the BPA taxonomy. runbook.md: - Candidates: same columns for every pattern (mechanical and guided); Planned action vocabulary keyed off the catalog `fix` field (mechanical → apply/skipped; guided → migrate (guided)); never collapse a guided pattern to a file list. - Step 2: acknowledge guided patterns may carry steps inline (no recipe.md/path-*.md) and read the Discovery + Resolution contract sections. No detector/code changes. Test suite 113/113. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../code-assessment/asset-manager/SKILL.md | 24 +++++++++++++++ .../code-assessment/event-migration/SKILL.md | 26 +++++++++++++++++ .../code-assessment/references/runbook.md | 12 ++++++-- .../code-assessment/replication/SKILL.md | 24 +++++++++++++++ .../resource-change-listener/SKILL.md | 25 ++++++++++++++++ .../skills/code-assessment/scheduler/SKILL.md | 29 +++++++++++++++++++ 6 files changed, 138 insertions(+), 2 deletions(-) diff --git a/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md index 70b3e5cf..32dd7aa5 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md @@ -56,6 +56,30 @@ license: Apache-2.0 --- +## Discovery + +Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook: + +```bash +bash ../scripts/analyze.sh --pattern asset-manager +``` + +**Match criteria (what the detector flags):** in a file importing **`com.day.cq.dam.api.AssetManager`**, a call to **`createAssetForBinary`**, **`getAssetForBinary`**, **`removeAssetForBinary`**, or **`createAsset`**. **One finding per call site** (each call is individually actionable, unlike the class-level patterns), with the call as the snippet. Parse-level only — gated on the import; the receiver type is not resolved, so an unrelated `createAsset(...)` in the same file could match (rare). + +## Resolution contract + +**guided** — `migrate (guided)`. The analyzer reports each legacy call site; remediation is judgment-based, routed by the call (create/upload → C1–C3, delete → D1–D3) and applied in an apply session. + +| Call site | Disposition | +|---|---| +| `createAssetForBinary` / `getAssetForBinary` (removed on CS) | migrate (guided) → C1–C3 | +| `removeAssetForBinary` (removed on CS) | migrate (guided) → D1–D3 | +| Client-facing `createAsset(...)` upload | migrate (guided) → C1–C3 (Direct Binary Access) | +| In-JVM back-office `createAsset(...)` with a service-user resolver | skipped: `already-compliant` | +| Test code (`src/test/`) | skipped: `test-scope` | + +--- + ## Complete example — Path A (create / upload) ### Before (client-facing upload via `AssetManager.createAsset`) diff --git a/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md index 718560fb..379cb4bf 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md @@ -75,6 +75,32 @@ A typo'd topic string compiles fine and silently subscribes to a topic that noth --- +## Discovery + +Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook: + +```bash +bash ../scripts/analyze.sh --pattern event-migration +``` + +**Match criteria (what the detector flags):** a class that **`implements org.osgi.service.event.EventHandler`** or **`javax.jcr.observation.EventListener`** (import-aware) — both map here per the BPA subtype taxonomy. + +Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; the subscribed topic (resource vs non-resource) is not resolved at detection, so topic-based routing to `resource-change-listener` happens during remediation (see Routing / Classification). + +## Resolution contract + +**guided** — `migrate (guided)`. The analyzer locates each handler/listener; remediation is judgment-based and applied via E0–E5 (per the Classification above) in an apply session. + +| Site shape | Disposition | +|---|---| +| `EventHandler` on a non-resource topic (replication / workflow / custom), `handleEvent()` does heavy work | migrate (guided) → E1–E5 | +| Legacy `javax.jcr.observation.EventListener` that cannot be a `ResourceChangeListener` | migrate (guided) → E0 then E1–E5 | +| Handler observes repository content / a resource topic (`org/apache/sling/api/resource/Resource/*`) | skipped: `wrong-pattern` (use `resource-change-listener`) | +| `EventHandler` on a non-resource topic, `handleEvent()` only enqueues a Sling Job | skipped: `already-compliant` | +| Test code (`src/test/`) | skipped: `test-scope` | + +--- + ## Complete example — before and after ### Before (replication EventHandler with inline business logic) diff --git a/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md b/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md index fbf91be9..a307af1a 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md +++ b/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md @@ -51,7 +51,7 @@ Match the request to one expert skill under `code-assessment//` using t ### 2. Read reference module -Read the chosen expert skill's `SKILL.md` then its `recipe.md` (or `path-*.md`) in full — **Discovery**, input contract, recipe, Unlocatable, editing strategy. +Read the chosen expert skill's `SKILL.md` in full, then its `recipe.md` / `path-*.md` if present (guided patterns may carry the migration steps inline instead) — **Discovery**, **Resolution contract**, input contract, recipe/steps, Unlocatable, editing strategy. ### 3. Resolve findings (with_findings or discover) @@ -181,7 +181,15 @@ Copy this structure; keep section headings so runs are comparable across session ## Candidates | File | Finding | Planned action | Target / notes | |------|---------|----------------|----------------| -| | | apply \| skipped | | +| : | | apply \| skipped \| migrate (guided) | | + +**Same columns for every pattern — mechanical *and* guided.** `Finding` is the analyzer `snippet` +(present for every pattern). `Planned action` follows the pattern's catalog `fix`: `fix: mechanical` +→ `apply` or `skipped — `; `fix: guided` → `migrate (guided)` (the analyzer locates the +site; remediation opens the pattern's expert guide — name it in *Target / notes*). When several +patterns are in play, group rows under per-pattern subheadings ordered by `severity`, but **never +drop a column or render a guided pattern as a bare file list** — guided patterns carry concrete +findings exactly like mechanical ones. ## Summary counts - Apply: diff --git a/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md index 32d7f8a7..4350a22c 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md @@ -53,6 +53,30 @@ Identify the source pattern in the file: --- +## Discovery + +Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook: + +```bash +bash ../scripts/analyze.sh --pattern replication +``` + +**Match criteria (what the detector flags):** a file that imports **`com.day.cq.replication.Replicator`** or any **`org.apache.sling.replication.*`** type — the legacy replication APIs removed on Cloud Service. One finding per file, at the file's primary type, with the class header as the snippet. Parse-level only — import-based, no type resolution. The modern `org.apache.sling.distribution.*` API is not flagged. + +> Analyzer-only: `replication` has no BPA subtype, so a `replication` finding originates from the local analyzer, never from a BPA/CAM report. + +## Resolution contract + +**guided** — `migrate (guided)`. The analyzer locates each legacy replication caller; remediation is judgment-based (CQ `Replicator` / Sling Replication Agent → Sling Distribution API) and applied via P1–P4 in an apply session. + +| Site shape | Disposition | +|---|---| +| `Replicator` / Sling Replication Agent usage in custom code | migrate (guided) → P1–P4 | +| Replication tied to a workflow step (workflow owns it) | skipped: `workflow-owned` | +| Test code (`src/test/`) | skipped: `test-scope` | + +--- + ## Complete example — before and after ### Before (legacy CQ Replicator with admin resolver) diff --git a/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md index 370e39ba..08cf1d17 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md @@ -53,6 +53,31 @@ Three CS-specific constraints every listener must satisfy: --- +## Discovery + +Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook: + +```bash +bash ../scripts/analyze.sh --pattern resource-change-listener +``` + +**Match criteria (what the detector flags):** a class that **`implements org.apache.sling.api.resource.observation.ResourceChangeListener`** or **`ExternalResourceChangeListener`** (import-aware) — the modern API, flagged for review against the lightweight + JobConsumer contract. + +Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; reached-via-base-class is not resolved. Legacy `javax.jcr.observation.EventListener` is detected by the `event-migration` pattern (matching the BPA subtype taxonomy); when its logic is plain content observation, that guide routes it back here. + +## Resolution contract + +**guided** — `migrate (guided)`. The analyzer locates each `ResourceChangeListener`; remediation is judgment-based and applied via R0–R5 (per the Classification above) in an apply session. + +| Site shape | Disposition | +|---|---| +| `implements ResourceChangeListener`, `onChange()` does repository/JCR/heavy work | migrate (guided) → R1–R5 | +| Legacy `javax.jcr.observation.EventListener` / resource-topic `EventHandler` routed here | migrate (guided) → R0 then R1–R5 | +| `implements ResourceChangeListener`, `onChange()` only enqueues a Sling Job | skipped: `already-compliant` | +| Test code (`src/test/`) | skipped: `test-scope` | + +--- + ## Complete example — before and after ### Before (legacy JCR `EventListener` with inline logic and admin resolver) diff --git a/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md index 0e865a22..062ec88d 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md @@ -43,6 +43,35 @@ Three properties control every scheduler: --- +## Discovery + +Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook: + +```bash +bash ../scripts/analyze.sh --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** — `migrate (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` | migrate (guided) → path-a.md | +| Config-driven cron, multiple schedules, `implements Job`, or `ScheduleOptions.config()` | migrate (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. From 227b023893290aad237dd70ee42d57106eccadd5 Mon Sep 17 00:00:00 2001 From: Himani Chauhan Date: Fri, 26 Jun 2026 00:08:00 +0530 Subject: [PATCH 2/3] fix(aem-cloud-service): Planned action from each pattern's Resolution contract; rename to apply (guided) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes to the guided-pattern report rendering: 1. Regression: the runbook Candidates note keyed Planned action off the catalog `fix` field (`fix: guided` → one blanket label). That overrode unbounded-query — itself `fix: guided` but with a per-site triage — flattening its `apply → 1` / `skipped — needs-pagination` / `skipped — test-scope` rows into a uniform label, and (worse) turning test-scope skips into apply candidates. Planned action now comes from each pattern's own Resolution contract; the `fix` field no longer dictates a blanket action. 2. Rename `migrate (guided)` → `apply (guided)` across the runbook note and all five architectural Resolution contracts. Keeps one action vocabulary (`apply` / `apply → N` / `apply (guided)` / `skipped — `), ties to the `apply ` command users type, and avoids overloading "migrate" (the name of the sibling skill); fits resource-change-listener (review/refactor) too. No detector/code changes. Test suite 103/103. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../code-assessment/asset-manager/SKILL.md | 8 ++++---- .../code-assessment/event-migration/SKILL.md | 6 +++--- .../code-assessment/references/runbook.md | 17 ++++++++++------- .../skills/code-assessment/replication/SKILL.md | 4 ++-- .../resource-change-listener/SKILL.md | 6 +++--- .../skills/code-assessment/scheduler/SKILL.md | 6 +++--- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md index 32dd7aa5..6d0e272e 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/asset-manager/SKILL.md @@ -68,13 +68,13 @@ bash ../scripts/analyze.sh --pattern asset-manager ## Resolution contract -**guided** — `migrate (guided)`. The analyzer reports each legacy call site; remediation is judgment-based, routed by the call (create/upload → C1–C3, delete → D1–D3) and applied in an apply session. +**guided** — `apply (guided)`. The analyzer reports each legacy call site; remediation is judgment-based, routed by the call (create/upload → C1–C3, delete → D1–D3) and applied in an apply session. | Call site | Disposition | |---|---| -| `createAssetForBinary` / `getAssetForBinary` (removed on CS) | migrate (guided) → C1–C3 | -| `removeAssetForBinary` (removed on CS) | migrate (guided) → D1–D3 | -| Client-facing `createAsset(...)` upload | migrate (guided) → C1–C3 (Direct Binary Access) | +| `createAssetForBinary` / `getAssetForBinary` (removed on CS) | apply (guided) → C1–C3 | +| `removeAssetForBinary` (removed on CS) | apply (guided) → D1–D3 | +| Client-facing `createAsset(...)` upload | apply (guided) → C1–C3 (Direct Binary Access) | | In-JVM back-office `createAsset(...)` with a service-user resolver | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` | diff --git a/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md index 379cb4bf..2cee4cec 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md @@ -89,12 +89,12 @@ Emitted at the class declaration, with the class header as the snippet. Parse-le ## Resolution contract -**guided** — `migrate (guided)`. The analyzer locates each handler/listener; remediation is judgment-based and applied via E0–E5 (per the Classification above) in an apply session. +**guided** — `apply (guided)`. The analyzer locates each handler/listener; remediation is judgment-based and applied via E0–E5 (per the Classification above) in an apply session. | Site shape | Disposition | |---|---| -| `EventHandler` on a non-resource topic (replication / workflow / custom), `handleEvent()` does heavy work | migrate (guided) → E1–E5 | -| Legacy `javax.jcr.observation.EventListener` that cannot be a `ResourceChangeListener` | migrate (guided) → E0 then E1–E5 | +| `EventHandler` on a non-resource topic (replication / workflow / custom), `handleEvent()` does heavy work | apply (guided) → E1–E5 | +| Legacy `javax.jcr.observation.EventListener` that cannot be a `ResourceChangeListener` | apply (guided) → E0 then E1–E5 | | Handler observes repository content / a resource topic (`org/apache/sling/api/resource/Resource/*`) | skipped: `wrong-pattern` (use `resource-change-listener`) | | `EventHandler` on a non-resource topic, `handleEvent()` only enqueues a Sling Job | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` | diff --git a/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md b/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md index a307af1a..01d8bb90 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md +++ b/plugins/aem/cloud-service/skills/code-assessment/references/runbook.md @@ -181,15 +181,18 @@ Copy this structure; keep section headings so runs are comparable across session ## Candidates | File | Finding | Planned action | Target / notes | |------|---------|----------------|----------------| -| : | | apply \| skipped \| migrate (guided) | | +| : | | apply \| skipped \| apply (guided) | | **Same columns for every pattern — mechanical *and* guided.** `Finding` is the analyzer `snippet` -(present for every pattern). `Planned action` follows the pattern's catalog `fix`: `fix: mechanical` -→ `apply` or `skipped — `; `fix: guided` → `migrate (guided)` (the analyzer locates the -site; remediation opens the pattern's expert guide — name it in *Target / notes*). When several -patterns are in play, group rows under per-pattern subheadings ordered by `severity`, but **never -drop a column or render a guided pattern as a bare file list** — guided patterns carry concrete -findings exactly like mechanical ones. +(present for every pattern). `Planned action` is the **per-site disposition from that pattern's own +Resolution contract** — `apply`, `apply → N`, `skipped — ` (e.g. `needs-pagination`, +`test-scope`, `already-compliant`), or `apply (guided)` for architectural patterns whose +Resolution contract has no finer per-site action (remediation opens the expert guide — name it in +*Target / notes*). **Do not collapse a pattern to one blanket action from its `fix` kind:** a +`fix: guided` pattern that triages sites (e.g. `unbounded-query`: bound some, skip others) shows +that triage per row, not a uniform `apply (guided)`. When several patterns are in play, group rows +under per-pattern subheadings ordered by `severity`; never drop a column or render a pattern as a +bare file list. ## Summary counts - Apply: diff --git a/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md index 4350a22c..c350f61b 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/replication/SKILL.md @@ -67,11 +67,11 @@ bash ../scripts/analyze.sh --pattern replication ## Resolution contract -**guided** — `migrate (guided)`. The analyzer locates each legacy replication caller; remediation is judgment-based (CQ `Replicator` / Sling Replication Agent → Sling Distribution API) and applied via P1–P4 in an apply session. +**guided** — `apply (guided)`. The analyzer locates each legacy replication caller; remediation is judgment-based (CQ `Replicator` / Sling Replication Agent → Sling Distribution API) and applied via P1–P4 in an apply session. | Site shape | Disposition | |---|---| -| `Replicator` / Sling Replication Agent usage in custom code | migrate (guided) → P1–P4 | +| `Replicator` / Sling Replication Agent usage in custom code | apply (guided) → P1–P4 | | Replication tied to a workflow step (workflow owns it) | skipped: `workflow-owned` | | Test code (`src/test/`) | skipped: `test-scope` | diff --git a/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md index 08cf1d17..583aa16b 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md @@ -67,12 +67,12 @@ Emitted at the class declaration, with the class header as the snippet. Parse-le ## Resolution contract -**guided** — `migrate (guided)`. The analyzer locates each `ResourceChangeListener`; remediation is judgment-based and applied via R0–R5 (per the Classification above) in an apply session. +**guided** — `apply (guided)`. The analyzer locates each `ResourceChangeListener`; remediation is judgment-based and applied via R0–R5 (per the Classification above) in an apply session. | Site shape | Disposition | |---|---| -| `implements ResourceChangeListener`, `onChange()` does repository/JCR/heavy work | migrate (guided) → R1–R5 | -| Legacy `javax.jcr.observation.EventListener` / resource-topic `EventHandler` routed here | migrate (guided) → R0 then R1–R5 | +| `implements ResourceChangeListener`, `onChange()` does repository/JCR/heavy work | apply (guided) → R1–R5 | +| Legacy `javax.jcr.observation.EventListener` / resource-topic `EventHandler` routed here | apply (guided) → R0 then R1–R5 | | `implements ResourceChangeListener`, `onChange()` only enqueues a Sling Job | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` | diff --git a/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md index 062ec88d..856b3667 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/scheduler/SKILL.md @@ -61,12 +61,12 @@ Emitted at the class declaration, with the class header as the snippet. Parse-le ## Resolution contract -**guided** — `migrate (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. +**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` | migrate (guided) → path-a.md | -| Config-driven cron, multiple schedules, `implements Job`, or `ScheduleOptions.config()` | migrate (guided) → path-b.md | +| 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` | From 28b99dc3f10bb4a10f9ca41443922e30c3fb6f00 Mon Sep 17 00:00:00 2001 From: Himani Chauhan Date: Fri, 26 Jun 2026 00:16:55 +0530 Subject: [PATCH 3/3] docs(aem-cloud-service): disambiguate EventListener/EventHandler/RCL routing in Resolution contracts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detection is unambiguous (each interface maps to exactly one detector), but the fix-routing across the three observation patterns relied on prose to disambiguate. Make the Resolution-contract tables self-disambiguating: - event-migration: reorder rows so "observes repository content" (JCR EventListener on content, or EventHandler on a resource topic) comes first and routes to resource-change-listener; add an explicit "first match wins" note; mark the E0 path as the rare residual fall-through ("genuinely cannot be modeled as a ResourceChangeListener"), not a competing option. - resource-change-listener: annotate the R0 row as redirect-only — it arrives via event-migration redirect or migration handoff; this pattern's own detector flags only the modern API. - Both: note that a class implementing both ResourceChangeListener and an event interface is flagged by both patterns (rare). Doc-only; no detector/code changes. Test suite 103/103. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/code-assessment/event-migration/SKILL.md | 8 ++++---- .../code-assessment/resource-change-listener/SKILL.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md index 2cee4cec..8f9d6159 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/event-migration/SKILL.md @@ -85,17 +85,17 @@ bash ../scripts/analyze.sh --pattern event-migration **Match criteria (what the detector flags):** a class that **`implements org.osgi.service.event.EventHandler`** or **`javax.jcr.observation.EventListener`** (import-aware) — both map here per the BPA subtype taxonomy. -Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; the subscribed topic (resource vs non-resource) is not resolved at detection, so topic-based routing to `resource-change-listener` happens during remediation (see Routing / Classification). +Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; the subscribed topic (resource vs non-resource) is not resolved at detection, so topic-based routing to `resource-change-listener` happens during remediation (see Routing / Classification). A class implementing both an event interface and `ResourceChangeListener` is flagged by both patterns (rare). ## Resolution contract -**guided** — `apply (guided)`. The analyzer locates each handler/listener; remediation is judgment-based and applied via E0–E5 (per the Classification above) in an apply session. +**guided** — `apply (guided)`. The analyzer locates each handler/listener; remediation is judgment-based and applied via E0–E5 (per the Classification above) in an apply session. **Rows are evaluated top-down — first match wins**, so resource/content observation routes to `resource-change-listener` *before* the E0 fall-through is considered. | Site shape | Disposition | |---|---| +| Observes repository content — a `javax.jcr.observation.EventListener` watching content, or an `EventHandler` on a resource topic (`org/apache/sling/api/resource/Resource/*`) | skipped: `wrong-pattern` (use `resource-change-listener`) | | `EventHandler` on a non-resource topic (replication / workflow / custom), `handleEvent()` does heavy work | apply (guided) → E1–E5 | -| Legacy `javax.jcr.observation.EventListener` that cannot be a `ResourceChangeListener` | apply (guided) → E0 then E1–E5 | -| Handler observes repository content / a resource topic (`org/apache/sling/api/resource/Resource/*`) | skipped: `wrong-pattern` (use `resource-change-listener`) | +| Legacy `javax.jcr.observation.EventListener` that **genuinely cannot** be modeled as a `ResourceChangeListener` (rare residual) | apply (guided) → E0 then E1–E5 | | `EventHandler` on a non-resource topic, `handleEvent()` only enqueues a Sling Job | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` | diff --git a/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md b/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md index 583aa16b..69ef81b3 100644 --- a/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md +++ b/plugins/aem/cloud-service/skills/code-assessment/resource-change-listener/SKILL.md @@ -63,7 +63,7 @@ bash ../scripts/analyze.sh --pattern resource-change-listener **Match criteria (what the detector flags):** a class that **`implements org.apache.sling.api.resource.observation.ResourceChangeListener`** or **`ExternalResourceChangeListener`** (import-aware) — the modern API, flagged for review against the lightweight + JobConsumer contract. -Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; reached-via-base-class is not resolved. Legacy `javax.jcr.observation.EventListener` is detected by the `event-migration` pattern (matching the BPA subtype taxonomy); when its logic is plain content observation, that guide routes it back here. +Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; reached-via-base-class is not resolved. Legacy `javax.jcr.observation.EventListener` is detected by the `event-migration` pattern (matching the BPA subtype taxonomy); when its logic is plain content observation, that guide routes it back here. A class implementing both `ResourceChangeListener` and an event interface is flagged by both patterns (rare). ## Resolution contract @@ -72,7 +72,7 @@ Emitted at the class declaration, with the class header as the snippet. Parse-le | Site shape | Disposition | |---|---| | `implements ResourceChangeListener`, `onChange()` does repository/JCR/heavy work | apply (guided) → R1–R5 | -| Legacy `javax.jcr.observation.EventListener` / resource-topic `EventHandler` routed here | apply (guided) → R0 then R1–R5 | +| Legacy `javax.jcr.observation.EventListener` / resource-topic `EventHandler` routed here *(arrives via `event-migration` redirect or migration handoff — this pattern's own detector flags only the modern API)* | apply (guided) → R0 then R1–R5 | | `implements ResourceChangeListener`, `onChange()` only enqueues a Sling Job | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` |