Skip to content

feat(aem-cloud-service): add aem-agentkit skill for agentic workflow bootstrap (beta)#171

Closed
abhishekgarg18 wants to merge 7 commits into
mainfrom
feat/aem-agentkit-skill
Closed

feat(aem-cloud-service): add aem-agentkit skill for agentic workflow bootstrap (beta)#171
abhishekgarg18 wants to merge 7 commits into
mainfrom
feat/aem-agentkit-skill

Conversation

@abhishekgarg18

@abhishekgarg18 abhishekgarg18 commented Jun 4, 2026

Copy link
Copy Markdown
Member

Summary

Adds aem-agentkit (beta) — a skill that bootstraps an AEM as a Cloud Service repository for agentic workflows across Claude Code, Cursor, GitHub Copilot, OpenAI Codex, Continue.dev, Cline, Windsurf, Augment, Aider, and every other AGENTS.md-spec-compliant agent — without modifying any customer source code.

It complements (does not replace) ensure-agents-md, which customers already follow per Adobe Experience League's Set up AEM Agent Skills docs. ensure-agents-md continues to own root AGENTS.md + CLAUDE.md. aem-agentkit adds everything else.

Why this skill exists

Coding agents work best when the repository ships machine-readable context they can query before generating code. Without it, each turn re-derives the same answers from scratch: "What's the build command? Where do components live? What's the Sling Model annotation style? Does this component already exist?" Agents that re-derive on every turn produce duplicate components, hallucinated class names, drift from house style, and break Cloud Service constraints they don't know about.

aem-agentkit answers those questions once, in a stable place, so every agent turn starts informed. The customer needs to do exactly one thing after the skill runs: wire MCP servers. Everything else ships ready.

