Skip to content

feat(aem-cloud-service): add vault-package-dependencies analyzer detector + expert skill - #330

Merged
Himanich merged 12 commits into
mainfrom
feat/vault-package-dependencies-pattern
Sep 18, 2026
Merged

Himanich merged 12 commits into
mainfrom
feat/vault-package-dependencies-pattern

Conversation

@bharat941

Copy link
Copy Markdown
Contributor

Adds the vault-package-dependencies code-assessment pattern — surface missing <dependencies> inside filevault-package-maven-plugin (or content-package-maven-plugin) configurations that block AEM as a Cloud Service package deploys. Follows the references/adding-a-pattern.md procedure; the slug vault-package-dependencies is wired into all four required places so the [wiring] test stays green.

Changes

  • Detector — scripts/analyzer/detectors/VaultPackageDependencies.java, registered in Registry.all(). Pom-only, mechanical fix; flags vault/content-package plugin configurations without a <dependencies> block.
  • Expert skill — vault-package-dependencies/SKILL.md (control plane) + vault-package-dependencies/recipe.md (mechanical fix: add <dependencies> block with correct group/artifact refs).
  • Catalog + routing — references/patterns.md row (high | ready | analyzer | mechanical) and Manual Pattern Hints row in code-assessment/SKILL.md.
  • Migration deploy-blocker note — migration/SKILL.md entry pointing at the new pattern.
  • Fixtures + test — test/code-assessment/fixtures/vault-package-dependencies/ (legacy pom missing dependencies) and .../vault-package-dependencies-clean/ (correct pom) + a block in run-tests.sh.
  • Removes the duplicate standalone skills/vault-package-dependencies/ skill superseded by the code-assessment version.

Test Plan

run-tests.sh — all blocks PASS (incl. [wiring] and the new [vault-package-dependencies] block). Detector flags vault/content-package plugin configs missing <dependencies>; does not flag the clean fixture. End-to-end on a real legacy AEM project: analyzer detects → recipe transform applied → analyzer re-scan clean → mvn package builds and the resulting content package deploys on AEMaaCS.

…nt pattern

- Add VaultPackageDependencies detector (pom-only, mechanical fix)
- Add expert skill SKILL.md + recipe.md under code-assessment/
- Register detector in Registry.java
- Add routing hint to code-assessment/SKILL.md
- Add catalog row (high severity, ready, analyzer, mechanical) to patterns.md
- Add vault deploy blocker entry to migration/SKILL.md
- Add positive + negative test fixtures
- Add test cases to run-tests.sh
- Remove duplicate standalone skills/vault-package-dependencies/ skill
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Tessl Plugin Lint

⚠️ edge-delivery-services — 2 warning(s)
⚠ Skill 'code-review': SKILL.md is approximately 10352 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'figma-to-content': SKILL.md is approximately 14744 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.

✔ Plugin adobe/aem-edge-delivery-services@0.1.0 is valid
⚠️ app-management — 2 warning(s)
⚠ Skill 'commerce-app-storage': SKILL.md is approximately 5451 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'commerce-app-admin-ui': SKILL.md is approximately 5915 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.

✔ Plugin adobe/commerce-app-management@1.4.0 is valid

app-migration — clean

app-review — clean

run-workflow — clean

⚠️ stardust — 11 warning(s)
⚠ Skill 'stardust': SKILL.md is approximately 5687 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'extract': SKILL.md is approximately 10095 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'direct': SKILL.md is approximately 12045 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'prototype': SKILL.md is approximately 18059 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'migrate': SKILL.md is approximately 6614 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'uplift': SKILL.md is approximately 6612 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'audit': SKILL.md is approximately 5433 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'replica': SKILL.md is approximately 5422 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'reskin': SKILL.md is approximately 6011 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'deploy': SKILL.md is approximately 46450 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.
⚠ Skill 'rollout': SKILL.md is approximately 7030 tokens (recommended maximum: 5000). Consider moving detailed content to separate reference files.

