CLAUDE LABJP
APPLY — ant CLI v1.30.0 introduces ant apply, which creates and updates agents, environments, skills, memory stores, and deployments straight from files in your repositoryLOCKFILE — Commit the claude-lock.json it writes. Skip that step and every later run quietly creates fresh resources instead of updating the ones you already havePLAN — ant apply prints a plan and waits for your approval before applying. If you intend to run it unattended in CI, decide how that approval is handled firstPRICING — The increase to $3 / $15 per MTok for Claude Sonnet 5 never happened. The introductory $2 / $10 is now simply the standard priceSOURCE — Prices and version numbers get garbled in secondhand coverage. It is worth checking the release notes on platform.claude.com before you quote either oneBETA — Agent Skills and the Skills API left beta, so the skills-2025-10-02 header is optional now. The Files API did too, but there the header still changes the response shapeAPPLY — ant CLI v1.30.0 introduces ant apply, which creates and updates agents, environments, skills, memory stores, and deployments straight from files in your repositoryLOCKFILE — Commit the claude-lock.json it writes. Skip that step and every later run quietly creates fresh resources instead of updating the ones you already havePLAN — ant apply prints a plan and waits for your approval before applying. If you intend to run it unattended in CI, decide how that approval is handled firstPRICING — The increase to $3 / $15 per MTok for Claude Sonnet 5 never happened. The introductory $2 / $10 is now simply the standard priceSOURCE — Prices and version numbers get garbled in secondhand coverage. It is worth checking the release notes on platform.claude.com before you quote either oneBETA — Agent Skills and the Skills API left beta, so the skills-2025-10-02 header is optional now. The Files API did too, but there the header still changes the response shape
Articles/API & SDK
API & SDK/2026-09-09Intermediate

The agent I built in the Console could not be adopted by ant apply

ant apply refuses some drift and silently duplicates other resources. Here is where that line sits, why claude-lock.json belongs in your commits, and the five doors a second copy walks through.

ant CLIManaged Agents4claude-lock.jsonCI6operations28

The automation that keeps my sites moving is split in two. The run configuration lives in the Console; the skills live in the repository. Every session used to start with me remembering which side was authoritative, and a few minutes of every morning went there.

ant apply, added in ant CLI v1.30.0, looks like it dissolves that split. You describe agents, environments, skills, memory stores, and deployments as files in your repository, run the command, read the plan it prints, and approve it. The configuration then lives in git history — and the remembering stops.

Before moving anything, though, I paused on one question. What happens to the resources I already built in the Console? Read that wrong and the same agent quietly becomes two.

Start by reading --dry-run

Don't apply first. Read the plan first.

# Print the detailed plan and exit without changing anything
ant apply --dry-run .

In an interactive terminal, ant apply prints the creates and updates and waits for approval. Answer d and you get the fields of each new resource, or a field-by-field diff for each update. --dry-run prints that same detail and stops. It writes no lockfile.

One caveat is worth pinning down. --dry-run exits 0 even when the plan is blocked. If you wire it into CI as the gate that decides whether a plan is acceptable, it will report a blocked plan as a clean one. Treat it as information for reviewers, nothing more.

Refusal only covers what the lockfile knows

This is the part I misread first.

If a resource that came from your files is later edited, archived, or deleted somewhere else — in the Console, say — the next ant apply ends with This plan cannot be applied: and the reason, then exits with refusing to apply. You pass --force only when you actually want to overwrite that change. The CLI tells you when your declarations and reality have drifted apart.

But write a file describing a resource you created in the Console, apply it, and you get a second resource. ant apply cannot adopt existing resources. Only what the lockfile tracks is managed; everything else reads as a declaration of something that does not exist yet.

Refusal happens inside the lockfile. Outside it, things are duplicated without complaint. Swallow that sentence early and the migration order takes care of itself.

There is a way through. The Console's Export as code download ships with its own claude-lock.json. Apply that, and you are updating the resources you assembled in the Console rather than creating rivals for them. Without knowing this, I would have parked a same-named agent that does nothing right next to a deployment that was actually running.

claude-lock.json is the record, not an artifact

The first apply writes claude-lock.json at the directory you ran it from. It looks roughly like this.

{
  "version": 1,
  "origin": {
    "base_url": "https://api.anthropic.com",
    "organization_id": "YOUR_ORGANIZATION_ID",
    "workspace_id": "YOUR_WORKSPACE_ID"
  },
  "resources": {
    "./agents/release-notes.md": {
      "kind": "agent",
      "id": "agent_XXXXXXXXXXXXXXXX",
      "version": "1",
      "hash": "…",
      "remote_hash": "…"
    }
  }
}