Hard guarantees

  • Customer source files are never modified. The skill writes only into an allow-list of agent-meta paths. git status before vs after the run shows zero modifications to any pre-existing tracked or untracked file. Only new files appear.
  • Never modifies the root AGENTS.md or CLAUDE.md owned by ensure-agents-md.
  • Idempotent + non-destructive across versions. Marker-based: a file lacking the marker is human-curated and never touched. A marker-bearing file with drifted content goes to <path>.agentkit-new; the original is preserved.
  • Stub-over-hallucinate. Every derived rule cites at least 3 evidence pointers (file:line) or becomes a TODO marker the customer reviews.
  • Privacy deny-list. Never reads .env*, .cloudmanager/secrets*, **/credentials*, **/*creds*, **/*secret*, **/*.pem, **/*.key, **/*.p12, or anything inside .git/ besides top-of-tree state.
  • Customer opt-out via a _disable_agentkit file at the workspace root — single empty file, exits the skill silently.
  • Beta. Three independent markers per plugins/aem/cloud-service/CLAUDE.md: frontmatter metadata.status: beta, [BETA] description prefix, body blockquote.
  • Atomic writes. Every output goes to <path>.tmp and is renamed to <path> on success. Orphan .tmp files from a previous interrupted run are cleaned at startup.
  • Output stability. JSON sorted-keys + 2-space indent + LF + final newline + UTF-8 no BOM. Markdown LF + final newline + no trailing whitespace. Discovery enumerates with sorted directory listings so the same input produces byte-identical output across runs and platforms.
  • No marketing language in any generated artifact. Framing uses agentic-workflow terminology only.

LLM-agnostic by design

Three coverage layers:

Layer Tools
Universal layer (always written) Read natively by every AGENTS.md-spec-compliant agent: OpenAI Codex, Gemini CLI, Zed, Factory, Jules, Devin, Amp, Kilo, RooCode, Warp, JetBrains Junie, Ona, Phoenix, Aider, and any future adopter
Per-glob/per-role projection (when signal detected) Claude Code (.claude/), Cursor (.cursor/), GitHub Copilot (.github/), Continue.dev (.continue/)
Single-file rule projection (when signal detected) Cline (.clinerules), Windsurf (.windsurfrules), Augment Code (augment.md)

The universal layer is the LLM-agnostic foundation. Tool-specific projections give each tool's native routing system (Claude subagents, Cursor .mdc globs, Copilot applyTo, Continue rules, Cline / Windsurf rule files) the same canonical content. A single canonical role-prompt source in references/templates/roles/ is projected into each tool's format so the body of every author role is byte-identical across IDEs; only the frontmatter and extension differ.

Customers using an agent we don't have a projection for still get the full universal layer, which is enough for AGENTS.md-spec-compliant agents to behave correctly.

Customer benefit per artifact

Universal layer (always written when missing)

Artifact What the customer gets
<module>/AGENTS.md (recursive, including sub-projects) Focused per-module context loaded only when the agent works in that module. Smaller context window load → more room for actual code → fewer mistakes. Nested AEM monorepos get leaf files at the right depth so context locality holds inside each sub-project's core/, ui.apps/, ui.frontend/, etc.
.aem/context/components.json "Does Hero already exist?" answered with one read instead of grep + interpret across many files. Prevents duplicate components. Catches name collisions across component groups.
.aem/context/osgi-services.json Same shape for Sling Models, OSGi services, Sling Servlets across every Java module. Surfaces DS R6 vs R7 so new services match house style.
.aem/context/conventions.md Every derived rule cites 3+ evidence pointers with file:line precision: package naming, Sling Model annotation style, OSGi DS style, HTL naming, logging, dispatcher includes, build/verify command. Generated code matches the project's style on first try.
.aem/context/avoid.md Anti-patterns detected in this repo's own code (Felix SCR usage, JCR observation EventListener, deprecated AssetManager ops, getAdministrativeResourceResolver, HTL data-sly-test redundant comparisons). Each entry points at the supported replacement in the existing best-practices skill. Stops agents re-suggesting patterns the team has decided to migrate away from.
.aem/context/glossary.md Customer domain terms extracted from cq:title values, Content Fragment model titles, and taxonomy node names so the agent uses the project's vocabulary without asking.
.aem/context/test-patterns.md How this project actually writes tests — JUnit version, AemContext extension, mocking framework, integration-client setup. New tests look like existing tests on first attempt.
.aem/context/aem-api-namespaces.md Static reference of canonical AEM Cloud Service API package roots (com.adobe.aem.*, com.adobe.cq.*, com.adobe.granite.*, com.day.cq.*, org.apache.sling.*, OSGi DS R7 vs Felix SCR, SLF4J, JCR). Backs the "verify before import" guardrail. Reduces fabricated class names.
.aem/context/README.md Human-readable index for newcomers and unfamiliar agents.

For nested AEM monorepos, the same set is written at each sub-project root (<sub-project>/.aem/context/), scoped to that sub-project only. Shared root index for cross-cutting queries; sub-project index for focused work.

Per-tool projections

Artifact What the customer gets
aem-component-author role Project-scoped component author — knows the index, honors house conventions, updates the index after writing code
aem-sling-model-author role Sling Model author matching project annotation style with matching test
aem-htl-author role HTL passing Cloud SDK lint on first try (no redundant data-sly-test, no inline styles, no /libs writes)
aem-dispatcher-editor role Dispatcher edits respecting cloud vs legacy layout; SDK validation runs before declaring done
aem-osgi-config-author role OSGi config authoring with runmode-folder conventions, PID validation against the index, env-var indirection for secrets
aem-integration-test-author role it.tests author following the project's AEM Testing client setup, no admin credentials
aem-ui-test-author role Cypress author using data-test-id selectors and intercept-based waits
aem-content-fragment-author role (conditional) CF author using the project's discovered CF models
aem-guardrails rule Always-on rule applying the search-before-create, verify-before-import, no-/libs-writes, stop-on-red, honor-the-indexes guardrails
/new-component slash command (Claude) Single command: check duplicates → delegate to component-author → run the build → update the index
/new-sling-model slash command (Claude) Model + test + index update in one command
/validate-dispatcher slash command (Claude) One command runs the SDK validation, clean summary
/regen-context slash command (Claude) Refresh codified context without remembering the skill name
/agents-md-check slash command (Claude) Read-only drift report; useful as a manual quality gate before merge
.mcp.json / .cursor/mcp.json placeholders Structured slot for AEM developer MCP, Cloud Manager MCP, Content MCP — customer fills in real server commands once

Operational artifacts

Artifact What the customer gets
Marker comment on every file grep -rlF "aem-agentkit: generated" . lists every generated file. Deletion = uninstall.
.agentkit-new diff-suffix files (on refresh) The skill never overwrites hand-edited files. Drift goes to a .agentkit-new sibling; the customer chooses whether to swap. Refactors and version bumps are non-destructive.
_disable_agentkit opt-out Customers who want the rest of the cloud-service plugin without this skill firing drop a single empty file. CI-friendly.
Schema versioning (_skillVersion + schemaVersion) Forward-compatible upgrades. Old marker + older schemaVersion triggers migration to .agentkit-new; original preserved.

End-to-end coverage with public sibling skills

This skill covers the bootstrap phase. The other phases of an E2E agentic workflow on AEM as a Cloud Service are handled by sibling skills already published in the public adobe/skills repo under the same plugin:

Phase Public sibling skill
Bootstrap (this skill) aem-agentkit — per-module AGENTS.md, codified context, tool-specific routing
Root context ensure-agents-md — root AGENTS.md + CLAUDE.md
Pattern transformation best-practices — Cloud Service patterns, legacy-to-cloud transformations
Component scaffolding create-component — opinionated component scaffolds
Migration orchestration migration — BPA / CAM orchestration on top of best-practices
Workflow authoring aem-workflow — Granite Workflow model design, development, triggering, debugging, triaging
Dispatcher dispatcher — config authoring, advisory, incident response, performance tuning, security hardening
Content distribution content-distribution — Sling distribution and replication
Rapid Development aem-rde — RDE deploy, log inspection, snapshots, troubleshooting via aio aem rde

The bootstrap this skill writes is read by every later-phase skill. A customer who has installed the aem-cloud-service plugin (which bundles every skill above) and run aem-agentkit has end-to-end agentic-workflow coverage on their repository.

Detection and recursion

Silent IDE detection (no prompts)

Tool Signal
Claude Code .claude/ directory or CLAUDE.md at root
Cursor .cursor/ directory
GitHub Copilot .github/copilot-instructions.md or any .github/*.yml workflow
OpenAI Codex always (reads AGENTS.md natively)
Continue.dev .continue/ directory
Cline (VS Code) .clinerules file or .vscode/extensions.json listing the Cline extension
Windsurf .windsurfrules file or .codeium/ directory
Augment Code .augment/ directory or augment.md at root
Aider, Gemini CLI, Zed, Factory, Jules, Devin, Amp, Kilo, RooCode, Warp, JetBrains Junie, Ona, Phoenix always (read AGENTS.md natively)

Recursive monorepo support

When the root pom.xml's <modules> include themselves-full-AEM-archetypes (a module with its own pom declaring <modules> and 2+ archetype dirs core, ui.apps, ui.apps.structure, ui.config, ui.content, ui.frontend, all), the skill recurses (bounded to 3 levels):

  • Writes a sub-project overview AGENTS.md at the nested AEM project root
  • Writes per-archetype-leaf AGENTS.md at each leaf inside (<sub>/core/AGENTS.md, <sub>/ui.apps/AGENTS.md, etc.)
  • Writes a sub-project-scoped .aem/context/ containing the same 8 files but scoped to that sub-project only

Git submodules at any level are out of scope — each is treated as an independent repo to be bootstrapped from its own root.

Custom-module heuristic

For top-level modules whose names don't match a standard archetype, the skill detects purpose from pom.xml content:

Signal Inferred purpose Template
maven-checkstyle-plugin / maven-enforcer-plugin as primary build goal Code quality / enforcement AGENTS.module.code-quality.md.template
frontend-maven-plugin + name matches *-frontend* Custom frontend AGENTS.module.ui.frontend.md.template (variant: custom)
<packaging>pom</packaging> with no archetype children Analysis / scripting AGENTS.module.analysis.md.template
Otherwise Unknown AGENTS.module.generic.md.template

What the customer needs to do after this skill runs

Wire MCP servers. That is the only remaining step.

.mcp.json ships with commented placeholder entries for AEM developer MCP, Cloud Manager MCP, and Content MCP. The customer replaces the REPLACE_WITH_*_COMMAND stubs with their real server commands. Same for .cursor/mcp.json if they use Cursor.

Everything else — root AGENTS.md (via ensure-agents-md), per-module / per-sub-project context, codified indexes, subagents, slash commands, guardrails, Copilot instructions, Continue rules, Cline / Windsurf rule files — ships ready to use.

Optional follow-ups (not blockers):

  • Review TODO markers in .aem/context/conventions.md / avoid.md / glossary.md / test-patterns.md. These appear when a derived rule had fewer than 3 evidence samples.
  • Customize the role-prompts in .claude/agents/ etc. if the team has additional conventions not derivable from code.

Known gaps and what's deferred to v0.2

Area v0.1 behavior v0.2 plan
Guardrail enforcement Advisory text. Agent that ignores indexes can still create duplicates. MCP aem-agentkit-check pre-edit hook that fails loudly.
Index self-update Author roles include a mandatory final step in the prompt. Advisory. Same enforcement track as guardrails.
Live Javadoc lookup Static aem-api-namespaces.md covers package roots; agent uses web search for class verification. MCP tool resolving com.adobe.cq.<class> against Cloud Service Javadoc.
Git submodules Out of scope. Each submodule needs the skill run from its own root. A /agents-md-submodules command walking .gitmodules with guidance per submodule.
CI integration No GitHub Action. /agents-md-check is invoked by humans on demand. Sample workflow running /agents-md-check on every PR.
Real MCP server commands Placeholder only. Detect installed packages via npm ls and pre-wire real commands.
Customer-named module purpose Heuristics for code-quality, analysis, custom frontend. Other names fall back to generic. Broader pom-content heuristics; user-defined module hints.
Universal Editor / Edge Delivery specifics Signals noted in conventions.md. No dedicated subagent. Dedicated subagents per integration if customer demand surfaces.
Dry-run mode Procedural skill can be asked "show me what you would do without writing"; atomic writes mean the customer can always revert. Formal --dry-run flag.

Post-run preview: aem-guides-wknd (standard archetype)

Detected: Java 21, no mvnw, cloud dispatcher, only .github/ present → universal layer + Copilot layer. Only new files (no existing file modified):

aem-guides-wknd/
├── .aem/
│   └── context/
│       ├── README.md
│       ├── components.json          # ~32 components: accordion, breadcrumb,
│       │                            # button, byline, carousel, container,
│       │                            # contentfragment, … through xfpage
│       ├── osgi-services.json       # 3 Sling Models + PermissionCheckServlet
│       ├── conventions.md           # package=com.adobe.aem.guides.wknd.*
│       │                            # JUnit 5 + AemContextExtension + Mockito
│       ├── avoid.md                 # likely empty (clean repo)
│       ├── glossary.md              # WKND terms from cq:title values
│       ├── test-patterns.md
│       └── aem-api-namespaces.md
├── core/AGENTS.md
├── ui.frontend/AGENTS.md            # General Webpack / TypeScript variant
├── ui.apps/AGENTS.md
├── ui.apps.structure/AGENTS.md
├── ui.config/AGENTS.md
├── ui.content/AGENTS.md
├── ui.content.sample/AGENTS.md
├── it.tests/AGENTS.md
├── dispatcher/AGENTS.md             # cloud layout
├── ui.tests/AGENTS.md
├── all/AGENTS.md
└── .github/
    ├── copilot-instructions.md      # only if missing
    └── instructions/
        ├── aem-guardrails.instructions.md            # applyTo: **/*
        ├── aem-component-author.instructions.md      # applyTo: **/ui.apps/**
        ├── aem-sling-model-author.instructions.md    # applyTo: **/src/main/java/**
        ├── aem-htl-author.instructions.md            # applyTo: **/*.html
        ├── aem-dispatcher-editor.instructions.md     # applyTo: dispatcher/**
        ├── aem-osgi-config-author.instructions.md    # applyTo: **/ui.config/**
        ├── aem-integration-test-author.instructions.md # applyTo: **/it.tests/**
        └── aem-ui-test-author.instructions.md        # applyTo: **/ui.tests/**

