OC · Fleet Ops
Provision and operate containers across self-managed infra — k8s/Nomad/Compose/VMs. Terraform when it fits, not always.
/oc-fleet/oc-fleet topology/oc-fleet provision/oc-fleet deploy/oc-fleet verify/oc-fleet scale/oc-fleet drain/oc-fleet rollback/oc-fleet statusbuild
Drop the bundle into .claude/skills/ and Claude Code auto-discovers it on the next session — or point Codex / any MCP agent at the hosted opchain.dev/mcp endpoint.
How you'll use it
Multi-container / orchestration deployment operator for self-managed infrastructure. Declares topology (containers/services × target environment), provisions infra with the right IaC tool (Terraform/OpenTofu when it fits, else Ansible/cloud-init/k8s-manifests/ Helm/Nomad/Compose), rolls the fleet with a rollout strategy, verifies fleet-wide health, and operates day-2 (scale/drain/replace/rollback). Specifically lands the modules oc-modularize-ops carves out of a monolith. Use for /oc-fleet, "deploy multiple containers", "kubernetes", "terraform", "orchestrate containers", "on-prem deployment", "deploy to VMs", "self-managed infra", "container fleet". Complements oc-deploy-ops (single-app managed PaaS): managed app → deploy-ops; multi-container/self-managed/IaC → fleet-ops. Trigger on multi-container / IaC / self-managed deployment.
Trigger with natural language or a slash command:
/oc-fleet/oc-fleet topology/oc-fleet provision/oc-fleet deploy/oc-fleet verify/oc-fleet scale+3 moreOn this page
Fleet Ops
On first invocation, read references/orchestrator.md and follow its welcome protocol.
Deploy and operate one-or-more containers across arbitrary, self-managed environments
— the territory oc-deploy-ops routes elsewhere today. Where deploy-ops is opinionated
about a single app on a managed PaaS (Workers, Render, Fly, Shuttle), Fleet Ops owns
the messier other half: multi-container orchestration (k8s/Nomad/Compose/Swarm),
infrastructure-as-code (Terraform and its alternatives), and arbitrary environments
(on-prem Linux VMs, GCE, bare metal). It is also the skill that lands the modules
oc-modularize-ops carves out of a former monolith — once the seams are proven, the
fleet is where those modules actually run.
This is not a tri-agent forge and not a managed-PaaS deployer. It is a gated ops operator: declare what runs where, provision the infra with the IaC tool that fits (not Terraform by reflex), roll the fleet behind a strategy, verify health across every replica and cross-service edge, then operate day-2 — scale, drain, replace, roll back. The IaC apply step is the highest-blast-radius operation in the entire opchain catalog, so it is gated behind a mandatory dry-run/plan that never auto-applies.
Mnemonic: deploy-ops pushes one app to a platform; fleet-ops stands up and runs a fleet on infra you own. deploy-ops ↔ fleet-ops are peers, not a chain — route by “managed app vs self-managed fleet” (see Boundaries). When a project carries both checkpoints, that’s the disambiguator. The rule lives in this skill’s description and in the orchestrator’s routing table (“Kubernetes” / “Terraform” / “Deploy multiple containers” →
/oc-fleet topology).
/oc-fleet — Command Reference
FLEET OPS COMMANDS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PLAN & PROVISION
/oc-fleet Show fleet status for the current project (this menu if none)
/oc-fleet topology Declare containers/services × target environment(s)
/oc-fleet provision Stand up infra with the right IaC tool (gated dry-run first)
DEPLOY & VERIFY
/oc-fleet deploy Roll the fleet with a rollout strategy (rolling/blue-green/canary)
/oc-fleet verify Fleet-wide health: every replica, every cross-service edge
DAY-2 OPERATE
/oc-fleet scale Scale replicas/nodes to a target (applies; never decides)
/oc-fleet drain Cordon + drain a node for maintenance / replace a container
/oc-fleet rollback Roll the whole fleet back to last-good
UTILITIES
/oc-fleet status Current topology, fleet_health, last deploy, rollback availability
/checkpoint Show checkpoint status
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Type any command to begin. /oc-fleet to see this again.
How This Skill Fits the Build Pipeline
oc-modularize-ops (module set) ──┐
oc-stack-forge (infra decision)──┼──► oc-fleet-ops ──► running fleet
oc-scale-ops (replica targets) ──┘ │ on self-managed infra
oc-app-architect (07-devops.md)──┘ │
▼
topology ─► provision ─★plan-gate─► deploy ─► verify ─► operate
│
▼
oc-monitoring-ops
(post-deploy observability across the fleet)
Fleet Ops sits next to oc-deploy-ops as a peer, downstream of oc-modularize-ops
(it deploys what modularize carved out) and oc-stack-forge (it actuates the infra
decision). oc-scale-ops decides replica/capacity targets; fleet-ops applies
them — it never decides them itself. After a fleet is up, it chains to
oc-monitoring-ops for continuous prod observability and oc-git-ops to commit the IaC.
Operating model — gated ops phases
topology ──► provision ──★plan-gate──► deploy ──► verify ──► operate
(what runs (IaC, tool (dry-run (roll the (fleet-wide (scale / drain /
where) chosen to NEVER fleet) health) replace / rollback)
fit the env) auto-apply)
The shape is deliberately not a forge loop. Provisioning real infra is one-directional and dangerous: the gate is between plan and apply, not between a builder and an evaluator. Each phase writes its result to the checkpoint so a half-rolled fleet survives across sessions.
Phase 0 — Topology (/oc-fleet topology)
Declare what runs where: the set of containers/services, replica counts, the target environment(s), networking, secrets, and state/volumes. This is the anchor every later phase grades against.
When the input is a module map from oc-modularize-ops, read
modularization/module-map.json (format in the Handoff contract below) — each module
becomes one (or more) container(s) (module.id → container, module.image_hint → image;
when a module carries no image_hint, use its id as the image name). Topology never
re-derives the seams, it reads them from that file.
Topology output is recorded in skill_state (containers[], nodes[], environment)
so provision and deploy operate against a declared, diffable target — not a verbal
description.
Phase 1 — Provision (/oc-fleet provision) — IaC, Terraform when it fits
Stand up the infra. The tool is chosen to fit the environment — not Terraform by reflex (the explicit “Terraform is not always the answer” stance):
| Tool | When |
|---|---|
| Terraform / OpenTofu | cloud resources, multi-provider, long-lived infra worth a state file |
| Ansible / cloud-init | configuring existing VMs, on-prem boxes, no desire for TF state |
| k8s manifests / Helm / Kustomize | you already have a cluster |
| Nomad jobspecs | a Nomad shop |
| docker-compose | single-host, small fleet, dev/staging |
The skill picks and justifies — the rationale is recorded in
skill_state.iac_choice_rationale (e.g. “existing cluster → Helm; no new TF state
needed”), so the choice is auditable in the PR diff, not buried in chat.
IaC apply is gated: dry-run/plan first, never auto-apply. This is the
prov-gate ★ in the checkpoint and the single most dangerous act any opchain skill
performs (see Failure modes and Principle 4):
- Always run the plan/dry-run (
terraform plan,helm diff/--dry-run,ansible --check,kubectl diff,nomad plan) and surface the full diff first. - Require explicit target-environment confirmation before apply — name the environment so prod is never applied to by accident.
- Apply only after the human approves the plan-gate.
Phase 2 — Deploy (/oc-fleet deploy)
Roll the fleet: build/push images, apply manifests/plans, and deploy per-container with a rollout strategy — rolling, blue-green, or canary across nodes. This is per-node and per-container granularity, not a single atomic push: a fleet is many moving parts, and the rollout marches through them with a failure threshold, not all at once.
The chosen strategy is recorded in skill_state.rollout_strategy, and last_deploy is
stamped on completion. Image references are pinned (name:sha) so a rollback target is
always unambiguous.
Phase 3 — Verify (/oc-fleet verify)
Health across the fleet, not one URL. A self-managed multi-node fleet has far more surface than a single PaaS app, so verify checks all of:
- every container/replica is healthy (
healthy == replicasper container); - cross-service connectivity is up (service A can actually reach service B);
- the new module-boundary calls — the seams
oc-modularize-opscarved (Phase 2 of that chain) — actually resolve end to end; - no node left behind (every node in
nodes[]reporting).
fleet_health is written as a tri-state: ok | partial | unhealthy. A fleet can be
partially healthy — some replicas up, some down — which is operationally distinct from a
flat pass/fail and must not be collapsed to ok.
Phase 4 — Operate (/oc-fleet scale|drain|rollback)
Day-2, the part deploy-ops doesn’t have:
/oc-fleet scale— apply replica/node counts to a target. Fleet-ops actuates;oc-scale-opsis the skill that decides the target (advisory). Its capacity plan (/oc-scale plan) gives tiers and the changes each needs, not per-service replica counts — when the numbers are not in that plan, ask the user for them. Fleet-ops never invents capacity numbers./oc-fleet drain— cordon + drain a node for maintenance, or replace a failed container, rescheduling its workloads first./oc-fleet rollback— roll the whole fleet back to last-good. Recordedrollback_availableand the pinned prior image set make this a 30-second action.
Failure modes (verify + operate)
A self-managed multi-node fleet has more failure surface than a single PaaS app, so
these are first-class, mirroring oc-deploy-ops / oc-monitoring-ops:
- Partial rollout → halt at the configured failure threshold and auto-rollback the rolled subset; never march a broken image across the whole fleet.
- Undrainable node → escalate (workloads won’t reschedule) rather than force-kill.
- Image-pull / registry-auth failure → fail the deploy at the pull step, keep prior replicas serving; a registry auth problem must not take the fleet down.
fleet_healthis tri-state (ok | partial | unhealthy), not a flatok— a fleet can be partially healthy, which the operator must be able to see and reason about.
Scope discipline — first-class environments (like deploy-ops’s Platform Matrix)
fleet-ops is broad; it focuses on a first-class shortlist rather than claiming
universal coverage (mirrors oc-deploy-ops’s intentionally-short Platform Matrix — better
to be excellent at three environments than mediocre at ten):
| Environment | Status |
|---|---|
| docker-compose (single host) | first-class |
| Kubernetes (manifests / Helm) | first-class |
| Linux VMs (Ansible / cloud-init), on-prem or GCE | first-class |
| Nomad | reachable, not first-class |
| Terraform multi-cloud (large) | reachable, not first-class |
“First-class” means the phases above are written with that environment’s tooling in mind
(Compose, kubectl/Helm, Ansible/cloud-init). “Reachable, not first-class” means the skill
will help, with less environment-specific guidance. No environment ships a vetted IaC
recipe or a /demo scenario yet — the reference docs below are unwritten.
Boundaries — what oc-fleet-ops does NOT own
| Concern | Owner |
|---|---|
| Single app → managed PaaS (Workers/Render/Heroku/Fly/Shuttle) | oc-deploy-ops (peer, not chain) |
| Continuous prod observability (uptime/errors/SLOs) | oc-monitoring-ops |
| Capacity/caching/query strategy + replica targets | oc-scale-ops (advisory) — fleet-ops actuates replica/node counts, it never decides them |
| Choosing the target platform | oc-stack-forge |
| The engine swap / platform migration | oc-migration-ops |
The peer relationship with oc-deploy-ops is the load-bearing boundary: a managed app on
a PaaS is not in scope here, and a self-managed multi-container fleet is not in
scope for deploy-ops. Route by “managed app vs self-managed fleet” — see the note below.
Cross-skill wiring
| Reads from | Why |
|---|---|
oc-modularize-ops | the module set to deploy (modularization/module-map.json, per the Handoff contract) |
oc-stack-forge | target infra / platform decision |
oc-scale-ops | capacity plan (/oc-scale plan) → replica / node targets (fleet-ops applies; scale-ops decides) |
oc-app-architect (07-devops.md) | deploy pattern intent |
| Chains to | Why |
|---|---|
oc-monitoring-ops | post-deploy observability across the fleet |
oc-git-ops | commit the IaC |
Seam with deploy-ops: deploy-ops’s Platform Matrix routes bare-metal, VPS, multi-container and IaC deploys here (moving a live system onto one is oc-migration-ops). The orchestrator records fleet-ops in its upstream/downstream map, handoff table (modularize → fleet → monitoring) and routing table; the peer rule with deploy-ops is carried by this skill’s description.
Handoff contract — the modularize → migration → fleet chain
The orchestrator passes context through checkpoints, not conversation (orchestrator.md
§3, Context Passing), and oc-modularize-ops’s skill_state is private to it. So the chain’s
payload is a named file, modularization/module-map.json, which oc-modularize-ops writes:
{ "modules": [
{ "id": "billing", "seam_contract": "modularization/seams/billing.md",
"owns_data": ["invoices", "charges"], "image_hint": "billing",
"equivalence_verified": true }
] }
- oc-migration-ops reads it (
/oc-migrate plan) to build the Structural cutover plan (which module moves, what data it owns, dual-write boundaries). - oc-fleet-ops
topologyreads it to seed containers (module.id → container,module.image_hint → image, falling back tomodule.id).
Fleet-ops only deploys modules whose equivalence_verified is true — an unverified
module is not a deployable container.
PM-Tool MCP Integration (fleet-deploy ticket, like deploy-ops)
Like its peer oc-deploy-ops, fleet-ops posts to a linked ticket when one is in context
(e.g. /oc-fleet --ticket TICKET-1234). It defers the runtime contract (tool names,
retry/backoff, idempotency markers, pm_deferred_actions[]) to
oc-integrations-engineer/references/pm-mcp-protocol.md and only shapes:
- Fleet-deploy ticket per
environment + commiton/oc-fleet deploy, marker<!-- opchain:oc-fleet-ops:fleet-deploy:<env>:<sha> -->, body = topology summary + rollout strategy + IaC tool. - Per-event updates:
rolled/partial-halt/rolled-backtransitions, each with its own marker; records the ticket ids inpm_refs(role: deploy,created_by_skill: oc-fleet-ops) and the per-environment map inskill_state.pm.deploy_tickets[]. - No PM-MCP availability → deploy proceeds; updates are deferred
(
pm_deferred_actions[]). PM writes never block a deploy.
Checkpoint
The shared checkpoint schema, write rules and resume protocol live in
references/checkpoint-protocol.md, bundled with this skill. This section adds only
what is specific to oc-fleet-ops.
Location
{project-dir}/.checkpoints/oc-fleet-ops.checkpoint.json
Progress table
"progress_table": [
{ "id": "topology", "label": "Containers × environment", "status": "not_started" },
{ "id": "provision", "label": "Provision infra (IaC)", "status": "not_started" },
{ "id": "prov-gate", "label": "★ Dry-run / plan approval", "status": "not_started" },
{ "id": "deploy", "label": "Roll the fleet", "status": "not_started" },
{ "id": "verify", "label": "Fleet-wide health", "status": "not_started" },
{ "id": "operate", "label": "Day-2 (scale/drain/roll)", "status": "not_started" }
]
Skill state
"skill_state": {
"environment": "kubernetes | nomad | compose | vms | gce | baremetal",
"iac_tool": "terraform | ansible | helm | nomad | compose",
"iac_choice_rationale": "existing cluster → Helm; no new TF state needed",
"containers": [
{ "name": "billing", "image": "billing:abc123", "replicas": 3, "healthy": 3 }
],
"nodes": ["node-a","node-b"],
"rollout_strategy": "rolling",
"last_deploy": "2026-07-xxTxx:xxZ",
"rollback_available": true,
"fleet_health": "ok | partial | unhealthy"
}
When to write
| Event | What to save |
|---|---|
| Topology declared | environment, containers[], nodes[] |
| IaC tool chosen | iac_tool, iac_choice_rationale |
| Plan-gate approved | prov-gate → done after explicit env confirmation |
| Fleet rolled | rollout_strategy, last_deploy, rollback_available |
| Health verified | fleet_health (tri-state), per-container healthy counts |
| Day-2 action | scale/drain/rollback result + new fleet_health |
references/
Planned companion docs (not yet written).
topology-design.md— containers × environment, networking, secrets, state.iac-selection.md— Terraform-vs-alternatives decision matrix.rollout-strategies.md— rolling / blue-green / canary on self-managed infra.fleet-verification.md— fleet-wide health, cross-service + module-boundary checks.
Principles
- Terraform is a tool, not the answer. Pick IaC to fit the environment; justify it
in
iac_choice_rationale. Cloud + state file → Terraform/OpenTofu; existing VMs → Ansible; existing cluster → Helm; single host → Compose. - A fleet is not one URL. Verify every replica and every cross-service edge — and the module-boundary calls the seams introduced — not a single health endpoint.
- Roll, don’t big-bang. Rolling / blue-green / canary, per-node granularity, with a failure threshold that halts and auto-rolls-back the rolled subset.
- Dry-run before apply, always. IaC apply is the catalog’s highest-blast-radius act; the plan-gate never auto-applies and demands explicit target-environment confirmation.
- 30-second rollback for the whole fleet. Record last-good (pinned images) every
deploy so
rollback_availableis always true after a successful roll. - deploy-ops’s peer, not its rival. Managed PaaS → deploy-ops; self-managed fleet → here. Scale targets come from scale-ops — fleet-ops actuates, it never decides.
Use OC · Fleet Ops in your project
Drop the SKILL.md into .claude/skills/ or.codex/skills/, download the bundle, or reach it over the hosted MCP endpoint.