feat(skills): add terraform-patterns skill

Terraform and OpenTofu patterns: project layout, version pinning, remote
state with locking, validated variables, secrets handling, for_each vs
count, moved/import refactoring, plan/apply CI workflow, and debugging.

Registered in package.json files, install-modules.json (platform module),
and the skill-triggers registry digest. Catalog counts synced (311 -> 312).
This commit is contained in:
Naga Chand Putta committed 2026-10-16 23:07:48 -04:00
1 parent 76aaa77df6
commit 028692c2d7
15 files changed
+489 -19

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@
{
"name": "ecc",
"source": "./",
"description": "Harness-native ECC operator layer - 71 agents, 311 skills, 97 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
"description": "Harness-native ECC operator layer - 71 agents, 312 skills, 97 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
"version": "2.2.3",
"author": {
"name": "Affaan Mustafa",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "ecc",
"version": "2.2.3",
"description": "Harness-native ECC plugin for engineering teams - 71 agents, 311 skills, 97 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses",
"description": "Harness-native ECC plugin for engineering teams - 71 agents, 312 skills, 97 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses",
"author": {
"name": "Affaan Mustafa",
"url": "https://x.com/affaanmustafa"
+1 -1
View File
@@ -4,7 +4,7 @@ This file provides Gemini CLI with the baseline ECC workflow, review standards,
## Overview
ECC is a cross-harness coding system with 71 specialized agents, 311 skills, and 97 commands.
ECC is a cross-harness coding system with 71 specialized agents, 312 skills, and 97 commands.
Gemini support is currently focused on a strong project-local instruction layer via `.gemini/GEMINI.md`, plus the shared MCP catalog and package-manager setup assets shipped by the installer.
+2 -2
View File
@@ -1,6 +1,6 @@
# ECC — Agent Instructions
This is a **production-ready AI coding plugin** providing 71 specialized agents, 311 skills, 97 commands, and automated hook workflows for software development.
This is a **production-ready AI coding plugin** providing 71 specialized agents, 312 skills, 97 commands, and automated hook workflows for software development.
**Version:** 2.2.3
@@ -202,7 +202,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
```
agents/ — 71 specialized subagents
skills/ — 311 workflow skills and domain knowledge
skills/ — 312 workflow skills and domain knowledge
commands/ — 97 slash commands
hooks/ — Trigger-based automations
rules/ — Always-follow guidelines (common + per-language)
+3 -3
View File
@@ -141,12 +141,12 @@ Instead of rebuilding that process in every prompt, you install it once and make
ECC is MIT-licensed open source. It works best with Claude Code today, has native Codex and Qoder plugin paths, and provides capability-limited adapters for Cursor, OpenCode, Gemini, Zed, GitHub Copilot, Antigravity, Qwen, and other harnesses. See the [support status matrix](#platform-support) before assuming feature parity.
Access to 71 agents, 311 skills, and 97 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work.
Access to 71 agents, 312 skills, and 97 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work.
| Included | Count | What it gives you |
| ---------------- | ----------: | ------------------------------------------------------------------------------------ |
| Agents | 71 agents | Planning, review, build repair, security, architecture, and domain work |
| Skills | 311 skills | TDD, research, security, docs, frontend, data, ML, operations, and more |
| Skills | 312 skills | TDD, research, security, docs, frontend, data, ML, operations, and more |
| Commands | 97 commands | Convenient entry points while ECC moves to a skills-first surface |
| Hooks and memory | Runtime | Enforcement, session summaries, continuous learning, instincts, and context controls |
| Rules | Selective | Always-loaded standards you choose by language or project |
@@ -859,7 +859,7 @@ Stable graduation of the 2.0 line: control-pane substrate, worktree lifecycle se
```text
ECC/
|-- agents/ # 71 specialized subagents for delegation
|-- skills/ # 311 reusable workflows loaded on demand
|-- skills/ # 312 reusable workflows loaded on demand
|-- commands/ # 97 maintained slash-command shims
|-- rules/ # opt-in common and language standards
|-- hooks/ # runtime automation and enforcement
+1 -1
View File
@@ -200,7 +200,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
/plugin list ecc@ecc
```
**完成!** 你现在可以使用 71 个代理、311 个技能和 97 个命令。
**完成!** 你现在可以使用 71 个代理、312 个技能和 97 个命令。
### multi-* 命令需要额外配置
+1 -1
View File
@@ -1,7 +1,7 @@
# Soul
## Core Identity
ECC is a production-ready AI coding plugin with 71 specialized agents, 311 skills, 97 commands, and automated hook workflows for software development.
ECC is a production-ready AI coding plugin with 71 specialized agents, 312 skills, 97 commands, and automated hook workflows for software development.
## Core Principles
1. **Agent-First** — route work to the right specialist as early as possible.
+1
View File
@@ -168,6 +168,7 @@ skills:
- tasteforge-video
- tdd-workflow
- team-builder
- terraform-patterns
- test-collaboration
- token-budget-advisor
- token-card
+2 -2
View File
@@ -1,6 +1,6 @@
# ECC — Agent Talimatları
Bu, yazılım geliştirme için 71 özel agent, 311 skill, 97 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
Bu, yazılım geliştirme için 71 özel agent, 312 skill, 97 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
**Sürüm:** 2.2.3
@@ -142,7 +142,7 @@ Başarısızlık sorunlarını giderin: test izolasyonunu kontrol edin → mockl
```
agents/ — 71 özel subagent
skills/ — 311 iş akışı skillleri ve alan bilgisi
skills/ — 312 iş akışı skillleri ve alan bilgisi
commands/ — 97 slash command
hooks/ — Tetikleyici tabanlı otomasyonlar
rules/ — Her zaman uyulması gereken kurallar (ortak + dile özel)
+2 -2
View File
@@ -1,6 +1,6 @@
# ECC — 智能体指令
这是一个**生产就绪的 AI 编码插件**,提供 71 个专业代理、311 项技能、97 条命令以及自动化钩子工作流,用于软件开发。
这是一个**生产就绪的 AI 编码插件**,提供 71 个专业代理、312 项技能、97 条命令以及自动化钩子工作流,用于软件开发。
**版本:** 2.2.3
@@ -147,7 +147,7 @@
```
agents/ — 71 个专业子代理
skills/ — 311 个工作流技能和领域知识
skills/ — 312 个工作流技能和领域知识
commands/ — 97 个斜杠命令
hooks/ — 基于触发的自动化
rules/ — 始终遵循的指导方针(通用 + 每种语言)
+3 -3
View File
@@ -264,7 +264,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
/plugin list ecc@ecc
```
**搞定!** 你现在可以使用 71 个智能体、311 项技能和 97 个命令了。
**搞定!** 你现在可以使用 71 个智能体、312 项技能和 97 个命令了。
***
@@ -1178,7 +1178,7 @@ opencode
|---------|---------------|----------|--------|
| 智能体 | PASS: 71 个 | PASS: 12 个 | **Claude Code 领先** |
| 命令 | PASS: 97 个 | PASS: 35 个 | **Claude Code 领先** |
| 技能 | PASS: 311 项 | PASS: 37 项 | **Claude Code 领先** |
| 技能 | PASS: 312 项 | PASS: 37 项 | **Claude Code 领先** |
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
@@ -1286,7 +1286,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|---------|-----------------------|------------|-----------|----------|
| **智能体** | 71 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
| **命令** | 97 | 共享 | 基于指令 | 35 |
| **技能** | 311 | 共享 | 10 (原生格式) | 37 |
| **技能** | 312 | 共享 | 10 (原生格式) | 37 |
| **钩子事件** | 8 种类型 | 15 种类型 | SessionStart(1 种类型) | 11 种类型 |
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | 1 个 SessionStart 引导脚本 | 插件钩子 |
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
@@ -1,6 +1,6 @@
{
"coverage": {
"skills": 311,
"skills": 312,
"withTriggers": 33
},
"generatedAt": "2026-09-24T23:51:22.784Z",
@@ -10,7 +10,7 @@
"id": "hand-seeded",
"source": "manual-curation-pending-regeneration"
},
"registryDigest": "498caee2738006d64530e9be34883111cf67742d8c4389f39d61299e1a1c4400",
"registryDigest": "07b4280fa84180336038837365cb8b4e2b0f0c55569967f9f5599b96f58ba79e",
"schemaVersion": 1,
"triggers": {
"skill:agent-runtime-gateway-smoke-test": [
+1
View File
@@ -1033,6 +1033,7 @@
"skills/homelab-vlan-segmentation",
"skills/homelab-wireguard-vpn",
"skills/kubernetes-patterns",
"skills/terraform-patterns",
"skills/uncloud"
],
"targets": [
+1
View File
@@ -380,6 +380,7 @@
"skills/tdd-workflow/",
"skills/team-agent-orchestration/",
"skills/team-builder/",
"skills/terraform-patterns/",
"skills/terminal-opener/",
"skills/terminal-ops/",
"skills/token-budget-advisor/",
+467
View File
@@ -0,0 +1,467 @@
---
name: terraform-patterns
description: Terraform and OpenTofu infrastructure-as-code patterns for project layout, remote state, modules, variables, secrets, safe plan/apply workflows, and CI. Use when writing or reviewing .tf files, structuring modules or environments, configuring backends and state locking, or debugging plan drift, state, and provider errors.
metadata:
origin: ECC
---
# Terraform Patterns
Production-grade Terraform (and OpenTofu) patterns for writing infrastructure code that is reviewable, repeatable, and safe to apply.
## When to Activate
- Writing or reviewing `.tf` or `.tfvars` files
- Structuring a repo into modules and environments
- Configuring a remote backend, state locking, or state migration
- Passing variables and handling secrets in infrastructure code
- Setting up plan/apply automation in CI
- Debugging a failed plan, state drift, a lock error, or a provider version conflict
- Refactoring resources without destroying them (`moved`, `import`)
## When to Use
> Same as **When to Activate** above. This alias satisfies repo skill-format conventions. Use this skill any time you are writing, reviewing, or debugging Terraform or OpenTofu configuration.
## How It Works
This skill gives **copy-pasteable patterns** and a **safe command workflow**, organized by task:
1. **Project layout** - Environments as thin roots that call reusable modules.
2. **Versions and providers** - Pin Terraform and provider versions; commit the lock file.
3. **Remote state** - Encrypted, locked, per-environment state.
4. **Variables and outputs** - Typed, validated inputs and minimal outputs.
5. **Secrets** - Keep values out of code, state exposure in mind.
6. **Resources** - `for_each` over `count`, `lifecycle`, and tagging.
7. **Modules** - Small, single-purpose, versioned.
8. **Refactoring** - `moved` and `import` blocks instead of destroy/recreate.
9. **Safe workflow and CI** - Plan is saved, reviewed, then applied.
10. **Debugging and anti-patterns** - Common errors and what to avoid.
Examples use AWS for concreteness. The structure applies to any provider.
## Examples
| Task | Jump to |
|------|---------|
| Layout for dev/staging/prod | [Project Layout](#project-layout) |
| Remote state with locking | [Remote State](#remote-state) |
| Validated variables | [Variables and Outputs](#variables-and-outputs) |
| Avoid secrets in state and code | [Secrets](#secrets) |
| Rename a resource without destroying it | [Refactoring Without Destroying](#refactoring-without-destroying) |
| Plan/apply in CI | [Safe Workflow and CI](#safe-workflow-and-ci) |
| Lock or drift errors | [Debugging](#debugging) |
---
## Project Layout
Keep environments as thin root modules. Put logic in reusable modules.
```text
infra/
├── modules/
│ └── network/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── versions.tf
└── envs/
├── dev/
│ ├── main.tf
│ ├── backend.tf
│ ├── providers.tf
│ └── terraform.tfvars
└── prod/
├── main.tf
├── backend.tf
├── providers.tf
└── terraform.tfvars
```
Rules of thumb:
- One state per environment and per blast-radius boundary (network, data, apps). A single giant state makes every plan slow and every mistake large.
- Environments differ by **inputs** (`terraform.tfvars`), not by copy-pasted resources.
- Do not use workspaces as a substitute for separate production and non-production state with separate credentials.
## Versions and Providers
Pin everything. Unpinned providers change behavior between runs.
```hcl
# versions.tf
terraform {
required_version = ">= 1.6.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
}
```
```hcl
# providers.tf
provider "aws" {
region = var.region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Owner = var.owner
}
}
}
```
- Commit `.terraform.lock.hcl`. It records provider checksums so CI and teammates install identical providers.
- Do **not** commit the `.terraform/` directory or any `*.tfstate` file.
- Upgrade deliberately: `terraform init -upgrade`, review the lock file diff, then plan.
## Remote State
Local state does not work for teams and is easy to lose. Use a remote backend with encryption and locking.
```hcl
# backend.tf
terraform {
backend "s3" {
bucket = "example-org-tfstate-prod"
key = "network/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true # S3-native locking (Terraform >= 1.10)
}
}
```
Older Terraform versions use a DynamoDB table for locking instead. Replace
`use_lockfile = true` with:
```hcl
dynamodb_table = "example-org-tfstate-lock"
```
Backend rules:
- Backend blocks cannot reference variables. Pass values with `-backend-config` or a partial config file when you need per-environment values.
- Enable bucket versioning so a bad state write can be recovered.
- Block public access on the state bucket and restrict who can read it. State often contains sensitive values.
- Never use `-lock=false` to get past a lock error. See [Debugging](#debugging).
## Variables and Outputs
Type and validate inputs so mistakes fail at plan time.
```hcl
variable "environment" {
type = string
description = "Deployment environment."
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be one of: dev, staging, prod."
}
}
variable "instance_count" {
type = number
description = "Number of application instances."
default = 2
validation {
condition = var.instance_count >= 1 && var.instance_count <= 20
error_message = "instance_count must be between 1 and 20."
}
}
variable "allowed_cidrs" {
type = list(string)
description = "CIDR blocks allowed to reach the service."
default = []
}
```
```hcl
output "vpc_id" {
description = "ID of the VPC."
value = aws_vpc.this.id
}
output "db_password" {
description = "Generated database password."
value = random_password.db.result
sensitive = true
}
```
- Every variable and output gets a `description`. Modules are read far more often than written.
- Give a `default` only when one is safe in every environment. Required inputs force the caller to decide.
- Mark outputs that carry credentials `sensitive = true`. Note this only hides them from CLI output; they are still stored in state.
## Secrets
Terraform state stores resource attributes in plain text, including passwords and keys that you pass in or that the provider generates.
Do:
- Keep secrets out of `.tf` and committed `.tfvars` files. Supply them at runtime with `TF_VAR_*` environment variables or your CI secret store.
- Reference secrets that already exist in a secret manager instead of creating the value in Terraform.
- Use ephemeral values and write-only attributes where your Terraform and provider versions support them, so the value never lands in state.
- Encrypt state at rest and restrict read access to it.
- Authenticate with short-lived credentials (OIDC from CI, assumed roles) rather than long-lived access keys.
```hcl
# Read an existing secret instead of defining its value in code
data "aws_secretsmanager_secret_version" "db" {
secret_id = "prod/app/db-password"
}
resource "aws_db_instance" "app" {
identifier = "app-${var.environment}"
# ...
password = data.aws_secretsmanager_secret_version.db.secret_string
}
```
> Reading a secret through a data source still places the value in state. Treat the state file as sensitive in every case.
Do not:
- Hardcode credentials in `provider` blocks.
- Commit `terraform.tfvars` files that contain secrets. Add them to `.gitignore`.
- Print secrets with `terraform output -json` in shared CI logs.
## Resources
### Prefer `for_each` over `count`
`count` identifies resources by index. Removing an item from the middle of a list shifts every later item down one index, so Terraform plans to modify or replace the resources after it (and destroy the last one) instead of just removing the one you meant to.
```hcl
# Fragile: removing "b" shifts "c" into b's slot and destroys the last index
resource "aws_iam_user" "bad" {
count = length(var.users)
name = var.users[count.index]
}
# Stable: each resource is keyed by its name; removing "b" destroys only "b"
resource "aws_iam_user" "good" {
for_each = toset(var.users)
name = each.key
}
```
Use `count` only for a simple on/off toggle:
```hcl
resource "aws_cloudwatch_log_group" "app" {
count = var.enable_logging ? 1 : 0
name = "/app/${var.environment}"
}
```
### Lifecycle guards
```hcl
resource "aws_s3_bucket" "data" {
bucket = "example-org-data-${var.environment}"
lifecycle {
prevent_destroy = true
}
}
resource "aws_launch_template" "app" {
# ...
lifecycle {
create_before_destroy = true
}
}
```
- `prevent_destroy` on stateful resources (databases, buckets, encryption keys) turns an accidental destroy into a plan error.
- `ignore_changes` hides drift. Use it narrowly and add a comment saying why.
### Secure defaults
```hcl
resource "aws_s3_bucket_public_access_block" "data" {
bucket = aws_s3_bucket.data.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
```
Avoid `0.0.0.0/0` ingress rules except for deliberately public endpoints, and keep IAM policies to the specific actions and resources needed.
## Modules
A good module does one thing, exposes a small interface, and is versioned.
```hcl
module "network" {
source = "../../modules/network"
environment = var.environment
cidr_block = "10.20.0.0/16"
}
# Registry or git modules: pin a version or ref
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
}
```
- Pin registry modules with `version` and git modules with `?ref=<tag>`. Never track a moving branch.
- Do not configure `provider` blocks inside child modules. Declare `required_providers` and let the root pass providers in.
- Prefer composition: a module takes IDs as inputs rather than creating everything itself.
- Do not wrap a single resource in a module that only forwards its arguments.
## Refactoring Without Destroying
Renaming a resource or moving it into a module looks like a destroy plus a create. Tell Terraform it is the same object.
```hcl
# Resource renamed in code: keep the existing object
moved {
from = aws_instance.web
to = aws_instance.app
}
# Adopt an existing resource into state (Terraform >= 1.5)
import {
to = aws_s3_bucket.legacy
id = "example-org-legacy-bucket"
}
```
Always read the plan after a refactor. A correct `moved` block shows as "has moved" with no destroy.
Use `terraform state` commands only as a last resort, take a backup first, and never edit the state file by hand.
## Safe Workflow and CI
Format and validate locally, then plan, review, and apply the **same** plan.
```bash
terraform fmt -recursive
terraform init
terraform validate
terraform plan -out=tfplan
terraform show tfplan # review before applying
terraform apply tfplan # applies exactly what was reviewed
```
Why `-out`: a bare `terraform apply` re-plans, so what runs can differ from what you reviewed.
Read the plan for:
- `-/+` (replace) and `-` (destroy) lines on stateful resources
- Changes you did not make, which indicate drift or a provider upgrade
- Unexpected counts in the summary line
### CI pipeline shape
```yaml
# .github/workflows/terraform.yml (shape only)
name: terraform
on:
pull_request:
paths: ["infra/**"]
permissions:
id-token: write # OIDC to the cloud provider, no stored keys
contents: read
pull-requests: write
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.9.8
- run: terraform fmt -check -recursive
working-directory: infra/envs/dev
- run: terraform init -input=false
working-directory: infra/envs/dev
- run: terraform validate
working-directory: infra/envs/dev
- run: terraform plan -input=false -out=tfplan
working-directory: infra/envs/dev
```
- Run `plan` on pull requests and post it for review. Run `apply` only from the main branch, after approval, using the saved plan.
- Use `-input=false` in automation so a missing variable fails instead of hanging.
- Give the plan job read-only credentials where the provider allows it, and keep the apply role separate.
- Consider a policy or static-analysis step (for example `tflint`, `checkov`, or `trivy config`) before apply.
## Debugging
| Symptom | Likely cause | What to do |
|---------|--------------|------------|
| `Error acquiring the state lock` | Another run holds the lock, or a run crashed | Confirm no run is active, then `terraform force-unlock <LOCK_ID>`. Do not disable locking. |
| Plan shows changes you did not make | Drift, a provider upgrade, or a changed default | `terraform plan -refresh-only` to see drift. Check the lock file diff. |
| `Provider produced inconsistent result` | Provider bug or version mismatch | Pin and upgrade the provider; check its issue tracker. |
| `Cycle` error | Two resources depend on each other | Break the loop with an intermediate resource or remove the redundant reference. |
| Resource already exists | It was created outside Terraform | Add an `import` block, then plan. |
| `for_each` value depends on unknown values | Keys come from an attribute known only after apply | Key by a static value, or apply in two steps with `-target` as a one-time exception. |
| Destroy and recreate on rename | Missing `moved` block | Add a `moved` block, re-plan. |
Useful commands:
```bash
terraform state list # what is tracked
terraform state show aws_instance.app # inspect one resource
terraform plan -refresh-only # detect drift without proposing changes
terraform providers # which providers and versions are in use
TF_LOG=DEBUG terraform plan 2> debug.log # verbose logs; may contain secrets
```
> `TF_LOG` output can contain credentials. Do not paste it into issues or CI logs without scrubbing it.
## Anti-Patterns
| Anti-pattern | Why it hurts | Instead |
|--------------|--------------|---------|
| `terraform apply` without a saved plan | Applies something other than what was reviewed | `plan -out`, review, `apply tfplan` |
| `-auto-approve` on a human workflow | Removes the last safety check | Reserve it for pipelines that apply a reviewed plan |
| Local state for shared infrastructure | Lost state, no locking | Remote backend with locking |
| One huge state for everything | Slow plans, large blast radius | Split by environment and domain |
| Secrets in `.tf` or committed `.tfvars` | Leaked credentials | Runtime variables or a secret manager |
| Unpinned providers or modules | Surprise changes between runs | Version constraints plus the lock file |
| `count` over a list of named things | Index shift causes destroys | `for_each` with stable keys |
| `-target` as routine practice | Skips dependencies, leaves state inconsistent | Fix the root cause; use only as a one-time exception |
| Editing state by hand | Corrupts state | `moved`, `import`, or `terraform state` with a backup |
| Disabling locking to bypass a stuck lock | Concurrent writes corrupt state | Verify and `force-unlock` the specific lock |
## Checklist
Before opening a PR:
- [ ] `terraform fmt -recursive` and `terraform validate` pass
- [ ] Terraform and provider versions are pinned; `.terraform.lock.hcl` is committed
- [ ] State is remote, encrypted, and locked
- [ ] No secrets in code, `.tfvars`, or outputs that are not `sensitive`
- [ ] Variables have types, descriptions, and validation where a bad value is plausible
- [ ] Stateful resources have `prevent_destroy` or an explicit reason they do not
- [ ] The plan was read: no unexpected destroys or replacements
- [ ] Refactors use `moved` or `import`, not destroy and recreate
- [ ] CI applies the saved, reviewed plan
## Related Skills
- `kubernetes-patterns` - cluster workloads once the infrastructure exists
- `docker-patterns` - container images that Terraform-managed infrastructure runs
- `deployment-patterns` - release and rollout strategies
- `security-review` - broader security checklist
- `database-migrations` - schema changes, distinct from infrastructure changes