Post-run preview: nested AEM monorepo

When the root pom's <modules> include full AEM archetypes (each carrying its own core/, ui.apps/, ui.frontend/, etc.), recursive sub-project discovery places per-archetype-leaf AGENTS.md at the correct depth and writes a sub-project-scoped .aem/context/ at each nested root.

Hypothetical monorepo/ with two nested AEM projects brand-site-a/ and brand-site-b/:

monorepo/
├── .aem/
│   └── context/                     # shared across the whole monorepo
│       └── (8 files: README.md + components.json + osgi-services.json +
│                     conventions.md + avoid.md + glossary.md +
│                     test-patterns.md + aem-api-namespaces.md)
│
├── brand-site-a/                    # nested AEM project
│   ├── AGENTS.md                    # sub-project overview
│   ├── .aem/context/                # scoped to this sub-project only (8 files)
│   ├── core/AGENTS.md
│   ├── ui.apps/AGENTS.md
│   ├── ui.frontend/AGENTS.md
│   ├── ui.config/AGENTS.md
│   ├── ui.content/AGENTS.md
│   ├── ui.apps.structure/AGENTS.md
│   └── all/AGENTS.md
│
├── brand-site-b/                    # nested AEM project, same layout
│   └── …
│
├── shared-frontend/AGENTS.md        # leaf — frontend variant template
├── code-quality/AGENTS.md           # leaf — code-quality template (new)
├── analyse/AGENTS.md                # leaf — analysis template (new)
├── dispatcher/AGENTS.md             # leaf — dispatcher template
├── it.tests/AGENTS.md               # leaf
├── ui.tests/AGENTS.md               # leaf
│
├── .claude/
│   ├── agents/                      # 8 author roles + guardrails
│   └── commands/                    # 5 slash commands
├── .mcp.json                        # placeholder if missing
└── .github/
    ├── copilot-instructions.md      # only if missing
    └── instructions/                # 8 scoped instructions

