# How to Migrate from Rudder CLI Spec v0.1 to v1


This migration guide helps you migrate your Rudder CLI project from spec version 0.1 to v1.

## Overview

`rudder/v1` is a comprehensive redesign of the original `rudder/v0.1` spec format. It improves consistency across resource kinds, replaces ambiguous field names, adopts snake case conventions, and moves to compact URN-style references throughout.

When you run the `migrate` command, Rudder CLI automatically rewrites the spec files present in the specified location and applies changes related to:

- Spec version upgrades
- Rename resource kinds
- Using compact URN references
- Updating property and custom type definitions
- Updating Tracking Plan rules
- Updating variant definitions
- Updating import metadata
- Updating event stream source references

{{< warning >}}
As the migration process rewrites the spec files in place, RudderStack recommends committing or backing up your project before running the `migrate` command.
{{< /warning >}}

## Run the migration

To migrate your project automatically, run the following command:

```bash
rudder-cli migrate --location <path-to-project>
```

## Breaking changes

The following sections list the breaking changes introduced in the spec format upgrade from v0.1 to v1.

{{< info >}}
The `migrate` command automatically handles these changes.
{{< /info >}}

### Update the version field

The field-name, convention, and reference changes in `rudder/v1` break the existing spec contract. The version is bumped to signal the new contract, so tooling and validation can distinguish it from legacy spec version `rudder/v0.1`.