hash fingerprints what was last sent; remote_hash fingerprints what the API returned. Together they let a later run tell an edited file apart from a resource that was changed outside your files.

Three operational decisions follow from that.

Run it from the repository root, so the lockfile lands in one predictable place. Commit the lockfile even when the apply failed partway, because a partial apply still records what it managed to create. And if you work across organizations or workspaces, keep one lockfile each and name it with --lock-file; ant apply refuses credentials that resolve to an organization or workspace other than the one recorded there.

Five doors a second copy walks through

DoorWhat happensWhat to do
Rewriting a Console resource as a fileIt is not adopted — a second one is createdBring the Export as code download with its claude-lock.json
Renaming or moving a fileIt declares a new resource; the old one staysRestore the name, or clear the old one with --prune
Deleting a fileThe resource remains, with a warning--prune (deletes a skill, archives everything else)
Bare ant apply --yes in CIReconciles only tracked files and skips newly added onesName the directory: ant apply --yes .
Forgetting to commit claude-lock.jsonThe next run cannot find the resources and rebuilds themCommit it alongside the files

Four of those five happen to people who wrote their files carefully. The cause sits not in the description but in where the identity of the resource is recorded.

Cross-references are relative paths, not IDs

Wherever the API expects another resource's ID, write the relative path to that resource's file. ant apply creates things in dependency order and fills in the real IDs.

---
name: Release notes writer
model: claude-opus-5
tools:
  - type: agent_toolset_20260401
skills:
  - ../skills/release-notes
---
 
You draft release notes. Summarize what changed in three short sections.

Deployments follow the same shape: frontmatter is the request body, and the prose becomes the message that starts each session.

---
name: Nightly release notes
agent: ../agents/release-notes.md
environment_id: ../environments/cloud.yaml
schedule:
  type: cron
  expression: "0 3 * * *"
  timezone: Asia/Tokyo
---
 
Draft release notes from the commits that have not been written up yet.

Paths are what let a single run pin the referring side to the version it just applied. Hard-code an ID and that follow-through stops. Reach for a literal agent_... or skill_... ID only when you are pointing at something these files do not manage.

A file's kind is inferred in order: a top-level type field, then the directory it sits directly in (agents/, environments/, memory_stores/, deployments/), then a filename that starts with the kind, such as environment_staging.md. Files matching none of these are skipped during a directory walk — which keeps READMEs and CI configuration out, and also means that the moment you tidy a file out of agents/, it stops being seen.

Write the local run and the CI run as separate procedures

With no terminal, ant apply prints the plan and stops rather than asking for confirmation. So CI looks like this.

# On pull requests: print the plan for reviewers (informational, always exits 0)
ant apply --dry-run .
 
# On the default branch after merge: name the directory and apply
ant apply --yes .

Run one apply at a time, since nothing locks the lockfile. And authenticate with Workload Identity Federation rather than a stored API key, as an identity that reaches the organization and workspace the lockfile records.

I keep a number of unattended jobs running, and the pain in setups like this almost always arrives the same way: it worked on my machine and did something else in CI. Keep the one-off local run and the repeated CI run as two separate write-ups. It looks like extra work and it prevents more accidents than anything else I've tried.

The one file to start with

You don't have to move everything at once. Take the agent you touch least often, write it into a single file, and stop at reading the plan from ant apply --dry-run. If the plan says create, that is a new resource, not an adoption — and that is the moment to decide between starting from Export as code and standing up a fresh one.

I'm still mid-migration myself, with pieces left in the Console. If you're hesitating at the same spot, I hope this saves you the duplicate.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API & SDK2026-08-30
Read the Catalog Your CLI Already Ships Before Bulk-Replacing Model IDs
A newer generation makes older model IDs look stale. Here is how to pull the catalog embedded in your installed binary, sort every reference into matched, date-mismatched, and malformed, and stop the replacement that would break working IDs.
API & SDK2026-07-28
The Line That Disappears at 100K: Measuring What Tool-Output Spill Actually Keeps
When agent tool output passes 100,000 characters, the full text spills to a file and the model sees only a head-truncated preview. Here are measured survival rates from a real repository, and the output envelope I built to push decision-relevant lines to the front.
API & SDK2026-07-24
Running Four Sites From One Managed Agent Definition
A design for collapsing four near-identical Managed Agents into one base definition, using agent version pinning and session-local overrides — with a validated factory and the traps I hit along the way.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links