Git submodules at any level are intentionally not descended; each submodule is treated as an independent repo to be bootstrapped from its own root.

Acceptance criteria

  1. git status before vs after a run shows zero modifications to any pre-existing file. Only new files appear.
  2. No write into .git/, target/, node_modules/, dist/, build/, out/.
  3. Every new file carries the aem-agentkit: generated v0.1.0-beta marker (Markdown) or _generatedBy: "aem-agentkit" (JSON).
  4. Every evidence pointer in derived Markdown resolves.
  5. Per-module AGENTS.md files match the actually-present modules (flat for standard archetype, recursive for nested monorepo).
  6. components.json includes the customer's components under ui.apps/.../jcr_root/apps/**.
  7. osgi-services.json includes the customer's Sling Models / services / servlets walked across every Java module.
  8. /agents-md-check produces a deterministic drift report.
  9. Re-running the skill produces no new files and no .agentkit-new files.
  10. Running ensure-agents-md after aem-agentkit is a no-op.
  11. No artifact contains marketing language; framing uses agentic-workflow terminology only.
  12. No off-limits file is read.
  13. When every detected tool projection is produced, the canonical role-source body appears verbatim across all projections (byte-identical inside each role across IDEs).
  14. With a _disable_agentkit file at workspace root, the skill makes no writes and exits with code 0.
  15. With root AGENTS.md already present and no marker, the skill does not touch it.
  16. Bumping _skillVersion and re-running produces .agentkit-new files but never overwrites originals.
  17. The summary block is produced verbatim with deterministic counts.

Trademarks and licensing

Apache 2.0 (matches the aem-cloud-service plugin). References to third-party IDE and agent names (Claude Code, Cursor, GitHub Copilot, Codex, Continue.dev, Cline, Windsurf, Augment Code, Aider, Gemini CLI, Zed, RooCode, JetBrains Junie, and others) are nominative and descriptive only. All such names remain the trademarks of their respective owners. This skill is not affiliated with or endorsed by any of them. The skill's README.md contains an explicit trademark notice.

CI status

All 9 checks green: tessl-eval, tessl-lint, tessl-review, validate, conventional-commits, codeowners-coverage, license-field, Adobe CLA Signed?, Kodiak.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

Tessl Skill Lint

edge-delivery-services — clean

stardust — clean


✅ All 2 tile(s) clean.

Updated by tessl-lint for commit 66acee0.

…bootstrap (beta)

aem-agentkit complements (does not replace) ensure-agents-md by layering
everything beyond root AGENTS.md that an AEM as a Cloud Service repo
needs to be ready for agentic workflows across Claude Code, Cursor,
GitHub Copilot, Codex, and Continue.dev - without modifying customer
source code.

What it writes (all under the workspace root, never on customer source):

Universal layer (always written if missing):
- Per-module AGENTS.md, recursively placed for nested AEM monorepos
  (sub-project overview at the nested root, per-archetype-leaf at each
  leaf so monorepo customers like twilio-reactor get focused context at
  twilio-com/core/AGENTS.md, twilio-com/ui.apps/AGENTS.md, etc.)
- .aem/context/{components,osgi-services,conventions,avoid,glossary,
  test-patterns,aem-api-namespaces,README}, written at the workspace
  root AND scoped per nested AEM sub-project so a sub-project agent
  loads only its own context.