{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
version: rudder/v1
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
version: rudder/0.1
# or
version: rudder/v0.1
```
{{% /tab %}}
{{< /tabs >}}

### Rename the Tracking Plan kind

The Tracking Plan kind changes from `tp` to `tracking-plan`. The abbreviated `tp` was an opaque abbreviation, while `tracking-plan` is self-documenting and consistent with how the resource is named everywhere else.

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
version: rudder/v1
kind: tracking-plan
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
version: rudder/v0.1
kind: tp
```
{{% /tab %}}
{{< /tabs >}}

### Compact URN references

All cross-resource references change from the path-based format (`#/<kind>/<group>/<id>`) to the compact URN format (`#<resource-type>:<id>`). Path-based references were verbose and order-dependent, while compact URNs are shorter and encode the resource type, making references unambiguous and easier to read.

The following table lists the updated reference format for each resource type:

| Resource | v0.1 | v1 |
|----------|------|-----|
| Property | `#/properties/group/prop_id` | `#property:prop_id` |
| Event | `#/events/group/event_id` | `#event:event_id` |
| Category | `#/categories/group/cat_id` | `#category:cat_id` |
| Custom type | `#/custom-types/group/type_id` | `#custom-type:type_id` |
| Tracking plan | `#/tp/group/tp_id` | `#tracking-plan:tp_id` |

The following example shows an event referencing a category:

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
version: rudder/v1
kind: events
spec:
  events:
    - id: api_tracking
      name: API Tracking
      event_type: track
      category: "#category:user_actions"
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
version: rudder/v0.1
kind: events
spec:
  events:
    - id: api_tracking
      name: API Tracking
      event_type: track
      category: "#/categories/app_categories/user_actions"
```
{{% /tab %}}
{{< /tabs >}}

### Updated property definitions

Property definitions undergo four changes: 

- `propConfig` is renamed to `config` for brevity and consistency with other resource field naming
- `config` keys move to snake case to align with YAML ecosystem conventions
- `type` becomes `types` (an array) to eliminate fragile comma-separated string parsing
- Array item types are hoisted to top-level to reduce nesting and make type information more discoverable

#### `propConfig` renamed to `config`

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
config:
  enum: ["GET", "PUT", "POST"]
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
propConfig:
  enum: ["GET", "PUT", "POST"]
```
{{% /tab %}}
{{< /tabs >}}

#### `config` keys converted to snake case

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
config:
  minimum: 0
  maximum: 10
  multiple_of: 2
  min_length: 10
  max_length: 64
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
propConfig:
  minimum: 0
  maximum: 10
  multipleOf: 2
  minLength: 10
  maxLength: 64
```
{{% /tab %}}
{{< /tabs >}}

#### Multi-type `type` string replaced by `types` array

Properties with multiple types use a `types` array instead of a comma-separated `type` string.

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
- id: status_code
  types:
    - integer
    - "null"
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
- id: status_code
  type: "integer,null"
```
{{% /tab %}}
{{< /tabs >}}

#### Array item types hoisted to top-level

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
- id: tag_list
  type: array
  item_type: string

# Multiple item types
- id: user_scores
  type: array
  item_types:
    - integer
    - number
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
- id: tag_list
  type: array
  propConfig:
    itemTypes:
      - string
```
{{% /tab %}}
{{< /tabs >}}

### Full property definition example

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
version: rudder/v1
kind: properties
metadata:
  name: api_tracking
spec:
  properties:
    - id: api_method
      name: API Method
      type: string
      description: "http method of the api called"
      config:
        enum: ["GET", "PUT", "POST", "DELETE", "PATCH"]
    - id: http_retry_count
      name: HTTP Retry Count
      type: integer
      description: "Number of times to retry the API call"
      config:
        minimum: 0
        maximum: 10
        multiple_of: 2
    - id: password
      name: Password
      type: string
      config:
        min_length: 10
        max_length: 64
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
version: rudder/v0.1
kind: properties
metadata:
  name: api_tracking
spec:
  properties:
    - id: api_method
      name: API Method
      type: string
      description: "http method of the api called"
      propConfig:
        enum: ["GET", "PUT", "POST", "DELETE", "PATCH"]
    - id: http_retry_count
      name: HTTP Retry Count
      type: integer
      description: "Number of times to retry the API call"
      propConfig:
        minimum: 0
        maximum: 10
        multipleOf: 2
    - id: password
      name: Password
      type: string
      propConfig:
        minLength: 10
        maxLength: 64
```
{{% /tab %}}
{{< /tabs >}}

### Custom type definitions

Custom type definitions undergo two changes: 

- `$ref` (a JSON Schema artifact with no semantic meaning in this context) is replaced by the clearer `property`
- `config` keys follow the same snake case convention as properties

#### `$ref` replaced by `property` in type properties

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
spec:
  types:
    - id: login
      type: object
      properties:
        - property: "#property:username"
          required: true
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
spec:
  types:
    - id: login
      type: object
      properties:
        - $ref: "#/properties/api_tracking/username"
          required: true
```
{{% /tab %}}
{{< /tabs >}}

#### Config keys converted from camel to snake case {#custom-type-config-keys}

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
config:
  min_length: 10
  max_length: 255
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
config:
  minLength: 10
  maxLength: 255
```
{{% /tab %}}
{{< /tabs >}}

### Tracking Plan rules

Tracking Plan rules undergo two structural changes: 

- The `event` object wrapper was redundant, so `event` now holds a direct reference string consistent with how other references are expressed. 
- `allow_unplanned` is renamed to `additional_properties` and moved to rule level for clarity.
- `$ref` is replaced by `property` in rule properties, using compact URNs

#### The `event` object replaced with a direct reference string

The `event` field changes from an object (containing `$ref`, `allow_unplanned`, and `identity_section`) to a direct reference string. The `allow_unplanned` and `identity_section` fields move to rule level:

- `event.$ref` becomes `event` (direct string)
- `event.allow_unplanned` becomes `additional_properties` (moved to rule level and renamed)
- `event.identity_section` becomes `identity_section` (moved to rule level)

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
rules:
  - type: event_rule
    id: login
    event: "#event:api_tracking"
    additional_properties: false
    identity_section: properties
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
rules:
  - type: event_rule
    id: login
    event:
      $ref: "#/events/api_tracking/api_tracking"
      allow_unplanned: false
      identity_section: properties
```
{{% /tab %}}
{{< /tabs >}}

#### `$ref` replaced by `property` in rule properties

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
properties:
  - property: "#property:username"
    required: true
  - property: "#property:password"
    required: true
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
properties:
  - $ref: "#/properties/api_tracking/username"
    required: true
  - $ref: "#/properties/api_tracking/password"
    required: true
```
{{% /tab %}}
{{< /tabs >}}

### Full Tracking Plan example

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
version: rudder/v1
kind: tracking-plan
metadata:
  name: api_tracking
spec:
  id: api_tracking
  display_name: API Tracking
  rules:
    - type: event_rule
      id: login
      event: "#event:api_tracking"
      additional_properties: false
      properties:
        - property: "#property:username"
          required: true
        - property: "#property:password"
          required: true
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
version: rudder/v0.1
kind: tp
metadata:
  name: api_tracking
spec:
  id: api_tracking
  display_name: API Tracking
  rules:
    - type: event_rule
      id: login
      event:
        $ref: "#/events/api_tracking/api_tracking"
        allow_unplanned: false
      properties:
        - $ref: "#/properties/api_tracking/username"
          required: true
        - $ref: "#/properties/api_tracking/password"
          required: true
```
{{% /tab %}}
{{< /tabs >}}

### Variant definitions

Variants appear in both custom types and Tracking Plan rules — they undergo the following changes:

- The variant `default` is restructured to remove an array-of-objects pattern in favor of an explicit `properties` wrapper that matches the shape used in rules
- All `$ref` fields within variant discriminators and cases are replaced by `property` with compact URN references

#### Variant `default` restructured from array to object

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
variants:
  - type: discriminator
    default:
      properties:
        - property: "#property:prop_a"
          required: true
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
variants:
  - type: discriminator
    default:
      - $ref: "#/properties/group/prop_a"
        required: true
```
{{% /tab %}}
{{< /tabs >}}

#### Variant discriminator and case properties

Variant discriminator and case properties follow the same `$ref` to `property` change.

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
variants:
  - type: discriminator
    discriminator: "#property:api_method"
    cases:
      - display_name: "Create Entity"
        match: ["POST"]
        properties:
          - property: "#property:user_agent"
            required: true
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
variants:
  - type: discriminator
    discriminator: "#/properties/api_tracking/api_method"
    cases:
      - display_name: "Create Entity"
        match: ["POST"]
        properties:
          - $ref: "#/properties/api_tracking/user_agent"
            required: true
```
{{% /tab %}}
{{< /tabs >}}

### Import metadata

The `local_id` field in import metadata is replaced by a `urn` field that includes the resource type. The `urn` field encodes the resource type alongside the local ID, eliminating ambiguity when multiple resource types share the same ID values.

The URN format is `<resource-type>:<local-id>`. 

Valid resource types include:

- `property`
- `event`
- `category`
- `custom-type`
- `tracking-plan`
- `event-stream-source`
- `retl-source-sql-model`

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
metadata:
  name: test_props
  import:
    workspaces:
      - workspace_id: ws-123
        resources:
          - urn: property:prop1
            remote_id: remote-prop-1
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
metadata:
  name: test_props
  import:
    workspaces:
      - workspace_id: ws-123
        resources:
          - local_id: prop1
            remote_id: remote-prop-1
```
{{% /tab %}}
{{< /tabs >}}

### Event stream source references

The Tracking Plan reference in event stream source governance uses the compact URN format, consistent with the unified compact URN format adopted for all cross-resource references in v1.

{{< tabs tabTotal="2" >}}
{{% tab tabName="v1" %}}
```yaml
version: rudder/v1
kind: event-stream-source
spec:
  id: test-source
  name: Test Source
  type: javascript
  governance:
    validations:
      tracking_plan: "#tracking-plan:tp-abc123"
```
{{% /tab %}}
{{% tab tabName="v0.1" %}}
```yaml
version: rudder/v0.1
kind: event-stream-source
spec:
  id: test-source
  name: Test Source
  type: javascript
  governance:
    validations:
      tracking_plan: "#/tp/group/tp-abc123"
```
{{% /tab %}}
{{< /tabs >}}

<br />

