{"id":17095,"plugin_id":"plugins_6a71c05d80248191a9a73c2ea2c07978","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:13:56.971Z","digest":"965258db9b85eaba6b17f75c0c72428c55c4594a3ac3ebf7d386c22ce67e1ebb","against":null,"payload":{"description":"Create and validate Testkube TestWorkflow YAML files. Use when writing test workflow YAML, choosing step types, or when the user asks to create a Testkube TestWorkflow. Covers shell steps, container run steps, execute composition, templates, services, artifacts, config parameters, and cron triggers. By default it takes free-form requirements and writes a YAML file; it can OPTIONALLY take a structured JSON authoring request and/or emit the finished workflow as JSON for programmatic callers, without changing the default behavior. Does NOT run workflows — that is the testworkflow-runner skill's responsibility.","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":231},{"relative_path":"assets/json-mode-input.schema.json","size_in_bytes":2587},{"relative_path":"assets/json-mode-output.schema.json","size_in_bytes":3351},{"relative_path":"assets/template-schema.yaml","size_in_bytes":15534},{"relative_path":"assets/workflow-schema.yaml","size_in_bytes":15952},{"relative_path":"examples/artillery-distributed.yaml","size_in_bytes":2250},{"relative_path":"examples/command-args.yaml","size_in_bytes":1650},{"relative_path":"examples/content-file-mixed.yaml","size_in_bytes":1607},{"relative_path":"examples/cron-trigger.yaml","size_in_bytes":955},{"relative_path":"examples/cypress-git.yaml","size_in_bytes":1476},{"relative_path":"examples/distributed-k6.yaml","size_in_bytes":2454},{"relative_path":"examples/go-build-lint.yaml","size_in_bytes":1528},{"relative_path":"examples/gradle-java.yaml","size_in_bytes":1277},{"relative_path":"examples/healthcheck-config.yaml","size_in_bytes":2138},{"relative_path":"examples/jmeter-template.yaml","size_in_bytes":1598},{"relative_path":"examples/k6-browser.yaml","size_in_bytes":1300},{"relative_path":"examples/k6-inline.yaml","size_in_bytes":2185},{"relative_path":"examples/locust-load-test.yaml","size_in_bytes":1480},{"relative_path":"examples/maven-java.yaml","size_in_bytes":1302},{"relative_path":"examples/parallel-matrix.yaml","size_in_bytes":2971},{"relative_path":"examples/playwright-git.yaml","size_in_bytes":2014},{"relative_path":"examples/pod-volumes.yaml","size_in_bytes":2212},{"relative_path":"examples/postman-git.yaml","size_in_bytes":1113},{"relative_path":"examples/robot-framework.yaml","size_in_bytes":1729},{"relative_path":"examples/selenium-services.yaml","size_in_bytes":2034},{"relative_path":"examples/step-level-use.yaml","size_in_bytes":1520},{"relative_path":"examples/suite-execute.yaml","size_in_bytes":954},{"relative_path":"references/analysis-guide.md","size_in_bytes":3720},{"relative_path":"references/cli-reference.md","size_in_bytes":4527},{"relative_path":"references/docs-concepts.md","size_in_bytes":6803},{"relative_path":"references/examples-catalog.md","size_in_bytes":4928},{"relative_path":"references/schema.md","size_in_bytes":9013},{"relative_path":"references/step-patterns.md","size_in_bytes":14073}],"name":"testworkflow-author","skill_md_contents":"---\nname: testworkflow-author\ndescription: \"Create and validate Testkube TestWorkflow YAML files. Use when writing test workflow YAML, choosing step types, or when the user asks to create a Testkube TestWorkflow. Covers shell steps, container run steps, execute composition, templates, services, artifacts, config parameters, and cron triggers. By default it takes free-form requirements and writes a YAML file; it can OPTIONALLY take a structured JSON authoring request and/or emit the finished workflow as JSON for programmatic callers, without changing the default behavior. Does NOT run workflows — that is the testworkflow-runner skill's responsibility.\"\n---\n\n# testworkflow-author\n\nCreate and validate Testkube TestWorkflow YAML files. This skill writes the workflow and validates its schema. It does\nNOT run the workflow or analyze execution results — the `testworkflow-runner` skill handles that.\n\nWrite files in the current working directory. Default to the filename `testworkflow.yaml` unless a different path is\nspecified. Do not invent credentials, secrets, tokens, or private URLs — if you need them, describe what you need in\nyour final message (see Credentials below).\n\n## Input and output: default behavior vs. optional JSON mode\n\n**Default (UNCHANGED — this is what you do unless the invocation explicitly opts in below).**\nRequirements arrive as free-form natural language. You write the workflow to a YAML file in the current\nworking directory (default filename `testworkflow.yaml`), validate it, and describe the result in your\nfinal message. Nothing about this behavior changes. If you are unsure which mode you are in, you are in\nthis one — it is the correct default.\n\n**Optional JSON mode (opt-in only).** Some callers are programs, not people. When — and only when — the\ninvocation explicitly opts in, you may take a structured JSON authoring request as input, emit the\nfinished workflow as a structured JSON envelope as output, or both. The two directions are independent;\neither can be requested without the other.\n\nOpt-in signals:\n- **JSON input**: the invocation supplies a JSON object (an authoring request) as its payload, or a path\n  to a `.json` request file. Read requirements from it instead of from prose.\n- **JSON output**: the invocation contains a phrase like \"output as JSON\", \"respond with JSON\",\n  \"format: json\", or gives an output path ending in `.json`. Emit the JSON envelope below to stdout\n  instead of prose.\n\n**What does NOT change in JSON mode.** The entire Core Loop and every Rule still apply — read lock files,\nmatch image versions, install dependencies in a separate step, validate with `--dry-run`. The YAML you\nproduce in JSON mode is byte-for-byte the YAML you would have written to disk in default mode. JSON mode\nonly changes how requirements come in and how the finished workflow is handed back; it is a thin wrapper\naround the exact same authoring, never a different authoring. When no opt-in signal is present, ignore\nthis section entirely.\n\n### JSON input schema (authoring request)\n\nWhen JSON input is provided, it has the shape below. Only `framework` (or an explicit `testCommand`) is\nrequired; every other field is optional and, when absent, falls back to the same inference you do today\n(read lock files, pick the framework's default command and image). This shape is intentionally compatible\nwith a `suites[]` entry from the `test-discovery` skill, so that skill's output can be piped straight in.\n\n```json\n{\n  \"workflowName\": \"playwright-e2e\",\n  \"framework\": \"playwright\",\n  \"language\": \"typescript\",\n  \"workingDir\": \"/data/repo\",\n  \"content\": { \"git\": { \"uri\": \"https://github.com/acme/app\", \"revision\": \"main\" } },\n  \"image\": \"mcr.microsoft.com/playwright:v1.49.0-noble\",\n  \"installCommand\": \"npm ci\",\n  \"testCommand\": \"npx playwright test\",\n  \"envVars\": [\"BASE_URL\", \"TEST_USER\"],\n  \"artifacts\": [\"playwright-report/**\", \"test-results/**\"],\n  \"config\": {}\n}\n```\n\nIf required information is missing and cannot be inferred, emit the `needs_input` envelope rather than\nguessing (same discipline as the Credentials rule). Full field list: `assets/json-mode-input.schema.json`.\n\n### JSON output envelope\n\nWhen JSON output is requested, emit exactly one JSON document to stdout — no prose, no code fences; it\nstarts with `{` and ends with `}`:\n\n```json\n{\n  \"schemaVersion\": 1,\n  \"status\": \"success\",\n  \"workflow\": {\n    \"name\": \"playwright-e2e\",\n    \"filename\": \"testworkflow.yaml\",\n    \"yaml\": \"apiVersion: testworkflows.testkube.io/v1\\nkind: TestWorkflow\\nmetadata:\\n  name: playwright-e2e\\nspec:\\n  ...\"\n  },\n  \"validation\": { \"method\": \"dry-run\", \"passed\": true, \"errors\": [] },\n  \"notes\": []\n}\n```\n\n- `workflow.yaml` is the complete workflow as a string — identical to the file the default mode writes.\n- If a target file path was also given, still write the YAML file; stdout only mirrors it inside the envelope.\n- `validation.method` is `\"dry-run\"` when the `testkube` CLI was available, else `\"skipped\"` (say why in `notes`).\n\nAlternate statuses (same JSON discipline — never guess, report instead):\n\n```json\n{ \"schemaVersion\": 1, \"status\": \"needs_input\", \"reason\": \"<short tag>\", \"message\": \"<one sentence>\", \"context\": {} }\n```\n\n```json\n{ \"schemaVersion\": 1, \"status\": \"error\", \"code\": \"<short tag>\", \"message\": \"<one sentence>\" }\n```\n\nFull envelope schema: `assets/json-mode-output.schema.json`. Contract tests: `tests/testworkflow-author-json-mode/`.\n\n## The Core Loop\n\nEvery TestWorkflow task follows this sequence:\n\n1. **Review context** — check for context from previous steps (what tool was chosen, what version, how to run it, what\n   files exist).\n2. **Gather requirements** — test tool, environment variables, expected artifacts, resource needs.\n3. **Read dependency files** — read the lock file (package-lock.json, go.sum, poetry.lock, Gemfile.lock, pom.xml) to\n   determine exact resolved versions. If no lock file exists, read the manifest (package.json, go.mod, etc.)\n4. **Read schema** — load `references/schema.md` with the `read` tool (Rule 7)\n5. **Choose step types** — use the Decision Tree and Step Type Guide below\n6. **Write the YAML** — load a matching example from `examples/` as your starting template (e.g.\n   `examples/playwright-git.yaml` for Playwright, `examples/go-build-lint.yaml` for Go,\n   `examples/k6-inline.yaml` for K6)\n7. **Validate** — `testkube create testworkflow --dry-run -f <file>`\n8. **Fix and re-validate** — address every validation error before proceeding\n\n## Credentials\n\nWhen the workflow requires credentials, tokens, or secrets you do not have, describe what you need clearly in your final\nmessage. Do not guess or use placeholder values. The credential will be provided on your next invocation.\n\n### How to reference credentials in workflows\n\nUse Testkube-managed `credential()` expressions:\n\n```yaml\nenv:\n  - name: API_KEY\n    value: '{{ credential(\"my-api-key\") }}'\n```\n\nOr Kubernetes Secret references:\n\n```yaml\nenv:\n  - name: API_TOKEN\n    valueFrom:\n      secretKeyRef:\n        name: my-secret\n        key: token\n```\n\nIf you don't know the credential name, say what you need in your response and it will be provided.\n\n## Rules\n\nThese are mandatory. Violating any rule produces a broken workflow.\n\n1. **MUST install dependencies in a separate step.** If the project has a dependency manifest (package.json, go.mod,\n   requirements.txt, Gemfile, pom.xml, build.gradle), add a dedicated install step BEFORE the test step. Never assume\n   dependencies are pre-installed in the container image. Example: `npm ci`, `go mod download`,\n   `pip install -r requirements.txt`.\n\n2. **MUST read the lock file to determine resolved versions.** Semver ranges in manifests (`^1.52.0`, `~2.3`, `>=1.0`)\n   do NOT tell you what version gets installed. Read package-lock.json, go.sum, poetry.lock, Gemfile.lock to find the\n   actual resolved version. If no lock file exists, run the install command and check what was resolved, or use the\n   latest stable image for the tool.\n\n3. **MUST match the container image tag to the resolved dependency version.** The image tag must match the major.minor\n   version from the lock file. Example: if package-lock.json resolves `@playwright/test` to `1.61.1`, use image\n   `mcr.microsoft.com/playwright:v1.61.1-noble`. A mismatch causes `browserType.launch: Executable doesn't exist` or\n   equivalent errors.\n\n4. **MUST use `npx`/equivalent for locally-installed CLI tools.** After `npm ci`, binaries are in `node_modules/.bin/`,\n   not on PATH. Run tools via `npx <tool>` (Node.js), `python -m <module>` (Python), or the built binary path (Go).\n   Never assume a tool is globally available just because its package is installed.\n\n5. **MUST set `condition: always` on artifact collection steps.** Artifacts (reports, screenshots, traces) are most\n   valuable when tests fail. Without `condition: always`, the artifact step is skipped on failure — exactly when you\n   need it most.\n\n6. **MUST understand that `--dry-run` validates schema only.** A passing dry-run means the YAML structure is valid. It\n   does NOT mean the workflow will succeed at runtime. Missing dependencies, wrong image versions, and incorrect\n   commands all pass dry-run without error.\n\n7. **MUST read `references/schema.md` before writing any YAML.** This reference covers the most-used fields\n   with examples. Load it with the `read` tool before starting to write. Do not rely on memory alone.\n\n## Decision Tree\n\n### Choosing a step type\n\n```\nNeed to run a test?\n├── Single command, default image?\n│   └── shell step (simplest)\n├── Different image or custom env per step?\n│   └── run step\n├── Reuse an existing pattern (official/community template)?\n│   └── template step\n├── Compose multiple existing workflows or tests?\n│   └── execute step\n├── Run same test with varying parameters (matrix/shard)?\n│   └── parallel step\n├── Need a companion service (database, Selenium, Docker-in-Docker)?\n│   └── services\n└── Need pre/post cleanup that always runs?\n    └── setup / after\n```\n\n## Schema Quick Reference\n\nThe 15 most-used spec fields. See `references/schema.md` for the full schema.\n\n| Field                       | Purpose                            | Example                                 |\n| --------------------------- | ---------------------------------- | --------------------------------------- |\n| `spec.content`              | Where test code comes from         | `{git: {uri, revision}}` or `{files}`   |\n| `spec.container.image`      | Default container image for steps  | `\"node:22\"`                             |\n| `spec.container.resources`  | CPU/memory requests and limits     | `{requests: {cpu: 128m, memory: 128Mi}}`|\n| `spec.container.workingDir` | Working directory for all steps    | `\"/data/repo\"`                          |\n| `spec.steps[].shell`        | Inline shell command (simplest)    | `\"npm test\"`                            |\n| `spec.steps[].run`          | Step with custom container/image   | `{image, shell, env}`                   |\n| `spec.steps[].execute`      | Run other workflows or tests       | `{workflows: [{name}]}`                 |\n| `spec.steps[].template`     | Use a TestWorkflowTemplate         | `{name: \"official/k6\", config: {...}}`  |\n| `spec.steps[].parallel`     | Parallel matrix or sharding        | `{count: 5}`                            |\n| `spec.steps[].artifacts`    | Collect output files               | `{paths: [\"**/*\"]}`                     |\n| `spec.config`               | Parameterized inputs for workflow  | `{VUS: {type: integer, default: 5}}`    |\n| `spec.events`               | CronJob triggers                   | `[{cronjob: {cron: \"0 * * * *\"}}]`      |\n| `spec.services`             | Sidecar containers (DB, Selenium)  | `{chrome: {image, readinessProbe}}`     |\n| `spec.setup` / `spec.after` | Pre/post hooks, same as steps      | `setup: [{shell: \"...\"}]`               |\n\n## CLI Quick Start\n\nCommands for authoring. Full flags: `references/cli-reference.md`.\n\n```bash\n# Validate a workflow (schema check only — does NOT guarantee runtime success)\ntestkube create testworkflow --dry-run -f workflow.yaml\n\n# Create a workflow (use --update if it already exists)\ntestkube create testworkflow -f workflow.yaml\ntestkube create testworkflow --update -f workflow.yaml\n\n# Inspect a workflow definition\ntestkube get testworkflow <name>\n```\n\nNote: Running workflows and reading execution logs is the `testworkflow-runner` skill's job, not yours.\n\n## Step Type Decision Guide\n\nDetailed patterns with YAML examples: `references/step-patterns.md`.\n\n| Step Type    | Use When                        | Key Fields                            | Example Scenario                               |\n| ------------ | ------------------------------- | ------------------------------------- | ---------------------------------------------- |\n| **shell**    | Single command, default image   | `shell`                               | \"Run npm test in the default Playwright image\" |\n| **run**      | Different image/env per step    | `run: {image, shell, env, resources}` | \"Lint Go code with golang:1.26 image\"          |\n| **execute**  | Run existing Testkube resources | `execute: {workflows, tests}`         | \"Orchestrate a test suite of 3 workflows\"      |\n| **template** | Reuse a TestWorkflowTemplate    | `template: {name, config}`            | \"Use official/k6 template with custom VUS\"     |\n| **use**      | Include template defaults       | `use: [{name}]`                       | \"Add close-istio sidecar template\"             |\n| **parallel** | Matrix or sharded execution     | `parallel: {count, shards, matrix}`   | \"Run K6 with 5 VUs in parallel\"                |\n\n### Step nesting\n\nSteps can be nested: a parent step with `steps:` runs its children sequentially within that step's container context.\nUse nesting for logical grouping — install deps, run tests, save artifacts — within a single container image.\n\n### Step ordering\n\nSteps run sequentially by default. Use `condition` to control when a step runs (`\"passed\"`, `\"failed\"`, `\"always\"`). Use\n`optional: true` for non-critical steps whose failure should not fail the workflow.\n\n## Gotchas\n\nEnvironment-specific facts that defy reasonable assumptions.\n\n- **Container merging**: By default, multiple simple steps may merge into one container for efficiency. Use\n  `spec.system.isolatedContainers: true` for full isolation between steps.\n- **Init containers**: Steps execute as init containers sequentially; the last step runs as the main container. Sidecar\n  containers injected by operators (Istio, Linkerd) may not be available to init containers.\n- **workingDir resolution**: Relative paths resolve against the container's WORKDIR, not the workflow root. Set\n  `workingDir` explicitly to the directory where your test code lives.\n- **Artifact paths**: Relative artifact paths resolve against the step's `workingDir`. Use absolute paths\n  (`/data/artifacts/**`) to avoid ambiguity.\n- **`{{ }}` expressions**: Usable in most string fields. `{{ config.param }}` for config values,\n  `{{ services.name.0.ip }}` for service IPs, `{{ shellquote(env.VAR) }}` for safe shell quoting of env vars.\n- **Image pull**: Images must come from a container registry — local images are not supported. Private registries\n  require `imagePullSecrets` in `spec.pod`.\n- **`activeDeadlineSeconds`**: Without this, a stuck workflow runs until the cluster kills it. Set in `spec.job` for\n  workflow-level timeout, or `spec.pod` for per-pod limits.\n- **Template inlining**: Templates are expanded before execution. `use` at top-level shares defaults across all steps;\n  `use` at step-level scopes defaults to that step only; `template` at step-level is fully isolated.\n- **`shell` auto-prepends `set -e`**: The shell step type exits on the first failing command by default. Use `run.shell`\n  if you need more control over error handling behavior.\n- **Playwright images**: Use `mcr.microsoft.com/playwright:v<VERSION>-noble` where `<VERSION>` matches the resolved\n  `@playwright/test` version from the lock file. A bare `node` image will fail with\n  `browserType.launch: Executable doesn't exist`. Check\n  [Microsoft Artifact Registry](https://mcr.microsoft.com/en-us/product/playwright/about) for available tags.\n\n## Validation Loop\n\nBefore finishing, always validate the schema:\n\n```bash\n# 1. Validate against the CRD schema\ntestkube create testworkflow --dry-run -f workflow.yaml\n\n# 2. If validation fails, read the error message, fix the file, re-run step 1\n\n# 3. Only proceed when dry-run passes\n```\n\nNever skip validation. A YAML that looks correct can still fail schema checks (missing required fields, wrong types,\ninvalid enum values). Remember: dry-run validates structure only — it cannot catch runtime errors like missing\ndependencies or image version mismatches (see Rule 6).\n\n## Reference Index\n\nLoad these files on demand — when the task calls for them.\n\n| Reference                        | When to Load                                              | Status    |\n| -------------------------------- | --------------------------------------------------------- | --------- |\n| `assets/workflow-schema.yaml`    | When needing every available field and description        | Available |\n| `assets/template-schema.yaml`    | When authoring TestWorkflowTemplates                      | Available |\n| `assets/json-mode-input.schema.json`  | When taking a JSON authoring request (optional JSON mode) | Available |\n| `assets/json-mode-output.schema.json` | When emitting the JSON output envelope (optional JSON mode)| Available |\n| `references/schema.md`           | When writing or editing any YAML field                    | Available |\n| `references/cli-reference.md`    | When needing CLI flags for create/dry-run                 | Available |\n| `references/step-patterns.md`    | When choosing between step types or seeking YAML snippets | Available |\n| `references/docs-concepts.md`    | When a concept (templates, merging, artifacts) is unclear | Available |\n| `references/examples-catalog.md` | When seeking real-world patterns by tool/pattern          | Available |\n| `examples/*.yaml`                | When needing concrete, runnable examples                  | Available |\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}