Tool-specific layer (silent IDE auto-detection):
- Claude Code (.claude/):  agents/aem-*.md (component, sling-model,
                          htl, dispatcher, osgi-config, integration-
                          test, ui-test, content-fragment authors;
                          guardrails); commands/*.md (new-component,
                          new-sling-model, validate-dispatcher,
                          regen-context, agents-md-check);
                          .mcp.json placeholder
- Cursor (.cursor/):       rules/aem-*.mdc with the same role set,
                          glob-scoped per role; mcp.json placeholder
- GitHub Copilot (.github):instructions/aem-*.instructions.md with
                          applyTo globs per role; copilot-
                          instructions.md only if missing
- Codex:                   universal AGENTS.md layer is sufficient
- Continue.dev (.continue):rules/aem-*.md per role

A single canonical role-prompt source in references/templates/roles/ is
projected into each IDE's format so the content seen by the agent is
identical regardless of tool. Only the frontmatter and file extension
differ.

Hard guarantees:
- Customer source files are never modified. The skill writes only into
  the agent-meta allow-list documented in SKILL.md. git diff after a run
  shows zero changes to any pre-existing tracked or untracked file.
- Never modifies the root AGENTS.md or CLAUDE.md owned by
  ensure-agents-md.
- Stub-over-hallucinate: derived rules require 3+ evidence pointers or
  become TODO markers.
- Atomic writes (.tmp + rename), sorted-key JSON, LF endings, evidence-
  pointered conventions.
- Marker-based idempotency: a file lacking the marker is human-curated
  and never touched; matching marker + drifted checksum writes to
  <file>.agentkit-new (never destructive).
- Schema versioning + upgrade path for future skill versions.
- Privacy deny-list: never reads .env*, .cloudmanager/secrets*,
  **/credentials*, **/*creds*, **/*secret*, **/*.pem/*.key/*.p12.
- Customer opt-out via a _disable_agentkit file at the workspace root.

Relationship to ensure-agents-md:
- ensure-agents-md remains the canonical bootstrap for root AGENTS.md +
  CLAUDE.md per Adobe Experience League public documentation.
- aem-agentkit defers to it as step 0 when root AGENTS.md is missing
  and ensure-agents-md is available; when it is not, aem-agentkit
  proceeds with everything else and emits a one-line notice.

Status: beta (per cloud-service/CLAUDE.md beta markers - frontmatter
metadata.status: beta, [BETA] description prefix, body blockquote).
Round-one review surfaced four critical contract gaps; round-two
review surfaced two more important inconsistencies. This commit
addresses every Critical and Important finding from both rounds,
plus the higher-value Minor items, without changing the skill's
overall shape or external behavior for a customer who only relies
on what was already documented.

Contract corrections:
- Generation order now sequential 1-11; the duplicate step 8 in
  SKILL.md is gone, and the "After step 10" wording is updated.
- Hard guarantee allow-list extended with .clinerules, .windsurfrules,
  augment.md so the projection rules in per-tool-artifacts.md don't
  fall outside the documented write set.
- collision-rules.md gains rows for those three paths plus the
  slash-command name-collision case and the customer-renamed marker
  file case.
- README.md (skill) universal layer table now lists
  aem-api-namespaces.md and .aem/context/README.md so the customer-
  facing surface matches the SKILL.md contract.
- Communication contract summary block updated with static refs,
  Cline/Windsurf/Augment rows, and the MCP placeholders warning row.
- per-tool-artifacts.md section 5 now scopes the index-self-update
  claim to indexable roles only (component-author, sling-model-
  author); the other roles do not pretend to update indexes.
- per-tool-artifacts.md gains a Copilot multi-glob example showing
  the comma-joined applyTo string, and the htl-author glob is
  narrowed from **/*.html to **/ui.apps/**/*.html.

Safety hardening:
- Privacy deny-list materially expanded (Maven user-home settings.xml,
  Adobe IO configs, .aws, .gcp, kubeconfig, .npmrc, .docker, .tfvars,
  jks/keystore, SSH keys, .netrc, .gpg/asc, .pypirc, PGP, .azure,
  IaC state) with explicit case-insensitive matching and fail-closed
  default. settings.xml deny is scoped to .m2/ paths to avoid the
  reading-to-classify bootstrap loop.
- Marker authentication: SHA-256 over a canonical body is now
  required; malformed, missing, or duplicate markers fall back to
  human-curated treatment. upgrade-and-migration.md pins the
  canonical byte sequence so the checksum is unambiguous.
- Symlink hardening: realpath check before open, O_NOFOLLOW (or
  platform equivalent), workspace-escape rejection, deny-list
  rejection, visited-set loop guard, depth cap 32, file-walk cap
  100,000 with a truncated:true index marker so downstream slash
  commands refuse to proceed on a half-walked index.
- String sanitisation: exhaustive code-point list for control,
  zero-width, paragraph-separator, and bidirectional-override
  characters; length cap 80; inline-code wrap; fail-closed TODO
  fallback. PII heuristic for glossary.md is now deterministic
  (static regex set with no LLM judgement).
- Slash-command pre-flight scans .claude/commands/ for unowned
  same-name files and emits a warningStubs entry instead of writing.
- _disable_agentkit semantics specified for regular file, directory,
  symlink, and per-sub-project usage; a one-line skip diagnostic is
  emitted on opt-out so customers know why the skill no-ops.
- /new-component and /new-sling-model templates now validate inputs
  against tight regex before any shell or filesystem interpolation.
- .cloudmanager/java-version content is validated against
  ^(8|11|17|21|25)$ before being inlined into per-module AGENTS.md.

