From migrate-drupal-canvas-source
Full site migration to Acquia Source using specialized sub-agents. Discovers all pages, extracts verbatim content and responsive CSS, builds missing components with Storybook visual verification, uploads to Source, configures site identity and menus, creates all page content from verbatim text files, and validates every page visually against the source with side-by-side comparison. Invoke with /migrate-site <source-url>. Handles complete multi-page site migration including branding, navigation, media assets, layout matching, and structured post-migration review.
npx claudepluginhub ajv009/drupal-devkitThis skill uses the workspace's default tool permissions.
Migrate an entire website into Acquia Source — every page, every menu, every asset — validated visually against the original.
Triage and build any site shape — lander (1 page), minisite (~4 pages), or full website — and graduate between them. Routes to per-shape build flow, reads from business reference files, deploys to Cloudflare Pages with git auto-deploy. Use when: (1) Operator says 'I want a site' / 'I want a lander' / 'spin up a one-pager' (2) Setting up a new site of any shape from offer + audience material (3) Updating / iterating on an existing site (4) Graduating a site to a new shape (lander → minisite → website → website + CMS) (5) Previewing or publishing changes Triggered by: /mb-site, 'build a site', 'landing page', 'lander', 'minisite', 'website', 'I need a site', 'spin up a site', 'put this online', 'publish site', 'deploy site', 'update my site', 'graduate my site', 'add a CMS to my site'
Executes Webflow CMS migrations from WordPress, Contentful, Strapi, CSV/JSON using Data API v2 bulk endpoints, data mapping, validation, and strangler fig pattern.
Autonomously builds multi-page websites via baton-passing loop: generates HTML/CSS/Tailwind pages with Claude or Stitch, integrates, verifies visually via browser automation, queues next tasks.
Share bugs, ideas, or general feedback.
Migrate an entire website into Acquia Source — every page, every menu, every asset — validated visually against the original.
$1 — Source site URL (required). The origin website to migrate from. Example: https://example.comAUTONOMOUS EXECUTION — DO NOT ASK FOR USER INPUT.
This skill runs end-to-end without asking for user confirmation or approval. Make decisions independently — but decisions must be informed by reading source HTML/CSS, component specifications, and Canvas documentation before acting. Never use plan mode (it requires user approval). Never ask "should I proceed?" or "would you like me to..." — just do it.
Autonomous does NOT mean fast or careless. You must still:
Before each phase, reason through what needs to happen step by step. Read component.yml AND index.jsx before composing JSON. Map full component trees before writing content. Think hard — autonomous doesn't mean impulsive.
If a step fails, attempt to fix it. If it cannot be fixed after two attempts, log the failure in the migration artifacts and move on to the next step.
This skill uses specialized sub-agents for different phases. The main session acts as an orchestrator — it delegates work, collects results, and coordinates phase transitions. It does NOT do the heavy lifting itself.
| Agent | Model | When Used |
|---|---|---|
site-analyzer | Sonnet | Phase 0-1: Crawl source, extract verbatim content, take screenshots |
css-extractor | Sonnet | Phase 0-1: Multi-viewport CSS analysis |
component-auditor | Sonnet | Phase 2: Audit components against section requirements, identify gaps |
component-builder | Opus | Phase 3: Build components with structural Storybook verification |
storybook-qa | Sonnet | Phase 3.5: Visual QA scan — compare Storybook renders against source screenshots |
component-fixer | Opus | Phase 3.5: Targeted fix for ONE QA issue at a time |
upload-verifier | Sonnet | Phase 4: Preflight, disable-all, enable-one-at-a-time with live verification |
media-handler | sonnet | Phase 5: Download and upload media assets |
site-configurator | Sonnet | Phase 6: Site identity (name, logo, favicon) and page path aliases |
menu-builder | Sonnet | Phase 6: Create and configure all menus via browser automation |
content-composer | Opus | Phase 7: Compose page JSON from verbatim content files |
phase-verifier | sonnet | Phase 4, 7: Focused single-target verification (component smoke test, section check, page check) |
artifact-checker | sonnet | Phase transitions: Validates artifacts before allowing next phase |
visual-verifier | Sonnet | Phase 8: Comprehensive final review across ALL pages with responsive testing |
The main session (orchestrator) is a lightweight coordinator. It spawns agents, verifies their output artifacts, and decides when to proceed. It does NOT do heavy lifting itself — every phase is delegated to a specialized agent.
The orchestrator handles ONLY:
blocked.mdThe orchestrator does NOT:
The orchestrator NEVER invokes skills directly. All skill-dependent work is delegated to agents that have those skills. This prevents skill content from loading into the orchestrator's context and wasting tokens.
| Skill | Used By Agent(s) |
|---|---|
acquia-source-docs-explorer | component-auditor, upload-verifier, site-configurator, menu-builder, component-builder, component-fixer |
component-authoring | component-builder, component-fixer, component-auditor, upload-verifier, content-composer |
create-component | component-builder |
stories | component-builder |
content-management | content-composer, menu-builder |
If the orchestrator needs platform info, it checks SKILL.md's Troubleshooting section or delegates a targeted query to the agent that has the skill.
Custom agents are defined in .claude/agents/. Spawn them using the Agent tool with the agent's name field as subagent_type. Always provide a detailed prompt with all input file paths — agents start with no context.
| Phase | Agent | subagent_type | run_in_background |
|---|---|---|---|
| 0-1 | site-analyzer | site-analyzer | No (need artifacts before Phase 2) |
| 0-1 | css-extractor | css-extractor | No (need artifacts before Phase 2) |
| 2 | component-auditor | component-auditor | No |
| 3 | component-builder | component-builder | No (need components before Phase 3.5) |
| 3.5 | storybook-qa | storybook-qa | No (orchestrator dispatches per iteration) |
| 3.5 | component-fixer | component-fixer | No (orchestrator dispatches per issue) |
| 4 | upload-verifier | upload-verifier | No |
| 4 | phase-verifier | phase-verifier | Yes (parallel per-component smoke tests) |
| 5 | media-handler | media-handler | Yes — launch alongside Phase 3 |
| 6 | site-configurator | site-configurator | No |
| 6 | menu-builder | menu-builder | Launch in parallel with site-configurator |
| 7 | content-composer | content-composer | No (need page deployed before verify) |
| 7 | phase-verifier | phase-verifier | Yes (verify page N while composing page N+1) |
| 1→2 | artifact-checker | artifact-checker | No (gate before Phase 2) |
| 2→3 | artifact-checker | artifact-checker | No (gate before Phase 3) |
| 3→3.5 | artifact-checker | artifact-checker | No (gate before Phase 3.5) |
| 3.5→4 | artifact-checker | artifact-checker | No (gate before Phase 4) |
| 4→5 | artifact-checker | artifact-checker | No (gate before Phase 5) |
| 8 | visual-verifier | visual-verifier | No (final review, need results) |
Parallel launch examples:
site-analyzer AND css-extractor in one message (two Agent tool calls)media-handler with run_in_background: true as early as Phase 3site-configurator AND menu-builder in one messagecontent-composer finishes page N, launch phase-verifier for page N with run_in_background: true, then immediately start content-composer for page N+1The #1 failure mode of previous migrations was text hallucination. After context compaction, the agent had no verbatim text reference and fabricated plausible-sounding but factually wrong copy for every paragraph, card description, and subtitle.
Prevention: The site-analyzer agent extracts ALL text verbatim into docs/migration/content/<page>.md files. These files are the ONLY source of truth for text content. The content-composer agent copies text character-for-character from these files. It NEVER generates text. If text is missing from the content file, it flags [MISSING CONTENT] rather than inventing.
These are not guidelines — they are the standards that separate a faithful migration from a superficial copy.
progress.md.component.yml AND index.jsx before composing component JSON.When something fails or is unexpected, investigate before logging as blocked. This is a migration, not a checklist — the source platform may work differently than expected.
/admin/config/system/site-information returns 403, browse /admin/config/system/ to discover what's available. Try /admin/config/system/site-settings./admin and explore the menu tree. Click into categories. Read labels. The admin UI is discoverable.npm run content — each gives a different view. When one fails, try another.decisions.md.Acquia Source provides web-based editors for inspecting deployed components and composing pages.
| Tool | URL | Purpose |
|---|---|---|
| Component Code Editor | <CMS_URL>/canvas/code-editor/component/<name> | View deployed component source, props, slots, and live preview |
| Page Editor | <CMS_URL>/canvas/editor/canvas_page/<id> | Visual page composition, component tree inspection |
Use the Code Editor to verify that uploaded components have the correct props, slots, and rendering. Use the Page Editor to inspect how components are composed on a page and debug layout issues.
Two browser tools are available. They serve different purposes — do not mix them up.
Claude Chrome (claude-in-chrome MCP) — use for:
.env as CANVAS_PUBLIC_URL.Playwright — use for:
The rule: If the action should be visible to the user watching the migration (admin changes, live site verification, visual comparisons), use Claude Chrome. If the action is background development work (inspecting source, checking Storybook, extracting data), use Playwright.
After every major visual change (component upload, page content creation, section update, site config change), switch to the public site in Claude Chrome and refresh. Navigate to the page and section you just changed. The user is evaluating your work in real time — show them the result.
All migration state is persisted to docs/migration/ in the repo root. This serves as memory across sessions if context runs out.
| File | Purpose |
|---|---|
docs/migration/plan.md | Full migration plan: page inventory, section inventories, component mapping |
docs/migration/section-reference.md | Section-to-screenshot mapping with structural notes for each section |
docs/migration/content/<page>.md | Verbatim text content for each page — one file per page, extracted by site-analyzer |
docs/migration/css-audit.md | Responsive CSS analysis: computed styles at 5 viewport widths, breakpoint transitions |
docs/migration/design-tokens.md | Global theme tokens: colors, gradients, typography, spacing, effects |
docs/migration/media-map.md | Table of downloaded media: source URL → local path → target_id |
docs/migration/progress.md | Current progress: which pages/sections are done, which remain, any blockers |
docs/migration/decisions.md | Decisions made autonomously: component choices, approximations, skipped items |
docs/migration/blocked.md | All blocked, failed, or skipped operations for the user to review and retry |
docs/migration/ at the start. Write plan.md first.progress.md current so a new session can pick up exactly where the last one left off.progress.md exists, resume from where it stopped.decisions.md with the error, what was tried, and the resolution or workaround.docs/migration/blocked.md with enough detail for the user to act on. Then move on.blocked.md first. If the user has resolved items (marked with [x]), retry those operations before continuing.docs/migration/content/<page>.md files contain verbatim source text. Never edit them to "improve" the text. Only edit to fix extraction errors (e.g., missing sections, garbled characters).progress.md:
# Migration Progress
Source: https://example.com Target: https://mysite.acquia.site Started: 2026-03-10
## Phases
- [x] Phase 0-1: Discovery + Deep Analysis (parallel agents)
- [x] Phase 2: Component audit
- [x] Phase 3: Build missing components
- [x] Phase 3.5: Storybook QA loop
- [x] Phase 4: Upload components
- [ ] Phase 5: Media assets (7/12 done)
- [ ] Phase 6: Site configuration
- [ ] Phase 7: Page content
- [ ] Phase 8: Post-migration review
## Pages
| # | Page | Path | Status |
|---|------|------|--------|
| 1 | Homepage | / | Content ready |
| 2 | Services | /services | Not started |
| 3 | About | /about | Not started |
| 4 | Contact | /contact | Not started |
## Site Configuration
- [ ] Site name
- [ ] Logo
- [ ] Favicon
- [ ] Main menu (0/4 items)
- [ ] Footer menu (0/8 items)
- [ ] Social media menu (0/3 items)
blocked.md:
# Blocked / Failed Operations
Items the migration could not complete. Mark with [x] once resolved, and the next run will retry them automatically.
## API / Auth Failures
- [ ] `npm run content -- create content/page/new-about.json` — 422 Unprocessable Entity: "langcode: permission denied". Remove langcode field from JSON before retrying.
## Media Downloads
- [ ] `https://example.com/hero-bg.webp` — 403 Forbidden. Asset behind auth. Needs manual download to `/tmp/migration-hero-bg.webp`.
## Site Configuration
- [ ] Site name — browser automation failed, admin UI not accessible. Set manually at /admin/config/system/site-settings.
- [ ] Logo upload — file too large for admin UI upload. Resize and retry.
## Content / Page Composition
- [ ] Section 4 (Testimonials) — 422: "inputs.quote: field is required". Component expects `quote` prop but content used `text`.
## Out of Scope
- [ ] Contact form — platform does not support webforms. Left as placeholder component. Requires manual setup or third-party integration.
section-reference.md:
# Section Reference
Visual and structural reference for each section across all pages. Screenshots saved to `docs/migration/screenshots/`.
## Homepage
| # | Section | Screenshot (Desktop) | Screenshot (Mobile) | Structure |
|---|---------|-----------|-------------------|-----------|
| 1 | Hero banner | `screenshots/pages/home/desktop/section-01-hero.png` | `screenshots/pages/home/mobile/section-01-hero.png` | Full-width, background image, h1 + subtitle + CTA button, dark overlay |
| 2 | Services grid | `screenshots/pages/home/desktop/section-02-services.png` | `screenshots/pages/home/mobile/section-02-services.png` | 3-column card grid, icon + heading + text per card |
| 3 | About preview | `screenshots/pages/home/desktop/section-03-about.png` | `screenshots/pages/home/mobile/section-03-about.png` | 2-column: image left, text + button right |
| 4 | Footer | `screenshots/pages/home/desktop/section-04-footer.png` | `screenshots/pages/home/mobile/section-04-footer.png` | 4-column: logo+tagline, 2 menu columns, contact info. Social icons row. Copyright bar. |
Full-page screenshots: `screenshots/pages/home/desktop/full-page.png`, `screenshots/pages/home/mobile/full-page.png`
## Cross-Page Elements
| Element | Screenshot (Desktop) | Screenshot (Mobile) | Structure |
|---------|-----------|-------------------|-----------|
| Header | `screenshots/global/header-desktop.png` | `screenshots/global/header-mobile.png` | Logo left, main menu center, CTA button right. Sticky. |
| Footer | `screenshots/global/footer-desktop.png` | `screenshots/global/footer-mobile.png` | Logo + tagline column, Services menu, Company menu, contact info. Social row. Copyright. |
content/homepage.md (example):
# Homepage Content (Verbatim)
Source: https://example.com/
Extracted: 2026-03-11 via Playwright
## Section 1: Hero
### h1
AI Implementation Specialists
### subtitle (p)
We build the AI frameworks others use. From strategy to production, we help organisations transform with AI that actually works.
### button 1
Text: "Explore Solutions"
Link: /services
Variant: primary (solid white on dark)
### button 2
Text: "Get In Touch"
Link: /contact
Variant: outline-light
## Section 2: What We Do
### h2
What We Do
### cards
#### Card 1: AI Strategy
Heading: AI Strategy
Text: We sit with your leadership team, identify the highest-impact opportunities, and build an AI roadmap you can actually execute.
Link text: Learn more
Link href: /services
Before starting, verify the project is ready:
docs/migration/progress.md if it exists. If a previous session made progress, resume from where it stopped.src/components/ has components with component.yml and index.jsx files.npm run canvas:validate to confirm Canvas CLI can connect. If it fails, check .env configuration.npm run content -- list --types to confirm JSON:API access works. Verify write access by checking that menu_items--main and page types are listed.npm run content -- list page to verify read works. If writes later fail with HTTP 405, JSON:API is in read-only mode — see content-management skill for fix.If pre-flight fails, log the failure to docs/migration/decisions.md and attempt to fix it. Do not stop.
An append-only JSONL journal (docs/migration/state.jsonl) tracks every significant action during migration, surviving context compaction. Hooks handle agent-boundary and system logging automatically.
Before launching any agents, create the journal and append the migration_started event:
mkdir -p docs/migration
touch docs/migration/state.jsonl
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","phase":0,"agent":"orchestrator","action":"migration_started","detail":{"source_url":"<url>","target_url":"<url>","session_id":"'$(date +%s)'"},"level":"system"}' >> "${CLAUDE_PROJECT_DIR:-.}/docs/migration/state.jsonl"
This activates convention-based hooks (SubagentStop, PreCompact, SessionStart) which check for state.jsonl existence.
Before moving to the next phase, append a phase_completed event:
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","phase":N,"agent":"orchestrator","action":"phase_completed","detail":{"phase":N,"summary":"<one-line summary>"},"level":"system"}' >> "${CLAUDE_PROJECT_DIR:-.}/docs/migration/state.jsonl"
After compaction, the SessionStart hook prints a context message. The orchestrator must:
state-summarizer agentdocs/migration/state-summary.md)After Phase 8 completes:
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","phase":8,"agent":"orchestrator","action":"migration_completed","detail":{"pages_deployed":N,"components_enabled":N,"blockers_remaining":N,"total_events":'$(wc -l < docs/migration/state.jsonl)'},"level":"system"}' >> "${CLAUDE_PROJECT_DIR:-.}/docs/migration/state.jsonl"
The JSONL file becomes the permanent audit trail for the migration.
Each sub-agent fetches its own relevant documentation via the acquia-source-docs-explorer skill as part of its "Before Starting" step. The orchestrator does NOT need to fetch docs — the agents that have the skill handle this:
component-auditor fetches docs on components, props, slots, known issuescomponent-builder fetches docs on component structure, props, slots, and known issues via acquia-source-docs-explorercomponent-fixer fetches docs on component styling and known issues via acquia-source-docs-explorer; storybook-qa does not fetch docs (uses design-tokens.md and css-audit.md as ground truth)upload-verifier fetches docs on upload behavior and deploymentsite-configurator fetches docs on site settings and admin paths; menu-builder fetches docs on menu managementupload-verifier fetches docs on known issues when neededAgents log which docs they consulted in docs/migration/decisions.md.
Launch TWO agents in parallel — use two parallel Agent tool calls in a single message:
Agent 1: site-analyzer (Sonnet)
Prompt the agent with the source URL. It will:
docs/migration/content/<page>.md — one file per pageplan.md, section-reference.md, media-map.md (URLs only, not yet downloaded)Agent 2: css-extractor (Sonnet)
Prompt with the source URL and page list (if known from sitemap). It will:
docs/migration/css-audit.md and docs/migration/design-tokens.mdAfter both agents complete, verify artifacts:
ls docs/migration/screenshots/pages/find docs/migration/screenshots/pages/ -name '*.png' | wc -lCreate docs/migration/progress.md and mark Phase 0-1 complete.
Artifact gate: Spawn artifact-checker (sonnet) with mode phase-1. If it returns FAIL, re-run the relevant agent targeting the specific gaps listed. Do not proceed to the next phase until artifact-checker returns PASS.
Component auditing requires reading every component's component.yml AND index.jsx, comparing capabilities against every section across all pages, and checking global.css design tokens. This is expensive in the orchestrator's context. Delegate to the component-auditor agent.
Spawn component-auditor agent (Sonnet) with:
src/components/ (all existing components)docs/migration/plan.md (page inventories with per-page section inventories)docs/migration/section-reference.md (section structural notes and screenshot paths)docs/migration/css-audit.md (responsive CSS data)docs/migration/design-tokens.md (extracted theme tokens)docs/migration/screenshots/ (source section screenshots for visual reference)The agent will:
component.yml and index.jsx to inventory capabilities (props, slots, layout, variants)global.css @theme block against design tokens — flag missing colors, gradients, fontsdocs/migration/plan.md and gap list + decisions to docs/migration/decisions.mdAfter the agent completes, verify:
Update progress.md.
Artifact gate: Spawn artifact-checker (sonnet) with mode phase-2. If it returns FAIL, re-run the relevant agent targeting the specific gaps listed. Do not proceed to the next phase until artifact-checker returns PASS.
For each gap identified in Phase 2 — whether a new component or a modification to an inadequate existing one:
Spawn component-builder agent (Opus) for each component. The agent will:
index.jsx + component.yml) following existing patterns.src/stories/atoms/<name>.stories.tsxsrc/stories/molecules/<name>.stories.tsxsrc/stories/organisms/<name>.stories.tsxcanvas:validate and canvas:ssr-test.Storybook Atomic Structure:
All stories must be placed in the correct atomic level:
src/stories/atoms/ — button, heading, text, image, spacer, logo, videosrc/stories/molecules/ — card, blockquote, logo_card, search_form, search_button, breadcrumbsrc/stories/organisms/ — hero, card_container, two_column_text, grid_container, stats_banner, contact_section, header, footer, main_navigation, sectionsrc/stories/templates/ — PageLayoutsrc/stories/pages/ — Full page compositions (Home, Services, About, Contact)The component-builder agent creates stories at the correct level with VERBATIM content from docs/migration/content/. After individual component stories pass structural verification, compose full-page stories in src/stories/pages/ and verify sections are present in correct order.
If no gaps exist, skip this phase. Update progress.md.
Artifact gate: Spawn artifact-checker (sonnet) with mode phase-3. If it returns FAIL, re-run the relevant agent targeting the specific gaps listed. Do not proceed to the next phase until artifact-checker returns PASS.
After the Phase 3 artifact gate passes, run the QA loop. This is a mechanical scan-fix-rescan cycle using only two agents: storybook-qa (finds issues) and component-fixer (fixes issues). The orchestrator dispatches agents and tracks state — nothing more.
Orchestrator rules for Phase 3.5:
The loop — max 3 iterations:
┌─ Iteration N ──────────────────────────────────────────────┐
│ 1. SCAN: spawn storybook-qa → finds issues, writes │
│ docs/migration/issues/NN-*/issue.md + qa-summary.md │
│ 2. READ: orchestrator reads qa-summary.md only │
│ - If 0 critical/high issues → QA CLEAN → exit loop │
│ - If issues found → continue to FIX │
│ 3. FIX: for each open critical/high issue, spawn │
│ component-fixer with the issue path │
│ 4. Log iteration to state.jsonl │
│ 5. Go to next iteration (re-scan to verify fixes) │
└─────────────────────────────────────────────────────────────┘
Step by step:
Create docs/migration/issues/ directory
SCAN — Spawn storybook-qa agent (Sonnet) with:
section-reference.md, design-tokens.md, css-audit.md, screenshots directoryREAD summary — After storybook-qa completes, read ONLY docs/migration/qa-summary.md. This file contains a table of issues with severity, component name, and issue path. Do NOT read individual issue.md files.
CHECK — Count critical/high issues with status open:
qa_loop_clean to state.jsonl → Phase 3.5 complete → continue to artifact gateblocked.md → Phase 3.5 complete with known issuesFIX — For each open critical/high issue listed in qa-summary.md, spawn component-fixer agent (Opus) with:
docs/migration/issues/01-hero-parallax/)design-tokens.md, css-audit.mdresolved, deferred, or open with incremented fix_attempts)fix_attempts >= 2 for an issue, component-fixer auto-escalates to deferred and logs to blocked.mdLOG — After all fixers complete for this iteration, append to state.jsonl:
{"phase":3.5,"agent":"orchestrator","action":"qa_iteration_complete","detail":{"iteration":N,"issues_found":X,"issues_fixed":Y,"issues_deferred":Z}}
VERIFY — Go back to step 2 (SCAN) for the next iteration. The storybook-qa agent will re-scan ONLY the components that were fixed, plus check for regressions from global.css changes. This verification scan is mandatory — do NOT skip it.
After the loop exits, update progress.md.
Artifact gate: Spawn artifact-checker (sonnet) with mode phase-3.5. If it returns FAIL, review the remaining issues and decide whether to re-run the QA loop or proceed with known issues logged to blocked.md. Do not proceed to the next phase until artifact-checker returns PASS.
Upload all components and verify each one renders correctly in the Canvas Code Editor. Delegate the pipeline to the upload-verifier agent.
Spawn upload-verifier agent (Sonnet) with:
.env CANVAS_SITE_URL)The agent will:
npm run canvas:preflight to validate all components locally — fix any failuresnpm run canvas:upload<CMS_URL>/canvas/code-editor/component/<name>:
component.ymlindex.jsxdocs/migration/blocked.mddocs/migration/progress.mdAfter the agent completes, check its report:
Do NOT proceed to Phase 5 until every component is individually verified (or explicitly blocked).
Update progress.md.
Artifact gate: Spawn artifact-checker (sonnet) with mode phase-4. If it returns FAIL, re-run the relevant agent targeting the specific gaps listed. Do not proceed to the next phase until artifact-checker returns PASS.
Launch media-handler with run_in_background: true at the START of Phase 3 (not after Phase 4). It downloads media while components are being built and uploaded — this phase is fully independent and benefits from early start.
Spawn media-handler agent (sonnet) with the path to docs/migration/media-map.md. The agent will:
npm run content -- uploaddocs/migration/media-map.md with Media UUIDs and target_idsCritical fix from V3: Always use original file paths (/sites/default/files/YYYY-MM/filename.ext), never style/thumbnail URLs (/sites/default/files/styles/.../public/...). Style URLs return AVIF-compressed thumbnails that are too small for production use.
If an image download fails, the agent logs it in blocked.md and continues.
Update progress.md.
Configure the Drupal site settings, page paths, and navigation. Both sub-agents (site-configurator and menu-builder) handle their own browser setup via claude-in-chrome MCP — the orchestrator does NOT interact with the browser in this phase.
CRITICAL — Pre-flight check: Before spawning these agents, verify that claude-in-chrome MCP tools are available by calling mcp__claude-in-chrome__tabs_context_mcp. If this tool is not available or returns an error, STOP and report "BLOCKED: claude-in-chrome MCP is not available. Site configuration and menu building require browser automation via the Chrome extension. Ensure the Claude-in-Chrome extension is running in Chrome." Do NOT proceed with Phase 6 without claude-in-chrome — both agents will waste their entire context trying workarounds that cannot work (Playwright can't authenticate to Acquia Source SSO, JSON:API doesn't expose menus or site settings for writes, curl admin form scraping is unreliable).
Launch site-configurator and menu-builder in parallel (two Agent tool calls in one message). They don't depend on each other — site identity and menus are independent operations.
Read the .env file to extract CANVAS_SITE_URL and CANVAS_PUBLIC_URL for passing to agents.
Site identity setup (name, logo, favicon) and page path alias management require multiple browser automation steps with form inputs, file uploads, and verification. Delegate to the site-configurator agent.
Spawn site-configurator agent (Sonnet) with:
.env CANVAS_SITE_URL).env CANVAS_PUBLIC_URL)docs/migration/plan.md (site identity info: name, logo path, favicon path; page inventory with source paths)docs/migration/logo.svg or docs/migration/logo.png (if available)docs/migration/media-map.md (for favicon if downloaded)The agent handles:
/admin/config/system/site-settings (NOT standard Drupal paths — Acquia Source uses a custom settings page)/ — creating redirects if needed/admin/config/search/path to match source site URLs (e.g., /services, /about, /contact)After the agent completes, check its report:
blocked.mdMenu building via browser automation is extremely expensive (~10-13 tool calls per item) and prone to silent save failures. Delegate ALL menu creation to the menu-builder agent to protect the orchestrator's context.
Spawn menu-builder agent (Sonnet) with:
.env CANVAS_SITE_URL).env CANVAS_PUBLIC_URL)docs/migration/plan.md (extracted by site-analyzer in Phase 0-1)The agent handles:
main, footer, and social-media menusAfter the agent completes, check its report:
blocked.mdcache: 'no-store' fix to the navigation component (see Troubleshooting section: "Menu Items Not Showing in Header/Footer")Update progress.md.
Wrapper discipline: When restructuring page JSON composition (adding/removing wrapper components, changing section nesting), always check the source page's wrapper hierarchy CSS (max-width, padding, margin) from css-audit.md BEFORE making changes. Compare after. Never remove a wrapper component without understanding all the CSS properties it provides.
Build every page in the page inventory, one at a time. Each page is composed by the content-composer agent using VERBATIM text from the content files.
For EACH page in the inventory, use the overlap pattern:
Spawn content-composer agent (Opus) with:
docs/migration/content/<page>.md (verbatim text source — includes contextual metadata comments)docs/migration/section-reference.md (section-to-component mapping)docs/migration/media-map.md (image target_ids)The content-composer reads all inputs and composes JSON with EXACT text from the content file. It deploys the page via npm run content -- create or -- update.
After content-composer finishes page N, spawn phase-verifier for page N in background (run_in_background: true, mode page-check) with:
CANVAS_PUBLIC_URL in .env, with ?cb=<timestamp> cache buster)docs/migration/content/<page>.md for text verification
Then immediately spawn content-composer for page N+1 (don't wait for verifier).Check phase-verifier results before marking page N as complete. If it reports text mismatches:
Update progress.md after each page.
The content-composer must NEVER generate text. Every text value in the JSON must come from the content MD file. The agent prompt explicitly says: "You are a transcription agent. Copy text exactly. Do not improve, rephrase, or embellish."
Important: On Acquia Source SaaS, header and footer MUST be added as components to EVERY page's JSON. Global page regions exist but are currently buggy with JS code components (slot children get lost, editor crashes — Canvas issue #3536807). Until this is fixed, add header and footer to each page's component array. Header goes first (with logo, main_navigation, and CTA button in slots), footer goes last (with all props and logo in slot). Verify the logo component renders the source site's brand mark, not the placeholder "LOGO" SVG.
This is a SECOND pass — Phase 7 already verified each page individually via phase-verifier. This phase uses the comprehensive visual-verifier agent for cross-page issues, responsive testing, link validation, regressions from later changes, and anything Phase 7 missed.
Spawn visual-verifier agent (Sonnet) with ALL pages to review, mode Final Review. The agent will:
For EACH section on EACH page:
docs/migration/content/<page>.md verbatim referenceAlso check:
Save results to docs/migration/post-migration.md with per-section match/mismatch table
blocked.md with enough detail for manual fix.progress.md — mark Phase 8 complete with final status summary.When composing components for a section, use this JSON structure as a starting point. Adjust component IDs and inputs based on the actual components needed:
{
"uuid": "<generated-uuid>",
"component_id": "js.section",
"inputs": {
"width": "Normal",
"background": "white"
},
"parent_uuid": null,
"slot": null
}
With a child heading inside it:
{
"uuid": "<generated-uuid>",
"component_id": "js.heading",
"inputs": {
"heading": "Section Title",
"headingElement": "h2",
"headingSize": "Large"
},
"parent_uuid": "<section-uuid>",
"slot": "content"
}
If components show errors after uploading, use the Canvas Code Editor to diagnose.
<CMS_URL>/canvas/code-editor/component/<name> — this shows props, source code, and a live preview regardless of component status.npm run content -- list page. If this returns data, the backend is fine.npm run canvas:validate. If all components pass locally, the issue is likely in the page data or a runtime error in the component JavaScript.| Cause | Symptom | Fix |
|---|---|---|
| Component JS throws at runtime | Error in Code Editor preview | Add null guards to props that access .src, .value, etc. Destructure with defaults: const { src } = image || {} |
| Missing null guards on image props | TypeError: Cannot read properties of null | Always guard image prop access: image?.src or const { src } = image || {} |
| target_id as integer instead of string | Image fails to resolve, component receives null | Use "target_id": "31" (string), not "target_id": 31 (integer) |
| link prop as object instead of string | Component receives {"uri": "/", "options": []} instead of "/" | Link/URL props must be plain strings per component.yml format: uri-reference |
| Component removed via Component Library UI | Site breaks completely (known Acquia Source bug) | Never remove components via the library UI — use the Code component panel |
| Global CSS breaks | All components fail | Check global.css for invalid Tailwind directives (directives only work in global.css, not component CSS) |
npm run canvas:upload -- -c <name>. Verify in Code Editor.npm run content -- get page <uuid>), inspect the components array for malformed inputs, fix them, and update.blocked.md and consult Acquia Source documentation via /acquia-source-docs-explorer known issues site unavailable.npm run canvas:validate catches structural issues. But it does NOT catch runtime JavaScript errors (like null pointer exceptions).<CMS_URL>/canvas/code-editor/component/<name> to verify props and preview render correctly.Some source site features cannot be replicated via Canvas components or JSON:API. When encountered:
contact_section with static info) and log in blocked.md as out-of-scope.Do not let out-of-scope items block progress on the rest of the migration.
Use the acquia-source-docs-explorer skill to look up official Acquia Source documentation whenever you need to understand platform behavior, verify admin paths, check for limitations, or troubleshoot unexpected errors.
Critical moments to consult the docs:
components, props, slots, and known-issues-and-limitations to understand what the platform supports and what will break.site-settings, menus, adding-links-menu, and content-workflows to use the correct admin paths and avoid destructive actions.Invoke with: /acquia-source-docs-explorer <query>
Canonical Canvas component development reference skills from the official canvas-starter are available at .claude/reference/canvas-starter/. Key references: canvas-component-definition for component contracts, canvas-component-metadata for component.yml schema, canvas-component-upload for upload error handling.