---
name: cse-confluence-migrate
description: Safely migrate Confluence page trees for CSE content. Inventories pages, records mappings and local pre-write snapshots, executes supported create/reparent workflows, verifies links and attachments, and reports rollback coverage.
argument-hint: "<source-scope> <destination-scope> [--execute]"
---

# CSE Confluence Migrate

Use this skill for bulk Confluence page-tree movement or restructuring. It is structural: if page prose needs rewriting, pause and hand that page to `cse-confluence-maintenance`.

## Compact MCP routing

- Follow the shared [compact MCP routing contract](../../shared/compact-mcp-routing.md) and [read strategy](../../shared/read-strategy.md). Interactive facade tools are `cse_capabilities`, `cse_read`, `cse_apply`, `context_assemble`, and `cse_session_info`; named operations are capability ids. Call reads through `cse_read` with the capability id. Every write goes through `cse_apply` twice: dry-run preview first, then the identical capability and arguments with `execute:true`, justification of at least 16 characters, and the returned `preview_digest`. Use `cse_session_info` directly for auth recovery.
- **`context_assemble`: N/A.** Migration inventory and tree changes are structural page operations, not entity packs. Call Confluence operations directly.

```bash
source "${PLUGIN_ROOT:-${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$PWD/plugins/cse-tools}}}/.agents/shared/skill-bootstrap.sh"
```

Confluence create, update, reparent, and attachment operations are dual-mode. Dry-run first; execute only with `execute: true`, a concrete justification, and the dry-run's `preview_digest`. Check the tool's inputSchema in `tools/list` for uncertain contracts.

## Hard boundaries

- Default mode is dry-run.
- Execute only after the user approves a machine-readable plan.
- All writes go through the local `cse-tools` MCP Confluence write tools in `cse-toold`; direct `/wiki` writes are forbidden.
- This skill cannot copy, trash, delete, restore, or clean up source pages. Cross-space moves that require copy/trash are unsupported and stop for manual/operator handling.
- Never infer a parent move from Markdown/frontmatter. Content restores and body updates preserve the current parent unless the plan has an explicit `reparent` operation with destructive confirmation.
- Mermaid rendering is optional. If `mmdc` is unavailable, keep Mermaid as code or report `diagram_render_skipped: mmdc_missing`; do not install it during a migration.

## Phases

### 1. Inventory

Read source and destination with Confluence storage APIs:

- source page ids, titles, parent ids, space keys
- descendants and child counts
- labels/properties returned by `confluence_get_page`, plus the attachment metadata manifest
- inbound links/backlinks where available

The agent writes a dry-run inventory report under the Confluence state dir using the concrete formats in `Output files`. This is an agent-maintained review checklist, not a separate planning engine.

### 2. Classify

Classify each page:

- move/reparent candidate
- create destination/parent
- skip: copy required, collision, restriction unreadable, attachment over cap, missing mapping

### 3. Plan

Record a reviewable JSON plan with:

- source page id/title/space/version
- destination parent id/title/space
- operation (`create`, `reparent`, `skip`)
- required snapshot axes
- expected version
- risk notes
- attachment operations (`skip_existing`, `upload_new_filename`, `original_bytes_supplied`, `not_captured`)

The report must include counts: `planned`, `written`, `skipped`, `failed`, `rollback_available`.

### 4. Dry-run verification

Before writes:

- Verify every destination parent is readable.
- Detect title collisions.
- Verify attachment snapshot limits (default 50MB total / 50 attachments per operation).
- Record which labels/properties are visible from `confluence_get_page`; the available migration capabilities cannot write labels, properties, or restrictions, so report those as manual follow-up rather than claiming to copy them.
- Confirm no other approved migration run is writing the same page ids; this checklist does not provide a single-writer lock.
- For import/restore plans, compare source frontmatter parent data to live destination parent data and treat mismatches as warnings unless the operation is explicitly `reparent`.

### 5. Execute

Execute one page at a time. Each mutation must:

1. Fetch the full pre-write page and save that raw JSON in the run directory with mode `0600`; use its filename as the snapshot reference.
2. Perform the write through the local Confluence MCP tool.
3. GET back and verify body/version/parent/attachments as applicable.
4. Append one JSON object per line to the agent-maintained migration event file, including page id, operation, snapshot filename, outcome, and verification assertions.

If any write fails after a mutation, stop the workflow before the next mutation and report rollback options.

### Worked dual-mode calls

Author all page bodies as storage XHTML in `/tmp`; inline `body_storage` is forbidden. Preview first and preserve the reviewed arguments for apply.

**Create a destination page**

```js
cse_apply({
  capability: "confluence_create_page",
  arguments: {
    title: "Migrated Playbook",
    space_id: "123",
    parent_id: "456",
    body_storage_path: "/tmp/migrated-page.html"
  },
  execute: true,
  justification: "Preview the migrated destination page create"
})
```

Inspect the dry-run's request and snapshot, and retain the returned `preview_digest`. Then apply the same operation:

```js
cse_apply({
  capability: "confluence_create_page",
  arguments: {
    title: "Migrated Playbook",
    space_id: "123",
    parent_id: "456",
    body_storage_path: "/tmp/migrated-page.html",
    execute: true,
    preview_digest: "<digest-from-preview>"
  },
  execute: true,
  justification: "Create the migrated destination page"
})
```

**Update copied content**