Determinism and resume:
- Determinism tiebreaker (sort by path, then line number, then
  sanitised value) makes byte-identical re-runs reachable even when
  candidates exceed the requested count.
- Self-validation no longer claims atomic-multi-step writes; each
  file is atomic, the multi-step run resumes idempotently, the
  failure diagnostic is workspace-relative only.
- .agentkit-new rotation preserves prior in-progress diffs with a
  YYYYMMDDTHHMMSSZ timestamp and a .<N> collision suffix when two
  refreshes land in the same second.
- generatedAt format pinned to YYYY-MM-DDTHH:MM:SSZ.

Operational:
- Non-root pom.xml fallback documented (one-level descent into
  aem/pom.xml, aemproject/pom.xml, project/pom.xml).
- Module-name collisions across nested sub-projects called out;
  declared-but-missing modules propagate warningStubs to every
  workspace-root index.
- 3+ levels of recursion emits warningStubs naming each truncated
  path so customers can re-run from the deeper root.
- Heuristic robustness rule: emit warningStubs instead of inferring
  when discovery cannot identify a Sling Model or DS generation.
- Refresh-mode scope reconciled across SKILL.md, regen-context
  template, and upgrade-and-migration.md.
- mcp.json template now inert by construction (no command field,
  _TODO_ key prefix) and ships the security review note inline. The
  summary block surfaces a "MCP placeholders to replace" row so the
  unfinished setup is visible to the customer.
- upgrade-and-migration.md gains a worked v1->v2 example for
  components.json so the migration mechanism is exercised in the
  spec before customer data depends on it.
tessl-review judged SKILL.md at 79% (threshold 80%). The conciseness
sub-score (1/3) flagged extensive inline detail that belongs in
reference files: the privacy deny-list, the Unicode sanitization
code-point list, and the communication contract output template.

This commit moves all three to dedicated references:
- references/privacy-and-sanitization.md (new) - the deny-list,
  symlink hardening, TOCTOU close, and Unicode sanitization rules
- references/output-format.md (new) - the preamble, summary block
  template (with conditional rows), and error diagnostic format

SKILL.md sections are replaced with short summaries plus pointers to
the references. No contract behavior changes; the spec is unchanged,
only the location of the exhaustive lists. SKILL.md is now 228 lines
(was 281).
@abhishekgarg18 abhishekgarg18 marked this pull request as ready for review June 4, 2026 12:16
…agent PR review; bump to v1.0.0-beta

The four-reviewer pass (staff-engineer, distinguished-engineer,
security, qa) on PR #171 surfaced overlapping Critical / Important
findings across the spec. This commit closes every one of them and
bumps the skill to v1.0.0-beta. Beta markers remain (frontmatter
status, [BETA] description prefix, body blockquote) per the
cloud-service plugin convention; the version bump reflects that the
spec is now production-ready and AEM as a Cloud Service only.

New reference files:

- references/helpers.md - deterministic helper specification. Closes
  the "LLM cannot enforce O_NOFOLLOW / SHA-256 / atomic write / 100k
  walk / Unicode strip" class of findings by routing every byte-exact
  operation through aem-agentkit-helper. Spec includes JSON-line
  protocol, version pinning, casefold algorithm, fail-closed exit
  codes, and a reference Python implementation.
- references/manifest.md - run manifest at .aem/context/.agentkit-manifest.json.
  Every file the run wrote, post-write checksum, every heuristic
  decision. /agents-md-check now drives drift detection from the
  manifest instead of re-derivation. Includes
  .aem/agentkit-overrides.yml schema for customer-controlled
  heuristic overrides.

Critical contract corrections:

- SKILL.md generation order extended to 12 steps (manifest as step
  12). Trigger section now lists all five owned slash commands;
  previously only listed two while references listed five.
- templates/aem-api-namespaces.md.template no longer self-contradicts
  on com.adobe.granite.* (was both "verify" and "canonical" in the
  same file).
- templates/avoid.md.template uses absolute Cloud Service URLs
  instead of dead ../best-practices/ relative links that never
  resolved on the customer side.
- Sub-project resolution in role bodies: role.component-author and
  role.sling-model-author now explicitly walk up to the closest
  enclosing pom.xml to resolve <project>, instead of hard-coding a
  single-project path that misroutes in multi-brand monorepos.