✔ Plugin adobe/stardust@0.21.1 is valid

✅ All 6 plugin(s) lint passed with 15 warning(s) total.

Updated by tessl-lint for commit 589caa2.

…core

Address tessl-review content-judge feedback (was 3/5, target 3.5+):
- Inline analyze.sh invocations under Findings sources for actionability.
- Add a 5-step inline checkpoint loop (detect/plan/apply/validate/retry) to
  the Routing section for workflow clarity.
- Remove the third restatement of the remove-deprecated-api / Maven-Central
  exception in Critical rules — cross-reference the first bullet instead
  of restating the preflight detail.

No semantic change; the runbook still owns the full flow.
…ration

"Package install fails on AEMaaCS" is a migration blocker, but the pattern
was only reachable via code-assessment directly — migration's analyzer
cascade silently dropped its findings because analyzer-runner.js's
ANALYZER_TO_CANONICAL allowlist didn't include it (same bucket as
inject-in-sling-model/outdated-dependencies, which are correctly excluded
since they're not migration-relevant).

Unlike every other cascade pattern, there is no BPA subtype for this at all —
day/cq60/product Vault install-time dependencies live in pom.xml, which a
deployed-artifact BPA scan can never see (confirmed against a real 64k-row
BPA report: zero hits). So this is wired as `strategy: 'cascade'` with an
empty `bpaSlugs` — the BPA tier is skipped entirely, straight to the
analyzer, same model as htlLint being BPA-less but still migration-routed.

- Add to ANALYZER_TO_CANONICAL so the migration cascade's analyzer tier
  stops silently dropping these findings.
- Add PATTERN_META entry, Quick start row, Branch B routing bullet, "When to
  Use This Skill" bullet, and Step 0 runbook table row in migration/SKILL.md.
- Verified end-to-end against the real analyzer + real fixture: gatherFindings
  now correctly sources it via 'analyzer' (not needsLlmScan).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same reasoning as guava-cache: day/cq60/product Vault install-time
dependencies never occur in native AEMaaCS code, only in pom.xml carried
over from legacy AEM 6.x — this is a migration-only concern, not a
code-assessment pattern, even though the previous commit gave it a full
analyzer detector + expert-skill folder there.

- Remove the expert skill, Java detector (Registry.java), and fixtures from
  code-assessment; revert SKILL.md/patterns.md/run-tests.sh hooks.
- Add migration/references/vault-package-dependencies.md (merged from the
  old SKILL.md + recipe.md content).
- Replace the analyzer detector with a pure-Node heuristic pom.xml scan
  (migration/scripts/vault-package-scan-runner.js), mirroring htlLint's
  html-scan model — no analyzer, no BPA subtype (confirmed against a real
  64k-row BPA report: zero hits for day/cq60/vault/content-package-maven-plugin).
  Verified byte-for-byte identical output to the removed Java detector
  against both real fixtures (antipattern + clean), plus new edge-case
  coverage (legacy group under an unrelated plugin correctly not flagged).
- Wire PATTERN_META (new 'pom-scan' strategy) + gatherFindings dispatch +
  migration/SKILL.md (Quick start, Branch B, Step 0 table). Removed the
  now-redundant "Vault deploy blocker" doc-only stub row that existed before
  this branch (never wired to any code) in favor of one real, wired entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adding the vault-package-dependencies mention pushed the frontmatter
description to 1033 chars, over skills-ref's 1024 limit. This broke both
the `validate` CI check and `tessl-review` (tessl validates the skill
before reviewing it, so it errored out rather than scoring it — "tooling
failure", not a low score). Trimmed redundant wording to 972 chars;
verified locally with `npx skills-ref validate`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Per explicit request: reverts the tessl-review score fix from earlier in
this session (Findings-sources rewording, Inline checkpoint loop,
remove-deprecated-api sentence tweak). This file had zero vault-package-
dependencies content — the revert removes unrelated content from this PR,
not anything about the pattern move.

Note: this was the fix for tessl-review's below-threshold score (77%,
needed 80%+) on this PR. Reverting it will likely reintroduce that failure
until it's fixed again, separately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…pendencies-pattern

