Full site migration to Drupal CMS using specialized sub-agents. Discovers all pages, extracts verbatim content and responsive CSS, builds missing components with Storybook visual verification, uploads to Drupal CMS, 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 Drupal CMS — every page, every menu, every asset — validated visually against the original.
Executes Webflow CMS migrations from WordPress, Contentful, Strapi, CSV/JSON using Data API v2 bulk endpoints, data mapping, validation, and strangler fig pattern.
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'
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Migrate an entire website into Drupal CMS — 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 |
|---|---|---|
project-setup | Sonnet | Phase -1: Full Drupal CMS + Canvas setup (DDEV, config, OAuth, CSS layer fix, Storybook) |
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 | Haiku | 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 | Haiku | Phase 4, 7: Focused single-target verification (component smoke test, section check, page check) |
artifact-checker | Haiku | 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) |
|---|---|
canvas-docs-explorer | component-auditor, upload-verifier, site-configurator, menu-builder, component-builder, component-fixer |
drupal-cms-setup | project-setup (Phase -1) |
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 |
|---|---|---|---|
| -1 | project-setup | project-setup | No (must complete before any migration work) |
| 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 platform may work differently than expected.
/admin/config/system/ to discover what's available./admin and explore the menu tree. Click into categories. Read labels. The admin UI is discoverable.cd canvas && npm run content — each gives a different view. When one fails, try another.decisions.md.Drupal CMS 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_SITE_URL (on DDEV, this is the same as the 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://efi-ed.ddev.site Started: 2026-03-10
## Phases
- [x] Phase -1: Drupal CMS Setup
- [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
- [ ] `cd canvas && 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-information.
- [ ] 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. The project-setup agent (Phase -1) handles all configuration. After it completes, the orchestrator only needs these quick checks:
docs/migration/progress.md if it exists. If a previous session made progress, resume from where it stopped.docs/migration/setup-report.md — confirms all configuration is in place.cd canvas && npm run canvas:validate to confirm Canvas CLI connects.cd canvas && npm run content -- list page to verify JSON:API read access works.If any check fails and no setup report exists, spawn the project-setup agent. If the setup report exists but validation fails, check the report for which step failed and fix manually.
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 canvas-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 canvas-docs-explorerupload-verifier fetches docs on upload behavior and deploymentsite-configurator fetches docs on site settings and admin paths; menu-builder fetches docs on menu managementAgents log which docs they consulted in docs/migration/decisions.md.
Delegate ALL setup to the project-setup agent. This keeps the orchestrator's context clean — setup involves ~15 bash commands, browser automation for OAuth, and CSS layer fixes that would consume significant context.
Spawn project-setup agent (Sonnet) with:
The agent will:
.envglobal.css (prevents the #1 time sink from previous migrations)After the agent completes, read its setup report. If it reports any failures:
.env and OAuth configurationDo NOT proceed to Phase 0-1 until the setup report shows validation passed.
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 (Haiku) 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:
canvas/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 (Haiku) 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.canvas/src/stories/atoms/<name>.stories.tsxcanvas/src/stories/molecules/<name>.stories.tsxcanvas/src/stories/organisms/<name>.stories.tsxcd canvas && npm run canvas:validate and cd canvas && npm run canvas:ssr-test.Storybook Atomic Structure:
All stories must be placed in the correct atomic level:
canvas/src/stories/atoms/ — button, heading, text, image, spacer, logo, videocanvas/src/stories/molecules/ — card, blockquote, logo_card, search_form, search_button, breadcrumbcanvas/src/stories/organisms/ — hero, card_container, two_column_text, grid_container, stats_banner, contact_section, header, footer, main_navigation, sectioncanvas/src/stories/templates/ — PageLayoutcanvas/src/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 visual verification, compose full-page stories in canvas/src/stories/pages/ and verify the complete page against the source full-page screenshot.
If no gaps exist, skip this phase. Update progress.md.
Artifact gate: Spawn artifact-checker (Haiku) 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 (Haiku) 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:
canvas/.env CANVAS_SITE_URL)The agent will:
cd canvas && npm run canvas:preflight to validate all components locally — fix any failurescd canvas && npm 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 (Haiku) 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 (Haiku) with the path to docs/migration/media-map.md. The agent will:
cd canvas && 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.
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 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)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:
ddev drush config:set system.site name "<Site Name>" -y or via /admin/config/system/site-information/admin/appearance/settings/mercury (Mercury theme settings page) or via drush if a file upload field is available/ — 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)docs/migration/plan.md (extracted by site-analyzer in Phase 0-1)The agent handles:
main, footer, and social-media menussocial-media menu if it does not exist yet (ddev drush menu:create social-media "Social Media" or via /admin/structure/menu/add)After the agent completes, check its report:
blocked.mdUpdate 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 cd canvas && 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_SITE_URL in .env)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: Do NOT add footer components to page JSON. The site renders a global footer automatically via page regions. Adding a footer component to the page creates a duplicate footer.
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 the site shows errors or a component doesn't render after uploading, the issue is typically in the component's JavaScript or its data.
cd canvas && npm run content -- list page. If this returns data, the backend is fine — only the frontend renderer is broken.cd canvas && 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 (not a structural validation issue).<CMS_URL>/homepage (the CMS URL). If the CMS renders the page, the issue is in the Canvas frontend rendering.| Cause | Symptom | Fix |
|---|---|---|
| Component JS throws at runtime | All pages break if it's header/footer; single page if page-specific | 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 (reading 'src') | 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 Canvas bug) | Never remove components via the library UI — use the Code component panel |
| Global CSS breaks | All pages break | Check global.css for invalid Tailwind directives (directives only work in global.css, not component CSS) |
cd canvas && npm run canvas:upload -- -c <name>cd canvas && npm run canvas:uploadcd canvas && npm run content -- get page <uuid>), inspect the components array for malformed inputs, fix them, and update.blocked.md and consult Canvas documentation via /canvas-docs-explorer known issues site unavailable.cd canvas && npm run canvas:validate catches structural issues. But it does NOT catch runtime JavaScript errors (like null pointer exceptions).cd canvas && npm run canvas:upload -- -c <name>.If pages show content but no header or footer, page regions are not configured.
Fix:
ddev drush config:set canvas.page_region.mercury.header status true -y
ddev drush config:set canvas.page_region.mercury.footer status true -y
ddev drush cr
Then verify by refreshing the site in Claude Chrome — header and footer should appear.
If all write operations (POST, PATCH, DELETE) return HTTP 405 Method Not Allowed, JSON:API is in read-only mode.
Fix:
ddev drush config:set jsonapi.settings read_only false -y
ddev drush cr
If API requests return 403 Forbidden, check Drupal permissions.
Fix: Navigate to /admin/people/permissions in Claude Chrome and ensure the appropriate role has the needed permissions. For Canvas API access, the API client's role needs content CRUD permissions.
If menu item endpoints return 404 or are not available in the types list, the module is not enabled.
Fix:
ddev drush en jsonapi_menu_items -y
ddev drush cr
Then verify: cd canvas && npm run content -- list menu_items--main
When the site breaks after component changes and you don't know which component caused it:
status: false in ALL component.yml files.cd canvas && npm run canvas:uploadstatus: true for one component.
b. Upload: cd canvas && npm run canvas:upload -- -- -c <name>
c. Check the site — does it still load?
d. If yes: that component is safe, move to the next.
e. If broken: that component is the culprit.<CMS_URL>/canvas/code-editor/component/<name>) to inspect its source — this works even when status is false.cd canvas && npm run canvas:ssr-test, re-enable, re-upload.Some source site features cannot be replicated via Canvas components or JSON:API. When encountered:
ddev drush pm:list | grep webform. If not available, leave a placeholder component and log in blocked.md.Do not let out-of-scope items block progress on the rest of the migration.
Use the canvas-docs-explorer skill to look up official Canvas 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: /canvas-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.