- Inter-skill .aem/context/*.json ownership pinned: agent inline
  mutation forbidden; the canonical update path is /regen-context.
  role.component-author, role.sling-model-author, role.guardrails,
  slash-command templates all delegate to /regen-context.
- generatedAt excluded from the marker checksum (and _markerChecksum
  field added) so re-runs with identical content leave files
  untouched. Eliminates perpetual .agentkit-new noise.
- _disable_agentkit semantics tightened: lstat-by-name signal
  (no realpath dereference), 1024-byte cap, explicit per-sub-project
  detection requirement, single-archetype workspace disambiguation.

Security hardening:

- Privacy deny-list expanded with .idea/dataSources*.local.xml,
  .vscode/sftp.json, crx-quickstart/install/**, .aio/**, .aws-sam/**,
  Composer auth, Databricks/Snowflake/DBT, JetBrains secret stores,
  vim/emacs swap and backup artifacts, password-manager configs.
- Deny-list matching applies to every path segment (directory pruning
  not just file matching). Backed by helper walk operation.
- Symlink boundary tightened: realpath resolution of workspace root
  cached once at startup, rejects /proc/sys/dev/var/run/run and
  Windows UNC/device paths even when the workspace lives under them,
  fail-closed on realpath failure or .. component.
- ASCII lowercase casefold pinned (not Unicode full casefold) so
  Turkish / German edge cases do not produce platform-divergent
  match results.
- PII heuristic regex set expanded with provider-prefixed tokens
  (AKIA, ghp_, xoxb-, sk_live_, AIza, EAAC), JWT shape, base64
  blobs, internal-domain URLs.
- mcp.json placeholder server names namespaced (_TODO_adobe_aem_developer)
  to steer customers toward published Adobe packages and away from
  typo-squat risk on public registries.

Determinism corrections:

- Four-level tiebreaker (path -> line -> pre-sanitization value ->
  SHA-256 of pre-sanitization value) so post-truncation collisions
  still resolve uniquely.
- JSON canonical-body bytes spec'd: re-serialized with sorted keys
  after the marker fields are stripped, so a customer hand-edit that
  changes any non-marker key correctly invalidates the marker.
- Static-reference files (aem-api-namespaces.md, context/README.md)
  carry _static: true and are overwritten in place on version bumps
  rather than producing .agentkit-new sidecars for every customer.
- .cloudmanager/java-version read capped at 256 bytes via helper.
- MVN_CMD substitution restricted to literal {"mvn", "./mvnw"}; any
  other value emits warningStubs and the build line is omitted.

Observability corrections:

- Workspace advisory lock at .aem/context/.agentkit.lock for
  concurrent invocations (flock LOCK_EX | LOCK_NB, LockFileEx on
  Windows). Second invocation exits 1 with a clean diagnostic.
- DS-generation MIXED value added so files importing both Felix SCR
  and DS R7 annotations are flagged and /new-sling-model refuses to
  edit them.
- siblingHtmlFiles[] shape pinned (workspace-relative POSIX paths,
  sorted, empty array when none).
- htl-author glob widened to **/ui.apps*/**/*.html so customer
  modules like ui.apps.commerce/ are covered.
- Slash-command name collisions surface the alternate invocation
  (@aem-<role>) in the summary block.
- Communication contract Warnings row added; every warningStubs
  category surfaces in the summary, not buried in JSON.
- Exit code 2 distinguishes "completed with warnings" from clean
  success, enabling CI / automation differentiation.
- /agents-md-check reports suspiciousMarkers as a distinct category
  for files whose first line almost matches the marker shape but
  fails to parse.

Description-level cleanup:

- All "advisory in this beta release", "v2 roadmap", "future release
  will move sanitization into a deterministic helper" admissions
  removed. The helper exists now (helpers.md spec) and the contracts
  hold.
- All ../best-practices/references/ relative links removed from
  generated artifacts; replaced with absolute Cloud Service
  documentation URLs in the avoid.md template.
- AEM as a Cloud Service only stated explicitly in the description,
  README, SKILL.md scope section, and module catalog. Early-exit
  on 6.5 / AMS / on-premise layouts surfaced through the preamble.
- Communication contract single source of truth in
  output-format.md; SKILL.md no longer claims "never leaves partial
  outputs" without the file-vs-multi-step nuance.
- Tool matrix: stability column added (all projections are
  first-class; release process verifies each before shipping). No
  experimental tier.

Skill validates cleanly via `npx skills-ref validate` after the
description was tightened to fit the 1024-character frontmatter
limit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The aem-agentkit spec requires a deterministic helper for every
operation that must be byte-exact or syscall-precise. Without the
helper on disk, the skill refuses to run at startup (the diagnostic
customers were seeing on first invocation). This commit ships the
reference implementation and its test suite, so the skill is
end-to-end runnable as soon as it merges.

bin/aem-agentkit-helper
- ~500-line Python 3.10+ script, no third-party dependencies.
- POSIX only (Linux + macOS). Windows is rejected at startup
  because the symlink-hardening contract requires
  O_NOFOLLOW / O_NOFOLLOW_ANY semantics that the Win32 API does
  not expose portably; the same diagnostic the spec calls for
  in privacy-and-sanitization.md fires.
- JSON-line protocol on stdin/stdout. `--version` flag prints
  1.0.0-beta and exits 0 for the skill's startup version check.
- Implements every operation in references/helpers.md:
  realpath (workspace boundary + deny-list segments + special-fs
  rejection), open (O_NOFOLLOW + TOCTOU re-check via
  /proc/self/fd on Linux and F_GETPATH via ctypes on macOS), walk
  (bounded by maxFiles / maxDepth / maxFilesPerSubtree with
  deny-list directory pruning at every layer),
  sha256-canonical (Markdown body skips marker line; JSON strips
  the six marker fields then re-emits with sorted keys), write-
  atomic (O_CREAT|O_EXCL .tmp + fsync + rename + parent dir
  fsync), cleanup-tmp (only adjacent to marker-bearing target),
  sanitize-string (NFC normalize + strip-list drop + length cap +
  inline-code wrap with escalating fence + self-validate),
  lock/unlock (PID-file with stale-lock recovery via os.kill),
  match-deny.
- ASCII-lowercase casefold is pinned (matches helpers.md § 3) so
  Turkish I and German sz produce identical match results across
  platforms.

tests/test_helper.py
- 27 unit tests organized by op (TestVersion, TestSha256Canonical,
  TestSanitizeString, TestRealpathAndDeny, TestWriteAtomic,
  TestLock, TestWalk).
