一键导入
qa-blueprint
AI-QA an Aviatrix blueprint by deploying it as a customer using only the README, capturing UX gaps, and opening a fix PR. Replaces test-blueprint.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
AI-QA an Aviatrix blueprint by deploying it as a customer using only the README, capturing UX gaps, and opening a fix PR. Replaces test-blueprint.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Tear down an Aviatrix blueprint and clean up all cloud resources. Handles both simple single-layer and complex multi-layer blueprints in the correct reverse order.
Pre-QA quality gate for Aviatrix blueprints. Runs tiered validation (blockers / standards / quality) and produces a QA-readiness verdict. Use before submitting a blueprint to QA, before merging, or whenever asked to "validate", "check", or "grade" a blueprint.
Analyze a blueprint directory and generate comprehensive documentation about what it creates and requires. Use when the user asks to analyze, understand, or document a blueprint.
Deploy an Aviatrix blueprint to your environment with guided, multi-step orchestration. Handles both simple single-layer and complex multi-layer blueprints.
| name | qa-blueprint |
| description | AI-QA an Aviatrix blueprint by deploying it as a customer using only the README, capturing UX gaps, and opening a fix PR. Replaces test-blueprint. |
| argument-hint | <blueprint-name> [--dry-run] |
| disable-model-invocation | true |
| allowed-tools | ["Bash(terraform *)","Bash(az *)","Bash(aws *)","Bash(gcloud *)","Bash(kubectl *)","Bash(helm *)","Bash(curl *)","Bash(gh *)","Bash(git *)","Bash(ls *)","Bash(cp *)","Bash(mkdir *)","Bash(rm *)","Bash(grep *)","Bash(awk *)","Bash(sed *)","Bash(python3 *)","Bash(jq *)","Bash(source *)","Bash(test *)","Read","Edit","Write","Glob","Grep"] |
AI-QA an Aviatrix blueprint by deploying it as a customer using only the README. Captures UX gaps that surface during the run (copy-paste failures, stale values, README↔code mismatches, missing prereqs), applies fixes, and opens a PR.
This skill replaces /test-blueprint. The difference is mindset: test-blueprint asked "does it work?"; qa-blueprint asks "does it work for a customer reading only the README?". The README is the agent's only source of truth for what to do during the run.
/qa-blueprint <blueprint-name> [--dry-run]
<blueprint-name> — directory under blueprints/ (e.g., azure-aks-multicluster). Multi-cloud blueprints with aws/, azure/, gcp/ subdirs require the cloud subpath (e.g., k8s-cluster-aas/aws).--dry-run — parse the README and print the phase plan without deploying. Useful for verifying the skill itself.One pass per invocation. Each pass produces one PR with all fixes from that run.
Before invoking the skill, verify the following are set up:
$AVIATRIX_ENV_FILE exported — typically in ~/.zshenv. Default is ~/Documents/Scripting/chris-avx-lab/controller_env_ga.sh. Override with any path containing AVIATRIX_CONTROLLER_IP / AVIATRIX_USERNAME / AVIATRIX_PASSWORD exports.az account show returns the right subscriptionaws sts get-caller-identity worksgcloud auth list shows an active accountgh authenticated — gh auth status returns logged in. Required for opening the PR at the end.--no-verify is never used).During Phases 1–5 (parse, pre-flight, deploy, test, destroy), the README is the only source of truth for what to do. Project memory, prior conversation context, codebase reading, controller API queries — these are off-limits for deciding the next action. They remain available for verifying whether the README's claims hold.
README is silent → log a gap, pick the simplest interpretation, continue. Don't fail the run on ambiguity; document it. Example: README says cp tfvars.example tfvars without specifying which variables to fill — pick defaults from example file comments, log "README doesn't specify required vs optional vars".
README says X, code says Y → log a gap, follow the README first. The README is what a customer reads; code is what insiders read. Example: README says context name is frontend; terraform output kubectl_config_command returns no --context. Run the README's manual az aks get-credentials … --context frontend block, log the mismatch.
A documented command fails → log the failure verbatim, retry once if it looks transient, then either pragmatic-workaround + continue or abort the phase. The workaround proves the gap fix. Example: kubectl apply -f webgrouppolicy-dev.yaml fails with namespaces "dev" not found → log gap, run kubectl create namespace dev, retry the apply, continue.
Insider knowledge is for verification only. terraform state list, az resource list, controller API queries, etc. — fine for checking whether a documented behavior actually happened. Not for deciding what step to do next — that's always the README's job.
Memory cross-checks are diagnostic, not corrective. If memory says "this controller version always fails step X with error Y", the agent still runs step X exactly as documented, observes the failure, logs the gap. The point is to verify the README accommodates that failure path; pre-empting it would mask the gap.
| Category | Example |
|---|---|
| copy-paste-failure | Missing kubectl create namespace, ${ENV_VAR} left unexpanded in tfvars instructions |
| stale-value | Threat IP rotated out of feed, version bump needed |
| readme-code-mismatch | tf output missing --context, wrong subnet name |
| unstated-prereq | Step Y depends on X but X is in a separate non-obvious section |
| wrong-expected-output | "Health: Healthy" claimed at a step where probe is still failing |
| missing-recovery | Documented step fails reliably; recovery exists but is buried in a callout |
| ambiguous-wording | "your IP" vs "controller IP" vs "client IP" used inconsistently |
git log --since="7 days ago" -- <file> covers it)All run artifacts live in:
/tmp/qa-blueprint-<blueprint-name>-<timestamp>/
├── gaps.md # accumulating gap log
├── phase-0-bootstrap.log
├── phase-2-preflight.log
├── phase-3-deploy.log
├── phase-4-test.log
├── phase-5-destroy.log
├── phase-6-fix-plan.md
└── report.md # final report, also used as PR body
<timestamp> is date +%Y%m%d-%H%M%S at run start.
The dir is auto-cleaned at end of a fully successful run; preserved on failure for inspection or resume.
Each gap is a fenced YAML block in gaps.md. Append one block per gap as it surfaces.
- id: 1
category: copy-paste-failure
phase: test-scenario-6
file: blueprints/azure-aks-multicluster/README.md
line: 605
symptom: |
`kubectl apply -f webgrouppolicy-dev.yaml --context frontend` failed with:
Error from server (NotFound): namespaces "dev" not found
expected_per_readme: WebgroupPolicy applied to dev namespace
actual: namespace dev does not exist; YAML targets it
workaround_used: kubectl create namespace dev --context frontend
fix_proposal: |
Insert `kubectl create namespace dev --context frontend` before the
webgrouppolicy-dev apply in the Scenario 6 code block. Add a
one-line note explaining the namespace is not deployed by Terraform.
files_to_edit:
- blueprints/azure-aks-multicluster/README.md
Required fields: id, category, phase, file, symptom, fix_proposal, files_to_edit.
Optional fields: line (when known), expected_per_readme, actual, workaround_used.
id is monotonic within the run (1, 2, 3, …). Final report references gaps by these IDs.
Generated at end of run. Used as PR body. Format:
# QA run: <blueprint-name>
- Branch: qa/<name>-YYYY-MM-DD-<n>
- Wall-clock: <X>m
- Test scenarios: <P>/<T> pass (<W> worked-around, see gap list)
## Gaps found and fixed (<N>)
- #1 [copy-paste-failure] README.md:605 — missing `kubectl create namespace dev`
- #2 [stale-value] README.md:564 — threat IP synced to gatus.yaml
…
## Test scenarios
| # | Scenario | Result |
|---|---|---|
| 1 | Internet → AppGW | PASS |
| 2 | East-west cross-cluster | PASS |
| 3 | DCF egress allowed | PASS |
…
## Resources verified clean
- 0 orphan resource groups in <subscription>
- 0 stale state entries
The skill writes this file before opening the PR; gh pr create --body @<report-path> consumes it directly.
The skill walks the README from top to bottom, treating each section as ground truth. Phases run in order:
<blueprint-name>. Resolve to blueprints/<arg>/. If that directory doesn't exist, abort with: Blueprint not found: blueprints/<arg>/.git rev-parse --abbrev-ref HEAD.main (or whatever gh repo view --json defaultBranchRef -q .defaultBranchRef.name returns), construct branch name qa/<blueprint-name>-$(date +%Y-%m-%d)-<n> where <n> is the lowest unused integer (check both local git branch --list and remote git ls-remote --heads origin). Run git checkout -b <branch>.${AVIATRIX_ENV_FILE:-$HOME/Documents/Scripting/chris-avx-lab/controller_env_ga.sh}. Verify file exists (test -f). Source it. Verify AVIATRIX_CONTROLLER_IP, AVIATRIX_USERNAME, AVIATRIX_PASSWORD are now set.grep -lE "provider \"(azurerm|aws|google)\"" blueprints/<name>/**/*.tf — collect the union of provider blocks across all .tf files in the blueprint.az account show --query id -o tsv — non-emptyaws sts get-caller-identity --query Account --output text — non-emptygcloud auth list --filter=status:ACTIVE --format="value(account)" — non-emptygh auth. gh auth status exits 0.RUN_DIR=/tmp/qa-blueprint-<name>-$(date +%Y%m%d-%H%M%S); mkdir -p "$RUN_DIR"; touch "$RUN_DIR/gaps.md".If any step in Phase 0 fails, abort with a specific remediation message. No cloud resources have been touched yet, so no destroy is required.
blueprints/<name>/README.md.## headers):
### Step N: as a phase to execute. Note which steps are described as "parallel with step N+1" or "in parallel" — those are parallelism opportunities.### Scenario N: as a test to run.### Step N: as a destroy phase to execute in order. Note any > [!IMPORTANT] callouts inside destroy steps — those usually flag known eventual-consistency issues with documented recovery.$RUN_DIR/phase-plan.md.--dry-run was passed, print phase-plan.md to stdout and exit 0. Do not proceed to Phase 2.# Fail-fast pre-flight check or are described as "preflight" / "verify quotas" / similar. Run them as-is. Capture exit code and output to $RUN_DIR/phase-2-preflight.log. Non-zero exit → log a gap (category: wrong-expected-output or unstated-prereq) but do not abort. A customer hitting the same wall would simply note it and try the deploy anyway.terraform fmt -check -recursive blueprints/<name>/. If non-zero, log a gap (category: readme-code-mismatch if there's documented "always run fmt" guidance, otherwise just note as phase-2 fmt drift). Do not abort.terraform validate per layer. For each .tf-containing leaf directory under blueprints/<name>/, run terraform init -backend=false && terraform validate. Failures here are gaps; do not abort.For each step in the parsed Deployment Guide:
Read the step's prose + code blocks. Identify the working directory (cd ...), the variables to set in terraform.tfvars, and the apply command.
Variable filling. For each variable the README requires:
aviatrix_azure_account_name = "Azure").terraform.tfvars.example says name_prefix = "aks-demo", use that.azure_region matches Tested With table, IP ranges from CIDR Allocation table, etc.<var> without a documented value", set placeholder qa-test-<random>.Run terraform init + apply. Use background invocation + Monitor for long-running applies (>5 min). Log full stdout/stderr to $RUN_DIR/phase-3-deploy.log.
Parallelism. When the README marks two steps as "parallel with…" (e.g., "Steps 5 and 6 can run in parallel in separate terminals"), launch both terraform applies in the background concurrently. Wait for both to complete before moving to the next non-parallel step.
Transient retries. If an apply fails with one of the known transient signatures below, retry once with the same args:
connection reset by peer502 Bad Gatewayi/o timeoutResourceGroupBeingDeletedlistClusterUserCredential.*404Successful retry → informational note in report (only a gap if the README didn't already document the transient). Second failure → log gap "deploy step failed: ", abort Phase 3, jump to Phase 5 (destroy).
Per-step verification. Some steps include # Verify sub-blocks. Run these and log their pass/fail. Failures are gaps.
For each scenario identified in Phase 1's parse:
MANUAL in the scenario table — do not log a gap unless the README claims the API/CLI exposes the same data.# Expected: 200 or similar comments. Match the actual output against expected. Mismatch → gap (category: wrong-expected-output), but continue — failed scenarios are documentation, not deploy aborts./api/v1/endpoints/statuses JSON endpoint via the public AppGW IP rather than relying on visual dashboard inspection. Parse the JSON; for each endpoint, check success matches the documented expectation (Egress = true, Threats = false).$RUN_DIR/phase-4-test.log and the running scenario table in report.md.Walk the README's Destroy Instructions in order (the README's destroy section is already in reverse-deploy order).
> [!IMPORTANT] If you hit X, do Y…, treat that as a known recovery path. If step X fails with the matching signature, run the documented recovery Y, then re-run step X. If recovery succeeds, log informational note "documented recovery used"; if recovery fails, log a gap (category: missing-recovery) and continue best-effort.terraform state list to determine which layers have non-empty state.name_prefix. Examples:
az group list --query "[?contains(name, '<name_prefix>')].name" -o tsvaws ec2 describe-vpcs --filters "Name=tag:Blueprint,Values=<name>" --query "Vpcs[].VpcId" --output textterraform state list | wc -l should return 0.report.md.If destroy fails entirely (orphan resources remain), do not abort the run — proceed to Phase 6 with the gap logged. The PR will document the orphans and include manual cleanup commands in the body.
$RUN_DIR/gaps.md.file: — every gap targets one or more files via files_to_edit. Build a per-file list.fix_proposal. Use unique-anchor strings, not line numbers.terraform fmt -check -recursive blueprints/<name>/. Non-zero exit → revert the edits for the offending file (git checkout -- <file>), log "fmt conflict for <file>: " to $RUN_DIR/phase-6-fix-plan.md, do not commit.$RUN_DIR/phase-6-fix-plan.md with one section per fixed file (gap IDs + diff summary). This is for the human reviewing the PR.Generate $RUN_DIR/report.md per the schema in the Gap-Tracking Format section.
Stage only files that gaps touched. Do not git add -A (avoids staging unrelated working-tree changes).
Commit on the current branch:
git commit -m "$(cat <<'EOF'
<blueprint>: QA pass — <N> gap fix(es)
<one-line summary per gap>
- <gap #1 summary>
- <gap #2 summary>
Run report: $RUN_DIR/report.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
No --no-verify. If a hook fails, surface the error and abort Phase 7 — fixes stay uncommitted, state dir kept.
Push. git push -u origin <branch>. If the upstream is already set, just git push.
Open PR.
default_branch=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
gh pr create \
--base "$default_branch" \
--title "<blueprint>: QA pass YYYY-MM-DD" \
--body-file "$RUN_DIR/report.md"
No labels, milestones, assignees, or auto-merge in v1.
Print PR URL to stdout.
rm -rf "$RUN_DIR" and print one-line summary.$RUN_DIR in place, print its path so the user can resume / investigate.Retry-once errors:
connection reset by peer502 Bad Gatewayi/o timeoutResourceGroupBeingDeletedlistClusterUserCredential.*404Retry behavior is identical for terraform apply (Phase 3) and terraform destroy (Phase 5). One retry, same args, no escalation. Succeeded retry → informational note in report.md (only logged as a gap if the README didn't already mention the transient).
| Phase | On fatal failure |
|---|---|
| 0 — bootstrap | Abort. No cloud touched. Print remediation. State dir not created. |
| 1 — parse | Abort. Likely malformed README. Print parse failure. |
| 2 — pre-flight | Continue. Pre-flight failures are gaps, not aborts. |
| 3 — deploy | Skip Phase 4. Always run Phase 5. Log gap "deploy failed at ". |
| 4 — test | Continue. Failed scenarios are gaps. |
| 5 — destroy | Best-effort. Orphan resources reported with explicit cleanup commands. State dir kept. |
| 6 — fix | If fmt-check fails after edits, revert edits for affected files, log conflict, do not commit. State dir kept. |
| 7 — commit/PR | If push or gh fails, fixes stay on the local branch with the commit; user pushes manually. |
/qa-blueprint <name> detects an existing /tmp/qa-blueprint-<name>-* dir and prompts the user before starting fresh.If the agent itself errors out (LSP died, an unexpected tool error), print:
QA run aborted. State preserved at /tmp/qa-blueprint-<name>-<ts>/
Resources may still be deployed. Verify with:
<cloud-specific check derived from the parsed README>
e.g. Azure: az group list --query "[?contains(name, '<name_prefix>')].name" -o tsv
To clean up, re-run /qa-blueprint <name> or destroy manually:
<cloud-specific destroy command derived from the parsed README's destroy section>
The remediation hints come from the parsed Phase 1 plan, not hardcoded.
source is a shell builtin, not a binary. Each Bash tool call spawns a fresh shell, so chain source <file> with the actual command via &&. Example:
source "$AVIATRIX_ENV_FILE" && terraform apply -auto-approve
If Bash(source *) doesn't get permission-allowed at runtime, fall back to inlining: set -a; . "$AVIATRIX_ENV_FILE"; set +a.
Default-branch detection.
default_branch=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
Use this rather than hardcoding main.
Resume detection (Phase 5 always-run guarantee). On entry, after parsing args:
existing=$(ls -1d "/tmp/qa-blueprint-<name>"-* 2>/dev/null | head -1)
If $existing is non-empty, prompt the user: "Existing run dir found at $existing. Resume from destroy, or start fresh? (resume/fresh)". Only continue once the user picks.
Long-running waits. Phase 3 applies (network, AKS clusters) take 5–15 min. Use Bash(... &) + the Monitor tool with a tight regex (Apply complete|Error:|^Error |502 Bad Gateway) to avoid context bloat from Still creating… lines.
Multi-layer parallelism. When the README marks two layers as "parallel with…", launch both in the background and use a single Monitor watching tail -F file1 file2. Both must complete before moving on.
Tool permissions. allowed-tools covers binary invocations and the major Claude tools. If the runtime rejects an unlisted invocation, document the missing entry as a follow-up (do not bypass with shell tricks).
These are intentionally out of scope for v1. PRs welcome.
k8s-cluster-aas/aws, …/azure, …/gcp): v1 handles one cloud per invocation. The arg <blueprint-name> accepts the cloud subpath (e.g., k8s-cluster-aas/aws). Future: orchestrate all three in one run.test-blueprint had Playwright integration; if we need it back, it goes into a separate --with-ui mode.--loop later.--max-cost-usd <N> could abort if estimated spend exceeds the cap.