# Conflicts:
#	plugins/aem/cloud-service/skills/migration/SKILL.md
#	plugins/aem/cloud-service/skills/migration/scripts/runbook-generator.js
#	plugins/aem/cloud-service/skills/migration/scripts/runbook-generator.test.js
…pendencies-pattern

# Conflicts:
#	plugins/aem/cloud-service/skills/migration/SKILL.md
#	plugins/aem/cloud-service/skills/migration/scripts/runbook-generator.test.js

@Himanich Himanich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes. The concept and placement (migration-only heuristic, no analyzer/BPA) are right, but the detector under-covers its stated surface and the PR description doesn't match the diff.

Blockers:

  1. Only content-package-maven-plugin is matched — filevault-package-maven-plugin poms are silently skipped, even though the title/doc claim both.
  2. Only the first plugin occurrence per pom is scanned; <profiles>/<pluginManagement> cases are missed.
  3. The PR description documents a Java detector, a code-assessment pattern/recipe, fixtures, and run-tests.sh blocks that aren't in this diff. It also states "run-tests.sh — all blocks PASS" and an end-to-end deploy test. Please rewrite the description to match what actually ships (a pure-Node pom-scan reference under migration, tested via runbook-generator.test.js) so reviewers aren't validating against absent code/tests.

No internal-detail or licensing concerns — only public AEM package coordinates, Apache-2.0 header present.

* Assumes non-nested <plugin>/<configuration> tags, true for Maven poms.
*/
function findVaultDependenciesBlock(content) {
const artifactIdx = content.indexOf('<artifactId>content-package-maven-plugin</artifactId>');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only matches the literal content-package-maven-plugin, but the PR title, the SKILL.md description, and this reference's own Classification bullet ("regardless of whether the plugin is com.day.jcr.vault or org.apache.jackrabbit filevault-package-maven-plugin") all claim both plugins are covered. Modern migrated projects use filevault-package-maven-plugin, so their deploy-blocking <dependencies> are never flagged.

Please match both artifactIds, e.g.:

const PLUGIN_ARTIFACTS = ['content-package-maven-plugin', 'filevault-package-maven-plugin'];

and add a filevault fixture to runbook-generator.test.js.

* <dependencies> substring, or null if this pom has no such plugin/config.
* Assumes non-nested <plugin>/<configuration> tags, true for Maven poms.
*/
function findVaultDependenciesBlock(content) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findVaultDependenciesBlock runs indexOf/lastIndexOf once and returns a single block, so only the first plugin occurrence is examined. This contradicts this PR's own reference doc, which says the plugin can appear in both <build> and one or more <profiles>, and whose checklist says "All occurrences removed — check both main <build> and any <profiles> sections."

Concrete miss: plugin declared first under <pluginManagement> (no <dependencies>) and again under <build> with the legacy block → the first match returns null and the real block is never found. Please iterate over every plugin occurrence and emit one finding per <dependencies> block (as the doc states), not one per file.

if (artifactIdx === -1) return null;

const pluginStart = content.lastIndexOf('<plugin>', artifactIdx);
const pluginEnd = content.indexOf('</plugin>', artifactIdx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pluginBlock.indexOf('<configuration>') grabs the first <configuration>, but plugins can carry per-<execution> configurations. If an <execution><configuration> precedes the plugin-level <configuration>, the block is sliced to the wrong config and its </configuration> truncates the search, dropping a legacy <dependencies> block in the plugin-level config. Please scope to the plugin-level configuration explicitly (or scan all configuration blocks within the plugin).

.map(m => m.replace(/<\/?group>/g, '').trim())
.find(isLegacyGroup);
if (!legacyGroup) continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line is derived from depsBlock.artifactIdx (the plugin's artifactId), not the <dependencies> block being flagged. In a large pom these can be far apart, which undercuts the "anchor on ≥3 lines of context around the <dependencies> block" guidance in the reference. Please return the block's own offset from findVaultDependenciesBlock and report that line.

- Match both content-package-maven-plugin and filevault-package-maven-plugin;
  modern migrated projects use the filevault artifactId and were silently
  skipped.
- Iterate every plugin occurrence per pom (pluginManagement, build, profiles)
  and emit one finding per <dependencies> block, not one per file.
- Scan every <configuration> block within each plugin occurrence so a
  per-<execution> <configuration> can no longer shadow the plugin-level one.
- Report the <dependencies> block's own line instead of the <artifactId>
  line, which can be far away in a large pom.
- Snippet now names the actual matched plugin.

Adds 4 test fixtures covering each of the above cases (45 tests, all pass).
@bharat941

Copy link
Copy Markdown
Contributor Author

Thanks @Himanich — all four points addressed in fcb34b5.

1. filevault-package-maven-plugin silently skipped — introduced PLUGIN_ARTIFACTS = ['content-package-maven-plugin', 'filevault-package-maven-plugin']; both are matched now. Added a filevault fixture.

2. Only first plugin occurrence per pom scannedfindVaultDependenciesBlock is now findVaultDependenciesBlocks (plural), returns an array, and iterates over every occurrence. The runner emits one finding per <dependencies> block, so <pluginManagement> + <build> + <profiles> all get considered. Added two fixtures: (a) <pluginManagement> occurrence first (no deps) + <build> with real block → real block is found; (b) same plugin under <build> and <profiles> → two findings.

3. Per-<execution> <configuration> shadowing the plugin-level one — the inner scan now walks every <configuration> block within each plugin occurrence rather than grabbing only the first one. Added a fixture where a per-<execution> <configuration> precedes the plugin-level one.

4. Wrong line numberfindVaultDependenciesBlocks now also returns depsIdx (absolute offset of the <dependencies> open tag), and the runner reports lineAt(content, depsBlock.depsIdx). Added a fixture with <artifactId> on line 5 and <dependencies> on line 11, asserts reported line = 11.

Also cleaned up a related nit while in there: the snippet used to hard-code content-package-maven-plugin: in the label — it now names whichever plugin actually matched.

Test suite is 41 → 45 tests, all passing. Ready for another look.

@rombert rombert left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new runner does a lot of work that can be replaced with mvn help:effective-pom , please use that instead.

…vn help:effective-pom`

Per @rombert's review, the runner no longer reasons about <pluginManagement>
inheritance, per-execution vs plugin-level <configuration>, or profile
inheritance textually. Maven does the resolution work — the runner scans the
resolved output.

- Delete raw-POM regex traversal in favor of shelling out to
  `mvn help:effective-pom` per Maven project root.
- Remove textual handling of <pluginManagement> shadowing and per-execution
  <configuration> — both are already resolved / cleanly separated in the
  effective pom.
- Line reporting now anchors on the offending <group> tag in the source
  pom.xml (more actionable than the previous <dependencies>-tag line);
  inherited blocks are marked `(inherited)` with line 1 as anchor.
- `getEffectivePom` is dependency-injectable so unit tests supply canned
  effective-pom XML directly; threaded through `gatherFindings` for the
  dispatcher integration test.
- Effective-pom resolution failures surface as warnings (not scan errors),
  so a single unresolvable module doesn't fail the workspace scan.

Verified end-to-end against `aem-guides-wknd-legacy` — Maven resolves the
project (6 modules, ~28s), reports the real legacy dependency at
`ui.content/pom.xml:63`, and surfaces the non-Maven `dispatcher/` subdir
as a warning.

47/47 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bharat941

Copy link
Copy Markdown
Contributor Author

Thanks @rombert — addressed in 53cb2eb.

The runner now shells out to mvn help:effective-pom per Maven project root and scans the resolved output. That removes all the textual reasoning about <pluginManagement> inheritance, per-execution vs plugin-level <configuration>, and profile inheritance — Maven does the resolution and the runner only looks at what came out.

Structural simplifications:

  • No more <pluginManagement> walking — merged into <build> by Maven.
  • No more per-execution <configuration> shadowing — the runner scopes explicitly to the plugin-level portion (everything before <executions>).
  • No more parent-inheritance blind spots — a legacy block declared in a parent pom is now visible in the child's effective pom and flagged.

Line reporting: anchors on the offending <group> tag in the source pom.xml (more actionable than the previous <dependencies>-tag line). If the block was inherited from a parent and doesn't appear in the source, the finding is marked (inherited) with line 1.

Testability: getEffectivePom is dependency-injectable so unit tests supply canned effective-pom XML directly — no real mvn invocation needed in the test run. Threaded through gatherFindings for the dispatcher integration test. Effective-pom resolution failures surface as warnings, not scan errors, so a single unresolvable module doesn't fail the whole workspace scan.

End-to-end check: ran against aem-guides-wknd-legacy — Maven resolves the 6-module project (~28s), reports the real legacy dependency at ui.content/pom.xml:63, and the non-Maven dispatcher/ subdir surfaces as a warning.

Test suite: 45 → 47, all passing. Ready for another look.

@rombert rombert left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No other comments from me, thank you @bharat941 . Let's wait for @Himanich's approval as well, please.

…pendencies-pattern

# Conflicts:
#	plugins/aem/cloud-service/skills/migration/SKILL.md
#	plugins/aem/cloud-service/skills/migration/scripts/runbook-generator.js
#	plugins/aem/cloud-service/skills/migration/scripts/runbook-generator.test.js
…— no silent false-clean, text-scan fallback, reactor bulk mvn, pluginManagement dedup

- False-clean guard: runVaultPackageScan returns ok:false when it can't
  produce a scan target for a single project root (mvn failed AND raw
  pom.xml unreadable). The runbook dispatcher then routes the pattern to
  needsLlmScan rather than reporting a clean bill of health. Warnings
  from the runner are propagated to analyzerWarnings regardless of ok, so
  a degraded scan is never silent — the previous 'if (res.ok)' block
  dropped res.warnings entirely.
- Text-scan fallback: when mvn help:effective-pom fails for a module
  (legacy AEM 6.x/AMS projects — dead parent repos, missing artifacts,
  offline — the pattern's target audience), the runner falls back to a
  raw text-scan of the source pom.xml. Mirrors the pure-Node scan that
  predated the Maven delegation; degraded (inherited-only
  pluginManagement config isn't visible without Maven) but never silent,
  every fallback emits a warning naming the degradation.
- Reactor bulk mvn: detect reactor roots (pom.xml with <modules>) and
  run mvn help:effective-pom ONCE per reactor, splitting the resulting
  <projects>-wrapped file into per-module effective-pom XML by
  artifactId. On a normal multi-module AEM repo this collapses 8+
  network-bound mvn subprocesses into one.
- pluginManagement dedup: findVaultDependenciesInEffectivePom skips
  <plugin> entries nested under <pluginManagement>. Maven retains
  pluginManagement blocks in the effective POM even after merging their
  config into <build><plugins>, so without the skip an inherited block
  is reported once from pluginManagement and once from the merged build
  entry.
- Docs: header comment of vault-package-scan-runner.js, the pom-scan
  block in runbook-generator.js, PATTERN_META['vault-package-dependencies']
  description, SKILL.md pattern-strategy table row, and
  references/vault-package-dependencies.md now describe the mvn +
  network path AND the text-scan fallback + false-clean guard.

Tests: 71/71 pass. New unit tests cover text-scan fallback surfacing
findings, pluginManagement dedup on effective POM, the false-clean guard
(scan returns ok:false when nothing could be read), and the reactor
bulk path firing exactly once and skipping per-module fallback.
@Himanich
Himanich merged commit abab619 into main Sep 18, 2026
10 checks passed
@Himanich
Himanich deleted the feat/vault-package-dependencies-pattern branch September 18, 2026 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants