Status: active
The system extension ships 67 skill executors at extensions/system/server/app/services/system/ai/skills/ (the directory holds 68 *_executor.rb files; one is the abstract BaseSkillExecutor, and all 67 concrete executors declare binds_to). Each pairs with an Ai::Skill record (seeded by system_skills_seed.rb) that makes it discoverable via platform.discover_skills. Skills are bound to autonomy + chat agents via Ai::AgentSkill.
For the full 67-executor list with descriptor I/O, see SKILL_EXECUTOR_CATALOG.md — auto-generated by
cd server && bundle exec rails system:skills:generate_catalog(run from the parent platformserver/tree; the extension has noserver/lib/tasks). That catalog is the canonical enumeration (re-run the rake task rather than trusting counts restated in prose) and groups executors under the 8descriptor()categories — Devops, Documentation, Federation, Fleet, Governance, Sdwan, Security, System. The per-category tallies that used to be restated here were stale by more than one release (they summed to 54 against a 57-executor list) and are deliberately NOT repeated: read them off the generated catalog, which is the only copy a rake task keeps true. It covers every executor: CVE remediation orchestration, full-stack provisioning, package management, SDWAN OVN/host-bridge/IPFIX/full-topology composition,federation_manager, thearchitecture_*catalog skills, workload relocation, andplatform_deploy/platform_maintenance/platform_resilience.This document focuses on architecture — agent bindings, plan-vs-execute pattern, invocation surfaces — and covers 14 representative executors in depth as worked examples. New executors should ship a
descriptor()method (so they appear in the auto-generated catalog) and optionally extend this doc when their orchestration pattern adds something new.Two category taxonomies (don't confuse them): an executor's
descriptor()[:category]is one of the 8 executor categories above (used to organize the catalog). The seed insystem_skills_seed.rbmaps each executor onto a platformAi::Skillenum value (devops,security,sre_observability,release_management,documentation) for cross-platform skill discovery. The two taxonomies are independent — the per-executor reference below cites theAi::Skillenum value the seed assigns.
The 2026-05-10 agent split moved CVE work out of Fleet Autonomy into a dedicated CVE Responder agent; Disk Image Manager and System Topology Designer followed, and the GitOps Reconciler plus the four HIER-P2 operations managers (Capacity, Storage, Ingress, Supply Chain) brought the system extension to 12 seed agents (see the extension's CLAUDE.md for the full roster). The binding map below is representative — Topology Designer's 8 topology skills (the 5 SDWAN composition skills plus the three composer gates it took at HIER-P2F) and the federation/platform-deploy skills are enumerated in SKILL_EXECUTOR_CATALOG.md:
| Skill | Bound To | Why |
|---|---|---|
system-capacity-recommend |
System Concierge | Read-shape — operator chat ("do I need more nodes?") |
system-attribute-failure |
System Concierge | Read-shape — diagnostic chat ("why did instance X fail?") |
system-runbook-generate |
System Concierge | Read-shape — generates docs |
system-cve-runbook-generate |
System Concierge + CVE Responder | Read-shape — generates CVE remediation runbooks |
system-drift-remediate |
Fleet Autonomy | Autonomous reconciliation |
system-sdwan-failover |
Fleet Autonomy | Autonomous SDWAN remediation |
system-sdwan-peer-remediate |
Fleet Autonomy | Autonomous SDWAN remediation |
system-sdwan-bgp-session-remediate |
Fleet Autonomy | Autonomous SDWAN remediation |
system-sdwan-vip-failover |
Fleet Autonomy | Autonomous SDWAN remediation |
system-module-compose |
Fleet Autonomy | Autonomous module planning |
system-rolling-module-upgrade |
Fleet Autonomy + CVE Responder | Autonomous release planning (CVE-driven via CVE Responder) |
system-package-repository-sync |
Supply Chain Manager | Package catalog sync (system.package_repository.sync, auto_approve); re-bound from Fleet Autonomy by HIER-P2E — the sensor path gates the same category under this agent |
system-package-module-create |
Supply Chain Manager + System Concierge | Package-derived module creation (system.package_module.create, require_approval — every new package is operator-audited); re-bound from Fleet Autonomy by HIER-P2E |
system-package-module-refresh |
Supply Chain Manager + System Concierge + CVE Responder | Package drift refresh; nothing sensor-routed reaches it — the CVE Responder's remediation orchestration invokes it directly under the CVE lane's gate, so that binding stays (HIER-P2E) |
system-architecture-propose / -create / -update / -delete |
Supply Chain Manager | Architecture catalog (system.architecture.*); re-bound from Fleet Autonomy by HIER-P2E |
system-suggest-architectures-for-fleet |
Supply Chain Manager + System Concierge | Read-shape — ranks a repo's architectures by fleet coverage; re-bound from Fleet Autonomy by HIER-P2E |
system-discover-packages-by-intent |
Fleet Autonomy + System Concierge | Intent-based semantic package discovery — operator chat ("find me a web server") + autonomous module composition |
system-cve-response |
CVE Responder | CVE triage (moved from Fleet Autonomy 2026-05-11) |
system-cve-remediation-orchestration |
CVE Responder | Chains triage → refresh → rolling upgrade for inline notify_and_proceed dispatch |
system-provision-cluster |
Runtime Manager | Container runtime lifecycle (Phase 2 K3s) |
system-restore-volume |
Storage Manager | Approval-gated volume restore (system.restore_volume); re-bound from Fleet Autonomy by HIER-P2C so the gate resolves the owning agent's row |
system-docker-provision |
Runtime Manager | Container runtime lifecycle (Phase 1 Docker) |
system-disk-image-promote |
Disk Image Manager | Promote a published publication to the platform default (system.disk_image_publication_promote, require_approval); thin over Executors::DiskImage::PromotePublication — HIER-P2F |
system-disk-image-rollback |
Disk Image Manager | Roll a platform back to a prior publication (system.disk_image_publication_rollback, require_approval); same target selection as system_revert_disk_image — HIER-P2F |
system-disk-image-retention |
Disk Image Manager | Set a platform's retention count (system.disk_image_retention_update, auto_approve — the row decides, not the descriptor) — HIER-P2F |
system-gitops-sync-repository |
GitOps Reconciler | Sync one repository now — mints the run, hands it to Gitops::Reconciler, applies nothing (system.gitops_sync_repository, auto_approve) — HIER-P2F |
system-gitops-apply-proposal |
GitOps Reconciler | Apply an approved drift proposal via Gitops::ApplyService (system.gitops_apply_proposal, require_approval — the SAME control the system_gitops_apply_proposal MCP verb parks under) — HIER-P2F |
system-gitops-register-repository |
GitOps Reconciler | Register a declarative source of fleet state (system.gitops_register_repository, require_approval) — HIER-P2F |
system-sdwan-federation-compose / system-multi-tenant-isolation / system-service-discovery-composer |
System Topology Designer | The three composer gates in TOPOLOGY_DESIGNER_POLICIES (all require_approval, chain Topology Designer Actions); bound via binds_to "topology_designer" since HIER-P2F |
system-expose-service-local |
Ingress Manager + System Concierge | Approval-gated /svc/<slug> publish (system.expose_service_local); re-bound from System Concierge alone by HIER-P2D — the Concierge keeps the operator-chat door, the gate resolves the executing agent's row |
system-expose-service-publicly |
Ingress Manager + System Concierge | Approval-gated public HTTPS exposure (system.expose_service_publicly, VIP → port map → ACME → Traefik); HIER-P2D |
system-expose-service-public-tcp |
Ingress Manager + System Concierge | Approval-gated public TLS-carrying TCP exposure ON (system.expose_service_public_tcp); the OFF arm is the ungated system_unexpose_service_public_tcp MCP action on the same executor; HIER-P2D |
system-acme-certificate-provision |
Ingress Manager + System Concierge | Approval-gated DNS-01 issuance (system.acme_certificate_provision); Fleet Autonomy dropped by HIER-P2D — no sensor routes to this executor, the system.acme_cert_rotate renewal lane fires platform_maintenance and stays Fleet Autonomy's |
system-replace-instance |
Capacity Manager | Disaster-recovery replace of an unrecoverable instance (system.instance_replace, require_approval; the instance_unrecoverable_sensor lane gates under this agent). Re-bound from Fleet Autonomy by HIER-P2B |
system-reap-instance |
Capacity Manager | The destructive half of a replace, asked as a second approval (system.instance_reap). Re-bound from Fleet Autonomy by HIER-P2B |
system-relocate-workload |
Capacity Manager | Cross-region workload relocation (system.relocate_workload, require_approval). Re-bound from Fleet Autonomy by HIER-P2B |
system-scale-project |
Capacity Manager | Provisioning-project footprint changes (add/remove replicas, add region, vertical resize plan); dispatched by System::AdaptationGate under project.scale_horizontal. Re-bound from Fleet Autonomy by HIER-P2B |
system-provision-full-stack |
Capacity Manager | Compute + storage + SDWAN stack from a template — the composer scale_project / relocate_workload call. Re-bound from Fleet Autonomy by HIER-P2B |
system-governance-gap-propose |
Platform Architect | HIER-P3 — the first executor bound to a CORE canonical (binds_to "platform_architect"): files or updates the one improvement offer for a system.governance_gap signal and materialises the runtime-closable kinds through System::Governance::GapMaterializer (dev.campaign_propose, auto_approve — the offer is the gate; a skill binding / prompt refinement applies from the trusted tier under dev.skill_refine / dev.prompt_refine where that trust-conditioned pair is seeded — the Powernode Admin account — and at EVERY tier elsewhere, through core's account-wide floor for those two categories (IMP-a51963f8717f), an edge / delegation policy parks under dev.governance_materialize) |
system-attach-storage |
Capacity Manager | Provision + attach + mount a cloud volume on a running instance — the provisioning mission's adaptive-evolution step (Ai::Provisioning::AdaptationProposerService, provisioning subdomain). Re-bound from Fleet Autonomy by HIER-P2SWEEP (driver ruling): it runs DURING PROVISIONING, so it belongs to the provisioning-step owner; the Storage Manager owns the volume DATA plane (restore, snapshot delete, assignment reconcile), not a fresh attach, and Fleet Autonomy declares no provisioning category since HIER-P2DECL |
system-platform-resilience |
System Concierge + Capacity Manager | Operator chat door (Concierge) and the capacity keeper's scale / drain arm (System::Platform::ReplicaReconciler resolves system.platform.scale_out / scale_in against the executing agent — HIER-P2B) |
Every executor implements:
def self.descriptor → { name, description, category, inputs, outputs }
def initialize(account:, agent:, user:)
def execute(**inputs) → { success: true, data: {...} } | { success: false, error: "..." }Most fleet/SDWAN executors support dry_run: true mode — return the plan without side effects. The Fleet Autonomy reconciler uses dry-run for sensor analysis, then commits via dry_run: false once approval policies allow.
Source: attribute_failure_executor.rb
Category: sre_observability (subdomain: fleet)
Inputs: instance_id (required), lookback_hours (default 24)
Outputs: candidates, top_candidate, confidence, reasoning
Given a failed NodeInstance, ranks recent module changes + version promotions in the lookback window by likelihood of being the cause. Returns a structured rationale operators can read in chat or paste into a postmortem.
Source: capacity_recommend_executor.rb
Category: sre_observability (subdomain: fleet)
Inputs: template_id (required), target_min_active (default from constant)
Outputs: template_id, total_count, active_count, silent_count, errored_count, recommendation, confidence
Looks at heartbeat health + module assignment density across a Template's instances. Returns a sized recommendation (e.g. "+2 instances") with a confidence label.
Source: cve_response_executor.rb
Category: security (subdomain: cve)
Inputs: cve_id, severity, affected_packages, summary (optional)
Outputs: risk_score, exposed_modules, exposed_instance_count, remediation_plan, requires_approval
Triages a CVE against the fleet — enumerates exposure, scores risk, proposes a remediation plan. Sets requires_approval=true when the plan touches >5% of fleet instances.
Source: cve_runbook_generate_executor.rb
Category: security (subdomain: cve)
Inputs: cve_id, persist_as_page (default false)
Outputs: runbook_markdown, exposed_module_count, exposed_instance_count, risk_score, requires_approval, persisted_page_id
Generates a markdown remediation runbook for a CVE — exposed modules, recommended steps, verification commands. Optionally persists as a Pages document for operator team review.
Source: docker_provision_executor.rb
Category: devops (subdomain: runtime)
Inputs: node_instance_id (required), dry_run (default false)
Outputs: host_id, host_status, api_endpoint, already_provisioned, plan (dry_run only)
Wraps System::DockerDaemonProvisionerService.provision! for skill-based dispatch. Idempotent — already_provisioned: true on re-call. Bound to Runtime Manager.
Source: drift_remediate_executor.rb
Category: sre_observability (subdomain: fleet)
Inputs: instance_id (required), max_disruption_pct (default 20)
Outputs: resolved, requires_approval, disruption_pct, planned_actions: { attach, detach, update }
Reconciles a NodeInstance's running modules against its assigned modules. Returns planned attach/detach/update actions with disruption %. Sets requires_approval=true when disruption exceeds threshold.
The executor applies nothing, and nothing applies its plan. resolved is true only when there was no drift to begin with; whenever a plan is returned it is false, whatever requires_approval says.
Three DecisionEngine bindings invoke this executor. system.module_drift and system.config_drift go on to dispatch a System::Task (sync_modules / apply_config) — but only when requires_approval is false, which #dispatch_reconcile_task re-reads as its auto-apply budget. system.instance_silent reboots the instance and ignores the result entirely.
The dispatched task is not an instruction to perform planned_actions. It copies them into its options, where nothing reads them: the on-node agent's sync handler takes only force_resync and module_id and calls Reconciler.RunOnce, re-deriving the desired set on the node. The same drift converges, by the generic reconcile the 60s tick already runs. Treat planned_actions as an operator-facing preview (it is what the ApprovalRequest displays), and poll the task — not this return — to learn whether the fleet converged.
Source: module_compose_executor.rb
Category: devops (subdomain: modules)
Inputs: description (free text), platform_id (optional), max_modules
Outputs: draft_template, conflicts, candidate_count, reasoning
Ranks modules against a workload description semantically (embedding cosine similarity, falling back to keyword overlap when the embedding provider can't embed), then runs conflict checks and surfaces package gaps to materialize when no module covers a capability. Useful when an operator describes a workload ("nginx with TLS") and wants a starter Template draft.
Source: provision_cluster_executor.rb
Category: devops (subdomain: fleet)
Inputs: template_id, count (1-50), provider_region_id, provider_instance_type_id, name_prefix, dry_run
Outputs: count, created_nodes, provisioned, failures, partial, plan (dry_run only)
Composes system_create_node + system_provision_instance per node. Hard cap at 50 instances per call — larger rolls go through rolling_module_upgrade with explicit operator approval. Bound to Runtime Manager.
Source: rolling_module_upgrade_executor.rb
Category: release_management (subdomain: modules)
Inputs: template_id, module_id, target_version_id, max_consecutive_failures, health_timeout_sec
Outputs: total_instances, affected_instance_ids, estimated_total_seconds, circuit_breaker
Sizes a module upgrade. Two independent limits, and both matter:
FLEET-ATOMIC — the upgrade cannot be batched or staged. The version an
instance receives resolves from NodeModule#current_version_id, a
per-module pointer read at download, and system_node_module_assignments
(the only per-node row for a module) carries no version column of any kind.
So there is no per-instance version selection to batch over: every instance
carrying the module converges together. batch_pct was removed from this
executor's contract in IMP-b948ea7fa382 rather than kept as an accepted-and-
ignored input. For a genuine blast-radius bound, separate the scope —
instance pools, or a second NodeModule row
with its own pointer (Tutorial 06 § If you need a real blast-radius bound).
PLAN ONLY — NOT IMPLEMENTED beyond that point: the executor returns the
plan and nothing executes it. There is no advancer or circuit breaker for
module upgrades; max_consecutive_failures and health_timeout_sec are echoed
into the returned circuit_breaker hash (status: "not_implemented") and read
by nothing. Operator procedure:
Tutorial 06 § What to do instead.
For a batched rollout that IS implemented, see boot_image_drift_rollout — it
dispatches each batch and converges tick-by-tick off its own drift sensor.
Batching works there because boot images are selected per-instance; modules are
not.
Source: runbook_generate_executor.rb
Category: documentation (subdomain: docs)
Inputs: template_id, persist_as_page (default false)
Outputs: runbook_markdown, section_count, persisted_page_id, source_artifacts
Generates a markdown operational runbook for a Template — boot order, common failure modes, recovery procedures. Optionally persists as a Pages document.
Source: sdwan_bgp_session_remediate_executor.rb
Category: sre_observability (subdomain: sdwan)
Inputs: bgp_session_id OR (peer_id + neighbor_address), dry_run (default true)
Outputs: resolved, session_id, state, likely_cause, recommended_action
Triages an unhealthy iBGP session. v1 returns analysis only — does NOT auto-restart FRR. Operators run the recommended command after review.
Source: sdwan_failover_executor.rb
Category: sre_observability (subdomain: sdwan)
Inputs: network_id, dry_run (default true)
Outputs: resolved, network_id, current_hub_count, candidate_count, candidates
Identifies hub-promotion candidates when a network's hub is unreachable. Returns spokes ranked by last_handshake_at. v1 only supports planning — operator manually flips publicly_reachable=true after review.
Source: sdwan_peer_remediate_executor.rb
Category: sre_observability (subdomain: sdwan)
Inputs: peer_id, dry_run (default false)
Outputs: resolved, rotated_from_key_id, new_key_id, new_public_key
Rotates an SDWAN peer's WireGuard keypair. The agent re-establishes the tunnel from a clean key on its next reconcile.
Source: sdwan_credential_refresh_executor.rb
Category: sdwan
Inputs: peer_id, dry_run (default false)
Outputs: resolved, membership_credential_id, revision, not_after
Re-issues an SDWAN peer's expiring MembershipCredential server-side via Sdwan::MembershipCredentialSigner.ensure_fresh!, so a fresh envelope is ready for the agent's next pull. Deliberately never touches the WireGuard keypair — rotating the key revokes the active pubkey and cuts the working tunnel of exactly the peer that isn't polling (IMP-df40782d3f4d). Serves system.sdwan_credential_expiring via the DecisionEngine.
Source: sdwan_vip_failover_executor.rb
Category: sre_observability (subdomain: sdwan)
Inputs: virtual_ip_id, dry_run (default false)
Outputs: resolved, virtual_ip_id, previous_holder_peer_id, new_holder_peer_id, anycast
Promotes the next failover candidate of a silent-holder VIP. Anycast VIPs return informational only (failover handled by routing).
Path A — direct skill execution (chat agent):
operator chat → System Concierge → discover_skills(task) → get_skill_context(slug)
→ build args from chat context → System::Ai::Skills::*Executor.new.execute(...)
Path B — autonomy decision loop (monitor agent):
Fleet Autonomy reconciler → sensors emit signals → DecisionEngine
→ policy match (auto_approve / notify_and_proceed / require_approval)
→ if allowed: System::Ai::Skills::*Executor.new.execute(...)
→ if require_approval: ApprovalRequest queued, operator reviews
Path C — workspace task (multi-agent coordination):
Workspace mission → spawn task → agent picks executor by name
→ execute(...) returns structured result → next task in mission
- Create
extensions/system/server/app/services/system/ai/skills/<name>_executor.rb. Match the canonical shape:module System::Ai::Skills class FoobarExecutor def self.descriptor = { name: "foobar", description: "...", category: "...", inputs: {...}, outputs: {...} } def initialize(account:, agent: nil, user: nil) def execute(**inputs) = { success: bool, data: {...} } | { success: false, error: "..." } end end
- Add the skill to
extensions/system/server/db/seeds/system_skills_seed.rb. The executor's owndescriptor[:category]is one of the 8 executor categories (Devops / Documentation / Federation / Fleet / Governance / Sdwan / Security / System); the seed separately maps it to a platformAi::Skillenum value (devops,security,sre_observability,release_management,documentation) for cross-platform discovery. Keep the two consistent in intent. SeeSKILL_EXECUTOR_AUTHORING.mdfor the fullskill_descriptor+binds_tocontract. - Bind to an agent in the appropriate seed (
system_concierge_agent.rb,fleet_autonomy_agent.rb, orsystem_runtime_manager_agent.rb). - Re-run seeds:
cd server && bundle exec rails db:seed. - Verify discoverability:
platform.discover_skills query: "your task"should return the new skill.
Every executor returns { success: true, data: {...} } on the happy path or { success: false, error: "..." } on failure. The data shape per executor:
// Input
{ "instance_id": "0193cdef-1234-7890-abcd-001122334455", "lookback_hours": 24 }
// Output (success.data)
{
"candidates": [
{ "kind": "module_promotion", "module": "nginx", "from_version": "1.24.0", "to_version": "1.26.0",
"promoted_at": "2026-05-04T08:12:30Z", "score": 0.74,
"reason": "version promoted within 1.5h before instance went silent" },
{ "kind": "module_assignment_change", "module": "tls-config", "action": "attached",
"changed_at": "2026-05-04T08:55:12Z", "score": 0.41 }
],
"top_candidate": { "kind": "module_promotion", "module": "nginx", "score": 0.74 },
"confidence": "medium",
"reasoning": "Most recent change in lookback window: nginx 1.24→1.26 promoted at 08:12; instance silent at 09:30. Module-promote pattern with high recency."
}// Input
{ "template_id": "tmpl-abc-7890", "target_min_active": 3 }
// Output (success.data)
{
"template_id": "tmpl-abc-7890",
"total_count": 5,
"active_count": 2,
"silent_count": 2,
"errored_count": 1,
"recommendation": { "action": "scale_up", "delta": 1, "rationale": "active=2 < target_min_active=3" },
"confidence": "low"
}confidence: "low" is the v0 default — real telemetry (M-D2-2) will lift this.
// Input
{
"cve_id": "CVE-2026-12345",
"severity": "critical",
"affected_packages": [{ "name": "openssl", "version": "<3.1.4" }],
"summary": "Buffer overflow in OpenSSL TLS handshake"
}
// Output (success.data)
{
"cve_id": "CVE-2026-12345",
"severity": "critical",
"risk_score": 85,
"exposed_modules": [
{ "id": "mod-abc", "name": "system-base", "assignment_count": 12 },
{ "id": "mod-def", "name": "nginx", "assignment_count": 8 }
],
"exposed_instance_count": 20,
"remediation_plan": {
"actions": [
{ "module": "system-base", "from_version": "1.0.3", "to_version": "1.0.4", "instance_count": 12 },
{ "module": "nginx", "from_version": "1.24.0", "to_version": "1.26.0", "instance_count": 8 }
],
"estimated_seconds": 2400
},
"requires_approval": true
}requires_approval=true because risk_score ≥ AUTO_GATE_RISK_THRESHOLD (50).
// Input
{ "cve_id": "CVE-2026-12345", "persist_as_page": true }
// Output (success.data)
{
"runbook_markdown": "# CVE-2026-12345 — Remediation Runbook\n\n## Exposure\n\n- 2 NodeModules affected (system-base, nginx)\n- 20 NodeInstances exposed\n\n## Steps\n\n1. ...\n",
"exposed_module_count": 2,
"exposed_instance_count": 20,
"risk_score": 85,
"requires_approval": true,
"persisted_page_id": "page-cve-2026-12345"
}// Input (live)
{ "node_instance_id": "0193cdef-1234-7890-abcd-001122334455", "dry_run": false }
// Output (success.data, live)
{
"dry_run": false,
"host_id": "host-9876",
"host_status": "pending",
"api_endpoint": "tcp://[fd00:abcd:1::42]:2376",
"already_provisioned": false
}
// Output (success.data, dry_run)
{
"dry_run": true,
"plan": {
"node_instance_id": "0193cdef-...",
"sdwan_peer_address": "fd00:abcd:1::42",
"actions": [
"Mint client mTLS cert via InternalCaService",
"Create Devops::DockerHost row (status=pending)",
"Wait for agent to install docker-ce + report phase=ready"
]
}
}already_provisioned: true is returned (with no side effects) when a managed host already exists — idempotent.
// Input
{ "instance_id": "0193cdef-1234-7890-abcd-001122334455", "max_disruption_pct": 20 }
// Output (success.data, drift detected)
{
"resolved": false,
"requires_approval": false,
"disruption_pct": 20,
"planned_actions": {
"attach": ["security-hardening"],
"detach": [],
"update": ["nginx (1.24.0 → 1.26.0)"]
},
"note": "plan only, nothing applied: within the auto-apply budget, so the fleet lane may dispatch a sync_modules task for these actions",
"drift_report": { "/* full system_drift_report payload */": null }
}
// Output (success.data, no drift)
{ "resolved": true, "requires_approval": false, "disruption_pct": 0, "planned_actions": { "attach": [], "detach": [], "update": [] }, "reason": "no drift" }5 changes ≈ 100% disruption (linear v0 model). requires_approval=true when disruption_pct > max_disruption_pct.
// Input
{ "description": "nginx with TLS termination + rate limiting", "platform_id": "platform-abc", "max_modules": 5 }
// Output (success.data)
{
"draft_template": {
"name_suggestion": "nginx-tls-rate-limited",
"modules": [
{ "name": "system-base", "priority": 10, "reason": "always required" },
{ "name": "security-hardening", "priority": 20, "reason": "TLS hardening baseline" },
{ "name": "nginx", "priority": 50, "reason": "keyword match: nginx" }
]
},
"conflicts": [],
"candidate_count": 3,
"reasoning": "Matched 'nginx' (nginx module), 'TLS' (security-hardening). Rate limiting requires custom config — recommend operator add a config-variety override module."
}// Input (live)
{
"template_id": "tmpl-k3s-template",
"count": 3,
"provider_region_id": "region-aws-us-east-1",
"provider_instance_type_id": "type-t3-medium",
"name_prefix": "k3s-prod",
"dry_run": false
}
// Output (success.data, live)
{
"count": 3,
"created_nodes": ["node-1", "node-2", "node-3"],
"provisioned": 3,
"failures": [],
"partial": false
}
// Output (success.data, dry_run)
{
"count": 3,
"plan": {
"actions": [
"Create 3 Node rows with name_prefix=k3s-prod",
"Provision 3 NodeInstances in region us-east-1, type t3.medium",
"First instance gets k3s-server module assignment; remaining get k3s-agent"
],
"estimated_seconds": 600
}
}Hard-capped at 50 per call — larger fleets go through rolling_module_upgrade.
// Input — there is no batch_pct: the upgrade is FLEET-ATOMIC.
{ "template_id": "tmpl-abc", "module_id": "mod-nginx", "target_version_id": "v-1.26.0",
"max_consecutive_failures": 2, "health_timeout_sec": 300 }
// Output (success.data) — a plan. Nothing executes it.
{
"total_instances": 50,
"affected_instance_ids": ["...", "...50 ids — they all move together"],
"estimated_total_seconds": 6000,
"circuit_breaker": { "trips_after_consecutive_failures": 2,
"health_timeout_sec": 300,
"status": "not_implemented" },
"requires_approval": true,
"executed": false,
"note": "PLAN ONLY — nothing moves the fleet from this plan, and the upgrade is FLEET-ATOMIC ..."
}NOT IMPLEMENTED — nothing executes this plan. Nothing reads it and nothing moves the pointer from it; max_consecutive_failures / health_timeout_sec are echoed into the circuit_breaker hash above and read by nothing.
FLEET-ATOMIC — and there is no batch_pct to size. The version an instance receives resolves from NodeModule#current_version_id — a per-module pointer — and system_node_module_assignments has no version column, so moving the pointer moves every instance carrying that module at once. affected_instance_ids is that whole population, not a first batch. Real staging means separating the scope: an instance pool, or a second NodeModule row with its own pointer. See docs/tutorials/06-rolling-upgrade.md for the manual procedure that does work.
// Input
{ "template_id": "tmpl-abc", "persist_as_page": true }
// Output (success.data)
{
"runbook_markdown": "# nginx-tls Runbook\n\n## Boot order\n\n1. system-base\n2. security-hardening\n3. nginx\n\n## Common failure modes\n\n- ...\n",
"section_count": 6,
"persisted_page_id": "page-tmpl-abc-runbook",
"source_artifacts": ["module_manifest:system-base", "module_manifest:nginx"]
}// Input
{ "bgp_session_id": "bgp-sess-abc", "dry_run": true }
// Output (success.data)
{
"resolved": false,
"session_id": "bgp-sess-abc",
"state": "idle",
"likely_cause": "wrong AS number on neighbor (expected 65000, observed 65001)",
"recommended_action": "vtysh -c 'show ip bgp summary' on the holding peer to confirm; then `clear ip bgp <neighbor>` to force re-handshake"
}v1 is planning-only — never auto-restarts FRR. The recommended clear ip bgp command is operator-driven.
// Input
{ "network_id": "sdwan-net-abc", "dry_run": true }
// Output (success.data)
{
"resolved": false,
"network_id": "sdwan-net-abc",
"current_hub_count": 1,
"candidate_count": 2,
"candidates": [
{ "peer_id": "peer-spoke-A", "last_handshake_at": "2026-05-04T09:30:12Z", "score": 0.92 },
{ "peer_id": "peer-spoke-B", "last_handshake_at": "2026-05-04T09:28:55Z", "score": 0.84 }
]
}// Input
{ "peer_id": "peer-abc", "dry_run": false }
// Output (success.data)
{
"resolved": true,
"rotated_from_key_id": "key-old-abc",
"new_key_id": "key-new-def",
"new_public_key": "AbCd...EfGh="
}The agent picks up the new key on its next reconcile (~30 s) and re-establishes the WireGuard tunnel.
// Input (single-holder VIP, live)
{ "virtual_ip_id": "vip-abc", "dry_run": false }
// Output (success.data)
{
"resolved": true,
"virtual_ip_id": "vip-abc",
"previous_holder_peer_id": "peer-old",
"new_holder_peer_id": "peer-new",
"anycast": false
}
// Output for anycast VIP
{
"resolved": false,
"virtual_ip_id": "vip-abc",
"previous_holder_peer_id": null,
"new_holder_peer_id": null,
"anycast": true,
"note": "Anycast VIPs use routing for failover; this skill is informational only for anycast."
}SKILL_EXECUTOR_CATALOG.md— auto-generated canonical list of all 67 executors with descriptor I/OSKILL_EXECUTOR_AUTHORING.md— how to write a composable executor (descriptor, binds_to, cross-step data flow, rollback)CONTAINER_RUNTIMES.md—docker_provision+provision_clusterintegrationFLEET_SENSORS.md— sensor signals that trigger autonomous skill invocationARCHITECTURE.md— autonomy + decision engine subsystemrunbooks/cve-response.md— CVE response operator runbook (usescve_response+cve_runbook_generate+rolling_module_upgrade)runbooks/sdwan-network-setup.md— SDWAN runbook (usessdwan_failover+sdwan_peer_remediate+sdwan_vip_failover)
Last verified: 2026-08-04