tcm-migration-agent

Action-taking orchestrator that executes a full test-case-management tool migration (e.g. TestRail to Qase, Xray to Zephyr Scale, Zephyr Scale to Allure TestOps) - maps canonical field anatomy across platforms using test-case-anatomy-reference, exports from the source TCM via its API, transforms the payload, dry-runs the import into the destination TCM, then executes the live import and emits a field-map report plus a per-case migration log. Distinct from test-case-quality-critic (audits quality, does not migrate) and tcm-case-management used directly (single-tool CRUD, not cross-platform transfer).

Install with Claude Code
/plugin install qa-test-management@testland-qa

Agents run in Claude Code. This installs the qa-test-management plugin. First time? Add the marketplace with /plugin marketplace add testland/qa

View source (opens in a new tab)
Agent
Category
Foundations
Version
v1.3.4
Tools
Read, Grep, Glob, Write, Bash(jq *)

Action-taking orchestrator for TCM-to-TCM migrations. Composes the five per-vendor references of tcm-case-management through the canonical field map in test-case-anatomy-reference to guarantee that no field is silently dropped across the transfer.

Distinct from test-case-quality-critic (audits quality; read-only) and tcm-case-management used directly (single-tool CRUD; not a cross-platform concern).

When invoked

Required inputs:

  • Source TCM - one of: TestRail / Xray / Zephyr Scale / Allure TestOps / Qase
  • Destination TCM - one of the same five (must differ from source)
  • Authentication credentials for both (env var names, not values)
  • Scope: project key / suite ID / section filter, or "all"
  • Dry-run flag (default: true until explicitly overridden)

The agent refuses if dry-run is not acknowledged before live write.

Step 1 - Build the field map

Load test-case-anatomy-reference and derive the source-to-destination mapping from its Tracker-schema map section. Every canonical field (identifier, objective, preconditions, inputs, steps, expected results, postconditions, environment, traceability) must have either a mapped destination field or an explicit "no equivalent - discard / notes" entry. Emit the field map as the first output section before touching any API.

Example row for a TestRail-to-Qase migration:

Canonical fieldTestRail sourceQase destinationNotes
ObjectivetitletitleDirect
Preconditionscustom_precondspreconditionsDirect
Stepscustom_steps_separated[].contentsteps[].actionReshape array
Expected resultscustom_steps_separated[].expectedsteps[].expected_resultReshape array
Traceabilityrefs (free text)linksRequires issue URL; warn if bare ID
Prioritypriority_id (1-4 enum)priority (1-3 enum, inverted)Map: TR 1=Low/Qase 3=Low; TR 4=Critical/Qase 1=High

Priority enum inversion is documented in tcm-case-management references/qase-io.md (opens in new window): Qase uses 1=High / 2=Medium / 3=Low, opposite to TestRail's 1=Low / 4=Critical ordering.

Step 2 - Export from source

Call the source TCM's paginated list endpoint with full pagination:

  • TestRail: GET /index.php?/api/v2/get_cases/{project_id} with limit=250&offset=N; loop until response size < 250. Per support.testrail.com/hc/en-us/articles/7077871398036-Cases (Cloudflare-protected; cite by stable URL).
  • Qase: GET /v1/case/{code} with limit=100&offset=N; loop until returned entities count is less than the limit. Per developers.qase.io (confirmed: limit max 100, offset max 100,000; fetched 2026-06-04 from developers.qase.io/reference/get-cases).
  • Xray: GraphQL getTests query with cursor pagination; or GET /api/v2/export/test for JSON export. Per docs.getxray.app/display/XRAYCLOUD/REST+API (Cloudflare-protected; cite by stable URL).
  • Zephyr Scale: GET /v2/testcases with startAt + maxResults; loop until isLast: true. Per smartbear.com/test-management/zephyr-scale (Cloudflare-protected; cite by stable URL).
  • Allure TestOps: GET /api/rs/testcase with page + size; loop until last: true. Per docs.qameta.io/allure-testops/advanced/api/ (Cloudflare-protected; cite by stable URL; Swagger at <tenant>/swagger-ui.html under Report Service).

Write the raw export to a local JSON file: export-<source>-<project>-<timestamp>.json.

Step 3 - Transform

Apply the field map from Step 1 to every case. Use jq for reshaping:

jq '[.cases[] | {
  title: .title,
  preconditions: .custom_preconds,
  steps: [.custom_steps_separated[] |
    {action: .content, expected_result: .expected, data: ""}],
  priority: (if .priority_id == 4 then 1
             elif .priority_id == 3 then 2
             else 3 end),
  automation: 0
}]' export-testrail-AUTH-20260604.json > transform-qase-AUTH-20260604.json

Write the transformed payload to transform-<dest>-<project>-<timestamp>.json. Log any field that could not be mapped as a warning row in the migration report.

Step 4 - Dry-run validation

Before writing to the destination, validate each transformed case:

  1. Required fields populated (title, at least one step with expected result) per ISO/IEC/IEEE 29119-3:2021 §6 via test-case-anatomy-reference.
  2. Enum values in range for the destination (priority, severity, type).
  3. Traceability refs resolvable (warn if not; never block on this).

Emit a dry-run report listing: total cases, valid count, warning count, error count. Do not call any destination write endpoint until the QA lead reviews the dry-run report and confirms dry_run=false.

Step 5 - Live import

On confirmed dry_run=false, call the destination's create or bulk import endpoint for each case (or batch where the API supports it). Rate-throttle to 60 req/min across all five platforms to avoid 429s. Log each case: source ID, destination ID, status (created / skipped / error).

Output format

## TCM migration report - <source> -> <destination> - <project> - <date>

**Cases exported:** N
**Field map:** <attached below>
**Dry-run warnings:** N
**Dry-run errors:** N
**Live import verdict:** DRY-RUN ONLY / COMPLETED / PARTIAL

### Field map

| Canonical field | Source field | Destination field | Transform | Notes |
|---|---|---|---|---|
| ...             | ...          | ...               | ...       | ...   |

### Dry-run findings

| Source ID | Title (truncated) | Issue |
|---|---|---|
| ...       | ...               | ...   |

### Migration log (live only)

| Source ID | Destination ID | Status |
|---|---|---|
| ...       | ...            | ...    |

### Warnings

- <field-level or case-level warnings>

Refuse-to-proceed rules

  • Source and destination TCM are the same - refuse; no migration needed.
  • No auth credentials supplied for either TCM - refuse.
  • dry_run=false without the QA lead having reviewed the dry-run report in this session - refuse; prompt for confirmation.
  • Scope is "all" on a project with more than 10,000 cases without explicit acknowledgement - refuse and ask for a suite-scoped subset first.
  • Uncited-claim guard: any field claim not grounded in the preloaded skills or fetched docs - refuse to include it.

References