Project Handover - Development
Generate a complete technical guide for developers. This skill analyzes the codebase and produces actionable documentation that enables developers to understand, maintain, and extend the project.
When to Use This Skill
- Onboarding new developers to a project
- Technical handover to maintenance team
- Code review and architecture documentation
- Creating developer-focused documentation
Step 0: Navigate to Project Root and Verify Edge Delivery Services Project (CONDITIONAL)
Skip this step if flag is set (orchestrator already validated and navigated).
CRITICAL: If NOT skipped, you MUST execute the command. Do NOT use absolute paths — actually change directory.
bash
ALL_GUIDES=$(cat .claude-plugin/project-config.json 2>/dev/null | grep -o '"allGuides"[[:space:]]*:[[:space:]]*true')
if [ -z "$ALL_GUIDES" ]; then
# Navigate to git project root (works from any subdirectory)
cd "$(git rev-parse --show-toplevel)"
# Verify it's an Edge Delivery Services project
ls scripts/aem.js
fi
IMPORTANT:
- You MUST run the command above using the Bash tool
- All subsequent steps operate from project root
- Do NOT use absolute paths to verify — actually navigate
- Guides will be created at
project-root/project-guides/
If NOT skipped AND does NOT exist, respond:
"This skill is designed for AEM Edge Delivery Services projects. The current directory does not appear to be an Edge Delivery Services project (
not found).
Please navigate to an Edge Delivery Services project and try again."
STOP if check fails. Otherwise proceed — you are now at project root.
Communication Guidelines
- NEVER use "EDS" as an acronym for Edge Delivery Services in any generated documentation or chat responses
- Always use the full name "Edge Delivery Services" or "AEM Edge Delivery Services"
- This applies to all output files (PDF, HTML, markdown) and all communication with the user
⚠️ CRITICAL PATH REQUIREMENT
YOU MUST SAVE THE FILE TO THIS EXACT PATH:
project-guides/DEVELOPER-GUIDE.md
BEFORE WRITING ANY FILE:
- First, create the directory:
- Then write to:
project-guides/DEVELOPER-GUIDE.md
WHY THIS MATTERS: Files must be in
for proper organization and PDF conversion.
❌
WRONG: (root)
❌
WRONG:
❌
WRONG: /workspace/DEVELOPER-GUIDE.md
✅
CORRECT: project-guides/DEVELOPER-GUIDE.md
Output Format
MANDATORY OUTPUT: project-guides/DEVELOPER-GUIDE.pdf
STRICTLY FORBIDDEN:
- ❌ Do NOT read or analyze — it does NOT exist in most projects and does NOT show all sites
- ❌ Do NOT create files
- ❌ Do NOT use tool — this converts the FULL guide to HTML with raw frontmatter visible, which is NOT what we want
- ❌ Do NOT tell user to "convert markdown to PDF manually"
- ❌ Do NOT save markdown to root directory or any path other than
- ❌ Do NOT say "PDF will be generated later" or "at session end" — generate it NOW
REQUIRED WORKFLOW:
- Run to ensure directory exists
- Generate markdown content with YAML frontmatter (title, date)
- Save to
project-guides/DEVELOPER-GUIDE.md
(EXACT PATH - no exceptions)
- IMMEDIATELY invoke PDF conversion (see Phase 5.1)
- Clean up all source files (only PDF remains)
- Final output:
project-guides/DEVELOPER-GUIDE.pdf
Execution Checklist
markdown
- [ ] Phase 1: Gather Project Information
- [ ] Phase 2: Analyze Project Architecture
- [ ] Phase 3: Document Design System
- [ ] Phase 4: Document Blocks, Models, and Templates
- [ ] Phase 5: Generate Professional PDF
Phase 0: Get Organization Name (Required First)
Whenever this skill runs — whether the user triggered it directly (e.g. "generate developer guide") or via the handover flow — you must have the Config Service organization name before doing anything else. Do not skip this phase.
0.1 Check for Saved Organization
bash
# Check if org name is already saved
cat .claude-plugin/project-config.json 2>/dev/null | grep -o '"org"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1
0.2 Prompt for Organization Name (If Not Saved)
If no org name is saved, you MUST pause and ask the user directly:
"What is your Config Service organization name? This is the
part of your Edge Delivery Services URLs (e.g.,
https://main--site--{org}.aem.page
). The org name may differ from your GitHub organization."
IMPORTANT RULES:
- DO NOT use with predefined options — ask as a plain text question
- Organization name is MANDATORY — do not offer a "skip" option
- Wait for user to type the org name before proceeding
- If user doesn't provide a valid org name, ask again
0.3 Save Organization Name
Once you have the org name (either from saved config or user input), save it for future use:
bash
# Create config directory if needed
mkdir -p .claude-plugin
# Ensure .claude-plugin is in .gitignore (contains auth tokens)
grep -qxF '.claude-plugin/' .gitignore 2>/dev/null || echo '.claude-plugin/' >> .gitignore
# Save org name to config file (create or update)
if [ -f .claude-plugin/project-config.json ]; then
cat .claude-plugin/project-config.json | sed 's/"org"[[:space:]]*:[[:space:]]*"[^"]*"/"org": "{ORG_NAME}"/' > /tmp/project-config.json && mv /tmp/project-config.json .claude-plugin/project-config.json
else
echo '{"org": "{ORG_NAME}"}' > .claude-plugin/project-config.json
fi
Replace
with the actual organization name provided by the user.
Phase 1: Gather Project Information
1.1 Get Project URLs and Repository
bash
# Get repository info
git remote -v | head -1
# Get branch info
git branch -a | head -10
Extract:
- Repository owner and name
- Main branch name
1.2 Check Configuration Method
bash
# Check for helix-config (very old projects)
ls helix-config.yaml 2>/dev/null && echo "Uses legacy helix-config" || echo "Uses Config Service (modern)"
Document: Project configuration method (Config Service for modern projects).
1.3 Fetch Sites via Config Service API
⚠️ MANDATORY DATA SOURCE — NO ALTERNATIVES ALLOWED
You MUST call the Config Service API. This is the ONLY acceptable source for site information.
❌ PROHIBITED APPROACHES (will produce incorrect results):
- Analyzing — does NOT show all sites in repoless setups
- Reading — may be outdated or incomplete
- Inferring from codebase structure — misses CDN configs and additional sites
- Using git remote URLs — org name may differ from Config Service org
- Making assumptions based on project folder names
✅ REQUIRED: Execute and save response:
bash
ORG=$(cat .claude-plugin/project-config.json | grep -o '"org"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"org"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//')
# Save response to file - Phase 2 depends on this file
curl -s -H "Accept: application/json" "https://admin.hlx.page/config/${ORG}/sites.json" > .claude-plugin/sites-config.json
📁 REQUIRED ARTIFACT: .claude-plugin/sites-config.json
The response is a JSON object with a
array (each entry has a
field). Extract site names and construct per-site URLs:
- Preview:
https://main--{site-name}--{org}.aem.page/
- Live:
https://main--{site-name}--{org}.aem.live/
Multiple sites = repoless setup. Single site = standard setup.
Then fetch individual site config for code and content details:
bash
AUTH_TOKEN=$(cat .claude-plugin/project-config.json | grep -o '"authToken"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"authToken"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//')
curl -s -H "x-auth-token: ${AUTH_TOKEN}" "https://admin.hlx.page/config/${ORG}/sites/{site-name}.json"
Example response:
json
{
"code": {
"owner": "github-owner",
"repo": "repo-name",
"source": { "type": "github", "url": "https://github.com/owner/repo" }
},
"content": {
"source": {
"url": "https://content.da.live/org-name/site-name/",
"type": "markup"
}
}
}
Extract from response:
- / — GitHub repository
- — Content mountpath (e.g.,
https://content.da.live/org/site/
)
- — Content source type (markup, onedrive, google)
⚠️ Do NOT use — use Config Service API instead.
Record the result (repoless or standard) — it is used in the Local Development Setup section to decide whether to include the
flag for
.
1.4 Check Node.js Requirements
bash
# Check for Node version requirements
cat .nvmrc 2>/dev/null || cat package.json | grep -A2 '"engines"'
Phase 2: Analyze Project Architecture
Read site config from Phase 1:
bash
cat .claude-plugin/sites-config.json
2.1 Map Project Structure
bash
# List top-level structure
ls -la
# List blocks
ls -la blocks/
# List scripts
ls -la scripts/
# List styles
ls -la styles/
# Check for templates
ls -la templates/ 2>/dev/null || echo "No templates folder"
2.2 Identify Boilerplate vs Custom Files
CRITICAL: Only document files that were actually customized.
bash
# Check commit history for a file (skip if only "Initial commit")
git log --oneline --follow {file_path} | head -5
# Check who modified a file (skip aem-aemy[bot] only files)
git log --format="%an - %s" --follow {file_path} | head -5
Rules for filtering:
| Git History | Action |
|---|
| Only "Initial commit" | Skip - boilerplate default, never worked on |
| Only commits | Skip - auto-generated |
| Multiple commits by team | Document - customized |
| Commits with feature descriptions | Document - customized |
IMPORTANT: Files with only an "initial commit" are boilerplate defaults that were never modified. Do not document these - they add no value to the developer guide.
2.3 Analyze scripts/aem.js (Core Library)
bash
# List what aem.js exports (DO NOT MODIFY this file)
grep -E "^export" scripts/aem.js
Document which functions the project imports from (e.g.,
,
,
,
,
,
). This helps developers understand which core utilities are available.
2.4 Analyze scripts/scripts.js (Eager + Lazy Phases)
bash
# Extract imports and key function signatures
grep -E "^import|^export|^function|^async function|buildAutoBlocks|loadTemplate|getLanguage|getSiteRoot|decorateMain|loadEager|loadLazy|loadDelayed" scripts/scripts.js
Document these customizations:
| Pattern to Look For | What to Document |
|---|
| statements | What it imports from and |
| / functions | Any custom logic added to E-L-D phases |
| function | Auto-blocking logic |
| or template handling | Template system |
| or language detection | Multi-language setup |
| or site detection | Multi-site configuration (see also Phase 1.3) |
| Custom additions | Page decoration extensions |
| External script loading | Which phase loads it — flag if loaded in eager (performance risk) |
E-L-D check: Note any third-party scripts or heavy logic added to
or
that could impact LCP. These should typically be in
.
2.5 Analyze scripts/delayed.js (Delayed Phase)
bash
# Extract imports and function calls to identify integrations
grep -E "^import|function|google|analytics|gtag|alloy|martech|OneTrust|launch|chatbot|widget" scripts/delayed.js
Document:
- Analytics integrations (Google Analytics, Adobe Analytics, etc.)
- Marketing tools (OneTrust, chat widgets, etc.)
- Performance monitoring
- Any custom delayed features
- Confirm no render-critical code is in this file (it loads ~3s after page load)
2.6 Check for Utility Functions
bash
# Extract exported functions from utils
grep -E "^export|^function" scripts/utils.js 2>/dev/null || echo "No utils.js"
# List all script files
ls scripts/*.js
# Check which blocks/scripts import from utils
grep -rl "utils.js" blocks/ scripts/ 2>/dev/null
Document: Shared utility functions, their purposes, and which blocks/scripts import them.
2.7 Check for External Dependencies
bash
# Check package.json for dependencies
grep -A 20 '"dependencies"' package.json 2>/dev/null | head -25
# Check for CDN imports in code
grep -r "cdn\|unpkg\|jsdelivr" scripts/ blocks/ --include="*.js" 2>/dev/null
Phase 3: Document Design System
4.1 Extract CSS Custom Properties
bash
# Get all CSS variables from styles.css
grep -E "^\s*--" styles/styles.css
Organize into categories:
| Category | Example Properties |
|---|
| Typography | , , |
| Colors | , , |
| Spacing | , , |
| Layout | , |
4.2 Document Font Setup
bash
# Extract font-face declarations (font names, weights, formats)
grep -E "@font-face|font-family|font-weight|src:" styles/fonts.css 2>/dev/null
# Check fonts directory
ls fonts/ 2>/dev/null
Document:
- Font files and formats
- Font family names
- Font weights available
- Fallback fonts
4.3 Document Breakpoints
bash
# Find media query breakpoints
grep -E "@media.*min-width|@media.*max-width" styles/styles.css | sort -u
Standard EDS breakpoints:
- Mobile: < 600px
- Tablet: 600px - 899px
- Desktop: 900px+
- Large: 1200px+
Document any deviations from standard.
4.4 Document Section Styles
bash
# Find section-related styles
grep -A 5 "\.section\." styles/styles.css
grep -A 5 "\.section\[" styles/styles.css
Document available section styles (dark, highlight, narrow, etc.)
Phase 4: Document Blocks, Models, and Templates
Boilerplate Filtering (applies to blocks, models, and templates)
Run all filtering silently — do not show output to the user.
- Include: Items with 2+ commits AND at least one commit after "initial commit"
- Exclude: Items with only 1 commit that is "Initial commit" or similar boilerplate setup
- Exclude: Items with only commits (auto-generated)
- Items that only have an "initial commit" were never customized — do NOT document them.
4.1 Identify and Analyze Customized Blocks
For each block that passed the filter, run:
bash
# Get block purpose from JS comments/structure
head -30 blocks/{blockname}/{blockname}.js
# Get block variants from CSS
grep -E "^\." blocks/{blockname}/{blockname}.css | head -30
# Check for variant handling
grep -E "classList\.contains|classList\.add" blocks/{blockname}/{blockname}.js
Document for each customized block:
| Field | What to Record |
|---|
| Name | Block folder name |
| Purpose | What it does (from code analysis) |
| DOM Input | Expected HTML structure from CMS |
| DOM Output | Transformed structure after decoration |
| Variants | CSS classes that modify behavior |
| Dependencies | External libraries, other blocks, utils |
| Key Functions | Important internal functions |
4.2 Document Block Dependencies
bash
grep "^import" blocks/{blockname}/{blockname}.js
4.3 Document Universal Editor Models (If Customized)
Apply the same boilerplate filtering to
. Also exclude standard boilerplate models (
,
,
,
,
,
) if unchanged. If all models are boilerplate, skip this section in the output.
4.4 Document Customized Templates
Apply the same boilerplate filtering to
. For each customized template, document its purpose, how it's applied (
in metadata), and what it changes.
Phase 5: Generate Developer Guide
Output File: project-guides/DEVELOPER-GUIDE.md
Generate a markdown document with the following structure:
markdown
# [Project Name] - Developer Guide
## Quick Reference
|----------|-----|
| Code Repository | {code.owner}/{code.repo} (from Config Service — may be GitHub or Cloud Manager) |
| Preview | https://main--{repo}--{owner}.aem.page/ |
| Live | https://main--{repo}--{owner}.aem.live/ |
| Local Dev | http://localhost:3000 |
## Architecture Overview
### Tech Stack
- Vanilla JavaScript (ES6+)
- CSS3 with Custom Properties
- No build step - files served directly
- Content from Document Authoring (DA)
### Project Structure
├── blocks/ # [X] blocks implemented
├── templates/ # [Y] templates (or N/A)
├── scripts/
│ ├── aem.js # Core library (DO NOT MODIFY)
│ ├── scripts.js # Custom page decoration
│ ├── delayed.js # Analytics, marketing tools
│ └── utils.js # Shared utilities
├── styles/
│ ├── styles.css # Critical styles + design tokens
│ ├── fonts.css # Font definitions
│ └── lazy-styles.css # Non-critical styles
└── icons/ # SVG icons
### Three-Phase Loading (E-L-D)
Edge Delivery Services uses a strict Eager-Lazy-Delayed loading strategy to achieve a Lighthouse score of 100. Every file and script must be placed in the correct phase.
| Phase | Purpose | What Loads | Performance Impact |
|-------|---------|------------|-------------------|
| **Eager** | Render above-the-fold content as fast as possible (LCP) | `styles/styles.css`, first section's blocks, `scripts/scripts.js` | Blocks LCP — keep minimal |
| **Lazy** | Load remaining page content after first paint | Remaining blocks, header, footer, `fonts.css`, `lazy-styles.css` | Runs after Eager completes — safe for non-critical UI |
| **Delayed** | Load non-essential third-party scripts | `scripts/delayed.js` (analytics, marketing tags, chat widgets) | Runs ~3s after page load — never blocks rendering |
**Rules for developers:**
- **Never add third-party scripts to `scripts.js`** — they block LCP. Always use `delayed.js`.
- **Never load fonts eagerly** — `fonts.css` is loaded lazily to avoid render-blocking.
- Blocks in the first section load eagerly; all others load lazily. This is automatic based on DOM position.
- The header and footer are loaded in the lazy phase via `loadHeader()` / `loadFooter()` from `aem.js`.
### Key Files & How They Connect
| File | Role | Connects To |
|------|------|-------------|
| `scripts/aem.js` | Core library — `loadBlock`, `loadCSS`, `decorateBlock`, `sampleRUM`, `loadHeader`/`loadFooter`. **DO NOT MODIFY.** | Imported by `scripts.js` and blocks |
| `scripts/scripts.js` | Entry point — orchestrates E-L-D phases (`loadEager` → `loadLazy` → `loadDelayed`). Contains `buildAutoBlocks`, `decorateMain`, template loading. | Imports from `aem.js`; may import `utils.js` |
| `scripts/delayed.js` | Loaded last via `loadDelayed()` — analytics, marketing tags, third-party scripts. | Called by `scripts.js` in delayed phase |
| `scripts/utils.js` | Shared helpers used across blocks and scripts. | Imported by blocks and `scripts.js` |
| `styles/styles.css` | Critical CSS + design tokens (CSS custom properties). Loaded eagerly. | Referenced by all blocks and pages |
| `styles/fonts.css` | Font `@font-face` declarations. Loaded lazily. | Font families referenced in `styles.css` |
| `styles/lazy-styles.css` | Non-critical global styles. Loaded lazily. | Supplements `styles.css` |
| `blocks/{name}/{name}.js` | Block logic — exports `default function decorate(block)`. Auto-loaded when the block class appears in DOM. | May import from `utils.js` or `aem.js` |
| `blocks/{name}/{name}.css` | Block styles — auto-loaded alongside block JS. | May use CSS custom properties from `styles.css` |
| `templates/{name}/{name}.js` | Template logic — loaded when page metadata has matching `template` value. Customizes page structure before blocks load. | Called by `scripts.js` via `loadTemplate()` |
**Execution flow:** page load → `scripts.js` → `loadEager()` (first section + eager blocks) → `loadLazy()` (remaining blocks, header, footer, `fonts.css`, `lazy-styles.css`) → `loadDelayed()` (loads `delayed.js`)
## Local Development Setup
### Prerequisites
- Node.js [version from .nvmrc]
- AEM CLI: `npm install -g @adobe/aem-cli`
### Setup Steps
```bash
# Clone repository (use the code repo URL from Config Service)
git clone {code-repo-url}
cd {repo}
# Install dependencies
npm install
# Start local server
aem up
Local Server
- URL: http://localhost:3000
- Auto-reload on file changes
- Uses preview content by default
If repoless/multi-site was detected in Phase 1.3, also include this subsection in the output:
Local Server — Repoless/Multi-Site
For repoless setups with multiple sites sharing one repository, you must specify the site's preview URL when starting the local server:
bash
aem up --pages-url=https://main--{site}--{org}.aem.page
Replace
and
with the actual site and organization names from the Config Service.
Without
, the AEM CLI cannot resolve content for the correct site and local preview will fail or show wrong content.
If the project is a standard single-site setup, omit the repoless subsection entirely — it would only confuse developers.
Linting
Design System
CSS Custom Properties
Typography
css
--body-font-family: [value];
--heading-font-family: [value];
Colors
css
--primary-color: [value];
--secondary-color: [value];
--background-color: [value];
--text-color: [value];
Spacing
css
--spacing-s: [value];
--spacing-m: [value];
--spacing-l: [value];
--nav-height: [value];
Breakpoints
| Name | Min-Width | Usage |
|---|
| Mobile | 0 | Default styles |
| Tablet | 600px | @media (min-width: 600px)
|
| Desktop | 900px | @media (min-width: 900px)
|
| Large | 1200px | @media (min-width: 1200px)
|
Fonts
| Family | Weights | Usage |
|---|
| [Font Name] | [weights] | [body/headings] |
Blocks Reference
| Block | Purpose | Variants | Key Features |
|---|
| [block-name] | [What it does] | , | [Important details, gotchas] |
[Generate one row per customized block]
Templates
| Template | Purpose | Applied Via | Special Behavior |
|---|
| [template-name] | [What type of pages] | | [What it does differently] |
[Generate one row per customized template. Skip templates with only "initial commit".]
Common Development Tasks
Add a New Block
- Create
- Create
- Implement
export default function decorate(block)
- Test locally at http://localhost:3000
- Push to feature branch
Modify Global Styles
- Edit
- Use CSS custom properties
- Test across multiple pages
- Watch for CLS impact
Add Analytics/Marketing Tool
- Add to
- Never add to (blocks performance)
- Test with Network tab to verify delayed loading
Debug a Block
- Check browser console for errors
- Inspect DOM: expected structure vs actual
- Check if variant classes are applied
- Verify CSS specificity
Environments
Git Workflow
Branch Naming
- Features:
- Fixes:
- Keep names short (URL length limits)
PR Requirements
- Include preview URL in PR description
- Pass linting
- Test on feature branch preview
- No console errors
Troubleshooting
Block not loading?
- Check block folder name matches class name
- Verify JS exports
default function decorate
- Check browser Network tab for 404s
Styles not applying?
- Check CSS specificity
- Verify file is loading (Network tab)
- Check for CSS syntax errors
Content not updating?
- Clear browser cache
- Check preview was triggered in DA
- Wait for CDN cache (1-2 min)
Resources
Support Contacts
[Add project-specific contacts]
---
### 5.1 Convert to Professional PDF (MANDATORY)
**THIS STEP IS NOT OPTIONAL. YOU MUST GENERATE THE PDF NOW.**
1. Save markdown to: `project-guides/DEVELOPER-GUIDE.md`
- File MUST start with YAML frontmatter:
```yaml
---
title: "[Project Name] - Developer Guide"
date: "[Full Date - e.g., February 17, 2026]"
---
```
- **Date format**: Always use full date with day, month, and year (e.g., "February 17, 2026"), NOT just month and year
2. **IMMEDIATELY after saving the markdown**, invoke the PDF conversion skill:
Skill({ skill: "project-management:whitepaper", args: "project-guides/DEVELOPER-GUIDE.md project-guides/DEVELOPER-GUIDE.pdf" })
3. Wait for PDF generation to complete (whitepaper skill auto-cleans source files)
**DO NOT:**
- Skip the PDF conversion step
- Tell user "PDF will be generated later" — generate it NOW
### 5.2 Deliver to User
After PDF is generated, inform the user:
"✓ Developer guide complete: project-guides/DEVELOPER-GUIDE.pdf"
---
## Output
**FINAL OUTPUT:** `project-guides/DEVELOPER-GUIDE.pdf`
All source files (.md, .html, .plain.html) are deleted after PDF generation. Only the PDF remains.
**Location:** `project-guides/` folder
---
## Success Criteria
**Data Source Validation (CRITICAL):**
- [ ] Config Service API was called (`https://admin.hlx.page/config/{ORG}/sites.json`)
- [ ] Site list came from API response, NOT from fstab.yaml or codebase analysis
- [ ] Repoless/standard determination came from Config Service, NOT inferred from code
**Content Validation:**
- [ ] Quick Reference with all project URLs
- [ ] Architecture overview accurate to project
- [ ] Design system fully documented (tokens, fonts, breakpoints)
- [ ] Project-specific blocks documented
- [ ] Custom scripts.js functions documented
- [ ] delayed.js integrations documented
- [ ] Templates documented (if applicable)
- [ ] Local development setup verified
- [ ] Common tasks have clear instructions
- [ ] Troubleshooting section covers common issues
- [ ] Resources linked
**Output Validation:**
- [ ] PDF generated successfully
- [ ] All source files cleaned up (only PDF remains)
---
## Tips for Clear Documentation
1. **Focus on what's unique** - Document project-specific implementations
2. **Use code examples** - Show actual code from the project
3. **Document the "why"** - Explain reasoning behind custom implementations
4. **Include gotchas** - Note any tricky behavior or edge cases
5. **Test the setup instructions** - Verify they work from scratch
6. **Keep it maintainable** - Don't over-document things that change often