```js
cse_apply({
  capability: "confluence_update_page",
  arguments: {
    page_id: "789",
    expected_version: 4,
    title: "Migrated Playbook",
    body_storage_path: "/tmp/migrated-playbook.storage.xhtml",
    message: "Apply migration mapping"
  },
  execute: true,
  justification: "Preview the approved destination-content update"
})
```

Then apply:

```js
cse_apply({
  capability: "confluence_update_page",
  arguments: {
    page_id: "789",
    expected_version: 4,
    title: "Migrated Playbook",
    body_storage_path: "/tmp/migrated-playbook.storage.xhtml",
    message: "Apply migration mapping",
    execute: true,
    preview_digest: "<digest-from-preview>"
  },
  execute: true,
  justification: "Apply the approved destination-content update for plan M-2026-07-15"
})
```

**Reparent**

```js
cse_apply({
  capability: "confluence_reparent_page",
  arguments: {
    page_id: "789",
    target_parent_id: "654",
    expected_version: 5
  },
  execute: true,
  justification: "Preview reparent of page 789 under 654"
})
```

After plan approval, fetch the page again. Immediately before each apply, ask for a final destructive confirmation that names the page id/title, its current parent id/title, and the target parent id/title. If any value changed or confirmation is withheld, stop and re-plan. After that distinct confirmation, apply with the preview digest:

```js
cse_apply({
  capability: "confluence_reparent_page",
  arguments: {
    page_id: "789",
    target_parent_id: "654",
    expected_version: 5,
    execute: true,
    preview_digest: "<digest-from-preview>"
  },
  execute: true,
  justification: "Reparent page 789 under 654 per approved plan M-2026-07-15"
})
```

Every preview for an existing page must expose the raw pre-write page snapshot; persist it before apply. Then GET the affected page back:

```js
cse_read({
  capability: "confluence_get_page",
  arguments: { page_id: "789", body_format: "storage" }
})
```

For create, use the returned created page id. Verify title, version, parent, and storage body as applicable before appending the successful JSONL event or starting the next mutation.

### 6. Unsupported copy/trash handoff

If cross-space movement or a Confluence move failure requires copy or trash, stop. The compact capability surface has neither operation, and this skill must not emulate them with create/delete calls. Report the affected source and destination ids, preserve both sides unchanged, and hand the operation to the operator for manual handling.

### 7. Post-check

After execution:

- Verify destination tree shape.
- Parse created destination bodies for attachment references and confirm they resolve.
- Re-run link/backlink survey and report likely orphans.
- Emit final JSON summary and human-readable summary.

## Local review-file shape

When the approved migration plan uses local review files, use this shape:

- page files: `<page-id>-<sanitized-title>.md`
- attachment dirs: `<page-id>_attachments/`
- frontmatter: page id, space, version, parent, children, labels, attachment manifest
- body: Markdown converted from storage HTML for review; storage snapshots remain the rollback source of truth

Upload-side frontmatter is advisory metadata only. `parent.id` is never passed into a content update; only a planned `reparent` operation may change it.

## Native no-app content patterns

For imported Markdown, call the `confluence_markdown_to_storage` capability so page bodies use built-in Confluence constructs instead of Marketplace/Forge app macros:

- callouts: `> [!info]`, `> [!note]`, `> [!tip]`, `> [!warning]`
- status lozenges in tables: `{{status:green\|READY}}`
- expanders: `<details><summary>Details</summary>...</details>`
- tasks: `- [ ]`, `- [x]`
- navigation and issue macros: `{{toc}}`, `{{children:2}}`, `{{jira:CSE-123}}`, `{{jira-jql:...}}`

Rendered diagram/image files should be uploaded as attachments and referenced as images; keep Mermaid code blocks when rendering is unavailable.

## Output files

Store under `${XDG_STATE_HOME:-~/.local/state}/cse-tools/confluence/${OKTA_PROFILE||current}` unless `CSE_CONFLUENCE_STATE_DIR` is set:

- `migration-<timestamp>.plan.json`
- `migration-<timestamp>.jsonl`
- `migration-<timestamp>.summary.json`
- `snapshots/<page-id>-prewrite.json`, referenced by filename from the plan and event rows

Reports must not include secrets. Snapshot JSON is mode `0600`; summaries are mode `0644`. The available attachment read returns metadata, not bytes. Same-name attachment replacement has no rollback unless original bytes were captured separately through a supported read, so otherwise upload under a new filename and set `rollback_available: false` for replacement.

The agent maintains these formats directly:

- plan JSON: `{run_id, source_scope, destination_scope, pages:[{source, destination, operation, expected_version, snapshot_file, attachments, risk_notes}]}`, where `operation` is `create`, `reparent`, or `skip`
- event JSONL: one `{timestamp, page_id, operation, snapshot_file, outcome, assertions, error}` object per attempted operation
- summary JSON: `{run_id, planned, written, skipped, failed, rollback_available, manual_cleanup}` derived from the event rows

## Success criteria

- Dry-run plan lists every page and operation in the documented JSON shape.
- Execute report has counts derived from its JSONL events and a per-page outcome.
- Every existing page mutation has a mode-`0600` raw pre-write snapshot file referenced by filename.
- `rollback_available` is false for any page whose restore requires manual Confluence trash/history action; do not overstate recovery.
- `rollback_available` is false for created pages (no delete capability) and attachment replacement without captured original bytes.
- Plans never contain copy or trash operations; pages requiring either are `skip` with a manual-handoff reason.