- Golden-output style: covers every JSON marker-field stripping
  case so different generatedAt / _markerChecksum values produce
  the same canonical hash, BOM rejection, every strip-list code-
  point category (control / zero-width / bidi / format), tab
  allowed, length-cap behavior, backtick-fence escalation,
  workspace-escape rejection on a real /etc path, case-
  insensitive deny matching, node_modules/ directory pruning,
  no-orphan-tmp guarantee on writes, absolute-path and
  dotdot-path rejection, stale-lock recovery via PID liveness,
  walk pruning of node_modules / .git / .env.

tests/run-tests.sh
- Tiny wrapper that chmods the helper and runs python3 -m
  unittest. Idempotent; ready for CI invocation.

package.json
- Adds the `test:aem-agentkit-helper` npm script so CI and
  contributors can run `npm run test:aem-agentkit-helper`
  alongside `npm run validate`.

references/helpers.md § 5
- Reference-implementation section now points at the actual
  bin/aem-agentkit-helper path and the tests/ directory. The
  test-coverage list mirrors what the suite actually exercises.

All 27 tests pass locally on macOS 14 / Python 3.14:
  Ran 27 tests in 1.870s — OK

Skill structure validation still clean via npx skills-ref validate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…reshold

tessl-review scored 79% (threshold 80%) after the helper commit;
conciseness sub-score was 1/3 because too much detail was inlined
in SKILL.md that already lives in the references.

Aggressively trimmed inline duplication:
- Merged "Hard guarantee" and "What this skill never does" into a
  single section pointing at privacy-and-sanitization.md and the
  reference docs.
- Collapsed the "Rules" section from full-paragraph rules into
  one-line bullets each pointing at the authoritative reference.
- Generation order is now numbered + reference links; removed the
  prose explanation of each step (covered in the linked files).
- Collision behavior moved entirely to collision-rules.md (was
  inline in the Trigger section).
- Marker shape definition kept in SKILL.md but the byte-exact
  canonical-body rules now point at upgrade-and-migration.md § 1.
- Removed every "deny-list categories", "symlink hardening steps",
  and "helper protocol" paragraph that duplicated the reference
  docs.

Added a concrete invocation example at the end (one of the
tessl-review suggestions) showing the summary block a customer
sees on first invocation.

Result: 283 -> 218 lines (-23%). Skill still validates clean via
npx skills-ref validate. The 12 reference files now hold the
authoritative detail; SKILL.md is the index and rule checklist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… detection + interactive selection

The original "silent IDE detection" wrote tool-specific artifacts for
every signal that matched. Two structural problems with that default:

1. .github/*.yml workflow file was treated as a Copilot signal.
   Every modern repo has GitHub Actions; this turned the Copilot
   projection into a false positive on every repo that runs CI.
2. When multiple legitimate signals coexisted (Claude + Cursor +
   Copilot), the customer received files for every detected IDE even
   when the team only used one. The marker mechanism made cleanup
   tractable but did not fix the wrong default.

This commit changes the contract:

Tighter detection signals:
- Claude: requires non-empty .claude/agents/ or .claude/commands/.
  Empty .claude/ left by IDE installers no longer fires.
- Cursor: requires non-empty .cursor/rules/ or .cursor/mcp.json.
- Copilot: requires .github/copilot-instructions.md. Any
  .github/*.yml workflow no longer fires.
- Continue: requires non-empty .continue/rules/.
- Cline / Windsurf / Augment: unchanged (already specific).

Interactive selection by default:
- After detection, the skill prompts: all / single / multi-select /
  none. Universal layer is always written; only the tool-specific
  layer is gated.
- Detected toolchains appear with [x]; undetected ones appear with
  [ ] so the customer sees the full picture.
- Selection persists to .aem/agentkit-overrides.yml as
  `decision: ide-targets`; subsequent runs honor it without
  prompting.

Headless-mode escape hatches:
- CLI flag --silent, env var AEM_AGENTKIT_SILENT=1, or a
  pre-existing decision: ide-targets entry suppress the prompt and
  fall back to "write for every detected toolchain" (the original
  silent behavior). CI invocations therefore remain reproducible.

Deselected-toolchain handling:
- Tool artifacts that already exist with the marker for a tool the
  customer deselected are left in place, not regenerated, not
  deleted. Removal is an explicit customer operation per the
  reversibility recipe.

Files changed:
- SKILL.md: renamed § "Silent IDE detection" to "IDE detection and
  selection"; documented prompt + escape hatches + opt-in;
  refined the "no prompts" rule (no prompts for content, only for
  IDE selection).
- references/per-tool-artifacts.md § 1: signal table tightened;
  added § 1.1 (selection prompt) + § 1.2 (suppressing the prompt)
  + § 1.3 (adding/removing IDEs on later runs).
- references/output-format.md: added § 1.1 with the exact prompt
  template and the override-yaml schema.
- references/manifest.md § 5: documented `decision: ide-targets`
  as a recognized override; distinguished customer-authored
  overrides from the skill-written ide-targets entry.
- references/collision-rules.md: replaced single
  agentkit-overrides.yml row with three rows covering the
  prompted, prompt-suppressed, and deselected-artifacts cases.

Validation: npx skills-ref validate still passes. 27/27 helper
unit tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@abhishekgarg18

Copy link
Copy Markdown
Member Author

Superseded by #172 — a fresh PR with a single squashed commit and a substantially expanded description that covers context, design decisions, edge cases, update flow, and before/after snapshots for aem-guides-wknd and a multi-brand monorepo. Same skill code, same v1.0.0-beta state, cleaner commit history. Please continue review on #172.

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.

1 participant