> ## Documentation Index
> Fetch the complete documentation index at: https://anaconda.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Asset promotion

export const GCell = ({children, className}) => <div className={`grid-table-cell ${className || ""}`} role="cell">
    {children}
  </div>;

export const GTH = ({children, className}) => <div className={`grid-table-th ${className || ""}`} role="columnheader">
    {children}
  </div>;

export const GRow = ({children}) => <div className="grid-table-row" role="row">{children}</div>;

export const GBody = ({children}) => <div className="grid-table-body" role="rowgroup">{children}</div>;

export const GHead = ({children}) => <div className="grid-table-head" role="rowgroup">{children}</div>;

export const GTable = ({children, className, cols}) => <div className={`grid-table not-prose overflow-hidden rounded-2xl ${className || ""}`} style={{
  "--grid-table-cols": cols
}} role="table">
    {children}
  </div>;

Promote assets across project branches with `promote_assets`, carrying metadata and aliases.

```python theme={null}
from obproject.assets import promote_assets

promote_assets(project, source, target, alias="candidate")
```

Promote assets from one branch to another by copying metadata pointers (the underlying data is not duplicated). Each promoted instance gets an alias on the target branch for stable referencing.

<GTable cols="14% 10% 20% 56%">
  <GHead>
    <GRow>
      <GTH>Parameter</GTH>
      <GTH>Type</GTH>
      <GTH>Default</GTH>
      <GTH>Description</GTH>
    </GRow>
  </GHead>

  <GBody>
    <GRow>
      <GCell>`project`</GCell>
      <GCell>str</GCell>
      <GCell>—</GCell>
      <GCell>Project name</GCell>
    </GRow>

    <GRow>
      <GCell>`source`</GCell>
      <GCell>str</GCell>
      <GCell>—</GCell>
      <GCell>Source branch name</GCell>
    </GRow>

    <GRow>
      <GCell>`target`</GCell>
      <GCell>str</GCell>
      <GCell>—</GCell>
      <GCell>Target branch name</GCell>
    </GRow>

    <GRow>
      <GCell>`kinds`</GCell>
      <GCell>list</GCell>
      <GCell>`["data", "models"]`</GCell>
      <GCell>Asset types to promote</GCell>
    </GRow>

    <GRow>
      <GCell>`asset`</GCell>
      <GCell>str</GCell>
      <GCell>`None`</GCell>
      <GCell>Specific asset name, or all if omitted</GCell>
    </GRow>

    <GRow>
      <GCell>`instance`</GCell>
      <GCell>str</GCell>
      <GCell>`"latest"`</GCell>
      <GCell>Instance to promote (`"latest"`, ID, or `"@alias"`)</GCell>
    </GRow>

    <GRow>
      <GCell>`alias`</GCell>
      <GCell>str</GCell>
      <GCell>`"candidate"`</GCell>
      <GCell>Alias to set on the promoted instance. Must be in the allowed list. Set to `None` to skip.</GCell>
    </GRow>

    <GRow>
      <GCell>`with_aliases`</GCell>
      <GCell>bool</GCell>
      <GCell>`False`</GCell>
      <GCell>Copy existing aliases from source branch</GCell>
    </GRow>
  </GBody>
</GTable>

**Returns:** `{"promoted": [...], "errors": [...]}`

Promoted instances are tagged with aliases that represent lifecycle stages:

<GTable cols="15% 52% 33%">
  <GHead>
    <GRow>
      <GTH>Alias</GTH>
      <GTH>Meaning</GTH>
      <GTH>Typical setter</GTH>
    </GRow>
  </GHead>

  <GBody>
    <GRow>
      <GCell>`@candidate`</GCell>
      <GCell>Promoted from a branch, ready for evaluation</GCell>
      <GCell>`promote_assets()` (default)</GCell>
    </GRow>

    <GRow>
      <GCell>`@validated`</GCell>
      <GCell>Passed quality gates</GCell>
      <GCell>Evaluation flow</GCell>
    </GRow>

    <GRow>
      <GCell>`@production`</GCell>
      <GCell>Actively consumed by downstream flows/apps</GCell>
      <GCell>Approval step</GCell>
    </GRow>
  </GBody>
</GTable>

```python theme={null}
# Feature branch merges - model arrives on main as @candidate
promote_assets('my_project', source='feature-v2', target='main')

# Evaluation flow passes - re-alias to @validated
promote_assets('my_project', source='main', target='main',
               asset='classifier', instance='@candidate',
               alias='validated')

# Manual approval - promote to @production
promote_assets('my_project', source='main', target='main',
               asset='classifier', instance='@validated',
               alias='production')
```

Downstream consumers can then read a specific stage:

```python theme={null}
model = self.prj.get_model("classifier", instance="@production")
```

To customize the allowed aliases, add to `obproject.toml`:

```toml theme={null}
[promotion]
aliases = ["candidate", "validated", "production"]  # default
```

Add a `promote` job to your GitHub Actions workflow that runs before teardown when a PR is merged:

```yaml theme={null}
promote:
  if: >
    github.event_name == 'pull_request' &&
    github.event.action == 'closed' &&
    github.event.pull_request.merged == true
  steps:
    # ... setup steps ...
    - name: Promote assets to main
      run: |
        BRANCH=${{ github.head_ref }}
        PROJECT=$(yq .project obproject.toml)
        python -c "
        from obproject.assets import promote_assets
        result = promote_assets('$PROJECT', source='$BRANCH', target='main')
        for p in result['promoted']:
            print(f\"Promoted {p['kind']}/{p['name']} with @{p.get('alias', 'candidate')}\")
        "

teardown:
  needs: promote
  # ... existing teardown job ...
```

This ensures assets are promoted to main with `@candidate` before the feature branch is torn down.

<Tip>
  **`[dev-assets]` and promotion pipelines:** `[dev-assets] branch = "main"` redirects all asset reads to main, which is ideal for consumer flows (dashboards, reports). But in a promotion pipeline where a flow trains a model and then evaluates it on the same branch, reads need to come from the branch that just wrote the asset. Either omit `[dev-assets]` in promotion projects, or use a try/except fallback to read from the write branch when the asset doesn't exist on main yet.
</Tip>
