Workflow for Versioned Agent Updates and Skill Publishing

The anthropics/cwc-workshops repository implements an idempotent deployment pipeline that versions skills under .claude/skills/, caches resource IDs in .stockpilot_ids.json, and performs atomic versioned updates to Claude Managed Agents without recreating them.

The repository provides a deterministic workflow for versioned agent updates and skill publishing through the Claude Managed Agents (CMA) helpers located in agents/cma.py. This architecture allows developers to package skills as zip archives, publish new versions without changing skill identifiers, and update existing agent configurations while preserving their identity and state.

Skill Packaging and Versioning

Skills reside in the .claude/skills/ directory and are packaged into zip archives before upload. The upload_skills function in agents/cma.py (lines 97-126) handles the complete lifecycle:

  • If a skill does not exist, it calls c.beta.skills.create() with the zipped archive.
  • If the skill already exists, it invokes c.beta.skills.versions.create() to produce a new version while retaining the original skill ID.

This approach ensures that existing agent configurations referencing the skill remain valid while allowing iterative improvements to the skill logic.


# From agents/cma.py - simplified upload logic

sid = c.beta.skills.create(
    display_title=title,
    files=[(f"{name}.zip", buf, "application/zip")],
)

# If skill exists, versions.create is called instead

Environment and Agent Lifecycle Management

The workflow separates environment provisioning from agent updates to support repeatable deployments.

Creating the Managed Environment

The ensure_env function in agents/cma.py (lines 30-38) verifies whether a "managed-agents" environment exists and creates one if absent. This provides the execution context for all agents in the deployment.

Versioned Agent Updates

When updating an existing agent, the ensure_agent function in agents/cma.py (lines 40-55) implements a version-aware update strategy:

  1. Retrieve the existing agent by deterministic name
  2. Extract the current version number (defaulting to 1 if uninitialized)
  3. Issue an update targeting that specific version

This prevents race conditions and ensures the configuration change applies to the correct revision.


# From agents/cma.py - versioned update logic

current = c.beta.agents.retrieve(existing)
version = getattr(current, "version", None) or (
    getattr(current, "versions", [1]) or [1]
)[-1]
c.beta.agents.update(existing, version=version, **config)

Configuration and Deployment

Referencing Latest Skill Versions

The agent configuration specifies skill dependencies using the "version": "latest" pointer. In agents/starter/agent.py (lines 100-126), the build_config function constructs a configuration that references uploaded skills:

"skills": [
    {"type": "custom", "skill_id": skill_ids[n], "version": "latest"}
    for n in SKILLS if n in skill_ids
],

By using "latest", the runtime automatically resolves to the most recent skill version uploaded during the deployment phase, eliminating the need to hardcode version strings.

The Deployment Orchestration

The deploy function in agents/cma.py (lines 76-95) orchestrates the complete workflow:

  1. Invokes upload_skills to package and version skills
  2. Calls ensure_env to verify the execution environment
  3. Executes ensure_agent to create or update the agent
  4. Persists all generated IDs via save_ids

This single entry point ensures that running deploy(slot, config_builder) multiple times produces consistent results without creating duplicate resources.

Idempotent Deployment with Persistent IDs

To support repeatable deployments for workshop attendees or CI/CD pipelines, the system caches all resource identifiers in .stockpilot_ids.json. The load_ids and save_ids functions in agents/cma.py (lines 56-62) manage this cache:

  • On startup, the system reads existing IDs from the JSON file
  • If resources exist, they are reused rather than recreated
  • Newly created IDs are written back to the cache

This persistence layer ensures that subsequent deployments reference the same skills, environment, and agent instances, creating a truly idempotent workflow.

Complete Deployment Example

The following example demonstrates deploying a "starter" agent with forecasting and reorder-policy skills:

from agents.cma import client, upload_skills, ensure_agent, deploy

ALL_SKILLS = ["forecasting", "reorder-policy"]

def make_config(skill_ids):
    return {
        "name": "stockpilot-starter",
        "model": "claude-3-5-sonnet-20240620",
        "system": "You are StockPilot...",
        "tools": [{"type": "agent_toolset_20260401"}],
        "skills": [
            {"type": "custom", "skill_id": skill_ids[n], "version": "latest"}
            for n in ALL_SKILLS
        ],
    }

# Execute deployment

deployment_info = deploy("starter", make_config)
print("Agent deployed:", deployment_info["agents"]["starter"])

Running uv run deploy starter executes this workflow, uploading skill versions, ensuring the environment exists, updating the agent to the latest version, and caching all IDs for future runs.

Summary

  • Skill versioning occurs in upload_skills within agents/cma.py, which creates new versions for existing skills without changing their IDs.
  • Versioned updates use ensure_agent to retrieve the current agent version and issue targeted updates to that specific revision.
  • Latest version resolution happens at runtime through the "version": "latest" pointer in skill configurations defined in agents/starter/agent.py.
  • Idempotency is maintained by caching skill, environment, and agent IDs in .stockpilot_ids.json between deployments.
  • Orchestration is handled by the deploy function, which sequences skill uploads, environment verification, and agent configuration.

Frequently Asked Questions

How does the workflow handle existing skills during deployment?

When upload_skills detects that a skill already exists, it calls c.beta.skills.versions.create() instead of c.beta.skills.create(). This generates a new skill version while preserving the original skill ID, ensuring that existing agent configurations continue to reference valid resources while receiving the updated logic.

What happens if an agent already exists when running the deployment?

The ensure_agent function retrieves the existing agent by name, extracts its current version number, and issues an update to that specific version. If no agent exists, it creates a fresh one. This prevents duplicate agent creation and maintains continuity across deployments.

Where are the resource IDs stored between deployments?

All identifiers for skills, environments, and agents are persisted in a local file named .stockpilot_ids.json. The load_ids and save_ids utilities in agents/cma.py manage this cache, enabling the system to reuse existing resources on subsequent runs rather than creating new ones.

How do I ensure my agent uses the latest version of a skill?

Configure the agent's skill references with "version": "latest" in the configuration builder, as implemented in agents/starter/agent.py. The deployment process uploads new skill versions first, and the runtime resolves the "latest" pointer to the most recently uploaded version when the agent executes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →