Skip to main content

cluster-blueprint-assistant

Create, customize, validate and troubleshoot Cluster Toolkit blueprints with gcluster. Use when asked to set up or modify a Slurm/GKE cluster blueprint, change node counts, machine types, zones, Spot or reservations, add storage like Lustre or Filestore, or fix gcluster expand/create/deploy errors.

Source facts

Repository
GoogleCloudPlatform/cluster-toolkit
Last source activity
November 1, 2026 at 07:29
Detected SKILL.md language
English
Stars
370
Forks
331

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
cluster-blueprint-assistant
description
Create, customize, validate and troubleshoot Cluster Toolkit blueprints with gcluster. Use when asked to set up or modify a Slurm/GKE cluster blueprint, change node counts, machine types, zones, Spot or reservations, add storage like Lustre or Filestore, or fix gcluster expand/create/deploy errors.
compatibility
Requires the gcluster binary (built from this repo via `make`) and a Cluster Toolkit checkout.
metadata
{"author":"GoogleCloudPlatform","status":"experimental","domain":"blueprints"}
allowed-tools
Bash(make) Bash(./gcluster explain:*) Bash(./gcluster expand:*) Bash(./gcluster create:*) Bash(gcluster explain:*) Bash(gcluster expand:*) Bash(gcluster create:*)
allowed_read_only_commands
["make","./gcluster explain","./gcluster expand","./gcluster create","gcluster explain","gcluster expand","gcluster create"]
# Cluster Toolkit Blueprint Assistant > [!WARNING] > This skill is experimental. Always notify the user before executing diagnostic steps and explicitly request confirmation for any proposed remediations. ## Core rules (read before touching any YAML) 1. **Start from `./gcluster explain` or the catalog, never from scratch.** Run `./gcluster explain <specs...>` (e.g. `./gcluster explain slurm a3u lustre`) or read [`references/catalog.yaml`](references/catalog.yaml) to find an existing blueprint under `examples/` or `community/examples/` that already matches the request. Do not run `ls`, `find`, or `grep` across `examples/` or `.tf` files to discover templates or modules; run `./gcluster explain <module>` (e.g. `./gcluster explain modules/file-system/managed-lustre`) or check the catalog's `module_wiring` section for a module's `required_inputs`, which module sources satisfy them (`recommended_use`), and which modules consume its outputs (`consumed_by`). If the request is a question ("do we have…", "can I get…", "what's in…"), answer it using `./gcluster explain` / `catalog.yaml` and confirm the approach with the user **before** editing anything. 2. **Customize through a new, untracked `deployment.yaml`, not module blocks.** If the request only changes project, region/zone, node counts, machine types, Spot/reservation, or storage size: - **Case 1 — All requested knobs already exist in `vars:`:** Create or edit **only** a **new, untracked** `deployment.yaml` (or `<name>-deployment.yaml`) passed with `-d`, containing only `vars:` (omit `terraform_backend_defaults: type: gcs` unless the user provides a GCS state bucket). Do **not** copy or edit the blueprint `.yaml` file, and **never modify git-tracked companion `*-deployment.yaml` files** under `examples/` or `community/examples/` in place (read them only as a reference for variable names). - **Case 2 — Promoting a hardcoded module setting to `vars:`:** When a requested setting is hardcoded inside a module block and missing from `vars:`, never modify a tracked template under `examples/` or `community/examples/` in place. Instead, copy the template to a new file in the **same directory** with `ai_assisted: true` (or reuse an existing custom copy in place), add the missing knob to `vars:` with a default **equal to the value that was hardcoded**, reference it in the module via `$(vars.<name>)`, and put the user's override value in `deployment.yaml`. - **A template only exposes knobs it already set explicitly.** Tracked blueprints promote to `vars:` only settings that were literal values in a module block; they do not add Spot, reservation, DWS or any other input the module block was silent about. If the knob you need is not in `vars:` (check the catalog entry), it is Case 2 — copy the blueprint. Never add the knob to the tracked template, and never pass it as a bare module-input name in `deployment.yaml` hoping it will be picked up. - **Mutually exclusive consumption models (Spot vs. DWS Flex Start vs. Reservations):** Spot VMs (`spot` / `enable_spot_vm`), DWS Flex Start (`enable_flex_start` / `dws_flex`), and Reservations (`reservation` / `SPECIFIC_RESERVATION`) cannot be combined on the same node pool or nodeset. If the user asks to enable Spot on a DWS Flex Start or Reservation blueprint (or vice versa), **never silently disable** the blueprint's existing consumption model (e.g. do not set `enable_flex_start: false` unprompted); stop, explain that the two models are mutually exclusive, and ask the user which single consumption model they want. - On casual or vague requests (e.g. a "small" or "cheap" cluster with Spot VMs and N nodes), override **only** the requested Spot and node-count variables (such as `compute_enable_spot_vm: true`, `debug_enable_spot_vm: true`, `h3_enable_spot_vm: true`, `compute_node_count_dynamic_max: 2`) in `deployment.yaml`—keep the blueprint's default machine types (never downgrade `c2-standard-60` to `e2-standard-2`, which fails `gvnic_enabled` validation) and never remove partitions or storage modules unprompted. - Do **not** add, remove, rename or restructure `deployment_groups[].modules[]` entries for value overrides. 3. **Only edit module blocks when adding or removing capability** (Case 3 — e.g. adding a Managed Lustre instance or a second node pool) — and do it on a **copy**, not the shared template: copy the blueprint to a new file in the **same directory** (e.g. `examples/gke-a4/my-gke-a4.yaml`) or copy the whole folder, because blueprints reference sibling files via `$(ghpc_stage("./…"))`. Never modify tracked template files under `examples/` or `community/examples/` in place (once a custom copy exists—identifiable by `ai_assisted: true` or untracked `git status`—edit that copy directly in place for follow-ups rather than copying it a second time). Copy the module block from `./gcluster explain <module>` or a blueprint in the catalog that already has it, keep its `use:` list, and check `module_wiring.<source>.required_inputs` to confirm every required input is satisfied by a module in `use:`. 4. **Mark agent-authored blueprints.** Add `ai_assisted: true` at the top level of any blueprint you create, copy, or repair/fix (if an existing custom blueprint you are fixing lacks `ai_assisted: true`, add it). 5. **Validate with gcluster, fix the blueprint, never the output.** `./gcluster expand` and `./gcluster create` are the validation step. Once `./gcluster create` or `./gcluster expand` succeeds, **stop immediately**—do not run `terraform init` or `terraform validate` afterwards. If they or `./gcluster deploy` fail, fix the blueprint YAML or `deployment.yaml` and re-run `./gcluster`. Never bypass validators via CLI flags (`-l IGNORE`, `--validation-level IGNORE`, `--skip-validators`), environment variables (`GHPC_MOCK_MACHINE_CONFIG`), or YAML fields (`validation_level: IGNORE`, or `skip: true` under `validators:`), **even if the user explicitly asks to skip a validator by name**—refuse the bypass, explain that skipping `test_machine_type_in_zone` still fails with a 404 Compute API error during module expansion (for Slurm `machine_configs` lookup) or deployment when the machine type does not exist in that zone, and ask the user for a supported zone or machine type. Never edit generated files under `<deployment_name>/` (`main.tf`, `variables.tf`, `terraform.tfvars`, `defaults.auto.pkrvars.hcl`, `.ghpc/`) and never run `terraform` in any form — not `init`, `validate`, `plan`, `apply`, `destroy`, or `fmt` — even if the user asks you to; refuse, explain, and offer the gcluster equivalent. `./gcluster` has no dry-run `plan` command: if asked for a dry-run plan or resource preview before deploying, never run `terraform plan` or pipe `yes n | ./gcluster deploy`; instead run `./gcluster explain <blueprint.yaml>` and inspect `<deployment_name>/.ghpc/artifacts/expanded_blueprint.yaml`. gcluster regenerates deployment files on every run. Generated `.tf`, `.tfvars` and `.pkrvars.hcl` files have a `# DO NOT EDIT: Generated by gcluster.` line just below the license header — if you see that line, you are in the wrong file, even if a terraform error points at it. A failed `./gcluster deploy` prints a `Hint:` with the exact `create … -w` / `deploy … -w` command to re-run after fixing the source. If you cannot reproduce a reported error with `./gcluster create`, stop and ask the user for the full error output instead of searching further. 6. **Destructive or mutating actions require confirmation.** `./gcluster deploy`, `./gcluster destroy`, and anything that creates or deletes cloud resources must be presented as a `[PROPOSED REMEDIATION PLAN]` and wait for the user to reply `yes`. The same applies to local destructive steps: deleting a deployment folder (`rm -rf <deployment_name>`), deleting or overwriting user `.yaml` files, and `git checkout` / `git restore` / `git reset` / `git clean` of user edits — present a `[PROPOSED REMEDIATION PLAN]` stating exactly what will be removed and wait for `yes`. Prefer `./gcluster create … -w` over deleting the deployment folder. 7. **Do only what was asked.** If you notice unrelated warnings or problems (deprecation notices, bugs in Go code, other blueprints), report them in your summary — do not fix, migrate or refactor them unprompted. ## Workflow ### 0. Ensure `./gcluster` is built From the root of the repository, verify that `./gcluster` exists or build it via `make`: ```bash test -x ./gcluster || make ``` ### 1. Find the right template Use `./gcluster explain` or search `references/catalog.yaml`: ```bash # Find the exact or closest blueprint for given specs (plus missing module snippets): ./gcluster explain slurm a3u lustre ./gcluster explain gke a3-megagpu lustre # Inspect a specific blueprint's required GCP APIs, customizable vars, and stages: ./gcluster explain examples/machine-learning/a3-ultragpu-8g/a3ultra-slurm-blueprint.yaml # Inspect a module's required APIs, auto-injected inputs, and ready-to-copy 'use:' snippet: ./gcluster explain modules/file-system/managed-lustre # Or search the catalog directly by scheduler, machine type, storage, or module source: grep -n "scheduler: slurm" skills/cluster-blueprint-assistant/references/catalog.yaml grep -n "managed-lustre" skills/cluster-blueprint-assistant/references/catalog.yaml ``` Each `blueprints[]` entry gives you: `path`, optional `deployment_path`, `scheduler`, `description`, `machine_types`, the default `vars`, and the `modules` list with `id`, `source`, `use`, and configured `settings` keys. Prefer blueprints under `examples/` over `community/examples/` when both match. ### 2. Customize via deployment.yaml Always create a **new, untracked** deployment file (e.g. `my-deployment.yaml` in the repo root or `examples/<name>-deployment.yaml`) containing only `vars:`. **Never modify git-tracked companion `*-deployment.yaml` files** under `examples/` or `community/examples/` in place (`deployment_path` in the catalog entry, such as `examples/gke-a4/gke-a4-deployment.yaml` or `examples/hpc-slurm-h4d/hpc-slurm-h4d-deployment.yaml`) — read them only to inspect variable names, and omit `terraform_backend_defaults: type: gcs` unless the user explicitly provides a GCS state bucket. Your new deployment file is user-specific — do not commit it. Only override keys that already exist in the blueprint's `vars:` (see the catalog entry). Variable syntax is Cluster Toolkit's `$(vars.name)`, not Terraform's `${var.name}`. ```yaml # my-deployment.yaml --- vars: project_id: my-proj deployment_name: hpc-slurm-small region: us-central1 zone: us-central1-a compute_node_count_dynamic_max: 4 compute_machine_type: c2-standard-30 ``` > Global `vars:` are injected into **every** module that declares an input with > the same name and does not set it explicitly. That is why blueprint vars use > prefixed names such as `compute_machine_type` / > `compute_node_count_dynamic_max`. Never add an unprefixed variable name that > is shared by multiple modules in the blueprint: in `hpc-slurm.yaml`, for > example, a bare `enable_spot_vm` would silently apply to all three nodesets > (`debug_nodeset`, `compute_nodeset`, `h3_nodeset` — none of them sets it), and > a bare `machine_type` would silently apply to `slurm_controller` only (the one > module that does not set it explicitly). Where every module already sets the > input explicitly (as `node_count_dynamic_max` is on all three nodesets in > `hpc-slurm.yaml`), the bare name is injected nowhere and `gcluster` fails with > `test_deployment_variable_not_used` instead. #### Variable naming & override rules Customizable vars in `examples/**/*.yaml` and `community/examples/**/*.yaml` follow `<functional_role>_<exact_terraform_input>`; the suffix is the module input the var feeds (`_machine_type`, `_node_count_dynamic_max`, `_node_count_static`, `_static_node_count`, `_initial_node_count`, `_num_instances`, `_instance_count`, `_autoscaling_total_min_nodes` / `_autoscaling_total_max_nodes`, `_size_gb`, `_size_gib`, `_capacity_gb`, `_capacity_gib`, `_disk_size_gb`, `_disk_size`, `_enable_spot_vm`, `_spot`), so no translation is needed between the var and the setting. Every sizing, machine-type, storage-capacity and Spot knob on a blueprint's worker compute and storage modules is exposed this way with a concrete default equal to what the module would otherwise use (for example `compute_enable_spot_vm: false` in `hpc-slurm.yaml`), so a user only ever *has* to fill in `project_id`, `deployment_name`, `region` and `zone`. Control-plane roles (`login_`, `controller_`, `system_node_pool_`) expose machine type and disk size but never Spot: those modules do not accept it. | Role prefix | Meaning | | :--- | :--- | | `compute_` | the worker nodes — a Slurm nodeset **or** a GKE node pool — when the blueprint has one | | `login_` / `controller_` | the Slurm login / controller VM | | `system_node_pool_` | the GKE system node pool (literal `gke-cluster` input) | | `client_` | a plain VM that mounts storage (e.g. `pfs-managed-lustre-vm`) | | `image_build_` | the temporary Packer VM that builds a custom image (`ml-slurm`, `ml-slurm-g4`) | | `<partition>_` / `<pool>_` (`debug_`, `hp_pool_`, `local_ssd_pool_`, `n2_`, `h3_`, `a2_8_`) | one of several worker pools; the prefix is the partition / pool name the user already addresses | | `homefs_`, `appsfs_`, `scratchfs_`, `sharefs_`, `lustre_`, `hyperdisk_<type>_`, `data_disk_` | storage, by mount role | Rules the agent must respect: - **Hardware-named partitions expose node counts and Spot, never `machine_type`** (`h3`, `a2`, `g2`, `n2`, `c2`, `c2d`, `c3`, `a2_8`, `a2_16`). The hardware name *is* the partition's identity; "run the `g2` partition on g4" is "add a partition" → Case 2/3 (copy the blueprint), not a `deployment.yaml` override. Functionally named pools (`compute`, `debug`, `hp_pool`, …) do expose `<role>_machine_type`. - **Hardware-locked blueprints hardcode `machine_type` in the module.** The GKE and Slurm A3 / A4 / A4X blueprints have multi-NIC RDMA / GPUDirect wiring that only works with one shape, so they expose no compute machine-type var at all (the shape still appears in the catalog's `machine_types`). If the user asks for a different accelerator or machine series (e.g. `a3-highgpu-8g` on `gke-a4.yaml`), do **not** copy that blueprint and change `machine_type` — instead, use `./gcluster explain` to switch to the matching blueprint in `examples/` (e.g. `examples/gke-a3-highgpu/gke-a3-highgpu.yaml`). Multi-shape
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub