# How to Promote an Approved Candidate Version in Distilly

> Promote an approved candidate version in Distilly with a single command. Turn staged candidates into stable releases, validating approvals and updating manifests. Learn how.

- Repository: [Tianyi Zhou/distilly](https://github.com/titanwings/distilly)
- Tags: how-to-guide
- Published: 2026-09-10

---

**To promote an approved candidate version in Distilly, run `distilly version promote <candidate-id>` to convert the staged candidate into a stable release, which validates the approval flag, moves the directory to a permanent version path, and updates the version manifest.**

Distilly, an open-source skill generation framework maintained at `titanwings/distilly`, manages skill releases through a lightweight version-manager system defined in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py). Before agents can install a skill, new versions must pass through a candidate staging phase and undergo a formal promotion process. Understanding how to promote an approved candidate version ensures your skills transition safely from testing to production availability.

## Understanding Candidate Versions and Approval

### What is a Candidate Version?

When you generate a new skill using `distilly create`, the system writes the output to a **candidate version** directory following the naming pattern `v0.0.0-candidate-<hash>`. These directories reside under `.distilly/versions/` and serve as isolated sandboxes for testing. The candidate naming convention distinguishes experimental builds from stable releases that production agents consume.

### The Approval Gate

Before promotion can occur, a candidate must be explicitly marked **approved**. The approval mechanism writes `"approved": true` into the candidate’s [`meta.json`](https://github.com/titanwings/distilly/blob/main/meta.json) metadata file, typically triggered by `distilly test <candidate-id>` or manual review of the generated [`SKILL.md`](https://github.com/titanwings/distilly/blob/main/SKILL.md). The `VersionManager` class in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) treats this flag as a mandatory prerequisite; any call to `promote_candidate()` without this metadata entry will fail validation.

## How to Promote an Approved Candidate

### Using the Distilly CLI

The standard method to promote an approved candidate version in Distilly is the `distilly version promote` command. If only one approved candidate exists, you may omit the ID; otherwise, specify the full candidate identifier.

Follow this complete workflow:

1. Generate the candidate:

```bash
distilly create --name my-skill --family colleague

```

2. Run tests to mark the candidate approved:

```bash
distilly test v0.0.0-candidate-abc123

```

3. Promote to stable:

```bash
distilly version promote v0.0.0-candidate-abc123

```

4. Verify the new stable version:

```bash
distilly version list

```

After promotion, `distilly install my-skill` automatically resolves to the new stable version without requiring version specifiers.

### Programmatic Promotion

For CI/CD pipelines or custom automation, instantiate `VersionManager` and invoke `promote_candidate()` directly:

```python
from tools.version_manager import VersionManager

vm = VersionManager()
candidate_id = "v0.0.0-candidate-abc123"

if vm.is_approved(candidate_id):
    vm.promote_candidate(candidate_id)  # Moves candidate → stable version

    print(f"Successfully promoted {candidate_id}")

```

This approach allows you to embed promotion logic into larger workflows defined in [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) or external orchestrators.

## Internal Promotion Mechanics

The `VersionManager.promote_candidate()` function in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) executes three atomic operations to ensure consistency.

First, it performs strict validation by checking that the candidate’s [`meta.json`](https://github.com/titanwings/distilly/blob/main/meta.json) contains `approved = true`. If this flag is missing or false, the function raises a validation error and aborts.

Second, it physically moves the candidate directory from its temporary path (e.g., `.distilly/versions/v0.0.0-candidate-abc123`) to the stable version path (e.g., `.distilly/versions/v1.2.3`). This rename operation makes the skill immediately discoverable by agents requesting the latest stable release.

Third, it updates [`.distilly/versions.json`](https://github.com/titanwings/distilly/blob/main/.distilly/versions.json) to record the new current stable version and archives the previous stable version entry. This manifest update ensures that version listings reflect the promotion instantly.

## Summary

- Distilly stages new skills as **candidate versions** using the format `v0.0.0-candidate-<hash>` inside `.distilly/versions/`.
- **Approval** requires `"approved": true` in the candidate’s [`meta.json`](https://github.com/titanwings/distilly/blob/main/meta.json), typically set via `distilly test`.
- Execute `distilly version promote <candidate-id>` to trigger promotion through the CLI.
- The `VersionManager.promote_candidate()` method in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) handles validation, directory moves, and manifest updates atomically.
- After promotion, the skill becomes the default target for `distilly install` operations.

## Frequently Asked Questions

### How does Distilly verify a candidate is approved before promotion?

The `VersionManager.promote_candidate()` function explicitly checks the candidate’s metadata file for `"approved": true`. This validation occurs in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) and raises an error if the flag is absent, preventing accidental promotion of untested skills.

### Can I promote a candidate without using the CLI?

Yes. Import `VersionManager` from [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) and call `promote_candidate(candidate_id)` programmatically. This method is tested in [`tests/test_cli_lifecycle.py`](https://github.com/titanwings/distilly/blob/main/tests/test_cli_lifecycle.py) and performs the same validation and file operations as the CLI equivalent.

### What happens to previous stable versions after promotion?

Distilly archives the previous stable version within [`.distilly/versions.json`](https://github.com/titanwings/distilly/blob/main/.distilly/versions.json) while updating the `current` pointer to the newly promoted version. The old version files remain in `.distilly/versions/` but are no longer served as the default installation target for agents.

### Where does Distilly store the current stable version information?

The [`.distilly/versions.json`](https://github.com/titanwings/distilly/blob/main/.distilly/versions.json) file acts as the source of truth for version metadata. During promotion, `VersionManager` updates this file to reflect the new stable release, ensuring that `distilly version list` and installation commands resolve correctly.